Anonymous avatar Anonymous committed 2b957be

new publisher/subscriber system, added FileSelector widget

Comments (0)

Files changed (9)

src/de/matthiasmann/twlscala/Button.scala

 
 import twlinternal._
 
+case class ButtonClicked(button:Button)
+
 class Button extends de.matthiasmann.twl.Button with SimpleCallbackSupport with ThemeSupport {
     theme = "button"
     def this(text:String) { this(); setText(text) }
 
     def text:String = getText
     def text_= (t:String) { setText(t) }
+
+    protected def simplePublisherMsg = ButtonClicked(this)
 }

src/de/matthiasmann/twlscala/ComboBox.scala

 import twlinternal._
 import de.matthiasmann.twl.model.{ListModel => TWLListModel}
 
+case class ComboBoxSelectionChanged(combobox:ComboBox)
+
 class ComboBox extends de.matthiasmann.twl.ComboBox with SimpleCallbackSupport with ThemeSupport {
     theme = "combobox"
     def this(m:TWLListModel[_]) { this(); setModel(m) }
     def selected_= (i:Int) { setSelected(i) }
     def computeWidthFromModel = isComputeWidthFromModel
     def computeWidthFromModel_= (b:Boolean) { setComputeWidthFromModel(b) }
+
+    protected def simplePublisherMsg = ComboBoxSelectionChanged(this)
 }

src/de/matthiasmann/twlscala/DialogLayout.scala

 import twlinternal._
 import de.matthiasmann.twl.{Widget=>TWLWidget, DialogLayout=>TWLDialogLayout}
 
