/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/mainwindow.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/mainwindow.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 6 by bh, Tue Aug 28 15:41:52 2001 UTC revision 1293 by jonathan, Mon Jun 23 13:05:58 2003 UTC
# Line 1  Line 1 
1  # Copyright (C) 2001 by Intevation GmbH  # Copyright (C) 2001, 2002, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Jan-Oliver Wagner <[email protected]>  # Jan-Oliver Wagner <[email protected]>
4  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
5    # Frank Koormann <[email protected]>
6  #  #
7  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
8  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 12  The main window Line 13  The main window
13    
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    
16  import sys, os  __ThubanVersion__ = "0.8" #"$THUBAN_0_2$"
17    #__BuildDate__ = "$Date$"
18    
19    import os
20    import copy
21    
22  from wxPython.wx import *  from wxPython.wx import *
23    from wxPython.wx import __version__ as wxPython_version
24    
25  import Thuban  import Thuban
26  from Thuban.Model.session import Session  import Thuban.version
27  from Thuban.Model.map import Map  
28  from Thuban.Model.layer import Layer  from Thuban import _
29  from Thuban.Model.color import Color  from Thuban.Model.session import create_empty_session
30  from Thuban.Model.proj import Projection  from Thuban.Model.layer import Layer, RasterLayer
31    
32    # XXX: replace this by
33    # from wxPython.lib.dialogs import wxMultipleChoiceDialog
34    # when Thuban does not support wxPython 2.4.0 any more.
35    from Thuban.UI.multiplechoicedialog import wxMultipleChoiceDialog
36    
37  import view  import view
38  import tree  import tree
 import proj4dialog  
39  import tableview, identifyview  import tableview, identifyview
40    from Thuban.UI.classifier import Classifier
41    import legend
42    from menu import Menu
43    
44    from context import Context
45    from command import registry, Command, ToolCommand
46    from messages import LAYER_SELECTED, SHAPES_SELECTED, VIEW_POSITION
47    
48    from Thuban.UI.dock import DockFrame
49    from Thuban.UI.join import JoinDialog
50    
51    import resource
52    import Thuban.Model.resource
53    
54    import projdialog
55    
56    class MainWindow(DockFrame):
57    
58        # Some messages that can be subscribed/unsubscribed directly through
59        # the MapCanvas come in fact from other objects. This is a map to
60        # map those messages to the names of the instance variables they
61        # actually come from. This delegation is implemented in the
62        # Subscribe and unsubscribed methods
63        delegated_messages = {LAYER_SELECTED: "canvas",
64                              SHAPES_SELECTED: "canvas"}
65    
66        # Methods delegated to some instance variables. The delegation is
67        # implemented in the __getattr__ method.
68        delegated_methods = {"SelectLayer": "canvas",
69                             "SelectShapes": "canvas",
70                             "SelectedLayer": "canvas",
71                             "SelectedShapes": "canvas",
72                             }
73    
74        def __init__(self, parent, ID, title, application, interactor,
75                     initial_message = None, size = wxSize(-1, -1)):
76            DockFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
77            #wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
78    
79  import main          self.application = application
 from command import registry, Command  
   
   
 # the directory where the toolbar icons are stored  
 bitmapdir = os.path.join(Thuban.__path__[0], os.pardir, "Resources", "Bitmaps")  
 bitmapext = ".xpm"  
   
   
 class MainWindow(wxFrame):  
   
     def __init__(self, parent, ID):  
         wxFrame.__init__(self, parent, ID, 'Thuban',  
                          wxDefaultPosition, wxSize(400, 300))  
80    
81          self.CreateStatusBar()          self.CreateStatusBar()
82          self.SetStatusText("This is the wxPython-based "          if initial_message:
83                        "Graphical User Interface for exploring geographic data")              self.SetStatusText(initial_message)
84    
85          self.identify_view = None          self.identify_view = None
86    
87          self.init_ids()          self.init_ids()
88    
89          menuBar = wxMenuBar()          # creat the menubar from the main_menu description
90            self.SetMenuBar(self.build_menu_bar(main_menu))
         menu = wxMenu()  
         menuBar.Append(menu, "&File");  
         for name in ["new_session", "open_session", None,  
                      "save_session", "save_session_as", None,  
                      "exit"]:  
             self.add_menu_command(menu, name)  
   
         menu = wxMenu()  
         menuBar.Append(menu, "&Map");  
         for name in ["map_projection",  
                      None,  
                      "map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",  
                      "map_identify_tool", "map_label_tool",  
                      None,  
                      "map_full_extent",  
                      None,  
                      "map_print"]:  
             self.add_menu_command(menu, name)  
   
         menu = wxMenu()  
         menuBar.Append(menu, "&Layer");  
         for name in ["layer_add", "layer_remove",  
                      None,  
                      "layer_fill_color", "layer_transparent_fill",  
                      "layer_ourline_color", "layer_no_outline",  
                      None,  
                      "layer_raise", "layer_lower",  
                      None,  
                      "layer_show", "layer_hide",  
                      None,  
                      "layer_show_table"]:  
             self.add_menu_command(menu, name)  
   
         menu = wxMenu()  
         menuBar.Append(menu, "&Help");  
         self.add_menu_command(menu, "help_about")  
91    
92          self.SetMenuBar(menuBar)          # Similarly, create the toolbar from main_toolbar
93            toolbar = self.build_toolbar(main_toolbar)
94            # call Realize to make sure that the tools appear.
95            toolbar.Realize()
96    
         # toolbar  
         toolbar = self.CreateToolBar(wxTB_3DBUTTONS)  
         for name in ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",  
                      "map_identify_tool", "map_label_tool"]:  
             self.add_toolbar_command(toolbar, name)  
