Commits

Andrew Peterson committed 320ea52

Import of pyui2-0.2.1 source.

Comments (0)

Files changed (150)

+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+## imports of publis interface from core
+from core import init
+from core import quit
+from core import update
+from core import draw
+from core import run
+from core import isRunning
+
+## imports of all modules
+import core, locals, desktop, base, widgets, frame, layouts, dialogs, viewer
+
+from core import gRenderer
+
+readTimer = desktop.readTimer
+
+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+veri = 0
+
+import pyui2
+from pyui2.desktop import getDesktop, getTheme
+from pyui2 import layouts
+#from window import Window
+
+class Base:
+    """Base GUI object that all other drawable object derive from.
+    this object implements:
+        - containment of other GUI objects
+        - position and size
+        - event handling
+        - unique identity of all GUI objects
+
+    self.rect is in absolute co-ordinates, self.windowRect is in relative window co-ordinates.
+    self.posX and self.posY are relative to the parent object
+    """
+    widgetLabel = "BASE"
+
+    def __init__(self):
+        """Initialize and register the base widget. widgets are added to the global widget list initially.
+        """
+        self.canTab = 0     # this widget acts as a tab stop
+        self.widgetID = ""
+        self.parent = None
+        self.window = None
+        self.posX = 0
+        self.posY = 0
+        self.width = 1
+        self.height = 1
+        self.show = 1
+        self.dirty = 1
+        self.children = []
+        self.eventMap = {}
+        self.calcSize()
+        getDesktop().registerWidget(self)
+        self.popup = None
+        self.tooltipText = ""
+        self.font = None
+
+
+#    def hit(self, pos):
+#        """Check for a hit using absolute coordinates.
+#        """
+#        #if self.rect[0] < pos[0] < self.rect[0] + self.rect[2] and self.rect[1] < pos[1] < self.rect[1] + self.rect[3]:
+#        if self.rect[0] < pos[0] < self.rect[0] + self.rect[2] and self.rect[1] > pos[1] > self.rect[1] - self.rect[3]:
+#            return 1
+#        else:
+#            return 0
+
+    def getParentWindow(self, par):
+        while par != None:
+            if issubclass(par.__class__, pyui2.window.Window):
+                return par
+            par = par.parent
+        return None
+
+    def convertToWindowCoords(self, pos):
+        newPos = pos
+        win = self.getParentWindow(self.parent)
+        if win != None:
+            newPos = win.desktopToWindow(pos)
+        return newPos
+
+    def hit(self, pos):
+        """Check for a hit adjusting the position relative to the parent window.
+        """
+        adjPos = self.convertToWindowCoords(pos)
+        rect = (self.posX, self.posY, self.rect[2], self.rect[3])
+
+        #print "hitRel: pos =", pos,  "adjPos =", adjPos, "rect=", rect
+
+        if rect[0] < adjPos[0] < rect[0] + rect[2] and rect[1] < adjPos[1] < rect[1] + rect[3]:
+            return 1
+        else:
+            return 0
+
+    def isWindow(self):
+        return False
+
+    def getFocus(self):
+        """Acquire the gui system's focus. only one Base may have the focus
+        """
+        #if isinstance(self, Window):
+        if self.isWindow():
+            getDesktop().activateWindow(self)
+        else:
+            getDesktop().activateWindow(self.window)
+        getDesktop().setFocus(self)
+        self.setDirty()
+
+    def loseFocus(self):
+        """lose the gui system's focus.
+        """
+        getDesktop().setFocus(None)
+        self.setDirty()
+
+    def hasFocus(self):
+        return getDesktop().getFocus() == self
+
+    def postEvent(self, eventType):
+        """Post an event to be processed next time through the event loop
+        """
+        if getDesktop():
+            return getDesktop().postEvent(eventType, self.id)
+
+
+    def calcSize(self):
+        """This sets up self.rect to be absolute co-ordinates. also sets up self.windowRect
+        to be relative to the upper left of the parent Window
+        """
+        (x, y) = (self.posX, self.posY)
+        p = self.parent
+        while p and not p.isWindow(): #isinstance(p, Window):
+            x += p.posX
+            y += p.posY
+            p = p.parent
+
+        if self.window:
+            self.rect = (x + self.window.posX, y + self.window.posY, self.width, self.height)
+            self.windowRect = (x, y, self.width, self.height)
+        else:
+            self.rect = (self.posX, self.posY, self.width, self.height)
+            self.windowRect = (0, 0, self.width, self.height)
+
+        for child in self.children:
+            child.calcSize()
+
+    def getPreferredSize(self):
+        """Determine the recommended size for this widget.
+        This size should never be the value 'Much'.
+        """
+        return (self.width, self.height)
+
+    def getMaximumSize(self):
+        """Determine the recommended maximum size for this widget
+        """
+        return (layouts.Much, layouts.Much)
+
+    def addChild(self, child):
+        """Add a child widget.
+        """
+        self.children.append(child)
+        child.setWindow(self.window)
+        child.setParent(self)
+
+    def removeChild(self, child):
+        try:
+            self.children.remove(child)
+            child.setParent(None)
+            return child
+        except:
+            print "ERROR: couldn't find the child to remove."
+            return None
+
+    def addPopup(self, popup):
+        ### arg... dont know about popups here..
+        ### assert isinstance(popup, MenuPopup)
+        self.popup = popup
+
+    def setParent(self, parent):
+        """Set the parent of this widget
+        """
+        self.parent = parent
+
+    def setWindow(self, window):
+        self.window = window
+        for child in self.children:
+            child.setWindow(window)
+
+    def present(self, presenter, graphicsContext, parentRect=None):
+        presenter.drawWidget(self.widgetLabel, self, graphicsContext, parentRect)
+
+    def handleEvent(self, event):
+        """ event processing for base objects
+        """
+        if not self.show:
+            return
+        i = len(self.children) - 1
+        while i > -1:
+            child = self.children[i]
+            if child.handleEvent(event):
+                #print child, "handled", event.type
+                return 1
+            i = i  - 1
+        if self.eventMap.has_key(event.type):
+            if self.eventMap[event.type](event):
+                #print self, "handled", event.type
+                return 1
+
+        # popup handling here so it's not overridden with subclass event behavior
+        if self.popup and event.type == pyui2.locals.RMOUSEBUTTONDOWN and self.hit(event.pos):
+            self.popup.activate(event.pos[0], event.pos[1])
+            return 1
+        return 0
+
+    def moveto(self, x, y):
+        """move to absolute position.
+        """
+        self.posX = x
+        self.posY = y
+        self.calcSize()
+
+    def move(self, dx, dy):
+        """move relative to current position.
+        """
+        self.posX = self.posX + dx
+        self.posY = self.posY + dy
+        self.calcSize()
+
+    def resize(self, w, h):
+        """ resize absolute size of the widget
+        """
+        self.setDirty()
+        self.width = w
+        self.height = h
+        self.calcSize()
+
+    def registerEvent(self, eventType, handler):
+        """Setup handler for an event
+        """
+        self.eventMap[eventType] = handler
+
+    def unregisterEvent(self, eventType):
+        """Remove handler for an event
+        """
+        if self.eventMap.has_key(eventType):
+            del self.eventMap[eventType]
+
+    def pack(self):
+        """used by panels & layout managers
+        """
+        pass
+
+    def setDirty(self, collide = 1):
+        """Sets this widget to redraw itself and notifies window.
+        """
+        self.dirty = 1
+        if self.window:
+            self.window.setDirty()
+
+    def clearDirty(self):
+        """Clears this widgets dirty flag.
+        """
+        self.dirty = 0
+
+    def setID(self, widgetID):
+        self.widgetID = widgetID
+
+    def getID(self):
+        return self.widgetID
+
+
+    def destroy(self):
+        """Call this to remove all references to the widget from the system.
+        """
+        #print "destroying %s (%d)" % (self, self.id)
+        self.window = None
+        self.setParent(None)
+        if self.popup:
+            self.popup.destroy()
+            self.popup = None
+        if self.children:
+            for child in self.children:
+                child.destroy()
+            self.children = []
+        self.eventMap.clear()
+        getDesktop().destroyWidget(self)
+
+    def setShow(self,value):
+        self.show = value
+        self.setDirty()
+        for child in self.children:
+            child.setShow(value)
+        if not value:
+            getDesktop().getTheme().setArrowCursor()
+
+    def __del__(self):
+        #print "Deleting widget %s (%d)" % (self, self.id)
+        pass
+
+    def getToolTipInfo(self, pos):
+        """return a tuple of the text and rectangle for the tooltip for when the
+        mouse is in <pos> within the window. This uses the member variable toolTipInfo
+        if it is populated.
+        """
+        if self.tooltipText:
+            return (self.tooltipText,  (pos[0]-50, pos[1]-20, 120, 30) )
+        else:
+            return None
+
+    def checkHit(self, pos):
+        if not self.show:
+            return None
+        if self.hit(pos):
+            for child in self.children:
+                result = child.checkHit(pos)
+                if result:
+                    return result
+            return self
+        else:
+            return None
+
+
+Changes for PyUI2 since PyUI 1.0 Release
+
+
+Version 0.2.1
+General
+* Added the toolbar widget in the file toolbar.py, currently not complete.
+
+In desktop.py
+* Fixed typo in quit method.
+
+In window.py
+* In the setDirty method, removed self.dirty = 1 which was resulting in the method always exiting before doing anything.
+
+In system/pygamedevice.py
+* The openglgraphics is now being imported when OGL Mode is specified to fix a crash when PyOpenGL is not installed and developer only wants to use Pygame graphics (or other.)
+
+In themes/presenter.py
+* Made changes to the present method to conform to pythonic style.
+
+In test/themesetup.py
+* Typo getThemer changed to getTheme
+
+
+Version 0.2
+
+General
+* In Themes added __all__ and imports for the all available themes to allow for easier importing.
+* Added some test applications.
+* Added ThemeSwitcher demo application
+* The GreenTheme has been deprecated. It still there but highly incomplete, best not to use at all.
+* Fixed issue with the caption bar not resizing when the frame was resized.
+* Major rewrite of the way themes are handled to make it easier to create new themes and to remove redundancy. This is a Work In Progress. 
+* Added IDs to widgets. These are strings. The widget creator can choose to set the ID, or the ID will be automatically created based on the widget type.
+* Extracted Window and Panel from base.py into their own files named window.py and panel.py. Made changes to widgets and other code to accomodate the change.
+* Created a new package called system. This effectively replaces the renderers package.
+  - Added file __init__.py.
+  - Added file dcx.py.
+  - Added file gcx.py.
+  - Added file pygamedevice.py.
+  - Added file pygamegraphics.py.
+  - Added file openglgrapghics.py.
+  - Added file glutdevice.py.
+
+In file renderers\pygame2D.py
+* Fixed bug where the text was always rendered in the default font regardless of which font was passed to the renderers drawText method.
+* Looked into why creating fonts always seemed to fail. Turns out that the PyGame font object wants a full path, not just a fontname. Changed to just use SysFont for now.
+
+In file frame.py
+* Several classes, FrameMenu, FrameMenuItem, and FrameMenuBar were defined but are never used. These have been removed as they are simply duplicating the standard menu functionality. The reference in viewwindow.py was also removed.
+
+In file core.py
+* The default startup theme has been changed from the Future theme to the base Theme.
+
+In file renderBase.py
+* If a callback was specified for the run function, desktop drawing and updating would never be done. This has been fixed.
+
+
+
+
+Version 0.1 - Released May 8th, 2005
+
+General
+* Extracted classes from widgets.py into individual files named for each class under a widgets subdirectory/package. 
+* Extracted classes from layouts.py into individual files named for each class under a layouts subdirectory/package. 
+* Extracted classes from dialogs.py into individual files named for each class under a dialogs subdirectory/package. 
+* Moved other widgets into the widgets directory.
+* Added OSXTheme in themes/osx.py. This is a copy of themes/win2k.py.
+* Added WinXPTheme in themes/winxp.py. This is a copy of themes/win2k.py.
+* Added flags when creating frames.
+  - NO_CAPTION stops the frame from displaying a caption bar.
+  - NO_RESIZE creates a fixed size frame without a resizing area.
+  - TOPMOST replaces the topmost parameter passed to the Frame constructor.
+
+* Added CaptionBar widget in widgets/captionbar.py
+* Removed Escape 'special event handler' from handleEvent in desktop.py
+* Modified the update method in pygame2D.py and openglPygame.py to post a pyui2.locals.QUIT event, when a Pygame QUIT event is received. This fixes the issue where the app doesn't close when the application window is closed and it then hangs.
+* Searched through code base and corrected spelling of 'primative' to 'primitive.'
+
+* Added a new set of tests in the test subdirectory
+* Added a unittest subdirectory under the test subdirectory for future unit testing
+* Added a docs subdirectory for project documentation
+* Added a demos subdirectory under the docs subdirectory to contain demonstration code and projects.
+
+In file core.py
+* Added rendererBase and renderer3d imports to fix a crash
+
+In file renderers\pygame2D.py
+* Added createFont method and removed old font creation from __init__ method. Now uses a pygame/sdl font object. If unable to load normally, will now use pygame.SysFont instead of Font(none) initially.
+* Modified getTextSize to return the size of the text using pygame/sdl font object.
+* Modified doDrawCommand to use the default font when rendering text.
+* Fixed a problem where title text was not displaying in the main window title bar. Added pygame.display.set_caption(title) in the __init__ method.
+
+In file frame.py
+* Added a new method called centerInDesktop, to the Frame class.
+* The caption bar is now a widget. Several methods added or modified to handle this.
+
+In file themes\win2k.py
+* Added the drawDropDown method for drawing the dropDownBox widget.
+* Moved caption bar drawing from drawFrame to a new method called drawCaptionBar.
+* Modified drawFrame to actually draw a border.
+* Modifed the frameColor to gray.
+* Changed the color used for drawing the checkbox check from white to the defined foreground color since the white was really hard to see.
+* Changed the color used for drawing the background of the selected area in an edit field.
+* Fixed problem where text that was longer than the edit box was drawing over the end of the border.
+* If the edit is read only, it is no longer possible to select text and the caret no longer draws.
+
+In file themes\winxp.py
+* Added a method called drawDropDown to draw the dropDownBox widget.
+
+In widgets\checkbox.py
+* Added the getPreferredSize and getMaximumSize methods.
+* Added a parameter to the constructor to create a checkbox already checked.
+
+In widgets\dropdownbox.py
+* Fixed up the draw function to properly render the drop box and selection list.
+* Modifed the constructor to initialise the widget with a list of items, the item to be selected, and to flag the widget as editable.
+* Added the getPreferredSize and getMaximumSize methods.
+* Adding an item to the DropDownBox no longer sets the selection to that item.
+
+
+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Some default colors for use in pyui2 applications.
+These get packed by the specified renderer at initialization.
+"""
+
+black = 0
+white = 0
+red = 0
+green = 0
+blue = 0
+grey = 0
+yellow = 0
+orange = 0
+
+def init():
+    global black, white, red, green, blue, grey, yellow, orange
+    black =   (0,   0,   0, 255)
+    white =   (255, 255, 255, 255)
+    red =     (255, 0,   0, 255)
+    green =   (0,   255, 0, 255)
+    blue =    (0,   0,   255, 255)
+    grey =    (150, 150, 150, 255)
+    yellow =  (255,255,0, 255)
+    orange =  (255,128,0, 255)
+    
+    
+
+# pyui2
+# Copyright (C) 2005 John Judd
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import os
+import string
+import pyui2
+
+def getImagePath(filename):
+    """getImagePath takes a filename and generates a full path based on the
+    location of the images folder in the PyUI2 library.
+    """
+    pathSep = os.sep
+    #print "Separator =", pathSep
+    path = pyui2.__path__[0]
+    filename = path + pathSep + "images" + pathSep + filename
+    return filename
+
+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# system imports
+import sys
+import string
+import os
+import time
+
+# library imports
+import pyui2.locals
+import colors
+
+from desktop import Desktop, readTimer
+from themes import theme
+
+###########################################################
+# Section: Globals
+##########################################################
+
+gRenderer = None
+gDesktop = None
+gVersion = 0.2
+gFrameCounter = 0
+gLastTime = time.time()
+
+#########################################################
+# Section: external/public functions
+#
+# External/Public Module level functions
+#########################################################
+
+def init(w, h, deviceName = "p3d", fullscreen = 0, title=""):
+    """Initialize pyui2. Will set to fullscreen if specified. default is to run in a window.
+    This will return a Desktop Object.
+    (public)
+    """
+    print "init"
+    global gDesktop
+    # create the theme and desktop
+    gDesktop = Desktop(w, h, fullscreen, theme.Theme, deviceName)
+    pyui2.system.getDeviceContext().setWindowTitle(title)
+    colors.init()
+    return gDesktop
+
+def quit():
+    """Sets the running flag so that the application knows to quit.
+    (public)
+    """
+    global gDesktop
+    gDesktop.quit()
+
+def update():
+    """Process events from the renderer, and events posted by users or widgets.
+    Will return 1 if execution should continue, 0 if we should exit.
+    (public)
+    """
+    global gDesktop
+    return gDesktop.update()
+
+def draw():
+    """
+    fills the background and draws the widgets.
+    (public)
+    """
+    global gDesktop
+    gDesktop.draw()
+
+
+def version():
+    """return the version number of pyui2"""
+    global gVersion
+    return gVersion
+
+
+def run(callback=None, trackFPS=True):
+    """This is a default way of _running_ an application using
+    the current renderer.
+    """
+    pyui2.system.getDeviceContext().run(callback)
+
+
+def isRunning():
+    global gDesktop
+    return gDesktop.running
+
+
+def loadpyui2Image(filename):
+    """This loads an image file from the images directory in the pyui2 install.
+    The directory pyui2/images holds general images used in pyui2 that are not
+    application specific.
+    """
+    path = pyui2.__path__[0]
+    pathElements = list(os.path.split(path)) # string.split(path, "\\")
+    pathElements.pop( len(pathElements) -1)
+    realName = string.join( pathElements, "/") + "/images/" + filename
+    getPresenter().loadImage(realName, filename)
+
+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import copy
+
+"""The pyui2 Desktop. This manages the parts of pyui2 that are above the scope of individual windows.
+"""
+import pyui2
+
+
+
+
+######## Exported method to grab the renderer, desktop and theme #########
+
+def getDesktop():
+    return Desktop.theDesktop
+
+def getTheme():
+    return Desktop.theDesktop.theme
+
+def readTimer():
+    return Desktop.theDesktop.readTimer()
+
+######## New theme management #########
+from pyui2.themes.presenter import Presenter
+
+def setTheme(theme):
+    Desktop.theDesktop.theme = theme
+    Desktop.theDesktop.presenter.setTheme(theme)
+
+def getPresenter():
+    return Desktop.theDesktop.presenter
+
+def setBackgroundDrawingFunction(backFunction):
+    Desktop.theDesktop.presenter.setBackgroundDrawing(backFunction)
+
+################# utility classes #######################
+
+class guiEvent:
+    """an event that can be processed by the system.
+    """
+    def __init__(self, type, id = 0, x = 0, y = 0, key = 0, mods = 0):
+        self.type = type
+        self.id = id      # this is the ID of the widgets that caused the event.
+        self.pos = (x,y)
+        self.key = key
+        self.mods = mods
+
+class guiCallback:
+    """a timed callback object. If the callback method returns a logical true, then the callback will
+    automatically be removed. This makes it easy to provide 1-shot timed callbacks. A callback with an interval
+    of 0 will be called on every frame.
+    """
+    def __init__(self, method, interval, lastTrigger):
+        self.method = method
+        self.interval = interval
+        self.lastTrigger = lastTrigger
+
+    def process(self, time):
+        if time >= self.lastTrigger + self.interval:
+            result = self.method(time - self.lastTrigger)
+            self.lastTrigger = time
+            return result
+        else:
+            return 0
+
+################### the desktop class ####################
+
+class Desktop:
+    """The Desktop. I am the container for all global pyui2 data.
+    """
+
+    lastEvent = 999    # used to generate user event numbers
+    theDesktop = None
+
+    def __init__(self, width, height, fullscreen, ThemeClass, deviceName):
+        # setup singleton desktop
+        Desktop.theDesktop = self
+
+        self.presenter = Presenter(width, height, fullscreen)
+        self.presenter.determineDeviceContext(deviceName)
+
+        newTheme = ThemeClass()
+        setTheme(newTheme)
+
+        self.width = width
+        self.height = height
+
+        self.lastID= 1000
+        self.windows = []               # list of top level windows
+        self.allWidgets = {}            # list of ALL the widgets
+        self.running = 1                # set this to zero to exit
+        self.userEvents = []            # list of user events to be processed
+        self.focusWidget = None         # widget with the focus
+        self.userHandlers = {}          # map of user event handlers
+        self.callbacks = {}             # list of timed callback methods (dict for fast removal)
+        self.modal = None               # current modal dialog
+        self.waiting = 0                # flag for waiting mode
+        self.tooltipCallback = None     # callback to enable tooltip
+        self.toooltipFocus = None       # widget with tooltip focus
+        self.tooltipPosition = None     # position of mouse when tooltip is activated
+        self.tooltipWindow = None       #pyui2.widgets.TooltipWindow(5,50,100,100)
+
+        #self.theme.setArrowCursor()
+
+
+    def setDeviceContext(self, deviceName):
+        self.presenter.determineDeviceContext(deviceName)
+
+
+    def quit(self):
+        """Called to destroy all the windows on shutdown.
+        """
+        #use a copy of list as we delete from it.
+        ww = copy.copy(self.windows)
+        for w in ww:
+            w.destroy()
+        self.running = 0
+        self.windows = []
+        self.allWidgets = {}
+        del Desktop.theDesktop
+        Desktop.theDesktop = None
+        self.presenter.getDeviceContext().quit()
+
+
+    def update(self):
+        """Called constantly by the application to process pyui2 events and scheduled callbacks.
+        """
+        self.presenter.getDeviceContext().update()
+        # process user events
+        while self.running and self.userEvents:
+            e = self.userEvents.pop(0)
+
+            self.handleEvent(e)
+            if not self.running:
+                return self.running
+        # process timer callbacks
+        timer = self.presenter.getDeviceContext().readTimer()
+        for callback in self.callbacks.keys():
+            if callback.process(timer):
+                del self.callbacks[callback]
+        return self.running
+
+    def draw(self):
+        """Called to draw the widgets!
+        """
+        self.presenter.present(self.windows)
+
+    def findWidget(self, id):
+        """Lookup a widget by its ID.
+        (public)
+        """
+        return self.allWidgets.get(id, None)
+
+    def registerWidget(self, widget):
+        """Register a top level window instance with the system.
+        (internal)
+        """
+        widget.id = self.nextID()
+        self.allWidgets[widget.id] = widget
+
+    def removeWidget(self, widget):
+        """Remove a widget previously registered from the top-level widget list
+        """
+        self.windows.remove(widget)
+
+    def destroyWidget(self, widget):
+        """cleanup all global references to widget. This is called by destroy() so don't call it twice.
+        (internal)
+        """
+        if self.allWidgets.has_key(widget.id):
+            del self.allWidgets[widget.id]
+        for w in self.windows:
+            if w.id == widget.id:
+                self.windows.remove(w)
+                break
+
+        if self.focusWidget == widget:
+            self.focusWidget = None
+
+    def activateWindow(self, window):
+        """This adds a window to the set of windows and puts it on top, except
+        for any topMost widgets on the screen.
+        """
+        if window == None:
+            return
+
+        i = 0
+        try:
+            self.windows.remove(window)
+        except ValueError:
+            pass
+
+        if not window.topMost:
+            for w in self.windows:
+                if not w.topMost:
+                    break
+                i += 1
+
+        self.windows.insert(i, window)
+
+    def addWindow(self, window):
+        """This adds a window to the set of windows and puts it on top, except
+        for any topMost widgets on the screen.
+        """
+        self.activateWindow(window)
+        self.focusWidget = None
+
+    def registerHandler(self, eventType, handler):
+        """Register a user defined handler for an event.
+        (public)
+        """
+        self.userHandlers[eventType] = handler
+
+    def unregisterHandler(self, eventType):
+        """Un-Register a user defined handler for an event.
+        (public)
+        """
+        del self.userHandlers[eventType]
+
+    def readTimer(self):
+        return self.presenter.getDeviceContext().readTimer()
+
+    def handleEvent(self, event):
+        """Process all outstanding events.
+        (private)
+        """
+        if event.type != 0:
+            if event.type == pyui2.locals.QUIT:
+                self.quit()
+                print "DONE!"
+                return
+
+            # pre-handle mouse move events here for tool tips
+            if event.type == pyui2.locals.MOUSEMOVE:
+                self.updateToolTips(event)
+
+            # events go to the focus branch first
+            w = self.focusWidget
+            while w != None:
+                if w.handleEvent(event):
+                    return
+                if w == self.modal:
+                    break
+                w = w.parent
+
+            if self.modal and ((event.type & pyui2.locals.EVENT_MASK) == pyui2.locals.EVENT_MOUSE or
+                           (event.type & pyui2.locals.EVENT_MASK) == pyui2.locals.EVENT_KEYBOARD):
+                return self.updateModal(event)
+
+            # pass events to all widgets
+            for w in self.windows:
+                if w.handleEvent(event):
+                    return
+
+            # check for application handlers
+            if self.userHandlers.has_key(event.type):
+                if self.userHandlers[event.type](event) == 1:
+                    return
+
+
+    def updateToolTips(self, event):
+        ##NOTE: turned off for now...
+        return
+        # find the widget under the mouse
+        found = None
+        for w in self.windows:
+            if w == self.tooltipWindow:
+                continue
+            found = w.checkHit(event.pos)
+            if found:
+                break
+        #print "Mouse Move. found = ", found
+        # cancel current tooltip callback
+        if self.tooltipCallback:
+            self.removeCallback(self.tooltipCallback)
+            self.tooltipCallback = None
+        # disable current tooltip window
+        if self.tooltipWindow:
+            self.tooltipWindow.setShow(0)
+        if found:
+            # setup a callback to enable the tooltip after a delay
+            self.tooltipCallback = addCallback(self.enableTooltip, 0.333)
+            self.tooltipFocus = found
+            self.tooltipPosition = event.pos
+        else:
+            self.tooltipFocus = None
+
+    def updateModal(self, event):
+        """in modal mode, only the modal dialog gets mouse events
+        """
+        if self.modal.handleEvent(event):
+            return
+        if event.type == pyui2.locals.KEYDOWN and event.key == pyui2.locals.K_ESCAPE:
+            self.modal.setShow(0)
+            self.modal = None
+        return
+
+    def setModal(self, window):
+        """Sets the modal window.
+        (internal)
+        """
+        self.modal = window
+        if window:
+            window.getFocus()
+
+    def getModal(self):
+        return self.modal
+
+    def setWaiting(self, value):
+        self.waiting = value
+        if value:
+            self.theme.setWaitCursor()
+        else:
+            self.theme.setArrowCursor()
+
+    def setFocus(self, widget):
+        """Set the focus to this widget.
+        """
+        if not widget:
+            self.focusWidgets = None
+            return
+        if self.focusWidget and self.focusWidget != widget:
+            self.focusWidget.loseFocus()
+        self.focusWidget = widget
+
+    def getFocus(self):
+        """return the current focused widget.
+        """
+        return self.focusWidget
+
+    def getTheme(self):
+        """return the global theme object"""
+        return self.theme
+
+    def setTheme(self, theme):
+        """sets the global theme object
+        """
+        self.theme = theme
+
+    def postUserEvent(self, type, x = 0, y = 0, key = 0, mods = 0):
+        """Post a user event into the system. This comes from a non-widget object
+        (public)
+        """
+        newEvent = guiEvent(type, 0, x, y, key, mods)
+        self.userEvents.append(newEvent)
+        return newEvent
+
+    def postEvent(self, type, id = 0):
+        """Post an event object into the system. Comes from a widget object.
+        (internal)
+        """
+        newEvent = guiEvent(type, id, 0, 0)
+        self.userEvents.append(newEvent)
+        return newEvent
+
+    def addCallback(self, method, interval = 0):
+        callback = guiCallback(method, interval, self.presenter.readTimer() )
+        self.callbacks[callback] = None
+        return callback
+
+    def removeCallback(self, callback):
+        if self.callbacks.has_key(callback):
+            del self.callbacks[callback]
+
+    def findWindowByHandle(self, handle):
+        for w in self.windows:
+            if w.handle == handle:
+                return w
+        return None
+
+    def enableTooltip(self, interval):
+        result = self.ooltipFocus.getToolTipInfo(self.tooltipPosition)
+        self.removeCallback(self.tooltipCallback)
+        self.tooltipCallback = None
+        #print "Enabling tool tip", result, gTooltipWindow
+        if result:
+            (text, rect) = result
+            self.tooltipWindow.activate(text, rect)
+            self.activateWindow(self.tooltipWindow)
+
+    def setMustFill(self):
+        """tell the UI the screen must be cleared. if this is not set, only dirt rects get updated.
+        Setting this means that all the windows will redraw.
+        """
+        self.presenter.setMustFill()
+
+    def getMustFill(self):
+        return self.presenter.mustFill
+
+    def dirtyCollidingWindows(self, inRect):
+        """If a dirty rect collides with any other rects, they should be dirty also. This recurses
+        so that all colliding rects get dirtied. the second parameter to setDirty() prevents infinite
+        recursion.
+        """
+        self.presenter.dirtyCollidingWindows(inRect)
+
+    def nextID(self):
+        self.lastID = self.lastID + 1
+        return self.lastID
+
+    def getSize(self):
+        return (self.width, self.height)
+
+
+def getUserEvent():
+    """Request an event ID in the user event space.
+    NOTE: this is not part of the desktop as it can be called before the desktop object
+    is created.
+    (public)
+    """
+    e = pyui2.locals.USEREVENT + Desktop.lastEvent
+    Desktop.lastEvent = Desktop.lastEvent + 1
+    return e

dialogs/__init__.py

+# pyui2
+# Copyright (C) 2005 John Judd
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+__all__ = [
+			"console"
+			"colordialog"
+			"dialog",
+			"filedialog"
+			"stddialog"
+			]
+
+
+from console import Console
+from colordialog import ColorDialog
+from dialog import Dialog
+from filedialog import FileDialog
+from stddialog import StdDialog
+

dialogs/colordialog.py

+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import sys
+import re
+import os, stat
+import string
+
+import pyui2
+from pyui2 import locals
+from pyui2.desktop import getDesktop, getTheme
+from dialog import Dialog
+
+
+EVENT_OUTPUT = pyui2.desktop.getUserEvent()
+
+    
+class ColorDialog(Dialog):
+    """Allows the user to select a color.
+    """
+    def __init__(self, callback, r=255, g=0, b=0):
+        self.callback = callback
+        self.r = r
+        self.g = g
+        self.b = b
+        
+        Dialog.__init__(self, -1, -1, 400,240, "Color Dialog")
+        self.setLayout(pyui2.layouts.TableLayoutManager(4,9))
+
+        self.colorGradient = ColorGradient(None, self)
+        self.colorStrip = ColorStrip(None, self)
+        self.colorSolid = ColorSolid(None)
+
+        self.okButton = pyui2.widgets.Button("OK", self._pyui2OK)
+        self.cancelButton = pyui2.widgets.Button("Cancel", self._pyui2Cancel)
+        
+        self.redLabel = pyui2.widgets.Label("Red:")
+        self.greenLabel = pyui2.widgets.Label("Green:")
+        self.blueLabel = pyui2.widgets.Label("Blue:")
+
+        self.redBox = pyui2.widgets.SliderBar(self._pyui2Red, 255, r)
+        self.greenBox = pyui2.widgets.SliderBar(self._pyui2Green, 255, g)
+        self.blueBox = pyui2.widgets.SliderBar(self._pyui2Blue, 255, b)
+
+        self.addChild( self.colorGradient,  (0,0,2,5) )
+        self.addChild( self.colorStrip,     (2,0,2,5) )
+        self.addChild( self.colorSolid,     (0,5,1,3) )
+        self.addChild( self.redLabel,       (1,5,1,1) )
+        self.addChild( self.greenLabel,     (1,6,1,1) )
+        self.addChild( self.blueLabel,      (1,7,1,1) )        
+        self.addChild( self.redBox,         (2,5,2,1) )
+        self.addChild( self.greenBox,       (2,6,2,1) )
+        self.addChild( self.blueBox,        (2,7,2,1) )        
+        self.addChild( self.cancelButton,   (0,8,2,1) )
+        self.addChild( self.okButton,       (2,8,2,1) )        
+
+        self.pack()
+        self.setColor()
+        
+    def setColor(self, strip=1):
+        self.color = (self.r,self.g,self.b, 255)
+        self.colorSolid.color = self.color
+        if strip:
+            self.colorStrip.color = self.color
+        self.setDirty(1)
+        
+    def _pyui2OK(self, button):
+        self.callback(self.color)
+        self.close(1)
+
+    def _pyui2Cancel(self, button):
+        self.close(0)
+
+    def _pyui2Red(self, value):
+        self.r = value
+        self.setColor()
+
+    def _pyui2Green(self, value):
+        self.g = value
+        self.setColor()
+        pass
+
+    def _pyui2Blue(self, value):
+        self.b = value
+        self.setColor()
+
+    def setRGB(self, r, g, b,strip=1):
+        self.r = r
+        self.g = g
+        self.b = b
+        self.redBox.setValue(r)
+        self.greenBox.setValue(g)
+        self.blueBox.setValue(b)        
+        self.setColor(strip)
+    
+class ColorSolid(pyui2.widgets.Base):
+    def __init__(self, color):
+        pyui2.widgets.Base.__init__(self)
+        self.color = color
+
+#    def draw(self, renderer):
+#        renderer.drawRect(pyui2.colors.black, self.windowRect)
+#        renderer.drawRect(self.color, (self.windowRect[0]+2,self.windowRect[1]+2,self.windowRect[2]-4,self.windowRect[3]-4) )
+
+class ColorGradient(pyui2.widgets.Base):
+
+    colors = [
+        (255,0,0),
+        (255,255,0),
+        (0,255,0),
+        (0,255,255),
+        (0,0,255),
+        (255,0,255),
+        (255,0,0)
+        ]
+
+    segments = 6
+    
+    def __init__(self, color, dialog):
+        pyui2.widgets.Base.__init__(self)
+        self.color = color
+        self.dialog = dialog
+        self.registerEvent(pyui2.locals.LMOUSEBUTTONDOWN, self._pyui2MouseDown)         
+
+#    def draw(self, renderer):
+#        renderer.drawRect(pyui2.colors.black, self.windowRect)
+#        top = self.windowRect[1]+2
+#        height = self.windowRect[3]-4
+#        width = self.windowRect[2] / float(self.segments)
+#        for i in range(0,self.segments):
+#            renderer.drawGradient( (self.windowRect[0]+int(i*width),top,int(width+1),height),
+#                                   apply(renderer.packColor,self.colors[i]),
+#                                  apply(renderer.packColor,self.colors[i+1]),
+#                                  apply(renderer.packColor,self.colors[i]),
+#                                  apply(renderer.packColor,self.colors[i+1]))
+#
+
+    def _pyui2MouseDown(self, event):
+        if not self.hit(event.pos):
+            return 0
+        x = event.pos[0] - self.rect[0]
+        ratio = float(x) / float(self.windowRect[2])
+        hitSegment = int(ratio * self.segments)
+        before = self.colors[hitSegment]
+        after = self.colors[hitSegment+1]
+        innerRatio = (ratio * self.segments) - hitSegment
+        
+        newColor = []
+        for i in range(0,3):
+            diff = after[i] - before[i]
+            value = before[i] + innerRatio*diff
+            newColor.append(int(value))
+        apply(self.dialog.setRGB, newColor)
+        return 1
+    
+class ColorStrip(pyui2.widgets.Base):
+    def __init__(self, color, dialog):
+        pyui2.widgets.Base.__init__(self)
+        self.color = color
+        self.dialog = dialog
+        self.registerEvent(pyui2.locals.LMOUSEBUTTONDOWN, self._pyui2MouseDown)
+        
+#    def draw(self, renderer):
+#        renderer.drawRect(pyui2.colors.black, self.windowRect)
+#        top = self.windowRect[1]
+#
+#        w = self.windowRect[2]-4
+#        h = self.windowRect[3]
+#        rect1 =(self.windowRect[0]+2,self.windowRect[1], w, h/2)
+#        rect2 =(self.windowRect[0]+2,self.windowRect[1]+(h/2), w, h/2)
+#       renderer.drawGradient(rect1, pyui2.colors.white, pyui2.colors.white, self.color, self.color )
+#       renderer.drawGradient(rect2,self.color, self.color, pyui2.colors.black, pyui2.colors.black )        
+#       
+    def _pyui2MouseDown(self, event):
+        if not self.hit(event.pos):
+            return 0
+        y = event.pos[1] - self.rect[1]
+        ratio = 2 - (float(y) / float(self.windowRect[3]))*2
+
+        newColor = []
+        for i in range(0,3):
+            if ratio <= 1.0:
+                value = self.color[i] * ratio
+            else:
+                if self.color[i] == 0:
+                    value = 255 * (ratio/2)
+                elif self.color[i] == 255:
+                    value = 255
+                else:
+                    value = self.color[i] + (255 - self.color[i]) * (ratio/2)
+            newColor.append(int(value))
+        newColor.append(0) # this make setRGB not reset self.color
+        apply(self.dialog.setRGB, newColor)
+        return 1

dialogs/console.py

+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import sys
+import re
+import os, stat
+import string
+
+import pyui2
+from pyui2 import locals
+from pyui2.desktop import getDesktop, getTheme
+
+EVENT_OUTPUT = pyui2.desktop.getUserEvent()
+
+class ConsoleOutput:
+    def __init__(self):
+        self.lines = []
+        self.oldout = sys.stdout
+        self.olderr = sys.stderr
+        
+    def write(self, text):
+        self.oldout.write("%s" % text)
+        text = string.rstrip(text)
+        if len(text) < 1:
+            return
+        text = string.replace(text, "\n", " ")
+        text = string.replace(text, "\r", " ")      
+        self.lines.append(text)
+        getDesktop().postUserEvent(EVENT_OUTPUT)
+
+    def beginCapture(self):
+        sys.stdout = self
+        sys.stderr = self
+
+    def endCapture(self):
+        sys.stdout = self.oldout
+        self.stderr = self.olderr
+
+    def getLines(self):
+        return self.lines
+    
+    def clear(self):
+        self.lines = []
+
+    def __del__(self):
+        self.endCapture()
+        
+#####################
+# edit box with history
+#####################
+class ConsoleEdit(pyui2.widgets.Edit):
+    def __init__(self, text, max, execCallback):
+        pyui2.widgets.Edit.__init__(self, text, max, self._pyui2Enter)
+        self.history = []
+        self.historyPos = 0
+        self.execCallback = execCallback
+        self.registerEvent(locals.KEYDOWN, self._pyui2KeyDown)
+
+    def _pyui2Enter(self, object):
+        self.execCallback(self.text)
+        self.history.append(self.text)
+        self.historyPos = len(self.history) - 1
+        self.setText("")
+        self.setDirty()
+        return 1
+
+    def _pyui2KeyDown(self, event):
+        if not self.hasFocus():
+            return 0
+
+        if event.key == locals.K_UP:
+            if self.history:
+                self.historyPos = (self.historyPos - 1) % len(self.history)
+                self.setText(self.history[self.historyPos])
+            return 1
+
+        if event.key == locals.K_DOWN:
+            if self.history:
+                self.historyPos = (self.historyPos + 1) % len(self.history)
+                self.setText(self.history[self.historyPos])
+            return 1
+                
+        return pyui2.widgets.Edit._pyui2KeyDown(self, event)
+    
+#####################
+# box to display lines of text for console output
+# or chat windows...
+####################
+class LineDisplay(pyui2.widgets.Base):
+    def __init__(self):
+        pyui2.widgets.Base.__init__(self)
+        #self.bgColor = (0,0,49, 255)
+        self.lines = []
+        self.displayLines = []
+        font = getTheme().getProperty("DEFAULT FONT")
+        self.numVisible = self.height / font.getTextSize("x")[1]
+        self.numItems = 0
+        self.topItem = 0
+        self.rewrap = 0
+        self.textWidth = 0
+        self.vscroll = pyui2.widgets.VScroll()
+        self.addChild(self.vscroll)
+        self.registerEvent(locals.SCROLLPOS, self._pyui2ScrollPos)
+
+    def clear(self):
+        self.lines = []
+        self.displayLines = []
+        font = getTheme().getProperty("DEFAULT FONT")
+        self.numVisible = self.height / font.getTextSize("x")[1]
+        self.numItems = 0
+        self.topItem = 0
+        self.rewrap = 0
+
+    def rewrapAll(self):
+        self.displayLines = []
+        for (line, color) in self.lines:
+            self.wrapLine(line, color)
+        numLines = len(self.displayLines)
+        self.topItem = numLines - self.numVisible
+        self.vscroll.setNumItems(numLines, self.numVisible)
+        self.vscroll.scrollToItem(self.topItem)
+
+    def wrapLine(self, line, color):
+        """Add a line of text to the display lines list with wrapping."""
+        (words, spaces) = self.splitLine(line)
+
+        displayLine = ""
+        width = 0
+        space = ""
+        spaceWidth = 0
+        while words:
+            word = words.pop(0)
+            font = getTheme().getProperty("DEFAULT FONT")
+            wordWidth = font.getTextSize(word)[0]
+
+            if width + spaceWidth + wordWidth <= self.textWidth:
+                displayLine = displayLine + space + word
+                width += spaceWidth + wordWidth
+            else:
+                self.addDisplayLine(displayLine, color)
+                displayLine = word
+                width = wordWidth
+            space = spaces.pop(0)
+            font = getTheme().getProperty("DEFAULT FONT")
+            spaceWidth = font.getTextSize(space)[0]
+                
+        if displayLine:
+            self.addDisplayLine(displayLine, color)
+
+    def splitLine(self, line):
+        """Works like split(), but also returns whitespace between words"""
+        words = []
+        spaces = []
+        nEnd = 0
+        while nEnd < len(line):
+            nStart = nEnd
+            while nEnd < len(line) and not line[nEnd].isspace():
+                nEnd += 1
+            words.append(line[nStart:nEnd])
+                
+            nStart = nEnd
+            nEnd += 1
+            while nEnd < len(line) and line[nEnd].isspace():
+                nEnd += 1
+            spaces.append(line[nStart:nEnd])
+
+        return (words,spaces)
+
+    def addLine(self, line, color = None):
+        """This adds lines to the display. it does text wrapping."""
+        if not color:
+            color = getTheme().fgColor
+            
+        self.lines.append((line, color))
+        self.wrapLine(line, color)
+        numLines = len(self.displayLines)
+        self.topItem = numLines - self.numVisible
+        self.vscroll.setNumItems(numLines, self.numVisible)
+        self.vscroll.scrollToItem(self.topItem)
+
+    def addDisplayLine(self, displayLine, color):
+        self.displayLines.append((displayLine, color))
+        self.setDirty()
+
+#    def draw(self, renderer):
+#        #renderer.drawRect(getTheme().bgColor2, self.windowRect)
+#        if self.rewrap:
+#            self.rewrapAll()
+#            self.rewrap = 0
+#            
+#        i = 0
+#        font = getTheme().getProperty("DEFAULT FONT")
+#        h = font.getTextSize("x")[1]
+#        for (line, color) in self.displayLines:
+#            if i >= self.topItem and i < (self.topItem + self.numVisible):
+#                renderer.drawText(line, (self.windowRect[0]+2, self.windowRect[1]+2+((i-self.topItem)*h)), color )
+#            i += 1
+#        self.vscroll.draw(renderer)
+#        self.clearDirty()
+#        
+    def _pyui2ScrollPos(self, event):
+        if event.id == self.vscroll.id:
+            self.topItem = event.pos
+            self.setDirty(1)
+
+    def resize(self, w, h):
+        pyui2.widgets.Base.resize(self, w,h)
+        self.vscroll.resize(getTheme().getScrollerSize(), h)
+        self.vscroll.moveto(w-getTheme().getScrollerSize(), 0)
+        self.textWidth = self.width - self.vscroll.width
+        font = getTheme().getProperty("DEFAULT FONT")
+        self.numVisible = self.height / font.getTextSize("x")[1]
+        self.rewrap = 1
+
+    def destroy(self):
+        self.vscroll.destroy()
+        self.vscroll = None
+        pyui2.widgets.Base.destroy(self)
+
+####################################
+# python console window
+class Console(pyui2.widgets.Frame):
+
+    def __init__(self, x, y, w, h, callback = None):
+        pyui2.widgets.Frame.__init__(self, x, y, w, h, "Console")
+        self.setLayout(pyui2.layouts.BorderLayoutManager())
+        self.output = ConsoleOutput()
+        self.locals = {}
+        if not callback:
+            callback = self._pyui2Go
+            
+        # create gui objects
+        self.inputBox = ConsoleEdit("", 80, callback)
+        self.goButton = pyui2.widgets.Button("Go", self._pyui2Go)
+        self.outputBox = LineDisplay()
+
+        self.panel = pyui2.widgets.Panel()
+        self.panel.setLayout(pyui2.layouts.BorderLayoutManager())
+        self.panel.addChild(self.inputBox, locals.CENTER)
+        self.panel.addChild(self.goButton,locals.EAST)
+
+        self.addChild(self.outputBox, locals.CENTER)
+        self.addChild(self.panel, locals.SOUTH)
+        self.panel.setWindow(self)
+        self.pack()
+
+        self.registerEvent(EVENT_OUTPUT, self._pyui2Output)
+
+    def _pyui2Output(self, event):
+        if not self.output:
+            return 1
+        lines = self.output.getLines()
+        for l in lines:
+            self.outputBox.addLine(l)
+        self.output.clear()
+        self.setDirty()
+        return 1
+
+    def _pyui2Go(self, command):
+        self.output.beginCapture()
+        try:
+            print ">%s" % command
+            exec command in globals(), self.locals
+        except:
+            print "Exception on command '%s':" % command
+            print ">    %s" % sys.exc_value
+        self.output.endCapture()
+        return 1
+
+    def destroy(self):
+        #sys.stdout = None
+        self.inputBox = None
+        self.goButton = None
+        self.outputBox = None
+        self.panel = None
+        pyui2.widgets.Frame.destroy(self)
+        self.output = None
+

dialogs/dialog.py

+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import sys
+import re
+import os, stat
+import string
+
+import pyui2
+from pyui2 import locals
+from pyui2.desktop import getDesktop, getTheme
+
+
+EVENT_OUTPUT = pyui2.desktop.getUserEvent()
+
+
+##################################
+# Dialog
+#
+##################################
+class Dialog(pyui2.widgets.Frame):
+    def __init__(self, x = -1, y = -1, w = 300, h = 200, title = None):
+        # center if position not specified
+        if x < 0:
+            x = (getDesktop().width - w) / 2
+        if y < 0:
+            y = (getDesktop().height - h) / 2
+        pyui2.widgets.Frame.__init__(self, x, y, w, h, title)
+        self.modal = -1   # this is set to the return value of the dialog
+        self.setShow(1)
+        self.setDirty()
+        self.cb = None
+        
+    def doModal(self, cb = None):
+        self.setShow(1)
+        self.cb = cb
+        getDesktop().setModal(self)
+
+    def close(self, value = 1):
+        #print "closed - " , value
+        self.modal = value
+        getDesktop().setModal(None)
+        self.setShow(0)
+        self.loseFocus()
+        self.postEvent(locals.DIALOGCLOSED)
+        pyui2.desktop.getTheme().setArrowCursor()
+        if self.cb:
+            self.cb(value)
+
+    def destroy(self):
+        if getDesktop().getModal() == self:
+            getDesktop().setModal(None)
+        pyui2.widgets.Frame.destroy(self)
+

dialogs/filedialog.py

+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import sys
+import re
+import os, stat
+import string
+
+import pyui2
+from pyui2 import locals
+from pyui2.desktop import getDesktop, getTheme
+from dialog import Dialog
+
+
+EVENT_OUTPUT = pyui2.desktop.getUserEvent()
+
+
+class FileDialog(Dialog):
+    """A dialog to allow the user to select a file. Allows regex wildcard filters
+    and calls the "callback" method when done.
+
+    Always use the forward slash '/' to separate paths, not the other slash '\'.
+
+    The filter matches using python regular expressions. Note that the wildcard for
+    matching anything is ".*" not just "*" as the asterisk is a "repeating character"
+    modifier in the regular expression language...
+    """
+    def __init__(self, startDir, callback, filter = ".*"):
+        currentDir = startDir.replace('\\','/')
+        self.callback = callback
+        self.filter = filter
+        Dialog.__init__(self, -1, -1, 400,240, "File Dialog")
+        self.setLayout(pyui2.layouts.TableLayoutManager(6,8))
+        
+        self.dirLabel = pyui2.widgets.Label("Directory:")
+        self.fileLabel = pyui2.widgets.Label("Filename:")
+        self.filterLabel = pyui2.widgets.Label("Filter:")
+
+        self.dirBox = pyui2.widgets.Label(currentDir)
+        self.filesBox = pyui2.widgets.ListBox(self._pyui2Selected, self._pyui2DoubleClicked)
+        self.nameBox = pyui2.widgets.Label("")
+        self.filterBox = pyui2.widgets.Edit(self.filter,10,self._pyui2Filter)
+
+        self.dirButton = pyui2.widgets.Button("Up", self._pyui2Up)
+        self.openButton = pyui2.widgets.Button("Open", self._pyui2Open)
+        self.closeButton = pyui2.widgets.Button("Close", self._pyui2Close)
+
+        self.addChild( self.dirLabel,    (0,0,2,1) )
+        self.addChild( self.fileLabel,   (0,6,2,1) )
+        self.addChild( self.filterLabel, (0,7,2,1) )
+        self.addChild( self.dirBox,      (2,0,3,1) )
+        self.addChild( self.filesBox,    (0,1,6,5) )
+        self.addChild( self.nameBox,     (2,6,3,1) )
+        self.addChild( self.filterBox,   (2,7,3,1) )
+        self.addChild( self.dirButton,   (5,0,1,1) )
+        self.addChild( self.openButton,  (5,6,1,1) )
+        self.addChild( self.closeButton, (5,7,1,1) )        
+
+        self.pack()
+        self.setCurrentDir(currentDir)
+
+    def setCurrentDir(self, newDir):
+        """This will fail if newDir is not a valid directory.
+        """
+        try:
+            info = os.stat(newDir)
+            isdir = stat.S_ISDIR(info[stat.ST_MODE])
+        except OSError, e:
+            print "Invalid Dir:", newDir
+            return None
+        if isdir:
+            self.currentDir = newDir
+            return self.populateDir()
+        return None
+            
+    def populateDir(self):
+        """Load the current directory. Load directories first, then all
+        the files.
+        """
+        self.filesBox.clear()
+        self.dirBox.setText(self.currentDir)
+        self.nameBox.setText("")
+        all = os.listdir(self.currentDir+"/")
+        files = []
+        for filename in all:
+            info = os.stat(self.currentDir+"/"+filename)
+            isdir = stat.S_ISDIR(info[stat.ST_MODE])
+            if isdir:
+                self.filesBox.addItem(filename, 1, pyui2.colors.blue)
+            else:
+                files.append(filename)
+        for filename in files:
+            if re.search(self.filter, filename):
+                self.filesBox.addItem(filename, 0, pyui2.colors.black)
+
+    def _pyui2Filter(self, filter):
+        self.filter = filter.text
+        self.populateDir()
+        self.filterBox.setText(self.filter)
+        return 1
+    
+    def _pyui2Up(self, button):
+        path = self.currentDir.split("/")[:-1]
+        self.setCurrentDir( string.join(path, "/") )
+
+    def _pyui2Selected(self, item):
+        if not item:
+            self.nameBox.setText("")
+        else:
+            self.nameBox.setText(item.name)
+        return 1
+
+    def _pyui2DoubleClicked(self, item):
+        if not item:
+            self.nameBox.setText("")
+        else:
+            self.nameBox.setText(item.name)
+            self._pyui2Open(None)
+        return 1
+        
+    def _pyui2Open(self, button):
+        """Open a file or a directory
+        """
+        if len(self.nameBox.text) == 0:
+            return 0
+        fullpath = self.currentDir+"/"+self.nameBox.text
+        info = os.stat(fullpath)
+        isdir = stat.S_ISDIR(info[stat.ST_MODE])
+        if isdir:
+            self.setCurrentDir( fullpath )
+        else:
+            self.close(1)
+            self.callback(fullpath)
+        return 1
+
+    def _pyui2Close(self, button):
+        self.close(0)
+

dialogs/stddialog.py

+# pyui2
+# Copyright (C) 2001-2002 Sean C. Riley
+# 
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import sys
+import re
+import os, stat
+import string
+
+import pyui2
+from pyui2 import locals
+from pyui2.desktop import getDesktop, getTheme
+from dialog import Dialog
+
+
+EVENT_OUTPUT = pyui2.desktop.getUserEvent()
+
+
+class StdDialog(Dialog):
+    def __init__(self, title, text):
+
+        font = getTheme().getProperty("DEFAULT FONT")
+        size = font.getTextSize(title)
+        Dialog.__init__(self, title = title)
+        self.setLayout(pyui2.layouts.BorderLayoutManager())
+
+        self.textLabel = pyui2.widgets.Label(text)
+        self.textLabel.setText(text)
+        self.buttonPanel = pyui2.widgets.Panel()
+        self.buttonPanel.setLayout(pyui2.layouts.BorderLayoutManager())
+        self.okButton = pyui2.widgets.Button("OK", self._pyui2OK)
+        self.okButton.resize(self.innerRect[2]/2, self.okButton.height)
+        self.cancelButton = pyui2.widgets.Button("Cancel", self._pyui2Cancel)
+        self.cancelButton.resize(self.innerRect[2]/2, self.cancelButton.height)     
+        self.buttonPanel.addChild(self.okButton, locals.WEST)
+        self.buttonPanel.addChild(self.cancelButton, locals.EAST)
+        self.buttonPanel.pack()
+        
+        self.addChild(self.textLabel, locals.CENTER)
+        self.addChild(self.buttonPanel, locals.SOUTH)
+
+        self.pack()
+
+    def _pyui2OK(self, button):
+        self.close(1)
+
+    def _pyui2Cancel(self, button):
+        self.close(0)
+
+    def draw(self, renderer):
+        #print "drawing!!!"
+        return Dialog.draw(self, renderer)
+
+    def destroy(self):
+        self.buttonPanel = None
+        self.okButton = None
+        self.cancelButton = None
+        self.textLabel = None
+        Dialog.destroy(self)
+

docs/demos/mitlicence.txt

+The MIT License
+
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

docs/demos/themeswitcher.py

+###################################################################################
+# Copyright (c) 2005 John Judd
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+###################################################################################
+
+import pyui2
+from optparse import OptionParser
+
+from pyui2.desktop import getDesktop, getTheme
+
+from pyui2.themes import Theme
+from pyui2.themes import Win2kTheme
+from pyui2.themes import WinXPTheme
+from pyui2.themes import OSXTheme
+from pyui2.themes import ComicTheme
+
+
+
+class ThemeSwitcher:
+
+    frame = None
+
+
+    def init(self, sx, sy, deviceName):
+        pyui2.init(sx, sy, deviceName, 0, "Test Window")
+
+        self.themes = { "Standard" : Theme,
+                        "Windows 2000" : Win2kTheme,
+                        "Windows XP" : WinXPTheme,
+                        "Mac OSX" : OSXTheme,
+                        "Comic" : ComicTheme,
+                      }
+
+        self.themeTitle = "Standard Theme"
+
+
+    def setCurrentTheme(self, themeName):
+        self.currentTheme = themeName
+
+        SelectedTheme = self.themes[themeName]
+
+        pyui2.desktop.setTheme(SelectedTheme())
+
+        self.themeTitle = themeName + " Theme"
+
+        if self.frame:
+            self.frame.setTitle(self.themeTitle)
+
+
+    def onThemeChange(self, menuitem):
+        self.setCurrentTheme(menuitem.text)
+
+
+    def onOpenTabs(self, arg):
+        self.tabbedFrame = pyui2.widgets.Frame(150, 150, 250, 200, "Tabbed Frame")
+
+        self.tabPanel = pyui2.widgets.TabbedPanel()
+
+        for title in ("Tab 1", "Tab 2", "Tab 3"):
+            self.tabPanel.addPanel(title)
+
+        self.tabbedFrame.replacePanel(self.tabPanel)
+
+        self.tabPanel.getPanel(0).setLayout(pyui2.layouts.GridLayoutManager(2,4))
+        self.tabPanel.getPanel(1).setLayout(pyui2.layouts.GridLayoutManager(2,4))
+        self.tabPanel.getPanel(2).setLayout(pyui2.layouts.GridLayoutManager(2,4))
+
+        self.tabbedFrame.pack()
+
+    def onCheckbox(self, data):
+        pass
+        #print "Checkbox clicked with data = ", data
+
+    def run(self):
+        parser = OptionParser()
+        parser.add_option("-D", action="store", type="string", dest="deviceName", default="2d")
+        (options, args) = parser.parse_args()
+        #print options.deviceName
+
+        self.init(800, 600, options.deviceName)
+
+        menu1 = pyui2.widgets.Menu("Themes")
+        for item in self.themes:
+            menu1.addItem(item, self.onThemeChange)
+
+        self.mbar = pyui2.widgets.MenuBar()
+        self.mbar.addMenu(menu1)
+
+        self.frame = pyui2.widgets.Frame(40, 40, 720, 520, self.themeTitle)
+        self.frame.setLayout(pyui2.layouts.TableLayoutManager(21, 21))
+
+        btn = pyui2.widgets.Button("Open Tabs", self.onOpenTabs)
+
+        lb_items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Item 9"]
+        lb = pyui2.widgets.ListBox()
+        lb.populateList(lb_items)
+
+        dd_items = [("Item 1", None), ("Item 2", None), ("Item 3", None), ("Item 4", None), ("Item 5", None)]
+        dd = pyui2.widgets.DropDownBox(3, None, dd_items)
+
+        pic = pyui2.widgets.Picture("../../images/cursor_drag.png")
+        ib1 = pyui2.widgets.ImageButton("../../images/cursor_wait.png", None)
+        ib2 = pyui2.widgets.ImageButton("../../images/cursor_hand.png", None)
+        ib3 = pyui2.widgets.ImageButton("../../images/cursor_resize.png", None)
+
+        lbl1 = pyui2.widgets.Label("Label 1", None, None, 0)
+        lbl2 = pyui2.widgets.Label("Label 2", None, None, 1)
+
+        cb1 = pyui2.widgets.CheckBox("CheckBox 1", self.onCheckbox)
+        cb2 = pyui2.widgets.CheckBox("CheckBox 2", None)
+        cb2.setID("Happy Fun Time")
+        cb3 = pyui2.widgets.CheckBox("CheckBox 3", None, 1)
+
+        READWRITE = 0
+        READONLY = 1
+
+        eb1 = pyui2.widgets.Edit("Edit 1", 5, None, READONLY)
+        eb2 = pyui2.widgets.Edit("some text for this edit", 10, None, READWRITE)
+        eb3 = pyui2.widgets.Edit("Another lot of text", 7, None)
+
+        slb = pyui2.widgets.SliderBar(None, 6, 3)
+
+        self.frame.addChild(btn, (10, 20, 3, 1))
+        self.frame.addChild(pic, (1, 1, 2, 2))
+        self.frame.addChild(ib1, (3, 1, 2, 2))
+        self.frame.addChild(ib2, (5, 1, 2, 2))
+        self.frame.addChild(ib3, (7, 1, 2, 2))
+        self.frame.addChild(lbl1, (1, 4, 3, 1))
+        self.frame.addChild(lbl2, (4, 4, 3, 1))
+        self.frame.addChild(cb1, (1, 6, 3, 1))
+        self.frame.addChild(cb2, (1, 7, 3, 1))
+        self.frame.addChild(cb3, (1, 8, 3, 1))
+        self.frame.addChild(eb1, (5, 6, 2, 1))
+        self.frame.addChild(eb2, (5, 7, 2, 1))
+        self.frame.addChild(eb3, (5, 8, 2, 1))
+        self.frame.addChild(dd, (11, 1, 4, 1))
+        self.frame.addChild(lb, (15, 1, 5, 5))