-class DialogLayout extends TWLDialogLayout with ThemeSupport {
+class DialogLayout extends TWLDialogLayout with ThemeSupport with Reactor {
     theme = "dialoglayout"
 
     type TWLGroup = TWLDialogLayout#Group

src/de/matthiasmann/twlscala/FileSelector.scala

+/*
+ * Copyright (c) 2008-2009, Matthias Mann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Matthias Mann nor the names of its contributors may
+ *       be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.matthiasmann.twlscala
+
+import twlinternal._
+import de.matthiasmann.twl.{FileSelector=>TWLFileSelector}
+import de.matthiasmann.twl.model.{FileSystemModel, JavaFileSystemModel}
+
+case class File(fsm:FileSystemModel, obj:AnyRef) {
+    def name = fsm.getName(obj)
+    def path = fsm.getPath(obj)
+    def size = fsm.getSize(obj)
+    def lastModified = new java.util.Date(fsm.getLastModified(obj))
+    def isFolder = fsm.isFolder(obj)
+    def parent:Option[File] = fsm.getParent(obj) match { case null => None; case x => Some(File(fsm, x)) }
+    def openStream = fsm.openStream(obj)
+    def openChannel = fsm.openChannel(obj)
+}
+
+case class FileSelectorFilesSelected(fileSelector:FileSelector, files:List[File])
+case class FileSelectorCanceled(fileSelector:FileSelector)
+
+class FileSelector extends TWLFileSelector with ThemeSupport with Publisher {
+    theme = "fileselector"
+    
+    def allowMultiSelection = getAllowMultiSelection
+    def allowMultiSelection_= (b:Boolean) { setAllowMultiSelection(b) }
+    def allowFolderSelection = getAllowFolderSelection
+    def allowFolderSelection_= (b:Boolean) { setAllowFolderSelection(b) }
+    def fileSystemModel = getFileSystemModel
+    def fileSystemModel_= (fsm:FileSystemModel) { setFileSystemModel(fsm) }
+    def currentFolder = File(fileSystemModel, getCurrentFolder)
+    def currentFolder_= (f:File) {
+        if(f.fsm ne fileSystemModel) fileSystemModel = f.fsm
+        setCurrentFolder(f.obj)
+    }
+
+    def createJavaFileSystemModel { fileSystemModel = new JavaFileSystemModel }
+    
+    addCallback(new TWLFileSelector.Callback {
+        def filesSelected(objs:Array[AnyRef]) {
+            val fsm = fileSystemModel
+            publish(FileSelectorFilesSelected(FileSelector.this, objs.map(File(fsm, _)).toList))
+        }
+        def canceled {
+            publish(FileSelectorCanceled(FileSelector.this))
+        }
+    })
+}

src/de/matthiasmann/twlscala/Publisher.scala

+/*
+ * Copyright (c) 2008-2009, Matthias Mann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Matthias Mann nor the names of its contributors may
+ *       be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.matthiasmann.twlscala
+
+trait Reactor {
+    lazy val reactions = new Reactions
+    def listenTo(p:Publisher) { p.subscribers += reactions }
+    def deafTo(p:Publisher) { p.subscribers -= reactions }
+}
+
+trait Publisher extends Reactor {
+    def publish(msg:Any) { subscribers.publish(msg) }
+    listenTo(this)
+    
+    private[twlscala] lazy val subscribers = new SubscriberList
+}
+
+final class Reactions {
+    type Reaction = PartialFunction[Any,Unit]
+
+    def += (f:Reaction) {
+        list = f :: list
+    }
+
+    private var list:List[Reaction] = Nil
+    private[twlscala] def handle(msg:Any) {
+        def walkList(l:List[Reaction]):Unit = l match {
+            case r :: rest =>
+                if(r.isDefinedAt(msg)) r(msg)
+                walkList(rest)
+            case _ =>
+        }
+        walkList(list)
+    }
+}
+
+final private class SubscriberList {
+    import scala.ref.WeakReference
+    type ListType = List[WeakReference[Reactions]]
+    private var list:ListType = Nil
+
+    def += (r:Reactions) { list = new WeakReference(r) :: list}
+    def -= (r:Reactions) {
+        list = list.filter(_.get match {
+            case None    => false
+            case Some(x) => x != r
+        })
+    }
+
+    def publish(msg:Any) {
+        val hasDeadEntries = list.foldLeft(false){(a,b) => b.get match {
+            case Some(r) =>
+                r.handle(msg)
+                a
+            case None =>
+                true
+        }}
+        if(hasDeadEntries) {
+            list = list.filter(!_.get.isEmpty)
+        }
+    }
+}

src/de/matthiasmann/twlscala/TWL.scala

 
     def contents = root.contents
 
-    def ! (body: => Unit) { gui.invokeLater(new RunnableImpl(body)) }
+    def ! (body: => Unit) { gui.invokeLater(new Runnable { def run { body }}) }
 }

src/de/matthiasmann/twlscala/twlinternal/EnumCallbackSupport.scala

 
 import de.matthiasmann.twl.CallbackWithReason
 
-private[twlscala] class CallbackWithReasonImpl[T <: Enum[T]](handler: PartialFunction[T,Unit]) extends CallbackWithReason[T] {
-    def callback(e:T) = if(handler.isDefinedAt(e)) handler(e)
+trait EnumCallbackSupport[T <: Enum[T]] extends Publisher { self =>
+    def addCallback(h: CallbackWithReason[T])
+    addCallback(new CallbackWithReason[T] {
+        def callback(e:T) = self.publish(e)
+    })
 }
-
-trait EnumCallbackSupport[T <: Enum[T]] {
-    def addCallback(h: CallbackWithReason[T])
-    def addCallback(h: PartialFunction[T,Unit]) { addCallback(new CallbackWithReasonImpl(h)) }
-    def removeCallback(h: CallbackWithReason[T])
-    lazy val callbacks = new {
-        def + (h:CallbackWithReason[T]):this.type = { +=(h); this }
-        def += (h:CallbackWithReason[T]) { addCallback(h) }
-        def + (h: PartialFunction[T,Unit]):this.type = { +=(h); this }
-        def += (h: PartialFunction[T,Unit]) { addCallback(h) }
-        def - (h:CallbackWithReason[T]):this.type = { -=(h); this }
-        def -= (h:CallbackWithReason[T]) { removeCallback(h) }
-    }
-}

src/de/matthiasmann/twlscala/twlinternal/SimpleCallbackSupport.scala

  */
 package de.matthiasmann.twlscala.twlinternal
 
-private[twlscala] class RunnableImpl(body: => Unit) extends Runnable { def run = body }
-
-trait SimpleCallbackSupport {
-    def addCallback(r: Runnable)
-    def addCallback(body: => Unit) { addCallback(new RunnableImpl(body)) }
-    def removeCallback(r: Runnable)
-    lazy val callbacks = new {
-        def + (r:Runnable):this.type = { +=(r); this }
-        def += (r:Runnable) { addCallback(r) }
-        def + (b: => Unit):this.type = { +=(b); this }
-        def += (b: => Unit) { addCallback(b) }
-        def - (r:Runnable):this.type = { -=(r); this }
-        def -= (r:Runnable) { removeCallback(r) }
-    }
+trait SimpleCallbackSupport extends Publisher {
+    def addCallback(r:Runnable)
+    protected def simplePublisherMsg:Any
+    addCallback(new Runnable { def run { publish(simplePublisherMsg) }})
 }

src/de/matthiasmann/twlscala/twlinternal/ThemeSupport.scala

 package de.matthiasmann.twlscala.twlinternal
 
 import de.matthiasmann.twl.{Widget=>TWLWidget, Event=>TWLEvent}
+import de.matthiasmann.twlscala._
 
 trait ThemeSupport extends TWLWidget { this:TWLWidget =>
     def theme = getTheme
     def visible_= (b:Boolean) { setVisible(b) }
     def clip = isClip
     def clip_= (b:Boolean) { setClip(b) }
-    def canAcceptKeyboardFocus:Boolean
-    def canAcceptKeyboardFocus_= (b:Boolean) { setCanAcceptKeyboardFocus(b) }
+    def acceptKeyboardFocus = canAcceptKeyboardFocus
+    def acceptKeyboardFocus_= (b:Boolean) { setCanAcceptKeyboardFocus(b) }
+    def focusKeyEnabled = isFocusKeyEnabled
+    def focusKeyEnabled_= (b:Boolean) { setFocusKeyEnabled(b) }
     def size = Size(getWidth, getHeight)
     def size_= (size:Size) { setSize(size.width, size.height) }
     def position = Position(getX, getY)
         doInsertAll(n, iter.elements)
     }
     def +: (w:TWLWidget) = { peer.insertChild(w, 0); this }
-    def += (w:TWLWidget) { peer.add(w) }
+    def += (w:TWLWidget) = { peer.add(w); this }
+    def ++= (xs:TWLWidget*) = { insertAll(length, xs) }
 }
+
+/* Version for Scala 2.8
+
+import scala.collection._
+import mutable.Buffer
+
+class WidgetContent private[twlscala] (peer:TWLWidget) extends RandomAccessSeq[TWLWidget] {
+    override def stringPrefix = "WidgetContent"
+    def length = peer.getNumChildren
+    def apply(n:Int) = peer.getChild(n)
+    def update(n:Int, w:TWLWidget) { peer.removeChild(n); peer.insertChild(w, n) }
+    def clear { peer.removeAllChildren }
+    def remove(n:Int) = peer.removeChild(n)
+    def insertAll(n:Int, iter:Traversable[TWLWidget]) {
+        if(!iter.isEmpty) {
+            peer.insertChild(iter.head, n)
+            insertAll(n+1, iter.tail)
+        }
+    }
+    def +: (w:TWLWidget) = { peer.insertChild(w, 0); this }
+    def += (w:TWLWidget) = { peer.add(w); this }
+}
+*/
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.