97    
98          # Create the map canvas          # Create the map canvas
99          canvas = view.MapCanvas(self, -1)          canvas = view.MapCanvas(self, -1)
100            canvas.Subscribe(VIEW_POSITION, self.view_position_changed)
101            canvas.Subscribe(SHAPES_SELECTED, self.identify_view_on_demand)
102          self.canvas = canvas          self.canvas = canvas
103    
104            self.SetMainWindow(self.canvas)
105    
106            self.SetAutoLayout(True)
107    
108            self.init_dialogs()
109    
110            self.ShowLegend()
111    
112          EVT_CLOSE(self, self.OnClose)          EVT_CLOSE(self, self.OnClose)
113    
114        def Subscribe(self, channel, *args):
115            """Subscribe a function to a message channel.
116    
117            If channel is one of the delegated messages call the appropriate
118            object's Subscribe method. Otherwise do nothing.
119            """
120            if channel in self.delegated_messages:
121                object = getattr(self, self.delegated_messages[channel])
122                object.Subscribe(channel, *args)
123            else:
124                print "Trying to subscribe to unsupported channel %s" % channel
125    
126        def Unsubscribe(self, channel, *args):
127            """Unsubscribe a function from a message channel.
128    
129            If channel is one of the delegated messages call the appropriate
130            object's Unsubscribe method. Otherwise do nothing.
131            """
132            if channel in self.delegated_messages:
133                object = getattr(self, self.delegated_messages[channel])
134                object.Unsubscribe(channel, *args)
135    
136        def __getattr__(self, attr):
137            """If attr is one of the delegated methods return that method
138    
139            Otherwise raise AttributeError.
140            """
141            if attr in self.delegated_methods:
142                return getattr(getattr(self, self.delegated_methods[attr]), attr)
143            raise AttributeError(attr)
144    
145      def init_ids(self):      def init_ids(self):
146          """Initialize the ids"""          """Initialize the ids"""
147          self.current_id = 6000          self.current_id = 6000
148          self.id_to_name = {}          self.id_to_name = {}
149          self.name_to_id = {}          self.name_to_id = {}
150            self.events_bound = {}
151    
152      def get_id(self, name):      def get_id(self, name):
153          """Return the wxWindows id for the command named name.          """Return the wxWindows id for the command named name.
# Line 121  class MainWindow(wxFrame): Line 160  class MainWindow(wxFrame):
160              self.name_to_id[name] = ID              self.name_to_id[name] = ID
161              self.id_to_name[ID] = name              self.id_to_name[ID] = name
162          return ID          return ID
163            
164        def bind_command_events(self, command, ID):
165            """Bind the necessary events for the given command and ID"""
166            if not self.events_bound.has_key(ID):
167                # the events haven't been bound yet
168                EVT_MENU(self, ID, self.invoke_command)
169                if command.IsDynamic():
170                    EVT_UPDATE_UI(self, ID, self.update_command_ui)
171    
172        def build_menu_bar(self, menudesc):
173            """Build and return the menu bar from the menu description"""
174            menu_bar = wxMenuBar()
175    
176            for item in menudesc.items:
177                # here the items must all be Menu instances themselves
178                menu_bar.Append(self.build_menu(item), item.title)
179    
180            return menu_bar
181    
182        def build_menu(self, menudesc):
183            """Return a wxMenu built from the menu description menudesc"""
184            wxmenu = wxMenu()
185            last = None
186            for item in menudesc.items:
187                if item is None:
188                    # a separator. Only add one if the last item was not a
189                    # separator
190                    if last is not None:
191                        wxmenu.AppendSeparator()
192                elif isinstance(item, Menu):
193                    # a submenu
194                    wxmenu.AppendMenu(wxNewId(), item.title, self.build_menu(item))
195                else:
196                    # must the name the name of a command
197                    self.add_menu_command(wxmenu, item)
198                last = item
199            return wxmenu
200    
201        def build_toolbar(self, toolbardesc):
202            """Build and return the main toolbar window from a toolbar description
203    
204            The parameter should be an instance of the Menu class but it
205            should not contain submenus.
206            """
207            toolbar = self.CreateToolBar(wxTB_3DBUTTONS)
208    
209            # set the size of the tools' bitmaps. Not needed on wxGTK, but
210            # on Windows, although it doesn't work very well there. It seems
211            # that only 16x16 icons are really supported on windows.
212            # We probably shouldn't hardwire the bitmap size here.
213            toolbar.SetToolBitmapSize(wxSize(24, 24))
214    
215            for item in toolbardesc.items:
216                if item is None:
217                    toolbar.AddSeparator()
218                else:
219                    # assume it's a string.
220                    self.add_toolbar_command(toolbar, item)
221    
222            return toolbar
223    
224      def add_menu_command(self, menu, name):      def add_menu_command(self, menu, name):
225          """Add the command with name name to the menu menu.          """Add the command with name name to the menu menu.
226    
# Line 135  class MainWindow(wxFrame): Line 234  class MainWindow(wxFrame):
234                  ID = self.get_id(name)                  ID = self.get_id(name)
235                  menu.Append(ID, command.Title(), command.HelpText(),                  menu.Append(ID, command.Title(), command.HelpText(),
236                              command.IsCheckCommand())                              command.IsCheckCommand())
237                  EVT_MENU(self, ID, self.invoke_command)                  self.bind_command_events(command, ID)
                 if command.IsDynamic():  
                     EVT_UPDATE_UI(self, ID, self.update_command_ui)  
