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

Legend:
Removed from v.130  
changed lines
  Added in v.1219

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26