]> git.saurik.com Git - wxWidgets.git/commitdiff
Forward port recent changes on the 2.8 branch to HEAD
authorRobin Dunn <robin@alldunn.com>
Wed, 16 May 2007 23:39:42 +0000 (23:39 +0000)
committerRobin Dunn <robin@alldunn.com>
Wed, 16 May 2007 23:39:42 +0000 (23:39 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@46083 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

89 files changed:
wxPython/contrib/gizmos/gizmos.i
wxPython/contrib/gizmos/wxCode/src/treelistctrl.cpp
wxPython/demo/AUI_DockingWindowMgr.py
wxPython/demo/CollapsiblePane.py
wxPython/demo/DragImage.py
wxPython/demo/FloatCanvas.py
wxPython/demo/Main.py
wxPython/demo/MaskedNumCtrl.py
wxPython/demo/OGL.py
wxPython/demo/TreeMixin.py
wxPython/demo/bmp_source/book.png [new file with mode: 0644]
wxPython/demo/bmp_source/clipboard.png [new file with mode: 0644]
wxPython/demo/bmp_source/code.png [new file with mode: 0644]
wxPython/demo/bmp_source/core.png [new file with mode: 0644]
wxPython/demo/bmp_source/custom.png [new file with mode: 0644]
wxPython/demo/bmp_source/deleteperspective.png [new file with mode: 0644]
wxPython/demo/bmp_source/demo.png [new file with mode: 0644]
wxPython/demo/bmp_source/dialog.png [new file with mode: 0644]
wxPython/demo/bmp_source/exit.png [new file with mode: 0644]
wxPython/demo/bmp_source/expansion.png [new file with mode: 0644]
wxPython/demo/bmp_source/find.png [new file with mode: 0644]
wxPython/demo/bmp_source/findnext.png [new file with mode: 0644]
wxPython/demo/bmp_source/frame.png [new file with mode: 0644]
wxPython/demo/bmp_source/images.png [new file with mode: 0644]
wxPython/demo/bmp_source/inspect.png [new file with mode: 0644]
wxPython/demo/bmp_source/layout.png [new file with mode: 0644]
wxPython/demo/bmp_source/miscellaneous.png [new file with mode: 0644]
wxPython/demo/bmp_source/modifiedexists.png [new file with mode: 0644]
wxPython/demo/bmp_source/morecontrols.png [new file with mode: 0644]
wxPython/demo/bmp_source/moredialog.png [new file with mode: 0644]
wxPython/demo/bmp_source/overview.png [new file with mode: 0644]
wxPython/demo/bmp_source/process.png [new file with mode: 0644]
wxPython/demo/bmp_source/pyshell.png [new file with mode: 0644]
wxPython/demo/bmp_source/recent.png [new file with mode: 0644]
wxPython/demo/bmp_source/saveperspective.png [new file with mode: 0644]
wxPython/demo/encode_bitmaps.py
wxPython/distrib/DIRLIST
wxPython/distrib/README.win32.txt
wxPython/distrib/all/build-all
wxPython/distrib/all/build-chrpm [new file with mode: 0755]
wxPython/distrib/make_installer.py
wxPython/docs/CHANGES.txt
wxPython/misc/image.png [deleted file]
wxPython/misc/widgetLayoutTest.cfg [deleted file]
wxPython/misc/widgetLayoutTest.py [deleted file]
wxPython/samples/wxPIA_book/Chapter-09/progress_box.py
wxPython/setup.py
wxPython/src/_app.i
wxPython/src/_dc.i
wxPython/src/_defs.i
wxPython/src/_event.i
wxPython/src/_evtloop.i
wxPython/src/_functions.i
wxPython/src/_pywindows.i
wxPython/src/_sound.i
wxPython/src/_timer.i
wxPython/src/_toolbar.i
wxPython/src/_vscroll.i
wxPython/src/_window.i
wxPython/src/aui.i
wxPython/src/gtk/_core.py
wxPython/src/gtk/_misc_wrap.cpp
wxPython/src/helpers.cpp
wxPython/src/mac/_core.py
wxPython/src/msw/_core.py
wxPython/tests/TreeMixinTest.py
wxPython/tests/image.png [new file with mode: 0644]
wxPython/tests/test_dclick.py [new file with mode: 0644]
wxPython/tests/test_transparentFrame.py [new file with mode: 0644]
wxPython/tests/test_widgetLayout.py [new file with mode: 0644]
wxPython/tests/widgets.cfg [new file with mode: 0644]
wxPython/wx/lib/customtreectrl.py
wxPython/wx/lib/floatcanvas/FloatCanvas.py
wxPython/wx/lib/floatcanvas/GUIMode.py [new file with mode: 0644]
wxPython/wx/lib/floatcanvas/NavCanvas.py
wxPython/wx/lib/floatcanvas/Utilities/BBox.py [new file with mode: 0644]
wxPython/wx/lib/floatcanvas/Utilities/BBoxTest.py [new file with mode: 0644]
wxPython/wx/lib/floatcanvas/Utilities/GUI.py [new file with mode: 0644]
wxPython/wx/lib/floatcanvas/Utilities/__init__.py [new file with mode: 0644]
wxPython/wx/lib/floatcanvas/__init__.py
wxPython/wx/lib/imagebrowser.py
wxPython/wx/lib/inspection.py
wxPython/wx/lib/masked/combobox.py
wxPython/wx/lib/masked/maskededit.py
wxPython/wx/lib/masked/numctrl.py
wxPython/wx/lib/masked/textctrl.py
wxPython/wx/lib/mixins/treemixin.py
wxPython/wx/tools/pywxrc.py
wxPython/wxaddons/sized_controls.py

index 3c39f202dd1aa8fdd097d739d0dfe003cd99da64..0f520ecc52e30c6d783ae616425f8b184f178194 100644 (file)
@@ -170,6 +170,8 @@ enum {
     wxEL_ALLOW_NEW,
     wxEL_ALLOW_EDIT,
     wxEL_ALLOW_DELETE,
+    wxEL_NO_REORDER,
+    wxEL_DEFAULT_STYLE
 };
 
 
@@ -190,9 +192,16 @@ public:
                       const wxString& label = wxPyEmptyString,
                       const wxPoint& pos = wxDefaultPosition,
                       const wxSize& size = wxDefaultSize,
-                      long style = wxEL_ALLOW_NEW | wxEL_ALLOW_EDIT | wxEL_ALLOW_DELETE,
+                      long style = wxEL_DEFAULT_STYLE,
                       const wxString& name = wxPyEditableListBoxNameStr);
-
+    %RenameCtor(PreEditableListBox, wxEditableListBox());
+
+    bool Create(wxWindow *parent, wxWindowID id,
+                const wxString& label,
+                const wxPoint& pos = wxDefaultPosition,
+                const wxSize& size = wxDefaultSize,
+                long style = wxEL_DEFAULT_STYLE,
+                const wxString& name = wxEditableListBoxNameStr);
 
     void SetStrings(const wxArrayString& strings);
 
index 0ee61d3b0be3cf3720959599d50657eca281ed97..1e527f14929a0e0aadd930c16e1e4509fc86266f 100644 (file)
@@ -598,7 +598,8 @@ protected:
     wxTreeListItem       *m_shiftItem; // item, where the shift key was pressed
     wxTreeListItem       *m_editItem; // item, which is currently edited
     wxTreeListItem       *m_selectItem; // current selected item, not with wxTR_MULTIPLE
-
+    wxTreeListItem       *m_select_me;
+    
     int                  m_curColumn;
 
     int                  m_btnWidth, m_btnWidth2;
@@ -922,6 +923,29 @@ private:
 // implementation
 // ===========================================================================
 
+
+// ---------------------------------------------------------------------------
+// internal helpers
+// ---------------------------------------------------------------------------
+
+// check if the given item is under another one
+static bool IsDescendantOf(const wxTreeListItem *parent, const wxTreeListItem *item)
+{
+    while ( item )
+    {
+        if ( item == parent )
+        {
+            // item is a descendant of parent
+            return true;
+        }
+
+        item = item->GetItemParent();
+    }
+
+    return false;
+}
+
+
 // ---------------------------------------------------------------------------
 // wxTreeListRenameTimer (internal)
 // ---------------------------------------------------------------------------
@@ -1789,7 +1813,8 @@ void wxTreeListMainWindow::Init() {
     m_shiftItem = (wxTreeListItem*)NULL;
     m_editItem = (wxTreeListItem*)NULL;
     m_selectItem = (wxTreeListItem*)NULL;
-
+    m_select_me = (wxTreeListItem*)NULL;
+    
     m_curColumn = -1; // no current column
 
     m_hasFocus = false;
@@ -2418,6 +2443,27 @@ void wxTreeListMainWindow::Delete (const wxTreeItemId& itemId) {
     }
 
     wxTreeListItem *parent = item->GetItemParent();
+
+
+    // m_select_me records whether we need to select
+    // a different item, in idle time.
+    if ( m_select_me && IsDescendantOf(item, m_select_me) )
+    {
+        m_select_me = parent;
+    }
+
+    if ( IsDescendantOf(item, m_curItem) )
+    {
+        // Don't silently change the selection:
+        // do it properly in idle time, so event
+        // handlers get called.
+
+        // m_current = parent;
+        m_curItem = NULL;
+        m_select_me = parent;
+    }
+    
+    // remove the item from the tree
     if (parent) {
         parent->GetChildren().Remove (item);  // remove by value
     }
@@ -2426,6 +2472,10 @@ void wxTreeListMainWindow::Delete (const wxTreeItemId& itemId) {
     SendDeleteEvent (item);
     if (m_selectItem == item) m_selectItem = (wxTreeListItem*)NULL;
     item->DeleteChildren (this);
+
+    if (item == m_select_me)
+        m_select_me = NULL;
+    
     delete item;
 }
 
@@ -2660,7 +2710,6 @@ void wxTreeListMainWindow::SelectItem (const wxTreeItemId& itemId,
         if (unselect_others) {
             m_selectItem = (item->IsSelected())? item: (wxTreeListItem*)NULL;
         }
-
     }
 
     // send event to user code
@@ -3627,7 +3676,6 @@ void wxTreeListMainWindow::OnChar (wxKeyEvent &event) {
         m_curItem = (wxTreeListItem*)newItem.m_pItem; // make the new item the current item
         RefreshLine (oldItem);
     }
-
 }
 
 wxTreeItemId wxTreeListMainWindow::HitTest (const wxPoint& point, int& flags, int& column) {
@@ -3969,9 +4017,23 @@ void wxTreeListMainWindow::OnIdle (wxIdleEvent &WXUNUSED(event)) {
      * we actually redraw the tree when everything is over */
 
     if (!m_dirty) return;
-
     m_dirty = false;
 
+    // Check if we need to select the root item
+    // because nothing else has been selected.
+    // Delaying it means that we can invoke event handlers
+    // as required, when a first item is selected.
+    if (!m_owner->HasFlag(wxTR_MULTIPLE) && !m_owner->GetSelection().IsOk())
+    {
+        if (m_select_me)
+            m_owner->SelectItem(m_select_me);
+        else if (m_owner->GetRootItem().IsOk())
+            m_owner->SelectItem(m_owner->GetRootItem());
+        m_select_me = NULL;
+        m_curItem = (wxTreeListItem*)m_owner->GetSelection().m_pItem;
+
+    }
+    
     CalculatePositions();
     Refresh();
     AdjustMyScrollbars();
index 794b9b2ec6dcc07da224e45d2afb319ef645be07..eaa912928c2907dc17ca42db21836d52748b38d2 100644 (file)
@@ -395,7 +395,7 @@ class PyAUIFrame(wx.Frame):
         self.Bind(wx.EVT_MENU, self.OnChangeContentPane, id=ID_TextContent)
         self.Bind(wx.EVT_MENU, self.OnChangeContentPane, id=ID_SizeReportContent)
         self.Bind(wx.EVT_MENU, self.OnChangeContentPane, id=ID_HTMLContent)
-        self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)
+        self.Bind(wx.EVT_MENU, self.OnExit, id=wx.ID_EXIT)
         self.Bind(wx.EVT_MENU, self.OnAbout, id=ID_About)
 
         self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_TransparentHint)
@@ -431,12 +431,13 @@ class PyAUIFrame(wx.Frame):
         
 
     def OnClose(self, event):
-        
         self._mgr.UnInit()
+        del self._mgr
         self.Destroy()
 
-        event.Skip()        
 
+    def OnExit(self, event):
+        self.Close()
 
     def OnAbout(self, event):
 
index 15ab9ad54a2f2eb0ac8c7b6794392cd0dd8e9a69..2c35d712ad067eb0d4318a858ec40344b3100541 100644 (file)
@@ -19,7 +19,8 @@ class TestPanel(wx.Panel):
         title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
         title.SetForegroundColour("blue")
 
-        self.cp = cp = wx.CollapsiblePane(self, label=label1)
+        self.cp = cp = wx.CollapsiblePane(self, label=label1,
+                                          style=wx.CP_DEFAULT_STYLE|wx.CP_NO_TLW_RESIZE)
         self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged, cp)
         self.MakePaneContent(cp.GetPane())
 
index d8f7ef3458affcc013edb91217b840ef57aa4df8..d0173caf0349bd573c51a42e6c8b60ca04b0009f 100644 (file)
@@ -136,19 +136,11 @@ class DragCanvas(wx.ScrolledWindow):
                 return shape
         return None
 
-    # Remove a shape from the display
-    def EraseShape(self, shape, dc):
-        r = shape.GetRect()
-        dc.SetClippingRect(r)
-        self.TileBackground(dc)
-        self.DrawShapes(dc)
-        dc.DestroyClippingRegion()
 
     # Clears the background, then redraws it. If the DC is passed, then
     # we only do so in the area so designated. Otherwise, it's the whole thing.
     def OnEraseBackground(self, evt):
         dc = evt.GetDC()
-
         if not dc:
             dc = wx.ClientDC(self)
             rect = self.GetUpdateRegion().GetBox()
@@ -231,11 +223,11 @@ class DragCanvas(wx.ScrolledWindow):
             if dx <= tolerance and dy <= tolerance:
                 return
 
-            # erase the shape since it will be drawn independently now
-            dc = wx.ClientDC(self)
+            # refresh the area of the window where the shape was so it
+            # will get erased.
             self.dragShape.shown = False
-            self.EraseShape(self.dragShape, dc)
-
+            self.RefreshRect(self.dragShape.GetRect(), True)
+            self.Update()
 
             if self.dragShape.text:
                 self.dragImage = wx.DragString(self.dragShape.text,
index 038d496c662be5ae3b21f9e4887defcc17671973..0b1c54cfd63aa96c33ea5dd63fc4c0c86428d7fc 100644 (file)
@@ -1,27 +1,18 @@
+#!/usr/bin/env python
 
-
-#print "running:", wx.__version__
-##First, make sure Numeric or numarray can be imported.
 try:
-    import Numeric
-    import RandomArray
-    haveNumeric = True
+    import numpy as N
+    import numpy.random as RandomArray
+    haveNumpy = True
+    #print "Using numpy, version:", N.__version__
 except ImportError:
-    # Numeric isn't there, let's try numarray
-    try:
-        import numarray as Numeric
-        import numarray.random_array as RandomArray
-        haveNumeric = True
-    except ImportError:
-        # numarray isn't there either
-        haveNumeric = False
-        errorText = (
-        "The FloatCanvas requires either the Numeric or numarray module\n\n"
-        "You can get them at:\n"
-        "http://sourceforge.net/projects/numpy\n\n"
-        "NOTE: The Numeric module is substantially faster than numarray for this\n"
-        "purpose, if you have lots of objects\n"
-        )
+            # numpy isn't there
+            haveNumpy = False
+            errorText = (
+            "The FloatCanvas requires the numpy module, version 1.* \n\n"
+            "You can get info about it at:\n"
+            "http://numpy.scipy.org/\n\n"
+            )
       
 #---------------------------------------------------------------------------
 
@@ -100,7 +91,10 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             item = draw_menu.Append(-1, "&Arrows","Run a test of Arrows")
             self.Bind(wx.EVT_MENU, self.ArrowTest, item)
 
-            item = draw_menu.Append(-1, "&Hide","Run a test of the Show() Hide() Show() and methods")
+            item = draw_menu.Append(-1, "&ArrowLine Test","Run a test of drawing Arrow Lines")
+            self.Bind(wx.EVT_MENU, self.ArrowLineTest, item)
+
+            item = draw_menu.Append(-1, "&Hide","Run a test of hiding and showing objects")
             self.Bind(wx.EVT_MENU, self.HideTest, item)
 
             MenuBar.Append(draw_menu, "&Tests")
@@ -122,9 +116,11 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
 
             
             # Add the Canvas
-            self.Canvas = NavCanvas.NavCanvas(self,
-                                              Debug = 0,
-                                              BackgroundColor = "DARK SLATE BLUE")
+            NC = NavCanvas.NavCanvas(self,
+                                     Debug = 0,
+                                     BackgroundColor = "DARK SLATE BLUE")
+
+            self.Canvas = NC.Canvas # reference the contained FloatCanvas
 
             self.MsgWindow = wx.TextCtrl(self, wx.ID_ANY,
                                          "Look Here for output from events\n",
@@ -135,13 +131,14 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             
             ##Create a sizer to manage the Canvas and message window
             MainSizer = wx.BoxSizer(wx.VERTICAL)
-            MainSizer.Add(self.Canvas, 4, wx.EXPAND)
+            MainSizer.Add(NC, 4, wx.EXPAND)
             MainSizer.Add(self.MsgWindow, 1, wx.EXPAND | wx.ALL, 5)
 
             self.SetSizer(MainSizer)
-            wx.EVT_CLOSE(self, self.OnCloseWindow)
+            self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
 
-            FloatCanvas.EVT_MOTION(self.Canvas, self.OnMove ) 
+            self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove) 
+            self.Canvas.Bind(FloatCanvas.EVT_MOUSEWHEEL, self.OnWheel) 
 
             self.EventsAreBound = False
 
@@ -161,56 +158,39 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
         def BindAllMouseEvents(self):
             if not self.EventsAreBound:
                 ## Here is how you catch FloatCanvas mouse events
-                FloatCanvas.EVT_LEFT_DOWN(self.Canvas, self.OnLeftDown 
-                FloatCanvas.EVT_LEFT_UP(self.Canvas, self.OnLeftUp )
-                FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, self.OnLeftDouble 
+                self.Canvas.Bind(FloatCanvas.EVT_LEFT_DOWN, self.OnLeftDown
+                self.Canvas.Bind(FloatCanvas.EVT_LEFT_UP, self.OnLeftUp)
+                self.Canvas.Bind(FloatCanvas.EVT_LEFT_DCLICK, self.OnLeftDouble
 
-                FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, self.OnMiddleDown 
-                FloatCanvas.EVT_MIDDLE_UP(self.Canvas, self.OnMiddleUp 
-                FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, self.OnMiddleDouble 
+                self.Canvas.Bind(FloatCanvas.EVT_MIDDLE_DOWN, self.OnMiddleDown
+                self.Canvas.Bind(FloatCanvas.EVT_MIDDLE_UP, self.OnMiddleUp
+                self.Canvas.Bind(FloatCanvas.EVT_MIDDLE_DCLICK, self.OnMiddleDouble
 
-                FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, self.OnRightDown 
-                FloatCanvas.EVT_RIGHT_UP(self.Canvas, self.OnRightUp 
-                FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, self.OnRightDouble 
+                self.Canvas.Bind(FloatCanvas.EVT_RIGHT_DOWN, self.OnRightDown
+                self.Canvas.Bind(FloatCanvas.EVT_RIGHT_UP, self.OnRightUp
+                self.Canvas.Bind(FloatCanvas.EVT_RIGHT_DCLICK, self.OnRightDouble
 
-                FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, self.OnWheel ) 
             self.EventsAreBound = True
 
+
         def UnBindAllMouseEvents(self):
             ## Here is how you unbind FloatCanvas mouse events
-            FloatCanvas.EVT_LEFT_DOWN(self.Canvas, None ) 
-            FloatCanvas.EVT_LEFT_UP(self.Canvas, None )
-            FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, None) 
-
-            FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, None )
-            FloatCanvas.EVT_MIDDLE_UP(self.Canvas, None )
-            FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, None )
-
-            FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, None )
-            FloatCanvas.EVT_RIGHT_UP(self.Canvas, None )
-            FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, None )
-
-            FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, None )
-            FloatCanvas.EVT_LEFT_DOWN(self.Canvas, None ) 
-            FloatCanvas.EVT_LEFT_UP(self.Canvas, None )
-            FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, None) 
-
-            FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, None )
-            FloatCanvas.EVT_MIDDLE_UP(self.Canvas, None )
-            FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, None )
+            self.Canvas.Unbind(FloatCanvas.EVT_LEFT_DOWN)
+            self.Canvas.Unbind(FloatCanvas.EVT_LEFT_UP)
+            self.Canvas.Unbind(FloatCanvas.EVT_LEFT_DCLICK)
 
-            FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, None )
-            FloatCanvas.EVT_RIGHT_UP(self.Canvas, None )
-            FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, None )
+            self.Canvas.Unbind(FloatCanvas.EVT_MIDDLE_DOWN)
+            self.Canvas.Unbind(FloatCanvas.EVT_MIDDLE_UP)
+            self.Canvas.Unbind(FloatCanvas.EVT_MIDDLE_DCLICK)
 
-            FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, None )
+            self.Canvas.Unbind(FloatCanvas.EVT_RIGHT_DOWN)
+            self.Canvas.Unbind(FloatCanvas.EVT_RIGHT_UP)
+            self.Canvas.Unbind(FloatCanvas.EVT_RIGHT_DCLICK)
 
             self.EventsAreBound = False
 
 
         def PrintCoords(self,event):
-            #print "coords are: %s"%(event.Coords,)
-            #print "pixel coords are: %s\n"%(event.GetPosition(),)
             self.Log("coords are: %s"%(event.Coords,))
             self.Log("pixel coords are: %s\n"%(event.GetPosition(),))
 
@@ -256,7 +236,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.PrintCoords(event)
 
         def OnRightUp(self, event):
-            self.Log("RightDown")
+            self.Log("RightUp")
             self.PrintCoords(event)
 
         def OnRightDouble(self, event):
@@ -278,6 +258,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             Updates the status bar with the world coordinates
             """
             self.SetStatusText("%.2f, %.2f"%tuple(event.Coords))
+            event.Skip()
 
         def OnAbout(self, event):
             dlg = wx.MessageDialog(self,
@@ -293,8 +274,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
 
         def Clear(self,event = None):
             self.UnBindAllMouseEvents()
-            self.Canvas.ClearAll()
-            self.Canvas.SetProjectionFun(None)
+            self.Canvas.InitAll()
             self.Canvas.Draw()
 
         def OnQuit(self,event):
@@ -304,7 +284,12 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.Destroy()
 
         def DrawTest(self,event=None):
-            wx.GetApp().Yield()
+            """
+            This demo draws a few of everything
+
+            """
+            
+            wx.GetApp().Yield(True)
 
             Range = (-10,10)
             colors = self.colors
@@ -312,8 +297,12 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.BindAllMouseEvents()
             Canvas = self.Canvas
 
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
+            Canvas.InitAll()
+            #            
+            ## these set the limits for how much you can zoom in and out
+            Canvas.MinScale = 14
+            Canvas.MaxScale = 500
+            
 
             ############# Random tests of everything ##############
 
@@ -417,6 +406,18 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                 LineColor = colors[random.randint(0,len(colors)-1)],
                                 ArrowHeadAngle = random.uniform(20,90))
 
+            # ArrowLines
+            for i in range(5):
+                points = []
+                for j in range(random.randint(2,10)):
+                    point = (random.randint(Range[0],Range[1]),random.randint(Range[0],Range[1]))
+                    points.append(point)
+                lw = random.randint(1,10)
+                cf = random.randint(0,len(colors)-1)
+                cl = random.randint(0,len(colors)-1)
+                Canvas.AddArrowLine(points, LineWidth = lw, LineColor = colors[cl], ArrowHeadSize= 16)
+
+
             Canvas.ZoomToBB()
 
         def TestAnimation(self,event=None):
@@ -429,15 +430,13 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             having to re-draw the whole background.
 
             """
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
             Range = (-10,10)
             self.Range = Range
 
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
-
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
+            Canvas.InitAll()
 
             ##         Random tests of everything:
             colors = self.colors
@@ -543,14 +542,13 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.Timer.Start(self.FrameDelay)
             #print "Did %i frames in %f seconds"%(N, (time.time() - start) )
 
-        def TestHitTest(self,event=None):
-            wx.GetApp().Yield()
+        def TestHitTest(self, event=None):
+            wx.GetApp().Yield(True)
 
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
 
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
+            Canvas.InitAll()
 
             #Add a Hit-able rectangle
             w, h = 60, 20
@@ -558,7 +556,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             dx = 80
             dy = 40
             x, y = 20, 20
-            FontSize = 8
+            FontSize = 10
 
             #Add one that is not HitAble
             Canvas.AddRectangle((x,y), (w, h), LineWidth = 2)
@@ -709,7 +707,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             R.Name = color
             R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
             R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
-            Canvas.AddText("Mouse ENter&Leave", (x, y), Size = FontSize, Position = "bl")
+            Canvas.AddText("Mouse Enter&Leave", (x, y), Size = FontSize, Position = "bl")
             Canvas.AddText(R.Name, (x, y+h), Size = FontSize, Position = "tl")
 
             x = 20
@@ -722,7 +720,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
 
             x += dx
             color = "SEA GREEN"
-            Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+            Points = N.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), N.float_)
             R = Canvas.AddPolygon(Points,  LineWidth = 2, FillColor = color)
             R.Name = color + " Polygon"
             R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHitRight)
@@ -731,7 +729,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
 
             x += dx
             color = "Red"
-            Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+            Points = N.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), N.float_)
             R = Canvas.AddPointSet(Points,  Diameter = 4, Color = color)
             R.Name = "PointSet"
             R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.PointSetGotHit)
@@ -755,7 +753,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             x += dx
             color = "Cyan"
             Point = (x + w/2, y)
-            #Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+            #Points = N.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), N.float_)
             R = Canvas.AddSquarePoint(Point,  Size = 8, Color = color)
             R.Name = "SquarePoint"
             R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHit)
@@ -766,13 +764,12 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.Canvas.ZoomToBB()
 
         def TestHitTestForeground(self,event=None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
 
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
 
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
+            Canvas.InitAll()
 
             #Add a Hitable rectangle
             w, h = 60, 20
@@ -871,13 +868,14 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
 
 
         def TestText(self, event= None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
 
             self.BindAllMouseEvents()
             Canvas = self.Canvas
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
+            Canvas.InitAll()
+            
 
+            DefaultSize = 12           
             Point  = (3, 0)
 
             ## Add a non-visible rectangle, just to get a Bounding Box
@@ -889,29 +887,29 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
 
             # Text
             String = "Some text"
-            self.Canvas.AddText("Top Left",Point,Size = 14,Color = "Yellow",BackgroundColor = "Blue", Position = "tl")
-            self.Canvas.AddText("Bottom Left",Point,Size = 14,Color = "Cyan",BackgroundColor = "Black",Position = "bl")
-            self.Canvas.AddText("Top Right",Point,Size = 14,Color = "Black",BackgroundColor = "Cyan",Position = "tr")
-            self.Canvas.AddText("Bottom Right",Point,Size = 14,Color = "Blue",BackgroundColor = "Yellow",Position = "br")
+            self.Canvas.AddText("Top Left",Point,Size = DefaultSize,Color = "Yellow",BackgroundColor = "Blue", Position = "tl")
+            self.Canvas.AddText("Bottom Left",Point,Size = DefaultSize,Color = "Cyan",BackgroundColor = "Black",Position = "bl")
+            self.Canvas.AddText("Top Right",Point,Size = DefaultSize,Color = "Black",BackgroundColor = "Cyan",Position = "tr")
+            self.Canvas.AddText("Bottom Right",Point,Size = DefaultSize,Color = "Blue",BackgroundColor = "Yellow",Position = "br")
             Canvas.AddPointSet((Point), Color = "White", Diameter = 2)
 
             Point  = (3, 2)
 
             Canvas.AddPointSet((Point), Color = "White", Diameter = 2)
-            self.Canvas.AddText("Top Center",Point,Size = 14,Color = "Black",Position = "tc")
-            self.Canvas.AddText("Bottom Center",Point,Size = 14,Color = "White",Position = "bc")
+            self.Canvas.AddText("Top Center",Point,Size = DefaultSize,Color = "Black",Position = "tc")
+            self.Canvas.AddText("Bottom Center",Point,Size = DefaultSize,Color = "White",Position = "bc")
 
             Point  = (3, 4)
 
             Canvas.AddPointSet((Point), Color = "White", Diameter = 2)
-            self.Canvas.AddText("Center Right",Point,Size = 14,Color = "Black",Position = "cr")
-            self.Canvas.AddText("Center Left",Point,Size = 14,Color = "Black",Position = "cl")
+            self.Canvas.AddText("Center Right",Point,Size = DefaultSize,Color = "Black",Position = "cr")
+            self.Canvas.AddText("Center Left",Point,Size = DefaultSize,Color = "Black",Position = "cl")
 
             Point  = (3, -2)
 
             Canvas.AddPointSet((Point), Color = "White", Diameter = 2)
             self.Canvas.AddText("Center Center",
-                                Point, Size = 14,
+                                Point, Size = DefaultSize,
                                 Color = "Black",
                                 Position = "cc")
 
@@ -927,18 +925,17 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.Canvas.AddText("ROMAN ITALIC BOLD Font", (-10, -5), Family = wx.ROMAN, Weight=wx.BOLD, Style=wx.ITALIC)
 
             # NOTE: this font exists on my Linux box..who knows were else you'll find it!
-            Font = wx.Font(20, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "zapf chancery")
-            self.Canvas.AddText("zapf chancery Font", (-10, -6), Font = Font)
+            Font = wx.Font(20, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "helvetica")
+            self.Canvas.AddText("Helvetica Italic", (-10, -6), Font = Font)
 
             self.Canvas.ZoomToBB()
 
         def TestScaledText(self, event= None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
 
             self.BindAllMouseEvents()
             Canvas = self.Canvas
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
+            Canvas.InitAll()
 
             Point  = (0, 0)
 
@@ -977,24 +974,23 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             Canvas.AddPointSet((x,0), Color = "White", Diameter = 4)
 
 
-            # NOTE: this font exists on my Linux box..who knows were else you'll find it!
+            # NOTE: this font exists on my OS-X.who knows were else you'll find it!
             Point = (-100, 50)
-            Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "zapf chancery")
-            T = self.Canvas.AddScaledText("zapf chancery Font", Point, Size = 20, Font = Font, Position = 'bc')
+            Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "helvetica")
+            T = self.Canvas.AddScaledText("Helvetica Italic", Point, Size = 20, Font = Font, Position = 'bc')
 
             Point = (-50, -50)
-            Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "bookman")
-            T = self.Canvas.AddScaledText("Bookman Font", Point, Size = 8, Font = Font)
+            Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "times")
+            T = self.Canvas.AddScaledText("Times Font", Point, Size = 8, Font = Font)
 
             self.Canvas.ZoomToBB()
 
         def TestScaledTextBox(self, event= None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
 
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
+            Canvas.InitAll()
 
             Point = (45,40)
             Box = Canvas.AddScaledTextBox("A Two Line\nString",
@@ -1010,7 +1006,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           Family = wx.ROMAN,
                                           Style = wx.NORMAL,
                                           Weight = wx.NORMAL,
-                                          Underline = False,
+                                          Underlined = False,
                                           Position = 'br',
                                           Alignment = "left",
                                           InForeground = False)
@@ -1058,7 +1054,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           Family = wx.TELETYPE,
                                           Style = wx.NORMAL,
                                           Weight = wx.NORMAL,
-                                          Underline = False,
+                                          Underlined = False,
                                           Position = 'cr',
                                           Alignment = "left",
                                           InForeground = False)
@@ -1076,7 +1072,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           Family = wx.TELETYPE,
                                           Style = wx.NORMAL,
                                           Weight = wx.NORMAL,
-                                          Underline = False,
+                                          Underlined = False,
                                           Position = 'cl',
                                           Alignment = "left",
                                           InForeground = False)
@@ -1097,7 +1093,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           Family = wx.TELETYPE,
                                           Style = wx.NORMAL,
                                           Weight = wx.NORMAL,
-                                          Underline = False,
+                                          Underlined = False,
                                           Position = 'tc',
                                           Alignment = "left",
                                           InForeground = False)
@@ -1115,7 +1111,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           Family = wx.TELETYPE,
                                           Style = wx.NORMAL,
                                           Weight = wx.NORMAL,
-                                          Underline = False,
+                                          Underlined = False,
                                           Position = 'bc',
                                           Alignment = "left",
                                           InForeground = False)
@@ -1143,7 +1139,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           Family = wx.ROMAN,
                                           Alignment = "right"
                                           )
-            Point = Numeric.array((100, -20), Numeric.Float)
+            Point = N.array((100, -20), N.float_)
             Box = Canvas.AddScaledTextBox("Here is even more auto wrapped text. This time the line spacing is set to 0.8. \n\nThe Padding is set to 0.",
                                           Point,
                                           Size = 3,
@@ -1157,8 +1153,8 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           )
             Canvas.AddPoint(Point, "Red", 2)
 
-            Point = Numeric.array((0, -40), Numeric.Float)
-    #        Point = Numeric.array((0, 0), Numeric.Float)
+            Point = N.array((0, -40), N.float_)
+    #        Point = N.array((0, 0), N.float_)
             for Position in ["tl", "bl", "tr", "br"]:
     #        for Position in ["br"]:
                 Box = Canvas.AddScaledTextBox("Here is a\nfour liner\nanother line\nPosition=%s"%Position,
@@ -1176,7 +1172,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           )
             Canvas.AddPoint(Point, "Red", 4)
 
-            Point = Numeric.array((-20, 60), Numeric.Float)
+            Point = N.array((-20, 60), N.float_)
             Box = Canvas.AddScaledTextBox("Here is some\ncentered\ntext",
                                           Point,
                                           Size = 4,
@@ -1192,7 +1188,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           LineSpacing = 0.8
                                           )
 
-            Point = Numeric.array((-20, 20), Numeric.Float)
+            Point = N.array((-20, 20), N.float_)
             Box = Canvas.AddScaledTextBox("Here is some\nright aligned\ntext",
                                           Point,
                                           Size = 4,
@@ -1207,7 +1203,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
                                           LineSpacing = 0.8
                                           )
 
-            Point = Numeric.array((100, -60), Numeric.Float)
+            Point = N.array((100, -60), N.float_)
             Box = Canvas.AddScaledTextBox("Here is some auto wrapped text. This time it is centered, rather than right aligned.\n\nThe Padding is set to 2.",
                                           Point,
                                           Size = 3,
@@ -1230,13 +1226,12 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.Log("I'm the TextBox")
 
         def TestBitmap(self, event= None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
 
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
-
+            Canvas.InitAll()
+            
             Canvas.AddRectangle((10, 20),
                                 (400, 100),
                                 LineWidth = 3,
@@ -1295,16 +1290,17 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.Canvas.ZoomToBB()
 
         def DrawMap(self,event = None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
             import os, time
+            
+            self.Canvas.InitAll()
+            self.Canvas.SetProjectionFun("FlatEarth")
             self.BindAllMouseEvents()
 
         ## Test of Actual Map Data
-            self.Canvas.ClearAll()
-            self.Canvas.SetProjectionFun("FlatEarth")
             #start = time.clock()
             self.Log("Loading Map from a File")
-            wx.GetApp().Yield() # so log text will get displayed now.
+            wx.GetApp().Yield(True) # so log text will get displayed now.
             Shorelines = self.Read_MapGen(os.path.join("data",'world.dat'),stats = 0)
             #print "It took %f seconds to load %i shorelines"%(time.clock() - start,len(Shorelines) )
             #start = time.clock()
@@ -1316,17 +1312,17 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             #print "It took %f seconds to draw %i shorelines"%(time.clock() - start,len(Shorelines) )
 
 
+
         def LineTest(self,event = None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
             import os, time
 #            import random
             colors = self.colors
             Range = (-10,10)
         ## Test of drawing lots of lines
             Canvas = self.Canvas
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
-            #start = time.clock()
+            Canvas.InitAll()
+                        #start = time.clock()
             linepoints = []
             linecolors = []
             linewidths = []
@@ -1345,17 +1341,44 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             Canvas.ZoomToBB()
             #print "It took %f seconds to draw %i lines"%(time.clock() - start,len(linepoints) )
 
+        def ArrowLineTest(self,event = None):
+            wx.GetApp().Yield(True)
+            Canvas = self.Canvas
+            Canvas.InitAll()
+            #            import os, time
+##            import random
+            Range = (-100,100)
+            colors = self.colors
+
+            # Lines
+            for i in range(5):
+                points = []
+                for j in range(random.randint(2,10)):
+                    point = (random.randint(Range[0],Range[1]),random.randint(Range[0],Range[1]))
+                    points.append(point)
+                lw = random.randint(1,4)
+                cf = random.randint(0,len(colors)-1)
+                cl = random.randint(0,len(colors)-1)
+                al = random.randint(8,20)
+                aa = random.randint(20,90)
+                Canvas.AddArrowLine(points,
+                                    LineWidth = lw,
+                                    LineColor = colors[cl],
+                                    ArrowHeadSize = al,
+                                    ArrowHeadAngle = aa)
+
+            Canvas.ZoomToBB()
+
         def SpeedTest(self,event=None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
             BigRange = (-1000,1000)
             colors = self.colors
 
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
 
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
-
+            Canvas.InitAll()
+            
             # Pointset
             coords = []
             for i in range(1000):
@@ -1369,7 +1392,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             Canvas.ZoomToBB()
 
         def PropertiesChangeTest(self,event=None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
 
             Range = (-10,10)
             colors = self.colors
@@ -1377,9 +1400,8 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
 
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
-
+            Canvas.InitAll()
+            
             self.ColorObjectsAll = []
             self.ColorObjectsLine = []
             self.ColorObjectsColor = []
@@ -1512,13 +1534,14 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.Canvas.Draw(Force = True)
 
         def ArrowTest(self,event=None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
 
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
-
+            Canvas.InitAll()
+            Canvas.MinScale = 15
+            Canvas.MaxScale = 30
+            
             # put in a rectangle to get a bounding box
             Canvas.AddRectangle((0,0), (20,20), LineColor = None)
 
@@ -1534,16 +1557,18 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
 
             Canvas.AddText("Clickable Arrow", (4,18), Position = "bc")
             Arrow = Canvas.AddArrow((4,18), 80, Direction = 90 ,LineWidth = 3, LineColor = "Red",   ArrowHeadAngle = 30)
+            Arrow.HitLineWidth = 6
             Arrow.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.ArrowClicked)
 
-            Canvas.AddText("Changable Arrow", (16,4), Position = "cc")
+            Canvas.AddText("Changable Arrow: try clicking it", (16,4), Position = "tc")
             self.RotArrow = Canvas.AddArrow((16,4), 80, Direction = 0 ,LineWidth = 3, LineColor = "Green",   ArrowHeadAngle = 30)
+            self.RotArrow.HitLineWidth = 6
             self.RotArrow.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RotateArrow)
 
             Canvas.ZoomToBB()
 
         def ArrowClicked(self,event):
-            print "The Arrow was Clicked"
+            self.Log("The Arrow was Clicked")
 
         def RotateArrow(self,event):
             ##print "The Changeable Arrow was Clicked"
@@ -1557,13 +1582,12 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             self.Canvas.Draw(Force = True)
 
         def HideTest(self, event=None):
-            wx.GetApp().Yield()
+            wx.GetApp().Yield(True)
 
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
-
+            Canvas.InitAll()
+            
             Range = (-10,10)
 
             # Create a couple random Polygons
@@ -1616,22 +1640,32 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
 
         def HidePoly(self, Button):
             Poly = Button.HidePoly
+
             if Poly.Visible:
-                Poly.Hide()
+                Poly.Visible = False
                 Button.SetText(Button.String.replace("Hide","Show"))
             else:
-                Poly.Show()
+                Poly.Visible = True
                 Button.SetText(Button.String.replace("Show", "Hide"))
             self.Canvas.Draw(True)
 
+
         def TempTest(self, event= None):
-            wx.GetApp().Yield()
+            """
+            
+            This is the start of a poly editor test, but it's not complete
+            so you can only run it through a command line flag:
+            
+            python FloatCanvasDemo.py --temp
+            
+            """
+            
+            wx.GetApp().Yield(True)
 
             self.UnBindAllMouseEvents()
             Canvas = self.Canvas
-            Canvas.ClearAll()
-            Canvas.SetProjectionFun(None)
-
+            Canvas.InitAll()
+            
             Range = (-10,10)
 
             # Create a random Polygon
@@ -1654,7 +1688,6 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             Canvas.ZoomToBB()
 
         def SelectPoly(self, Object):
-            print "In SelectPoly"
             Canvas = self.Canvas
             if Object is self.SelectedPoly:
                 pass
@@ -1673,7 +1706,7 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             Canvas.Draw()
 
         def SelectPointHit(self, Point):
-            print "Point Num: %i Hit"%Point.VerticeNum
+            self.Log("Point Num: %i Hit"%Point.VerticeNum)
             self.SelectedPoint = Point
 
         def Read_MapGen(self, filename, stats = 0,AllLines=0):
@@ -1697,11 +1730,11 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
             for line in data:
                 if line:
                     if line == "# -b": #New segment beginning
-                        if segment: Shorelines.append(Numeric.array(segment))
+                        if segment: Shorelines.append(N.array(segment))
                         segment = []
                     else:
                         segment.append(map(float,string.split(line)))
-            if segment: Shorelines.append(Numeric.array(segment))
+            if segment: Shorelines.append(N.array(segment))
 
             if stats:
                 NumSegments = len(Shorelines)
@@ -1728,13 +1761,16 @@ def BuildDrawFrame(): # this gets called when needed, rather than on import
       
 if __name__ == "__main__":
 
+    # running stand alone, Use wxversion:
+#    import wxversion
+#    wxversion.select("2.6")
+#    wxversion.select("2.8")
     import wx
    
     
     # check options:
     import sys, getopt
-    optlist, args = getopt.getopt(sys.argv[1:],'l',["local",
-                                                    "all",
+    optlist, args = getopt.getopt(sys.argv[1:],'l',["all",
                                                     "text",
                                                     "map",
                                                     "stext",
@@ -1747,40 +1783,15 @@ if __name__ == "__main__":
                                                     "temp",
                                                     "props",
                                                     "arrow",
+                                                    "arrowline",
                                                     "hide"])
 
-    if not haveNumeric:
+    if not haveNumpy:
         raise ImportError(errorText)
     StartUpDemo = "all" # the default
-    for opt in optlist:
-        if opt[0] == "--all":
-            StartUpDemo = "all"
-        elif opt[0] == "--text":
-            StartUpDemo = "text"
-        elif opt[0] == "--map":
-            StartUpDemo = "map"
-        elif opt[0] == "--stext":
-            StartUpDemo = "stext"
-        elif opt[0] == "--stextbox":
-            StartUpDemo = "stextbox"
-        elif opt[0] == "--bitmap":
-            StartUpDemo = "bitmap"
-        elif opt[0] == "--hit":
-            StartUpDemo = "hit"
-        elif opt[0] == "--hitf":
-            StartUpDemo = "hitf"
-        elif opt[0] == "--animate":
-            StartUpDemo = "animate"
-        elif opt[0] == "--speed":
-            StartUpDemo = "speed"
-        elif opt[0] == "--temp":
-            StartUpDemo = "temp"
-        elif opt[0] == "--props":
-            StartUpDemo = "props"
-        elif opt[0] == "--arrow":
-            StartUpDemo = "arrow"
-        elif opt[0] == "--hide":
-            StartUpDemo = "hide"
+    if optlist:
+        StartUpDemo = optlist[0][0][2:]
+
 
     class DemoApp(wx.App):
         """
@@ -1844,6 +1855,7 @@ if __name__ == "__main__":
             frame.Show()
 
             ## check to see if the demo is set to start in a particular mode.
+            ## fixme: should this be in a dict instead?
             if StartUpDemo == "text":
                 frame.TestText()
             elif StartUpDemo == "stext":
@@ -1859,25 +1871,20 @@ if __name__ == "__main__":
             elif StartUpDemo == "hit":
                 frame.TestHitTest()
             elif StartUpDemo == "hitf":
-                "starting TestHitTestForeground"
                 frame.TestHitTestForeground()
             elif StartUpDemo == "animate":
-                "starting TestAnimation"
                 frame.TestAnimation()
             elif StartUpDemo == "speed":
-                "starting SpeedTest"
                 frame.SpeedTest()
             elif StartUpDemo == "temp":
-                "starting temp Test"
                 frame.TempTest()
             elif StartUpDemo == "props":
-                "starting PropertiesChange Test"
                 frame.PropertiesChangeTest()
             elif StartUpDemo == "arrow":
-                "starting arrow Test"
                 frame.ArrowTest()
+            elif StartUpDemo == "arrowline":
+                frame.ArrowLineTest()
             elif StartUpDemo == "hide":
-                "starting Hide Test"
                 frame.HideTest()
 
             return True
@@ -1889,14 +1896,14 @@ else:
     # It's not running stand-alone, set up for wxPython demo.
     # don't  neeed wxversion here.
     import wx
-    if not haveNumeric:
+    if not haveNumpy:
         ## TestPanel and runTest used for integration into wxPython Demo
         class TestPanel(wx.Panel):
             def __init__(self, parent, log):
                 self.log = log
                 wx.Panel.__init__(self, parent, -1)
 
-                import images
+                from wx.lib.floatcanvas.ScreenShot import getScreenShotBitmap
 
                 note1 = wx.StaticText(self, -1, errorText)
                 note2 = wx.StaticText(self, -1, "This is what the FloatCanvas can look like:")
@@ -1904,7 +1911,7 @@ else:
                 S.Add((10, 10), 1)
                 S.Add(note1, 0, wx.ALIGN_CENTER)
                 S.Add(note2, 0, wx.ALIGN_CENTER | wx.BOTTOM, 4)
-                S.Add(wx.StaticBitmap(self,-1,images.getFloatCanvasBitmap()),0,wx.ALIGN_CENTER)
+                S.Add(wx.StaticBitmap(self,-1,getScreenShotBitmap()),0,wx.ALIGN_CENTER)
                 S.Add((10, 10), 1)
                 self.SetSizer(S)
                 self.Layout()
index 2c99409724a78a7a1aa3f8745435756bc1509a05..495acedf769ae11fd83b16c0d8a31c2ae4bc9784 100644 (file)
 # * Annoying switching between tabs and resulting flicker
 #     how to replace a page in the notebook without deleting/adding?
 #     Where is SetPage!? tried freeze...tried reparent of dummy panel....
+#     AG: It looks like this issue is fixed by Freeze()ing and Thaw()ing the
+#         main frame and not the notebook
 
 # TODO List:
-# * UI design more prefessional
+# * UI design more professional (is the new version more professional?)
 # * save file positions (new field in demoModules) (@ LoadDemoSource)
 # * Update main overview
 
@@ -30,6 +32,7 @@
 import sys, os, time, traceback, types
 
 import wx                  # This module uses the new wx namespace
+import wx.aui
 import wx.html
 
 import images
@@ -43,6 +46,15 @@ import images
 
 #---------------------------------------------------------------------------
 
+USE_CUSTOMTREECTRL = False
+ALLOW_AUI_FLOATING = False
+DEFAULT_PERSPECTIVE = "Default Perspective"
+
+#---------------------------------------------------------------------------
+
+_demoPngs = ["overview", "recent", "frame", "dialog", "moredialog", "core",
+             "book", "customcontrol", "morecontrols", "layout", "process", "clipboard",
+             "images", "miscellaneous"]
 
 _treeList = [
     # new stuff
@@ -631,7 +643,9 @@ class DemoCodePanel(wx.Panel):
     def ActiveModuleChanged(self):
         self.LoadDemoSource(self.demoModules.GetSource())
         self.UpdateControlState()
+        self.mainFrame.Freeze()        
         self.ReloadDemo()
+        self.mainFrame.Thaw()
 
         
     def LoadDemoSource(self, source):
@@ -726,24 +740,37 @@ class DemoCodePanel(wx.Panel):
         self.demoModules.LoadFromFile(modModified, modifiedFilename)
         self.ActiveModuleChanged()
 
+        self.mainFrame.SetTreeModified(True)
+
 
     def OnRestore(self, event): # Handles the "Delete Modified" button
         modifiedFilename = GetModifiedFilename(self.demoModules.name)
         self.demoModules.Delete(modModified)
         os.unlink(modifiedFilename) # Delete the modified copy
         busy = wx.BusyInfo("Reloading demo module...")
+        
         self.ActiveModuleChanged()
 
+        self.mainFrame.SetTreeModified(False)
+
 
 #---------------------------------------------------------------------------
 
 def opj(path):
     """Convert paths to the platform-specific separator"""
-    str = apply(os.path.join, tuple(path.split('/')))
+    st = apply(os.path.join, tuple(path.split('/')))
     # HACK: on Linux, a leading / gets lost...
     if path.startswith('/'):
-        str = '/' + str
-    return str
+        st = '/' + st
+    return st
+
+
+def GetDataDir():
+    """
+    Return the standard location on this platform for application data
+    """
+    sp = wx.StandardPaths.Get()
+    return sp.GetUserDataDir()
 
 
 def GetModifiedDirectory():
@@ -751,7 +778,7 @@ def GetModifiedDirectory():
     Returns the directory where modified versions of the demo files
     are stored
     """
-    return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
+    return os.path.join(GetDataDir(), "modified")
 
 
 def GetModifiedFilename(name):
@@ -760,7 +787,7 @@ def GetModifiedFilename(name):
     """
     if not name.endswith(".py"):
         name = name + ".py"
-    return GetModifiedDirectory() + name
+    return os.path.join(GetModifiedDirectory(), name)
 
 
 def GetOriginalFilename(name):
@@ -780,6 +807,25 @@ def DoesModifiedExist(name):
         return False
 
 
+def GetConfig():
+    if not os.path.exists(GetDataDir()):
+        os.makedirs(GetDataDir())
+
+    config = wx.FileConfig(
+        localFilename=os.path.join(GetDataDir(), "options"))
+    return config
+
+
+def SearchDemo(name, keyword):
+    """ Returns whether a demo contains the search keyword or not. """
+    fid = open(GetOriginalFilename(name), "rt")
+    fullText = fid.read()
+    fid.close()
+    if fullText.find(keyword) >= 0:
+        return True
+
+    return False    
+
 #---------------------------------------------------------------------------
 
 class ModuleDictWrapper:
@@ -1132,10 +1178,13 @@ class wxPythonDemo(wx.Frame):
     overviewText = "wxPython Overview"
 
     def __init__(self, parent, title):
-        wx.Frame.__init__(self, parent, -1, title, size = (950, 720),
+        wx.Frame.__init__(self, parent, -1, title, size = (970, 720),
                           style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
 
         self.SetMinSize((640,480))
+        
+        self.mgr = wx.aui.AuiManager()
+        self.mgr.SetManagedWindow(self)
 
         self.loaded = False
         self.cwd = os.getcwd()
@@ -1154,8 +1203,6 @@ class wxPythonDemo(wx.Frame):
         except:
             self.tbicon = None
             
-        wx.CallAfter(self.ShowTip)
-
         self.otherWin = None
         self.Bind(wx.EVT_IDLE, self.OnIdle)
         self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
@@ -1165,119 +1212,67 @@ class wxPythonDemo(wx.Frame):
         self.Centre(wx.BOTH)
         self.CreateStatusBar(1, wx.ST_SIZEGRIP)
 
-        splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
-        splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
-
-        def EmptyHandler(evt): pass
-        #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
-        #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
-
-        # Prevent TreeCtrl from displaying all items after destruction when True
         self.dying = False
+        self.skipLoad = False
+        
+        def EmptyHandler(evt): pass
 
+        self.ReadConfigurationFile()
+        
         # Create a Notebook
-        self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
-
-        # Make a File menu
-        self.mainmenu = wx.MenuBar()
-        menu = wx.Menu()
-        item = menu.Append(-1, '&Redirect Output',
-                           'Redirect print statements to a window',
-                           wx.ITEM_CHECK)
-        self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
-        exitItem = menu.Append(-1, 'E&xit\tCtrl-Q', 'Get the heck outta here!')
-        self.Bind(wx.EVT_MENU, self.OnFileExit, exitItem)
-        wx.App.SetMacExitMenuItemId(exitItem.GetId())
-        self.mainmenu.Append(menu, '&File')
-
-        # Make a Demo menu
-        menu = wx.Menu()
-        for item in _treeList[:-1]:
-            submenu = wx.Menu()
-            for childItem in item[1]:
-                mi = submenu.Append(-1, childItem)
-                self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
-            menu.AppendMenu(wx.NewId(), item[0], submenu)
-        self.mainmenu.Append(menu, '&Demo')
-
-
-        # Make a Help menu
-        menu = wx.Menu()
-        findItem = menu.Append(-1, '&Find\tCtrl-F', 'Find in the Demo Code')
-        findnextItem = menu.Append(-1, 'Find &Next\tF3', 'Find Next')
-        menu.AppendSeparator()
-
-        shellItem = menu.Append(-1, 'Open Py&Shell Window\tF5',
-                                'An interactive interpreter window with the demo app and frame objects in the namesapce')
-        inspToolItem = menu.Append(-1, 'Open &Widget Inspector\tF6',
-                                'A tool that lets you browse the live widgets and sizers in an application')
-        menu.AppendSeparator()
-        helpItem = menu.Append(-1, '&About wxPython Demo', 'wxPython RULES!!!')
-        wx.App.SetMacAboutMenuItemId(helpItem.GetId())
-
-        self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
-        self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, inspToolItem)
-        self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
-        self.Bind(wx.EVT_MENU, self.OnHelpFind,  findItem)
-        self.Bind(wx.EVT_MENU, self.OnFindNext,  findnextItem)
-        self.Bind(wx.EVT_FIND, self.OnFind)
-        self.Bind(wx.EVT_FIND_NEXT, self.OnFind)
-        self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
-        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findItem)
-        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findnextItem)
-        self.mainmenu.Append(menu, '&Help')
-        self.SetMenuBar(self.mainmenu)
-
+        self.nb = wx.Notebook(self, -1, style=wx.CLIP_CHILDREN)
+        imgList = wx.ImageList(16, 16)
+        for png in ["overview", "code", "demo"]:
+            bmp = images.catalog[png].getBitmap()
+            imgList.Add(bmp)
+        self.nb.AssignImageList(imgList)
+
+        self.BuildMenuBar()
+        
         self.finddata = wx.FindReplaceData()
         self.finddata.SetFlags(wx.FR_DOWN)
 
-        if False:
-            # This is another way to set Accelerators, in addition to
-            # using the '\t<key>' syntax in the menu items.
-            aTable = wx.AcceleratorTable([(wx.ACCEL_ALT,  ord('X'), exitItem.GetId()),
-                                          (wx.ACCEL_CTRL, ord('H'), helpItem.GetId()),
-                                          (wx.ACCEL_CTRL, ord('F'), findItem.GetId()),
-                                          (wx.ACCEL_NORMAL, wx.WXK_F3, findnextItem.GetId()),
-                                          (wx.ACCEL_NORMAL, wx.WXK_F9, shellItem.GetId()),
-                                          ])
-            self.SetAcceleratorTable(aTable)
-
-
         # Create a TreeCtrl
-        tID = wx.NewId()
-        leftPanel = wx.Panel(splitter)
+        leftPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN)
+        self.treeMap = {}
+        self.searchItems = {}
+        
+        self.tree = wxPythonDemoTree(leftPanel)
         
-        self.filter = wx.SearchCtrl(leftPanel)
+        self.filter = wx.SearchCtrl(leftPanel, style=wx.TE_PROCESS_ENTER)
         self.filter.ShowCancelButton(True)
         self.filter.Bind(wx.EVT_TEXT, self.RecreateTree)
         self.filter.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN,
                          lambda e: self.filter.SetValue(''))
-        
-        self.treeMap = {}
-        self.tree = wx.TreeCtrl(leftPanel, tID, style =
-                                wx.TR_DEFAULT_STYLE #| wx.TR_HAS_VARIABLE_ROW_HEIGHT
-                               )
+        self.filter.Bind(wx.EVT_TEXT_ENTER, self.OnSearch)
+
+        searchMenu = wx.Menu()
+        item = searchMenu.AppendRadioItem(-1, "Sample Name")
+        self.Bind(wx.EVT_MENU, self.OnSearchMenu, item)
+        item = searchMenu.AppendRadioItem(-1, "Sample Content")
+        self.Bind(wx.EVT_MENU, self.OnSearchMenu, item)
+        self.filter.SetMenu(searchMenu)
 
-        self.root = self.tree.AddRoot("wxPython Overview")
         self.RecreateTree()
-        self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
-        self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
-        self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
+        self.tree.SetExpansionState(self.expansionState)
+        self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded)
+        self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed)
+        self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged)
         self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
         
         # Set up a wx.html.HtmlWindow on the Overview Notebook page
         # we put it in a panel first because there seems to be a
         # refresh bug of some sort (wxGTK) when it is directly in
         # the notebook...
+        
         if 0:  # the old way
             self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400))
-            self.nb.AddPage(self.ovr, self.overviewText)
+            self.nb.AddPage(self.ovr, self.overviewText, imageId=0)
 
         else:  # hopefully I can remove this hacky code soon, see SF bug #216861
             panel = wx.Panel(self.nb, -1, style=wx.CLIP_CHILDREN)
             self.ovr = wx.html.HtmlWindow(panel, -1, size=(400, 400))
-            self.nb.AddPage(panel, self.overviewText)
+            self.nb.AddPage(panel, self.overviewText, imageId=0)
 
             def OnOvrSize(evt, ovr=self.ovr):
                 ovr.SetSize(evt.GetSize())
@@ -1290,7 +1285,7 @@ class wxPythonDemo(wx.Frame):
 
 
         # Set up a log window
-        self.log = wx.TextCtrl(splitter2, -1,
+        self.log = wx.TextCtrl(self, -1,
                               style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
         if wx.Platform == "__WXMAC__":
             self.log.MacCheckSpelling(False)
@@ -1305,30 +1300,15 @@ class wxPythonDemo(wx.Frame):
         #wx.Log_SetActiveTarget(wx.LogStderr())
         #wx.Log_SetTraceMask(wx.TraceMessages)
 
-
         self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
         wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate)
 
         # add the windows to the splitter and split it.
-        splitter2.SplitHorizontally(self.nb, self.log, -160)
         leftBox = wx.BoxSizer(wx.VERTICAL)
         leftBox.Add(self.tree, 1, wx.EXPAND)
         leftBox.Add(wx.StaticText(leftPanel, label = "Filter Demos:"), 0, wx.TOP|wx.LEFT, 5)
         leftBox.Add(self.filter, 0, wx.EXPAND|wx.ALL, 5)
         leftPanel.SetSizer(leftBox)
-        splitter.SplitVertically(leftPanel, splitter2, 220)
-
-        splitter.SetMinimumPaneSize(120)
-        splitter2.SetMinimumPaneSize(60)
-
-        # Make the splitter on the right expand the top window when resized
-        def SplitterOnSize(evt):
-            splitter = evt.GetEventObject()
-            sz = splitter.GetSize()
-            splitter.SetSashPosition(sz.height - 160, False)
-            evt.Skip()
-
-        splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
 
         # select initial items
         self.nb.SetSelection(0)
@@ -1348,32 +1328,289 @@ class wxPythonDemo(wx.Frame):
                 self.tree.SelectItem(selectedDemo)
                 self.tree.EnsureVisible(selectedDemo)
 
+        # Use the aui manager to set up everything
+        self.mgr.AddPane(self.nb, wx.aui.AuiPaneInfo().CenterPane().Name("Notebook"))
+        self.mgr.AddPane(leftPanel,
+                         wx.aui.AuiPaneInfo().
+                         Left().Layer(2).BestSize((240, -1)).
+                         MinSize((160, -1)).
+                         Floatable(ALLOW_AUI_FLOATING).FloatingSize((240, 700)).
+                         Caption("wxPython Demos").
+                         CloseButton(False).
+                         Name("DemoTree"))
+        self.mgr.AddPane(self.log,
+                         wx.aui.AuiPaneInfo().
+                         Bottom().BestSize((-1, 150)).
+                         MinSize((-1, 60)).
+                         Floatable(ALLOW_AUI_FLOATING).FloatingSize((500, 160)).
+                         Caption("Demo Log Messages").
+                         CloseButton(False).
+                         Name("LogWindow"))
+
+        self.auiConfigurations[DEFAULT_PERSPECTIVE] = self.mgr.SavePerspective()
+        self.mgr.Update()
+
+        self.mgr.SetFlags(self.mgr.GetFlags() ^ wx.aui.AUI_MGR_TRANSPARENT_DRAG)
+        
+
 
-    #---------------------------------------------
-    
+    def ReadConfigurationFile(self):
+
+        self.auiConfigurations = {}
+        self.expansionState = [0, 1]
+
+        config = GetConfig()
+        val = config.Read('ExpansionState')
+        if val:
+            self.expansionState = eval(val)
+
+        val = config.Read('AUIPerspectives')
+        if val:
+            self.auiConfigurations = eval(val)
+        
+
+    def BuildMenuBar(self):
+
+        # Make a File menu
+        self.mainmenu = wx.MenuBar()
+        menu = wx.Menu()
+        item = menu.Append(-1, '&Redirect Output',
+                           'Redirect print statements to a window',
+                           wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
+        exitItem = wx.MenuItem(menu, -1, 'E&xit\tCtrl-Q', 'Get the heck outta here!')
+        exitItem.SetBitmap(images.catalog['exit'].getBitmap())
+        menu.AppendItem(exitItem)
+        self.Bind(wx.EVT_MENU, self.OnFileExit, exitItem)
+        wx.App.SetMacExitMenuItemId(exitItem.GetId())
+        self.mainmenu.Append(menu, '&File')
+
+        # Make a Demo menu
+        menu = wx.Menu()
+        for indx, item in enumerate(_treeList[:-1]):
+            menuItem = wx.MenuItem(menu, -1, item[0])
+            submenu = wx.Menu()
+            for childItem in item[1]:
+                mi = submenu.Append(-1, childItem)
+                self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
+            menuItem.SetBitmap(images.catalog[_demoPngs[indx+1]].getBitmap())
+            menuItem.SetSubMenu(submenu)
+            menu.AppendItem(menuItem)
+        self.mainmenu.Append(menu, '&Demo')
+
+        # Make an Option menu
+        # If we've turned off floatable panels then this menu is not needed
+        if ALLOW_AUI_FLOATING:
+            menu = wx.Menu()
+            auiPerspectives = self.auiConfigurations.keys()
+            auiPerspectives.sort()
+            perspectivesMenu = wx.Menu()
+            item = wx.MenuItem(perspectivesMenu, -1, DEFAULT_PERSPECTIVE, "Load startup default perspective", wx.ITEM_RADIO)
+            self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)
+            perspectivesMenu.AppendItem(item)
+            for indx, key in enumerate(auiPerspectives):
+                if key == DEFAULT_PERSPECTIVE:
+                    continue
+                item = wx.MenuItem(perspectivesMenu, -1, key, "Load user perspective %d"%indx, wx.ITEM_RADIO)
+                perspectivesMenu.AppendItem(item)
+                self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)
+
+            menu.AppendMenu(wx.ID_ANY, "&AUI Perspectives", perspectivesMenu)
+            self.perspectives_menu = perspectivesMenu
+
+            item = wx.MenuItem(menu, -1, 'Save Perspective', 'Save AUI perspective')
+            item.SetBitmap(images.catalog['saveperspective'].getBitmap())
+            menu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, self.OnSavePerspective, item)
+
+            item = wx.MenuItem(menu, -1, 'Delete Perspective', 'Delete AUI perspective')
+            item.SetBitmap(images.catalog['deleteperspective'].getBitmap())
+            menu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, self.OnDeletePerspective, item)
+
+            menu.AppendSeparator()
+
+            item = wx.MenuItem(menu, -1, 'Restore Tree Expansion', 'Restore the initial tree expansion state')
+            item.SetBitmap(images.catalog['expansion'].getBitmap())
+            menu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, self.OnTreeExpansion, item)
+
+            self.mainmenu.Append(menu, '&Options')
+        
+        # Make a Help menu
+        menu = wx.Menu()
+        findItem = wx.MenuItem(menu, -1, '&Find\tCtrl-F', 'Find in the Demo Code')
+        findItem.SetBitmap(images.catalog['find'].getBitmap())
+        findNextItem = wx.MenuItem(menu, -1, 'Find &Next\tF3', 'Find Next')
+        findNextItem.SetBitmap(images.catalog['findnext'].getBitmap())
+        menu.AppendItem(findItem)
+        menu.AppendItem(findNextItem)
+        menu.AppendSeparator()
+
+        shellItem = wx.MenuItem(menu, -1, 'Open Py&Shell Window\tF5',
+                                'An interactive interpreter window with the demo app and frame objects in the namesapce')
+        shellItem.SetBitmap(images.catalog['pyshell'].getBitmap())
+        menu.AppendItem(shellItem)
+        inspToolItem = wx.MenuItem(menu, -1, 'Open &Widget Inspector\tF6',
+                                   'A tool that lets you browse the live widgets and sizers in an application')
+        inspToolItem.SetBitmap(images.catalog['inspect'].getBitmap())
+        menu.AppendItem(inspToolItem)        
+        menu.AppendSeparator()
+        helpItem = menu.Append(-1, '&About wxPython Demo', 'wxPython RULES!!!')
+        wx.App.SetMacAboutMenuItemId(helpItem.GetId())
+
+        self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
+        self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, inspToolItem)
+        self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
+        self.Bind(wx.EVT_MENU, self.OnHelpFind,  findItem)
+        self.Bind(wx.EVT_MENU, self.OnFindNext,  findNextItem)
+        self.Bind(wx.EVT_FIND, self.OnFind)
+        self.Bind(wx.EVT_FIND_NEXT, self.OnFind)
+        self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
+        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findItem)
+        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findNextItem)
+        self.mainmenu.Append(menu, '&Help')
+        self.SetMenuBar(self.mainmenu)
+
+        if False:
+            # This is another way to set Accelerators, in addition to
+            # using the '\t<key>' syntax in the menu items.
+            aTable = wx.AcceleratorTable([(wx.ACCEL_ALT,  ord('X'), exitItem.GetId()),
+                                          (wx.ACCEL_CTRL, ord('H'), helpItem.GetId()),
+                                          (wx.ACCEL_CTRL, ord('F'), findItem.GetId()),
+                                          (wx.ACCEL_NORMAL, wx.WXK_F3, findnextItem.GetId()),
+                                          (wx.ACCEL_NORMAL, wx.WXK_F9, shellItem.GetId()),
+                                          ])
+            self.SetAcceleratorTable(aTable)
+            
+
+    #---------------------------------------------    
     def RecreateTree(self, evt=None):
+        # Catch the search type (name or content)
+        searchMenu = self.filter.GetMenu().GetMenuItems()
+        fullSearch = searchMenu[1].IsChecked()
+            
+        if evt:
+            if fullSearch:
+                # Do not`scan all the demo files for every char
+                # the user input, use wx.EVT_TEXT_ENTER instead
+                return
+
+        expansionState = self.tree.GetExpansionState()
+
+        current = None
+        item = self.tree.GetSelection()
+        if item:
+            prnt = self.tree.GetItemParent(item)
+            if prnt:
+                current = (self.tree.GetItemText(item),
+                           self.tree.GetItemText(prnt))
+                    
         self.tree.Freeze()
         self.tree.DeleteAllItems()
         self.root = self.tree.AddRoot("wxPython Overview")
+        self.tree.SetItemImage(self.root, 0)
+        self.tree.SetItemPyData(self.root, 0)
+
+        treeFont = self.tree.GetFont()
+        catFont = self.tree.GetFont()
+
+        # The old native treectrl on MSW has a bug where it doesn't
+        # draw all of the text for an item if the font is larger than
+        # the default.  It seems to be clipping the item's label as if
+        # it was the size of the same label in the default font.
+        if 'wxMSW' not in wx.PlatformInfo or wx.GetApp().GetComCtl32Version() >= 600:
+            treeFont.SetPointSize(treeFont.GetPointSize()+2)
+            treeFont.SetWeight(wx.BOLD)
+            catFont.SetWeight(wx.BOLD)
+            
+        self.tree.SetItemFont(self.root, treeFont)
+        
         firstChild = None
+        selectItem = None
         filter = self.filter.GetValue()
+        count = 0
+        
         for category, items in _treeList:
+            count += 1
             if filter:
-                items = [item for item in items if filter.lower() in item.lower()]
+                if fullSearch:
+                    items = self.searchItems[category]
+                else:
+                    items = [item for item in items if filter.lower() in item.lower()]
             if items:
-                child = self.tree.AppendItem(self.root, category)
+                child = self.tree.AppendItem(self.root, category, image=count)
+                self.tree.SetItemFont(child, catFont)
+                self.tree.SetItemPyData(child, count)
                 if not firstChild: firstChild = child
                 for childItem in items:
-                    theDemo = self.tree.AppendItem(child, childItem)
+                    image = count
+                    if DoesModifiedExist(childItem):
+                        image = len(_demoPngs)
+                    theDemo = self.tree.AppendItem(child, childItem, image=image)
+                    self.tree.SetItemPyData(theDemo, count)
                     self.treeMap[childItem] = theDemo
-
+                    if current and (childItem, category) == current:
+                        selectItem = theDemo
+                        
+                    
         self.tree.Expand(self.root)
         if firstChild:
             self.tree.Expand(firstChild)
         if filter:
             self.tree.ExpandAll()
+        elif expansionState:
+            self.tree.SetExpansionState(expansionState)
+        if selectItem:
+            self.skipLoad = True
+            self.tree.SelectItem(selectItem)
+            self.skipLoad = False
+        
         self.tree.Thaw()
-    
+        self.searchItems = {}
+
+
+    def OnSearchMenu(self, event):
+
+        # Catch the search type (name or content)
+        searchMenu = self.filter.GetMenu().GetMenuItems()
+        fullSearch = searchMenu[1].IsChecked()
+        
+        if fullSearch:
+            self.OnSearch()
+        else:
+            self.RecreateTree()
+            
+
+    def OnSearch(self, event=None):
+
+        value = self.filter.GetValue()
+        if not value:
+            self.RecreateTree()
+            return
+
+        wx.BeginBusyCursor()
+        
+        for category, items in _treeList:
+            self.searchItems[category] = []
+            for childItem in items:
+                if SearchDemo(childItem, value):
+                    self.searchItems[category].append(childItem)
+
+        wx.EndBusyCursor()
+        self.RecreateTree()            
+
+
+    def SetTreeModified(self, modified):
+        item = self.tree.GetSelection()
+        if modified:
+            image = len(_demoPngs)
+        else:
+            image = self.tree.GetItemPyData(item)
+        self.tree.SetItemImage(item, image)
+        
+        
     def WriteText(self, text):
         if text[-1:] == '\n':
             text = text[:-1]
@@ -1405,7 +1642,7 @@ class wxPythonDemo(wx.Frame):
 
     #---------------------------------------------
     def OnSelChanged(self, event):
-        if self.dying or  not self.loaded:
+        if self.dying or not self.loaded or self.skipLoad:
             return
 
         item = event.GetItem()
@@ -1416,6 +1653,7 @@ class wxPythonDemo(wx.Frame):
     def LoadDemo(self, demoName):
         try:
             wx.BeginBusyCursor()
+            self.Freeze()
             
             os.chdir(self.cwd)
             self.ShutdownDemoModule()
@@ -1433,13 +1671,13 @@ class wxPythonDemo(wx.Frame):
                     wx.LogMessage("Loading demo %s.py..." % demoName)
                     self.demoModules = DemoModules(demoName)
                     self.LoadDemoSource()
-                    self.tree.Refresh()
                 else:
                     self.SetOverview("wxPython", mainOverview)
                     self.codePage = None
                     self.UpdateNotebook(0)
         finally:
             wx.EndBusyCursor()
+            self.Thaw()
 
     #---------------------------------------------
     def LoadDemoSource(self):
@@ -1470,6 +1708,10 @@ class wxPythonDemo(wx.Frame):
                 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
                                                DemoError(sys.exc_info()), self)
 
+            bg = self.nb.GetThemeBackgroundColour()
+            if bg:
+                self.demoPage.SetBackgroundColour(bg)
+
             assert self.demoPage is not None, "runTest must return a window!"
             
         else:
@@ -1480,7 +1722,7 @@ class wxPythonDemo(wx.Frame):
         self.SetOverview(self.demoModules.name + " Overview", overviewText)
 
         if self.firstTime:
-            # cahnge to the demo page the first time a module is run
+            # change to the demo page the first time a module is run
             self.UpdateNotebook(2)
             self.firstTime = False
         else:
@@ -1500,6 +1742,7 @@ class wxPythonDemo(wx.Frame):
     def UpdateNotebook(self, select = -1):
         nb = self.nb
         debug = False
+        self.Freeze()
         
         def UpdatePage(page, pageText):
             pageExists = False
@@ -1513,15 +1756,13 @@ class wxPythonDemo(wx.Frame):
             if page:
                 if not pageExists:
                     # Add a new page
-                    nb.AddPage(page, pageText)
+                    nb.AddPage(page, pageText, imageId=nb.GetPageCount())
                     if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
                 else:
                     if nb.GetPage(pagePos) != page:
                         # Reload an existing page
-                        nb.Freeze()
                         nb.DeletePage(pagePos)
-                        nb.InsertPage(pagePos, page, pageText)
-                        nb.Thaw()
+                        nb.InsertPage(pagePos, page, pageText, imageId=pagePos)
                         if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
                     else:
                         # Excellent! No redraw/flicker
@@ -1541,7 +1782,9 @@ class wxPythonDemo(wx.Frame):
 
         if select >= 0 and select < nb.GetPageCount():
             nb.SetSelection(select)
-            
+
+        self.Thaw()
+        
     #---------------------------------------------
     def SetOverview(self, name, text):
         self.curOverview = text
@@ -1566,6 +1809,70 @@ class wxPythonDemo(wx.Frame):
         else:
             app.RestoreStdio()
             print "Print statements and other standard output will now be sent to the usual location."
+
+
+    def OnAUIPerspectives(self, event):
+        perspective = self.perspectives_menu.GetLabel(event.GetId())
+        self.mgr.LoadPerspective(self.auiConfigurations[perspective])
+        self.mgr.Update()
+
+
+    def OnSavePerspective(self, event):
+        dlg = wx.TextEntryDialog(self, "Enter a name for the new perspective:", "AUI Configuration")
+        
+        dlg.SetValue(("Perspective %d")%(len(self.auiConfigurations)+1))
+        if dlg.ShowModal() != wx.ID_OK:
+            return
+
+        perspectiveName = dlg.GetValue()
+        menuItems = self.perspectives_menu.GetMenuItems()
+        for item in menuItems:
+            if item.GetLabel() == perspectiveName:
+                wx.MessageBox("The selected perspective name:\n\n%s\n\nAlready exists."%perspectiveName,
+                              "Error", style=wx.ICON_ERROR)
+                return
+                
+        item = wx.MenuItem(self.perspectives_menu, -1, dlg.GetValue(),
+                           "Load user perspective %d"%(len(self.auiConfigurations)+1),
+                           wx.ITEM_RADIO)
+        self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)                
+        self.perspectives_menu.AppendItem(item)
+        item.Check(True)
+        self.auiConfigurations.update({dlg.GetValue(): self.mgr.SavePerspective()})
+
+
+    def OnDeletePerspective(self, event):
+        menuItems = self.perspectives_menu.GetMenuItems()[1:]
+        lst = []
+        loadDefault = False
+        
+        for item in menuItems:
+            lst.append(item.GetLabel())
+            
+        dlg = wx.MultiChoiceDialog(self, 
+                                   "Please select the perspectives\nyou would like to delete:",
+                                   "Delete AUI Perspectives", lst)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            selections = dlg.GetSelections()
+            strings = [lst[x] for x in selections]
+            for sel in strings:
+                self.auiConfigurations.pop(sel)
+                item = menuItems[lst.index(sel)]
+                if item.IsChecked():
+                    loadDefault = True
+                    self.perspectives_menu.GetMenuItems()[0].Check(True)
+                self.perspectives_menu.DeleteItem(item)
+                lst.remove(sel)
+
+        if loadDefault:
+            self.mgr.LoadPerspective(self.auiConfigurations[DEFAULT_PERSPECTIVE])
+            self.mgr.Update()
+
+
+    def OnTreeExpansion(self, event):
+        self.tree.SetExpansionState(self.expansionState)
+        
  
     def OnHelpAbout(self, event):
         from About import MyAboutBox
@@ -1686,6 +1993,12 @@ class wxPythonDemo(wx.Frame):
         self.mainmenu = None
         if self.tbicon is not None:
             self.tbicon.Destroy()
+
+        config = GetConfig()
+        config.Write('ExpansionState', str(self.tree.GetExpansionState()))
+        config.Write('AUIPerspectives', str(self.auiConfigurations))
+        config.Flush()
+
         self.Destroy()
 
 
@@ -1699,18 +2012,20 @@ class wxPythonDemo(wx.Frame):
 
     #---------------------------------------------
     def ShowTip(self):
-        try:
-            showTipText = open(opj("data/showTips")).read()
+        config = GetConfig()
+        showTipText = config.Read("tips")
+        if showTipText:
             showTip, index = eval(showTipText)
-        except IOError:
+        else:
             showTip, index = (1, 0)
+            
         if showTip:
             tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
             ##tp = MyTP(0)
             showTip = wx.ShowTip(self, tp)
             index = tp.GetCurrentTip()
-            open(opj("data/showTips"), "w").write(str( (showTip, index) ))
-
+            config.Write("tips", str( (showTip, index) ))
+            config.Flush()
 
     #---------------------------------------------
     def OnDemoMenu(self, event):
@@ -1775,8 +2090,54 @@ class MySplashScreen(wx.SplashScreen):
         frame.Show()
         if self.fc.IsRunning():
             self.Raise()
+        wx.CallAfter(frame.ShowTip)
+
+
+
+
+#---------------------------------------------------------------------------
+
+from wx.lib.mixins.treemixin import ExpansionState
+if USE_CUSTOMTREECTRL:
+    import wx.lib.customtreectrl as CT
+    TreeBaseClass = CT.CustomTreeCtrl
+else:
+    TreeBaseClass = wx.TreeCtrl
+    
+
+class wxPythonDemoTree(ExpansionState, TreeBaseClass):
+    def __init__(self, parent):
+        TreeBaseClass.__init__(self, parent, style=wx.TR_DEFAULT_STYLE|
+                               wx.TR_HAS_VARIABLE_ROW_HEIGHT)
+        self.BuildTreeImageList()
+        if USE_CUSTOMTREECTRL:
+            self.SetSpacing(10)
+            self.SetWindowStyle(self.GetWindowStyle() & ~wx.TR_LINES_AT_ROOT)
+
+    def AppendItem(self, parent, text, image=-1, wnd=None):
+        if USE_CUSTOMTREECTRL:
+            item = TreeBaseClass.AppendItem(self, parent, text, image=image, wnd=wnd)
+        else:
+            item = TreeBaseClass.AppendItem(self, parent, text, image=image)
+        return item
+            
+    def BuildTreeImageList(self):
+        imgList = wx.ImageList(16, 16)
+        for png in _demoPngs:
+            imgList.Add(images.catalog[png].getBitmap())
+            
+        # add the image for modified demos.
+        imgList.Add(images.catalog["custom"].getBitmap())
 
+        self.AssignImageList(imgList)
         
+
+    def GetItemIdentity(self, item):
+        return self.GetPyData(item)
+
+
+#---------------------------------------------------------------------------
+
 class MyApp(wx.App):
     def OnInit(self):
         """
@@ -1785,7 +2146,8 @@ class MyApp(wx.App):
         """
 
         wx.SystemOptions.SetOptionInt("mac.window-plain-transition", 1)
-
+        self.SetAppName("wxPyDemo")
+        
         # For debugging
         #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
 
index 28a488584b2f921cbb254119ce68e30e594f1be2..6926f8da5ad62fdaf488759cc73ed2c215f815bd 100644 (file)
@@ -54,6 +54,7 @@ The controls at the top reconfigure the resulting control at the bottom.
 
 
         self.limit_target = wx.CheckBox( panel, -1, "Limit control" )
+        self.limit_on_field_change = wx.CheckBox( panel, -1, "Limit on field change" )
         self.allow_none = wx.CheckBox( panel, -1, "Allow empty control" )
         self.group_digits = wx.CheckBox( panel, -1, "Group digits" )
         self.group_digits.SetValue( True )
@@ -102,21 +103,26 @@ value entry ctrl:""")
 
 
         grid1.Add( self.limit_target, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
-        grid1.Add( self.allow_none, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
+        grid1.Add( self.limit_on_field_change, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
+        
         hbox1 = wx.BoxSizer( wx.HORIZONTAL )
         hbox1.Add( (17,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
-        hbox1.Add( self.group_digits, 0, wx.ALIGN_LEFT|wx.LEFT, 5 )
+        hbox1.Add( self.allow_none, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
         grid1.Add( hbox1, 0, wx.ALIGN_LEFT|wx.ALL, 5)
         grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
 
-        grid1.Add( self.allow_negative, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
-        grid1.Add( self.use_parens, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
+        grid1.Add( self.group_digits, 0, wx.ALIGN_LEFT|wx.LEFT, 5 )
+        grid1.Add( self.allow_negative, 0, wx.ALIGN_LEFT|wx.ALL, 5 )        
         hbox2 = wx.BoxSizer( wx.HORIZONTAL )
         hbox2.Add( (17,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
-        hbox2.Add( self.select_on_entry, 0, wx.ALIGN_LEFT|wx.LEFT, 5 )
+        hbox2.Add( self.use_parens, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
         grid1.Add( hbox2, 0, wx.ALIGN_LEFT|wx.ALL, 5)
         grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
-
+        
+        grid1.Add( self.select_on_entry, 0, wx.ALIGN_LEFT|wx.LEFT, 5 )
+        grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
+        grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
+        grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
 
         grid2 = wx.FlexGridSizer( 0, 2, 0, 0 )
         grid2.Add( label, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
@@ -151,7 +157,8 @@ value entry ctrl:""")
         self.Bind(masked.EVT_NUM, self.SetTargetMinMax, self.min )
         self.Bind(masked.EVT_NUM, self.SetTargetMinMax, self.max )
 
-        self.Bind(wx.EVT_CHECKBOX, self.SetTargetMinMax, self.limit_target )
+        self.Bind(wx.EVT_CHECKBOX, self.OnSetLimited, self.limit_target )
+        self.Bind(wx.EVT_CHECKBOX, self.OnSetLimitOnFieldChange, self.limit_on_field_change )
         self.Bind(wx.EVT_CHECKBOX, self.OnSetAllowNone, self.allow_none )
         self.Bind(wx.EVT_CHECKBOX, self.OnSetGroupDigits, self.group_digits )
         self.Bind(wx.EVT_CHECKBOX, self.OnSetAllowNegative, self.allow_negative )
@@ -218,10 +225,26 @@ value entry ctrl:""")
         self.SetTargetMinMax()
 
 
+    def OnSetLimited( self, event ):
+        limited = self.limit_target.GetValue()
+        self.target_ctl.SetLimited( limited )
+        limit_on_field_change = self.limit_on_field_change.GetValue()
+        if limited and limit_on_field_change:
+            self.limit_on_field_change.SetValue(False)
+            self.target_ctl.SetLimitOnFieldChange( False )
+        self.SetTargetMinMax()
+
+
+    def OnSetLimitOnFieldChange( self, event ):
+        limit_on_field_change = self.limit_on_field_change.GetValue()
+        self.target_ctl.SetLimitOnFieldChange( limit_on_field_change )
+        limited = self.limit_target.GetValue()
+        if limited and limit_on_field_change:
+            self.limit_target.SetValue(False)
+            self.target_ctl.SetLimited( False )
+
     def SetTargetMinMax( self, event=None ):
         min = max = None
-        self.target_ctl.SetLimited( self.limit_target.GetValue() )
-
         if self.set_min.GetValue():
             min = self.min.GetValue()
         if self.set_max.GetValue():
index 871e6bc9cb416263ee6b8dc7a316a3e746a8646b..f0709c75a91973fbba200f3b08f9eaf3b5045f62 100644 (file)
@@ -211,7 +211,8 @@ class MyEvtHandler(ogl.ShapeEvtHandler):
 
         if shape.Selected():
             shape.Select(False, dc)
-            canvas.Redraw(dc)
+            #canvas.Redraw(dc)
+            canvas.Refresh(False)
         else:
             redraw = False
             shapeList = canvas.GetDiagram().GetShapeList()
@@ -230,7 +231,8 @@ class MyEvtHandler(ogl.ShapeEvtHandler):
                 for s in toUnselect:
                     s.Select(False, dc)
 
-                canvas.Redraw(dc)
+                ##canvas.Redraw(dc)
+                canvas.Refresh(False)
 
         self.UpdateStatusBar(shape)
 
@@ -251,9 +253,11 @@ class MyEvtHandler(ogl.ShapeEvtHandler):
 
 
     def OnMovePost(self, dc, x, y, oldX, oldY, display):
+        shape = self.GetShape()
         ogl.ShapeEvtHandler.OnMovePost(self, dc, x, y, oldX, oldY, display)
-        self.UpdateStatusBar(self.GetShape())
-
+        self.UpdateStatusBar(shape)
+        if "wxMac" in wx.PlatformInfo:
+            shape.GetCanvas().Refresh(False)
 
     def OnRightClick(self, *dontcare):
         self.log.WriteText("%s\n" % self.GetShape())
@@ -334,8 +338,8 @@ class TestWindow(ogl.ShapeCanvas):
         s.SetBitmap(bmp)
         self.MyAddShape(s, 225, 130, None, None, "Bitmap")
 
-        dc = wx.ClientDC(self)
-        self.PrepareDC(dc)
+        #dc = wx.ClientDC(self)
+        #self.PrepareDC(dc)
 
         for x in range(len(self.shapes)):
             fromShape = self.shapes[x]
index 7c31599a3f6238b968eb40307c91d5c729290efd..059ddd617935fb5f8c14fa686cd257046d470927 100644 (file)
@@ -1,6 +1,10 @@
 import wx, wx.lib.customtreectrl, wx.gizmos
-import treemixin
+try:
+    import treemixin 
+except ImportError:
+    from wx.lib.mixins import treemixin
 
+overview = treemixin.__doc__
 
 class TreeModel(object):
     ''' TreeModel holds the domain objects that are shown in the different
diff --git a/wxPython/demo/bmp_source/book.png b/wxPython/demo/bmp_source/book.png
new file mode 100644 (file)
index 0000000..7d863f9
Binary files /dev/null and b/wxPython/demo/bmp_source/book.png differ
diff --git a/wxPython/demo/bmp_source/clipboard.png b/wxPython/demo/bmp_source/clipboard.png
new file mode 100644 (file)
index 0000000..8e8b106
Binary files /dev/null and b/wxPython/demo/bmp_source/clipboard.png differ
diff --git a/wxPython/demo/bmp_source/code.png b/wxPython/demo/bmp_source/code.png
new file mode 100644 (file)
index 0000000..8fcf0f0
Binary files /dev/null and b/wxPython/demo/bmp_source/code.png differ
diff --git a/wxPython/demo/bmp_source/core.png b/wxPython/demo/bmp_source/core.png
new file mode 100644 (file)
index 0000000..1bbc1d3
Binary files /dev/null and b/wxPython/demo/bmp_source/core.png differ
diff --git a/wxPython/demo/bmp_source/custom.png b/wxPython/demo/bmp_source/custom.png
new file mode 100644 (file)
index 0000000..c1974cd
Binary files /dev/null and b/wxPython/demo/bmp_source/custom.png differ
diff --git a/wxPython/demo/bmp_source/deleteperspective.png b/wxPython/demo/bmp_source/deleteperspective.png
new file mode 100644 (file)
index 0000000..6d0d29d
Binary files /dev/null and b/wxPython/demo/bmp_source/deleteperspective.png differ
diff --git a/wxPython/demo/bmp_source/demo.png b/wxPython/demo/bmp_source/demo.png
new file mode 100644 (file)
index 0000000..47c73fc
Binary files /dev/null and b/wxPython/demo/bmp_source/demo.png differ
diff --git a/wxPython/demo/bmp_source/dialog.png b/wxPython/demo/bmp_source/dialog.png
new file mode 100644 (file)
index 0000000..bf02592
Binary files /dev/null and b/wxPython/demo/bmp_source/dialog.png differ
diff --git a/wxPython/demo/bmp_source/exit.png b/wxPython/demo/bmp_source/exit.png
new file mode 100644 (file)
index 0000000..6323241
Binary files /dev/null and b/wxPython/demo/bmp_source/exit.png differ
diff --git a/wxPython/demo/bmp_source/expansion.png b/wxPython/demo/bmp_source/expansion.png
new file mode 100644 (file)
index 0000000..477387c
Binary files /dev/null and b/wxPython/demo/bmp_source/expansion.png differ
diff --git a/wxPython/demo/bmp_source/find.png b/wxPython/demo/bmp_source/find.png
new file mode 100644 (file)
index 0000000..caa587d
Binary files /dev/null and b/wxPython/demo/bmp_source/find.png differ
diff --git a/wxPython/demo/bmp_source/findnext.png b/wxPython/demo/bmp_source/findnext.png
new file mode 100644 (file)
index 0000000..f5a4e1d
Binary files /dev/null and b/wxPython/demo/bmp_source/findnext.png differ
diff --git a/wxPython/demo/bmp_source/frame.png b/wxPython/demo/bmp_source/frame.png
new file mode 100644 (file)
index 0000000..4f9be45
Binary files /dev/null and b/wxPython/demo/bmp_source/frame.png differ
diff --git a/wxPython/demo/bmp_source/images.png b/wxPython/demo/bmp_source/images.png
new file mode 100644 (file)
index 0000000..cc4a4b9
Binary files /dev/null and b/wxPython/demo/bmp_source/images.png differ
diff --git a/wxPython/demo/bmp_source/inspect.png b/wxPython/demo/bmp_source/inspect.png
new file mode 100644 (file)
index 0000000..1dd74be
Binary files /dev/null and b/wxPython/demo/bmp_source/inspect.png differ
diff --git a/wxPython/demo/bmp_source/layout.png b/wxPython/demo/bmp_source/layout.png
new file mode 100644 (file)
index 0000000..b4aaad9
Binary files /dev/null and b/wxPython/demo/bmp_source/layout.png differ
diff --git a/wxPython/demo/bmp_source/miscellaneous.png b/wxPython/demo/bmp_source/miscellaneous.png
new file mode 100644 (file)
index 0000000..f06e510
Binary files /dev/null and b/wxPython/demo/bmp_source/miscellaneous.png differ
diff --git a/wxPython/demo/bmp_source/modifiedexists.png b/wxPython/demo/bmp_source/modifiedexists.png
new file mode 100644 (file)
index 0000000..99bec7a
Binary files /dev/null and b/wxPython/demo/bmp_source/modifiedexists.png differ
diff --git a/wxPython/demo/bmp_source/morecontrols.png b/wxPython/demo/bmp_source/morecontrols.png
new file mode 100644 (file)
index 0000000..31d03a5
Binary files /dev/null and b/wxPython/demo/bmp_source/morecontrols.png differ
diff --git a/wxPython/demo/bmp_source/moredialog.png b/wxPython/demo/bmp_source/moredialog.png
new file mode 100644 (file)
index 0000000..6e61e0e
Binary files /dev/null and b/wxPython/demo/bmp_source/moredialog.png differ
diff --git a/wxPython/demo/bmp_source/overview.png b/wxPython/demo/bmp_source/overview.png
new file mode 100644 (file)
index 0000000..12ec79e
Binary files /dev/null and b/wxPython/demo/bmp_source/overview.png differ
diff --git a/wxPython/demo/bmp_source/process.png b/wxPython/demo/bmp_source/process.png
new file mode 100644 (file)
index 0000000..2206448
Binary files /dev/null and b/wxPython/demo/bmp_source/process.png differ
diff --git a/wxPython/demo/bmp_source/pyshell.png b/wxPython/demo/bmp_source/pyshell.png
new file mode 100644 (file)
index 0000000..4eaa79a
Binary files /dev/null and b/wxPython/demo/bmp_source/pyshell.png differ
diff --git a/wxPython/demo/bmp_source/recent.png b/wxPython/demo/bmp_source/recent.png
new file mode 100644 (file)
index 0000000..6a9bf03
Binary files /dev/null and b/wxPython/demo/bmp_source/recent.png differ
diff --git a/wxPython/demo/bmp_source/saveperspective.png b/wxPython/demo/bmp_source/saveperspective.png
new file mode 100644 (file)
index 0000000..8d8bd6f
Binary files /dev/null and b/wxPython/demo/bmp_source/saveperspective.png differ
index d2b5402c3691c06641107747a02bf8e449c3a5b2..39366991d5e8e5ba41c353c063823b8453d93e9a 100644 (file)
@@ -100,6 +100,34 @@ command_lines = [
     "-a -u -n _book_green bmp_source/book_green.png images.py",
     "-a -u -n _book_blue  bmp_source/book_blue.png  images.py",
 
+    "-a -u -c bmp_source/book.png              images.py",
+    "-a -u -c bmp_source/clipboard.png         images.py",
+    "-a -u -c bmp_source/code.png              images.py",
+    "-a -u -c bmp_source/core.png              images.py",
+    "-a -u -c bmp_source/custom.png            images.py",
+    "-a -u -c bmp_source/deleteperspective.png images.py",
+    "-a -u -c bmp_source/demo.png              images.py",
+    "-a -u -c bmp_source/dialog.png            images.py",
+    "-a -u -c bmp_source/exit.png              images.py",
+    "-a -u -c bmp_source/expansion.png         images.py",
+    "-a -u -c bmp_source/find.png              images.py",
+    "-a -u -c bmp_source/findnext.png          images.py",
+    "-a -u -c bmp_source/frame.png             images.py",
+    "-a -u -c bmp_source/images.png            images.py",
+    "-a -u -c bmp_source/inspect.png           images.py",
+    "-a -u -c bmp_source/layout.png            images.py",
+    "-a -u -c bmp_source/miscellaneous.png     images.py",
+    "-a -u -c bmp_source/modifiedexists.png    images.py",
+    "-a -u -c bmp_source/morecontrols.png      images.py",
+    "-a -u -c bmp_source/moredialog.png        images.py",
+    "-a -u -c bmp_source/overview.png          images.py",
+    "-a -u -c bmp_source/process.png           images.py",
+    "-a -u -c bmp_source/pyshell.png           images.py",
+    "-a -u -c bmp_source/recent.png            images.py",
+    "-a -u -c bmp_source/saveperspective.png   images.py",
+    "-a -u -c bmp_source/customcontrol.png     images.py",
+
+
     "   -u -c bmp_source/001.png throbImages.py",
     "-a -u -c bmp_source/002.png throbImages.py",
     "-a -u -c bmp_source/003.png throbImages.py",
index bd6d97a0653463d4f1179ac4ca69d2306556a160..26908d2b5fd0c277eabe5d06c1998fdacd9989a5 100644 (file)
@@ -123,6 +123,7 @@ wxPython/wx/lib/art
 wxPython/wx/lib/colourchooser
 wxPython/wx/lib/editor
 wxPython/wx/lib/floatcanvas
+wxPython/wx/lib/floatcanvas/Utilities
 wxPython/wx/lib/masked
 wxPython/wx/lib/mixins
 wxPython/wx/lib/ogl
index 69f7b239c501b3da733dd87d8e3a8bd0957e0281..b502ae385f2edd1179591fc5c764659ede2d9f94 100644 (file)
@@ -1,7 +1,7 @@
 wxPython win32 README
 ---------------------
 
-The self-installer pacakge you have just installed contains the Python
+The self-installer package you have just installed contains the Python
 extension modules, python modules and packages needed to run wxPython
 applications.  If you selected the "Make this install be the default
 wxPython" option in the installer then this version will be the one
index 425c9acbd5414878cacc785d6dae712a86e2454f..2b117872b90b5621fffd248b3ae258145c1c94df 100755 (executable)
@@ -57,7 +57,10 @@ def getTasks(config_env):
                               [config.OSX_HOST_panther, "2.3", "both"], env=config_env),
                           Job("bigmac.24",
                               "distrib/all/build-osx",
-                              [config.OSX_HOST_panther, "2.4", "both"], env=config_env)
+                              [config.OSX_HOST_panther, "2.4", "both"], env=config_env),
+                          Job("bigmac.25",
+                              "distrib/all/build-osx",
+                              [config.OSX_HOST_panther, "2.5", "both"], env=config_env)
                           ])
 
     tigerTask =  Task([ #Job("smallfry.23",
@@ -88,9 +91,16 @@ def getTasks(config_env):
 #          Job("co-mdk2006.24","distrib/all/build-rpm", ["beast", "co-mdk2006", "mdk2006", "2.4"], env=config_env),
           ])
 
-    xavierTask = Task([
-        Job("xavier.d", "distrib/all/build-deb", ["xavier", "/work/chroot/dapper", "dapper"], env=config_env),
-        Job("xavier.f", "distrib/all/build-deb", ["xavier", "/work/chroot/feisty", "feisty"], env=config_env),
+    cyclopsTask1 = Task([
+        Job("cyclops.d", "distrib/all/build-deb", ["cyclops", "/work/chroot/dapper", "dapper"], env=config_env),
+        Job("cyclops.d64", "distrib/all/build-deb", ["cyclops", "/work/chroot/dapper64", "dapper64"], env=config_env),
+        Job("cyclops.fc6", "distrib/all/build-chrpm",
+            ["cyclops", "/work/chroot/fc6", "fc6", "fc6", "2.4"], env=config_env),
+        ])
+
+    cyclopsTask2 = Task([
+        Job("cyclops.f", "distrib/all/build-deb", ["cyclops", "/work/chroot/feisty", "feisty"], env=config_env),
+        Job("cyclops.f64", "distrib/all/build-deb", ["cyclops", "/work/chroot/feisty64", "feisty64"], env=config_env),
         ])
 
     buildTasks = [ #jaguarTask,
@@ -98,7 +108,8 @@ def getTasks(config_env):
                    tigerTask,
                    beastTask1,
                    beastTask2,
-                   xavierTask,
+                   cyclopsTask1,
+                   cyclopsTask2,
                    ]
     
     # Finalization.  This is for things that must wait until all the
diff --git a/wxPython/distrib/all/build-chrpm b/wxPython/distrib/all/build-chrpm
new file mode 100755 (executable)
index 0000000..f8e2928
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/bash
+#----------------------------------------------------------------------
+
+set -o errexit
+#set -o xtrace
+
+host=$1
+chRootRoot=$2
+chRootName=$3
+reltag=$4
+pyver=$5
+
+
+if [ $buildansi = yes ]; then
+    CHARTYPE=both
+else
+    CHARTYPE=unicode
+fi
+
+
+function TestOnline {
+    local host=$1
+    local message=$2
+
+    if ping -q -c1 -w1 $host > /dev/null; then
+       return 0
+    else
+       return 1
+    fi
+}
+
+
+
+if [ $skipdeb != yes ]; then
+    # We use a chroot environment on the build machine for the debian
+    # builds, so this build is pretty simple.  Just copy the tarball
+    # and a build script to the build machine, and then run
+    # do-build-deb in the chroot.
+
+    if TestOnline $host; then
+    
+       echo "The $host machine is online, build continuing..."
+
+       echo "Copying source files and build script..."
+       ssh root@$host "mkdir -p $chRootRoot/$LINUX_BUILD && rm -rf $chRootRoot/$LINUX_BUILD/*"
+       scp $STAGING_DIR/wxPython-src* distrib/all/do-build-rpm $STAGING_DIR/wxPython.spec \
+            root@$host:$chRootRoot/$LINUX_BUILD
+    
+       ssh root@$host "dchroot --chroot $chRootName --directory $LINUX_BUILD \"./do-build-rpm $reltag $skipclean $VERSION $pyver $CHARTYPE\""
+
+       echo "Fetching the results..."
+       ssh root@$host "rm $chRootRoot/$LINUX_BUILD/do-build-rpm"
+       scp "root@$host:$chRootRoot/$LINUX_BUILD/wxPython*.i[0-9]86.rpm"  $STAGING_DIR
+       ssh root@$host "rm $chRootRoot/$LINUX_BUILD/wxPython*.i[0-9]86.rpm"
+       echo "Done!"
+    else
+       echo "The $host machine is **OFFLINE**, skipping the binary RPM build."
+       exit 0
+    fi
+fi
+
index 7811ed143247c7e12ef629c4dc5e67b805aeb217..290fdfa84e695cb61703a9bcb7143e0137886da0 100644 (file)
@@ -121,6 +121,7 @@ Source: "wx\lib\mixins\*.py";                  DestDir: "{app}\%(PKGDIR)s\wx\lib
 Source: "wx\lib\masked\*.py";                  DestDir: "{app}\%(PKGDIR)s\wx\lib\masked"; Components: core
 Source: "wx\lib\ogl\*.py";                     DestDir: "{app}\%(PKGDIR)s\wx\lib\ogl"; Components: core
 Source: "wx\lib\floatcanvas\*.py";             DestDir: "{app}\%(PKGDIR)s\wx\lib\floatcanvas"; Components: core
+Source: "wx\lib\floatcanvas\Utilities\*.py";   DestDir: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\Utilities"; Components: core
 Source: "wx\py\*.py";                          DestDir: "{app}\%(PKGDIR)s\wx\py"; Components: core
 Source: "wx\py\*.txt";                         DestDir: "{app}\%(PKGDIR)s\wx\py"; Components: core
 Source: "wx\py\*.ico";                         DestDir: "{app}\%(PKGDIR)s\wx\py"; Components: core
@@ -216,6 +217,8 @@ Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\ogl\*.pyc";
 Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\ogl\*.pyo";
 Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\*.pyc";
 Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\*.pyo";
+Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\Utilities\*.pyc";
+Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\Utilities\*.pyo";
 
 Type: files; Name: "{app}\%(PKGDIR)s\wx\py\*.pyc";
 Type: files; Name: "{app}\%(PKGDIR)s\wx\py\*.pyo";
index e8fd57a3d2760240d21ba2a5905f4648dda2b660..2227a3c7fe900a8f1e94d248a0237f733cac9a8a 100644 (file)
@@ -86,6 +86,10 @@ did rely on the old behaviour you will have to update your code to set
 the minimal sizes of the sizer items to be in the same proportion as
 the items proportions to return to the old behaviour.
 
+Added support for toolbar buttons with dropdown menus.
+
+
+
 
 
 
diff --git a/wxPython/misc/image.png b/wxPython/misc/image.png
deleted file mode 100644 (file)
index 5a4ce74..0000000
Binary files a/wxPython/misc/image.png and /dev/null differ
diff --git a/wxPython/misc/widgetLayoutTest.cfg b/wxPython/misc/widgetLayoutTest.cfg
deleted file mode 100644 (file)
index fa9661b..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-[
-['wx', 'BitmapButton', '-1, wx.Bitmap("image.png")', ''],
-['wx', 'Button', '-1, "normal"', ''],
-['wx', 'Button', '-1, "with a longer, longer label"', ''],
-['wx', 'Button', '-1, "default"', 'w.SetDefault()'],
-['wx', 'Button', '-1, "larger font"', 'w.SetFont(wx.Font(20, wx.SWISS, wx.NORMAL, wx.NORMAL))\n\n\n'],
-['wx', 'CheckBox', '-1, "checkbox"', ''],
-['wx', 'CheckBox', '-1, "checkbox with longer label"', ''],
-['wx', 'CheckListBox', '-1, size=(100,-1), choices="one two three four five six seven eight".split()', ''],
-['wx', 'Choice', '-1, choices="one two three four five six seven eight".split()', ''],
-['wx', 'Choice', '-1, size=(50,-1), choices="one two three four five six seven eight".split()', ''],
-['wx', 'ComboBox', '-1, value="default", choices="one two three four five six seven eight".split()', ''],
-['wx', 'ComboBox', '-1, value="default", size=(75,-1), choices="one two three four five six seven eight".split()', ''],
-['wx', 'ComboBox', '-1, value="read-only", style=wx.CB_READONLY, choices="read-only one two three four five six seven eight".split()', ''],
-['wx', 'Gauge', '-1, 100', 'w.SetValue(40)'],
-['wx', 'Gauge', '-1, 100, style=wx.GA_VERTICAL', 'w.SetValue(60)'],
-['wx', 'ListBox', '-1, choices="one two three four five six seven eight".split()', ''],
-['wx', 'ListCtrl', 'style=wx.LC_REPORT', 'w.InsertColumn(0, "Col1")\nw.InsertStringItem(0, "Item 0")\nw.InsertStringItem(0, "Item 1")\n#w.SetSizeHints((200,100))'],
-['wx', 'ListCtrl', 'style=wx.LC_REPORT, size=(100,100)', 'w.InsertColumn(0, "Col1")\nw.InsertStringItem(0, "Item 0")\nw.InsertStringItem(0, "Item 1")\n#w.SetSizeHints((200,100))'],
-['wx', 'Notebook', '', 'p = wx.Panel(w)\np.SetMinSize((150,150))\nw.AddPage(p, "test")\n'],
-['wx', 'Panel', 'size=(150,150)', ''],
-['wx', 'RadioBox', '-1, "label", majorDimension=2, choices="one two three four five six seven eight".split()', ''],
-['wx', 'RadioBox', '-1, "label", style=wx.RA_VERTICAL, majorDimension=2, choices="one two three four five six seven eight".split()', ''],
-['wx', 'RadioBox', '-1, "label", choices="one two three four".split()', ''],
-['wx', 'RadioBox', '-1, "label", style=wx.RA_VERTICAL, choices="one two three four".split()', ''],
-['wx', 'RadioButton', '-1, "radio button"', ''],
-['wx', 'ScrollBar', '', ''],
-['wx', 'ScrollBar', 'style=wx.SB_VERTICAL', ''],
-['wx', 'Slider', '-1, 20, 0, 100', ''],
-['wx', 'Slider', '-1, 20, 0, 100, size=(400, -1)', ''],
-['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_LABELS', ''],
-['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_LABELS, size=(400,-1)', ''],
-['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_VERTICAL', ''],
-['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_VERTICAL|wx.SL_LABELS', ''],
-['wx', 'SpinButton', 'style=wx.SP_HORIZONTAL', ''],
-['wx', 'SpinButton', 'style=wx.SP_VERTICAL', ''],
-['wx', 'SpinCtrl', '', ''],
-['wx', 'SpinCtrl', 'size=(50, -1)', ''],
-['wx', 'StaticBitmap', '-1, wx.Bitmap("image.png")', ''],
-['wx', 'StaticBox', '-1, "a longer label"', ''],
-['wx', 'StaticBox', '-1, "label"', ''],
-['wx', 'StaticBox', '-1, "with a size", size=(100,75)', ''],
-['wx', 'StaticLine', '-1, size=(-1,100), style=wx.LI_VERTICAL', ''],
-['wx', 'StaticLine', '-1, size=(100,-1), style=wx.LI_HORIZONTAL', ''],
-['wx', 'StaticText', '-1, "New font"', 'f = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)\nw.SetFont(f)\n'],
-['wx', 'StaticText', '-1, "normal"', ''],
-['wx', 'StaticText', '-1, "normal"', 'w.SetLabel("This is a longer label")'],
-['wx', 'TextCtrl', '-1, "default"', ''],
-['wx', 'TextCtrl', '-1, "larger size", size=(200, -1)', ''],
-['wx', 'TextCtrl', '-1, "small", size=(30,-1)', ''],
-['wx', 'TextCtrl', '-1, "some\\ndefault text\\n", size=(200, -1), style=wx.TE_MULTILINE', 'w.AppendText("Here is some more text\\n")'],
-['wx', 'TreeCtrl', '', ''],
-['wx.calendar', 'CalendarCtrl', '-1', ''],
-['wx.calendar', 'CalendarCtrl', '-1, style=wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION', ''],
-['wx.calendar', 'CalendarCtrl', '-1', 'w.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL))\n'],
-['wx.lib.stattext', 'GenStaticText', '-1, "New font"', 'f = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)\nw.SetFont(f)\n'],
-['wx.grid', 'Grid', '', 'w.CreateGrid(5,5)\n'],
-['wx.grid', 'Grid', 'size=(400,200)', 'w.CreateGrid(100,50)\n'],
-['wx', 'Button', 'wx.ID_SAVE', ''],
-]
diff --git a/wxPython/misc/widgetLayoutTest.py b/wxPython/misc/widgetLayoutTest.py
deleted file mode 100644 (file)
index 21a6186..0000000
+++ /dev/null
@@ -1,522 +0,0 @@
-"""
-This test app is meant to help check if a widget has a good
-DoGetBestSize method (in C++) by allowing the user to dynamically
-create any non-toplevel widget which will be placed in a Sizer
-designed to show how the widget will be sized naturally.
-"""
-
-import wx
-import sys
-import os
-
-# stuff for debugging
-print "wx.VERSION_STRING = ", wx.VERSION_STRING
-print "pid:", os.getpid()
-##raw_input("Press Enter...")
-
-class LayoutTestFrame(wx.Frame):
-    def __init__(self):
-        wx.Frame.__init__(self, None, -1, "Widget Layout Tester")
-
-        p = wx.Panel(self)
-
-        # Create control widgets
-        self.testHistory = wx.ListBox(p, -1, size=(150, 300))
-        self.moduleName = wx.TextCtrl(p, -1, "wx")
-        self.className  = wx.TextCtrl(p, -1, "")
-        self.parameters = wx.TextCtrl(p, -1, "")
-        self.postCreate = wx.TextCtrl(p, -1, "", size=(1,75),
-                                      style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
-        self.expression = wx.TextCtrl(p, -1, "", style=wx.TE_READONLY)
-        self.docstring  = wx.TextCtrl(p, -1, "", size=(1,75),
-                                      style=wx.TE_READONLY|wx.TE_MULTILINE|wx.TE_DONTWRAP)
-
-        self.expression.SetBackgroundColour(
-            wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
-        self.docstring.SetBackgroundColour(
-            wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
-
-
-        addBtn = wx.Button(p, -1, "Add")
-        remBtn = wx.Button(p, -1, "Remove")
-        repBtn = wx.Button(p, -1, "Replace")
-        createBtn = wx.Button(p, -1, " Create Widget ")
-        createBtn.SetDefault()
-        destroyBtn = wx.Button(p, -1, "Destroy Widget")
-        clearBtn = wx.Button(p, -1, "Clear")
-
-        self.createBtn = createBtn
-        self.destroyBtn = destroyBtn
-
-        bottomPanel = wx.Panel(p, style=wx.SUNKEN_BORDER, name="bottomPanel")
-        bottomPanel.SetMinSize((640,240))
-        bottomPanel.SetOwnBackgroundColour("light blue")
-
-        self.testPanel = wx.Panel(bottomPanel, name="testPanel")
-        self.testPanel.SetOwnBackgroundColour((205, 183, 181)) # mistyrose3
-        self.testWidget = None
-
-        self.infoPane = InfoPane(p)
-
-        # setup event bindings
-        self.Bind(wx.EVT_TEXT, self.OnUpdate, self.moduleName)
-        self.Bind(wx.EVT_TEXT, self.OnUpdate, self.className)
-        self.Bind(wx.EVT_TEXT, self.OnUpdate, self.parameters)
-        self.Bind(wx.EVT_UPDATE_UI, self.OnEnableCreate, createBtn)
-        self.Bind(wx.EVT_UPDATE_UI, self.OnEnableDestroy, destroyBtn)
-        self.Bind(wx.EVT_BUTTON, self.OnCreateWidget, createBtn)
-        self.Bind(wx.EVT_BUTTON, self.OnDestroyWidget, destroyBtn)
-        self.Bind(wx.EVT_BUTTON, self.OnClear, clearBtn)
-
-        self.Bind(wx.EVT_CLOSE, self.OnSaveHistory)
-
-        self.Bind(wx.EVT_BUTTON, self.OnAddHistory, addBtn)
-        self.Bind(wx.EVT_BUTTON, self.OnRemoveHistory, remBtn)
-        self.Bind(wx.EVT_BUTTON, self.OnReplaceHistory, repBtn)
-        self.Bind(wx.EVT_LISTBOX, self.OnHistorySelect, self.testHistory)
-        self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnHistoryActivate, self.testHistory)
-
-        if 'wxMac' in wx.PlatformInfo or 'wxGTK' in wx.PlatformInfo:
-            self.testHistory.Bind(wx.EVT_KEY_DOWN, self.OnHistoryKey)
-            
-
-        # setup the layout
-        mainSizer = wx.BoxSizer(wx.VERTICAL)
-        topSizer  = wx.BoxSizer(wx.HORIZONTAL)
-        ctlsSizer = wx.FlexGridSizer(2, 2, 5, 5)
-        ctlsSizer.AddGrowableCol(1)
-        btnSizer =  wx.BoxSizer(wx.HORIZONTAL)
-
-        topSizer.Add(self.testHistory, 0, wx.RIGHT, 30)
-
-        ctlsSizer.Add(wx.StaticText(p, -1, "Module name:"),
-                      0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        mcSizer = wx.BoxSizer(wx.HORIZONTAL)
-        mcSizer.Add(self.moduleName, 0, 0)
-        mcSizer.Add(wx.StaticText(p, -1, "Class name:"),
-                      0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL |wx.LEFT|wx.RIGHT, 10)
-        mcSizer.Add(self.className, 1, 0)
-        ctlsSizer.Add(mcSizer, 0, wx.EXPAND)
-
-        ctlsSizer.Add(wx.StaticText(p, -1, "Parameters:"),
-                      0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        ctlsSizer.Add(self.parameters, 0, wx.EXPAND)
-        ctlsSizer.Add(wx.StaticText(p, -1, "Create Expr:"),
-                      0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        ctlsSizer.Add(self.expression, 0, wx.EXPAND)
-        ctlsSizer.Add(wx.StaticText(p, -1, "Post create:"), 0, wx.ALIGN_RIGHT)
-        ctlsSizer.Add(self.postCreate, 0, wx.EXPAND)
-        ctlsSizer.Add(wx.StaticText(p, -1, "DocString:"), 0, wx.ALIGN_RIGHT)
-        ctlsSizer.Add(self.docstring, 0, wx.EXPAND)
-        ctlsSizer.AddGrowableRow(4)
-        topSizer.Add(ctlsSizer, 1, wx.EXPAND)
-  
-        btnSizer.Add((5,5))
-        btnSizer.Add(addBtn, 0, wx.RIGHT, 5)
-        btnSizer.Add(remBtn, 0, wx.RIGHT, 5)
-        btnSizer.Add(repBtn, 0, wx.RIGHT, 5)
-        btnSizer.Add((0,0), 1)
-        btnSizer.Add(createBtn, 0, wx.RIGHT, 5)
-        btnSizer.Add(destroyBtn, 0, wx.RIGHT, 5)
-        btnSizer.Add(clearBtn, 0, wx.RIGHT, 5)
-        btnSizer.Add((0,0), 1)
-
-        mainSizer.Add(topSizer, 0, wx.EXPAND|wx.ALL, 10)
-        mainSizer.Add(btnSizer, 0, wx.EXPAND)
-        mainSizer.Add((10,10))
-        ##mainSizer.Add(wx.StaticLine(p, -1), 0, wx.EXPAND)
-        mainSizer.Add(bottomPanel, 1, wx.EXPAND)
-
-        mainSizer.Add(self.infoPane, 0, wx.EXPAND)
-
-        self.bottomSizer = sz = wx.BoxSizer(wx.VERTICAL)
-        sz.Add((0,0), 1)
-        sz.Add(self.testPanel, 0, wx.ALIGN_CENTER)
-        sz.Add((0,0), 1)
-        bottomPanel.SetSizer(sz)
-
-        p.SetSizerAndFit(mainSizer)
-        self.Fit()
-
-        self.PopulateHistory()
-
-
-
-
-    def PopulateHistory(self):
-        """
-        Load and eval a list of lists from a file, load the contents
-        into self.testHistory
-        """
-        fname = os.path.join(os.path.dirname(sys.argv[0]),
-                             'widgetLayoutTest.cfg')
-        try:
-            self.history = eval(open(fname).read())
-        except:
-            self.history = []
-
-        self.testHistory.Clear()
-
-        for idx in range(len(self.history)):
-            item = self.history[idx]
-            # check if it is too short
-            while len(item) < 4:
-                item.append('')
-
-            # add it to the listbox
-            self.testHistory.Append(item[0] + '.' + item[1])
-
-        self.needSaved = False
-
-
-    def OnSaveHistory(self, evt):
-        if self.needSaved:
-            fname = os.path.join(os.path.dirname(sys.argv[0]),
-                                 'widgetLayoutTest.cfg')
-            f = open(fname, 'wb')
-            f.write('[\n')
-            for item in self.history:
-                f.write(str(item) + ',\n')
-            f.write(']\n')
-            f.close()
-        evt.Skip()
-
-
-    def OnAddHistory(self, evt):
-        moduleName = self.moduleName.GetValue()
-        className  = self.className.GetValue()
-        parameters = self.parameters.GetValue()
-        postCreate = self.postCreate.GetValue()
-
-        item = [str(moduleName), str(className), str(parameters), str(postCreate)]
-        self.history.append(item)
-        self.testHistory.Append(item[0] + '.' + item[1])
-
-        self.testHistory.SetSelection(len(self.history)-1)
-        self.needSaved = True
-
-
-    def OnRemoveHistory(self, evt):
-        idx = self.testHistory.GetSelection()
-        if idx != wx.NOT_FOUND:
-            del self.history[idx]
-            self.testHistory.Delete(idx)
-        self.needSaved = True
-        self.OnClear(None)
-
-
-    def OnReplaceHistory(self, evt):
-        idx = self.testHistory.GetSelection()
-        if idx != wx.NOT_FOUND:
-            moduleName = self.moduleName.GetValue()
-            className  = self.className.GetValue()
-            parameters = self.parameters.GetValue()
-            postCreate = self.postCreate.GetValue()
-
-            item = [str(moduleName), str(className), str(parameters), str(postCreate)]
-            self.history[idx] = item
-            self.testHistory.SetString(idx, item[0] + '.' + item[1])
-        self.needSaved = True
-
-
-    def OnHistorySelect(self, evt):
-        #idx = self.testHistory.GetSelection()
-        idx = evt.GetInt()
-        if idx != wx.NOT_FOUND:
-            item = self.history[idx]
-            self.moduleName.SetValue(item[0])
-            self.className.SetValue(item[1])
-            self.parameters.SetValue(item[2])
-            self.postCreate.SetValue(item[3])
-            if 'wxMac' in wx.PlatformInfo:
-                self.OnUpdate(None)
-
-
-    def OnHistoryActivate(self, evt):
-        btn = self.testHistory.GetParent().GetDefaultItem()
-        if btn is not None:
-            e = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId())
-            btn.Command(e)
-
-
-    def OnHistoryKey(self, evt):
-        key = evt.GetKeyCode()
-        if key == wx.WXK_RETURN:
-            self.OnHistoryActivate(None)
-        else:
-            evt.Skip()
-
-
-    def OnUpdate(self, evt):
-        # get the details from the form
-        moduleName = self.moduleName.GetValue()
-        className  = self.className.GetValue()
-        parameters = self.parameters.GetValue()
-
-        expr = "w = %s.%s( testPanel, %s )" % (moduleName, className, parameters)
-        self.expression.SetValue(expr)
-
-        docstring = None
-        try:
-            docstring = eval("%s.%s.__init__.__doc__" % (moduleName, className))
-        except:
-            pass
-        if docstring is not None:
-            self.docstring.SetValue(docstring)
-        else:
-            self.docstring.SetValue("")
-
-    def OnEnableDestroy(self, evt):
-        evt.Enable(self.testWidget is not None)
-
-    def OnEnableCreate(self, evt):
-        evt.Enable(self.testWidget is None)
-
-
-    def OnCreateWidget(self, evt):
-        if self.testWidget is not None:
-            return
-
-        testPanel = self.testPanel
-
-        # get the details from the form
-        moduleName = self.moduleName.GetValue()
-        className  = self.className.GetValue()
-        parameters = self.parameters.GetValue()
-        expr       = self.expression.GetValue()[4:]
-        postCreate = self.postCreate.GetValue()
-        if 'wxMac' in wx.PlatformInfo:
-            postCreate = postCreate.replace('\r', '\n')
-
-        # make sure the module is imported already
-        if not sys.modules.has_key(moduleName):
-            try:
-                m = __import__(moduleName)
-            except importError:
-                wx.MessageBox("Unable to import module!", "Error")
-                return
-
-        # create the widget
-        try:
-            w = eval(expr)
-        except Exception, e:
-            wx.MessageBox("Got a '%s' Exception!" % e.__class__.__name__, "Error")
-            import traceback
-            traceback.print_exc()
-            return
-
-        # Is there postCreate code?
-        if postCreate:
-            ns = {}
-            ns.update(globals())
-            ns.update(locals())
-            try:
-                exec postCreate in ns
-            except Exception, e:
-                wx.MessageBox("Got a '%s' Exception!" % e.__class__.__name__, "Error")
-                import traceback
-                traceback.print_exc()
-                w.Destroy()
-                return
-
-        # Put the widget in a sizer and the sizer in the testPanel
-        sizer = wx.BoxSizer(wx.VERTICAL)
-        sizer.Add(w, 0, wx.ALL, 5)
-        self.testPanel.SetSizer(sizer)
-        self.testWidget = w
-        self.bottomSizer.Layout()
-
-        # make the destroy button be default now
-        self.destroyBtn.SetDefault()
-
-        self.infoPane.Update(w, testPanel)
-        
-
-    def OnDestroyWidget(self, evt):
-        self.testWidget.Destroy()
-        self.testWidget = None
-        self.testPanel.SetSizer(None, True)
-        self.testPanel.Refresh()
-
-        # ensure the panel shrinks again now that it has no sizer
-        self.testPanel.SetMinSize((20,20))
-        self.bottomSizer.Layout()
-        self.testPanel.SetMinSize(wx.DefaultSize)
-
-        # make the create button be default now
-        self.createBtn.SetDefault()
-
-        self.infoPane.Clear()
-        
-
-    def OnClear(self, evt):
-        self.moduleName.SetValue("")
-        self.className.SetValue("")
-        self.parameters.SetValue("")
-        self.expression.SetValue("")
-        self.docstring.SetValue("")
-        self.postCreate.SetValue("")
-
-
-
-
-
-
-class InfoPane(wx.Panel):
-    """
-    This class is used to display details of various properties of the
-    widget and the testPanel to aid with debugging.
-    """
-    def __init__(self, parent):
-        wx.Panel.__init__(self, parent)
-
-        # create subwidgets
-        self.wPane = SizeInfoPane(self, "Widget")
-        self.tpPane= SizeInfoPane(self, "testPanel")
-        self.cPane = ColourInfoPanel(self, "Widget colours")
-
-        # Setup the layout
-        sizer = wx.BoxSizer(wx.HORIZONTAL)
-        sizer.Add(self.wPane, 0, wx.EXPAND|wx.ALL, 5)
-        sizer.Add(self.tpPane, 0, wx.EXPAND|wx.ALL, 5)
-        sizer.Add(self.cPane, 0, wx.EXPAND|wx.ALL, 5)
-
-        self.SetSizer(sizer)
-
-        
-    def Update(self, w, tp):
-        self.wPane.Update(w)
-        self.tpPane.Update(tp)
-        self.cPane.Update(w)
-        
-    def Clear(self):
-        self.wPane.Clear()
-        self.tpPane.Clear()
-        self.cPane.Clear()
-
-
-
-class SizeInfoPane(wx.Panel):
-    """
-    A component of the InfoPane that shows vaious window size attributes.
-    """
-    def __init__(self, parent, label):
-        wx.Panel.__init__(self, parent)
-
-        # create subwidgets
-        sb = wx.StaticBox(self, -1, label)
-        self._size        = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
-        self._minsize     = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
-        self._bestsize    = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
-        self._adjbstsize  = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
-        self._bestfit     = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
-
-        # setup the layout
-        fgs = wx.FlexGridSizer(2, 2, 5, 5)
-        fgs.AddGrowableCol(1)
-
-        fgs.Add(wx.StaticText(self, -1, "Size:"),
-                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        fgs.Add(self._size, 0, wx.EXPAND)
-
-        fgs.Add(wx.StaticText(self, -1, "MinSize:"),
-                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        fgs.Add(self._minsize, 0, wx.EXPAND)
-
-        fgs.Add(wx.StaticText(self, -1, "BestSize:"),
-                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        fgs.Add(self._bestsize, 0, wx.EXPAND)
-
-        fgs.Add(wx.StaticText(self, -1, "AdjustedBestSize:"),
-                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        fgs.Add(self._adjbstsize, 0, wx.EXPAND)
-
-        fgs.Add(wx.StaticText(self, -1, "BestFittingSize:"),
-                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        fgs.Add(self._bestfit, 0, wx.EXPAND)
-
-        sbs = wx.StaticBoxSizer(sb, wx.VERTICAL)
-        sbs.Add(fgs, 0, wx.EXPAND|wx.ALL, 4)
-
-        self.SetSizer(sbs)
-
-
-    def Update(self, win):
-        self._size.SetValue(       str(win.GetSize()) )
-        self._minsize.SetValue(    str(win.GetMinSize()) )
-        self._bestsize.SetValue(   str(win.GetBestSize()) )
-        self._adjbstsize.SetValue( str(win.GetAdjustedBestSize()) )
-        self._bestfit.SetValue(    str(win.GetEffectiveMinSize()) )
-
-    def Clear(self):
-        self._size.SetValue("")
-        self._minsize.SetValue("")
-        self._bestsize.SetValue("")
-        self._adjbstsize.SetValue("")
-        self._bestfit.SetValue("")
-
-
-
-class ColourInfoPanel(wx.Panel):
-    """
-    A component of the InfoPane that shows fg and bg colour attributes.    
-    """
-    def __init__(self, parent, label):
-        wx.Panel.__init__(self, parent)
-
-        # create subwidgets
-        sb = wx.StaticBox(self, -1, label)
-        self._fgtxt = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
-        self._fgclr = wx.Panel(self, style=wx.SIMPLE_BORDER)
-        self._fgclr.SetMinSize((20,20))
-        self._bgtxt = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
-        self._bgclr = wx.Panel(self, style=wx.SIMPLE_BORDER)
-        self._bgclr.SetMinSize((20,20))
-
-        # setup the layout
-        fgs = wx.FlexGridSizer(2, 3, 5, 5)
-        fgs.AddGrowableCol(1)
-
-        fgs.Add(wx.StaticText(self, -1, "FG colour:"),
-                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        fgs.Add(self._fgtxt, 0, wx.EXPAND)
-        fgs.Add(self._fgclr)
-
-        fgs.Add(wx.StaticText(self, -1, "BG colour:"),
-                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
-        fgs.Add(self._bgtxt, 0, wx.EXPAND)
-        fgs.Add(self._bgclr)
-
-        sbs = wx.StaticBoxSizer(sb, wx.VERTICAL)
-        sbs.Add(fgs, 0, wx.EXPAND|wx.ALL, 4)
-
-        self.SetSizer(sbs)
-
-
-    def Update(self, win):
-        def clr2hex(c, cp):
-            cp.SetBackgroundColour(c)
-            cp.Refresh()
-            return "#%02X%02X%02X" % c.Get()
-        
-        self._fgtxt.SetValue( clr2hex(win.GetForegroundColour(), self._fgclr) )
-        self._bgtxt.SetValue( clr2hex(win.GetBackgroundColour(), self._bgclr) )
-
-
-    def Clear(self):
-        self._fgtxt.SetValue("")
-        self._bgtxt.SetValue("")
-        self._fgclr.SetBackgroundColour(self.GetBackgroundColour())
-        self._bgclr.SetBackgroundColour(self.GetBackgroundColour())
-        self._fgclr.Refresh()
-        self._bgclr.Refresh()
-
-
-        
-
-app = wx.PySimpleApp(redirect=False)
-frame = LayoutTestFrame()
-app.SetTopWindow(frame)
-frame.Show()
-app.MainLoop()
-
index f5e0253e119eb8cdd86f21e291829e599a486640..6be46c5f08a5addaaa8184e8e610b427296cdd62 100644 (file)
@@ -19,7 +19,10 @@ class Frame(wx.Frame):
                                             | wx.PD_REMAINING_TIME)
 
         self.count += 1
-        keepGoing = self.dialog.Update(self.count)
+        if wx.VERSION < (2,7,1,1):
+            keepGoing = self.dialog.Update(self.count)
+        else:
+            (keepGoing, skip) = self.dialog.Update(self.count)            
         if not keepGoing or self.count == self.progressMax:
             self.dialog.Destroy()
             self.timer.Stop()
index 28f221b0df163be5161c302a1ff2f56052afe0cf..b8a1f05f4f2888ffbdcc4dbeb0aba590c71e6afd 100755 (executable)
@@ -866,6 +866,7 @@ if __name__ == "__main__":
                           'wx.lib.colourchooser',
                           'wx.lib.editor',
                           'wx.lib.floatcanvas',
+                          'wx.lib.floatcanvas.Utilities',
                           'wx.lib.masked',
                           'wx.lib.mixins',
                           'wx.lib.ogl',
index bef4d71056068d8a7f929704a22630126eafc593..4c564c0b255fe506f59dd71a26a79ee28b92d7bd 100644 (file)
@@ -112,6 +112,11 @@ call this function to process posted events. This normally happens
 during each event loop iteration.", "");
 
 
+    DocDeclStr(
+        bool , HasPendingEvents() const,
+        "Check if there are pending events on global pending event list", "");
+    
+
     DocDeclStr(
         virtual bool, Yield(bool onlyIfNeeded = false),
         "Process all currently pending events right now, instead of waiting
index 6215187fcf5e15a89a02c290613e9862a8a1b234..38162ff4489cee2da551cbd916306f5fb4b71663 100644 (file)
@@ -630,7 +630,7 @@ works for single line strings.", "");
     DocDeclAStrName(
         void, GetTextExtent(const wxString& string,
                             wxCoord *OUTPUT, wxCoord *OUTPUT, wxCoord *OUTPUT, wxCoord* OUTPUT,
-                            wxFont* font = NULL),
+                            const wxFont* font = NULL),
         "GetFullTextExtent(wxString string, Font font=None) ->\n   (width, height, descent, externalLeading)",
         "Get the width, height, decent and leading of the text using the
 current or specified font. Only works for single line strings.", "",
@@ -641,7 +641,7 @@ current or specified font. Only works for single line strings.", "",
     DocDeclAStr(
         void, GetMultiLineTextExtent(const wxString& text,
                                      wxCoord *OUTPUT, wxCoord *OUTPUT, wxCoord *OUTPUT,
-                                     wxFont *font = NULL),
+                                     const wxFont *font = NULL),
         "GetMultiLineTextExtent(wxString string, Font font=None) ->\n   (width, height, lineHeight)",
         "Get the width, height, and line height of the text using the
 current or specified font. Works for single as well as multi-line
index a7eab282d945fcf26ca3bc60848745edcd505732..1eb46b0cf2209ce09d554c98a0352af21fea50b1 100644 (file)
@@ -1162,6 +1162,7 @@ enum wxItemKind
     wxITEM_NORMAL,
     wxITEM_CHECK,
     wxITEM_RADIO,
+    wxITEM_DROPDOWN,
     wxITEM_MAX
 };
 
index 5f7c7140b43de5d67f2759871a4f7fe6631e74f6..6a2333ddfef52ba46c1463fd5b069ab4b97f5981 100644 (file)
@@ -61,6 +61,7 @@ wxEventType wxNewEventType();
 %constant wxEventType wxEVT_COMMAND_COMBOBOX_SELECTED;
 %constant wxEventType wxEVT_COMMAND_TOOL_RCLICKED;
 %constant wxEventType wxEVT_COMMAND_TOOL_ENTER;
+%constant wxEventType wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED;
 
 // Mouse event types
 %constant wxEventType wxEVT_LEFT_DOWN;
@@ -354,6 +355,7 @@ EVT_TOOL_RANGE = wx.PyEventBinder( wxEVT_COMMAND_TOOL_CLICKED, 2)
 EVT_TOOL_RCLICKED = wx.PyEventBinder( wxEVT_COMMAND_TOOL_RCLICKED, 1)
 EVT_TOOL_RCLICKED_RANGE = wx.PyEventBinder( wxEVT_COMMAND_TOOL_RCLICKED, 2)
 EVT_TOOL_ENTER = wx.PyEventBinder( wxEVT_COMMAND_TOOL_ENTER, 1)
+EVT_TOOL_DROPDOWN = wx.PyEventBinder( wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED, 1)
 EVT_CHECKLISTBOX = wx.PyEventBinder( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, 1)
 
 
index de95253c705cec927a2c19c17cec092c5a2b4edb..e699c90c8ea7291e808b2124a7fffbe30cff1c4c 100644 (file)
 #if 0   // #ifdef __WXMAC__
 
 // A dummy class that raises an exception if used...    
-class wxEventLoop
+class wxEventLoopBase
 {
 public:
-    wxEventLoop() { wxPyRaiseNotImplemented(); }
+    wxEventLoopBase() { wxPyRaiseNotImplemented(); }
+    bool IsOk() const { return false; }
     int Run() { return 0; }
     void Exit(int rc = 0) {}
     bool Pending() const { return false; }
     bool Dispatch() { return false; }
     bool IsRunning() const { return false; }
+    void WakeUp() {}
     static wxEventLoop *GetActive() { wxPyRaiseNotImplemented(); return NULL; }
     static void SetActive(wxEventLoop* loop) { wxPyRaiseNotImplemented(); }
 };
@@ -43,12 +45,16 @@ public:
 #endif
 %}
 
-class wxEventLoop
+class wxEventLoopBase
 {
 public:
-    wxEventLoop();
+    wxEventLoopBase();
     virtual ~wxEventLoop();
 
+    // use this to check whether the event loop was successfully created before
+    // using it
+    virtual bool IsOk() const;
+
     // start the event loop, return the exit code when it is finished
     virtual int Run();
 
@@ -64,6 +70,8 @@ public:
     // is the event loop running now?
     virtual bool IsRunning() const;
 
+    virtual void WakeUp();
+    
     // return currently active (running) event loop, may be NULL
     static wxEventLoop *GetActive();
 
@@ -72,6 +80,34 @@ public:
 };
 
 
+class wxEventLoopManual : public wxEventLoopBase
+{
+public:
+    wxEventLoopManual();
+};
+
+
+class wxGUIEventLoop : public wxEventLoopBase
+{
+public:
+    wxGUIEventLoop();
+};
+
+
+%pythoncode {
+    class EventLoop(GUIEventLoop):
+        """Class using the old name for compatibility."""
+        pass
+}
+
+
+class wxModalEventLoop : public wxGUIEventLoop
+{
+public:
+    wxModalEventLoop(wxWindow *winModal)
+};
+
+
 
 // This object sets the wxEventLoop given to the ctor as the currently active
 // one and unsets it in its dtor, this is especially useful in presence of
index 481f99d87f1e1b0a832d0759c7cf257930e5b15d..baf74525ac64f3dd221f509793eb21ac1da6eaed 100644 (file)
@@ -337,8 +337,14 @@ MustHaveApp(wxGetTopLevelParent);
 wxWindow* wxGetTopLevelParent(wxWindow *win);
 
 
+// flags for wxLaunchDefaultBrowser
+enum
+{
+    wxBROWSER_NEW_WINDOW = 1
+};
+
 DocDeclStr(
-    bool , wxLaunchDefaultBrowser(const wxString& url),
+    bool , wxLaunchDefaultBrowser(const wxString& url, int flags = 0),
     "Launches the user's default browser and tells it to open the location
 at ``url``.  Returns ``True`` if the application was successfully
 launched.", "");
index 3dfc58acab658f388ff86b0ee60ce0a54312b28a..5ec3889dc3241dc7139f7bf2d96047c1e17ca9cf 100644 (file)
@@ -240,7 +240,7 @@ public:
     wxPyPanel(wxWindow* parent, const wxWindowID id,
                const wxPoint& pos = wxDefaultPosition,
                const wxSize& size = wxDefaultSize,
-               long style = 0,
+               long style = wxTAB_TRAVERSAL | wxNO_BORDER,
                const wxString& name = wxPyPanelNameStr)
         : wxPanel(parent, id, pos, size, style, name) {}
 
@@ -334,7 +334,7 @@ public:
     wxPyPanel(wxWindow* parent, const wxWindowID id=-1,
                const wxPoint& pos = wxDefaultPosition,
                const wxSize& size = wxDefaultSize,
-               long style = 0,
+               long style = wxTAB_TRAVERSAL | wxNO_BORDER,
                const wxString& name = wxPyPanelNameStr);
 
     %RenameCtor(PrePyPanel,  wxPyPanel());
@@ -415,7 +415,7 @@ public:
     wxPyScrolledWindow(wxWindow* parent, const wxWindowID id,
                const wxPoint& pos = wxDefaultPosition,
                const wxSize& size = wxDefaultSize,
-               long style = 0,
+               long style = wxHSCROLL | wxVSCROLL,
                const wxString& name = wxPyPanelNameStr)
         : wxScrolledWindow(parent, id, pos, size, style, name) {}
 
@@ -508,7 +508,7 @@ public:
     wxPyScrolledWindow(wxWindow* parent, const wxWindowID id=-1,
                const wxPoint& pos = wxDefaultPosition,
                const wxSize& size = wxDefaultSize,
-               long style = 0,
+               long style = wxHSCROLL | wxVSCROLL,
                const wxString& name = wxPyPanelNameStr);
 
     %RenameCtor(PrePyScrolledWindow,  wxPyScrolledWindow());
index bed230c51cc2e57dc6aca5b925b236b6d03f7578..8bd1d79abd27ae42f70a22c8bc07051229f10055 100644 (file)
@@ -33,7 +33,15 @@ enum wxSoundFlags
 
 %{
 #if !wxUSE_SOUND
-// A C++ stub class for wxWave for platforms that don't have it.
+// A C++ stub class for wxSound for platforms that don't have it.
+
+enum wxSoundFlags
+{
+    wxSOUND_SYNC,
+    wxSOUND_ASYNC,
+    wxSOUND_LOOP
+};
+    
 class wxSound : public wxObject
 {
 public:
index 3f227d5d376abf89f695c2735fd986fb6c2d0a20..74815e86077bfe8e09526965a50a2d00bf81e232 100644 (file)
@@ -140,8 +140,10 @@ class wxTimerEvent : public wxEvent
 public:
     wxTimerEvent(int timerid = 0, int interval = 0);
     int GetInterval() const;
-
+    wxTimer& GetTimer() const;
+    
     %property(Interval, GetInterval, doc="See `GetInterval`");
+    %property(Timer, GetTimer);
 };
 
 
index 629b1b2c366c336f064cd4e1ebc703c75e0a7b76..f027e49a7cae64023cbd3212886e4a16b42cd050 100644 (file)
@@ -102,6 +102,11 @@ public:
     void Detach();
     void Attach(wxToolBarBase *tbar);
 
+    // these methods are only for tools of wxITEM_DROPDOWN kind (but even such
+    // tools can have a NULL associated menu)
+    void SetDropdownMenu(wxMenu *menu);
+    wxMenu *GetDropdownMenu() const;
+
     //wxObject *GetClientData();
     %extend {
         // convert the ClientData back to a PyObject
@@ -404,6 +409,10 @@ public:
 
     size_t GetToolsCount() const;
 
+    // Set dropdown menu
+    bool SetDropdownMenu(int toolid, wxMenu *menu);
+
+    
     %property(Margins, GetMargins, SetMargins, doc="See `GetMargins` and `SetMargins`");
     %property(MaxCols, GetMaxCols, doc="See `GetMaxCols`");
     %property(MaxRows, GetMaxRows, doc="See `GetMaxRows`");
index 512f817f0e50331d197453c90c8179e5590b1bae..fb623fc0c03ebfed9e406aac046c1121cd0e530a 100644 (file)
@@ -110,9 +110,6 @@ public:
     virtual void RefreshRow(size_t row);
     virtual void RefreshRows(size_t from, size_t to);
 
-    virtual int HitTest(wxCoord y) const;
-
-
     size_t GetRowCount() const;
     size_t GetVisibleRowsBegin() const;
     size_t GetVisibleRowsEnd() const;
@@ -144,8 +141,6 @@ public:
 
     virtual void RefreshColumn(size_t column);
     virtual void RefreshColumns(size_t from, size_t to);
-    virtual int HitTest(wxCoord x) const;
-
 
     size_t GetColumnCount() const;
     size_t GetVisibleColumnsBegin() const;
@@ -212,8 +207,8 @@ public:
                                     const wxPosition& to);
 
     // Override wxPanel::HitTest to use our version
-//     virtual wxPosition HitTest(wxCoord x, wxCoord y) const;
-    virtual wxPosition HitTest(const wxPoint &pos) const;
+//     wxPosition VirtualHitTest(wxCoord x, wxCoord y) const;
+    wxPosition VirtualHitTest(const wxPoint &pos) const;
 
     // replacement implementation of wxWindow::Layout virtual method.  To
     // properly forward calls to wxWindow::Layout use
@@ -508,7 +503,6 @@ public:
                 const wxString& name = wxPyPanelNameStr);
 
     
-    int HitTest(const wxPoint& pt) const;
     wxCoord GetColumnsWidth(size_t columnMin, size_t columnMax) const;
     wxCoord EstimateTotalWidth() const;
 };
@@ -607,8 +601,6 @@ public:
                 const wxString& name = wxPyPanelNameStr);
 
     
-    wxPosition HitTest(const wxPoint& pt) const;
-
     wxCoord GetRowsHeight(size_t lineMin, size_t lineMax) const;
     wxCoord EstimateTotalHeight() const;
 
index 2c3d50a741b263e448ca682f50d721b38982c49a..ab8acd6b553efa3d3addffd648cd7ffa44b458d2 100644 (file)
@@ -1114,7 +1114,7 @@ handler is handed off to the next one in the chain.", "");
         void , PushEventHandler( wxEvtHandler *handler ),
         "Pushes this event handler onto the event handler stack for the window.
 An event handler is an object that is capable of processing the events
-sent to a window.  (In other words, is able to dispatch the events to
+sent to a window.  (In other words, is able to dispatch the events to a
 handler function.)  By default, the window is its own event handler,
 but an application may wish to substitute another, for example to
 allow central implementation of event-handling for a variety of
index cf177c960945499015be8154c2385c069ab38a17..815e8f0b4ea3a356e694c6d2c3747863c52e8e49 100755 (executable)
@@ -58,7 +58,7 @@ interface:
 **Usage**
 
 The following example shows a simple implementation that utilizes
-`wx.aui.FrameManager` to manage three text controls in a frame window::
+`wx.aui.AuiManager` to manage three text controls in a frame window::
 
     import wx
     import wx.aui
@@ -170,7 +170,7 @@ The following example shows a simple implementation that utilizes
                                             const wxPaneInfo& pane_info,
                                             const wxPoint& drop_pos);
 
-// A typemap for the return value of wxFrameManager::GetAllPanes
+// A typemap for the return value of wxAuiManager::GetAllPanes
 %typemap(out) wxAuiPaneInfoArray& {
     $result = PyList_New(0);
     for (size_t i=0; i < $1->GetCount(); i++) {
@@ -179,6 +179,7 @@ The following example shows a simple implementation that utilizes
     }
 }
 
+//%ignore wxAuiManager::~wxAuiManager;
 
 %nokwargs wxAuiTabContainer::SetActivePage;
 
@@ -234,7 +235,7 @@ The following example shows a simple implementation that utilizes
 #undef wxColor
 
 //---------------------------------------------------------------------------
-// Methods to inject into the FrameManager class that will sort out calls to
+// Methods to inject into the AuiManager class that will sort out calls to
 // the overloaded versions of GetPane and AddPane
 
 %extend wxAuiManager {
@@ -247,7 +248,7 @@ The following example shows a simple implementation that utilizes
         widget reference or by pane name, which acts as a unique id
         for a window pane. The returned `PaneInfo` object may then be
         modified to change a pane's look, state or position. After one
-        or more modifications to the `PaneInfo`, `FrameManager.Update`
+        or more modifications to the `PaneInfo`, `AuiManager.Update`
         should be called to realize the changes to the user interface.
 
         If the lookup failed (meaning the pane could not be found in
index 9c7fe0e1645297415acedf27c9d612b8cfdc4980..71ef20270f72aff0221f6972e879d8c7e04c897d 100644 (file)
@@ -9360,7 +9360,7 @@ class Window(EvtHandler):
 
         Pushes this event handler onto the event handler stack for the window.
         An event handler is an object that is capable of processing the events
-        sent to a window.  (In other words, is able to dispatch the events to
+        sent to a window.  (In other words, is able to dispatch the events to a
         handler function.)  By default, the window is its own event handler,
         but an application may wish to substitute another, for example to
         allow central implementation of event-handling for a variety of
index acd06368b204f25913295a3be491242fdc98870e..9cc7cb8f7b4c17a897118770ff510c0ca132b7ba 100644 (file)
@@ -3289,6 +3289,14 @@ public:
 
 #if !wxUSE_SOUND
 // A C++ stub class for wxWave for platforms that don't have it.
+
+enum wxSoundFlags
+{
+    wxSOUND_SYNC,
+    wxSOUND_ASYNC,
+    wxSOUND_LOOP
+};
+    
 class wxSound : public wxObject
 {
 public:
index 3e7a8e9c9cb939981be37ea1f1f2bafa7d16f04e..a99061c73289ab3a6f61f0313c081669413ff1d5 100644 (file)
@@ -842,6 +842,7 @@ void wxPyOORClientData_dtor(wxPyOORClientData* self) {
             //Py_INCREF(deadObjectClass);
             Py_DECREF(klass);
             Py_DECREF(name);
+            Py_DECREF(dict);
         }
     }
 
index 9c7fe0e1645297415acedf27c9d612b8cfdc4980..71ef20270f72aff0221f6972e879d8c7e04c897d 100644 (file)
@@ -9360,7 +9360,7 @@ class Window(EvtHandler):
 
         Pushes this event handler onto the event handler stack for the window.
         An event handler is an object that is capable of processing the events
-        sent to a window.  (In other words, is able to dispatch the events to
+        sent to a window.  (In other words, is able to dispatch the events to a
         handler function.)  By default, the window is its own event handler,
         but an application may wish to substitute another, for example to
         allow central implementation of event-handling for a variety of
index 7d7963abfbab27dda0c31147c77d1633c6289fac..ef7641dfd2eea2d511f094bc3e847a32676bb2bd 100644 (file)
@@ -9360,7 +9360,7 @@ class Window(EvtHandler):
 
         Pushes this event handler onto the event handler stack for the window.
         An event handler is an object that is capable of processing the events
-        sent to a window.  (In other words, is able to dispatch the events to
+        sent to a window.  (In other words, is able to dispatch the events to a
         handler function.)  By default, the window is its own event handler,
         but an application may wish to substitute another, for example to
         allow central implementation of event-handling for a variety of
index 1153f0243f966baf0fad861134c976dd62d81651..ac7909836e6154f1b1d59126a436ff195a4fd11b 100644 (file)
@@ -1,4 +1,9 @@
-import wx, wx.gizmos, wx.lib.customtreectrl, unittest, treemixin
+import wx, wx.gizmos, wx.lib.customtreectrl, unittest
+try:
+    import treemixin 
+except ImportError:
+    from wx.lib.mixins import treemixin
+
 
 
 # VirtualTree tests
diff --git a/wxPython/tests/image.png b/wxPython/tests/image.png
new file mode 100644 (file)
index 0000000..fc5db22
Binary files /dev/null and b/wxPython/tests/image.png differ
diff --git a/wxPython/tests/test_dclick.py b/wxPython/tests/test_dclick.py
new file mode 100644 (file)
index 0000000..639086c
--- /dev/null
@@ -0,0 +1,28 @@
+import wx
+
+def OnClick(evt):
+    print 'Click'
+
+def OnDClick(evt):
+    print 'DClick'
+
+def OnMouse(evt):
+    if evt.LeftDClick():
+        print 'DClick'
+    elif evt.LeftDown():
+        print 'Click'
+    
+app = wx.App(redirect=False)
+frame = wx.Frame(None, title="Test mouse clicks")
+panel = wx.Panel(frame)
+
+if True:
+    # try separate
+    panel.Bind(wx.EVT_LEFT_DOWN, OnClick)
+    panel.Bind(wx.EVT_LEFT_DCLICK, OnDClick)
+else:
+    # or together
+    panel.Bind(wx.EVT_MOUSE_EVENTS, OnMouse)
+
+frame.Show()
+app.MainLoop()
diff --git a/wxPython/tests/test_transparentFrame.py b/wxPython/tests/test_transparentFrame.py
new file mode 100644 (file)
index 0000000..7c785b5
--- /dev/null
@@ -0,0 +1,71 @@
+import wx
+
+class Frame(wx.Frame):
+    def __init__(self):
+        wx.Frame.__init__(self, None, title="Am I transparent?")
+        self.amount = 255
+        self.delta = -3
+
+        p = wx.Panel(self)
+        self.st = wx.StaticText(p, -1, str(self.amount), (25,25))
+        self.st.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.NORMAL))
+
+        self.timer = wx.Timer(self)
+        self.timer.Start(25)
+        self.Bind(wx.EVT_TIMER, self.AlphaCycle)
+
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+
+    def OnCloseWindow(self, evt):
+        self.timer.Stop()
+        del self.timer
+        self.Destroy()
+
+
+    def AlphaCycle(self, evt):
+        self.amount += self.delta
+        if self.amount == 0 or self.amount == 255:
+            self.delta = -self.delta
+        self.st.SetLabel(str(self.amount))
+
+        # Note that we no longer need to use ctypes or win32api to
+        # make transparent windows, however I'm not removing the
+        # MakeTransparent code from this sample as it may be helpful
+        # for somebody someday.
+        #self.MakeTransparent(self.amount)
+
+        # Instead we'll just call the SetTransparent method
+        self.SetTransparent(self.amount)
+        
+
+    def MakeTransparent(self, amount):
+        hwnd = self.GetHandle()
+        try:
+            import ctypes
+            _winlib = ctypes.windll.user32
+            style = _winlib.GetWindowLongA(hwnd, 0xffffffecL)
+            style |= 0x00080000
+            _winlib.SetWindowLongA(hwnd, 0xffffffecL, style)
+            _winlib.SetLayeredWindowAttributes(hwnd, 0, amount, 2)
+            
+        except ImportError:
+            import win32api, win32con, winxpgui
+            _winlib = win32api.LoadLibrary("user32")
+            pSetLayeredWindowAttributes = win32api.GetProcAddress(
+                _winlib, "SetLayeredWindowAttributes")
+            if pSetLayeredWindowAttributes == None:
+                return
+            exstyle = win32api.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
+            if 0 == (exstyle & 0x80000):
+                win32api.SetWindowLong(hwnd,
+                                       win32con.GWL_EXSTYLE,
+                                       exstyle | 0x80000)           
+            winxpgui.SetLayeredWindowAttributes(hwnd, 0, amount, 2)
+
+
+            
+app = wx.App(False)
+frm = Frame()
+frm.Show()
+app.MainLoop()
diff --git a/wxPython/tests/test_widgetLayout.py b/wxPython/tests/test_widgetLayout.py
new file mode 100644 (file)
index 0000000..8188e8d
--- /dev/null
@@ -0,0 +1,516 @@
+"""
+This test app is meant to help check if a widget has a good
+DoGetBestSize method (in C++) by allowing the user to dynamically
+create any non-toplevel widget which will be placed in a Sizer
+designed to show how the widget will be sized naturally.
+"""
+
+import wx
+import sys
+import os
+
+# stuff for debugging
+print "wx.VERSION_STRING = ", wx.VERSION_STRING
+print "pid:", os.getpid()
+##raw_input("Press Enter...")
+
+testImage = os.path.join(os.path.dirname(sys.argv[0]), 'image.png')
+
+
+class LayoutTestFrame(wx.Frame):
+    def __init__(self):
+        wx.Frame.__init__(self, None, -1, "Widget Layout Tester")
+
+        p = wx.Panel(self)
+
+        # Create control widgets
+        self.testHistory = wx.ListBox(p, -1, size=(150, 300))
+        self.moduleName = wx.TextCtrl(p, -1, "wx")
+        self.className  = wx.TextCtrl(p, -1, "")
+        self.parameters = wx.TextCtrl(p, -1, "")
+        self.postCreate = wx.TextCtrl(p, -1, "", size=(1,75),
+                                      style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
+        self.expression = wx.TextCtrl(p, -1, "", style=wx.TE_READONLY)
+        self.docstring  = wx.TextCtrl(p, -1, "", size=(1,75),
+                                      style=wx.TE_READONLY|wx.TE_MULTILINE|wx.TE_DONTWRAP)
+
+        self.expression.SetBackgroundColour(
+            wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
+        self.docstring.SetBackgroundColour(
+            wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
+
+
+        addBtn = wx.Button(p, -1, "Add")
+        remBtn = wx.Button(p, -1, "Remove")
+        repBtn = wx.Button(p, -1, "Replace")
+        createBtn = wx.Button(p, -1, " Create Widget ")
+        createBtn.SetDefault()
+        destroyBtn = wx.Button(p, -1, "Destroy Widget")
+        clearBtn = wx.Button(p, -1, "Clear")
+
+        self.createBtn = createBtn
+        self.destroyBtn = destroyBtn
+
+        bottomPanel = wx.Panel(p, style=wx.SUNKEN_BORDER, name="bottomPanel")
+        bottomPanel.SetMinSize((640,240))
+        bottomPanel.SetOwnBackgroundColour("light blue")
+
+        self.testPanel = wx.Panel(bottomPanel, name="testPanel")
+        self.testPanel.SetOwnBackgroundColour((205, 183, 181)) # mistyrose3
+        self.testWidget = None
+
+        self.infoPane = InfoPane(p)
+
+        # setup event bindings
+        self.Bind(wx.EVT_TEXT, self.OnUpdate, self.moduleName)
+        self.Bind(wx.EVT_TEXT, self.OnUpdate, self.className)
+        self.Bind(wx.EVT_TEXT, self.OnUpdate, self.parameters)
+        self.Bind(wx.EVT_UPDATE_UI, self.OnEnableCreate, createBtn)
+        self.Bind(wx.EVT_UPDATE_UI, self.OnEnableDestroy, destroyBtn)
+        self.Bind(wx.EVT_BUTTON, self.OnCreateWidget, createBtn)
+        self.Bind(wx.EVT_BUTTON, self.OnDestroyWidget, destroyBtn)
+        self.Bind(wx.EVT_BUTTON, self.OnClear, clearBtn)
+
+        self.Bind(wx.EVT_CLOSE, self.OnSaveHistory)
+
+        self.Bind(wx.EVT_BUTTON, self.OnAddHistory, addBtn)
+        self.Bind(wx.EVT_BUTTON, self.OnRemoveHistory, remBtn)
+        self.Bind(wx.EVT_BUTTON, self.OnReplaceHistory, repBtn)
+        self.Bind(wx.EVT_LISTBOX, self.OnHistorySelect, self.testHistory)
+        self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnHistoryActivate, self.testHistory)
+
+        if 'wxMac' in wx.PlatformInfo or 'wxGTK' in wx.PlatformInfo:
+            self.testHistory.Bind(wx.EVT_KEY_DOWN, self.OnHistoryKey)
+            
+
+        # setup the layout
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        topSizer  = wx.BoxSizer(wx.HORIZONTAL)
+        ctlsSizer = wx.FlexGridSizer(2, 2, 5, 5)
+        ctlsSizer.AddGrowableCol(1)
+        btnSizer =  wx.BoxSizer(wx.HORIZONTAL)
+
+        topSizer.Add(self.testHistory, 0, wx.RIGHT, 30)
+
+        ctlsSizer.Add(wx.StaticText(p, -1, "Module name:"),
+                      0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+        mcSizer = wx.BoxSizer(wx.HORIZONTAL)
+        mcSizer.Add(self.moduleName, 0, 0)
+        mcSizer.Add(wx.StaticText(p, -1, "Class name:"),
+                      0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL |wx.LEFT|wx.RIGHT, 10)
+        mcSizer.Add(self.className, 1, 0)
+        ctlsSizer.Add(mcSizer, 0, wx.EXPAND)
+
+        ctlsSizer.Add(wx.StaticText(p, -1, "Parameters:"),
+                      0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+        ctlsSizer.Add(self.parameters, 0, wx.EXPAND)
+        ctlsSizer.Add(wx.StaticText(p, -1, "Create Expr:"),
+                      0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+        ctlsSizer.Add(self.expression, 0, wx.EXPAND)
+        ctlsSizer.Add(wx.StaticText(p, -1, "Post create:"), 0, wx.ALIGN_RIGHT)
+        ctlsSizer.Add(self.postCreate, 0, wx.EXPAND)
+        ctlsSizer.Add(wx.StaticText(p, -1, "DocString:"), 0, wx.ALIGN_RIGHT)
+        ctlsSizer.Add(self.docstring, 0, wx.EXPAND)
+        ctlsSizer.AddGrowableRow(4)
+        topSizer.Add(ctlsSizer, 1, wx.EXPAND)
+  
+        btnSizer.Add((5,5))
+        btnSizer.Add(addBtn, 0, wx.RIGHT, 5)
+        btnSizer.Add(remBtn, 0, wx.RIGHT, 5)
+        btnSizer.Add(repBtn, 0, wx.RIGHT, 5)
+        btnSizer.Add((0,0), 1)
+        btnSizer.Add(createBtn, 0, wx.RIGHT, 5)
+        btnSizer.Add(destroyBtn, 0, wx.RIGHT, 5)
+        btnSizer.Add(clearBtn, 0, wx.RIGHT, 5)
+        btnSizer.Add((0,0), 1)
+
+        mainSizer.Add(topSizer, 0, wx.EXPAND|wx.ALL, 10)
+        mainSizer.Add(btnSizer, 0, wx.EXPAND)
+        mainSizer.Add((10,10))
+        ##mainSizer.Add(wx.StaticLine(p, -1), 0, wx.EXPAND)
+        mainSizer.Add(bottomPanel, 1, wx.EXPAND)
+
+        mainSizer.Add(self.infoPane, 0, wx.EXPAND)
+
+        self.bottomSizer = sz = wx.BoxSizer(wx.VERTICAL)
+        sz.Add((0,0), 1)
+        sz.Add(self.testPanel, 0, wx.ALIGN_CENTER)
+        sz.Add((0,0), 1)
+        bottomPanel.SetSizer(sz)
+
+        p.SetSizerAndFit(mainSizer)
+        self.Fit()
+
+        self.PopulateHistory()
+
+
+
+
+    def PopulateHistory(self):
+        """
+        Load and eval a list of lists from a file, load the contents
+        into self.testHistory
+        """
+        fname = os.path.join(os.path.dirname(sys.argv[0]), 'widgets.cfg')
+        try:
+            self.history = eval(open(fname).read())
+        except:
+            self.history = []
+
+        self.testHistory.Clear()
+
+        for idx in range(len(self.history)):
+            item = self.history[idx]
+            # check if it is too short
+            while len(item) < 4:
+                item.append('')
+
+            # add it to the listbox
+            self.testHistory.Append(item[0] + '.' + item[1])
+
+        self.needSaved = False
+
+
+    def OnSaveHistory(self, evt):
+        if self.needSaved:
+            fname = os.path.join(os.path.dirname(sys.argv[0]), 'widgets.cfg')
+            f = open(fname, 'wb')
+            f.write('[\n')
+            for item in self.history:
+                f.write(str(item) + ',\n')
+            f.write(']\n')
+            f.close()
+        evt.Skip()
+
+
+    def OnAddHistory(self, evt):
+        moduleName = self.moduleName.GetValue()
+        className  = self.className.GetValue()
+        parameters = self.parameters.GetValue()
+        postCreate = self.postCreate.GetValue()
+
+        item = [str(moduleName), str(className), str(parameters), str(postCreate)]
+        self.history.append(item)
+        self.testHistory.Append(item[0] + '.' + item[1])
+
+        self.testHistory.SetSelection(len(self.history)-1)
+        self.needSaved = True
+
+
+    def OnRemoveHistory(self, evt):
+        idx = self.testHistory.GetSelection()
+        if idx != wx.NOT_FOUND:
+            del self.history[idx]
+            self.testHistory.Delete(idx)
+        self.needSaved = True
+        self.OnClear(None)
+
+
+    def OnReplaceHistory(self, evt):
+        idx = self.testHistory.GetSelection()
+        if idx != wx.NOT_FOUND:
+            moduleName = self.moduleName.GetValue()
+            className  = self.className.GetValue()
+            parameters = self.parameters.GetValue()
+            postCreate = self.postCreate.GetValue()
+
+            item = [str(moduleName), str(className), str(parameters), str(postCreate)]
+            self.history[idx] = item
+            self.testHistory.SetString(idx, item[0] + '.' + item[1])
+        self.needSaved = True
+
+
+    def OnHistorySelect(self, evt):
+        #idx = self.testHistory.GetSelection()
+        idx = evt.GetInt()
+        if idx != wx.NOT_FOUND:
+            item = self.history[idx]
+            self.moduleName.SetValue(item[0])
+            self.className.SetValue(item[1])
+            self.parameters.SetValue(item[2])
+            self.postCreate.SetValue(item[3])
+            if 'wxMac' in wx.PlatformInfo:
+                self.OnUpdate(None)
+
+
+    def OnHistoryActivate(self, evt):
+        btn = self.testHistory.GetTopLevelParent().GetDefaultItem()
+        if btn is not None:
+            e = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId())
+            btn.Command(e)
+
+
+    def OnHistoryKey(self, evt):
+        key = evt.GetKeyCode()
+        if key == wx.WXK_RETURN:
+            self.OnHistoryActivate(None)
+        else:
+            evt.Skip()
+
+
+    def OnUpdate(self, evt):
+        # get the details from the form
+        moduleName = self.moduleName.GetValue()
+        className  = self.className.GetValue()
+        parameters = self.parameters.GetValue()
+
+        expr = "w = %s.%s( testPanel, %s )" % (moduleName, className, parameters)
+        self.expression.SetValue(expr)
+
+        docstring = None
+        try:
+            docstring = eval("%s.%s.__init__.__doc__" % (moduleName, className))
+        except:
+            pass
+        if docstring is not None:
+            self.docstring.SetValue(docstring)
+        else:
+            self.docstring.SetValue("")
+
+    def OnEnableDestroy(self, evt):
+        evt.Enable(self.testWidget is not None)
+
+    def OnEnableCreate(self, evt):
+        evt.Enable(self.testWidget is None)
+
+
+    def OnCreateWidget(self, evt):
+        if self.testWidget is not None:
+            return
+
+        testPanel = self.testPanel
+
+        # get the details from the form
+        moduleName = self.moduleName.GetValue()
+        className  = self.className.GetValue()
+        parameters = self.parameters.GetValue()
+        expr       = self.expression.GetValue()[4:]
+        postCreate = self.postCreate.GetValue()
+        if 'wxMac' in wx.PlatformInfo:
+            postCreate = postCreate.replace('\r', '\n')
+
+        # make sure the module is imported already
+        if not sys.modules.has_key(moduleName):
+            try:
+                m = __import__(moduleName)
+            except importError:
+                wx.MessageBox("Unable to import module!", "Error")
+                return
+
+        # create the widget
+        try:
+            w = eval(expr)
+        except Exception, e:
+            wx.MessageBox("Got a '%s' Exception!" % e.__class__.__name__, "Error")
+            import traceback
+            traceback.print_exc()
+            return
+
+        # Is there postCreate code?
+        if postCreate:
+            ns = {}
+            ns.update(globals())
+            ns.update(locals())
+            try:
+                exec postCreate in ns
+            except Exception, e:
+                wx.MessageBox("Got a '%s' Exception!" % e.__class__.__name__, "Error")
+                import traceback
+                traceback.print_exc()
+                w.Destroy()
+                return
+
+        # Put the widget in a sizer and the sizer in the testPanel
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(w, 0, wx.ALL, 5)
+        self.testPanel.SetSizer(sizer)
+        self.testWidget = w
+        self.bottomSizer.Layout()
+
+        # make the destroy button be default now
+        self.destroyBtn.SetDefault()
+
+        self.infoPane.Update(w, testPanel)
+        
+
+    def OnDestroyWidget(self, evt):
+        self.testWidget.Destroy()
+        self.testWidget = None
+        self.testPanel.SetSizer(None, True)
+        self.testPanel.Refresh()
+
+        # ensure the panel shrinks again now that it has no sizer
+        self.testPanel.SetMinSize((20,20))
+        self.bottomSizer.Layout()
+        self.testPanel.SetMinSize(wx.DefaultSize)
+
+        # make the create button be default now
+        self.createBtn.SetDefault()
+
+        self.infoPane.Clear()
+        
+
+    def OnClear(self, evt):
+        self.moduleName.SetValue("")
+        self.className.SetValue("")
+        self.parameters.SetValue("")
+        self.expression.SetValue("")
+        self.docstring.SetValue("")
+        self.postCreate.SetValue("")
+
+
+
+
+
+
+class InfoPane(wx.Panel):
+    """
+    This class is used to display details of various properties of the
+    widget and the testPanel to aid with debugging.
+    """
+    def __init__(self, parent):
+        wx.Panel.__init__(self, parent)
+
+        # create subwidgets
+        self.wPane = SizeInfoPane(self, "Widget")
+        self.tpPane= SizeInfoPane(self, "testPanel")
+        self.cPane = ColourInfoPanel(self, "Widget colours")
+
+        # Setup the layout
+        sizer = wx.BoxSizer(wx.HORIZONTAL)
+        sizer.Add(self.wPane, 0, wx.EXPAND|wx.ALL, 5)
+        sizer.Add(self.tpPane, 0, wx.EXPAND|wx.ALL, 5)
+        sizer.Add(self.cPane, 0, wx.EXPAND|wx.ALL, 5)
+
+        self.SetSizer(sizer)
+
+        
+    def Update(self, w, tp):
+        self.wPane.Update(w)
+        self.tpPane.Update(tp)
+        self.cPane.Update(w)
+        
+    def Clear(self):
+        self.wPane.Clear()
+        self.tpPane.Clear()
+        self.cPane.Clear()
+
+
+
+class SizeInfoPane(wx.Panel):
+    """
+    A component of the InfoPane that shows vaious window size attributes.
+    """
+    def __init__(self, parent, label):
+        wx.Panel.__init__(self, parent)
+
+        # create subwidgets
+        sb = wx.StaticBox(self, -1, label)
+        self._size        = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+        self._minsize     = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+        self._bestsize    = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+        self._effmin     = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+
+        # setup the layout
+        fgs = wx.FlexGridSizer(2, 2, 5, 5)
+        fgs.AddGrowableCol(1)
+
+        fgs.Add(wx.StaticText(self, -1, "Size:"),
+                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+        fgs.Add(self._size, 0, wx.EXPAND)
+
+        fgs.Add(wx.StaticText(self, -1, "MinSize:"),
+                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+        fgs.Add(self._minsize, 0, wx.EXPAND)
+
+        fgs.Add(wx.StaticText(self, -1, "BestSize:"),
+                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+        fgs.Add(self._bestsize, 0, wx.EXPAND)
+
+        fgs.Add(wx.StaticText(self, -1, "EffectiveMinSize:"),
+                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+        fgs.Add(self._effmin, 0, wx.EXPAND)
+
+        sbs = wx.StaticBoxSizer(sb, wx.VERTICAL)
+        sbs.Add(fgs, 0, wx.EXPAND|wx.ALL, 4)
+
+        self.SetSizer(sbs)
+
+
+    def Update(self, win):
+        self._size.SetValue(       str(win.GetSize()) )
+        self._minsize.SetValue(    str(win.GetMinSize()) )
+        self._bestsize.SetValue(   str(win.GetBestSize()) )
+        self._effmin.SetValue(    str(win.GetEffectiveMinSize()) )
+
+    def Clear(self):
+        self._size.SetValue("")
+        self._minsize.SetValue("")
+        self._bestsize.SetValue("")
+        self._effmin.SetValue("")
+
+
+
+class ColourInfoPanel(wx.Panel):
+    """
+    A component of the InfoPane that shows fg and bg colour attributes.    
+    """
+    def __init__(self, parent, label):
+        wx.Panel.__init__(self, parent)
+
+        # create subwidgets
+        sb = wx.StaticBox(self, -1, label)
+        self._fgtxt = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+        self._fgclr = wx.Panel(self, style=wx.SIMPLE_BORDER)
+        self._fgclr.SetMinSize((20,20))
+        self._bgtxt = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+        self._bgclr = wx.Panel(self, style=wx.SIMPLE_BORDER)
+        self._bgclr.SetMinSize((20,20))
+
+        # setup the layout
+        fgs = wx.FlexGridSizer(2, 3, 5, 5)
+        fgs.AddGrowableCol(1)
+
+        fgs.Add(wx.StaticText(self, -1, "FG colour:"),
+                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+        fgs.Add(self._fgtxt, 0, wx.EXPAND)
+        fgs.Add(self._fgclr)
+
+        fgs.Add(wx.StaticText(self, -1, "BG colour:"),
+                0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+        fgs.Add(self._bgtxt, 0, wx.EXPAND)
+        fgs.Add(self._bgclr)
+
+        sbs = wx.StaticBoxSizer(sb, wx.VERTICAL)
+        sbs.Add(fgs, 0, wx.EXPAND|wx.ALL, 4)
+
+        self.SetSizer(sbs)
+
+
+    def Update(self, win):
+        def clr2hex(c, cp):
+            cp.SetBackgroundColour(c)
+            cp.Refresh()
+            return "#%02X%02X%02X" % c.Get()
+        
+        self._fgtxt.SetValue( clr2hex(win.GetForegroundColour(), self._fgclr) )
+        self._bgtxt.SetValue( clr2hex(win.GetBackgroundColour(), self._bgclr) )
+
+
+    def Clear(self):
+        self._fgtxt.SetValue("")
+        self._bgtxt.SetValue("")
+        self._fgclr.SetBackgroundColour(self.GetBackgroundColour())
+        self._bgclr.SetBackgroundColour(self.GetBackgroundColour())
+        self._fgclr.Refresh()
+        self._bgclr.Refresh()
+
+
+        
+
+app = wx.PySimpleApp(redirect=False)
+frame = LayoutTestFrame()
+app.SetTopWindow(frame)
+frame.Show()
+app.MainLoop()
+
diff --git a/wxPython/tests/widgets.cfg b/wxPython/tests/widgets.cfg
new file mode 100644 (file)
index 0000000..22900c7
--- /dev/null
@@ -0,0 +1,61 @@
+[
+['wx', 'BitmapButton', '-1, wx.Bitmap("image.png")', ''],
+['wx', 'Button', '-1, "default"', 'w.SetDefault()'],
+['wx', 'Button', '-1, "larger font"', 'w.SetFont(wx.Font(20, wx.SWISS, wx.NORMAL, wx.NORMAL))\n\n\n'],
+['wx', 'Button', '-1, "normal"', ''],
+['wx', 'Button', '-1, "with a longer, longer label"', ''],
+['wx', 'Button', 'wx.ID_SAVE', ''],
+['wx', 'CheckBox', '-1, "checkbox with longer label"', ''],
+['wx', 'CheckBox', '-1, "checkbox"', ''],
+['wx', 'CheckListBox', '-1, size=(100,-1), choices="one two three four five six seven eight".split()', ''],
+['wx', 'Choice', '-1, choices="one two three four five six seven eight".split()', ''],
+['wx', 'Choice', '-1, size=(50,-1), choices="one two three four five six seven eight".split()', ''],
+['wx', 'ComboBox', '-1, value="default", choices="one two three four five six seven eight".split()', ''],
+['wx', 'ComboBox', '-1, value="default", size=(75,-1), choices="one two three four five six seven eight".split()', ''],
+['wx', 'ComboBox', '-1, value="read-only", style=wx.CB_READONLY, choices="read-only one two three four five six seven eight".split()', ''],
+['wx', 'Gauge', '-1, 100', 'w.SetValue(40)'],
+['wx', 'Gauge', '-1, 100, style=wx.GA_VERTICAL', 'w.SetValue(60)'],
+['wx', 'ListBox', '-1, choices="one two three four five six seven eight".split()', ''],
+['wx', 'ListCtrl', 'style=wx.LC_REPORT', 'w.InsertColumn(0, "Col1")\nw.InsertStringItem(0, "Item 0")\nw.InsertStringItem(0, "Item 1")\n#w.SetSizeHints((200,100))'],
+['wx', 'ListCtrl', 'style=wx.LC_REPORT, size=(100,100)', 'w.InsertColumn(0, "Col1")\nw.InsertStringItem(0, "Item 0")\nw.InsertStringItem(0, "Item 1")\n#w.SetSizeHints((200,100))'],
+['wx', 'Notebook', '', 'p = wx.Panel(w)\np.SetMinSize((150,150))\nw.AddPage(p, "test")\n'],
+['wx', 'Panel', 'size=(150,150)', ''],
+['wx', 'RadioBox', '-1, "label", choices="one two three four".split()', ''],
+['wx', 'RadioBox', '-1, "label", majorDimension=2, choices="one two three four five six seven eight".split()', ''],
+['wx', 'RadioBox', '-1, "label", style=wx.RA_VERTICAL, choices="one two three four".split()', ''],
+['wx', 'RadioBox', '-1, "label", style=wx.RA_VERTICAL, majorDimension=2, choices="one two three four five six seven eight".split()', ''],
+['wx', 'RadioButton', '-1, "radio button"', ''],
+['wx', 'ScrollBar', '', ''],
+['wx', 'ScrollBar', 'style=wx.SB_VERTICAL', ''],
+['wx', 'Slider', '-1, 20, 0, 100', ''],
+['wx', 'Slider', '-1, 20, 0, 100, size=(400, -1)', ''],
+['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_LABELS', ''],
+['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_LABELS, size=(400,-1)', ''],
+['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_VERTICAL', ''],
+['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_VERTICAL|wx.SL_LABELS', ''],
+['wx', 'SpinButton', 'style=wx.SP_HORIZONTAL', ''],
+['wx', 'SpinButton', 'style=wx.SP_VERTICAL', ''],
+['wx', 'SpinCtrl', '', ''],
+['wx', 'SpinCtrl', 'size=(50, -1)', ''],
+['wx', 'StaticBitmap', '-1, wx.Bitmap(testImage)', ''],
+['wx', 'StaticBitmap', '-1, wx.Bitmap(testImage), style=wx.SUNKEN_BORDER', ''],
+['wx', 'StaticBox', '-1, "a longer label"', ''],
+['wx', 'StaticBox', '-1, "label"', ''],
+['wx', 'StaticBox', '-1, "with a size", size=(100,75)', ''],
+['wx', 'StaticLine', '-1, size=(-1,100), style=wx.LI_VERTICAL', ''],
+['wx', 'StaticLine', '-1, size=(100,-1), style=wx.LI_HORIZONTAL', ''],
+['wx', 'StaticText', '-1, "New font"', 'f = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)\nw.SetFont(f)\n'],
+['wx', 'StaticText', '-1, "normal"', ''],
+['wx', 'StaticText', '-1, "normal"', 'w.SetLabel("This is a longer label")'],
+['wx', 'TextCtrl', '-1, "default"', ''],
+['wx', 'TextCtrl', '-1, "larger size", size=(200, -1)', ''],
+['wx', 'TextCtrl', '-1, "small", size=(30,-1)', ''],
+['wx', 'TextCtrl', '-1, "some\\ndefault text\\n", size=(200, -1), style=wx.TE_MULTILINE', 'w.AppendText("Here is some more text\\n")'],
+['wx', 'TreeCtrl', '', ''],
+['wx.calendar', 'CalendarCtrl', '-1', ''],
+['wx.calendar', 'CalendarCtrl', '-1', 'w.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL))\n'],
+['wx.calendar', 'CalendarCtrl', '-1, style=wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION', ''],
+['wx.grid', 'Grid', '', 'w.CreateGrid(5,5)\n'],
+['wx.grid', 'Grid', 'size=(400,200)', 'w.CreateGrid(100,50)\n'],
+['wx.lib.stattext', 'GenStaticText', '-1, "New font"', 'f = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)\nw.SetFont(f)\n'],
+]
index 817fa01b5409a30cfb8904715e127f40714d8349..a33b8574ce7e6c22ae45afab45ad1cc63e6317a2 100644 (file)
@@ -1766,7 +1766,7 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
             TR_NO_LINES                             # don't draw lines at all
             TR_LINES_AT_ROOT                        # connect top-level nodes
             TR_TWIST_BUTTONS                        # draw mac-like twist buttons
-            TR_SINGLE                               # single selection mode                           
+            TR_SINGLE                               # single selection mode     
             TR_MULTIPLE                             # can select multiple items
             TR_EXTENDED                             # todo: allow extended selection
             TR_HAS_VARIABLE_ROW_HEIGHT              # allows rows to have variable height
@@ -1870,12 +1870,13 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
         self._vistaselection = False        
 
         # Connection lines style
+        grey = (160,160,160)
         if wx.Platform != "__WXMAC__":
-            self._dottedPen = wx.Pen("grey", 1, wx.USER_DASH)
+            self._dottedPen = wx.Pen(grey, 1, wx.USER_DASH)
             self._dottedPen.SetDashes([1,1])
             self._dottedPen.SetCap(wx.CAP_BUTT)
         else:
-            self._dottedPen = wx.Pen("grey", 1)
+            self._dottedPen = wx.Pen(grey, 1)
 
         # Pen Used To Draw The Border Around Selected Items
         self._borderPen = wx.BLACK_PEN
@@ -1893,8 +1894,6 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
             if major < 10:
                 style |= TR_ROW_LINES
 
-        self._windowStyle = style
-
         # Create the default check image list        
         self.SetImageListCheck(13, 13)
 
@@ -2169,13 +2168,13 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
         dc = wx.ClientDC(self)
         self.RefreshLine(item)
 
-        if self._windowStyle & TR_AUTO_CHECK_CHILD:
+        if self.HasFlag(TR_AUTO_CHECK_CHILD):
             ischeck = self.IsItemChecked(item)
             self.AutoCheckChild(item, ischeck)
-        if self._windowStyle & TR_AUTO_CHECK_PARENT:
+        if self.HasFlag(TR_AUTO_CHECK_PARENT):
             ischeck = self.IsItemChecked(item)
             self.AutoCheckParent(item, ischeck)
-        elif self._windowStyle & TR_AUTO_TOGGLE_CHILD:
+        elif self.HasFlag(TR_AUTO_TOGGLE_CHILD):
             self.AutoToggleChild(item)
 
         e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId())
@@ -2310,12 +2309,6 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
         self._dirty = True
 
 
-    def HasFlag(self, flag):
-        """Returns whether CustomTreeCtrl has a flag."""
-
-        return self._windowStyle & flag
-    
-
     def HasChildren(self, item):
         """Returns whether an item has children or not."""
 
@@ -2349,19 +2342,19 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
         # want to update the inherited styles, but right now
         # none of the parents has updatable styles
 
-        if self._windowStyle & TR_MULTIPLE and not (styles & TR_MULTIPLE):
+        if self.HasFlag(TR_MULTIPLE) and not (styles & TR_MULTIPLE):
             selections = self.GetSelections()
             for select in selections[0:-1]:
                 self.SelectItem(select, False)
 
-        self._windowStyle = styles
+        self.SetWindowStyle(styles)
         self._dirty = True
 
 
     def GetTreeStyle(self):
         """Returns the CustomTreeCtrl style."""
 
-        return self._windowStyle
+        return self.GetWindowStyle()
     
 
     def HasButtons(self):
@@ -3155,10 +3148,10 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
     def DoInsertItem(self, parentId, previous, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
         """Actually inserts an item in the tree."""
 
-        if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
 
-        if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
 
         if ct_type < 0 or ct_type > 2:
@@ -3190,10 +3183,10 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
         if self._anchor:
             raise Exception("\nERROR: Tree Can Have Only One Root")
 
-        if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
 
-        if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
 
         if ct_type < 0 or ct_type > 2:
@@ -3226,10 +3219,10 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
     def PrependItem(self, parent, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
         """Appends an item as a first child of parent."""
 
-        if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
 
-        if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
         
         return self.DoInsertItem(parent, 0, text, ct_type, wnd, image, selImage, data)
@@ -3238,10 +3231,10 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
     def InsertItemByItem(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
         """Auxiliary function to cope with the C++ hideous multifunction."""
         
-        if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
 
-        if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
         
         parent = parentId
@@ -3264,10 +3257,10 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
     def InsertItemByIndex(self, parentId, before, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
         """Auxiliary function to cope with the C++ hideous multifunction."""
 
-        if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
 
-        if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
         
         parent = parentId
@@ -3282,10 +3275,10 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
     def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
         """Inserts an item after the given previous."""
 
-        if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
 
-        if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
         
         if type(input) == type(1):
@@ -3297,10 +3290,10 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
     def AppendItem(self, parentId, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
         """Appends an item as a last child of its parent."""
 
-        if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
 
-        if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+        if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
             raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
         
         parent = parentId
@@ -3562,7 +3555,8 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
         for child in self._itemWithWindow:
             if not self.IsVisible(child):
                 wnd = child.GetWindow()
-                wnd.Hide()
+                if wnd:
+                    wnd.Hide()
             
 
     def Unselect(self):
@@ -4565,7 +4559,7 @@ class CustomTreeCtrl(wx.PyScrolledWindow):
                     
                 else: # no custom buttons
 
-                    if self._windowStyle & TR_TWIST_BUTTONS:
+                    if self.HasFlag(TR_TWIST_BUTTONS):
                         # We draw something like the Mac twist buttons
                         
                         dc.SetPen(wx.BLACK_PEN)
index 5a9b74186c2fb63b1cda56b3a5334ddf125a59da..60d773a1d090661d72bc54ce4fef29082275d471 100644 (file)
@@ -1,82 +1,64 @@
+
 from __future__ import division
 
 try:
-    from Numeric import array,asarray,Float,cos, sin, pi,sum,minimum,maximum,Int32,zeros, ones, concatenate, sqrt, argmin, power, absolute, matrixmultiply, transpose, sometrue, arange, hypot
+    import numpy as N
 except ImportError:
-    try:
-        from numarray import array, asarray, Float, cos, sin, pi, sum, minimum, maximum, Int32, zeros, concatenate, matrixmultiply, transpose, sometrue, arange, hypot
-    except ImportError:
-        raise ImportError("I could not import either Numeric or numarray")
-
-from time import clock, sleep
-
-import Resources # A file with icons, etc for FloatCanvas
+    raise ImportError("I could not import numpy")
 
+from time import clock
 import wx
 
-import types
-import os        
+from Utilities import BBox
+
 
 ## A global variable to hold the Pixels per inch that wxWindows thinks is in use
 ## This is used for scaling fonts.
 ## This can't be computed on module __init__, because a wx.App might not have initialized yet.
-global ScreenPPI
+global FontScale
 
-## a custom Exceptions:
+## Custom Exceptions:
 
 class FloatCanvasError(Exception):
     pass
 
 ## Create all the mouse events
-# I don't see a need for these two, but maybe some day!
-#EVT_FC_ENTER_WINDOW = wx.NewEventType()
-#EVT_FC_LEAVE_WINDOW = wx.NewEventType()
-EVT_FC_LEFT_DOWN = wx.NewEventType() 
+EVT_FC_ENTER_WINDOW = wx.NewEventType()
+EVT_FC_LEAVE_WINDOW = wx.NewEventType()
+EVT_FC_LEFT_DOWN = wx.NewEventType()
 EVT_FC_LEFT_UP  = wx.NewEventType()
-EVT_FC_LEFT_DCLICK = wx.NewEventType() 
-EVT_FC_MIDDLE_DOWN = wx.NewEventType() 
-EVT_FC_MIDDLE_UP = wx.NewEventType() 
-EVT_FC_MIDDLE_DCLICK = wx.NewEventType() 
-EVT_FC_RIGHT_DOWN = wx.NewEventType() 
-EVT_FC_RIGHT_UP = wx.NewEventType() 
-EVT_FC_RIGHT_DCLICK = wx.NewEventType() 
-EVT_FC_MOTION = wx.NewEventType() 
-EVT_FC_MOUSEWHEEL = wx.NewEventType() 
+EVT_FC_LEFT_DCLICK = wx.NewEventType()
+EVT_FC_MIDDLE_DOWN = wx.NewEventType()
+EVT_FC_MIDDLE_UP = wx.NewEventType()
+EVT_FC_MIDDLE_DCLICK = wx.NewEventType()
+EVT_FC_RIGHT_DOWN = wx.NewEventType()
+EVT_FC_RIGHT_UP = wx.NewEventType()
+EVT_FC_RIGHT_DCLICK = wx.NewEventType()
+EVT_FC_MOTION = wx.NewEventType()
+EVT_FC_MOUSEWHEEL = wx.NewEventType()
 ## these two are for the hit-test stuff, I never make them real Events
+## fixme: could I use the PyEventBinder for the Object events too?
 EVT_FC_ENTER_OBJECT = wx.NewEventType()
 EVT_FC_LEAVE_OBJECT = wx.NewEventType()
 
-##Create all mouse event binding functions
-#def EVT_ENTER_WINDOW( window, function ):
-#    window.Connect( -1, -1, EVT_FC_ENTER_WINDOW, function ) 
-#def EVT_LEAVE_WINDOW( window, function ):
-#    window.Connect( -1, -1,EVT_FC_LEAVE_WINDOW , function ) 
-def EVT_LEFT_DOWN( window, function ):  
-    window.Connect( -1, -1,EVT_FC_LEFT_DOWN , function )
-def EVT_LEFT_UP( window, function ):
-    window.Connect( -1, -1,EVT_FC_LEFT_UP , function )
-def EVT_LEFT_DCLICK  ( window, function ):
-    window.Connect( -1, -1,EVT_FC_LEFT_DCLICK , function )
-def EVT_MIDDLE_DOWN  ( window, function ):
-    window.Connect( -1, -1,EVT_FC_MIDDLE_DOWN , function )
-def EVT_MIDDLE_UP  ( window, function ):
-    window.Connect( -1, -1,EVT_FC_MIDDLE_UP , function )
-def EVT_MIDDLE_DCLICK  ( window, function ):
-    window.Connect( -1, -1,EVT_FC_MIDDLE_DCLICK , function )
-def EVT_RIGHT_DOWN  ( window, function ):
-    window.Connect( -1, -1,EVT_FC_RIGHT_DOWN , function )
-def EVT_RIGHT_UP( window, function ):
-    window.Connect( -1, -1,EVT_FC_RIGHT_UP , function )
-def EVT_RIGHT_DCLICK( window, function ):
-    window.Connect( -1, -1,EVT_FC_RIGHT_DCLICK , function )
-def EVT_MOTION( window, function ):
-    window.Connect( -1, -1,EVT_FC_MOTION , function )
-def EVT_MOUSEWHEEL( window, function ):
-    window.Connect( -1, -1,EVT_FC_MOUSEWHEEL , function )
+##Create all mouse event binding objects
+EVT_LEFT_DOWN = wx.PyEventBinder(EVT_FC_LEFT_DOWN)
+EVT_LEFT_UP = wx.PyEventBinder(EVT_FC_LEFT_UP)
+EVT_LEFT_DCLICK = wx.PyEventBinder(EVT_FC_LEFT_DCLICK)
+EVT_MIDDLE_DOWN = wx.PyEventBinder(EVT_FC_MIDDLE_DOWN)
+EVT_MIDDLE_UP = wx.PyEventBinder(EVT_FC_MIDDLE_UP)
+EVT_MIDDLE_DCLICK = wx.PyEventBinder(EVT_FC_MIDDLE_DCLICK)
+EVT_RIGHT_DOWN = wx.PyEventBinder(EVT_FC_RIGHT_DOWN)
+EVT_RIGHT_UP = wx.PyEventBinder(EVT_FC_RIGHT_UP)
+EVT_RIGHT_DCLICK = wx.PyEventBinder(EVT_FC_RIGHT_DCLICK)
+EVT_MOTION = wx.PyEventBinder(EVT_FC_MOTION)
+EVT_ENTER_WINDOW = wx.PyEventBinder(EVT_FC_ENTER_WINDOW)
+EVT_LEAVE_WINDOW = wx.PyEventBinder(EVT_FC_LEAVE_WINDOW)
+EVT_MOUSEWHEEL = wx.PyEventBinder(EVT_FC_MOUSEWHEEL)
 
 class _MouseEvent(wx.PyCommandEvent):
 
-    """
+    """!
 
     This event class takes a regular wxWindows mouse event as a parameter,
     and wraps it so that there is access to all the original methods. This
@@ -99,24 +81,20 @@ class _MouseEvent(wx.PyCommandEvent):
         self.SetEventType( EventType )
         self._NativeEvent = NativeEvent
         self.Coords = Coords
-    
-# I don't think this is used.
-#    def SetCoords(self,Coords):
-#        self.Coords = Coords
-        
+
     def GetCoords(self):
         return self.Coords
 
     def __getattr__(self, name):
-        #return eval(self.NativeEvent.__getattr__(name) )
         return getattr(self._NativeEvent, name)
 
 def _cycleidxs(indexcount, maxvalue, step):
 
-    """
+    """!
     Utility function used by _colorGenerator
 
     """
+    
     if indexcount == 0:
         yield ()
     else:
@@ -126,97 +104,50 @@ def _cycleidxs(indexcount, maxvalue, step):
 
 def _colorGenerator():
 
-    """
+    """!
 
-    Generates a seris of unique colors used to do hit-tests with the HIt
+    Generates a series of unique colors used to do hit-tests with the Hit
     Test bitmap
-
     """
-    import sys
-    if sys.platform == 'darwin':
-        depth = 24
-    else:
-        b = wx.EmptyBitmap(1,1)
-        depth = b.GetDepth()
-    if depth == 16:
-        step = 8
-    elif depth >= 24:
+
+    depth = wx.GetDisplayDepth()
+##    ##there have been problems with 16 bbp displays, to I'm disabling this for now.
+##    if depth == 16:
+##        print "Warning: There have been problems with hit-testing on 16bbp displays"
+##        step = 8
+    if depth >= 24:
         step = 1
     else:
-        raise "ColorGenerator does not work with depth = %s" % depth
+        msg= ["ColorGenerator does not work with depth = %s" % depth]
+        msg.append("It is required for hit testing -- binding events to mouse")
+        msg.append("actions on objects on the Canvas.")
+        msg.append("Please set your display to 24bit")
+        msg.append("Alternatively, the code could be adapted to 16 bit if that's required")
+        raise FloatCanvasError(msg)
     return _cycleidxs(indexcount=3, maxvalue=256, step=step)
 
-
-#### I don't know if the Set objects are useful, beyond the pointset
-#### object. The problem is that when zoomed in, the BB is checked to see
-#### whether to draw the object.  A Set object can defeat this. One day
-#### I plan to write some custon C++ code to draw sets of objects
-
-##class ObjectSetMixin:
-##    """
-##    A mix-in class for draw objects that are sets of objects
-
-##    It contains methods for setting lists of pens and brushes
-
-##    """
-##    def SetPens(self,LineColors,LineStyles,LineWidths):
-##        """
-##        This method used when an object could have a list of pens, rather than just one
-##        It is used for LineSet, and perhaps others in the future.
-
-##        fixme: this should be in a mixin
-
-##        fixme: this is really kludgy, there has got to be a better way!
-
-##        """
-
-##        length = 1
-##        if type(LineColors) == types.ListType:
-##            length = len(LineColors)
-##        else:
-##            LineColors = [LineColors]
-
-##        if type(LineStyles) == types.ListType:
-##            length = len(LineStyles)
-##        else:
-##            LineStyles = [LineStyles]
-
-##        if type(LineWidths) == types.ListType:
-##            length = len(LineWidths)
-##        else:
-##            LineWidths = [LineWidths]
-
-##        if length > 1:
-##            if len(LineColors) == 1:
-##                LineColors = LineColors*length
-##            if len(LineStyles) == 1:
-##                LineStyles = LineStyles*length
-##            if len(LineWidths) == 1:
-##                LineWidths = LineWidths*length
-
-##        self.Pens = []
-##        for (LineColor,LineStyle,LineWidth) in zip(LineColors,LineStyles,LineWidths):
-##            if LineColor is None or LineStyle is None:
-##                self.Pens.append(wx.TRANSPARENT_PEN)
-##                # what's this for?> self.LineStyle = 'Transparent'
-##            if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
-##                Pen = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])
-##                self.Pens.append(Pen)
-##            else:
-##                self.Pens.append(self.PenList[(LineColor,LineStyle,LineWidth)])
-##        if length == 1:
-##            self.Pens = self.Pens[0]
-
 class DrawObject:
-    """
+    """!
     This is the base class for all the objects that can be drawn.
 
     One must subclass from this (and an assortment of Mixins) to create
     a new DrawObject.
 
+      \note This class contain a series of static dictionaries:
+
+      * BrushList
+      * PenList
+      * FillStyleList
+      * LineStyleList
+
+      Is this still necessary?
+
     """
 
     def __init__(self, InForeground  = False, IsVisible = True):
+        """! \param InForeground (bool)
+             \param IsVisible (Bool)
+        """
         self.InForeground = InForeground
 
         self._Canvas = None
@@ -235,7 +166,7 @@ class DrawObject:
         self.Pen = None
 
         self.FillStyle = "Solid"
-        
+
         self.Visible = IsVisible
 
     # I pre-define all these as class variables to provide an easier
@@ -287,12 +218,27 @@ class DrawObject:
             "DotDash"    : wx.DOT_DASH,
             }
 
+#    def BBFromPoints(self, Points):
+#        """!
+#        Calculates a Bounding box from a set of points (NX2 array of coordinates)
+#        \param Points (array?) 
+#        """
+#
+#        ## fixme: this could be done with array.min() and vstack() in numpy.
+#        ##        This could use the Utilities.BBox module now.
+#        #return N.array( (N.minimum.reduce(Points),
+#        #                 N.maximum.reduce(Points) ),
+#        #                )
+#        return BBox.fromPoints(Points)
+
     def Bind(self, Event, CallBackFun):
         self.CallBackFuncs[Event] = CallBackFun
         self.HitAble = True
         self._Canvas.UseHitTest = True
-        if not self._Canvas._HTdc:
-            self._Canvas.MakeNewHTdc()
+        if self.InForeground and self._Canvas._ForegroundHTBitmap is None:
+            self._Canvas.MakeNewForegroundHTBitmap()
+        elif self._Canvas._HTBitmap is None:
+            self._Canvas.MakeNewHTBitmap()
         if not self.HitColor:
             if not self._Canvas.HitColorGenerator:
                 self._Canvas.HitColorGenerator = _colorGenerator()
@@ -343,6 +289,21 @@ class DrawObject:
         else:
             self.HitPen = self.PenList.setdefault( (HitColor, "solid", self.HitLineWidth),  wx.Pen(HitColor, self.HitLineWidth, self.LineStyleList["Solid"]) )
 
+    ## Just to make sure that they will always be there
+    ##   the appropriate ones should be overridden in the subclasses
+    def SetColor(self, Color):
+        pass
+    def SetLineColor(self, LineColor):
+        pass
+    def SetLineStyle(self, LineStyle):
+        pass
+    def SetLineWidth(self, LineWidth):
+        pass
+    def SetFillColor(self, FillColor):
+        pass
+    def SetFillStyle(self, FillStyle):
+        pass
+
     def PutInBackground(self):
         if self._Canvas and self.InForeground:
             self._Canvas._ForeDrawList.remove(self)
@@ -358,11 +319,70 @@ class DrawObject:
             self.InForeground = True
 
     def Hide(self):
+        """! \brief Make an object hidden.
+        """
         self.Visible = False
 
     def Show(self):
+        """! \brief Make an object visible on the canvas.
+        """
         self.Visible = True
 
+class Group(DrawObject): 
+    """
+    A group of other FloatCanvas Objects
+    
+    Not all DrawObject methods may apply here. In particular, you can't Bind events to a group.
+    
+    Note that if an object is in more than one group, it will get drawn more than once.
+    
+    """
+
+    def __init__(self, ObjectList=[], InForeground  = False, IsVisible = True):
+        self.ObjectList = list(ObjectList)
+        DrawObject.__init__(self, InForeground, IsVisible)
+        self.CalcBoundingBox()
+
+    def AddObject(self, obj):
+        self.ObjectList.append(obj)
+        self.BoundingBox.Merge(obj.BoundingBox)
+
+    def AddObjects(self, Objects):
+        for o in Objects:
+            self.AddObject(o)
+            
+    def CalcBoundingBox(self):
+        if self.ObjectList:
+            BB = BBox.asBBox(self.ObjectList[0].BoundingBox)
+            for obj in self.ObjectList[1:]:
+                BB.Merge(obj.BoundingBox)
+        else:
+            BB = None
+        self.BoundingBox = BB
+
+    def SetColor(self, Color):
+        for o in self.ObjectList:
+            o.SetColor(Color)
+    def SetLineColor(self, Color):
+        for o in self.ObjectList:
+            o.SetLineColor(Color)
+    def SetLineStyle(self, LineStyle):
+        for o in self.ObjectList:
+            o.SetLineStyle(LineStyle)
+    def SetLineWidth(self, LineWidth):
+        for o in self.ObjectList:
+            o.SetLineWidth(LineWidth)
+    def SetFillColor(self, Color):
+        for o in self.ObjectList:
+            o.SetFillColor(Color)
+    def SetFillStyle(self, FillStyle):
+        for o in self.ObjectList:
+            o.SetFillStyle(FillStyle)
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel = None, HTdc=None):
+        for obj in self.ObjectList:
+            obj._Draw(dc, WorldToPixel, ScaleWorldToPixel, HTdc)
+            
+
 class ColorOnlyMixin:
     """
 
@@ -375,7 +395,7 @@ class ColorOnlyMixin:
         self.SetPen(Color,"Solid",1)
         self.SetBrush(Color,"Solid")
 
-    SetFillColor = SetColor # Just to provide a consistant interface 
+    SetFillColor = SetColor # Just to provide a consistant interface
 
 class LineOnlyMixin:
     """
@@ -388,7 +408,8 @@ class LineOnlyMixin:
     def SetLineColor(self, LineColor):
         self.LineColor = LineColor
         self.SetPen(LineColor,self.LineStyle,self.LineWidth)
-
+    SetColor = SetLineColor# so that it will do somethign reasonable
+    
     def SetLineStyle(self, LineStyle):
         self.LineStyle = LineStyle
         self.SetPen(self.LineColor,LineStyle,self.LineWidth)
@@ -411,7 +432,16 @@ class LineAndFillMixin(LineOnlyMixin):
     def SetFillStyle(self, FillStyle):
         self.FillStyle = FillStyle
         self.SetBrush(self.FillColor,FillStyle)
-    
+
+    def SetUpDraw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
+        dc.SetPen(self.Pen)
+        dc.SetBrush(self.Brush)
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.SetBrush(self.HitBrush)
+        return ( WorldToPixel(self.XY),
+                 ScaleWorldToPixel(self.WH) )
+
 class XYObjectMixin:
     """
 
@@ -427,29 +457,28 @@ class XYObjectMixin:
         (dx,dy) pair. Ideally a Numpy array of shape (2,)
 
         """
-        
-        Delta = asarray(Delta, Float)
+
+        Delta = N.asarray(Delta, N.float)
         self.XY += Delta
-        self.BoundingBox = self.BoundingBox + Delta
-        
+        self.BoundingBox += Delta
+
         if self._Canvas:
-            self._Canvas.BoundingBoxDirty = True      
+            self._Canvas.BoundingBoxDirty = True
 
     def CalcBoundingBox(self):
         ## This may get overwritten in some subclasses
-        self.BoundingBox = array( (self.XY, self.XY), Float )
+        self.BoundingBox = N.array( (self.XY, self.XY), N.float )
+        self.BoundingBox = BBox.asBBox((self.XY, self.XY))
 
     def SetPoint(self, xy):
-        xy = array( xy, Float)
+        xy = N.array(xy, N.float)
         xy.shape = (2,)
-        Delta = xy - self.XY
-        
+
         self.XY = xy
-        self.BoundingBox = self.BoundingBox + Delta
+        self.CalcBoundingBox()
 
-        #self.CalcBoundingBox()
         if self._Canvas:
-            self._Canvas.BoundingBoxDirty = True     
+            self._Canvas.BoundingBoxDirty = True
 
 class PointsObjectMixin:
     """
@@ -459,29 +488,21 @@ class PointsObjectMixin:
 
     """
 
-
-## This is code for the PointsObjectMixin object, it needs to be adapted and tested.
-## Is the neccesary at all: you can always do:
-##    Object.SetPoints( Object.Points + delta, copy = False)    
-##    def Move(self, Delta ):
-##        """
-
-##        Move(Delta): moves the object by delta, where delta is an (dx,
-##        dy) pair. Ideally a Numpy array of shape (2,)
-
-##        """
+    def Move(self, Delta):
+        """
+        Move(Delta): moves the object by delta, where delta is an (dx,
+        dy) pair. Ideally a Numpy array of shape (2,)
+        """
         
-##        Delta = array(Delta, Float)
-##        self.XY += Delta
-##        self.BoundingBox = self.BoundingBox + Delta##array((self.XY, (self.XY + self.WH)), Float)
-##        if self._Canvas:
-##            self._Canvas.BoundingBoxDirty = True      
+        Delta = N.asarray(Delta, N.float)
+        Delta.shape = (2,)
+        self.Points += Delta
+        self.BoundingBox += Delta
+        if self._Canvas:
+            self._Canvas.BoundingBoxDirty = True
 
     def CalcBoundingBox(self):
-        self.BoundingBox = array(((min(self.Points[:,0]),
-                                   min(self.Points[:,1]) ),
-                                  (max(self.Points[:,0]),
-                                   max(self.Points[:,1]) ) ), Float )
+        self.BoundingBox = BBox.fromPoints(self.Points)
         if self._Canvas:
             self._Canvas.BoundingBoxDirty = True
 
@@ -498,17 +519,17 @@ class PointsObjectMixin:
         Points = Object.Points
         Points += (5,10) # shifts the points 5 in the x dir, and 10 in the y dir.
         Object.SetPoints(Points, False) # Sets the points to the same array as it was
-        
+
         """
         if copy:
-            self.Points = array(Points, Float)
+            self.Points = N.array(Points, N.float)
             self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
         else:
-            self.Points = asarray(Points, Float)
+            self.Points = N.asarray(Points, N.float)
         self.CalcBoundingBox()
 
-     
-class Polygon(DrawObject,PointsObjectMixin,LineAndFillMixin):
+
+class Polygon(PointsObjectMixin, LineAndFillMixin, DrawObject):
 
     """
 
@@ -530,8 +551,8 @@ class Polygon(DrawObject,PointsObjectMixin,LineAndFillMixin):
                  FillColor    = None,
                  FillStyle    = "Solid",
                  InForeground = False):
-        DrawObject.__init__(self,InForeground)
-        self.Points = array(Points,Float) # this DOES need to make a copy
+        DrawObject.__init__(self, InForeground)
+        self.Points = N.array(Points ,N.float) # this DOES need to make a copy
         self.CalcBoundingBox()
 
         self.LineColor = LineColor
@@ -554,41 +575,8 @@ class Polygon(DrawObject,PointsObjectMixin,LineAndFillMixin):
             HTdc.SetPen(self.HitPen)
             HTdc.SetBrush(self.HitBrush)
             HTdc.DrawPolygon(Points)
-            
-##class PolygonSet(DrawObject):
-##    """
-##    The PolygonSet class takes a Geometry.Polygon object.
-##    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
-    
-##    it creates a set of line segments, from (x1,y1) to (x2,y2)
-    
-##    """
-    
-##    def __init__(self,PolySet,LineColors,LineStyles,LineWidths,FillColors,FillStyles,InForeground = False):
-##        DrawObject.__init__(self, InForeground)
-
-##        ##fixme: there should be some error checking for everything being the right length.
-
-        
-##        self.Points = array(Points,Float)
-##        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
 
-##        self.LineColors = LineColors
-##        self.LineStyles = LineStyles
-##        self.LineWidths = LineWidths
-##        self.FillColors = FillColors
-##        self.FillStyles = FillStyles
-
-##        self.SetPens(LineColors,LineStyles,LineWidths)
-
-##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
-##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
-##        Points = WorldToPixel(self.Points)
-##        Points.shape = (-1,4)
-##        dc.DrawLineList(Points,self.Pens)
-
-class Line(DrawObject,PointsObjectMixin,LineOnlyMixin):
+class Line(PointsObjectMixin, LineOnlyMixin, DrawObject,):
     """
 
     The Line class takes a list of 2-tuples, or a NX2 NumPy Float array
@@ -606,7 +594,7 @@ class Line(DrawObject,PointsObjectMixin,LineOnlyMixin):
         DrawObject.__init__(self, InForeground)
 
 
-        self.Points = array(Points,Float)
+        self.Points = N.array(Points,N.float)
         self.CalcBoundingBox()
 
         self.LineColor = LineColor
@@ -617,7 +605,7 @@ class Line(DrawObject,PointsObjectMixin,LineOnlyMixin):
 
         self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
 
-            
+
     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
         Points = WorldToPixel(self.Points)
         dc.SetPen(self.Pen)
@@ -626,18 +614,31 @@ class Line(DrawObject,PointsObjectMixin,LineOnlyMixin):
             HTdc.SetPen(self.HitPen)
             HTdc.DrawLines(Points)
 
-class Arrow(DrawObject,XYObjectMixin,LineOnlyMixin):
+class Spline(Line):
+    def __init__(self, *args, **kwargs):
+            Line.__init__(self, *args, **kwargs)
+
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+        Points = WorldToPixel(self.Points)
+        dc.SetPen(self.Pen)
+        dc.DrawSpline(Points)
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.DrawSpline(Points)
+
+
+class Arrow(XYObjectMixin, LineOnlyMixin, DrawObject):
     """
 
     Arrow(XY, # coords of origin of arrow (x,y)
           Length, # length of arrow in pixels
           theta, # angle of arrow in degrees: zero is straight up
-                 # angle is to the right
+                 # +angle is to the right
           LineColor = "Black",
           LineStyle = "Solid",
-          LineWidth    = 1, 
-          ArrowHeadSize = 4,
-          ArrowHeadAngle = 45,
+          LineWidth    = 1,
+          ArrowHeadSize = 4, # size of arrowhead in pixels
+          ArrowHeadAngle = 45, # angle of arrow head in degrees
           InForeground = False):
 
     It will draw an arrow , starting at the point, (X,Y) pointing in
@@ -658,12 +659,12 @@ class Arrow(DrawObject,XYObjectMixin,LineOnlyMixin):
 
         DrawObject.__init__(self, InForeground)
 
-        self.XY = array(XY, Float)
-        self.XY.shape = (2,) # Make sure it is a 1X2 array, even if there is only one point
+        self.XY = N.array(XY, N.float)
+        self.XY.shape = (2,) # Make sure it is a length 2 vector
         self.Length = Length
         self.Direction = float(Direction)
-        self.ArrowHeadSize = ArrowHeadSize 
-        self.ArrowHeadAngle = float(ArrowHeadAngle)        
+        self.ArrowHeadSize = ArrowHeadSize
+        self.ArrowHeadAngle = float(ArrowHeadAngle)
 
         self.CalcArrowPoints()
         self.CalcBoundingBox()
@@ -680,7 +681,7 @@ class Arrow(DrawObject,XYObjectMixin,LineOnlyMixin):
     def SetDirection(self, Direction):
         self.Direction = float(Direction)
         self.CalcArrowPoints()
-        
+
     def SetLength(self, Length):
         self.Length = Length
         self.CalcArrowPoints()
@@ -689,26 +690,37 @@ class Arrow(DrawObject,XYObjectMixin,LineOnlyMixin):
         self.Direction = float(Direction)
         self.Length = Length
         self.CalcArrowPoints()
-        
-    def SetLength(self, Length):
-        self.Length = Length
-        self.CalcArrowPoints()
 
-    ## fixme: cache this?
+##    def CalcArrowPoints(self):
+##        L = self.Length
+##        S = self.ArrowHeadSize
+##        phi = self.ArrowHeadAngle * N.pi / 360
+##        theta = (self.Direction-90.0) * N.pi / 180
+##        ArrowPoints = N.array( ( (0, L, L - S*N.cos(phi),L, L - S*N.cos(phi) ),
+##                               (0, 0, S*N.sin(phi),    0, -S*N.sin(phi)    ) ),
+##                             N.float )
+##        RotationMatrix = N.array( ( ( N.cos(theta), -N.sin(theta) ),
+##                                  ( N.sin(theta), N.cos(theta) ) ),
+##                                N.float
+##                                )
+##        ArrowPoints = N.matrixmultiply(RotationMatrix, ArrowPoints)
+##        self.ArrowPoints = N.transpose(ArrowPoints)
+
     def CalcArrowPoints(self):
         L = self.Length
         S = self.ArrowHeadSize
-        phi = self.ArrowHeadAngle * pi / 360
-        theta = (self.Direction-90.0) * pi / 180
-        ArrowPoints = array( ( (0, L, L - S*cos(phi),L, L - S*cos(phi) ),
-                               (0, 0, S*sin(phi),    0, -S*sin(phi)    ) ),
-                             Float )
-        RotationMatrix = array( ( ( cos(theta), -sin(theta) ),
-                                  ( sin(theta), cos(theta) ) ),
-                                Float
-                                )
-        ArrowPoints = matrixmultiply(RotationMatrix, ArrowPoints)
-        self.ArrowPoints = transpose(ArrowPoints)
+        phi = self.ArrowHeadAngle * N.pi / 360
+        theta = (270 - self.Direction) * N.pi / 180
+        AP = N.array( ( (0,0),
+                        (0,0),
+                        (N.cos(theta - phi), -N.sin(theta - phi) ),
+                        (0,0),
+                        (N.cos(theta + phi), -N.sin(theta + phi) ),
+                        ), N.float )
+        AP *= S
+        shift = (-L*N.cos(theta), L*N.sin(theta) )
+        AP[1:,:] += shift
+        self.ArrowPoints = AP
 
     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
         dc.SetPen(self.Pen)
@@ -719,38 +731,86 @@ class Arrow(DrawObject,XYObjectMixin,LineOnlyMixin):
             HTdc.SetPen(self.HitPen)
             HTdc.DrawLines(ArrowPoints)
 
-##class LineSet(DrawObject, ObjectSetMixin):
-##    """
-##    The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
-##    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
-    
-##    it creates a set of line segments, from (x1,y1) to (x2,y2)
-    
-##    """
-    
-##    def __init__(self,Points,LineColors,LineStyles,LineWidths,InForeground = False):
-##        DrawObject.__init__(self, InForeground)
 
-##        NumLines = len(Points) / 2
-##        ##fixme: there should be some error checking for everything being the right length.
+class ArrowLine(PointsObjectMixin, LineOnlyMixin, DrawObject):
+    """
+
+    ArrowLine(Points, # coords of points
+              LineColor = "Black",
+              LineStyle = "Solid",
+              LineWidth    = 1,
+              ArrowHeadSize = 4, # in pixels
+              ArrowHeadAngle = 45,
+              InForeground = False):
 
-        
-##        self.Points = array(Points,Float)
-##        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+    It will draw a set of arrows from point to point.
+
+    It takes a list of 2-tuples, or a NX2 NumPy Float array
+    of point coordinates.
 
-##        self.LineColors = LineColors
-##        self.LineStyles = LineStyles
-##        self.LineWidths = LineWidths
 
-##        self.SetPens(LineColors,LineStyles,LineWidths)
+    """
+
+    def __init__(self,
+                 Points,
+                 LineColor = "Black",
+                 LineStyle = "Solid",
+                 LineWidth    = 1, # pixels
+                 ArrowHeadSize = 8, # pixels
+                 ArrowHeadAngle = 30, # degrees
+                 InForeground = False):
+
+        DrawObject.__init__(self, InForeground)
+
+        self.Points = N.asarray(Points,N.float)
+        self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
+        self.ArrowHeadSize = ArrowHeadSize
+        self.ArrowHeadAngle = float(ArrowHeadAngle)
+
+        self.CalcArrowPoints()
+        self.CalcBoundingBox()
+
+        self.LineColor = LineColor
+        self.LineStyle = LineStyle
+        self.LineWidth = LineWidth
+
+        self.SetPen(LineColor,LineStyle,LineWidth)
+
+        self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
+
+    def CalcArrowPoints(self):
+        S = self.ArrowHeadSize
+        phi = self.ArrowHeadAngle * N.pi / 360
+        Points = self.Points
+        n = Points.shape[0]
+        self.ArrowPoints = N.zeros((n-1, 3, 2), N.float)
+        for i in xrange(n-1):
+            dx, dy = self.Points[i] - self.Points[i+1]
+            theta = N.arctan2(dy, dx)
+            AP = N.array( (
+                            (N.cos(theta - phi), -N.sin(theta-phi)),
+                            (0,0),
+                            (N.cos(theta + phi), -N.sin(theta + phi))
+                            ),
+                          N.float )
+            self.ArrowPoints[i,:,:] = AP
+        self.ArrowPoints *= S
+
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+        Points = WorldToPixel(self.Points)
+        ArrowPoints = Points[1:,N.newaxis,:] + self.ArrowPoints
+        dc.SetPen(self.Pen)
+        dc.DrawLines(Points)
+        for arrow in ArrowPoints:
+                dc.DrawLines(arrow)
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.DrawLines(Points)
+            for arrow in ArrowPoints:
+                HTdc.DrawLines(arrow)
 
-##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
-##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
-##        Points = WorldToPixel(self.Points)
-##        Points.shape = (-1,4)
-##        dc.DrawLineList(Points,self.Pens)
 
-class PointSet(DrawObject,PointsObjectMixin, ColorOnlyMixin):
+class PointSet(PointsObjectMixin, ColorOnlyMixin, DrawObject):
     """
 
     The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of
@@ -775,20 +835,20 @@ class PointSet(DrawObject,PointsObjectMixin, ColorOnlyMixin):
     def __init__(self, Points, Color = "Black", Diameter =  1, InForeground = False):
         DrawObject.__init__(self,InForeground)
 
-        self.Points = array(Points,Float)
+        self.Points = N.array(Points,N.float)
         self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
         self.CalcBoundingBox()
         self.Diameter = Diameter
 
-        self.HitLineWidth = self.MinHitLineWidth
+        self.HitLineWidth = min(self.MinHitLineWidth, Diameter)
         self.SetColor(Color)
 
     def SetDiameter(self,Diameter):
             self.Diameter = Diameter
-            
+
     def FindClosestPoint(self, XY):
         """
-        
+
         Returns the index of the closest point to the point, XY, given
         in World coordinates. It's essentially random which you get if
         there are more than one that are the same.
@@ -799,8 +859,8 @@ class PointSet(DrawObject,PointsObjectMixin, ColorOnlyMixin):
 
         """
         d = self.Points - XY
-        return argmin(hypot(d[:,0],d[:,1]))
-    
+        return N.argmin(N.hypot(d[:,0],d[:,1]))
+
 
     def DrawD2(self, dc, Points):
         # A Little optimization for a diameter2 - point
@@ -822,7 +882,7 @@ class PointSet(DrawObject,PointsObjectMixin, ColorOnlyMixin):
             ##fixme: I really should add a DrawCircleList to wxPython
             if len(Points) > 100:
                 xy = Points
-                xywh = concatenate((xy-radius, ones(xy.shape) * self.Diameter ), 1 )
+                xywh = N.concatenate((xy-radius, N.ones(xy.shape) * self.Diameter ), 1 )
                 dc.DrawEllipseList(xywh)
             else:
                 for xy in Points:
@@ -837,15 +897,15 @@ class PointSet(DrawObject,PointsObjectMixin, ColorOnlyMixin):
             else:
                 if len(Points) > 100:
                     xy = Points
-                    xywh = concatenate((xy-radius, ones(xy.shape) * self.Diameter ), 1 )
+                    xywh = N.concatenate((xy-radius, N.ones(xy.shape) * self.Diameter ), 1 )
                     HTdc.DrawEllipseList(xywh)
                 else:
                     for xy in Points:
                         HTdc.DrawCircle(xy[0],xy[1], radius)
 
-class Point(DrawObject,XYObjectMixin,ColorOnlyMixin):
+class Point(XYObjectMixin, ColorOnlyMixin, DrawObject):
     """
-    
+
     The Point class takes a 2-tuple, or a (2,) NumPy array of point
     coordinates.
 
@@ -858,9 +918,9 @@ class Point(DrawObject,XYObjectMixin,ColorOnlyMixin):
     """
     def __init__(self, XY, Color = "Black", Diameter =  1, InForeground = False):
         DrawObject.__init__(self, InForeground)
-        self.XY = array(XY, Float)
-        self.XY.shape = (2,) # Make sure it is a 1X2 array, even if there is only one point
+
+        self.XY = N.array(XY, N.float)
+        self.XY.shape = (2,) # Make sure it is a length 2 vector
         self.CalcBoundingBox()
         self.SetColor(Color)
         self.Diameter = Diameter
@@ -888,9 +948,9 @@ class Point(DrawObject,XYObjectMixin,ColorOnlyMixin):
                 HTdc.SetBrush(self.HitBrush)
                 HTdc.DrawCircle(xy[0],xy[1], radius)
 
-class SquarePoint(DrawObject,XYObjectMixin,ColorOnlyMixin):
+class SquarePoint(XYObjectMixin, ColorOnlyMixin, DrawObject):
     """
-    
+
     The SquarePoint class takes a 2-tuple, or a (2,) NumPy array of point
     coordinates. It produces a square dot, centered on Point
 
@@ -903,9 +963,9 @@ class SquarePoint(DrawObject,XYObjectMixin,ColorOnlyMixin):
     """
     def __init__(self, Point, Color = "Black", Size =  4, InForeground = False):
         DrawObject.__init__(self, InForeground)
-        self.XY = array(Point, Float)
-        self.XY.shape = (2,) # Make sure it is a 1X2 array, even if there is only one point
+
+        self.XY = N.array(Point, N.float)
+        self.XY.shape = (2,) # Make sure it is a length 2 vector
         self.CalcBoundingBox()
         self.SetColor(Color)
         self.Size = Size
@@ -919,7 +979,7 @@ class SquarePoint(DrawObject,XYObjectMixin,ColorOnlyMixin):
         Size = self.Size
         dc.SetPen(self.Pen)
         xc,yc = WorldToPixel(self.XY)
-        
+
         if self.Size <= 1:
             dc.DrawPoint(xc, yc)
         else:
@@ -935,7 +995,7 @@ class SquarePoint(DrawObject,XYObjectMixin,ColorOnlyMixin):
                 HTdc.SetBrush(self.HitBrush)
                 HTdc.DrawRectangle(x, y, Size, Size)
 
-class RectEllipse(DrawObject, XYObjectMixin, LineAndFillMixin):
+class RectEllipse(XYObjectMixin, LineAndFillMixin, DrawObject):
     def __init__(self, XY, WH,
                  LineColor = "Black",
                  LineStyle = "Solid",
@@ -943,14 +1003,10 @@ class RectEllipse(DrawObject, XYObjectMixin, LineAndFillMixin):
                  FillColor    = None,
                  FillStyle    = "Solid",
                  InForeground = False):
-        
+
         DrawObject.__init__(self,InForeground)
 
-        self.XY = array( XY, Float)
-        self.XY.shape = (2,)
-        self.WH = array( WH, Float )
-        self.WH.shape = (2,)
-        self.BoundingBox = array((self.XY, (self.XY + self.WH)), Float)
+        self.SetShape(XY, WH)
         self.LineColor = LineColor
         self.LineStyle = LineStyle
         self.LineWidth = LineWidth
@@ -963,23 +1019,19 @@ class RectEllipse(DrawObject, XYObjectMixin, LineAndFillMixin):
         self.SetBrush(FillColor,FillStyle)
 
     def SetShape(self, XY, WH):
-        self.XY = array( XY, Float)
-        self.WH = array( WH, Float )
+        self.XY = N.array( XY, N.float)
+        self.XY.shape = (2,)
+        self.WH = N.array( WH, N.float)
+        self.WH.shape = (2,)
         self.CalcBoundingBox()
 
 
-    def SetUpDraw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
-        dc.SetPen(self.Pen)
-        dc.SetBrush(self.Brush)
-        if HTdc and self.HitAble:
-            HTdc.SetPen(self.HitPen)
-            HTdc.SetBrush(self.HitBrush)
-        return ( WorldToPixel(self.XY),
-                 ScaleWorldToPixel(self.WH) )
-
     def CalcBoundingBox(self):
-        self.BoundingBox = array((self.XY, (self.XY + self.WH) ), Float)
-        self._Canvas.BoundingBoxDirty = True
+        # you need this in case Width or Height are negative
+        corners = N.array((self.XY, (self.XY + self.WH) ), N.float)
+        self.BoundingBox = BBox.fromPoints(corners)
+        if self._Canvas:
+            self._Canvas.BoundingBoxDirty = True
 
 
 class Rectangle(RectEllipse):
@@ -993,6 +1045,8 @@ class Rectangle(RectEllipse):
         if HTdc and self.HitAble:
             HTdc.DrawRectanglePointSize(XY, WH)
 
+
+
 class Ellipse(RectEllipse):
 
     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
@@ -1005,9 +1059,9 @@ class Ellipse(RectEllipse):
             HTdc.DrawEllipsePointSize(XY, WH)
 
 class Circle(Ellipse):
-
+    ## fixme: this should probably be use the DC.DrawCircle!
     def __init__(self, XY, Diameter, **kwargs):
-        self.Center = array(XY, Float)
+        self.Center = N.array(XY, N.float)
         Diameter = float(Diameter)
         RectEllipse.__init__(self ,
                              self.Center - Diameter/2.0,
@@ -1020,7 +1074,7 @@ class Circle(Ellipse):
         self.SetShape(XY,
                       (Diameter, Diameter)
                       )
-        
+
 class TextObjectMixin(XYObjectMixin):
     """
 
@@ -1028,29 +1082,29 @@ class TextObjectMixin(XYObjectMixin):
     the Text objects
 
     """
-    
+
     ## I'm caching fonts, because on GTK, getting a new font can take a
     ## while. However, it gets cleared after every full draw as hanging
     ## on to a bunch of large fonts takes a massive amount of memory.
 
     FontList = {}
 
-    LayoutFontSize = 12 # font size used for calculating layout
+    LayoutFontSize = 16 # font size used for calculating layout
 
-    def SetFont(self, Size, Family, Style, Weight, Underline, FaceName):
+    def SetFont(self, Size, Family, Style, Weight, Underlined, FaceName):
         self.Font = self.FontList.setdefault( (Size,
                                                Family,
                                                Style,
                                                Weight,
-                                               Underline,
+                                               Underlined,
                                                FaceName),
-                                               wx.Font(Size,
+                                               #wx.FontFromPixelSize((0.45*Size,Size), # this seemed to give a decent height/width ratio on Windows
+                                               wx.Font(Size, 
                                                        Family,
-                                                       Style, 
+                                                       Style,
                                                        Weight,
-                                                       Underline,
+                                                       Underlined,
                                                        FaceName) )
-        return self.Font
 
     def SetColor(self, Color):
         self.Color = Color
@@ -1086,16 +1140,16 @@ class TextObjectMixin(XYObjectMixin):
     ## pad is the extra space around the text
     ## if world = 1, the vertical shift is done in y-up coordinates
     ShiftFunDict = {'tl': lambda x, y, w, h, world=0, pad=0: (x + pad,     y + pad - 2*world*pad),
-                    'tc': lambda x, y, w, h, world=0, pad=0: (x - w/2,     y + pad - 2*world*pad), 
-                    'tr': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y + pad - 2*world*pad), 
-                    'cl': lambda x, y, w, h, world=0, pad=0: (x + pad,     y - h/2 + world*h), 
-                    'cc': lambda x, y, w, h, world=0, pad=0: (x - w/2,     y - h/2 + world*h), 
+                    'tc': lambda x, y, w, h, world=0, pad=0: (x - w/2,     y + pad - 2*world*pad),
+                    'tr': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y + pad - 2*world*pad),
+                    'cl': lambda x, y, w, h, world=0, pad=0: (x + pad,     y - h/2 + world*h),
+                    'cc': lambda x, y, w, h, world=0, pad=0: (x - w/2,     y - h/2 + world*h),
                     'cr': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y - h/2 + world*h),
                     'bl': lambda x, y, w, h, world=0, pad=0: (x + pad,     y - h + 2*world*h - pad + world*2*pad) ,
-                    'bc': lambda x, y, w, h, world=0, pad=0: (x - w/2,     y - h + 2*world*h - pad + world*2*pad) , 
+                    'bc': lambda x, y, w, h, world=0, pad=0: (x - w/2,     y - h + 2*world*h - pad + world*2*pad) ,
                     'br': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y - h + 2*world*h - pad + world*2*pad)}
 
-class Text(DrawObject, TextObjectMixin):
+class Text(TextObjectMixin, DrawObject, ):
     """
     This class creates a text object, placed at the coordinates,
     x,y. the "Position" argument is a two charactor string, indicating
@@ -1113,48 +1167,48 @@ class Text(DrawObject, TextObjectMixin):
     Family:
         Font family, a generic way of referring to fonts without
         specifying actual facename. One of:
-            wx.DEFAULT:  Chooses a default font. 
-            wx.DECORATIVE: A decorative font. 
-            wx.ROMAN: A formal, serif font. 
-            wx.SCRIPT: A handwriting font. 
-            wx.SWISS: A sans-serif font. 
+            wx.DEFAULT:  Chooses a default font.
+            wx.DECORATIVE: A decorative font.
+            wx.ROMAN: A formal, serif font.
+            wx.SCRIPT: A handwriting font.
+            wx.SWISS: A sans-serif font.
             wx.MODERN: A fixed pitch font.
         NOTE: these are only as good as the wxWindows defaults, which aren't so good.
     Style:
         One of wx.NORMAL, wx.SLANT and wx.ITALIC.
     Weight:
         One of wx.NORMAL, wx.LIGHT and wx.BOLD.
-    Underline:
+    Underlined:
         The value can be True or False. At present this may have an an
         effect on Windows only.
 
     Alternatively, you can set the kw arg: Font, to a wx.Font, and the
     above will be ignored.
-    
+
     The size is fixed, and does not scale with the drawing.
 
     The hit-test is done on the entire text extent
 
     """
-    
+
     def __init__(self,String, xy,
-                 Size =  12,
+                 Size =  14,
                  Color = "Black",
                  BackgroundColor = None,
                  Family = wx.MODERN,
                  Style = wx.NORMAL,
                  Weight = wx.NORMAL,
-                 Underline = False,
+                 Underlined = False,
                  Position = 'tl',
                  InForeground = False,
                  Font = None):
-        
+
         DrawObject.__init__(self,InForeground)
 
         self.String = String
-        # Input size in in Pixels, compute points size from PPI info.
+        # Input size in in Pixels, compute points size from FontScaleinfo.
         # fixme: for printing, we'll have to do something a little different
-        self.Size = int(round(72.0 * Size / ScreenPPI))
+        self.Size = Size * FontScale
 
         self.Color = Color
         self.BackgroundColor = BackgroundColor
@@ -1162,17 +1216,17 @@ class Text(DrawObject, TextObjectMixin):
         if not Font:
             FaceName = ''
         else:
-            FaceName           =  Font.GetFaceName()           
+            FaceName           =  Font.GetFaceName()
             Family             =  Font.GetFamily()
-            Size               =  Font.GetPointSize()          
+            Size               =  Font.GetPointSize()
             Style              =  Font.GetStyle()
-            Underlined         =  Font.GetUnderlined()         
+            Underlined         =  Font.GetUnderlined()
             Weight             =  Font.GetWeight()
-        self.SetFont(Size, Family, Style, Weight, Underline, FaceName)
+        self.SetFont(Size, Family, Style, Weight, Underlined, FaceName)
 
-        self.BoundingBox = array((xy, xy),Float)
+        self.BoundingBox = BBox.asBBox((xy, xy))
 
-        self.XY = asarray(xy)
+        self.XY = N.asarray(xy)
         self.XY.shape = (2,)
 
         (self.TextWidth, self.TextHeight) = (None, None)
@@ -1196,7 +1250,7 @@ class Text(DrawObject, TextObjectMixin):
             HTdc.SetBrush(self.HitBrush)
             HTdc.DrawRectanglePointSize(XY, (self.TextWidth, self.TextHeight) )
 
-class ScaledText(DrawObject, TextObjectMixin):
+class ScaledText(TextObjectMixin, DrawObject, ):
     """
     This class creates a text object that is scaled when zoomed.  It is
     placed at the coordinates, x,y. the "Position" argument is a two
@@ -1213,25 +1267,25 @@ class ScaledText(DrawObject, TextObjectMixin):
     Family:
         Font family, a generic way of referring to fonts without
         specifying actual facename. One of:
-            wx.DEFAULT:  Chooses a default font. 
-            wx.DECORATI: A decorative font. 
-            wx.ROMAN: A formal, serif font. 
-            wx.SCRIPT: A handwriting font. 
-            wx.SWISS: A sans-serif font. 
+            wx.DEFAULT:  Chooses a default font.
+            wx.DECORATI: A decorative font.
+            wx.ROMAN: A formal, serif font.
+            wx.SCRIPT: A handwriting font.
+            wx.SWISS: A sans-serif font.
             wx.MODERN: A fixed pitch font.
         NOTE: these are only as good as the wxWindows defaults, which aren't so good.
     Style:
         One of wx.NORMAL, wx.SLANT and wx.ITALIC.
     Weight:
         One of wx.NORMAL, wx.LIGHT and wx.BOLD.
-    Underline:
+    Underlined:
         The value can be True or False. At present this may have an an
         effect on Windows only.
 
     Alternatively, you can set the kw arg: Font, to a wx.Font, and the
     above will be ignored. The size of the font you specify will be
     ignored, but the rest of its attributes will be preserved.
-    
+
     The size will scale as the drawing is zoomed.
 
     Bugs/Limitations:
@@ -1239,7 +1293,7 @@ class ScaledText(DrawObject, TextObjectMixin):
     As fonts are scaled, the do end up a little different, so you don't
     get exactly the same picture as you scale up and doen, but it's
     pretty darn close.
-    
+
     On wxGTK1 on my Linux system, at least, using a font of over about
     3000 pts. brings the system to a halt. It's the Font Server using
     huge amounts of memory. My work around is to max the font size to
@@ -1252,38 +1306,41 @@ class ScaledText(DrawObject, TextObjectMixin):
     optional, but I haven't gotten around to it.
 
     """
-    
-    def __init__(self, String, XY , Size,
+
+    def __init__(self,
+                 String,
+                 XY,
+                 Size,
                  Color = "Black",
                  BackgroundColor = None,
                  Family = wx.MODERN,
                  Style = wx.NORMAL,
                  Weight = wx.NORMAL,
-                 Underline = False,
+                 Underlined = False,
                  Position = 'tl',
                  Font = None,
                  InForeground = False):
-        
+
         DrawObject.__init__(self,InForeground)
 
         self.String = String
-        self.XY = array( XY, Float)
+        self.XY = N.array( XY, N.float)
         self.XY.shape = (2,)
-        self.Size = Size     
+        self.Size = Size
         self.Color = Color
         self.BackgroundColor = BackgroundColor
-        self.Family = Family   
-        self.Style = Style    
-        self.Weight = Weight   
-        self.Underline = Underline
+        self.Family = Family
+        self.Style = Style
+        self.Weight = Weight
+        self.Underlined = Underlined
         if not Font:
             self.FaceName = ''
         else:
-            self.FaceName           =  Font.GetFaceName()           
-            self.Family             =  Font.GetFamily()    
-            self.Style              =  Font.GetStyle()     
-            self.Underlined         =  Font.GetUnderlined()         
-            self.Weight             =  Font.GetWeight()    
+            self.FaceName           =  Font.GetFaceName()
+            self.Family             =  Font.GetFamily()
+            self.Style              =  Font.GetStyle()
+            self.Underlined         =  Font.GetUnderlined()
+            self.Weight             =  Font.GetWeight()
 
         # Experimental max font size value on wxGTK2: this works OK on
         # my system. If it's a lot  larger, there is a crash, with the
@@ -1296,7 +1353,7 @@ class ScaledText(DrawObject, TextObjectMixin):
         # Windows and OS-X seem to be better behaved in this regard.
         # They may not draw it, but they don't crash either!
         self.MaxFontSize = 1000
-        
+
         self.ShiftFun = self.ShiftFunDict[Position]
 
         self.CalcBoundingBox()
@@ -1313,13 +1370,14 @@ class ScaledText(DrawObject, TextObjectMixin):
         dc.SelectObject(bitmap) #wxMac needs a Bitmap selected for GetTextExtent to work.
         DrawingSize = 40 # pts This effectively determines the resolution that the BB is computed to.
         ScaleFactor = float(self.Size) / DrawingSize
-        dc.SetFont(self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underline, self.FaceName) )
+        self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+        dc.SetFont(self.Font)
         (w,h) = dc.GetTextExtent(self.String)
         w = w * ScaleFactor
         h = h * ScaleFactor
         x, y = self.ShiftFun(self.XY[0], self.XY[1], w, h, world = 1)
-        self.BoundingBox = array(((x, y-h ),(x + w, y)),Float)
-        
+        self.BoundingBox = BBox.asBBox(((x, y-h ),(x + w, y)))
+
     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
         (X,Y) = WorldToPixel( (self.XY) )
 
@@ -1329,7 +1387,8 @@ class ScaledText(DrawObject, TextObjectMixin):
         ## If so, limit it. Would it be better just to not draw it?
         ## note that this limit is dependent on how much memory you have, etc.
         Size = min(Size, self.MaxFontSize)
-        dc.SetFont(self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underline, self.FaceName))
+        self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+        dc.SetFont(self.Font)
         dc.SetTextForeground(self.Color)
         if self.BackgroundColor:
             dc.SetBackgroundMode(wx.SOLID)
@@ -1348,7 +1407,7 @@ class ScaledText(DrawObject, TextObjectMixin):
             HTdc.SetBrush(self.HitBrush)
             HTdc.DrawRectanglePointSize(xy, (w, h) )
 
-class ScaledTextBox(DrawObject, TextObjectMixin):
+class ScaledTextBox(TextObjectMixin, DrawObject):
     """
     This class creates a TextBox object that is scaled when zoomed.  It is
     placed at the coordinates, x,y.
@@ -1356,7 +1415,7 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
     If the Width parameter is defined, the text will be wrapped to the width given.
 
     A Box can be drawn around the text, be specifying:
-    LineWidth and/or  FillColor 
+    LineWidth and/or  FillColor
 
     A space(margin) can be put all the way around the text, be specifying:
     the PadSize argument in world coordinates.
@@ -1376,25 +1435,25 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
     Family:
         Font family, a generic way of referring to fonts without
         specifying actual facename. One of:
-            wx.DEFAULT:  Chooses a default font. 
-            wx.DECORATIVE: A decorative font. 
-            wx.ROMAN: A formal, serif font. 
-            wx.SCRIPT: A handwriting font. 
-            wx.SWISS: A sans-serif font. 
+            wx.DEFAULT:  Chooses a default font.
+            wx.DECORATIVE: A decorative font.
+            wx.ROMAN: A formal, serif font.
+            wx.SCRIPT: A handwriting font.
+            wx.SWISS: A sans-serif font.
             wx.MODERN: A fixed pitch font.
         NOTE: these are only as good as the wxWindows defaults, which aren't so good.
     Style:
         One of wx.NORMAL, wx.SLANT and wx.ITALIC.
     Weight:
         One of wx.NORMAL, wx.LIGHT and wx.BOLD.
-    Underline:
+    Underlined:
         The value can be True or False. At present this may have an an
         effect on Windows only.
 
     Alternatively, you can set the kw arg: Font, to a wx.Font, and the
     above will be ignored. The size of the font you specify will be
     ignored, but the rest of its attributes will be preserved.
-    
+
     The size will scale as the drawing is zoomed.
 
     Bugs/Limitations:
@@ -1402,7 +1461,7 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
     As fonts are scaled, they do end up a little different, so you don't
     get exactly the same picture as you scale up and down, but it's
     pretty darn close.
-    
+
     On wxGTK1 on my Linux system, at least, using a font of over about
     1000 pts. brings the system to a halt. It's the Font Server using
     huge amounts of memory. My work around is to max the font size to
@@ -1415,7 +1474,7 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
     optional, but I haven't gotten around to it.
 
     """
-        
+
     def __init__(self, String,
                  Point,
                  Size,
@@ -1429,17 +1488,17 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
                  Family = wx.MODERN,
                  Style = wx.NORMAL,
                  Weight = wx.NORMAL,
-                 Underline = False,
+                 Underlined = False,
                  Position = 'tl',
                  Alignment = "left",
                  Font = None,
                  LineSpacing = 1.0,
                  InForeground = False):
-        
+
         DrawObject.__init__(self,InForeground)
 
-        self.XY = array(Point, Float)
-        self.Size = Size     
+        self.XY = N.array(Point, N.float)
+        self.Size = Size
         self.Color = Color
         self.BackgroundColor = BackgroundColor
         self.LineColor = LineColor
@@ -1450,22 +1509,22 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
             self.PadSize = Size/10.0
         else:
             self.PadSize = float(PadSize)
-        self.Family = Family   
-        self.Style = Style    
-        self.Weight = Weight   
-        self.Underline = Underline
+        self.Family = Family
+        self.Style = Style
+        self.Weight = Weight
+        self.Underlined = Underlined
         self.Alignment = Alignment.lower()
         self.LineSpacing = float(LineSpacing)
         self.Position = Position
-        
+
         if not Font:
             self.FaceName = ''
         else:
-            self.FaceName           =  Font.GetFaceName()           
-            self.Family             =  Font.GetFamily()    
-            self.Style              =  Font.GetStyle()     
-            self.Underlined         =  Font.GetUnderlined()         
-            self.Weight             =  Font.GetWeight()    
+            self.FaceName           =  Font.GetFaceName()
+            self.Family             =  Font.GetFamily()
+            self.Style              =  Font.GetStyle()
+            self.Underlined         =  Font.GetUnderlined()
+            self.Weight             =  Font.GetWeight()
 
         # Experimental max font size value on wxGTK2: this works OK on
         # my system. If it's a lot  larger, there is a crash, with the
@@ -1496,8 +1555,8 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
         DrawingSize = self.LayoutFontSize # pts This effectively determines the resolution that the BB is computed to.
         ScaleFactor = float(self.Size) / DrawingSize
         Width = (self.Width - 2*self.PadSize) / ScaleFactor #Width to wrap to
-        dc.SetFont(self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underline, self.FaceName) )
-
+        self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+        dc.SetFont(self.Font)
         NewStrings = []
         for s in self.Strings:
             #beginning = True
@@ -1546,13 +1605,13 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
         DrawingSize = self.LayoutFontSize # pts This effectively determines the resolution that the BB is computed to.
         ScaleFactor = float(self.Size) / DrawingSize
 
-        dc.SetFont(self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underline, self.FaceName) )
-
+        self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+        dc.SetFont(self.Font)
         TextHeight = dc.GetTextExtent("X")[1]
         SpaceWidth = dc.GetTextExtent(" ")[0]
         LineHeight = TextHeight * self.LineSpacing
 
-        LineWidths = zeros((len(self.Strings),), Float)
+        LineWidths = N.zeros((len(self.Strings),), N.float)
         y = 0
         Words = []
         AllLinePoints = []
@@ -1560,7 +1619,7 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
         for i, s in enumerate(self.Strings):
             LineWidths[i] = 0
             LineWords = s.split(" ")
-            LinePoints = zeros((len(LineWords),2), Float)
+            LinePoints = N.zeros((len(LineWords),2), N.float)
             for j, word in enumerate(LineWords):
                 if j > 0:
                     LineWidths[i] += SpaceWidth
@@ -1570,14 +1629,14 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
                 LineWidths[i] += w
             y -= LineHeight
             AllLinePoints.append(LinePoints)
-        TextWidth = maximum.reduce(LineWidths)
+        TextWidth = N.maximum.reduce(LineWidths)
         self.Words = Words
 
         if self.Width is None:
             BoxWidth = TextWidth * ScaleFactor + 2*self.PadSize
         else: # use the defined Width
             BoxWidth = self.Width
-        Points = zeros((0,2), Float)
+        Points = N.zeros((0,2), N.float)
 
         for i, LinePoints in enumerate(AllLinePoints):
             ## Scale to World Coords.
@@ -1588,10 +1647,10 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
                 LinePoints[:,0] += (BoxWidth - LineWidths[i]*ScaleFactor)/2.0
             elif self.Alignment == 'right':
                 LinePoints[:,0] += (BoxWidth - LineWidths[i]*ScaleFactor-self.PadSize)
-            Points = concatenate((Points, LinePoints))
+            Points = N.concatenate((Points, LinePoints))
 
         BoxHeight = -(Points[-1,1] - (TextHeight * ScaleFactor)) + 2*self.PadSize
-        (x,y) = self.ShiftFun(self.XY[0], self.XY[1], BoxWidth, BoxHeight, world=1)
+        #(x,y) = self.ShiftFun(self.XY[0], self.XY[1], BoxWidth, BoxHeight, world=1)
         Points += (0, -self.PadSize)
         self.Points = Points
         self.BoxWidth = BoxWidth
@@ -1601,14 +1660,14 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
     def CalcBoundingBox(self):
 
         """
-        
+
         Calculates the Bounding Box
 
         """
 
         w, h = self.BoxWidth, self.BoxHeight
         x, y = self.ShiftFun(self.XY[0], self.XY[1], w, h, world=1)
-        self.BoundingBox = array(((x, y-h ),(x + w, y)),Float)
+        self.BoundingBox = BBox.asBBox(((x, y-h ),(x + w, y)))
 
     def GetBoxRect(self):
         wh = (self.BoxWidth, self.BoxHeight)
@@ -1618,9 +1677,9 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
 
     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
         xy, wh = self.GetBoxRect()
-        
-        Points = self.Points + xy 
-        Points = WorldToPixel(Points) 
+
+        Points = self.Points + xy
+        Points = WorldToPixel(Points)
         xy = WorldToPixel(xy)
         wh = ScaleWorldToPixel(wh) * (1,-1)
 
@@ -1630,9 +1689,9 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
         ## If so, limit it. Would it be better just to not draw it?
         ## note that this limit is dependent on how much memory you have, etc.
         Size = min(Size, self.MaxFontSize)
-        
-        font = self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underline, self.FaceName)
-        dc.SetFont(font)
+
+        self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+        dc.SetFont(self.Font)
         dc.SetTextForeground(self.Color)
         dc.SetBackgroundMode(wx.TRANSPARENT)
 
@@ -1641,7 +1700,7 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
             dc.SetBrush(self.Brush)
             dc.SetPen(self.Pen)
             dc.DrawRectanglePointSize(xy , wh)
-            
+
         # Draw the Text
         dc.DrawTextList(self.Words, Points)
 
@@ -1651,7 +1710,7 @@ class ScaledTextBox(DrawObject, TextObjectMixin):
             HTdc.SetBrush(self.HitBrush)
             HTdc.DrawRectanglePointSize(xy, wh)
 
-class Bitmap(DrawObject, TextObjectMixin):
+class Bitmap(TextObjectMixin, DrawObject, ):
     """
     This class creates a bitmap object, placed at the coordinates,
     x,y. the "Position" argument is a two charactor string, indicating
@@ -1665,20 +1724,20 @@ class Bitmap(DrawObject, TextObjectMixin):
     The size is fixed, and does not scale with the drawing.
 
     """
-    
+
     def __init__(self,Bitmap,XY,
                  Position = 'tl',
                  InForeground = False):
-        
+
         DrawObject.__init__(self,InForeground)
 
         if type(Bitmap) == wx._gdi.Bitmap:
             self.Bitmap = Bitmap
         elif type(Bitmap) == wx._core.Image:
             self.Bitmap = wx.BitmapFromImage(Bitmap)
-        
+
         # Note the BB is just the point, as the size in World coordinates is not fixed
-        self.BoundingBox = array((XY,XY),Float)
+        self.BoundingBox = BBox.asBBox( (XY,XY) )
 
         self.XY = XY
 
@@ -1694,13 +1753,13 @@ class Bitmap(DrawObject, TextObjectMixin):
             HTdc.SetBrush(self.HitBrush)
             HTdc.DrawRectanglePointSize(XY, (self.Width, self.Height) )
 
-class ScaledBitmap(DrawObject, TextObjectMixin):
+class ScaledBitmap(TextObjectMixin, DrawObject, ):
     """
-    
+
     This class creates a bitmap object, placed at the coordinates, XY,
     of Height, H, in World coorsinates. The width is calculated from the
     aspect ratio of the bitmap.
-    
+
     the "Position" argument is a two charactor string, indicating
     where in relation to the coordinates the bitmap should be oriented.
 
@@ -1712,23 +1771,23 @@ class ScaledBitmap(DrawObject, TextObjectMixin):
     The size scales with the drawing
 
     """
-    
+
     def __init__(self,
                  Bitmap,
                  XY,
                  Height,
                  Position = 'tl',
                  InForeground = False):
-        
+
         DrawObject.__init__(self,InForeground)
 
         if type(Bitmap) == wx._gdi.Bitmap:
             self.Image = Bitmap.ConvertToImage()
         elif type(Bitmap) == wx._core.Image:
             self.Image = Bitmap
-            
+
         self.XY = XY
-        self.Height = Height 
+        self.Height = Height
         (self.bmpWidth, self.bmpHeight) = self.Image.GetWidth(), self.Image.GetHeight()
         self.Width = self.bmpWidth / self.bmpHeight * Height
         self.ShiftFun = self.ShiftFunDict[Position]
@@ -1738,9 +1797,10 @@ class ScaledBitmap(DrawObject, TextObjectMixin):
 
     def CalcBoundingBox(self):
         ## this isn't exact, as fonts don't scale exactly.
-        w,h = self.Width, self.Height
+        w, h = self.Width, self.Height
         x, y = self.ShiftFun(self.XY[0], self.XY[1], w, h, world = 1)
-        self.BoundingBox = array(((x, y-h ),(x + w, y)),Float)
+        self.BoundingBox = BBox.asBBox( ( (x, y-h ), (x + w, y) ) )
+
 
     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
         XY = WorldToPixel(self.XY)
@@ -1758,10 +1818,258 @@ class ScaledBitmap(DrawObject, TextObjectMixin):
             HTdc.SetBrush(self.HitBrush)
             HTdc.DrawRectanglePointSize(XY, (W, H) )
 
+class ScaledBitmap2(TextObjectMixin, DrawObject, ):
+    """
+
+    An alternative scaled bitmap that only scaled the required amount of
+    the main bitmap when zoomed in: EXPERIMENTAL!
+
+    """
+
+    def __init__(self,
+                 Bitmap,
+                 XY,
+                 Height,
+                 Width=None,
+                 Position = 'tl',
+                 InForeground = False):
+
+        DrawObject.__init__(self,InForeground)
+
+        if type(Bitmap) == wx._gdi.Bitmap:
+            self.Image = Bitmap.ConvertToImage()
+        elif type(Bitmap) == wx._core.Image:
+            self.Image = Bitmap
+
+        self.XY = N.array(XY, N.float)
+        self.Height = Height
+        (self.bmpWidth, self.bmpHeight) = self.Image.GetWidth(), self.Image.GetHeight()
+        self.bmpWH = N.array((self.bmpWidth, self.bmpHeight), N.int32)
+        ## fixme: this should all accommodate different scales for X and Y
+        if Width is None:
+            self.BmpScale = float(self.bmpHeight) / Height
+            self.Width = self.bmpWidth / self.BmpScale
+        self.WH = N.array((self.Width, Height), N.float)
+        ##fixme: should this have a y = -1 to shift to y-up?
+        self.BmpScale = self.bmpWH / self.WH
+
+        print "bmpWH:", self.bmpWH
+        print "Width, Height:", self.WH
+        print "self.BmpScale", self.BmpScale
+        self.ShiftFun = self.ShiftFunDict[Position]
+        self.CalcBoundingBox()
+        self.ScaledBitmap = None # cache of the last existing scaled bitmap
+
+    def CalcBoundingBox(self):
+        ## this isn't exact, as fonts don't scale exactly.
+        w,h = self.Width, self.Height
+        x, y = self.ShiftFun(self.XY[0], self.XY[1], w, h, world = 1)
+        self.BoundingBox = BBox.asBBox( ((x, y-h ), (x + w, y)) )
+
+    def WorldToBitmap(self, Pw):
+        """
+        computes bitmap coords from World coords
+        """
+        delta = Pw - self.XY
+        Pb = delta * self.BmpScale
+        Pb *= (1, -1) ##fixme: this may only works for Yup projection!
+                      ##       and may only work for top left position
+
+        return Pb.astype(N.int_)
+
+    def _DrawEntireBitmap(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
+        """
+        this is pretty much the old code
+
+        Scales and Draws the entire bitmap.
+
+        """
+        XY = WorldToPixel(self.XY)
+        H = ScaleWorldToPixel(self.Height)[0]
+        W = H * (self.bmpWidth / self.bmpHeight)
+        if (self.ScaledBitmap is None) or (self.ScaledBitmap[0] != (0, 0, self.bmpWidth, self.bmpHeight, W, H) ):
+        #if True: #fixme: (self.ScaledBitmap is None) or (H <> self.ScaledHeight) :
+            self.ScaledHeight = H
+            print "Scaling to:", W, H
+            Img = self.Image.Scale(W, H)
+            bmp = wx.BitmapFromImage(Img)
+            self.ScaledBitmap = ((0, 0, self.bmpWidth, self.bmpHeight , W, H), bmp)# this defines the cached bitmap
+        else:
+            print "Using Cached bitmap"
+            bmp = self.ScaledBitmap[1]
+        XY = self.ShiftFun(XY[0], XY[1], W, H)
+        dc.DrawBitmapPoint(bmp, XY, True)
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.SetBrush(self.HitBrush)
+            HTdc.DrawRectanglePointSize(XY, (W, H) )
+
+    def _DrawSubBitmap(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
+        """
+        Subsets just the part of the bitmap that is visible
+        then scales and draws that.
+
+        """
+        BBworld = BBox.asBBox(self._Canvas.ViewPortBB)
+        BBbitmap = BBox.fromPoints(self.WorldToBitmap(BBworld))
+
+        XYs = WorldToPixel(self.XY)
+        # figure out subimage:
+        # fixme: this should be able to be done more succinctly!
+
+        if BBbitmap[0,0] < 0:
+            Xb = 0
+        elif BBbitmap[0,0] >  self.bmpWH[0]: # off the bitmap
+            Xb = 0
+        else:
+            Xb = BBbitmap[0,0]
+            XYs[0] = 0 # draw at origin
+
+        if BBbitmap[0,1] < 0:
+            Yb = 0
+        elif BBbitmap[0,1] >  self.bmpWH[1]: # off the bitmap
+            Yb = 0
+            ShouldDraw = False
+        else:
+            Yb = BBbitmap[0,1]
+            XYs[1] = 0 # draw at origin
+
+        if BBbitmap[1,0] < 0:
+            #off the screen --  This should never happen!
+            Wb = 0
+        elif BBbitmap[1,0] > self.bmpWH[0]:
+            Wb = self.bmpWH[0] - Xb
+        else:
+            Wb = BBbitmap[1,0] - Xb
+
+        if BBbitmap[1,1] < 0:
+            # off the screen --  This should never happen!
+            Hb = 0
+            ShouldDraw = False
+        elif BBbitmap[1,1] > self.bmpWH[1]:
+            Hb = self.bmpWH[1] - Yb
+        else:
+            Hb = BBbitmap[1,1] - Yb
+
+        FullHeight = ScaleWorldToPixel(self.Height)[0]
+        scale = FullHeight / self.bmpWH[1]
+        Ws = int(scale * Wb + 0.5) # add the 0.5 to  round
+        Hs = int(scale * Hb + 0.5)
+        if (self.ScaledBitmap is None) or (self.ScaledBitmap[0] != (Xb, Yb, Wb, Hb, Ws, Ws) ):
+            Img = self.Image.GetSubImage(wx.Rect(Xb, Yb, Wb, Hb))
+            Img.Rescale(Ws, Hs)
+            bmp = wx.BitmapFromImage(Img)
+            self.ScaledBitmap = ((Xb, Yb, Wb, Hb, Ws, Ws), bmp)# this defines the cached bitmap
+            #XY = self.ShiftFun(XY[0], XY[1], W, H)
+            #fixme: get the shiftfun working!
+        else:
+            print "Using cached bitmap"
+            ##fixme: The cached bitmap could be used if the one needed is the same scale, but
+            ##       a subset of the cached one.
+            bmp = self.ScaledBitmap[1]
+        dc.DrawBitmapPoint(bmp, XYs, True)
+
+        if HTdc and self.HitAble:
+            HTdc.SetPen(self.HitPen)
+            HTdc.SetBrush(self.HitBrush)
+            HTdc.DrawRectanglePointSize(XYs, (Ws, Hs) )
+
+    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+        BBworld = BBox.asBBox(self._Canvas.ViewPortBB)
+        ## first see if entire bitmap is displayed:
+        if  BBworld.Inside(self.BoundingBox):
+            print "Drawing entire bitmap with old code"
+            self._DrawEntireBitmap(dc , WorldToPixel, ScaleWorldToPixel, HTdc)
+            return None
+        elif BBworld.Overlaps(self.BoundingBox):
+            #BBbitmap = BBox.fromPoints(self.WorldToBitmap(BBworld))
+            print "Drawing a sub-bitmap"
+            self._DrawSubBitmap(dc , WorldToPixel, ScaleWorldToPixel, HTdc)
+        else:
+            print "Not Drawing -- no part of image is showing"
+
+class DotGrid:
+    """
+    An example of a Grid Object -- it is set on teh FloatCAnvas with one of: 
+    
+    FloatCanvas.GridUnder = Grid
+    FloatCanvas.GridOver = Grid
+    
+    It will be drawn every time, regardless of the viewport.
+    
+    In its _Draw method, it computes what to draw, given the ViewPortBB
+    of the Canvas it's being drawn on.
+    
+    """
+    def __init__(self, Spacing, Size = 2, Color = "Black", Cross=False, CrossThickness = 1):
+
+        self.Spacing = N.array(Spacing, N.float)
+        self.Spacing.shape = (2,)
+        self.Size = Size
+        self.Color = Color
+        self.Cross = Cross
+        self.CrossThickness = CrossThickness
+
+    def CalcPoints(self, Canvas):
+        ViewPortBB = Canvas.ViewPortBB
+
+        Spacing = self.Spacing
+
+        minx, miny = N.floor(ViewPortBB[0] / Spacing) * Spacing
+        maxx, maxy = N.ceil(ViewPortBB[1] / Spacing) * Spacing
+
+        ##fixme: this could use vstack or something with numpy
+        x = N.arange(minx, maxx+Spacing[0], Spacing[0]) # making sure to get the last point
+        y = N.arange(miny, maxy+Spacing[1], Spacing[1]) # an extra is OK
+        Points = N.zeros((len(y), len(x), 2), N.float)
+        x.shape = (1,-1)
+        y.shape = (-1,1)
+        Points[:,:,0] += x
+        Points[:,:,1] += y
+        Points.shape = (-1,2)
+
+        return Points
+
+    def _Draw(self, dc, Canvas):
+        Points = self.CalcPoints(Canvas)
+
+        Points = Canvas.WorldToPixel(Points)
+
+        dc.SetPen(wx.Pen(self.Color,self.CrossThickness))
+
+        if self.Cross: # Use cross shaped markers
+            #Horizontal lines
+            LinePoints = N.concatenate((Points + (self.Size,0),Points + (-self.Size,0)),1)
+            dc.DrawLineList(LinePoints)
+            # Vertical Lines
+            LinePoints = N.concatenate((Points + (0,self.Size),Points + (0,-self.Size)),1)
+            dc.DrawLineList(LinePoints)
+            pass
+        else: # use dots
+            ## Note: this code borrowed from Pointset -- itreally shouldn't be repeated here!.
+            if self.Size <= 1:
+                dc.DrawPointList(Points)
+            elif self.Size <= 2:
+                dc.DrawPointList(Points + (0,-1))
+                dc.DrawPointList(Points + (0, 1))
+                dc.DrawPointList(Points + (1, 0))
+                dc.DrawPointList(Points + (-1,0))
+            else:
+                dc.SetBrush(wx.Brush(self.Color))
+                radius = int(round(self.Size/2))
+                ##fixme: I really should add a DrawCircleList to wxPython
+                if len(Points) > 100:
+                    xy = Points
+                    xywh = N.concatenate((xy-radius, N.ones(xy.shape) * self.Size ), 1 )
+                    dc.DrawEllipseList(xywh)
+                else:
+                    for xy in Points:
+                        dc.DrawCircle(xy[0],xy[1], radius)
+
+
 
 #---------------------------------------------------------------------------
 class FloatCanvas(wx.Panel):
-    ## fixme: could this be a wx.Window?
     """
     FloatCanvas.py
 
@@ -1796,7 +2104,7 @@ class FloatCanvas(wx.Panel):
     havn't bothered to add any checks for that yet.
 
     Zooming:
-    I have set no zoom limits. What this means is that if you zoom in really 
+    I have set no zoom limits. What this means is that if you zoom in really
     far, you can get integer overflows, and get wierd results. It
     doesn't seem to actually cause any problems other than wierd output, at
     least when I have run it.
@@ -1823,10 +2131,10 @@ class FloatCanvas(wx.Panel):
     Mouse Events:
 
     At this point, there are a full set of custom mouse events. They are
-    just like the rebulsr mouse events, but include an extra attribute:
+    just like the regular mouse events, but include an extra attribute:
     Event.GetCoords(), that returns the (x,y) position in world
     coordinates, as a length-2 NumPy vector of Floats.
-    
+
     Copyright: Christopher Barker
 
     License: Same as the version of wxPython you are using it with
@@ -1837,8 +2145,8 @@ class FloatCanvas(wx.Panel):
 
     Chris.Barker@noaa.gov
 
-    """ 
-    
+    """
+
     def __init__(self, parent, id = -1,
                  size = wx.DefaultSize,
                  ProjectionFun = None,
@@ -1846,16 +2154,9 @@ class FloatCanvas(wx.Panel):
                  Debug = False):
 
         wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
-        
-        global ScreenPPI ## A global variable to hold the Pixels per inch that wxWindows thinks is in use.
-        dc = wx.ScreenDC()
-        ScreenPPI = dc.GetPPI()[1] # Pixel height
-        del dc
 
-        self.HitColorGenerator = None
-        self.UseHitTest = None
-
-        self.NumBetweenBlits = 500
+        self.ComputeFontScale()
+        self.InitAll()
 
         self.BackgroundBrush = wx.Brush(BackgroundColor,wx.SOLID)
 
@@ -1863,22 +2164,55 @@ class FloatCanvas(wx.Panel):
 
         wx.EVT_PAINT(self, self.OnPaint)
         wx.EVT_SIZE(self, self.OnSize)
-        
-        wx.EVT_LEFT_DOWN(self, self.LeftDownEvent ) 
-        wx.EVT_LEFT_UP(self, self.LeftUpEvent ) 
-        wx.EVT_LEFT_DCLICK(self, self.LeftDoubleClickEvent ) 
-        wx.EVT_MIDDLE_DOWN(self, self.MiddleDownEvent ) 
-        wx.EVT_MIDDLE_UP(self, self.MiddleUpEvent ) 
-        wx.EVT_MIDDLE_DCLICK(self, self.MiddleDoubleClickEvent ) 
+
+        wx.EVT_LEFT_DOWN(self, self.LeftDownEvent)
+        wx.EVT_LEFT_UP(self, self.LeftUpEvent)
+        wx.EVT_LEFT_DCLICK(self, self.LeftDoubleClickEvent)
+        wx.EVT_MIDDLE_DOWN(self, self.MiddleDownEvent)
+        wx.EVT_MIDDLE_UP(self, self.MiddleUpEvent)
+        wx.EVT_MIDDLE_DCLICK(self, self.MiddleDoubleClickEvent)
         wx.EVT_RIGHT_DOWN(self, self.RightDownEvent)
-        wx.EVT_RIGHT_UP(self, self.RightUpEvent ) 
-        wx.EVT_RIGHT_DCLICK(self, self.RightDoubleCLickEvent ) 
-        wx.EVT_MOTION(self, self.MotionEvent ) 
-        wx.EVT_MOUSEWHEEL(self, self.WheelEvent ) 
+        wx.EVT_RIGHT_UP(self, self.RightUpEvent)
+        wx.EVT_RIGHT_DCLICK(self, self.RightDoubleCLickEvent)
+        wx.EVT_MOTION(self, self.MotionEvent)
+        wx.EVT_MOUSEWHEEL(self, self.WheelEvent)
 
         ## CHB: I'm leaving these out for now.
-        #wx.EVT_ENTER_WINDOW(self, self. ) 
-        #wx.EVT_LEAVE_WINDOW(self, self. ) 
+        #wx.EVT_ENTER_WINDOW(self, self. )
+        #wx.EVT_LEAVE_WINDOW(self, self. )
+
+        self.SetProjectionFun(ProjectionFun)
+        self.GUIMode = None
+
+        # timer to give a delay when re-sizing so that buffers aren't re-built too many times.
+        self.SizeTimer = wx.PyTimer(self.OnSizeTimer)
+
+        self.InitializePanel()
+        self.MakeNewBuffers()
+
+#        self.CreateCursors()
+
+    def ComputeFontScale(self):
+        ## A global variable to hold the scaling from pixel size to point size.
+        global FontScale
+        dc = wx.ScreenDC()
+        dc.SetFont(wx.Font(16, wx.ROMAN, wx.NORMAL, wx.NORMAL))
+        E = dc.GetTextExtent("X")
+        FontScale = 16/E[1]
+        del dc
+        
+    def InitAll(self):
+        """
+        InitAll() sets everything in the Canvas to default state.
+
+        It can be used to reset the Canvas
+
+        """
+
+        self.HitColorGenerator = None
+        self.UseHitTest = False
+
+        self.NumBetweenBlits = 500
 
         ## create the Hit Test Dicts:
         self.HitDict = None
@@ -1889,95 +2223,54 @@ class FloatCanvas(wx.Panel):
         self._ForegroundBuffer = None
         self.BoundingBox = None
         self.BoundingBoxDirty = False
-        self.ViewPortCenter= array( (0,0), Float)
+        self.MinScale = None
+        self.MaxScale = None
+        self.ViewPortCenter= N.array( (0,0), N.float)
 
-        self.SetProjectionFun(ProjectionFun)
-        
-        self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
-        self.TransformVector = array( (1,-1), Float) # default Transformation
-        
-        self.Scale = 1
+        self.SetProjectionFun(None)
 
-        self.GUIMode = None
-        self.StartRBBox = None
-        self.PrevRBBox = None
-        self.StartMove = None
-        self.PrevMoveXY = None
+        self.MapProjectionVector = N.array( (1,1), N.float) # No Projection to start!
+        self.TransformVector = N.array( (1,-1), N.float) # default Transformation
+
+        self.Scale = 1
         self.ObjectUnderMouse = None
-        
-        # called just to make sure everything is initialized
-        # this is a bug on OS-X, maybe it's not required?
-        self.SizeTimer = wx.PyTimer(self.OnSizeTimer) # timer to give a delay when re-sizing so that bufferes aren't re-built too many times.
-        
-        self.InitializePanel()
-        self.MakeNewBuffers()
-        
-        self.InHereNum = 0
 
-        self.CreateCursors()
-        
-    def CreateCursors(self):
-
-        ## create all the Cursors, so they don't need to be created each time.
-        ##
-        if "wxMac" in wx.PlatformInfo: # use 16X16 cursors for wxMac
-            self.HandCursor = wx.CursorFromImage(Resources.getHand16Image())
-            self.GrabHandCursor = wx.CursorFromImage(Resources.getGrabHand16Image())
-
-            img = Resources.getMagPlus16Image()
-            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
-            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
-            self.MagPlusCursor = wx.CursorFromImage(img)
-
-            img = Resources.getMagMinus16Image()
-            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
-            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
-            self.MagMinusCursor = wx.CursorFromImage(img)
-        else: # use 24X24 cursors for GTK and Windows
-            self.HandCursor = wx.CursorFromImage(Resources.getHandImage())
-            self.GrabHandCursor = wx.CursorFromImage(Resources.getGrabHandImage())
-
-            img = Resources.getMagPlusImage()
-            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
-            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
-            self.MagPlusCursor = wx.CursorFromImage(img)
-
-            img = Resources.getMagMinusImage()
-            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
-            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
-            self.MagMinusCursor = wx.CursorFromImage(img)
+        self.GridUnder = None
+        self.GridOver = None
+
+        self._BackgroundDirty = True
 
     def SetProjectionFun(self,ProjectionFun):
         if ProjectionFun == 'FlatEarth':
-            self.ProjectionFun = self.FlatEarthProjection 
+            self.ProjectionFun = self.FlatEarthProjection
         elif callable(ProjectionFun):
-            self.ProjectionFun = ProjectionFun 
+            self.ProjectionFun = ProjectionFun
         elif ProjectionFun is None:
-            self.ProjectionFun = lambda x=None: array( (1,1), Float)
+            self.ProjectionFun = lambda x=None: N.array( (1,1), N.float)
         else:
-            raise FloatCanvasError('Projectionfun must be either: "FlatEarth", None, or a callable object (function, for instance) that takes the ViewPortCenter and returns a MapProjectionVector')
+            raise FloatCanvasError('Projectionfun must be either:'
+                                   ' "FlatEarth", None, or a callable object '
+                                   '(function, for instance) that takes the '
+                                   'ViewPortCenter and returns a MapProjectionVector')
 
     def FlatEarthProjection(self, CenterPoint):
-        return array((cos(pi*CenterPoint[1]/180),1),Float)
-
-    def SetMode(self,Mode):
-        if Mode in ["ZoomIn","ZoomOut","Move","Mouse", None]:
-            if Mode == "Move":
-                self.SetCursor(self.HandCursor)
-            elif Mode == "ZoomIn":
-                self.SetCursor(self.MagPlusCursor) 
-            elif Mode == "ZoomOut":
-                self.SetCursor(self.MagMinusCursor) 
-            else:
-                self.SetCursor(wx.NullCursor)
-                
+        MaxLatitude = 75 # these were determined essentially arbitrarily
+        MinLatitude = -75
+        Lat = min(CenterPoint[1],MaxLatitude)
+        Lat = max(Lat,MinLatitude)
+        return N.array((N.cos(N.pi*Lat/180),1),N.float)
+
+    def SetMode(self, Mode):
+            '''
+            Set the GUImode to any of the availble mode.
+            '''
+            # Set mode
             self.GUIMode = Mode
-            
-        else:
-            raise FloatCanvasError('"%s" is Not a valid Mode'%Mode)
+            #self.GUIMode.SetCursor()
+            self.SetCursor(self.GUIMode.Cursor)
 
     def MakeHitDict(self):
-        ##fixme: Should this just be None if nothing has been bound? 
+        ##fixme: Should this just be None if nothing has been bound?
         self.HitDict = {EVT_FC_LEFT_DOWN: {},
                         EVT_FC_LEFT_UP: {},
                         EVT_FC_LEFT_DCLICK: {},
@@ -1989,27 +2282,47 @@ class FloatCanvas(wx.Panel):
                         EVT_FC_RIGHT_DCLICK: {},
                         EVT_FC_ENTER_OBJECT: {},
                         EVT_FC_LEAVE_OBJECT: {},
-                        }        
-            
+                        }
+
     def _RaiseMouseEvent(self, Event, EventType):
         """
         This is called in various other places to raise a Mouse Event
         """
-        #print "in Raise Mouse Event", Event
         pt = self.PixelToWorld( Event.GetPosition() )
         evt = _MouseEvent(EventType, Event, self.GetId(), pt)
-        self.GetEventHandler().ProcessEvent(evt)       
+        self.GetEventHandler().ProcessEvent(evt)
+
+    if wx.__version__ >= "2.8":
+        HitTestBitmapDepth = 32
+        #print "Using hit test code for 2.8"
+        def GetHitTestColor(self, xy):
+            if self._ForegroundHTBitmap:
+                pdata = wx.AlphaPixelData(self._ForegroundHTBitmap)
+            else:
+                pdata = wx.AlphaPixelData(self._HTBitmap)
+            if not pdata:
+                raise RuntimeError("Trouble Accessing Hit Test bitmap")
+            pacc = pdata.GetPixels()
+            pacc.MoveTo(pdata, xy[0], xy[1])
+            return pacc.Get()[:3]
+    else:
+        HitTestBitmapDepth = 24
+        #print "using pre-2.8 hit test code"
+        def GetHitTestColor(self,  xy ):
+            dc = wx.MemoryDC()
+            if self._ForegroundHTBitmap:
+                dc.SelectObject(self._ForegroundHTBitmap)
+            else:
+                dc.SelectObject(self._HTBitmap)
+            hitcolor = dc.GetPixelPoint( xy )
+            return hitcolor.Get()
 
     def HitTest(self, event, HitEvent):
         if self.HitDict:
             # check if there are any objects in the dict for this event
             if self.HitDict[ HitEvent ]:
                 xy = event.GetPosition()
-                if self._ForegroundHTdc:
-                    hitcolor = self._ForegroundHTdc.GetPixelPoint( xy )
-                else:
-                    hitcolor = self._HTdc.GetPixelPoint( xy )
-                color = ( hitcolor.Red(), hitcolor.Green(), hitcolor.Blue() )
+                color = self.GetHitTestColor( xy )
                 if color in self.HitDict[ HitEvent ]:
                     Object = self.HitDict[ HitEvent ][color]
                     ## Add the hit coords to the Object
@@ -2021,13 +2334,13 @@ class FloatCanvas(wx.Panel):
 
     def MouseOverTest(self, event):
         ##fixme: Can this be cleaned up?
-        if self.HitDict:
+        if (self.HitDict and
+
+                (self.HitDict[EVT_FC_ENTER_OBJECT ] or
+                 self.HitDict[EVT_FC_LEAVE_OBJECT ]    )
+            ):
             xy = event.GetPosition()
-            if self._ForegroundHTdc:
-                hitcolor = self._ForegroundHTdc.GetPixelPoint( xy )
-            else:
-                hitcolor = self._HTdc.GetPixelPoint( xy )
-            color = ( hitcolor.Red(), hitcolor.Green(), hitcolor.Blue() )
+            color = self.GetHitTestColor( xy )
             OldObject = self.ObjectUnderMouse
             ObjectCallbackCalled = False
             if color in self.HitDict[ EVT_FC_ENTER_OBJECT ]:
@@ -2068,249 +2381,112 @@ class FloatCanvas(wx.Panel):
                     except KeyError:
                         pass # this means the leave event isn't bound for that object
             return ObjectCallbackCalled
-
+        return False
 
     ## fixme: There is a lot of repeated code here
-    ##        Is there a better way?            
-    def LeftDoubleClickEvent(self,event):
-        if self.GUIMode == "Mouse":
-            EventType = EVT_FC_LEFT_DCLICK
-            if not self.HitTest(event, EventType):
-                self._RaiseMouseEvent(event, EventType)
-
-    def MiddleDownEvent(self,event):
-        if self.GUIMode == "Mouse":
-            EventType = EVT_FC_MIDDLE_DOWN
-            if not self.HitTest(event, EventType):
-                self._RaiseMouseEvent(event, EventType)
-
-    def MiddleUpEvent(self,event):
-        if self.GUIMode == "Mouse":
-            EventType = EVT_FC_MIDDLE_UP
-            if not self.HitTest(event, EventType):
-                self._RaiseMouseEvent(event, EventType)
-
-    def MiddleDoubleClickEvent(self,event):
-        if self.GUIMode == "Mouse":
-            EventType = EVT_FC_MIDDLE_DCLICK
-            if not self.HitTest(event, EventType):
-                self._RaiseMouseEvent(event, EventType)
-
-    def RightUpEvent(self,event):
-        if self.GUIMode == "Mouse":
-            EventType = EVT_FC_RIGHT_UP
-            if not self.HitTest(event, EventType):
-                self._RaiseMouseEvent(event, EventType)
-
-    def RightDoubleCLickEvent(self,event):
-        if self.GUIMode == "Mouse":
-            EventType = EVT_FC_RIGHT_DCLICK
-            if not self.HitTest(event, EventType):
-                self._RaiseMouseEvent(event, EventType)
-
-    def WheelEvent(self,event):
-        ##if self.GUIMode == "Mouse":
-        ## Why not always raise this?
-            self._RaiseMouseEvent(event, EVT_FC_MOUSEWHEEL)
-
-
-    def LeftDownEvent(self,event):
+    ##        Is there a better way?
+    def LeftDoubleClickEvent(self, event):
         if self.GUIMode:
-            if self.GUIMode == "ZoomIn":
-                self.StartRBBox = array( event.GetPosition() )
-                self.PrevRBBox = None
-                self.CaptureMouse()
-            elif self.GUIMode == "ZoomOut":
-                Center = self.PixelToWorld( event.GetPosition() )
-                self.Zoom(1/1.5,Center)
-            elif self.GUIMode == "Move":
-                self.SetCursor(self.GrabHandCursor)
-                self.StartMove = array( event.GetPosition() )
-                self.PrevMoveXY = (0,0)
-            elif self.GUIMode == "Mouse":
-                ## check for a hit
-                if not self.HitTest(event, EVT_FC_LEFT_DOWN):
-                   self._RaiseMouseEvent(event,EVT_FC_LEFT_DOWN)
-        else: 
-            pass
+            self.GUIMode.OnLeftDouble(event)
+        event.Skip()
+
+    def MiddleDownEvent(self, event):
+        if self.GUIMode:
+            self.GUIMode.OnMiddleDown(event)
+        event.Skip()
+
+    def MiddleUpEvent(self, event):
+        if self.GUIMode:
+            self.GUIMode.OnMiddleUp(event)
+        event.Skip()
+
+    def MiddleDoubleClickEvent(self, event):
+        if self.GUIMode:
+            self.GUIMode.OnMiddleDouble(event)
+        event.Skip()
 
-    def LeftUpEvent(self,event):
+    def RightDoubleCLickEvent(self, event):
+        if self.GUIMode:
+            self.GUIMode.OnRightDouble(event)
+        event.Skip()
+
+    def WheelEvent(self, event):
+        if self.GUIMode:
+            self.GUIMode.OnWheel(event)
+        event.Skip()
+
+    def LeftDownEvent(self, event):
+        if self.GUIMode:
+            self.GUIMode.OnLeftDown(event)
+        event.Skip()
+
+    def LeftUpEvent(self, event):
         if self.HasCapture():
             self.ReleaseMouse()
         if self.GUIMode:
-            if self.GUIMode == "ZoomIn":
-                if event.LeftUp() and not self.StartRBBox is None:
-                    self.PrevRBBox = None
-                    EndRBBox = event.GetPosition()
-                    StartRBBox = self.StartRBBox
-                    # if mouse has moved less that ten pixels, don't use the box.
-                    if ( abs(StartRBBox[0] - EndRBBox[0]) > 10
-                         and abs(StartRBBox[1] - EndRBBox[1]) > 10 ):
-                        EndRBBox = self.PixelToWorld(EndRBBox)
-                        StartRBBox = self.PixelToWorld(StartRBBox)
-                        BB = array(((min(EndRBBox[0],StartRBBox[0]),
-                                     min(EndRBBox[1],StartRBBox[1])),
-                                    (max(EndRBBox[0],StartRBBox[0]),
-                                     max(EndRBBox[1],StartRBBox[1]))),Float)
-                        self.ZoomToBB(BB)
-                    else:
-                        Center = self.PixelToWorld(StartRBBox)
-                        self.Zoom(1.5,Center)
-                    self.StartRBBox = None
-            elif self.GUIMode == "Move":
-                self.SetCursor(self.HandCursor)
-                if self.StartMove is not None:
-                    StartMove = self.StartMove
-                    EndMove = array((event.GetX(),event.GetY()))
-                    if sum((StartMove-EndMove)**2) > 16:
-                        self.MoveImage(StartMove-EndMove,'Pixel')
-                    self.StartMove = None
-            elif self.GUIMode == "Mouse":
-                EventType = EVT_FC_LEFT_UP
-                if not self.HitTest(event, EventType):
-                   self._RaiseMouseEvent(event, EventType)
-        else:
-            pass
+            self.GUIMode.OnLeftUp(event)
+        event.Skip()
 
-    def MotionEvent(self,event):
+    def MotionEvent(self, event):
         if self.GUIMode:
-            if self.GUIMode == "ZoomIn":
-                if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None):
-                    xy0 = self.StartRBBox
-                    xy1 = array( event.GetPosition() )
-                    wh  = abs(xy1 - xy0)
-                    wh[0] = max(wh[0], int(wh[1]*self.AspectRatio))
-                    wh[1] = int(wh[0] / self.AspectRatio)
-                    xy_c = (xy0 + xy1) / 2
-                    dc = wx.ClientDC(self)
-                    dc.BeginDrawing()
-                    dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
-                    dc.SetBrush(wx.TRANSPARENT_BRUSH)
-                    dc.SetLogicalFunction(wx.XOR)
-                    if self.PrevRBBox:
-                        dc.DrawRectanglePointSize(*self.PrevRBBox)
-                    self.PrevRBBox = ( xy_c - wh/2, wh )
-                    dc.DrawRectanglePointSize( *self.PrevRBBox )
-                    dc.EndDrawing()
-            elif self.GUIMode == "Move":
-                if event.Dragging() and event.LeftIsDown() and not self.StartMove is None:
-                    xy1 = array( event.GetPosition() )
-                    wh = self.PanelSize
-                    xy_tl = xy1 - self.StartMove
-                    dc = wx.ClientDC(self)
-                    dc.BeginDrawing()
-                    x1,y1 = self.PrevMoveXY
-                    x2,y2 = xy_tl
-                    w,h = self.PanelSize
-                    ##fixme: This sure could be cleaner!
-                    if x2 > x1 and y2 > y1:
-                        xa = xb = x1
-                        ya = yb = y1
-                        wa = w
-                        ha = y2 - y1
-                        wb = x2-  x1
-                        hb = h
-                    elif x2 > x1 and y2 <= y1:
-                        xa = x1
-                        ya = y1
-                        wa = x2 - x1
-                        ha = h
-                        xb = x1
-                        yb = y2 + h
-                        wb = w
-                        hb = y1 - y2
-                    elif x2 <= x1 and y2 > y1:
-                        xa = x1
-                        ya = y1
-                        wa = w
-                        ha = y2 - y1
-                        xb = x2 + w
-                        yb = y1
-                        wb = x1 - x2
-                        hb = h - y2 + y1 
-                    elif x2 <= x1 and y2 <= y1:
-                        xa = x2 + w
-                        ya = y1
-                        wa = x1 - x2
-                        ha = h
-                        xb = x1
-                        yb = y2 + h
-                        wb = w
-                        hb = y1 - y2
-               
-                    dc.SetPen(wx.TRANSPARENT_PEN)
-                    dc.SetBrush(self.BackgroundBrush)
-                    dc.DrawRectangle(xa, ya, wa, ha)
-                    dc.DrawRectangle(xb, yb, wb, hb)
-                    self.PrevMoveXY = xy_tl
-                    if self._ForeDrawList:
-                    ##if self._ForegroundBuffer:
-                        dc.DrawBitmapPoint(self._ForegroundBuffer,xy_tl)
-                    else:
-                        dc.DrawBitmapPoint(self._Buffer,xy_tl)
-                    dc.EndDrawing()
-            elif self.GUIMode == "Mouse":
-                ## Only do something if there are mouse over events bound
-                if self.HitDict and (self.HitDict[ EVT_FC_ENTER_OBJECT ] or self.HitDict[ EVT_FC_LEAVE_OBJECT ] ):
-                    if not self.MouseOverTest(event):
-                        self._RaiseMouseEvent(event,EVT_FC_MOTION)
-            else:
-                    pass
-            self._RaiseMouseEvent(event,EVT_FC_MOTION)
-        else:
-            pass
+            self.GUIMode.OnMove(event)
+        event.Skip()
 
-    def RightDownEvent(self,event):
+    def RightDownEvent(self, event):
         if self.GUIMode:
-            if self.GUIMode == "ZoomIn":
-                Center = self.PixelToWorld((event.GetX(),event.GetY()))
-                self.Zoom(1/1.5,Center)
-            elif self.GUIMode == "ZoomOut":
-                Center = self.PixelToWorld((event.GetX(),event.GetY()))
-                self.Zoom(1.5,Center)
-            elif self.GUIMode == "Mouse":
-                EventType = EVT_FC_RIGHT_DOWN
-                if not self.HitTest(event, EventType):
-                   self._RaiseMouseEvent(event, EventType)
-        else:
-            pass
-        
+            self.GUIMode.OnRightDown(event)
+        event.Skip()
+
+    def RightUpEvent(self, event):
+        if self.GUIMode:
+            self.GUIMode.OnRightUp(event)
+        event.Skip()
+
     def MakeNewBuffers(self):
         self._BackgroundDirty = True
         # Make new offscreen bitmap:
         self._Buffer = wx.EmptyBitmap(*self.PanelSize)
-        #dc = wx.MemoryDC()
-        #dc.SelectObject(self._Buffer)
-        #dc.Clear()
         if self._ForeDrawList:
             self._ForegroundBuffer = wx.EmptyBitmap(*self.PanelSize)
+            if self.UseHitTest:
+                self.MakeNewHTBitmap()
+            else:
+                self._ForegroundHTBitmap = None
         else:
             self._ForegroundBuffer = None
+            self._ForegroundHTBitmap = None
+
         if self.UseHitTest:
-            self.MakeNewHTdc()
+            self.MakeNewHTBitmap()
         else:
-            self._HTdc = None
-            self._ForegroundHTdc = None
-
-    def MakeNewHTdc(self):
-        ## Note: While it's considered a "bad idea" to keep a
-        ## MemoryDC around I'm doing it here because a wx.Bitmap
-        ## doesn't have a GetPixel method so a DC is needed to do
-        ## the hit-test. It didn't seem like a good idea to re-create
-        ## a wx.MemoryDC on every single mouse event, so I keep it
-        ## around instead
-        self._HTdc = wx.MemoryDC()
-        self._HTBitmap = wx.EmptyBitmap(*self.PanelSize) 
-        self._HTdc.SelectObject( self._HTBitmap )
-        self._HTdc.SetBackground(wx.BLACK_BRUSH)
-        if self._ForeDrawList:
-            self._ForegroundHTdc = wx.MemoryDC()
-            self._ForegroundHTBitmap = wx.EmptyBitmap(*self.PanelSize) 
-            self._ForegroundHTdc.SelectObject( self._ForegroundHTBitmap )
-            self._ForegroundHTdc.SetBackground(wx.BLACK_BRUSH)
-        else:
-           self._ForegroundHTdc = None 
-    
+            self._HTBitmap = None
+            self._ForegroundHTBitmap = None
+
+    def MakeNewHTBitmap(self):
+        """
+        Off screen Bitmap used for Hit tests on background objects
+        
+        """
+        self._HTBitmap = wx.EmptyBitmap(self.PanelSize[0],
+
+                                        self.PanelSize[1],
+
+                                        depth=self.HitTestBitmapDepth)
+
+    def MakeNewForegroundHTBitmap(self):
+        ## Note: the foreground and backround HT bitmaps are in separate functions
+        ##       so that they can be created separate --i.e. when a foreground is
+        ##       added after the backgound is drawn
+        """
+        Off screen Bitmap used for Hit tests on foreground objects
+        
+        """
+        self._ForegroundHTBitmap = wx.EmptyBitmap(self.PanelSize[0],
+
+                                                  self.PanelSize[1],
+
+                                                  depth=self.HitTestBitmapDepth)
+
     def OnSize(self, event=None):
         self.InitializePanel()
         self.SizeTimer.Start(50, oneShot=True)
@@ -2324,13 +2500,13 @@ class FloatCanvas(wx.Panel):
         if self.PanelSize == (0,0):
             ## OS-X sometimes gives a Size event when the panel is size (0,0)
             self.PanelSize = (2,2)
-        self.PanelSize  = array(self.PanelSize,  Int32)
+        self.PanelSize  = N.array(self.PanelSize,  N.int32)
         self.HalfPanelSize = self.PanelSize / 2 # lrk: added for speed in WorldToPixel
         if self.PanelSize[0] == 0 or self.PanelSize[1] == 0:
             self.AspectRatio = 1.0
         else:
             self.AspectRatio = float(self.PanelSize[0]) / self.PanelSize[1]
-        
+
     def OnPaint(self, event):
         dc = wx.PaintDC(self)
         if self._ForegroundBuffer:
@@ -2340,6 +2516,18 @@ class FloatCanvas(wx.Panel):
 
     def Draw(self, Force=False):
         """
+
+        Canvas.Draw(Force=False)
+
+        Re-draws the canvas.
+
+        Note that the buffer will not be re-drawn unless something has
+        changed. If you change a DrawObject directly, then the canvas
+        will not know anything has changed. In this case, you can force
+        a re-draw by passing int True for the Force flag:
+
+        Canvas.Draw(Force=True)
+
         There is a main buffer set up to double buffer the screen, so
         you can get quick re-draws when the window gets uncovered.
 
@@ -2351,26 +2539,37 @@ class FloatCanvas(wx.Panel):
         changing on the foreground, without having to wait for the
         background to get re-drawn. This can be used to support simple
         animation, for instance.
-        
+
         """
-        if sometrue(self.PanelSize <= 2 ): # it's possible for this to get called before being properly initialized.
+
+        if N.sometrue(self.PanelSize <= 2 ):
+            # it's possible for this to get called before being properly initialized.
             return
         if self.Debug: start = clock()
         ScreenDC =  wx.ClientDC(self)
-        ViewPortWorld = ( self.PixelToWorld((0,0)),
-                          self.PixelToWorld(self.PanelSize) )
-        ViewPortBB = array( ( minimum.reduce(ViewPortWorld),
-                              maximum.reduce(ViewPortWorld) ) )
+        ViewPortWorld = N.array(( self.PixelToWorld((0,0)),
+                                  self.PixelToWorld(self.PanelSize) )
+                                     )
+        self.ViewPortBB = N.array( ( N.minimum.reduce(ViewPortWorld),
+                              N.maximum.reduce(ViewPortWorld) ) )
+        #self.ViewPortWorld = ViewPortWorld
+
         dc = wx.MemoryDC()
         dc.SelectObject(self._Buffer)
         if self._BackgroundDirty or Force:
-            #print "Background is Dirty"
             dc.SetBackground(self.BackgroundBrush)
             dc.Clear()
-            if self._HTdc:
-                self._HTdc.Clear()
-            self._DrawObjects(dc, self._DrawList, ScreenDC, ViewPortBB, self._HTdc)
+            if self._HTBitmap is not None:
+                HTdc = wx.MemoryDC()
+                HTdc.SelectObject(self._HTBitmap)
+                HTdc.Clear()
+            else:
+                HTdc = None
+            if self.GridUnder is not None:
+                self.GridUnder._Draw(dc, self)
+            self._DrawObjects(dc, self._DrawList, ScreenDC, self.ViewPortBB, HTdc)
             self._BackgroundDirty = False
+            del HTdc
 
         if self._ForeDrawList:
             ## If an object was just added to the Foreground, there might not yet be a buffer
@@ -2381,62 +2580,56 @@ class FloatCanvas(wx.Panel):
             dc = wx.MemoryDC() ## I got some strange errors (linewidths wrong) if I didn't make a new DC here
             dc.SelectObject(self._ForegroundBuffer)
             dc.DrawBitmap(self._Buffer,0,0)
-            if self._ForegroundHTdc is None:
-                self._ForegroundHTdc = wx.MemoryDC()
-                self._ForegroundHTdc.SelectObject( wx.EmptyBitmap(
-                                                   self.PanelSize[0],
-                                                   self.PanelSize[1]) )
-            if self._HTdc:
-                ## blit the background HT buffer to the foreground HT buffer
-                self._ForegroundHTdc.Blit(0, 0,
-                                          self.PanelSize[0], self.PanelSize[1],
-                                          self._HTdc, 0, 0)
+            if self._ForegroundHTBitmap is not None:
+                ForegroundHTdc = wx.MemoryDC()
+                ForegroundHTdc.SelectObject( self._ForegroundHTBitmap)
+                ForegroundHTdc.Clear()
+                if self._HTBitmap is not None:
+                    #Draw the background HT buffer to the foreground HT buffer
+                    ForegroundHTdc.DrawBitmap(self._HTBitmap, 0, 0)
+            else:
+                ForegroundHTdc = None
             self._DrawObjects(dc,
                               self._ForeDrawList,
                               ScreenDC,
-                              ViewPortBB,
-                              self._ForegroundHTdc)
+                              self.ViewPortBB,
+                              ForegroundHTdc)
+        if self.GridOver is not None:
+            self.GridOver._Draw(dc, self)
         ScreenDC.Blit(0, 0, self.PanelSize[0],self.PanelSize[1], dc, 0, 0)
-        # If the canvas is in the middle of a zoom or move, the Rubber Band box needs to be re-drawn
-        # This seeems out of place, but it works.
-        if self.PrevRBBox:
-            ScreenDC.SetPen(wx.Pen('WHITE', 2,wx.SHORT_DASH))
-            ScreenDC.SetBrush(wx.TRANSPARENT_BRUSH)
-            ScreenDC.SetLogicalFunction(wx.XOR)
-            ScreenDC.DrawRectanglePointSize(*self.PrevRBBox)
+        # If the canvas is in the middle of a zoom or move,
+        # the Rubber Band box needs to be re-drawn
+        ##fixme: maybe GUIModes should never be None, and rather have a Do-nothing GUI-Mode.
+        if self.GUIMode is not None:
+            self.GUIMode.UpdateScreen()
+
         if self.Debug: print "Drawing took %f seconds of CPU time"%(clock()-start)
 
-        ## Clear the font cache
-        ## IF you don't do this, the X font server starts to take up Massive amounts of memory
-        ## This is mostly a problem with very large fonts, that you get with scaled text when zoomed in.
+        ## Clear the font cache. If you don't do this, the X font server
+        ## starts to take up Massive amounts of memory This is mostly a
+        ## problem with very large fonts, that you get with scaled text
+        ## when zoomed in.
         DrawObject.FontList = {}
 
     def _ShouldRedraw(DrawList, ViewPortBB): # lrk: adapted code from BBCheck
         # lrk: Returns the objects that should be redrawn
 
+        ## fixme: should this check be moved into the object?
+        ##        also: a BB object would make this cleaner too
         BB2 = ViewPortBB
         redrawlist = []
         for Object in DrawList:
             BB1 = Object.BoundingBox
+            ## note: this could use the Utilities.BBCheck function
+            ##       butthis saves a function call
             if (BB1[1,0] > BB2[0,0] and BB1[0,0] < BB2[1,0] and
                  BB1[1,1] > BB2[0,1] and BB1[0,1] < BB2[1,1]):
                 redrawlist.append(Object)
-        return redrawlist       
+        #return redrawlist
+        ##fixme: disabled this!!!!
+        return redrawlist
     _ShouldRedraw = staticmethod(_ShouldRedraw)
 
-
-##    def BBCheck(self, BB1, BB2):
-##        """
-
-##        BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise
-
-##        """
-##        if ( (BB1[1,0] > BB2[0,0]) and (BB1[0,0] < BB2[1,0]) and
-##             (BB1[1,1] > BB2[0,1]) and (BB1[0,1] < BB2[1,1]) ):
-##            return True
-##        else:
-##            return False
-
     def MoveImage(self,shift,CoordType):
         """
         move the image in the window.
@@ -2455,44 +2648,46 @@ class FloatCanvas(wx.Panel):
         in Floating point world coordinates
 
         """
-        
-        shift = asarray(shift,Float)
-        #print "shifting by:", shift
+        shift = N.asarray(shift,N.float)
         if CoordType == 'Panel':# convert from panel coordinates
-            shift = shift * array((-1,1),Float) *self.PanelSize/self.TransformVector
+            shift = shift * N.array((-1,1),N.float) *self.PanelSize/self.TransformVector
         elif CoordType == 'Pixel': # convert from pixel coordinates
             shift = shift/self.TransformVector
         elif CoordType == 'World': # No conversion
             pass
         else:
             raise FloatCanvasError('CoordType must be either "Panel", "Pixel", or "World"')
-        
-        self.ViewPortCenter = self.ViewPortCenter + shift 
+
+        self.ViewPortCenter = self.ViewPortCenter + shift
         self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
-        self.TransformVector = array((self.Scale,-self.Scale),Float) * self.MapProjectionVector
+        self.TransformVector = N.array((self.Scale,-self.Scale),N.float) * self.MapProjectionVector
         self._BackgroundDirty = True
         self.Draw()
 
-    def Zoom(self,factor,center = None):
-    
+    def Zoom(self, factor, center = None, centerCoords="world"):
+
         """
         Zoom(factor, center) changes the amount of zoom of the image by factor.
         If factor is greater than one, the image gets larger.
         If factor is less than one, the image gets smaller.
-        
-        Center is a tuple of (x,y) coordinates of the center of the viewport, after zooming.
+
+        center is a tuple of (x,y) coordinates of the center of the viewport, after zooming.
         If center is not given, the center will stay the same.
+
+        centerCoords is a flag indicating whether the center given is in pixel or world 
+        coords. Options are: "world" or "pixel"
         
         """
         self.Scale = self.Scale*factor
         if not center is None:
-            self.ViewPortCenter = array(center,Float)
-        self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
-        self.TransformVector = array((self.Scale,-self.Scale),Float) * self.MapProjectionVector
-        self._BackgroundDirty = True
-        self.Draw()
-        
-    def ZoomToBB(self, NewBB = None, DrawFlag = True):
+            if centerCoords == "pixel":
+                center = self.PixelToWorld( center )
+            else:
+                center = N.array(center,N.float)
+            self.ViewPortCenter = center
+        self.SetToNewScale()
+
+    def ZoomToBB(self, NewBB=None, DrawFlag=True):
 
         """
 
@@ -2501,18 +2696,18 @@ class FloatCanvas(wx.Panel):
 
         """
         
-        if not  NewBB is None:
+        if NewBB is not None:
             BoundingBox = NewBB
         else:
             if self.BoundingBoxDirty:
                 self._ResetBoundingBox()
             BoundingBox = self.BoundingBox
-        if not BoundingBox is None:
-            self.ViewPortCenter = array(((BoundingBox[0,0]+BoundingBox[1,0])/2,
-                                         (BoundingBox[0,1]+BoundingBox[1,1])/2 ),Float)
+        if BoundingBox is not None:
+            self.ViewPortCenter = N.array(((BoundingBox[0,0]+BoundingBox[1,0])/2,
+                                         (BoundingBox[0,1]+BoundingBox[1,1])/2 ),N.float_)
             self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
             # Compute the new Scale
-            BoundingBox = BoundingBox * self.MapProjectionVector
+            BoundingBox = BoundingBox*self.MapProjectionVector # this does need to make a copy!
             try:
                 self.Scale = min(abs(self.PanelSize[0] / (BoundingBox[1,0]-BoundingBox[0,0])),
                                  abs(self.PanelSize[1] / (BoundingBox[1,1]-BoundingBox[0,1])) )*0.95
@@ -2524,23 +2719,33 @@ class FloatCanvas(wx.Panel):
                         self.Scale = (self.PanelSize[1]  / (BoundingBox[1,1]-BoundingBox[0,1]))*0.95
                     except ZeroDivisionError: #zero size! (must be a single point)
                         self.Scale = 1
-                     
-            self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
+
             if DrawFlag:
                 self._BackgroundDirty = True
-                self.Draw()
         else:
             # Reset the shifting and scaling to defaults when there is no BB
-            self.ViewPortCenter= array( (0,0), Float)
-            self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
-            self.TransformVector = array( (1,-1), Float) # default Transformation
-            self.Scale = 1
-            
+            self.ViewPortCenter= N.array( (0,0), N.float)
+            self.Scale= 1
+        self.SetToNewScale(DrawFlag=DrawFlag)
+
+    def SetToNewScale(self, DrawFlag=True):
+        Scale = self.Scale
+        if self.MinScale is not None:
+            Scale = max(Scale, self.MinScale)
+        if self.MaxScale is not None:
+            Scale = min(Scale, self.MaxScale)
+        self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
+        self.TransformVector = N.array((Scale,-Scale),N.float) * self.MapProjectionVector
+        self.Scale = Scale
+        self._BackgroundDirty = True
+        if DrawFlag:
+            self.Draw()
+
     def RemoveObjects(self, Objects):
         for Object in Objects:
-            self.RemoveObject(Object, ResetBB = False)
+            self.RemoveObject(Object, ResetBB=False)
         self.BoundingBoxDirty = True
-        
+
     def RemoveObject(self, Object, ResetBB = True):
         ##fixme: Using the list.remove method is kind of slow
         if Object.InForeground:
@@ -2554,7 +2759,15 @@ class FloatCanvas(wx.Panel):
         if ResetBB:
             self.BoundingBoxDirty = True
 
-    def ClearAll(self, ResetBB = True):
+    def ClearAll(self, ResetBB=True):
+        """
+        ClearAll(ResetBB=True)
+
+        Removes all DrawObjects from the Canvas
+
+        If ResetBB is set to False, the original bounding box will remain
+
+        """
         self._DrawList = []
         self._ForeDrawList = []
         self._BackgroundDirty = True
@@ -2565,52 +2778,34 @@ class FloatCanvas(wx.Panel):
         self.MakeNewBuffers()
         self.HitDict = None
 
-##    No longer called
-##    def _AddBoundingBox(self,NewBB):
-##        if self.BoundingBox is None:
-##            self.BoundingBox = NewBB
-##            self.ZoomToBB(NewBB,DrawFlag = False)
-##        else:
-##            self.BoundingBox = array( ( (min(self.BoundingBox[0,0],NewBB[0,0]),
-##                                         min(self.BoundingBox[0,1],NewBB[0,1])),
-##                                        (max(self.BoundingBox[1,0],NewBB[1,0]),
-##                                         max(self.BoundingBox[1,1],NewBB[1,1]))),
-##                                      Float)
-
-    def _getboundingbox(bboxarray): # lrk: added this
-
-        upperleft = minimum.reduce(bboxarray[:,0])
-        lowerright = maximum.reduce(bboxarray[:,1])
-        return array((upperleft, lowerright), Float)
-
-    _getboundingbox = staticmethod(_getboundingbox)
-
     def _ResetBoundingBox(self):
         if self._DrawList or self._ForeDrawList:
-            bboxarray = zeros((len(self._DrawList)+len(self._ForeDrawList), 2, 2),Float) 
-            i = -1 # just in case _DrawList is empty
-            for (i, BB) in enumerate(self._DrawList):
-                bboxarray[i] = BB.BoundingBox
-            for (j, BB) in enumerate(self._ForeDrawList):
-                bboxarray[i+j+1] = BB.BoundingBox
-            self.BoundingBox = self._getboundingbox(bboxarray)
+            bblist = []
+            for (i, obj) in enumerate(self._DrawList):
+                bblist.append(obj.BoundingBox)
+            for (j, obj) in enumerate(self._ForeDrawList):
+                bblist.append(obj.BoundingBox)
+            self.BoundingBox = BBox.fromBBArray(bblist)
         else:
             self.BoundingBox = None
-            self.ViewPortCenter= array( (0,0), Float)
-            self.TransformVector = array( (1,-1), Float)
-            self.MapProjectionVector = array( (1,1), Float)
-            self.Scale = 1        
+            self.ViewPortCenter= N.array( (0,0), N.float)
+            self.TransformVector = N.array( (1,-1), N.float)
+            self.MapProjectionVector = N.array( (1,1), N.float)
+            self.Scale = 1
         self.BoundingBoxDirty = False
 
-    def PixelToWorld(self,Points):
+    def PixelToWorld(self, Points):
         """
         Converts coordinates from Pixel coordinates to world coordinates.
-        
-        Points is a tuple of (x,y) coordinates, or a list of such tuples, or a NX2 Numpy array of x,y coordinates.
-        
+
+        Points is a tuple of (x,y) coordinates, or a list of such tuples,
+        or a NX2 Numpy array of x,y coordinates.
+
         """
-        return  (((asarray(Points,Float) - (self.PanelSize/2))/self.TransformVector) + self.ViewPortCenter)
-        
+        return  (((N.asarray(Points, N.float) -
+                   (self.PanelSize/2))/self.TransformVector) +
+                 self.ViewPortCenter)
+
     def WorldToPixel(self,Coordinates):
         """
         This function will get passed to the drawing functions of the objects,
@@ -2618,8 +2813,8 @@ class FloatCanvas(wx.Panel):
         Coordinates should be a NX2 array of (x,y) coordinates, or
         a 2-tuple, or sequence of 2-tuples.
         """
-        #Note: this can be called by users code for various reasons, so asarray is needed.
-        return  (((asarray(Coordinates,Float) -
+        #Note: this can be called by users code for various reasons, so N.asarray is needed.
+        return  (((N.asarray(Coordinates,N.float) -
                    self.ViewPortCenter)*self.TransformVector)+
                  (self.HalfPanelSize)).astype('i')
 
@@ -2627,24 +2822,24 @@ class FloatCanvas(wx.Panel):
         """
         This function will get passed to the drawing functions of the objects,
         to Change a length from world to pixel coordinates.
-        
+
         Lengths should be a NX2 array of (x,y) coordinates, or
         a 2-tuple, or sequence of 2-tuples.
         """
-        return  ( (asarray(Lengths,Float)*self.TransformVector) ).astype('i')
+        return  ( (N.asarray(Lengths, N.float)*self.TransformVector) ).astype('i')
 
     def ScalePixelToWorld(self,Lengths):
         """
         This function computes a pair of x.y lengths,
         to change then from pixel to world coordinates.
-        
+
         Lengths should be a NX2 array of (x,y) coordinates, or
         a 2-tuple, or sequence of 2-tuples.
         """
 
-        return  (asarray(Lengths,Float) / self.TransformVector)
-    
-    def AddObject(self,obj):
+        return  (N.asarray(Lengths,N.float) / self.TransformVector)
+
+    def AddObject(self, obj):
         # put in a reference to the Canvas, so remove and other stuff can work
         obj._Canvas = self
         if  obj.InForeground:
@@ -2656,11 +2851,15 @@ class FloatCanvas(wx.Panel):
         self.BoundingBoxDirty = True
         return True
 
+    def AddObjects(self, Objects):
+        for Object in Objects:
+            self.AddObject(Object)
+
     def _DrawObjects(self, dc, DrawList, ScreenDC, ViewPortBB, HTdc = None):
         """
         This is a convenience function;
         This function takes the list of objects and draws them to specified
-        device context.  
+        device context.
         """
         dc.SetBackground(self.BackgroundBrush)
         dc.BeginDrawing()
@@ -2679,24 +2878,26 @@ class FloatCanvas(wx.Panel):
 
     def SaveAsImage(self, filename, ImageType=wx.BITMAP_TYPE_PNG):
         """
-        
+
         Saves the current image as an image file. The default is in the
-        PNG format. Other formats can be spcified using the wx flags:
+        PNG format. Other formats can be specified using the wx flags:
 
+        wx.BITMAP_TYPE_PNG
+        wx.BITMAP_TYPE_JPG
         wx.BITMAP_TYPE_BMP
         wx.BITMAP_TYPE_XBM
         wx.BITMAP_TYPE_XPM
         etc. (see the wx docs for the complete list)
 
         """
-        
+
         self._Buffer.SaveFile(filename, ImageType)
 
 
 def _makeFloatCanvasAddMethods(): ## lrk's code for doing this in module __init__
     classnames = ["Circle", "Ellipse", "Rectangle", "ScaledText", "Polygon",
-                  "Line", "Text", "PointSet","Point", "Arrow","ScaledTextBox",
-                  "SquarePoint","Bitmap", "ScaledBitmap"]
+                  "Line", "Text", "PointSet","Point", "Arrow", "ArrowLine", "ScaledTextBox",
+                  "SquarePoint","Bitmap", "ScaledBitmap", "Spline", "Group"]
     for classname in classnames:
         klass = globals()[classname]
         def getaddshapemethod(klass=klass):
@@ -2714,6 +2915,6 @@ def _makeFloatCanvasAddMethods(): ## lrk's code for doing this in module __init_
             docstring += ", whose docstring is:\n%s" % klass.__doc__
         FloatCanvas.__dict__[methodname].__doc__ = docstring
 
-_makeFloatCanvasAddMethods()    
+_makeFloatCanvasAddMethods()
 
 
diff --git a/wxPython/wx/lib/floatcanvas/GUIMode.py b/wxPython/wx/lib/floatcanvas/GUIMode.py
new file mode 100644 (file)
index 0000000..08ca473
--- /dev/null
@@ -0,0 +1,347 @@
+"""
+
+Module that holds the GUI modes used by FloatCanvas
+
+
+Note that this can only be imported after a wx.App() has been created.
+
+This approach was inpired by Christian Blouin, who also wrote the initial
+version of the code.
+
+"""
+
+import wx
+## fixme: events should live in their own module, so all of FloatCanvas
+##        wouldn't have to be imported here.
+import FloatCanvas, Resources
+import numpy as N
+
+## create all the Cursors, so they don't need to be created each time.
+if "wxMac" in wx.PlatformInfo: # use 16X16 cursors for wxMac
+    HandCursor = wx.CursorFromImage(Resources.getHand16Image())
+    GrabHandCursor = wx.CursorFromImage(Resources.getGrabHand16Image())
+
+    img = Resources.getMagPlus16Image()
+    img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
+    img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
+    MagPlusCursor = wx.CursorFromImage(img)
+
+    img = Resources.getMagMinus16Image()
+    img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
+    img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
+    MagMinusCursor = wx.CursorFromImage(img)
+else: # use 24X24 cursors for GTK and Windows
+    HandCursor = wx.CursorFromImage(Resources.getHandImage())
+    GrabHandCursor = wx.CursorFromImage(Resources.getGrabHandImage())
+
+    img = Resources.getMagPlusImage()
+    img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
+    img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
+    MagPlusCursor = wx.CursorFromImage(img)
+
+    img = Resources.getMagMinusImage()
+    img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
+    img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
+    MagMinusCursor = wx.CursorFromImage(img)
+
+
+class GUIBase:
+    """
+    Basic Mouse mode and baseclass for other GUImode.
+
+    This one does nothing with any event
+
+    """
+    def __init__(self, parent):
+        self.parent = parent
+    
+    Cursor = wx.NullCursor
+
+    # Handlers
+    def OnLeftDown(self, event):
+        pass
+    def OnLeftUp(self, event):
+        pass
+    def OnLeftDouble(self, event):
+        pass
+    def OnRightDown(self, event):
+        pass
+    def OnRightUp(self, event):
+        pass
+    def OnRightDouble(self, event):
+        pass
+    def OnMiddleDown(self, event):
+        pass
+    def OnMiddleUp(self, event):
+        pass
+    def OnMiddleDouble(self, event):
+        pass
+    def OnWheel(self, event):
+        pass
+    def OnMove(self, event):
+        pass
+            
+    def UpdateScreen(self):
+        """
+        Update gets called if the screen has been repainted in the middle of a zoom in
+        so the Rubber Band Box can get updated
+        """
+        pass
+
+class GUIMouse(GUIBase):
+    """
+
+    Mouse mode checks for a hit test, and if nothing is hit,
+    raises a FloatCanvas mouse event for each event.
+
+    """
+
+    Cursor = wx.NullCursor
+
+    # Handlers
+    def OnLeftDown(self, event):
+        EventType = FloatCanvas.EVT_FC_LEFT_DOWN
+        if not self.parent.HitTest(event, EventType):
+            self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnLeftUp(self, event):
+        EventType = FloatCanvas.EVT_FC_LEFT_UP
+        if not self.parent.HitTest(event, EventType):
+            self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnLeftDouble(self, event):
+        EventType = FloatCanvas.EVT_FC_LEFT_DCLICK
+        if not self.parent.HitTest(event, EventType):
+                self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnMiddleDown(self, event):
+        EventType = FloatCanvas.EVT_FC_MIDDLE_DOWN
+        if not self.parent.HitTest(event, EventType):
+            self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnMiddleUp(self, event):
+        EventType = FloatCanvas.EVT_FC_MIDDLE_UP
+        if not self.parent.HitTest(event, EventType):
+            self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnMiddleDouble(self, event):
+        EventType = FloatCanvas.EVT_FC_MIDDLE_DCLICK
+        if not self.parent.HitTest(event, EventType):
+            self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnRightDown(self, event):
+        EventType = FloatCanvas.EVT_FC_RIGHT_DOWN
+        if not self.parent.HitTest(event, EventType):
+            self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnRightUp(self, event):
+        EventType = FloatCanvas.EVT_FC_RIGHT_UP
+        if not self.parent.HitTest(event, EventType):
+            self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnRightDouble(self, event):
+        EventType = FloatCanvas.EVT_FC_RIGHT_DCLICK
+        if not self.parent.HitTest(event, EventType):
+            self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnWheel(self, event):
+        EventType = FloatCanvas.EVT_FC_MOUSEWHEEL
+        self.parent._RaiseMouseEvent(event, EventType)
+
+    def OnMove(self, event):
+        ## The Move event always gets raised, even if there is a hit-test
+        self.parent.MouseOverTest(event)
+        self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+
+
+class GUIMove(GUIBase):
+
+    Cursor = HandCursor
+    GrabCursor = GrabHandCursor
+    def __init__(self, parent):
+        GUIBase.__init__(self, parent)
+        self.StartMove = None
+        self.PrevMoveXY = None
+
+    def OnLeftDown(self, event):
+        self.parent.SetCursor(self.GrabCursor)
+        self.parent.CaptureMouse()
+        self.StartMove = N.array( event.GetPosition() )
+        self.PrevMoveXY = (0,0)
+
+    def OnLeftUp(self, event):
+        if self.StartMove is not None:
+            StartMove = self.StartMove
+            EndMove = N.array(event.GetPosition())
+            DiffMove = StartMove-EndMove
+            if N.sum(DiffMove**2) > 16:
+                self.parent.MoveImage(DiffMove, 'Pixel')
+            self.StartMove = None
+        self.parent.SetCursor(self.Cursor)
+
+    def OnMove(self, event):
+        # Allways raise the Move event.
+        self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+        if event.Dragging() and event.LeftIsDown() and not self.StartMove is None:
+            xy1 = N.array( event.GetPosition() )
+            wh = self.parent.PanelSize
+            xy_tl = xy1 - self.StartMove
+            dc = wx.ClientDC(self.parent)
+            dc.BeginDrawing()
+            x1,y1 = self.PrevMoveXY
+            x2,y2 = xy_tl
+            w,h = self.parent.PanelSize
+            ##fixme: This sure could be cleaner!
+            if x2 > x1 and y2 > y1:
+                xa = xb = x1
+                ya = yb = y1
+                wa = w
+                ha = y2 - y1
+                wb = x2-  x1
+                hb = h
+            elif x2 > x1 and y2 <= y1:
+                xa = x1
+                ya = y1
+                wa = x2 - x1
+                ha = h
+                xb = x1
+                yb = y2 + h
+                wb = w
+                hb = y1 - y2
+            elif x2 <= x1 and y2 > y1:
+                xa = x1
+                ya = y1
+                wa = w
+                ha = y2 - y1
+                xb = x2 + w
+                yb = y1
+                wb = x1 - x2
+                hb = h - y2 + y1
+            elif x2 <= x1 and y2 <= y1:
+                xa = x2 + w
+                ya = y1
+                wa = x1 - x2
+                ha = h
+                xb = x1
+                yb = y2 + h
+                wb = w
+                hb = y1 - y2
+
+            dc.SetPen(wx.TRANSPARENT_PEN)
+            dc.SetBrush(self.parent.BackgroundBrush)
+            dc.DrawRectangle(xa, ya, wa, ha)
+            dc.DrawRectangle(xb, yb, wb, hb)
+            self.PrevMoveXY = xy_tl
+            if self.parent._ForeDrawList:
+                dc.DrawBitmapPoint(self.parent._ForegroundBuffer,xy_tl)
+            else:
+                dc.DrawBitmapPoint(self.parent._Buffer,xy_tl)
+            dc.EndDrawing()
+
+    def OnWheel(self, event):
+        """
+           By default, zoom in/out by a 0.1 factor per Wheel event.
+        """
+        if event.GetWheelRotation() < 0:
+            self.parent.Zoom(0.9)
+        else:
+            self.parent.Zoom(1.1)
+
+class GUIZoomIn(GUIBase):
+    Cursor = MagPlusCursor
+    def __init__(self, parent):
+        GUIBase.__init__(self, parent)
+        self.StartRBBox = None
+        self.PrevRBBox = None
+
+    def OnLeftDown(self, event):
+        self.StartRBBox = N.array( event.GetPosition() )
+        self.PrevRBBox = None
+        self.parent.CaptureMouse()
+
+    def OnLeftUp(self, event):
+        #if self.parent.HasCapture():
+        #        self.parent.ReleaseMouse()
+        if event.LeftUp() and not self.StartRBBox is None:
+            self.PrevRBBox = None
+            EndRBBox = event.GetPosition()
+            StartRBBox = self.StartRBBox
+            # if mouse has moved less that ten pixels, don't use the box.
+            if ( abs(StartRBBox[0] - EndRBBox[0]) > 10
+                    and abs(StartRBBox[1] - EndRBBox[1]) > 10 ):
+                EndRBBox = self.parent.PixelToWorld(EndRBBox)
+                StartRBBox = self.parent.PixelToWorld(StartRBBox)
+                BB = N.array(((min(EndRBBox[0],StartRBBox[0]),
+                                min(EndRBBox[1],StartRBBox[1])),
+                            (max(EndRBBox[0],StartRBBox[0]),
+                                max(EndRBBox[1],StartRBBox[1]))),N.float_)
+                self.parent.ZoomToBB(BB)
+            else:
+                Center = self.parent.PixelToWorld(StartRBBox)
+                self.parent.Zoom(1.5,Center)
+            self.StartRBBox = None
+
+    def OnMove(self, event):
+        # Allways raise the Move event.
+        self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+        if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None):
+            xy0 = self.StartRBBox
+            xy1 = N.array( event.GetPosition() )
+            wh  = abs(xy1 - xy0)
+            wh[0] = max(wh[0], int(wh[1]*self.parent.AspectRatio))
+            wh[1] = int(wh[0] / self.parent.AspectRatio)
+            xy_c = (xy0 + xy1) / 2
+            dc = wx.ClientDC(self.parent)
+            dc.BeginDrawing()
+            dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
+            dc.SetBrush(wx.TRANSPARENT_BRUSH)
+            dc.SetLogicalFunction(wx.XOR)
+            if self.PrevRBBox:
+                dc.DrawRectanglePointSize(*self.PrevRBBox)
+            self.PrevRBBox = ( xy_c - wh/2, wh )
+            dc.DrawRectanglePointSize( *self.PrevRBBox )
+            dc.EndDrawing()
+            
+    def UpdateScreen(self):
+        """
+        Update gets called if the screen has been repainted in the middle of a zoom in
+        so the Rubber Band Box can get updated
+        """
+        if self.PrevRBBox is not None:
+            dc = wx.ClientDC(self.parent)
+            dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
+            dc.SetBrush(wx.TRANSPARENT_BRUSH)
+            dc.SetLogicalFunction(wx.XOR)
+            dc.DrawRectanglePointSize(*self.PrevRBBox)
+
+    def OnRightDown(self, event):
+        self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
+
+    def OnWheel(self, event):
+        if event.GetWheelRotation() < 0:
+            self.parent.Zoom(0.9)
+        else:
+            self.parent.Zoom(1.1)
+
+class GUIZoomOut(GUIBase):
+
+    Cursor = MagMinusCursor
+
+    def OnLeftDown(self, event):
+        self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
+
+    def OnRightDown(self, event):
+        self.parent.Zoom(1.5, event.GetPosition(), centerCoords="pixel")
+
+    def OnWheel(self, event):
+        if event.GetWheelRotation() < 0:
+            self.parent.Zoom(0.9)
+        else:
+            self.parent.Zoom(1.1)
+
+    def OnMove(self, event):
+        # Allways raise the Move event.
+        self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+
index 1770a109e8b5f27da9043529a0a135ba8694ee0b..b2c6d67422c501c4cdb889c45dc4ac13e6da5f5a 100644 (file)
@@ -4,18 +4,9 @@ A Panel that includes the FloatCanvas and Navigation controls
 """
 
 import wx
-
 import FloatCanvas, Resources
 
-ID_ZOOM_IN_BUTTON = wx.NewId()
-ID_ZOOM_OUT_BUTTON = wx.NewId()
-ID_ZOOM_TO_FIT_BUTTON = wx.NewId()
-ID_MOVE_MODE_BUTTON = wx.NewId()
-ID_POINTER_BUTTON = wx.NewId()
-
 
-#---------------------------------------------------------------------------
-    
 class NavCanvas(wx.Panel):
     """
     NavCanvas.py
@@ -23,96 +14,80 @@ class NavCanvas(wx.Panel):
     This is a high level window that encloses the FloatCanvas in a panel
     and adds a Navigation toolbar.
 
-    Copyright: Christopher Barker)
-
-    License: Same as the version of wxPython you are using it with
-
-    Please let me know if you're using this!!!
+    """
 
-    Contact me at:
+    def __init__(self,
+                   parent,
+                   id = wx.ID_ANY,
+                   size = wx.DefaultSize,
+                   **kwargs): # The rest just get passed into FloatCanvas
+        wx.Panel.__init__(self, parent, id, size=size)
 
-    Chris.Barker@noaa.gov
+        self.BuildToolbar()
+        ## Create the vertical sizer for the toolbar and Panel
+        box = wx.BoxSizer(wx.VERTICAL)
+        box.Add(self.ToolBar, 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
 
-    """ 
-    
-    def __init__(self, parent, id = -1,
-                 size = wx.DefaultSize,
-                 **kwargs): # The rest just get passed into FloatCanvas
+        self.Canvas = FloatCanvas.FloatCanvas(self, **kwargs)
+        box.Add(self.Canvas, 1, wx.GROW)
 
-        wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
+        self.SetSizerAndFit(box)
 
-        ## Create the vertical sizer for the toolbar and Panel
-        box = wx.BoxSizer(wx.VERTICAL)
-        box.Add(self.BuildToolbar(), 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
-        
-        self.Canvas = FloatCanvas.FloatCanvas( self, wx.NewId(),
-                                   size = wx.DefaultSize,
-                                   **kwargs)
-        box.Add(self.Canvas,1,wx.GROW)
 
-        box.Fit(self)
-        self.SetSizer(box)
+        import GUIMode # here so that it doesn't get imported before wx.App()
+        self.GUIZoomIn  =  GUIMode.GUIZoomIn(self.Canvas)
+        self.GUIZoomOut =  GUIMode.GUIZoomOut(self.Canvas)
+        self.GUIMove    =  GUIMode.GUIMove(self.Canvas)
+        self.GUIMouse   =  GUIMode.GUIMouse(self.Canvas)
 
         # default to Mouse mode
-        self.ToolBar.ToggleTool(ID_POINTER_BUTTON,1)
-        self.Canvas.SetMode("Mouse")
-        
-        return None
+        self.ToolBar.ToggleTool(self.PointerTool.GetId(), True)
+        self.Canvas.SetMode(self.GUIMouse)
 
-    def __getattr__(self, name):
-        """
-        Delegate all extra methods to the Canvas
-        """
-        attrib = getattr(self.Canvas, name)
-        ## add the attribute to this module's dict for future calls
-        self.__dict__[name] = attrib
-        return attrib
+        return None
 
     def BuildToolbar(self):
-        tb = wx.ToolBar(self,-1)
+        tb = wx.ToolBar(self)
         self.ToolBar = tb
-        
         tb.SetToolBitmapSize((24,24))
-        
-        tb.AddTool(ID_POINTER_BUTTON, Resources.getPointerBitmap(), isToggle=True, shortHelpString = "Pointer")
-        wx.EVT_TOOL(self, ID_POINTER_BUTTON, self.SetToolMode)
-
-        tb.AddTool(ID_ZOOM_IN_BUTTON, Resources.getMagPlusBitmap(), isToggle=True, shortHelpString = "Zoom In")
-        wx.EVT_TOOL(self, ID_ZOOM_IN_BUTTON, self.SetToolMode)
-        
-        tb.AddTool(ID_ZOOM_OUT_BUTTON, Resources.getMagMinusBitmap(), isToggle=True, shortHelpString = "Zoom Out")
-        wx.EVT_TOOL(self, ID_ZOOM_OUT_BUTTON, self.SetToolMode)
-        
-        tb.AddTool(ID_MOVE_MODE_BUTTON, Resources.getHandBitmap(), isToggle=True, shortHelpString = "Move")
-        wx.EVT_TOOL(self, ID_MOVE_MODE_BUTTON, self.SetToolMode)
-        
+
+        self.PointerTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getPointerBitmap(), shortHelp = "Pointer")
+        self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIMouse), self.PointerTool)
+
+        self.ZoomInTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getMagPlusBitmap(), shortHelp = "Zoom In")
+        self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIZoomIn), self.ZoomInTool)
+    
+        self.ZoomOutTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getMagMinusBitmap(), shortHelp = "Zoom Out")
+        self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIZoomOut), self.ZoomOutTool)
+
+        self.MoveTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getHandBitmap(), shortHelp = "Move")
+        self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIMove), self.MoveTool)
+
         tb.AddSeparator()
-        
-        tb.AddControl(wx.Button(tb, ID_ZOOM_TO_FIT_BUTTON, "Zoom To Fit",wx.DefaultPosition, wx.DefaultSize))
-        wx.EVT_BUTTON(self, ID_ZOOM_TO_FIT_BUTTON, self.ZoomToFit)
+
+        self.ZoomButton = wx.Button(tb, label="Zoom To Fit")
+        tb.AddControl(self.ZoomButton)
+        self.ZoomButton.Bind(wx.EVT_BUTTON, self.ZoomToFit)
 
         tb.Realize()
-        S = tb.GetSize()
-        tb.SetSizeHints(S[0],S[1])
+        ## fixme: remove this when the bug is fixed!
+        wx.CallAfter(self.HideShowHack) # this required on wxPython 2.8.3 on OS-X
+
         return tb
 
-    def SetToolMode(self,event):
-        for id in [ID_ZOOM_IN_BUTTON,
-                   ID_ZOOM_OUT_BUTTON,
-                   ID_MOVE_MODE_BUTTON,
-                   ID_POINTER_BUTTON]:
-            self.ToolBar.ToggleTool(id,0)
-        self.ToolBar.ToggleTool(event.GetId(),1)
-        if event.GetId() == ID_ZOOM_IN_BUTTON:
-            self.Canvas.SetMode("ZoomIn")
-        elif event.GetId() == ID_ZOOM_OUT_BUTTON:
-            self.Canvas.SetMode("ZoomOut")
-        elif event.GetId() == ID_MOVE_MODE_BUTTON:
-            self.Canvas.SetMode("Move")
-        elif event.GetId() == ID_POINTER_BUTTON:
-            self.Canvas.SetMode("Mouse")
+    def HideShowHack(self):
+        ##fixme: remove this when the bug is fixed!
+        """
+        Hack to hide and show button on toolbar to get around OS-X bug on
+        wxPython2.8 on OS-X
+        """
+        self.ZoomButton.Hide()
+        self.ZoomButton.Show()
 
+    def SetMode(self, Mode):
+        self.Canvas.SetMode(Mode)
 
     def ZoomToFit(self,Event):
         self.Canvas.ZoomToBB()
+        self.Canvas.SetFocus() # Otherwise the focus stays on the Button, and wheel events are lost.
 
diff --git a/wxPython/wx/lib/floatcanvas/Utilities/BBox.py b/wxPython/wx/lib/floatcanvas/Utilities/BBox.py
new file mode 100644 (file)
index 0000000..2815531
--- /dev/null
@@ -0,0 +1,170 @@
+"""
+A Bounding Box object and assorted utilities , subclassed from a numpy array
+
+"""
+
+import numpy as N
+
+class BBox(N.ndarray):
+    """
+    A Bounding Box object:
+    
+    Takes Data as an array. Data is any python sequence that can be turned into a 
+    2x2 numpy array of floats:
+
+    [[MinX, MinY ],
+     [MaxX, MaxY ]]
+
+    It is a subclass of numpy.ndarray, so for the most part it can be used as 
+    an array, and arrays that fit the above description can be used in its place.
+    
+    Usually created by the factory functions:
+    
+        asBBox
+        
+        and 
+        
+        fromPoints
+    
+    """
+    def __new__(subtype, data):
+        """
+        Takes Data as an array. Data is any python sequence that can be turned into a 
+        2x2 numpy array of floats:
+
+        [[MinX, MinY ],
+        [MaxX, MaxY ]]
+
+        You don't usually call this directly. BBox objects are created with the factory functions:
+        
+        asBBox
+        
+        and 
+        
+        fromPoints
+
+        """
+        arr = N.array(data, N.float)
+        arr.shape = (2,2)
+        if arr[0,0] > arr[1,0] or arr[0,1] > arr[1,1]:
+            # note: zero sized BB OK.
+            raise ValueError("BBox values not aligned: \n minimum values must be less that maximum values")
+        return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
+
+    def Overlaps(self, BB):
+        """
+        Overlap(BB):
+
+        Tests if the given Bounding Box overlaps with this one.
+        Returns True is the Bounding boxes overlap, False otherwise
+        If they are just touching, returns True
+        """
+
+        if ( (self[1,0] >= BB[0,0]) and (self[0,0] <= BB[1,0]) and
+             (self[1,1] >= BB[0,1]) and (self[0,1] <= BB[1,1]) ):
+            return True
+        else:
+            return False
+
+    def Inside(self, BB):
+        """
+        Inside(BB):
+
+        Tests if the given Bounding Box is entirely inside this one.
+
+        Returns True if it is entirely inside, or touching the
+        border.
+
+        Returns False otherwise
+        """
+        if ( (BB[0,0] >= self[0,0]) and (BB[1,0] <= self[1,0]) and
+             (BB[0,1] >= self[0,1]) and (BB[1,1] <= self[1,1]) ):
+            return True
+        else:
+            return False
+    
+    def Merge(self, BB):
+        """
+        Joins this bounding box with the one passed in, maybe making this one bigger
+
+        """ 
+
+        if BB[0,0] < self[0,0]: self[0,0] = BB[0,0]
+        if BB[0,1] < self[0,1]: self[0,1] = BB[0,1]
+        if BB[1,0] > self[1,0]: self[1,0] = BB[1,0]
+        if BB[1,1] > self[1,1]: self[1,1] = BB[1,1]
+    
+    ### This could be used for a make BB from a bunch of BBs
+
+    #~ def _getboundingbox(bboxarray): # lrk: added this
+        #~ # returns the bounding box of a bunch of bounding boxes
+        #~ upperleft = N.minimum.reduce(bboxarray[:,0])
+        #~ lowerright = N.maximum.reduce(bboxarray[:,1])
+        #~ return N.array((upperleft, lowerright), N.float)
+    #~ _getboundingbox = staticmethod(_getboundingbox)
+
+
+    ## Save the ndarray __eq__ for internal use.
+    Array__eq__ = N.ndarray.__eq__
+    def __eq__(self, BB):
+        """
+        __eq__(BB) The equality operator
+
+        A == B if and only if all the entries are the same
+
+        """
+        return N.all(self.Array__eq__(BB))
+        
+
+def asBBox(data):
+    """
+    returns a BBox object.
+
+    If object is a BBox, it is returned unaltered
+
+    If object is a numpy array, a BBox object is returned that shares a
+    view of the data with that array
+
+    """
+
+    if isinstance(data, BBox):
+        return data
+    arr = N.asarray(data, N.float)
+    return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
+
+def fromPoints(Points):
+    """
+    fromPoints (Points).
+
+    reruns the bounding box of the set of points in Points. Points can
+    be any python object that can be turned into a numpy NX2 array of Floats.
+
+    If a single point is passed in, a zero-size Bounding Box is returned.
+    
+    """
+    Points = N.asarray(Points, N.float).reshape(-1,2)
+
+    arr = N.vstack( (Points.min(0), Points.max(0)) )
+    return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
+
+def fromBBArray(BBarray):
+   """
+   Builds a BBox object from an array of Bounding Boxes. 
+   The resulting Bounding Box encompases all the included BBs.
+   
+   The BBarray is in the shape: (Nx2x2) where BBarray[n] is a 2x2 array that represents a BBox
+   """
+   
+   #upperleft = N.minimum.reduce(BBarray[:,0])
+   #lowerright = N.maximum.reduce(BBarray[:,1])
+
+#   BBarray = N.asarray(BBarray, N.float).reshape(-1,2)
+#   arr = N.vstack( (BBarray.min(0), BBarray.max(0)) )
+   BBarray = N.asarray(BBarray, N.float).reshape(-1,2,2)
+   arr = N.vstack( (BBarray[:,0,:].min(0), BBarray[:,1,:].max(0)) )
+   return asBBox(arr)
+   #return asBBox( (upperleft, lowerright) ) * 2
+   
+   
+   
+   
diff --git a/wxPython/wx/lib/floatcanvas/Utilities/BBoxTest.py b/wxPython/wx/lib/floatcanvas/Utilities/BBoxTest.py
new file mode 100644 (file)
index 0000000..3cca2e9
--- /dev/null
@@ -0,0 +1,354 @@
+
+"""
+Test code for the BBox Object
+
+"""
+
+import unittest
+
+from BBox import *
+
+class testCreator(unittest.TestCase):
+    def testCreates(self):
+        B = BBox(((0,0),(5,5)))
+        self.failUnless(isinstance(B, BBox))
+
+    def testType(self):
+        B = N.array(((0,0),(5,5)))
+        self.failIf(isinstance(B, BBox))
+
+    def testDataType(self):
+        B = BBox(((0,0),(5,5)))
+        self.failUnless(B.dtype == N.float)
+
+    def testShape(self):
+        B = BBox((0,0,5,5))
+        self.failUnless(B.shape == (2,2))
+        
+    def testShape2(self):
+        self.failUnlessRaises(ValueError, BBox, (0,0,5) )
+        
+    def testShape3(self):
+        self.failUnlessRaises(ValueError, BBox, (0,0,5,6,7) )
+
+    def testArrayConstruction(self):
+        A = N.array(((4,5),(10,12)), N.float_)
+        B = BBox(A)
+        self.failUnless(isinstance(B, BBox))
+        
+    def testMinMax(self):
+        self.failUnlessRaises(ValueError, BBox, (0,0,-1,6) )
+
+    def testMinMax2(self):
+        self.failUnlessRaises(ValueError, BBox, (0,0,1,-6) )
+
+    def testMinMax(self):
+        # OK to have a zero-sized BB
+        B = BBox(((0,0),(0,5)))
+        self.failUnless(isinstance(B, BBox))
+
+    def testMinMax2(self):
+        # OK to have a zero-sized BB
+        B = BBox(((10.0,-34),(10.0,-34.0)))
+        self.failUnless(isinstance(B, BBox))
+
+    def testMinMax3(self):
+        # OK to have a tiny BB
+        B = BBox(((0,0),(1e-20,5)))
+        self.failUnless(isinstance(B, BBox))
+
+    def testMinMax4(self):
+        # Should catch tiny difference
+        self.failUnlessRaises(ValueError, BBox, ((0,0), (-1e-20,5)) )
+
+class testAsBBox(unittest.TestCase):
+
+    def testPassThrough(self):
+        B = BBox(((0,0),(5,5)))
+        C = asBBox(B)
+        self.failUnless(B is C)
+
+    def testPassThrough2(self):
+        B = (((0,0),(5,5)))
+        C = asBBox(B)
+        self.failIf(B is C)
+    
+    def testPassArray(self):
+        # Different data type
+        A = N.array( (((0,0),(5,5))) )
+        C = asBBox(A)
+        self.failIf(A is C)
+    
+    def testPassArray2(self):
+        # same data type -- should be a view
+        A = N.array( (((0,0),(5,5))), N.float_ )
+        C = asBBox(A)
+        A[0,0] = -10
+        self.failUnless(C[0,0] == A[0,0])
+    
+class testIntersect(unittest.TestCase):
+
+    def testSame(self):
+        B = BBox(((-23.5, 456),(56, 532.0)))
+        C = BBox(((-23.5, 456),(56, 532.0)))
+        self.failUnless(B.Overlaps(C) )
+    
+    def testUpperLeft(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (0, 12),(10, 32.0) ) )
+        self.failUnless(B.Overlaps(C) )
+    
+    def testUpperRight(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (12, 12),(25, 32.0) ) )
+        self.failUnless(B.Overlaps(C) )
+    
+    def testLowerRight(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (12, 5),(25, 15) ) )
+        self.failUnless(B.Overlaps(C) )
+    
+    def testLowerLeft(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (-10, 5),(8.5, 15) ) )
+        self.failUnless(B.Overlaps(C) )
+        
+    def testBelow(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (-10, 5),(8.5, 9.2) ) )
+        self.failIf(B.Overlaps(C) )
+        
+    def testAbove(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (-10, 25.001),(8.5, 32) ) )
+        self.failIf(B.Overlaps(C) )
+        
+    def testLeft(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (4, 8),(4.95, 32) ) )
+        self.failIf(B.Overlaps(C) )
+        
+    def testRight(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (17.1, 8),(17.95, 32) ) )
+        self.failIf(B.Overlaps(C) )
+
+    def testInside(self):
+        B = BBox( ( (-15, -25),(-5, -10) ) )
+        C = BBox( ( (-12, -22), (-6, -8) ) )
+        self.failUnless(B.Overlaps(C) )
+        
+    def testOutside(self):
+        B = BBox( ( (-15, -25),(-5, -10) ) )
+        C = BBox( ( (-17, -26), (3, 0) ) )
+        self.failUnless(B.Overlaps(C) )
+    
+    def testTouch(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (15, 8),(17.95, 32) ) )
+        self.failUnless(B.Overlaps(C) )
+        
+    def testCorner(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (15, 25),(17.95, 32) ) )
+        self.failUnless(B.Overlaps(C) )
+        
+    def testZeroSize(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (15, 25),(15, 25) ) )
+        self.failUnless(B.Overlaps(C) )
+        
+    def testZeroSize2(self):
+        B = BBox( ( (5, 10),(5, 10) ) )
+        C = BBox( ( (15, 25),(15, 25) ) )
+        self.failIf(B.Overlaps(C) )
+        
+    def testZeroSize3(self):
+        B = BBox( ( (5, 10),(5, 10) ) )
+        C = BBox( ( (0, 8),(10, 12) ) )
+        self.failUnless(B.Overlaps(C) )
+
+    def testZeroSize4(self):
+        B = BBox( ( (5, 1),(10, 25) ) )
+        C = BBox( ( (8, 8),(8, 8) ) )
+        self.failUnless(B.Overlaps(C) )
+
+
+
+class testEquality(unittest.TestCase):
+    def testSame(self):
+        B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        C = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        self.failUnless(B == C)
+        
+    def testIdentical(self):
+        B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        self.failUnless(B == B)
+        
+    def testNotSame(self):
+        B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        C = BBox( ( (1.0, 2.0), (5.0, 10.1) ) )
+        self.failIf(B == C)
+        
+    def testWithArray(self):
+        B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        C = N.array( ( (1.0, 2.0), (5.0, 10.0) ) )
+        self.failUnless(B == C)
+        
+    def testWithArray2(self):
+        B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        C = N.array( ( (1.0, 2.0), (5.0, 10.0) ) )
+        self.failUnless(C == B)
+        
+    def testWithArray2(self):
+        B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        C = N.array( ( (1.01, 2.0), (5.0, 10.0) ) )
+        self.failIf(C == B)
+        
+class testInside(unittest.TestCase):
+    def testSame(self):
+        B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        C = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        self.failUnless(B.Inside(C))
+
+    def testPoint(self):
+        B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        C = BBox( ( (3.0, 4.0), (3.0, 4.0) ) )
+        self.failUnless(B.Inside(C))
+
+    def testPointOutside(self):
+        B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        C = BBox( ( (-3.0, 4.0), (0.10, 4.0) ) )
+        self.failIf(B.Inside(C))
+
+    def testUpperLeft(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (0, 12),(10, 32.0) ) )
+        self.failIf(B.Inside(C) )
+    
+    def testUpperRight(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (12, 12),(25, 32.0) ) )
+        self.failIf(B.Inside(C) )
+    
+    def testLowerRight(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (12, 5),(25, 15) ) )
+        self.failIf(B.Inside(C) )
+    
+    def testLowerLeft(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (-10, 5),(8.5, 15) ) )
+        self.failIf(B.Inside(C) )
+        
+    def testBelow(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (-10, 5),(8.5, 9.2) ) )
+        self.failIf(B.Inside(C) )
+        
+    def testAbove(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (-10, 25.001),(8.5, 32) ) )
+        self.failIf(B.Inside(C) )
+        
+    def testLeft(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (4, 8),(4.95, 32) ) )
+        self.failIf(B.Inside(C) )
+        
+    def testRight(self):
+        B = BBox( ( (5, 10),(15, 25) ) )
+        C = BBox( ( (17.1, 8),(17.95, 32) ) )
+        self.failIf(B.Inside(C) )
+
+class testFromPoints(unittest.TestCase):
+
+    def testCreate(self):
+        Pts = N.array( ((5,2),
+                (3,4),
+                (1,6),
+                ), N.float_ )
+        B = fromPoints(Pts)
+        #B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+        self.failUnless(B[0,0] == 1.0 and
+                        B[0,1] == 2.0 and
+                        B[1,0] == 5.0 and
+                        B[1,1] == 6.0
+                        )
+    def testCreateInts(self):
+        Pts = N.array( ((5,2),
+                (3,4),
+                (1,6),
+                ) )
+        B = fromPoints(Pts)
+        self.failUnless(B[0,0] == 1.0 and
+                        B[0,1] == 2.0 and
+                        B[1,0] == 5.0 and
+                        B[1,1] == 6.0
+                        )
+
+    def testSinglePoint(self):
+        Pts = N.array( (5,2), N.float_ )
+        B = fromPoints(Pts)
+        self.failUnless(B[0,0] == 5.0 and
+                        B[0,1] == 2.0 and
+                        B[1,0] == 5.0 and
+                        B[1,1] == 2.0
+                        )
+
+    def testListTuples(self):
+        Pts = [ (3, 6.5),
+                (13, 43.2),
+                (-4.32, -4),
+                (65, -23),
+                (-0.0001, 23.432),
+                ]
+        B = fromPoints(Pts)       
+        self.failUnless(B[0,0] == -4.32 and
+                        B[0,1] == -23.0 and
+                        B[1,0] == 65.0 and
+                        B[1,1] == 43.2
+                        )
+class testMerge(unittest.TestCase):
+    A = BBox( ((-23.5, 456), (56, 532.0)) )
+    B = BBox( ((-20.3, 460), (54, 465  )) )# B should be completely inside A
+    C = BBox( ((-23.5, 456), (58, 540.0)) )# up and to the right or A
+    D = BBox( ((-26.5, 12), (56, 532.0)) )
+
+    def testInside(self):
+        C = self.A.copy()
+        C.Merge(self.B)
+        self.failUnless(C == self.A)
+
+    def testFullOutside(self):
+        C = self.B.copy()
+        C.Merge(self.A)
+        self.failUnless(C == self.A)
+
+    def testUpRight(self):
+        A = self.A.copy()
+        A.Merge(self.C)
+        self.failUnless(A[0] == self.A[0] and A[1] == self.C[1])
+
+    def testDownLeft(self):
+        A = self.A.copy()
+        A.Merge(self.D)
+        self.failUnless(A[0] == self.D[0] and A[1] == self.A[1])
+
+class testBBarray(unittest.TestCase):
+    BBarray = N.array( ( ((-23.5, 456), (56, 532.0)),
+                         ((-20.3, 460), (54, 465  )),
+                         ((-23.5, 456), (58, 540.0)),
+                         ((-26.5,  12), (56, 532.0)),
+                       ),
+                       dtype=N.float)
+    print BBarray
+    BB = asBBox( ((-26.5,  12.), ( 58. , 540.)) )
+
+    def testJoin(self):
+        BB = fromBBArray(self.BBarray)
+        self.failUnless(BB == self.BB, "Wrong BB was created. It was:\n%s \nit should have been:\n%s"%(BB, self.BB))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/wxPython/wx/lib/floatcanvas/Utilities/GUI.py b/wxPython/wx/lib/floatcanvas/Utilities/GUI.py
new file mode 100644 (file)
index 0000000..5cf11fd
--- /dev/null
@@ -0,0 +1,115 @@
+"""
+
+Part of the floatcanvas.Utilities package.
+
+This module contains assorted GUI-related utilities that can be used
+with FloatCanvas
+
+So far, they are:
+
+RubberBandBox: used to draw a RubberBand Box on the screen
+
+"""
+import wx
+from floatcanvas import FloatCanvas
+
+class RubberBandBox:
+    """
+    Class to provide a rubber band box that can be drawn on a Window
+
+    """
+
+    def __init__(self, Canvas, CallBack, Tol=5):
+
+        """
+        To initialize:
+        
+        RubberBandBox(Canvas, CallBack)
+
+        Canvas:  the FloatCanvas you want the Rubber band box to be used on
+
+        CallBack: is the method you want called when the mouse is
+                  released. That method will be called, passing in a rect
+                  parameter, where rect is: (Point, WH) of the rect in
+                  world coords.
+
+        Tol: The tolerance for the smallest rectangle allowed. defaults
+             to 5. In pixels
+
+        Methods:
+        
+        Enable() : Enables the Rubber Band Box (Binds the events)
+        
+        Disable() : Enables the Rubber Band Box (Unbinds the events)
+
+        Attributes:
+
+        CallBack: The callback function, if it's replaced you need to
+                  call Enable() again.
+                  
+        """
+
+        self.Canvas = Canvas
+        self.CallBack = CallBack
+        self.Tol = Tol
+        
+        self.Drawing = False
+        self.RBRect = None
+        self.StartPointWorld = None
+
+        return None
+
+    def Enable(self):
+        """
+        Called when you want the rubber band box to be enabled
+
+        """
+
+        # bind events:
+        self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove ) 
+        self.Canvas.Bind(FloatCanvas.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Canvas.Bind(FloatCanvas.EVT_LEFT_UP, self.OnLeftUp ) 
+
+    def Disable(self):
+        """
+        Called when you don't want the rubber band box to be enabled
+
+        """
+
+        # unbind events:
+        self.Canvas.Unbind(FloatCanvas.EVT_MOTION)
+        self.Canvas.Unbind(FloatCanvas.EVT_LEFT_DOWN)
+        self.Canvas.Unbind(FloatCanvas.EVT_LEFT_UP)
+
+    def OnMove(self, event):
+        if self.Drawing:
+            x, y = self.StartPoint
+            Cornerx, Cornery = event.GetPosition()
+            w, h = ( Cornerx - x, Cornery - y)
+            if abs(w) > self.Tol and abs(h) > self.Tol:
+                # draw the RB box
+                dc = wx.ClientDC(self.Canvas)
+                dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
+                dc.SetBrush(wx.TRANSPARENT_BRUSH)
+                dc.SetLogicalFunction(wx.XOR)
+                if self.RBRect:
+                    dc.DrawRectangle(*self.RBRect)
+                self.RBRect = (x, y, w, h )
+                dc.DrawRectangle(*self.RBRect)
+        event.Skip() # skip so that other events can catch these
+
+    def OnLeftDown(self, event):
+        # Start drawing
+        self.Drawing = True
+        self.StartPoint = event.GetPosition()
+        self.StartPointWorld = event.Coords
+    
+    def OnLeftUp(self, event):
+        # Stop Drawing
+        if self.Drawing:
+            self.Drawing = False
+            if self.RBRect:
+                WH = event.Coords - self.StartPointWorld
+                wx.CallAfter(self.CallBack, (self.StartPointWorld, WH))
+        self.RBRect = None
+        self.StartPointWorld = None
diff --git a/wxPython/wx/lib/floatcanvas/Utilities/__init__.py b/wxPython/wx/lib/floatcanvas/Utilities/__init__.py
new file mode 100644 (file)
index 0000000..b3162b3
--- /dev/null
@@ -0,0 +1,7 @@
+"""
+__init__ for the floatcanvas Utilities package
+
+"""
+pass
+
+
index 1c7f9a6d3c5e7b673cf98c65a7c3c66d8d138d98..b03b8fd7c86dbe1a7f6f043ecdc76052d1cae5a8 100644 (file)
@@ -29,8 +29,7 @@ It is double buffered, so re-draws after the window is uncovered by
 something else are very quick.
 
 It relies on NumPy, which is needed for speed (maybe, I haven't profiled
-it). It will also use numarray, if you don't have Numeric, but it is
-slower.
+properly) and convenience. 
 
 Bugs and Limitations: Lots: patches, fixes welcome
 
@@ -60,12 +59,6 @@ If you are zoomed in, it checks the Bounding box of an object before
 drawing it. This makes it a great deal faster when there are a lot of
 objects and you are zoomed in so that only a few are shown.
 
-One solution is to be able to pass some sort of object set to the DC
-directly. I've used DC.DrawPointList(Points), and it helped a lot with
-drawing lots of points. However, when zoomed in, the Bounding boxes need
-to be checked, so I may some day write C++ code that does the loop and
-checks the BBs.
-
 Mouse Events:
 
 There are a full set of custom mouse events. They are just like the
@@ -80,19 +73,25 @@ clicked, mouse-over'd, etc.
 See the Demo for what it can do, and how to use it.
 
 Copyright: Christopher Barker
-
 License: Same as the version of wxPython you are using it with.
 
-Check for updates or answers to questions, send me an email.
+TRAC site for some docs and updates:
+http://morticia.cs.dal.ca/FloatCanvas/
 
-Please let me know if you're using this!!!
+SVN for latest code:
+svn://morticia.cs.dal.ca/FloatCanvas
 
+Mailing List:
+http://mail.mithis.com/cgi-bin/mailman/listinfo/floatcanvas
+
+Check for updates or answers to questions, send me an email.
+Please let me know if you're using this!!!
 Contact me at:
 
 Chris.Barker@noaa.gov
 
 """
 
-__version__ = "0.9.10"
+__version__ = "0.9.18"
 
 
index 576d0bcfbd9232f2ba76b354e3f3edd24611041c..72b1346d53988036b1be18603a6acb83b413d915 100644 (file)
@@ -95,7 +95,10 @@ class ImageView(wx.Window):
         if image is None:
             return
 
-        bmp = image.ConvertToBitmap()
+        try:
+            bmp = image.ConvertToBitmap()
+        except:
+            return
 
         iwidth = bmp.GetWidth()   # dimensions of image file
         iheight = bmp.GetHeight()
index 38137ea606232a267a9857a17019e7fd2c458ce7..8b4789ba87dfcfcfc03b288ff8d0423bc064b920 100644 (file)
@@ -221,6 +221,8 @@ class InspectionFrame(wx.Frame):
 
     def OnClose(self, evt):
         self.SaveSettings(self.config)
+        self.mgr.UnInit()
+        del self.mgr
         evt.Skip()
         
 
@@ -533,6 +535,9 @@ class InspectionInfoPanel(wx.stc.StyledTextCtrl):
 
 
     def FmtSizerItem(self, obj):
+        if obj is None:
+            return ['SizerItem: None']
+        
         st = ['SizerItem:']
         st.append(self.Fmt('proportion', obj.GetProportion()))
         st.append(self.Fmt('flag',
index 33ac41ad2dc09850c56b6614e2d2089a863346b6..1eba82de0c78469f759b91897d9b7ecfc8baccf6 100644 (file)
@@ -47,6 +47,30 @@ class MaskedComboBoxSelectEvent(wx.PyCommandEvent):
         this event was generated."""
         return self.__selection
 
+class MaskedComboBoxEventHandler(wx.EvtHandler):
+    """
+    This handler ensures that the derived control can react to events
+    from the base control before any external handlers run, to ensure 
+    proper behavior.
+    """
+    def __init__(self, combobox):
+        wx.EvtHandler.__init__(self)
+        self.combobox = combobox
+        combobox.PushEventHandler(self)
+        self.Bind(wx.EVT_SET_FOCUS, self.combobox._OnFocus )            ## defeat automatic full selection
+        self.Bind(wx.EVT_KILL_FOCUS, self.combobox._OnKillFocus )       ## run internal validator
+        self.Bind(wx.EVT_LEFT_DCLICK, self.combobox._OnDoubleClick)     ## select field under cursor on dclick
+        self.Bind(wx.EVT_RIGHT_UP, self.combobox._OnContextMenu )       ## bring up an appropriate context menu
+        self.Bind(wx.EVT_CHAR, self.combobox._OnChar )                  ## handle each keypress
+        self.Bind(wx.EVT_KEY_DOWN, self.combobox._OnKeyDownInComboBox ) ## for special processing of up/down keys
+        self.Bind(wx.EVT_KEY_DOWN, self.combobox._OnKeyDown )           ## for processing the rest of the control keys
+                                                                        ## (next in evt chain)
+        self.Bind(wx.EVT_COMBOBOX, self.combobox._OnDropdownSelect )    ## to bring otherwise completely independent base
+                                                                        ## ctrl selection into maskededit framework
+        self.Bind(wx.EVT_TEXT, self.combobox._OnTextChange )            ## color control appropriately & keep
+                                                                        ## track of previous value for undo
+
+
 
 class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
     """
@@ -152,18 +176,15 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         self._SetKeycodeHandler(wx.WXK_UP, self._OnSelectChoice)
         self._SetKeycodeHandler(wx.WXK_DOWN, self._OnSelectChoice)
 
+        self.replace_next_combobox_event = False
+        self.correct_selection = -1
+
         if setupEventHandling:
-            ## Setup event handlers
-            self.Bind(wx.EVT_SET_FOCUS, self._OnFocus )         ## defeat automatic full selection
-            self.Bind(wx.EVT_KILL_FOCUS, self._OnKillFocus )    ## run internal validator
-            self.Bind(wx.EVT_LEFT_DCLICK, self._OnDoubleClick)  ## select field under cursor on dclick
-            self.Bind(wx.EVT_RIGHT_UP, self._OnContextMenu )    ## bring up an appropriate context menu
-            self.Bind(wx.EVT_CHAR, self._OnChar )               ## handle each keypress
-            self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDownInComboBox ) ## for special processing of up/down keys
-            self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDown )        ## for processing the rest of the control keys
-                                                                ## (next in evt chain)
-            self.Bind(wx.EVT_TEXT, self._OnTextChange )         ## color control appropriately & keep
-                                                                ## track of previous value for undo
+            ## Setup event handling functions through event handler object,
+            ## to guarantee processing prior to giving event callbacks from
+            ## outside the class:
+            self.evt_handler = MaskedComboBoxEventHandler(self)
+            self.Bind(wx.EVT_WINDOW_DESTROY, self.OnWindowDestroy )
 
 
 
@@ -171,6 +192,13 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         return "<MaskedComboBox: %s>" % self.GetValue()
 
 
+    def OnWindowDestroy(self, event):
+        # clean up associated event handler object:
+        if self.RemoveEventHandler(self.evt_handler):
+            self.evt_handler.Destroy()
+        event.Skip()
+
+
     def _CalcSize(self, size=None):
         """
         Calculate automatic size if allowed; augment base mixin function
@@ -252,7 +280,9 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         # Record current selection and insertion point, for undo
         self._prevSelection = self._GetSelection()
         self._prevInsertionPoint = self._GetInsertionPoint()
+##        dbg('MaskedComboBox::_SetValue(%s), selection beforehand: %d' % (value, self.GetSelection()))
         wx.ComboBox.SetValue(self, value)
+##        dbg('MaskedComboBox::_SetValue(%s), selection now: %d' % (value, self.GetSelection()))
         # text change events don't always fire, so we check validity here
         # to make certain formatting is applied:
         self._CheckValid()
@@ -264,11 +294,14 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         masked control.  NOTE: this must be done in the class derived
         from the base wx control.
         """
+##        dbg('MaskedComboBox::SetValue(%s)' % value, indent=1)
         if not self._mask:
             wx.ComboBox.SetValue(value)   # revert to base control behavior
+##            dbg('no mask; deferring to base class', indent=0)
             return
         # else...
         # empty previous contents, replacing entire value:
+##        dbg('MaskedComboBox::SetValue: selection beforehand: %d' % (self.GetSelection()))
         self._SetInsertionPoint(0)
         self._SetSelection(0, self._masklength)
 
@@ -306,15 +339,26 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
                 dateparts = value.split(' ')
                 dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
                 value = string.join(dateparts, ' ')
-##                dbg('adjusted value: "%s"' % value)
                 value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
             else:
                 raise
+##        dbg('adjusted value: "%s"' % value)
 
-        self._SetValue(value)
-####        dbg('queuing insertion after .SetValue', replace_to)
-        wx.CallAfter(self._SetInsertionPoint, replace_to)
-        wx.CallAfter(self._SetSelection, replace_to, replace_to)
+        # Attempt to compensate for fact that calling .SetInsertionPoint() makes the
+        # selection index -1, even if the resulting set value is in the list.  
+        # So, if we are setting a value that's in the list, use index selection instead.
+        if value in self._choices:
+            index = self._choices.index(value)
+            self._prevValue = self._curValue
+            self._curValue = self._choices[index]
+            self._ctrl_constraints._autoCompleteIndex = index
+            self.SetSelection(index)
+        else:
+            self._SetValue(value)
+####            dbg('queuing insertion after .SetValue', replace_to)
+            wx.CallAfter(self._SetInsertionPoint, replace_to)
+            wx.CallAfter(self._SetSelection, replace_to, replace_to)
+##        dbg(indent=0)
 
 
     def _Refresh(self):
@@ -509,26 +553,60 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         Necessary override for bookkeeping on choice selection, to keep current value
         current.
         """
-##        dbg('MaskedComboBox::SetSelection(%d)' % index)
+##        dbg('MaskedComboBox::SetSelection(%d)' % index, indent=1)
         if self._mask:
             self._prevValue = self._curValue
-            self._curValue = self._choices[index]
             self._ctrl_constraints._autoCompleteIndex = index
+            if index != -1:
+                self._curValue = self._choices[index]
+            else:
+                self._curValue = None
         wx.ComboBox.SetSelection(self, index)
+##        dbg('selection now: %d' % self.GetCurrentSelection(), indent=0)
 
 
     def _OnKeyDownInComboBox(self, event):
         """
-        This function is necessary because navigation and control key
-        events do not seem to normally be seen by the wxComboBox's
-        EVT_CHAR routine.  (Tabs don't seem to be visible no matter
-        what... {:-( )
+        This function is necessary because navigation and control key events
+        do not seem to normally be seen by the wxComboBox's EVT_CHAR routine.
+        (Tabs don't seem to be visible no matter what, except for CB_READONLY
+        controls, for some bizarre reason... {:-( )
         """
+        key = event.GetKeyCode()
+##        dbg('MaskedComboBox::OnKeyDownInComboBox(%d)' % key)
         if event.GetKeyCode() in self._nav + self._control:
-            self._OnChar(event)
-            return
+            if not self._IsEditable():
+                # WANTS_CHARS with CB_READONLY apparently prevents navigation on WXK_TAB;
+                # ensure we can still navigate properly, as maskededit mixin::OnChar assumes
+                # that event.Skip() will just work, but it doesn't:
+                if self._keyhandlers.has_key(key):
+                    self._keyhandlers[key](event)
+                # else pass
+            else:
+##                dbg('calling OnChar()')
+                self._OnChar(event)
         else:
             event.Skip()    # let mixin default KeyDown behavior occur
+##        dbg(indent=0)
+
+
+    def _OnDropdownSelect(self, event):
+        """
+        This function appears to be necessary because dropdown selection seems to
+        manipulate the contents of the control in an inconsistent way, properly
+        changing the selection index, but *not* the value. (!)  Calling SetSelection()
+        on a selection event for the same selection would seem like a nop, but it seems to
+        fix the problem.
+        """
+##        dbg('MaskedComboBox::OnDropdownSelect(%d)' % event.GetSelection(), indent=1)
+        if self.replace_next_combobox_event:
+##            dbg('replacing EVT_COMBOBOX')
+            self.replace_next_combobox_event = False
+            self._OnAutoSelect(self._ctrl_constraints, self.correct_selection)
+        else:
+##            dbg('skipping EVT_COMBOBOX')
+            event.Skip()
+##        dbg(indent=0)
 
 
     def _OnSelectChoice(self, event):
@@ -585,7 +663,7 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         Override mixin (empty) autocomplete handler, so that autocompletion causes
         combobox to update appropriately.
         """
-##        dbg('MaskedComboBox::OnAutoSelect', field._index, indent=1)
+##        dbg('MaskedComboBox::OnAutoSelect(%d, %d)' % (field._index, match_index), indent=1)
 ##        field._autoCompleteIndex = match_index
         if field == self._ctrl_constraints:
             self.SetSelection(match_index)
@@ -594,7 +672,7 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
                 MaskedComboBoxSelectEvent( self.GetId(), match_index, self ) )
         self._CheckValid()
 ##        dbg('field._autoCompleteIndex:', match_index)
-##        dbg('self.GetSelection():', self.GetSelection())
+##        dbg('self.GetCurrentSelection():', self.GetCurrentSelection())
         end = self._goEnd(getPosOnly=True)
 ##        dbg('scheduling set of end position to:', end)
         # work around bug in wx 2.5
@@ -614,15 +692,24 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         item in the list. (and then does the usual OnReturn bit.)
         """
 ##        dbg('MaskedComboBox::OnReturn', indent=1)
-##        dbg('current value: "%s"' % self.GetValue(), 'current index:', self.GetSelection())
-        if self.GetSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
-            wx.CallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
-
+##        dbg('current value: "%s"' % self.GetValue(), 'current selection:', self.GetCurrentSelection())
+        if self.GetCurrentSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
+##            dbg('attempting to correct the selection to make it %d' % self._ctrl_constraints._autoCompleteIndex)
+##            wx.CallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
+            self.replace_next_combobox_event = True
+            self.correct_selection = self._ctrl_constraints._autoCompleteIndex
         event.m_keyCode = wx.WXK_TAB
         event.Skip()
 ##        dbg(indent=0)
 
 
+    def _LostFocus(self):
+##        dbg('MaskedComboBox::LostFocus; Selection=%d, value="%s"' % (self.GetSelection(), self.GetValue()))
+        if self.GetCurrentSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
+##            dbg('attempting to correct the selection to make it %d' % self._ctrl_constraints._autoCompleteIndex)
+            wx.CallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
+
+
 class ComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ):
     """
     The "user-visible" masked combobox control, this class is
@@ -659,6 +746,19 @@ class PreMaskedComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ):
 __i = 0
 ## CHANGELOG:
 ## ====================
+##  Version 1.4
+##  1. Added handler for EVT_COMBOBOX to address apparently inconsistent behavior
+##     of control when the dropdown control is used to do a selection.
+##     NOTE: due to misbehavior of wx.ComboBox re: losing all concept of the 
+##      current selection index if SetInsertionPoint() is called, which is required
+##      to support masked .SetValue(), this control is flaky about retaining selection
+##      information.  I can't truly fix this without major changes to the base control,
+##      but I've tried to compensate as best I can.
+##     TODO: investigate replacing base control with ComboCtrl instead...
+##  2. Fixed navigation in readonly masked combobox, which was not working because
+##      the base control doesn't do navigation if style=CB_READONLY|WANTS_CHARS.
+##
+##
 ##  Version 1.3
 ##  1. Made definition of "hack" GetMark conditional on base class not
 ##     implementing it properly, to allow for migration in wx code base
index b5696c7e1abcdbdc426e928a83fa2aa6712305da..b2b77840e33617c7481acc71a0542bd0839e6f91 100644 (file)
@@ -1,12 +1,12 @@
 #----------------------------------------------------------------------------
 # Name:         maskededit.py
-# Authors:      Jeff Childers, Will Sadkin
-# Email:        jchilders_98@yahoo.com, wsadkin@parlancecorp.com
+# Authors:      Will Sadkin, Jeff Childers
+# Email:        wsadkin@parlancecorp.com, jchilders_98@yahoo.com
 # Created:      02/11/2003
 # Copyright:    (c) 2003 by Jeff Childers, Will Sadkin, 2003
-# Portions:     (c) 2002 by Will Sadkin, 2002-2006
+# Portions:     (c) 2002 by Will Sadkin, 2002-2007
 # RCS-ID:       $Id$
-# License:      wxWindows license
+# License:      wxWidgets license
 #----------------------------------------------------------------------------
 # NOTE:
 #   MaskedEdit controls are based on a suggestion made on [wxPython-Users] by
@@ -27,7 +27,7 @@
 #
 #----------------------------------------------------------------------------
 #
-# 03/30/2004 - Will Sadkin (wsadkin@nameconnector.com)
+# 03/30/2004 - Will Sadkin (wsadkin@parlancecorp.com)
 #
 # o Split out TextCtrl, ComboBox and IpAddrCtrl into their own files,
 # o Reorganized code into masked package
@@ -337,7 +337,18 @@ to individual fields:
         raiseOnInvalidPaste    False by default; normally a bad paste simply is ignored with a bell;
                                if True, this will cause a ValueError exception to be thrown,
                                with the .value attribute of the exception containing the bad value.
-        =====================  ==================================================================
+
+        stopFieldChangeIfInvalid
+                               False by default; tries to prevent navigation out of a field if its
+                               current value is invalid.  Can be used to create a hybrid of validation
+                               settings, allowing intermediate invalid values in a field without
+                               sacrificing ability to limit values as with validRequired.
+                               NOTE: It is possible to end up with an invalid value when using
+                                 this option if focus is switched to some other control via mousing.
+                                 To avoid this, consider deriving a class that defines _LostFocus()
+                                 function that returns the control to a valid value when the focus
+                                 shifts.  (AFAICT, The change in focus is unpreventable.)
+        =====================  =================================================================
 
 
 Coloring Behavior
@@ -1327,12 +1338,14 @@ class Field:
               'emptyInvalid':  False,           ## Set to True to make EMPTY = INVALID
               'description': "",                ## primarily for autoformats, but could be useful elsewhere
               'raiseOnInvalidPaste': False,     ## if True, paste into field will cause ValueError
+              'stopFieldChangeIfInvalid': False,## if True, disallow field navigation out of invalid field
               }
 
     # This list contains all parameters that when set at the control level should
     # propagate down to each field:
     propagating_params = ('fillChar', 'groupChar', 'decimalChar','useParensForNegatives',
-                          'compareNoCase', 'emptyInvalid', 'validRequired', 'raiseOnInvalidPaste')
+                          'compareNoCase', 'emptyInvalid', 'validRequired', 'raiseOnInvalidPaste',
+                          'stopFieldChangeIfInvalid')
 
     def __init__(self, **kwargs):
         """
@@ -3021,7 +3034,7 @@ class MaskedEditMixin:
                     char = char.decode(self._defaultEncoding)
                 else:
                     char = unichr(event.GetUnicodeKey())
-                    dbg('unicode char:', char)
+##                    dbg('unicode char:', char)
                 excludes = u''
                 if type(field._excludeChars) != types.UnicodeType:
                     excludes += field._excludeChars.decode(self._defaultEncoding)
@@ -3767,6 +3780,21 @@ class MaskedEditMixin:
 ##            dbg(indent=0)
             return False
 
+        field = self._FindField(sel_to)
+        index = field._index
+        field_start, field_end = field._extent
+        slice = self._GetValue()[field_start:field_end]
+
+##        dbg('field._stopFieldChangeIfInvalid?', field._stopFieldChangeIfInvalid)
+##        dbg('field.IsValid(slice)?', field.IsValid(slice))
+        
+        if field._stopFieldChangeIfInvalid and not field.IsValid(slice):
+##            dbg('field invalid; field change disallowed')
+            if not wx.Validator_IsSilent():
+                wx.Bell()
+##            dbg(indent=0)
+            return False
+
 
         if event.ShiftDown():
 
@@ -3775,13 +3803,12 @@ class MaskedEditMixin:
             # NOTE: doesn't yet work with SHIFT-tab under wx; the control
             # never sees this event! (But I've coded for it should it ever work,
             # and it *does* work for '.' in IpAddrCtrl.)
-            field = self._FindField(pos)
-            index = field._index
-            field_start = field._extent[0]
+
             if pos < field_start:
 ##                dbg('cursor before 1st field; cannot change to a previous field')
                 if not wx.Validator_IsSilent():
                     wx.Bell()
+##                dbg(indent=0)
                 return False
 
             if event.ControlDown():
@@ -3821,8 +3848,6 @@ class MaskedEditMixin:
 
         else:
             # "Go forward"
-            field = self._FindField(sel_to)
-            field_start, field_end = field._extent
             if event.ControlDown():
 ##                dbg('queuing select to end of field:', pos, field_end)
                 wx.CallAfter(self._SetInsertionPoint, pos)
@@ -3888,10 +3913,19 @@ class MaskedEditMixin:
                             wx.CallAfter(self._SetInsertionPoint, next_pos)
 ##                        dbg(indent=0)
                         return False
+##                dbg(indent=0)
 
 
     def _OnDecimalPoint(self, event):
 ##        dbg('MaskedEditMixin::_OnDecimalPoint', indent=1)
+        field = self._FindField(self._GetInsertionPoint())
+        start, end = field._extent
+        slice = self._GetValue()[start:end]
+        
+        if field._stopFieldChangeIfInvalid and not field.IsValid(slice):
+            if not wx.Validator_IsSilent():
+                wx.Bell()
+            return False
 
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
 
@@ -4021,7 +4055,7 @@ class MaskedEditMixin:
 
     def _findNextEntry(self,pos, adjustInsert=True):
         """ Find the insertion point for the next valid entry character position."""
-##        dbg('MaskedEditMixin::_findNextEntry', indent=1)        
+##        dbg('MaskedEditMixin::_findNextEntry', indent=1)
         if self._isTemplateChar(pos) or pos in self._explicit_field_boundaries:   # if changing fields, pay attn to flag
             adjustInsert = adjustInsert
         else:                           # else within a field; flag not relevant
@@ -4280,7 +4314,9 @@ class MaskedEditMixin:
 ####                    dbg('field_len?', field_len)
 ####                    dbg('pos==end; len (slice) < field_len?', len(slice) < field_len)
 ####                    dbg('not field._moveOnFieldFull?', not field._moveOnFieldFull)
-                    if len(slice) == field_len and field._moveOnFieldFull:
+                    if( len(slice) == field_len and field._moveOnFieldFull
+                        and (not field._stopFieldChangeIfInvalid or
+                             field._stopFieldChangeIfInvalid and field.IsValid(slice))):
                         # move cursor to next field:
                         pos = self._findNextEntry(pos)
                         self._SetInsertionPoint(pos)
@@ -4317,11 +4353,14 @@ class MaskedEditMixin:
             # else make sure the user is not trying to type over a template character
             # If they are, move them to the next valid entry position
             elif self._isTemplateChar(pos):
-                if( not field._moveOnFieldFull
-                      and (not self._signOk
-                           or (self._signOk
-                               and field._index == 0
-                               and pos > 0) ) ):      # don't move to next field without explicit cursor movement
+                if( (not field._moveOnFieldFull
+                     and (not self._signOk
+                          or (self._signOk and field._index == 0 and pos > 0) ) )
+                    
+                    or (field._stopFieldChangeIfInvalid
+                        and not field.IsValid(self._GetValue()[start:end]) ) ):
+                    
+                    # don't move to next field without explicit cursor movement
                     pass
                 else:
                     # find next valid position
@@ -5092,7 +5131,11 @@ class MaskedEditMixin:
 ####            dbg('field._moveOnFieldFull?', field._moveOnFieldFull)
 ####            dbg('len(fstr.lstrip()) == end-start?', len(fstr.lstrip()) == end-start)
             if( field._moveOnFieldFull and pos == end
-                and len(fstr.lstrip()) == end-start):   # if field now full
+                and len(fstr.lstrip()) == end-start     # if field now full
+                and (not field._stopFieldChangeIfInvalid     # and we either don't care about valid
+                     or (field._stopFieldChangeIfInvalid     # or we do and the current field value is valid
+                         and field.IsValid(fstr)))):
+
                 newpos = self._findNextEntry(end)       #   go to next field
             else:
                 newpos = pos                            # else keep cursor at current position
@@ -5165,7 +5208,11 @@ class MaskedEditMixin:
 
             if( field._insertRight                                  # if insert-right field (but we didn't start at right edge)
                 and field._moveOnFieldFull                          # and should move cursor when full
-                and len(newtext[start:end].strip()) == end-start):  # and field now full
+                and len(newtext[start:end].strip()) == end-start    # and field now full
+                and (not field._stopFieldChangeIfInvalid            # and we either don't care about valid
+                     or (field._stopFieldChangeIfInvalid            # or we do and the current field value is valid
+                         and field.IsValid(newtext[start:end].strip())))):
+
                 newpos = self._findNextEntry(end)                   #   go to next field
 ##                dbg('newpos = nextentry =', newpos)
             else:
@@ -6723,6 +6770,12 @@ __i=0
 
 ## CHANGELOG:
 ## ====================
+##  Version 1.13
+##  1. Added parameter option stopFieldChangeIfInvalid, which can be used to relax the 
+##     validation rules for a control, but make best efforts to stop navigation out of 
+##     that field should its current value be invalid.  Note: this does not prevent the 
+##     value from remaining invalid if focus for the control is lost, via mousing etc.
+##
 ##  Version 1.12
 ##  1. Added proper support for NUMPAD keypad keycodes for navigation and control.
 ##
index 73a837039bce3f760163faa84d76f56d5a26b02d..dfcebc9065bf1afaf17d0b68ab5511153f497ede 100644 (file)
@@ -2,7 +2,7 @@
 # Name:         wxPython.lib.masked.numctrl.py
 # Author:       Will Sadkin
 # Created:      09/06/2003
-# Copyright:   (c) 2003 by Will Sadkin
+# Copyright:   (c) 2003-2007 by Will Sadkin
 # RCS-ID:      $Id$
 # License:     wxWidgets license
 #----------------------------------------------------------------------------
@@ -81,6 +81,7 @@ masked.NumCtrl:
              min = None,
              max = None,
              limited = False,
+             limitOnFieldChange = False,
              selectOnEntry = True,
              foregroundColour = "Black",
              signedForegroundColour = "Red",
@@ -155,6 +156,12 @@ masked.NumCtrl:
         If False and bounds are set, out-of-bounds values will
         result in a background colored with the current invalidBackgroundColour.
 
+  limitOnFieldChange
+        An alternative to limited, this boolean indicates whether or not a
+        field change should be allowed if the value in the control
+        is out of bounds.  If True, and control focus is lost, this will also
+        cause the control to take on the nearest bound value.
+
   selectOnEntry
         Boolean indicating whether or not the value in each field of the
         control should be automatically selected (for replacement) when
@@ -312,6 +319,18 @@ IsLimited()
     Returns <I>True</I> if the control is currently limiting the
     value to fall within the current bounds.
 
+SetLimitOnFieldChange()
+    If called with a value of True, will cause the control to allow
+    out-of-bounds values, but will prevent field change if attempted
+    via navigation, and if the control loses focus, it will change
+    the value to the nearest bound.
+    
+GetLimitOnFieldChange()
+
+IsLimitedOnFieldChange()
+    Returns <I>True</I> if the control is currently limiting the
+    value on field change.
+
 
 SetAllowNone(bool)
     If called with a value of True, this function will cause the control
@@ -390,7 +409,7 @@ MININT = -maxint-1
 
 from wx.tools.dbg import Logger
 from wx.lib.masked import MaskedEditMixin, Field, BaseMaskedTextCtrl
-dbg = Logger()
+##dbg = Logger()
 ##dbg(enable=1)
 
 #----------------------------------------------------------------------------
@@ -442,6 +461,7 @@ class NumCtrlAccessorsMixin:
          'emptyInvalid',
          'validFunc',
          'validRequired',
+         'stopFieldChangeIfInvalid',
         )
     for param in exposed_basectrl_params:
         propname = param[0].upper() + param[1:]
@@ -478,6 +498,7 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
         'min': None,                        # by default, no bounds set
         'max': None,
         'limited': False,                   # by default, no limiting even if bounds set
+        'limitOnFieldChange': False,        # by default, don't limit if changing fields, even if bounds set
         'allowNone': False,                 # by default, don't allow empty value
         'selectOnEntry': True,              # by default, select the value of each field on entry
         'foregroundColour': "Black",
@@ -759,6 +780,12 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
                 maskededit_kwargs['validRequired'] = False
             self._limited = kwargs['limited']
 
+        if kwargs.has_key('limitOnFieldChange'):
+            if kwargs['limitOnFieldChange'] and not self._limitOnFieldChange:
+                maskededit_kwargs['stopFieldChangeIfInvalid'] = True
+            elif kwargs['limitOnFieldChange'] and self._limitOnFieldChange:
+                maskededit_kwargs['stopFieldChangeIfInvalid'] = False
+
 ##        dbg('maskededit_kwargs:', maskededit_kwargs)
         if maskededit_kwargs.keys():
             self.SetCtrlParameters(**maskededit_kwargs)
@@ -923,6 +950,43 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
         wx.CallAfter(self.SetInsertionPoint, sel_start)      # preserve current selection/position
         wx.CallAfter(self.SetSelection, sel_start, sel_to)
 
+
+    def _OnChangeField(self, event):
+        """
+        This routine enhances the base masked control _OnFieldChange().  It's job
+        is to ensure limits are imposed if limitOnFieldChange is enabled.
+        """
+##        dbg('NumCtrl::_OnFieldChange', indent=1)
+        if self._limitOnFieldChange and not (self._min <= self.GetValue() <= self._max):
+            self._disallowValue()
+##            dbg('oob - field change disallowed',indent=0)
+            return False
+        else:
+##            dbg(indent=0)
+            return MaskedEditMixin._OnChangeField(self, event)     # call the baseclass function
+
+
+    def _LostFocus(self):
+        """
+        On loss of focus, if limitOnFieldChange is set, ensure value conforms to limits.
+        """
+##        dbg('NumCtrl::_LostFocus', indent=1)
+        if self._limitOnFieldChange:
+##            dbg("limiting on loss of focus")
+            value = self.GetValue()
+            if self._min is not None and value < self._min:
+##                dbg('Set to min value:', self._min)
+                self._SetValue(self._toGUI(self._min))
+
+            elif self._max is not None and value > self._max:
+##                dbg('Setting to max value:', self._max)
+                self._SetValue(self._toGUI(self._max))
+            # (else do nothing.)
+        # (else do nothing.)
+##        dbg(indent=0)
+        return True
+
+
     def _SetValue(self, value):
         """
         This routine supersedes the base masked control _SetValue().  It is
@@ -1346,7 +1410,32 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
 
     def GetLimited(self):
         """ (For regularization of property accessors) """
-        return self.IsLimited
+        return self.IsLimited()
+
+    def SetLimitOnFieldChange(self, limit):
+        """
+        If called with a value of True, this function will cause the control
+        to prevent navigation out of the current field if its value is out-of-bounds,
+        and limit the value to fall within the bounds currently specified if the
+        control loses focus.
+
+        If called with a value of False, this function will disable value
+        limiting, but coloring of out-of-bounds values will still take
+        place if bounds have been set for the control.
+        """
+        self.SetParameters(limitOnFieldChange = limit)
+
+
+    def IsLimitedOnFieldChange(self):
+        """
+        Returns True if the control is currently limiting the
+        value to fall within the current bounds.
+        """
+        return self._limitOnFieldChange
+
+    def GetLimitOnFieldChange(self):
+        """ (For regularization of property accessors) """
+        return self.IsLimitedOnFieldChange()
 
 
     def IsInBounds(self, value=None):
@@ -1794,6 +1883,13 @@ __i=0
 ##   1. Add support for printf-style format specification.
 ##   2. Add option for repositioning on 'illegal' insertion point.
 ##
+## Version 1.4
+##   1. In response to user request, added limitOnFieldChange feature, so that
+##      out-of-bounds values can be temporarily added to the control, but should
+##      navigation be attempted out of an invalid field, it will not navigate,
+##      and if focus is lost on a control so limited with an invalid value, it
+##      will change the value to the nearest bound.
+##
 ## Version 1.3
 ##   1. fixed to allow space for a group char.
 ##
index d374d7b247ace6ee85621d6cfc81ca5d26276919..a2953913fc11f9c9a0f97909266394bc6cf15f59 100644 (file)
@@ -157,13 +157,25 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         Allow mixin to set the raw value of the control with this function.
         REQUIRED by any class derived from MaskedEditMixin.
         """
-##        dbg('MaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1)
+##        dbg('MaskedTextCtrl::_SetValue("%(value)s", use_change_value=%(use_change_value)d)' % locals(), indent=1)
         # Record current selection and insertion point, for undo
         self._prevSelection = self._GetSelection()
         self._prevInsertionPoint = self._GetInsertionPoint()
         wx.TextCtrl.SetValue(self, value)
 ##        dbg(indent=0)
 
+    def _ChangeValue(self, value):
+        """
+        Allow mixin to set the raw value of the control with this function without
+        generating an event as a result. (New for masked.TextCtrl as of 2.8.4)
+        """
+##        dbg('MaskedTextCtrl::_ChangeValue("%(value)s", use_change_value=%(use_change_value)d)' % locals(), indent=1)
+        # Record current selection and insertion point, for undo
+        self._prevSelection = self._GetSelection()
+        self._prevInsertionPoint = self._GetInsertionPoint()
+        wx.TextCtrl.ChangeValue(self, value)
+##        dbg(indent=0)
+
     def SetValue(self, value):
         """
         This function redefines the externally accessible .SetValue() to be
@@ -171,10 +183,27 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         masked control.  NOTE: this must be done in the class derived
         from the base wx control.
         """
-##        dbg('MaskedTextCtrl::SetValue = "%s"' % value, indent=1)
+        self.ModifyValue(value, use_change_value=False)
+
+    def ChangeValue(self, value):
+        """
+        Provided to accomodate similar functionality added to base control in wxPython 2.7.1.1.
+        """
+        self.ModifyValue(value, use_change_value=True)
+
+
+    def ModifyValue(self, value, use_change_value=False):
+        """
+        This factored function of common code does the bulk of the work for SetValue 
+        and ChangeValue.
+        """
+##        dbg('MaskedTextCtrl::ModifyValue("%(value)s", use_change_value=%(use_change_value)d)' % locals(), indent=1)
 
         if not self._mask:
-            wx.TextCtrl.SetValue(self, value)    # revert to base control behavior
+            if use_change_value:
+                wx.TextCtrl.ChangeValue(self, value)    # revert to base control behavior
+            else:
+                wx.TextCtrl.SetValue(self, value)    # revert to base control behavior
             return
 
         # empty previous contents, replacing entire value:
@@ -198,7 +227,7 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
                 value = value[1:]
 ##            dbg('padded value = "%s"' % value)
 
-        # make SetValue behave the same as if you had typed the value in:
+        # make Set/ChangeValue behave the same as if you had typed the value in:
         try:
             value, replace_to = self._Paste(value, raise_on_invalid=True, just_return_value=True)
             if self._isFloat:
@@ -220,10 +249,12 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
             else:
 ##                dbg('exception thrown', indent=0)
                 raise
-
-        self._SetValue(value)   # note: to preserve similar capability, .SetValue()
-                                # does not change IsModified()
-####        dbg('queuing insertion after .SetValue', replace_to)
+        if use_change_value:
+            self._ChangeValue(value)
+        else:
+            self._SetValue(value)       # note: to preserve similar capability, .SetValue()
+                                        # does not change IsModified()
+####        dbg('queuing insertion after ._Set/ChangeValue', replace_to)
         # set selection to last char replaced by paste
         wx.CallAfter(self._SetInsertionPoint, replace_to)
         wx.CallAfter(self._SetSelection, replace_to, replace_to)
@@ -372,6 +403,10 @@ class PreMaskedTextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
 __i=0
 ## CHANGELOG:
 ## ====================
+##  Version 1.3
+##  - Added support for ChangeValue() function, similar to that of the base
+##    control, added in wxPython 2.7.1.1.
+##
 ##  Version 1.2
 ##  - Converted docstrings to reST format, added doc for ePyDoc.
 ##    removed debugging override functions.
index 41ad9fb8c661b7529e417331a6416ae9869a93c1..5df6a37d03559a2790f2288d286080701702908e 100644 (file)
@@ -1,4 +1,4 @@
-'''
+"""
 treemixin.py 
 
 This module provides three mixin classes that can be used with tree
@@ -18,7 +18,7 @@ controls:
   all items in the tree to restore it later.
 
 All mixin classes work with wx.TreeCtrl, wx.gizmos.TreeListCtrl, 
-and wx.lib.customtree.CustomTreeCtrl. They can be used together or 
+and wx.lib.customtreectrl.CustomTreeCtrl. They can be used together or 
 separately.
 
 The VirtualTree and DragAndDrop mixins force the wx.TR_HIDE_ROOT style.
@@ -30,15 +30,15 @@ Date: 15 April 2007
 
 ExpansionState is based on code and ideas from Karsten Hilbert.
 Andrea Gavana provided help with the CustomTreeCtrl integration.
-'''
+"""
 
 
-import wx, wx.lib.customtreectrl
+import wx
 
 
 class TreeAPIHarmonizer(object):
-    ''' This class attempts to hide the differences in API between the
-    different tree controls that are part of wxPython. '''
+    """ This class attempts to hide the differences in API between the
+    different tree controls that are part of wxPython. """
 
     def __callSuper(self, methodName, default, *args, **kwargs):
         # If our super class has a method called methodName, call it,
@@ -165,12 +165,12 @@ class TreeAPIHarmonizer(object):
                                                              **kwargs)
 
     def HitTest(self, *args, **kwargs):
-        ''' HitTest returns a two-tuple (item, flags) for tree controls
+        """ HitTest returns a two-tuple (item, flags) for tree controls
         without columns and a three-tuple (item, flags, column) for tree
         controls with columns. Our caller can indicate this method to
         always return a three-tuple no matter what tree control we're mixed
         in with by specifying the optional argument 'alwaysReturnColumn'
-        to be True. '''
+        to be True. """
         alwaysReturnColumn = kwargs.pop('alwaysReturnColumn', False)
         hitTestResult = super(TreeAPIHarmonizer, self).HitTest(*args, **kwargs)
         if len(hitTestResult) == 2 and alwaysReturnColumn:
@@ -209,11 +209,11 @@ class TreeAPIHarmonizer(object):
 
 
 class TreeHelper(object):
-    ''' This class provides methods that are not part of the API of any 
-    tree control, but are convenient to have available. '''
+    """ This class provides methods that are not part of the API of any 
+    tree control, but are convenient to have available. """
 
     def GetItemChildren(self, item=None, recursively=False):
-        ''' Return the children of item as a list. '''
+        """ Return the children of item as a list. """
         if not item:
             item = self.GetRootItem()
             if not item:
@@ -228,7 +228,7 @@ class TreeHelper(object):
         return children
 
     def GetIndexOfItem(self, item):
-        ''' Return the index of item. '''
+        """ Return the index of item. """
         parent = self.GetItemParent(item)
         if parent:
             parentIndices = self.GetIndexOfItem(parent)
@@ -238,7 +238,7 @@ class TreeHelper(object):
             return ()
 
     def GetItemByIndex(self, index):
-        ''' Return the item specified by index. '''
+        """ Return the item specified by index. """
         item = self.GetRootItem()
         for i in index:
             children = self.GetItemChildren(item)
@@ -247,7 +247,7 @@ class TreeHelper(object):
 
 
 class VirtualTree(TreeAPIHarmonizer, TreeHelper):
-    ''' This is a mixin class that can be used to allow for virtual tree
+    """ This is a mixin class that can be used to allow for virtual tree
     controls. It can be mixed in with wx.TreeCtrl, wx.gizmos.TreeListCtrl, 
     wx.lib.customtree.CustomTreeCtrl.
 
@@ -279,7 +279,7 @@ class VirtualTree(TreeAPIHarmonizer, TreeHelper):
     the fourth one. A tuple with two integers, e.g. (3,0), represents a 
     child of a visible root item, in this case the first child of the 
     fourth root item. 
-    '''
+    """
 
     def __init__(self, *args, **kwargs):
         kwargs['style'] = kwargs.get('style', wx.TR_DEFAULT_STYLE) | \
@@ -289,69 +289,69 @@ class VirtualTree(TreeAPIHarmonizer, TreeHelper):
         self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed)
 
     def OnGetChildrenCount(self, index):
-        ''' This function *must* be overloaded in the derived class.
+        """ This function *must* be overloaded in the derived class.
         It should return the number of child items of the item with the 
         provided index. If index == () it should return the number of 
-        root items. '''
+        root items. """
         raise NotImplementedError
 
     def OnGetItemText(self, index, column=0):
-        ''' This function *must* be overloaded in the derived class. It 
+        """ This function *must* be overloaded in the derived class. It 
         should return the string containing the text of the specified
-        item. '''
+        item. """
         raise NotImplementedError
 
     def OnGetItemFont(self, index):
-        ''' This function may be overloaded in the derived class. It 
-        should return the wx.Font to be used for the specified item. '''
+        """ This function may be overloaded in the derived class. It 
+        should return the wx.Font to be used for the specified item. """
         return wx.NullFont 
 
     def OnGetItemTextColour(self, index):
-        ''' This function may be overloaded in the derived class. It 
+        """ This function may be overloaded in the derived class. It 
         should return the wx.Colour to be used as text colour for the 
-        specified item. '''
+        specified item. """
         return wx.NullColour
 
     def OnGetItemBackgroundColour(self, index):
-        ''' This function may be overloaded in the derived class. It 
+        """ This function may be overloaded in the derived class. It 
         should return the wx.Colour to be used as background colour for 
-        the specified item. '''
+        the specified item. """
         return wx.NullColour
 
     def OnGetItemImage(self, index, which=wx.TreeItemIcon_Normal, column=0):
-        ''' This function may be overloaded in the derived class. It 
+        """ This function may be overloaded in the derived class. It 
         should return the index of the image to be used. Don't forget
-        to associate an ImageList with the tree control. '''
+        to associate an ImageList with the tree control. """
         return -1
 
     def OnGetItemType(self, index):
-        ''' This function may be overloaded in the derived class, but
+        """ This function may be overloaded in the derived class, but
         that only makes sense when this class is mixed in with a tree 
         control that supports checkable items, i.e. CustomTreeCtrl. 
         This method should return whether the item is to be normal (0,
         the default), a checkbox (1) or a radiobutton (2). 
         Note that OnGetItemChecked needs to be implemented as well; it
-        should return whether the item is actually checked. '''
+        should return whether the item is actually checked. """
         return 0 
 
     def OnGetItemChecked(self, index):
-        ''' This function may be overloaded in the derived class, but
+        """ This function may be overloaded in the derived class, but
         that only makes sense when this class is mixed in with a tree 
         control that supports checkable items, i.e. CustomTreeCtrl. 
         This method should return whether the item is to be checked. 
         Note that OnGetItemType should return 1 (checkbox) or 2
-        (radiobutton) for this item. '''
+        (radiobutton) for this item. """
         return False 
 
     def RefreshItems(self):
-        ''' Redraws all visible items. '''
+        """ Redraws all visible items. """
         rootItem = self.GetRootItem()
         if not rootItem:
             rootItem = self.AddRoot('Hidden root')
         self.RefreshChildrenRecursively(rootItem)
 
     def RefreshItem(self, index):
-        ''' Redraws the item with the specified index. '''
+        """ Redraws the item with the specified index. """
         try:
             item = self.GetItemByIndex(index)
         except IndexError:
@@ -362,8 +362,8 @@ class VirtualTree(TreeAPIHarmonizer, TreeHelper):
         self.DoRefreshItem(item, index, hasChildren)
 
     def RefreshChildrenRecursively(self, item, itemIndex=None):
-        ''' Refresh the children of item, reusing as much of the
-        existing items in the tree as possible. '''
+        """ Refresh the children of item, reusing as much of the
+        existing items in the tree as possible. """
         if itemIndex is None:
             itemIndex = self.GetIndexOfItem(item)
         reusableChildren = self.GetItemChildren(item)
@@ -377,7 +377,7 @@ class VirtualTree(TreeAPIHarmonizer, TreeHelper):
             self.Delete(child)
 
     def RefreshItemRecursively(self, item, itemIndex):
-        ''' Refresh the item and its children recursively. '''
+        """ Refresh the item and its children recursively. """
         hasChildren = bool(self.OnGetChildrenCount(itemIndex))
         item = self.DoRefreshItem(item, itemIndex, hasChildren)
         # We need to refresh the children when the item is expanded and
@@ -388,7 +388,7 @@ class VirtualTree(TreeAPIHarmonizer, TreeHelper):
         self.SetItemHasChildren(item, hasChildren)
  
     def DoRefreshItem(self, item, index, hasChildren):
-        ''' Refresh one item. '''
+        """ Refresh one item. """
         item = self.RefreshItemType(item, index)
         self.RefreshItemText(item, index)
         self.RefreshColumns(item, index)
@@ -458,7 +458,7 @@ class VirtualTree(TreeAPIHarmonizer, TreeHelper):
         event.Skip()
 
     def __refreshAttribute(self, item, index, attribute, *args):
-        ''' Refresh the specified attribute if necessary. '''
+        """ Refresh the specified attribute if necessary. """
         value = getattr(self, 'OnGet%s'%attribute)(index, *args)
         if getattr(self, 'Get%s'%attribute)(item, *args) != value:
             return getattr(self, 'Set%s'%attribute)(item, value, *args)
@@ -467,7 +467,7 @@ class VirtualTree(TreeAPIHarmonizer, TreeHelper):
 
 
 class DragAndDrop(TreeAPIHarmonizer, TreeHelper):
-    ''' This is a mixin class that can be used to easily implement
+    """ This is a mixin class that can be used to easily implement
     dragging and dropping of tree items. It can be mixed in with 
     wx.TreeCtrl, wx.gizmos.TreeListCtrl, or wx.lib.customtree.CustomTreeCtrl.
 
@@ -480,7 +480,7 @@ class DragAndDrop(TreeAPIHarmonizer, TreeHelper):
     dropped an item on top of another item. It's up to you to decide how
     to handle the drop. If you are using this mixin together with the
     VirtualTree mixin, it makes sense to rearrange your underlying data
-    and then call RefreshItems to let the virtual tree refresh itself. '''    
+    and then call RefreshItems to let the virtual tree refresh itself. """    
  
     def __init__(self, *args, **kwargs):
         kwargs['style'] = kwargs.get('style', wx.TR_DEFAULT_STYLE) | \
@@ -489,11 +489,11 @@ class DragAndDrop(TreeAPIHarmonizer, TreeHelper):
         self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag)
 
     def OnDrop(self, dropItem, dragItem):
-        ''' This function must be overloaded in the derived class.
+        """ This function must be overloaded in the derived class.
         dragItem is the item being dragged by the user. dropItem is the
         item dragItem is dropped upon. If the user doesn't drop dragItem
         on another item, dropItem equals the (hidden) root item of the
-        tree control. '''
+        tree control. """
         raise NotImplementedError
 
     def OnBeginDrag(self, event):
@@ -567,7 +567,7 @@ class DragAndDrop(TreeAPIHarmonizer, TreeHelper):
 
 
 class ExpansionState(TreeAPIHarmonizer, TreeHelper):
-    ''' This is a mixin class that can be used to save and restore
+    """ This is a mixin class that can be used to save and restore
     the expansion state (i.e. which items are expanded and which items
     are collapsed) of a tree. It can be mixed in with wx.TreeCtrl, 
     wx.gizmos.TreeListCtrl, or wx.lib.customtree.CustomTreeCtrl.
@@ -590,20 +590,20 @@ class ExpansionState(TreeAPIHarmonizer, TreeHelper):
     expansion doesn't depend on the position of items in the tree, but 
     rather on some more stable characteristic of the underlying domain 
     object, e.g. a social security number in case of persons or an isbn 
-    number in case of books. '''    
+    number in case of books. """    
 
     def GetItemIdentity(self, item):
-        ''' Return a hashable object that represents the identity of the
+        """ Return a hashable object that represents the identity of the
         item. By default this returns the position of the item in the 
         tree. You may want to override this to return the item label 
         (if you know that labels are unique and don't change), or return 
         something that represents the underlying domain object, e.g. 
-        a database key. ''' 
+        a database key. """ 
         return self.GetIndexOfItem(item)
  
     def GetExpansionState(self):
-        ''' GetExpansionState() -> list of expanded items. Expanded items 
-        are coded as determined by the result of GetItemIdentity(item). '''
+        """ GetExpansionState() -> list of expanded items. Expanded items 
+        are coded as determined by the result of GetItemIdentity(item). """
         root = self.GetRootItem()
         if not root:
             return []
@@ -613,9 +613,9 @@ class ExpansionState(TreeAPIHarmonizer, TreeHelper):
             return self.GetExpansionStateOfItem(root)
 
     def SetExpansionState(self, listOfExpandedItems):
-        ''' SetExpansionState(listOfExpandedItems). Expands all tree items 
+        """ SetExpansionState(listOfExpandedItems). Expands all tree items 
         whose identity, as determined by GetItemIdentity(item), is present
-        in the list and collapses all other tree items. '''
+        in the list and collapses all other tree items. """
         root = self.GetRootItem()
         if not root:
             return
index 9093b2e12591a64e78414a82f24c2b0a7b3e07e7..459a07674a9e6c1a26f0f24e7ade6fb9975cab31 100644 (file)
@@ -89,12 +89,19 @@ class xrc%(windowName)s(wx.%(windowClass)s):
         pre.thisown = 0
         if hasattr(self, '_setOORInfo'):
             self._setOORInfo(self)
-        if hasattr(self, '_setCallbackInfo'):
-            self._setCallbackInfo(self, self.__class__)
 
         # Define variables for the menu items
 """
 
+    MENUBAR_CLASS_HEADER = """\
+class xrc%(windowName)s(wx.%(windowClass)s):
+    def __init__(self):
+        pre = get_resources().LoadMenuBar("%(windowName)s")
+        self.PostCreate(pre)
+        
+        # Define variables for the menu items
+"""
+
     CREATE_MENUITEM_VAR = """\
         self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\"))
 """
@@ -227,7 +234,9 @@ class XmlResourceCompiler:
             windowClass = re.sub("^wx", "", windowClass)
             windowName = topWindow.getAttribute("name")
             
-            if windowClass in ["Menu", "MenuItem"]:
+            if windowClass in ["MenuBar"]:
+                outputList.append(self.templates.MENUBAR_CLASS_HEADER % locals())
+            elif windowClass in ["Menu"]:
                 outputList.append(self.templates.MENU_CLASS_HEADER % locals())
             else:
                 outputList.append(self.templates.CLASS_HEADER % locals())
index 04d74c1fc70adda2d7c966aad952a1356dd58ad7..4123752042807adc445f7ac77a0eb5a5efeab4ee 100644 (file)
@@ -457,10 +457,21 @@ class SizedPanel(wx.PyPanel):
         self.sizerType = "vertical"
         
     def AddChild(self, child):
-        wx.PyPanel.base_AddChild(self, child)
-        
+        if wx.VERSION < (2,8):
+            wx.PyPanel.base_AddChild(self, child)
+        else:
+            wx.PyPanel.AddChild(self, child)
+
+        # Note: The wx.LogNull is used here to suppress a log message
+        # on wxMSW that happens because when AddChild is called the
+        # widget's hwnd hasn't been set yet, so the GetWindowRect that
+        # happens as a result of sizer.Add (in wxSizerItem::SetWindow)
+        # fails.  A better fix would be to defer this code somehow
+        # until after the child widget is fully constructed.
         sizer = self.GetSizer()
+        nolog = wx.LogNull()
         item = sizer.Add(child)
+        del nolog
         item.SetUserData({"HGrow":0, "VGrow":0})
         
         # Note: One problem is that the child class given to AddChild