238              else:              else:
239                  print "Unknown command %s" % name                  print _("Unknown command %s") % name
240    
241      def add_toolbar_command(self, toolbar, name):      def add_toolbar_command(self, toolbar, name):
242          """Add the command with name name to the toolbar toolbar.          """Add the command with name name to the toolbar toolbar.
# Line 154  class MainWindow(wxFrame): Line 251  class MainWindow(wxFrame):
251              command = registry.Command(name)              command = registry.Command(name)
252              if command is not None:              if command is not None:
253                  ID = self.get_id(name)                  ID = self.get_id(name)
254                  filename = os.path.join(bitmapdir, command.Icon()) + bitmapext                  bitmap = resource.GetBitmapResource(command.Icon(),
255                  bitmap = wxBitmap(filename, wxBITMAP_TYPE_XPM)                                                      wxBITMAP_TYPE_XPM)
256                  toolbar.AddTool(ID, bitmap,                  toolbar.AddTool(ID, bitmap,
257                                  shortHelpString = command.HelpText(),                                  shortHelpString = command.HelpText(),
258                                  isToggle = command.IsCheckCommand())                                  isToggle = command.IsCheckCommand())
259                    self.bind_command_events(command, ID)
260              else:              else:
261                  print "Unknown command %s" % name                  print _("Unknown command %s") % name
262    
263        def Context(self):
264            """Return the context object for a command invoked from this window
265            """
266            return Context(self.application, self.application.Session(), self)
267    
268      def invoke_command(self, event):      def invoke_command(self, event):
269          name = self.id_to_name.get(event.GetId())          name = self.id_to_name.get(event.GetId())
270          if name is not None:          if name is not None:
271              command = registry.Command(name)              command = registry.Command(name)
272              command.Execute(self)              command.Execute(self.Context())
273          else:          else:
274              print "Unknown command ID %d" % event.GetId()              print _("Unknown command ID %d") % event.GetId()
275    
276      def update_command_ui(self, event):      def update_command_ui(self, event):
277          #print "update_command_ui", self.id_to_name[event.GetId()]          #print "update_command_ui", self.id_to_name[event.GetId()]
278            context = self.Context()
279          command = registry.Command(self.id_to_name[event.GetId()])          command = registry.Command(self.id_to_name[event.GetId()])
280          if command is not None:          if command is not None:
281              event.Enable(command.Sensitive(self))              sensitive = command.Sensitive(context)
282              event.Check(command.Checked(self))              event.Enable(sensitive)
283              event.SetText(command.DynText(self))              if command.IsTool() and not sensitive and command.Checked(context):
284                    # When a checked tool command is disabled deselect all
285                    # tools. Otherwise the tool would remain active but it
286                    # might lead to errors if the tools stays active. This
287                    # problem occurred in GREAT-ER and this fixes it, but
288                    # it's not clear to me whether this is really the best
289                    # way to do it (BH, 20021206).
290                    self.canvas.SelectTool(None)
291                event.SetText(command.DynText(context))
292                if command.IsCheckCommand():
293                        event.Check(command.Checked(context))
294    
295        def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
296            """Run a modal message box with the given text, title and flags
297            and return the result"""
298            dlg = wxMessageDialog(self, text, title, flags)
299            dlg.CenterOnParent()
300            result = dlg.ShowModal()
301            dlg.Destroy()
302            return result
303    
304        def init_dialogs(self):
305            """Initialize the dialog handling"""
306            # The mainwindow maintains a dict mapping names to open
307            # non-modal dialogs. The dialogs are put into this dict when
308            # they're created and removed when they're closed
309            self.dialogs = {}
310    
311        def add_dialog(self, name, dialog):
312            if self.dialogs.has_key(name):
313                raise RuntimeError(_("The Dialog named %s is already open") % name)
314            self.dialogs[name] = dialog
315    
316        def dialog_open(self, name):
317            return self.dialogs.has_key(name)
318    
319        def remove_dialog(self, name):
320            del self.dialogs[name]
321    
322        def get_open_dialog(self, name):
323            return self.dialogs.get(name)
324    
325        def view_position_changed(self):
326            pos = self.canvas.CurrentPosition()
327            if pos is not None:
328                text = "(%10.10g, %10.10g)" % pos
329            else:
330                text = ""
331            self.set_position_text(text)
332    
333        def set_position_text(self, text):
334            """Set the statusbar text showing the current position.
335    
336            By default the text is shown in field 0 of the status bar.
337            Override this method in derived classes to put it into a
338            different field of the statusbar.
339            """
340            self.SetStatusText(text)
341    
342        def save_modified_session(self, can_veto = 1):
343            """If the current session has been modified, ask the user
344            whether to save it and do so if requested. Return the outcome of
345            the dialog (either wxID_OK, wxID_CANCEL or wxID_NO). If the
346            dialog wasn't run return wxID_NO.
347    
348            If the can_veto parameter is true (default) the dialog includes
349            a cancel button, otherwise not.
350            """
351            if self.application.session.WasModified():
352                flags = wxYES_NO | wxICON_QUESTION
353                if can_veto:
354                    flags = flags | wxCANCEL
355                result = self.RunMessageBox(_("Exit"),
356                                            _("The session has been modified."
357                                             " Do you want to save it?"),
358                                            flags)
359                if result == wxID_YES:
360                    self.SaveSession()
361            else:
362                result = wxID_NO
363            return result
364    
365        def prepare_new_session(self):
366            for d in self.dialogs.values():
367                if not isinstance(d, tree.SessionTreeView):
368                    d.Close()
369    
370      def NewSession(self):      def NewSession(self):
371          session = Session("")          if self.save_modified_session() != wxID_CANCEL:
372          session.AddMap(Map(""))              self.prepare_new_session()
373          main.app.SetSession(session)              self.application.SetSession(create_empty_session())
374    
375      def OpenSession(self):      def OpenSession(self):
376          dlg = wxFileDialog(self, "Select a session file", ".", "",          if self.save_modified_session() != wxID_CANCEL:
377                             "*.session", wxOPEN)              dlg = wxFileDialog(self, _("Open Session"), ".", "",
378          if dlg.ShowModal() == wxID_OK:                                 "Thuban Session File (*.thuban)|*.thuban",
379              main.app.OpenSession(dlg.GetPath())                                 wxOPEN)
380          dlg.Destroy()              if dlg.ShowModal() == wxID_OK:
381                    self.prepare_new_session()
382                    self.application.OpenSession(dlg.GetPath())
383                dlg.Destroy()
384    
385      def SaveSession(self):      def SaveSession(self):
386          main.app.SaveSession()          if self.application.session.filename == None:
387                self.SaveSessionAs()
388            else:
389                self.application.SaveSession()
390    
391      def SaveSessionAs(self):      def SaveSessionAs(self):
392          dlg = wxFileDialog(self, "Enter a filename for session", ".", "",          dlg = wxFileDialog(self, _("Save Session As"), ".", "",
393                             "*.session", wxOPEN)                             "Thuban Session File (*.thuban)|*.thuban",
394                               wxSAVE|wxOVERWRITE_PROMPT)
395          if dlg.ShowModal() == wxID_OK:          if dlg.ShowModal() == wxID_OK:
396              main.app.session.SetFilename(dlg.GetPath())              self.application.session.SetFilename(dlg.GetPath())
397              main.app.SaveSession()              self.application.SaveSession()
398          dlg.Destroy()          dlg.Destroy()
399    
400      def Exit(self):      def Exit(self):
401          self.Close(false)          self.Close(False)
402    
403      def OnClose(self, event):      def OnClose(self, event):
404          veto = 0          result = self.save_modified_session(can_veto = event.CanVeto())
405          if main.app.session.WasModified():          if result == wxID_CANCEL:
             flags = wxYES_NO | wxICON_QUESTION  
             if event.CanVeto():  
                 flags = flags | wxCANCEL  
             dlg = wxMessageDialog(self,  
                                   ("The session has been modified."  
                                    " Do you want to save it?"),  
                                   "Exit", flags)  
             result = dlg.ShowModal()  
             dlg.Destroy()  
             if result == wxID_YES:  
                 self.SaveSession()  
             elif result == wxID_CANCEL:  
                 veto = 1  
   
         if veto:  
406              event.Veto()              event.Veto()
407          else:          else:
408                # FIXME: it would be better to tie the unsubscription to
409                # wx's destroy event, but that isn't implemented for wxGTK
410                # yet.
411                self.canvas.Unsubscribe(VIEW_POSITION, self.view_position_changed)
412                DockFrame.OnClose(self, event)
413                for dlg in self.dialogs.values():
414                    dlg.Destroy()
415                self.canvas.Destroy()
416              self.Destroy()              self.Destroy()
417    
418      def SetMap(self, map):      def SetMap(self, map):
419          self.canvas.SetMap(map)          self.canvas.SetMap(map)
420            self.__SetTitle(map.Title())
421    
422            dialog = self.FindRegisteredDock("legend")
423            if dialog is not None:
424                dialog.GetPanel().SetMap(self.Map())
425    
426        def Map(self):
427            """Return the map displayed by this mainwindow"""
428    
429            return self.canvas.Map()
430    
431        def ToggleSessionTree(self):
432            """If the session tree is shown close it otherwise create a new tree"""
433            name = "session_tree"
434            dialog = self.get_open_dialog(name)
435            if dialog is None:
436                dialog = tree.SessionTreeView(self, self.application, name)
437                self.add_dialog(name, dialog)
438                dialog.Show(True)
439            else:
440                dialog.Close()
441    
442        def SessionTreeShown(self):
443            """Return true iff the session tree is currently shown"""
444            return self.get_open_dialog("session_tree") is not None
445    
446      def About(self):      def About(self):
447          dlg = wxMessageDialog(self,          self.RunMessageBox(_("About"),
448                                ("Thuban is a program for\n"                             _("Thuban %s\n\n"
449                                 "exploring geographic data.\n"                              #"Build Date: %s\n"
450                                 "Copyright (C) 2001 Intevation GmbH.\n"                              "Currently using:\n"
451                                 "Thuban is licensed under the GPL"),                              "  %s\n"
452                                "About", wxOK | wxICON_INFORMATION)                              "  %s\n\n"
453          dlg.ShowModal()                              "Thuban is a program for\n"
454          dlg.Destroy()                              "exploring geographic data.\n"
455                                "Copyright (C) 2001-2003 Intevation GmbH.\n"
456                                "Thuban is licensed under the GNU GPL"
457                                % (Thuban.version.longversion,
458                                   "wxPython %s" % wxPython_version,
459                                   "Python %d.%d.%d" % sys.version_info[:3]
460                                  )),
461    #                           % __ThubanVersion__), #__BuildDate__)),
462                               wxOK | wxICON_INFORMATION)
463    
464      def AddLayer(self):      def AddLayer(self):
465          dlg = wxFileDialog(self, "Select a session file", ".", "", "*.*",          dlg = wxFileDialog(self, _("Select a data file"), ".", "", "*.*",
466                             wxOPEN)                             wxOPEN)
467          if dlg.ShowModal() == wxID_OK:          if dlg.ShowModal() == wxID_OK:
468              filename = dlg.GetPath()              filename = dlg.GetPath()
469              title = os.path.splitext(os.path.basename(filename))[0]              title = os.path.splitext(os.path.basename(filename))[0]
470              layer = Layer(title, filename)              map = self.canvas.Map()
471              self.canvas.Map().AddLayer(layer)              has_layers = map.HasLayers()
472                try:
473                    store = self.application.Session().OpenShapefile(filename)
474                except IOError:
475                    # the layer couldn't be opened
476                    self.RunMessageBox(_("Add Layer"),
477                                       _("Can't open the file '%s'.") % filename)
478                else:
479                    layer = Layer(title, store)
480                    map.AddLayer(layer)
481                    if not has_layers:
482                        # if we're adding a layer to an empty map, fit the
483                        # new map to the window
484                        self.canvas.FitMapToWindow()
485            dlg.Destroy()
486    
487        def AddRasterLayer(self):
488            dlg = wxFileDialog(self, _("Select an image file"), ".", "", "*.*",
489                               wxOPEN)
490            if dlg.ShowModal() == wxID_OK:
491                filename = dlg.GetPath()
492                title = os.path.splitext(os.path.basename(filename))[0]
493                map = self.canvas.Map()
494                has_layers = map.HasLayers()
495                try:
496                    layer = RasterLayer(title, filename)
497                except IOError:
498                    # the layer couldn't be opened
499                    self.RunMessageBox(_("Add Image Layer"),
500                                       _("Can't open the file '%s'.") % filename)
501                else:
502                    map.AddLayer(layer)
503                    if not has_layers:
504                        # if we're adding a layer to an empty map, fit the
505                        # new map to the window
506                        self.canvas.FitMapToWindow()
507          dlg.Destroy()          dlg.Destroy()
508    
509      def RemoveLayer(self):      def RemoveLayer(self):
510          layer = self.current_layer()          layer = self.current_layer()
511          if layer is not None:          if layer is not None:
512              self.canvas.Map().RemoveLayer(layer)              self.canvas.Map().RemoveLayer(layer)
513          else:  
514              dlg = wxMessageDialog(self, "No layer is selected.\n",      def CanRemoveLayer(self):
515                                    "Remove layer", wxOK | wxICON_INFORMATION)          """Return true if the currently selected layer can be deleted.
516              dlg.ShowModal()  
517              dlg.Destroy()          If no layer is selected return False.
518    
519            The return value of this method determines whether the remove
520            layer command is sensitive in menu.
521            """
522            layer = self.current_layer()
523            if layer is not None:
524                return self.canvas.Map().CanRemoveLayer(layer)
525            return False
526    
527      def RaiseLayer(self):      def RaiseLayer(self):
528          layer = self.current_layer()          layer = self.current_layer()
529          if layer is not None:          if layer is not None:
530              self.canvas.Map().RaiseLayer(layer)              self.canvas.Map().RaiseLayer(layer)
531            
532      def LowerLayer(self):      def LowerLayer(self):
533          layer = self.current_layer()          layer = self.current_layer()
534          if layer is not None:          if layer is not None:
# Line 274  class MainWindow(wxFrame): Line 539  class MainWindow(wxFrame):
539    
540          If no layer is selected, return None          If no layer is selected, return None
541          """          """
542          tree = main.app.tree.tree          return self.canvas.SelectedLayer()
         layer = tree.GetPyData(tree.GetSelection())  
         if isinstance(layer, Layer):  
             return layer  
         return None  
543    
544      def has_selected_layer(self):      def has_selected_layer(self):
545          """Return true if a layer is currently selected"""          """Return true if a layer is currently selected"""
546          tree = main.app.tree.tree          return self.canvas.HasSelectedLayer()
         layer = tree.GetPyData(tree.GetSelection())  
         return isinstance(layer, Layer)  
547    
548      def choose_color(self):      def has_selected_shapes(self):
549          """Run the color selection dialog and return the selected color.          """Return true if a shape is currently selected"""
550            return self.canvas.HasSelectedShapes()
551    
552          If the user cancels, return None.      def HideLayer(self):
         """  
         dlg = wxColourDialog(self)  
         color = None  
         if dlg.ShowModal() == wxID_OK:  
             data = dlg.GetColourData()  
             wxc = data.GetColour()  
             color = Color(wxc.Red() / 255.0,  
                           wxc.Green() / 255.0,  
                           wxc.Blue() / 255.0)  
         dlg.Destroy()  
         return color  
   
     def LayerFillColor(self):  
553          layer = self.current_layer()          layer = self.current_layer()
554          if layer is not None:          if layer is not None:
555              color = self.choose_color()              layer.SetVisible(0)
             if color is not None:  
                 layer.SetFill(color)  
556    
557      def LayerTransparentFill(self):      def ShowLayer(self):
558          layer = self.current_layer()          layer = self.current_layer()
559          if layer is not None:          if layer is not None:
560              layer.SetFill(None)              layer.SetVisible(1)
561    
562      def LayerOutlineColor(self):      def DuplicateLayer(self):
563            """Ceate a new layer above the selected layer with the same shapestore
564            """
565          layer = self.current_layer()          layer = self.current_layer()
566          if layer is not None:          if layer is not None and hasattr(layer, "ShapeStore"):
567              color = self.choose_color()              new_layer = Layer(_("Copy of `%s'") % layer.Title(),
568              if color is not None:                                layer.ShapeStore(),
569                  layer.SetStroke(color)                                projection = layer.GetProjection())
570                new_classification = copy.deepcopy(layer.GetClassification())
571                new_layer.SetClassification(new_classification)
572                self.Map().AddLayer(new_layer)
573    
574      def LayerNoOutline(self):      def CanDuplicateLayer(self):
575            """Return whether the DuplicateLayer method can create a duplicate"""
576          layer = self.current_layer()          layer = self.current_layer()
577          if layer is not None:          return layer is not None and hasattr(layer, "ShapeStore")
             layer.SetStroke(None)  
578    
579      def HideLayer(self):      def LayerShowTable(self):
580          layer = self.current_layer()          layer = self.current_layer()
581          if layer is not None:          if layer is not None:
582              layer.SetVisible(0)              table = layer.ShapeStore().Table()
583                        name = "table_view" + str(id(table))
584      def ShowLayer(self):              dialog = self.get_open_dialog(name)
585                if dialog is None:
586                    dialog = tableview.LayerTableFrame(self, name,
587                                             _("Layer Table: %s") % layer.Title(),
588                                             layer, table)
589                    self.add_dialog(name, dialog)
590                    dialog.Show(True)
591                else:
592                    # FIXME: bring dialog to front here
593                    pass
594    
595        def MapProjection(self):
596    
597            name = "map_projection"
598            dialog = self.get_open_dialog(name)
599    
600            if dialog is None:
601                map = self.canvas.Map()
602                dialog = projdialog.ProjFrame(self, name,
603                         _("Map Projection: %s") % map.Title(), map)
604                self.add_dialog(name, dialog)
605                dialog.Show()
606            dialog.Raise()
607    
608        def LayerProjection(self):
609    
610          layer = self.current_layer()          layer = self.current_layer()
         if layer is not None:  
             layer.SetVisible(1)  
611    
612      def LayerShowTable(self):          name = "layer_projection" + str(id(layer))
613            dialog = self.get_open_dialog(name)
614    
615            if dialog is None:
616                map = self.canvas.Map()
617                dialog = projdialog.ProjFrame(self, name,
618                         _("Layer Projection: %s") % layer.Title(), layer)
619                self.add_dialog(name, dialog)
620                dialog.Show()
621            dialog.Raise()
622    
623        def LayerEditProperties(self):
624    
625            #
626            # the menu option for this should only be available if there
627            # is a current layer, so we don't need to check if the
628            # current layer is None
629            #
630    
631          layer = self.current_layer()          layer = self.current_layer()
632            self.OpenLayerProperties(layer)
633    
634        def OpenLayerProperties(self, layer, group = None):
635            name = "layer_properties" + str(id(layer))
636            dialog = self.get_open_dialog(name)
637    
638            if dialog is None:
639                dialog = Classifier(self, name, self.Map(), layer, group)
640                self.add_dialog(name, dialog)
641                dialog.Show()
642            dialog.Raise()
643    
644        def LayerJoinTable(self):
645            layer = self.canvas.SelectedLayer()
646          if layer is not None:          if layer is not None:
647              tv = tableview.TableFrame(self, layer.table)              dlg = JoinDialog(self, _("Join Layer with Table"),
648              tv.Show(true)                               self.application.session,
649                                 layer = layer)
650                dlg.ShowModal()
651    
652      def Projection(self):      def LayerUnjoinTable(self):
653          map = self.canvas.Map()          layer = self.canvas.SelectedLayer()
654          proj = map.projection          if layer is not None:
655          if proj is None:              orig_store = layer.ShapeStore().OrigShapeStore()
656              proj4Dlg = proj4dialog.Proj4Dialog(NULL, None)              if orig_store:
657          else:                  layer.SetShapeStore(orig_store)
658              proj4Dlg = proj4dialog.Proj4Dialog(NULL, map.projection.params)  
659          if proj4Dlg.ShowModal() == wxID_OK:      def ShowLegend(self):
660              params = proj4Dlg.GetParams()          if not self.LegendShown():
661              if params is not None:              self.ToggleLegend()
662                  proj = Projection(params)  
663        def ToggleLegend(self):
664            """Show the legend if it's not shown otherwise hide it again"""
665            name = "legend"
666            dialog = self.FindRegisteredDock(name)
667    
668            if dialog is None:
669                dialog = self.CreateDock(name, -1, _("Legend"), wxLAYOUT_LEFT)
670                legend.LegendPanel(dialog, None, self)
671                dialog.Dock()
672                dialog.GetPanel().SetMap(self.Map())
673                dialog.Show()
674            else:
675                dialog.Show(not dialog.IsShown())
676    
677            self.canvas.FitMapToWindow()
678    
679        def LegendShown(self):
680            """Return true iff the legend is currently open"""
681            dialog = self.FindRegisteredDock("legend")
682            return dialog is not None and dialog.IsShown()
683    
684        def TableOpen(self):
685            dlg = wxFileDialog(self, _("Open Table"), ".", "",
686                               _("DBF Files (*.dbf)") + "|*.dbf|" +
687                               #_("CSV Files (*.csv)") + "|*.csv|" +
688                               _("All Files (*.*)") + "|*.*",
689                               wxOPEN)
690            if dlg.ShowModal() == wxID_OK:
691                filename = dlg.GetPath()
692                dlg.Destroy()
693                try:
694                    table = self.application.session.OpenTableFile(filename)
695                except IOError:
696                    # the layer couldn't be opened
697                    self.RunMessageBox(_("Open Table"),
698                                       _("Can't open the file '%s'.") % filename)
699              else:              else:
700                  proj = None                  self.ShowTableView(table)
701              map.SetProjection(proj)  
702          proj4Dlg.Destroy()      def TableClose(self):
703            tables = self.application.session.UnreferencedTables()
704    
705            lst = [(t.Title(), t) for t in tables]
706            lst.sort()
707            titles = [i[0] for i in lst]
708            dlg = wxMultipleChoiceDialog(self, _("Pick the tables to close:"),
709                                         _("Close Table"), titles,
710                                         size = (400, 300),
711                                         style = wxDEFAULT_DIALOG_STYLE |
712                                                 wxRESIZE_BORDER)
713            if dlg.ShowModal() == wxID_OK:
714                for i in dlg.GetValue():
715                    self.application.session.RemoveTable(lst[i][1])
716    
717    
718        def TableShow(self):
719            """Offer a multi-selection dialog for tables to be displayed
720    
721            The windows for the selected tables are opened or brought to
722            the front.
723            """
724            tables = self.application.session.Tables()
725    
726            lst = [(t.Title(), t) for t in tables]
727            lst.sort()
728            titles = [i[0] for i in lst]
729            dlg = wxMultipleChoiceDialog(self, _("Pick the table to show:"),
730                                         _("Show Table"), titles,
731                                         size = (400,300),
732                                         style = wxDEFAULT_DIALOG_STYLE |
733                                                 wxRESIZE_BORDER)
734            if (dlg.ShowModal() == wxID_OK):
735                for i in dlg.GetValue():
736                    # XXX: if the table belongs to a layer, open a
737                    # LayerTableFrame instead of QueryTableFrame
738                    self.ShowTableView(lst[i][1])
739    
740        def TableJoin(self):
741            dlg = JoinDialog(self, _("Join Tables"), self.application.session)
742            dlg.ShowModal()
743    
744        def ShowTableView(self, table):
745            """Open a table view for the table and optionally"""
746            name = "table_view%d" % id(table)
747            dialog = self.get_open_dialog(name)
748            if dialog is None:
749                dialog = tableview.QueryTableFrame(self, name,
750                                                   _("Table: %s") % table.Title(),
751                                                   table)
752                self.add_dialog(name, dialog)
753                dialog.Show(True)
754            # FIXME: else bring dialog to front
755    
756        def TableRename(self):
757            """Let the user rename a table"""
758    
759            # First, let the user select a table
760            tables = self.application.session.Tables()
761            lst = [(t.Title(), t) for t in tables]
762            lst.sort()
763            titles = [i[0] for i in lst]
764            dlg = wxMultipleChoiceDialog(self, _("Pick the table to rename:"),
765                                         _("Rename Table"), titles,
766                                         size = (400,300),
767                                         style = wxDEFAULT_DIALOG_STYLE |
768                                                 wxRESIZE_BORDER)
769            if (dlg.ShowModal() == wxID_OK):
770                to_rename = [lst[i][1] for i in dlg.GetValue()]
771                dlg.Destroy()
772            else:
773                to_rename = []
774    
775            # Second, let the user rename the layers
776            for table in to_rename:
777                dlg = wxTextEntryDialog(self, "Table Title: ", "Rename Table",
778                                        table.Title())
779                try:
780                    if dlg.ShowModal() == wxID_OK:
781                        title = dlg.GetValue()
782                        if title != "":
783                            table.SetTitle(title)
784    
785                            # Make sure the session is marked as modified.
786                            # FIXME: This should be handled automatically,
787                            # but that requires more changes to the tables
788                            # than I have time for currently.
789                            self.application.session.changed()
790                finally:
791                    dlg.Destroy()
792    
793    
794      def ZoomInTool(self):      def ZoomInTool(self):
795          self.canvas.ZoomInTool()          self.canvas.ZoomInTool()
# Line 368  class MainWindow(wxFrame): Line 801  class MainWindow(wxFrame):
801          self.canvas.PanTool()          self.canvas.PanTool()
802    
803      def IdentifyTool(self):      def IdentifyTool(self):
         if self.identify_view is None:  
             self.identify_view = identifyview.IdentifyView(self, main.app)  
             self.identify_view.Show(true)  
804          self.canvas.IdentifyTool()          self.canvas.IdentifyTool()
805            self.identify_view_on_demand(None, None)
806    
807      def LabelTool(self):      def LabelTool(self):
808          self.canvas.LabelTool()          self.canvas.LabelTool()
# Line 379  class MainWindow(wxFrame): Line 810  class MainWindow(wxFrame):
810      def FullExtent(self):      def FullExtent(self):
811          self.canvas.FitMapToWindow()          self.canvas.FitMapToWindow()
812    
813        def FullLayerExtent(self):
814            self.canvas.FitLayerToWindow(self.current_layer())
815    
816        def FullSelectionExtent(self):
817            self.canvas.FitSelectedToWindow()
818    
819        def ExportMap(self):
820            self.canvas.Export()
821    
822      def PrintMap(self):      def PrintMap(self):
823          self.canvas.Print()          self.canvas.Print()
824    
825        def RenameMap(self):
826            dlg = wxTextEntryDialog(self, "Map Title: ", "Rename Map",
827                                    self.Map().Title())
828            if dlg.ShowModal() == wxID_OK:
829                title = dlg.GetValue()
830                if title != "":
831                    self.Map().SetTitle(title)
832                    self.__SetTitle(title)
833    
834            dlg.Destroy()
835    
836        def RenameLayer(self):
837            """Let the user rename the currently selected layer"""
838            layer = self.current_layer()
839            if layer is not None:
840                dlg = wxTextEntryDialog(self, "Layer Title: ", "Rename Layer",
841                                        layer.Title())
842                try:
843                    if dlg.ShowModal() == wxID_OK:
844                        title = dlg.GetValue()
845                        if title != "":
846                            layer.SetTitle(title)
847                finally:
848                    dlg.Destroy()
849    
850        def identify_view_on_demand(self, layer, shapes):
851            """Subscribed to the canvas' SHAPES_SELECTED message
852    
853            If the current tool is the identify tool, at least one shape is
854            selected and the identify dialog is not shown, show the dialog.
855            """
856            # If the selection has become empty we don't need to do
857            # anything. Otherwise it could happen that the dialog was popped
858            # up when the selection became empty, e.g. when a new selection
859            # is opened while the identify tool is active and dialog had
860            # been closed
861            if not shapes:
862                return
863    
864            name = "identify_view"
865            if self.canvas.CurrentTool() == "IdentifyTool":
866                if not self.dialog_open(name):
867                    dialog = identifyview.IdentifyView(self, name)
868                    self.add_dialog(name, dialog)
869                    dialog.Show(True)
870                else:
871                    # FIXME: bring dialog to front?
872                    pass
873    
874        def __SetTitle(self, title):
875            self.SetTitle("Thuban - " + title)
876    
877  #  #
878  # Define all the commands available in the main window  # Define all the commands available in the main window
# Line 390  class MainWindow(wxFrame): Line 881  class MainWindow(wxFrame):
881    
882  # Helper functions to define common command implementations  # Helper functions to define common command implementations
883  def call_method(context, methodname, *args):  def call_method(context, methodname, *args):
884      """Call the context's method methodname with args *args"""      """Call the mainwindow's method methodname with args *args"""
885      apply(getattr(context, methodname), args)      apply(getattr(context.mainwindow, methodname), args)
886    
887  def _method_command(name, title, method, helptext = "", sensitive = None):  def _method_command(name, title, method, helptext = "",
888      """Add a command implemented by a method of the context object"""                      icon = "", sensitive = None, checked = None):
889        """Add a command implemented by a method of the mainwindow object"""
890      registry.Add(Command(name, title, call_method, args=(method,),      registry.Add(Command(name, title, call_method, args=(method,),
891                           helptext = helptext, sensitive = sensitive))                           helptext = helptext, icon = icon,
892                             sensitive = sensitive, checked = checked))
893    
894    def make_check_current_tool(toolname):
895        """Return a function that tests if the currently active tool is toolname
896    
897        The returned function can be called with the context and returns
898        true iff the currently active tool's name is toolname. It's directly
899        usable as the 'checked' callback of a command.
900        """
901        def check_current_tool(context, name=toolname):
902            return context.mainwindow.canvas.CurrentTool() == name
903        return check_current_tool
904    
905  def _tool_command(name, title, method, toolname, helptext = "",  def _tool_command(name, title, method, toolname, helptext = "",
906                    icon = ""):                    icon = "", sensitive = None):
907      """Add a tool command"""      """Add a tool command"""
908      def check_current_tool(context, name=toolname):      registry.Add(ToolCommand(name, title, call_method, args=(method,),
909          return context.canvas.CurrentTool() == name                               helptext = helptext, icon = icon,
910      registry.Add(Command(name, title, call_method, args=(method,),                               checked = make_check_current_tool(toolname),
911                           helptext = helptext, icon = icon,                               sensitive = sensitive))
                          checked = check_current_tool))  
912    
913  def _has_selected_layer(context):  def _has_selected_layer(context):
914      """Return true if a layer is selected in the context"""      """Return true if a layer is selected in the context"""
915      return context.has_selected_layer()      return context.mainwindow.has_selected_layer()
916    
917    def _has_selected_shapes(context):
918        """Return true if a layer is selected in the context"""
919        return context.mainwindow.has_selected_shapes()
920    
921    def _can_remove_layer(context):
922        return context.mainwindow.CanRemoveLayer()
923    
924    def _has_tree_window_shown(context):
925        """Return true if the tree window is shown"""
926        return context.mainwindow.SessionTreeShown()
927    
928    def _has_visible_map(context):
929        """Return true iff theres a visible map in the mainwindow.
930    
931        A visible map is a map with at least one visible layer."""
932        map = context.mainwindow.Map()
933        if map is not None:
934            for layer in map.Layers():
935                if layer.Visible():
936                    return 1
937        return 0
938    
939    def _has_legend_shown(context):
940        """Return true if the legend window is shown"""
941        return context.mainwindow.LegendShown()
942    
943    def _has_gdal_support(context):
944        """Return True if the GDAL is available"""
945        return Thuban.Model.resource.has_gdal_support()
946    
947  # File menu  # File menu
948  _method_command("new_session", "&New Session", "NewSession")  _method_command("new_session", _("&New Session"), "NewSession",
949  _method_command("open_session", "&Open Session", "OpenSession")                  helptext = _("Start a new session"))
950  _method_command("save_session", "&Save Session", "SaveSession")  _method_command("open_session", _("&Open Session..."), "OpenSession",
951  _method_command("save_session_as", "Save Session &As", "SaveSessionAs")                  helptext = _("Open a session file"))
952  _method_command("exit", "&Exit", "Exit")  _method_command("save_session", _("&Save Session"), "SaveSession",
953                    helptext =_("Save this session to the file it was opened from"))
954    _method_command("save_session_as", _("Save Session &As..."), "SaveSessionAs",
955                    helptext = _("Save this session to a new file"))
956    _method_command("toggle_session_tree", _("Session &Tree"), "ToggleSessionTree",
957                    checked = _has_tree_window_shown,
958                    helptext = _("Toggle on/off the session tree analysis window"))
959    _method_command("toggle_legend", _("Legend"), "ToggleLegend",
960                    checked = _has_legend_shown,
961                    helptext = _("Toggle Legend on/off"))
962    _method_command("exit", _("E&xit"), "Exit",
963                    helptext = _("Finish working with Thuban"))
964    
965  # Help menu  # Help menu
966  _method_command("help_about", "&About", "About")  _method_command("help_about", _("&About..."), "About",
967                    helptext = _("Info about Thuban authors, version and modules"))
968    
969    
970  # Map menu  # Map menu
971  _method_command("map_projection", "Pro&jection", "Projection")  _method_command("map_projection", _("Pro&jection..."), "MapProjection",
972                    helptext = _("Set or change the map projection"))
973    
974  _tool_command("map_zoom_in_tool", "&Zoom in", "ZoomInTool", "ZoomInTool",  _tool_command("map_zoom_in_tool", _("&Zoom in"), "ZoomInTool", "ZoomInTool",
975                helptext = "Switch to map-mode 'zoom-in'", icon = "zoom_in")                helptext = _("Switch to map-mode 'zoom-in'"), icon = "zoom_in",
976  _tool_command("map_zoom_out_tool", "Zoom &out", "ZoomOutTool", "ZoomOutTool",                sensitive = _has_visible_map)
977                helptext = "Switch to map-mode 'zoom-out'", icon = "zoom_out")  _tool_command("map_zoom_out_tool", _("Zoom &out"), "ZoomOutTool", "ZoomOutTool",
978  _tool_command("map_pan_tool", "&Pan", "PanTool", "PanTool",                helptext = _("Switch to map-mode 'zoom-out'"), icon = "zoom_out",
979                helptext = "Switch to map-mode 'pan'", icon = "pan")                sensitive = _has_visible_map)
980  _tool_command("map_identify_tool", "&Identify", "IdentifyTool", "IdentifyTool",  _tool_command("map_pan_tool", _("&Pan"), "PanTool", "PanTool",
981                helptext = "Switch to map-mode 'identify'", icon = "identify")                helptext = _("Switch to map-mode 'pan'"), icon = "pan",
982  _tool_command("map_label_tool", "&Label", "LabelTool", "LabelTool",                sensitive = _has_visible_map)
983                helptext = "Add/Remove labels", icon = "label")  _tool_command("map_identify_tool", _("&Identify"), "IdentifyTool",
984  _method_command("map_full_extent", "&Full extent", "FullExtent")                "IdentifyTool",
985  _method_command("map_print", "Prin&t", "PrintMap", helptext = "Print the map")                helptext = _("Switch to map-mode 'identify'"), icon = "identify",
986                  sensitive = _has_visible_map)
987    _tool_command("map_label_tool", _("&Label"), "LabelTool", "LabelTool",
988                  helptext = _("Add/Remove labels"), icon = "label",
989                  sensitive = _has_visible_map)
990    _method_command("map_full_extent", _("&Full extent"), "FullExtent",
991                   helptext = _("Zoom to the full map extent"), icon = "fullextent",
992                  sensitive = _has_visible_map)
993    _method_command("layer_full_extent", _("&Full layer extent"), "FullLayerExtent",
994                    helptext = _("Zoom to the full layer extent"),
995                    icon = "fulllayerextent", sensitive = _has_selected_layer)
996    _method_command("selected_full_extent", _("&Full selection extent"),
997                    "FullSelectionExtent",
998                    helptext = _("Zoom to the full selection extent"),
999                    icon = "fullselextent", sensitive = _has_selected_shapes)
1000    _method_command("map_export", _("E&xport"), "ExportMap",
1001                    helptext = _("Export the map to file"))
1002    _method_command("map_print", _("Prin&t"), "PrintMap",
1003                    helptext = _("Print the map"))
1004    _method_command("map_rename", _("&Rename..."), "RenameMap",
1005                    helptext = _("Rename the map"))
1006    _method_command("layer_add", _("&Add Layer..."), "AddLayer",
1007                    helptext = _("Add a new layer to the map"))
1008    _method_command("rasterlayer_add", _("&Add Image Layer..."), "AddRasterLayer",
1009                    helptext = _("Add a new image layer to the map"),
1010                    sensitive = _has_gdal_support)
1011    _method_command("layer_remove", _("&Remove Layer"), "RemoveLayer",
1012                    helptext = _("Remove selected layer"),
1013                    sensitive = _can_remove_layer)
1014    
1015  # Layer menu  # Layer menu
1016  _method_command("layer_add", "&Add", "AddLayer",  _method_command("layer_projection", _("Pro&jection..."), "LayerProjection",
1017                  helptext = "Add a new layer to active map")                  sensitive = _has_selected_layer,
1018  _method_command("layer_remove", "&Remove", "RemoveLayer",                  helptext = _("Specify projection for selected layer"))
1019                  helptext = "Remove selected layer(s)",  _method_command("layer_duplicate", _("&Duplicate"), "DuplicateLayer",
1020                    helptext = _("Duplicate selected layer"),
1021              sensitive = lambda context: context.mainwindow.CanDuplicateLayer())
1022    _method_command("layer_rename", _("Re&name ..."), "RenameLayer",
1023                    helptext = _("Rename selected layer"),
1024                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
1025  _method_command("layer_fill_color", "&Fill Color", "LayerFillColor",  _method_command("layer_raise", _("&Raise"), "RaiseLayer",
1026                  helptext = "Set the fill color of selected layer(s)",                  helptext = _("Raise selected layer"),
1027                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
1028  _method_command("layer_transparent_fill", "&Transparent Fill",  _method_command("layer_lower", _("&Lower"), "LowerLayer",
1029                  "LayerTransparentFill",                  helptext = _("Lower selected layer"),
                 helptext = "Do not fill the selected layer(s)",  
1030                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
1031  _method_command("layer_ourline_color", "&Outline Color", "LayerOutlineColor",  _method_command("layer_show", _("&Show"), "ShowLayer",
1032                  helptext = "Set the outline color of selected layer(s)",                  helptext = _("Make selected layer visible"),
1033                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
1034  _method_command("layer_no_outline", "&No Outline", "LayerNoOutline",  _method_command("layer_hide", _("&Hide"), "HideLayer",
1035                  helptext = "Do not draw the outline of the selected layer(s)",                  helptext = _("Make selected layer unvisible"),
1036                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
1037  _method_command("layer_raise", "&Raise", "RaiseLayer",  _method_command("layer_show_table", _("Show Ta&ble"), "LayerShowTable",
1038                  helptext = "Raise selected layer(s)",                  helptext = _("Show the selected layer's table"),
                 sensitive = _has_selected_layer)  
 _method_command("layer_lower", "&Lower", "LowerLayer",  
                 helptext = "Lower selected layer(s)",  
                 sensitive = _has_selected_layer)  
 _method_command("layer_show", "&Show", "ShowLayer",  
                 helptext = "Make selected layer(s) visible",  
                 sensitive = _has_selected_layer)  
 _method_command("layer_hide", "&Hide", "HideLayer",  
                 helptext = "Make selected layer(s) unvisible",  
                 sensitive = _has_selected_layer)  
 _method_command("layer_show_table", "Show Ta&ble", "LayerShowTable",  
                 helptext = "Show the selected layer's table",  
1039                  sensitive = _has_selected_layer)                  sensitive = _has_selected_layer)
1040    _method_command("layer_properties", _("&Properties..."), "LayerEditProperties",
1041                    sensitive = _has_selected_layer,
1042                    helptext = _("Edit the properties of the selected layer"))
1043    _method_command("layer_jointable", _("&Join Table..."), "LayerJoinTable",
1044                    sensitive = _has_selected_layer,
1045                    helptext = _("Join and attach a table to the selected layer"))
1046    
1047    def _can_unjoin(context):
1048        """Return whether the Layer/Unjoin command can be executed.
1049    
1050        This is the case if a layer is selected and that layer has a
1051        shapestore that has an original shapestore.
1052        """
1053        layer = context.mainwindow.SelectedLayer()
1054        if layer is None:
1055            return 0
1056        getstore = getattr(layer, "ShapeStore", None)
1057        if getstore is not None:
1058            return getstore().OrigShapeStore() is not None
1059        else:
1060            return 0
1061    _method_command("layer_unjointable", _("&Unjoin Table..."), "LayerUnjoinTable",
1062                    sensitive = _can_unjoin,
1063                    helptext = _("Undo the last join operation"))
1064    
1065    
1066    def _has_tables(context):
1067        return bool(context.session.Tables())
1068    
1069    # Table menu
1070    _method_command("table_open", _("&Open..."), "TableOpen",
1071                    helptext = _("Open a DBF-table from a file"))
1072    _method_command("table_close", _("&Close..."), "TableClose",
1073           sensitive = lambda context: bool(context.session.UnreferencedTables()),
1074                    helptext = _("Close one or more tables from a list"))
1075    _method_command("table_rename", _("&Rename..."), "TableRename",
1076                    sensitive = _has_tables,
1077                    helptext = _("Rename one or more tables"))
1078    _method_command("table_show", _("&Show..."), "TableShow",
1079                    sensitive = _has_tables,
1080                    helptext = _("Show one or more tables in a dialog"))
1081    _method_command("table_join", _("&Join..."), "TableJoin",
1082                    sensitive = _has_tables,
1083                    helptext = _("Join two tables creating a new one"))
1084    
1085    #  Export only under Windows ...
1086    map_menu = ["layer_add", "rasterlayer_add", "layer_remove",
1087                            None,
1088                            "map_rename",
1089                            "map_projection",
1090                            None,
1091                            "map_zoom_in_tool", "map_zoom_out_tool",
1092                            "map_pan_tool",
1093                            "map_full_extent",
1094                            "layer_full_extent",
1095                            "selected_full_extent",
1096                            None,
1097                            "map_identify_tool", "map_label_tool",
1098                            None,
1099                            "toggle_legend",
1100                            None]
1101    if wxPlatform == '__WXMSW__':
1102        map_menu.append("map_export")
1103    map_menu.append("map_print")
1104    
1105    # the menu structure
1106    main_menu = Menu("<main>", "<main>",
1107                     [Menu("file", _("&File"),
1108                           ["new_session", "open_session", None,
1109                            "save_session", "save_session_as", None,
1110                            "toggle_session_tree", None,
1111                            "exit"]),
1112                      Menu("map", _("&Map"), map_menu),
1113                      Menu("layer", _("&Layer"),
1114                           ["layer_rename", "layer_duplicate",
1115                            None,
1116                            "layer_raise", "layer_lower",
1117                            None,
1118                            "layer_show", "layer_hide",
1119                            None,
1120                            "layer_projection",
1121                            None,
1122                            "layer_show_table",
1123                            "layer_jointable",
1124                            "layer_unjointable",
1125                            None,
1126                            "layer_properties"]),
1127                      Menu("table", _("&Table"),
1128                           ["table_open", "table_close", "table_rename",
1129                           None,
1130                           "table_show",
1131                           None,
1132                           "table_join"]),
1133                      Menu("help", _("&Help"),
1134                           ["help_about"])])
1135    
1136    # the main toolbar
1137    
1138    main_toolbar = Menu("<toolbar>", "<toolbar>",
1139                        ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
1140                         "map_full_extent",
1141                         "layer_full_extent",
1142                         "selected_full_extent",
1143                         None,
1144                         "map_identify_tool", "map_label_tool"])
1145    

Legend:
Removed from v.6  
changed lines
  Added in v.1293

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26