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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2531 - (hide annotations)
Thu Jan 20 18:47:26 2005 UTC (20 years, 1 month ago) by bernhard
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 51773 byte(s)
	* Thuban/UI/mainwindow.py(view_position_changed): Added docstring
	and comment that the warning code here is a hack.

1 bh 2061 # Copyright (C) 2001, 2002, 2003, 2004 by Intevation GmbH
2 bh 6 # Authors:
3     # Jan-Oliver Wagner <[email protected]>
4     # Bernhard Herzog <[email protected]>
5 frank 911 # Frank Koormann <[email protected]>
6 bh 6 #
7     # This program is free software under the GPL (>=v2)
8     # Read the file COPYING coming with Thuban for details.
9    
10     """
11     The main window
12     """
13    
14     __version__ = "$Revision$"
15 bh 1644 # $Source$
16     # $Id$
17 bh 6
18 bh 188 import os
19 bh 1094 import copy
20 bh 6
21     from wxPython.wx import *
22    
23     import Thuban
24 frank 923
25 jan 374 from Thuban import _
26 bh 1782 from Thuban.Model.messages import TITLE_CHANGED
27 bh 188 from Thuban.Model.session import create_empty_session
28 jonathan 937 from Thuban.Model.layer import Layer, RasterLayer
29 bh 1625 from Thuban.Model.postgisdb import PostGISShapeStore, has_postgis_support
30 jan 1155 # XXX: replace this by
31     # from wxPython.lib.dialogs import wxMultipleChoiceDialog
32     # when Thuban does not support wxPython 2.4.0 any more.
33     from Thuban.UI.multiplechoicedialog import wxMultipleChoiceDialog
34    
35 bh 6 import view
36     import tree
37     import tableview, identifyview
38 jonathan 550 import legend
39 bh 188 from menu import Menu
40 bh 6
41 bh 222 from context import Context
42 bh 357 from command import registry, Command, ToolCommand
43 bh 1464 from messages import LAYER_SELECTED, SHAPES_SELECTED, VIEW_POSITION, \
44     MAP_REPLACED
45 jonathan 1309 from about import About
46 bh 6
47 bh 704 from Thuban.UI.dock import DockFrame
48 jonathan 879 from Thuban.UI.join import JoinDialog
49 bh 1648 from Thuban.UI.dbdialog import DBFrame, DBDialog, ChooseDBTableDialog
50 jonathan 653 import resource
51 jonathan 1164 import Thuban.Model.resource
52 jonathan 563
53 jonathan 713 import projdialog
54 jonathan 653
55 joey 2365 from Thuban.Lib.classmapper import ClassMapper
56 jonathan 1309
57 joey 2365 layer_properties_dialogs = ClassMapper()
58    
59 jonathan 573 class MainWindow(DockFrame):
60 bh 6
61 bh 535 # Some messages that can be subscribed/unsubscribed directly through
62     # the MapCanvas come in fact from other objects. This is a map to
63     # map those messages to the names of the instance variables they
64     # actually come from. This delegation is implemented in the
65     # Subscribe and unsubscribed methods
66     delegated_messages = {LAYER_SELECTED: "canvas",
67 bh 1464 SHAPES_SELECTED: "canvas",
68     MAP_REPLACED: "canvas"}
69 bh 535
70     # Methods delegated to some instance variables. The delegation is
71     # implemented in the __getattr__ method.
72     delegated_methods = {"SelectLayer": "canvas",
73     "SelectShapes": "canvas",
74 bh 1074 "SelectedLayer": "canvas",
75 jonathan 879 "SelectedShapes": "canvas",
76 bh 535 }
77    
78 bh 235 def __init__(self, parent, ID, title, application, interactor,
79 bh 238 initial_message = None, size = wxSize(-1, -1)):
80 jonathan 573 DockFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
81     #wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, size)
82 bh 6
83 bh 227 self.application = application
84 bh 37
85 bh 6 self.CreateStatusBar()
86 bh 235 if initial_message:
87     self.SetStatusText(initial_message)
88 bh 6
89     self.identify_view = None
90    
91     self.init_ids()
92    
93 bh 191 # creat the menubar from the main_menu description
94 bh 188 self.SetMenuBar(self.build_menu_bar(main_menu))
95 bh 6
96 bh 191 # Similarly, create the toolbar from main_toolbar
97     toolbar = self.build_toolbar(main_toolbar)
98 bh 13 # call Realize to make sure that the tools appear.
99     toolbar.Realize()
100 bh 6
101 jonathan 563
102 bh 6 # Create the map canvas
103 bh 535 canvas = view.MapCanvas(self, -1)
104 bh 123 canvas.Subscribe(VIEW_POSITION, self.view_position_changed)
105 bh 535 canvas.Subscribe(SHAPES_SELECTED, self.identify_view_on_demand)
106 bh 6 self.canvas = canvas
107 bh 1782 self.canvas.Subscribe(TITLE_CHANGED, self.title_changed)
108 bh 6
109 jonathan 573 self.SetMainWindow(self.canvas)
110    
111 jonathan 563 self.SetAutoLayout(True)
112    
113 bh 31 self.init_dialogs()
114    
115 jonathan 1233 self.ShowLegend()
116    
117 frank 951 EVT_CLOSE(self, self.OnClose)
118 bh 6
119 bh 535 def Subscribe(self, channel, *args):
120     """Subscribe a function to a message channel.
121    
122     If channel is one of the delegated messages call the appropriate
123     object's Subscribe method. Otherwise do nothing.
124     """
125     if channel in self.delegated_messages:
126     object = getattr(self, self.delegated_messages[channel])
127     object.Subscribe(channel, *args)
128     else:
129     print "Trying to subscribe to unsupported channel %s" % channel
130    
131     def Unsubscribe(self, channel, *args):
132     """Unsubscribe a function from a message channel.
133    
134     If channel is one of the delegated messages call the appropriate
135     object's Unsubscribe method. Otherwise do nothing.
136     """
137     if channel in self.delegated_messages:
138     object = getattr(self, self.delegated_messages[channel])
139 bh 2017 try:
140     object.Unsubscribe(channel, *args)
141     except wxPyDeadObjectError:
142     # The object was a wxObject and has already been
143     # destroyed. Hopefully it has unsubscribed all its
144     # subscribers already so that it's OK if we do nothing
145     # here
146     pass
147 bh 535
148     def __getattr__(self, attr):
149     """If attr is one of the delegated methods return that method
150    
151     Otherwise raise AttributeError.
152     """
153     if attr in self.delegated_methods:
154     return getattr(getattr(self, self.delegated_methods[attr]), attr)
155     raise AttributeError(attr)
156    
157 bh 6 def init_ids(self):
158     """Initialize the ids"""
159     self.current_id = 6000
160     self.id_to_name = {}
161     self.name_to_id = {}
162 bh 193 self.events_bound = {}
163 bh 6
164     def get_id(self, name):
165     """Return the wxWindows id for the command named name.
166    
167     Create a new one if there isn't one yet"""
168     ID = self.name_to_id.get(name)
169     if ID is None:
170     ID = self.current_id
171     self.current_id = self.current_id + 1
172     self.name_to_id[name] = ID
173     self.id_to_name[ID] = name
174     return ID
175 bh 188
176 bh 193 def bind_command_events(self, command, ID):
177     """Bind the necessary events for the given command and ID"""
178     if not self.events_bound.has_key(ID):
179     # the events haven't been bound yet
180     EVT_MENU(self, ID, self.invoke_command)
181     if command.IsDynamic():
182     EVT_UPDATE_UI(self, ID, self.update_command_ui)
183    
184 bh 188 def build_menu_bar(self, menudesc):
185     """Build and return the menu bar from the menu description"""
186     menu_bar = wxMenuBar()
187    
188     for item in menudesc.items:
189     # here the items must all be Menu instances themselves
190     menu_bar.Append(self.build_menu(item), item.title)
191    
192     return menu_bar
193    
194     def build_menu(self, menudesc):
195 bh 314 """Return a wxMenu built from the menu description menudesc"""
196 bh 188 wxmenu = wxMenu()
197     last = None
198     for item in menudesc.items:
199     if item is None:
200     # a separator. Only add one if the last item was not a
201     # separator
202     if last is not None:
203     wxmenu.AppendSeparator()
204     elif isinstance(item, Menu):
205     # a submenu
206     wxmenu.AppendMenu(wxNewId(), item.title, self.build_menu(item))
207     else:
208     # must the name the name of a command
209     self.add_menu_command(wxmenu, item)
210     last = item
211     return wxmenu
212    
213 bh 191 def build_toolbar(self, toolbardesc):
214     """Build and return the main toolbar window from a toolbar description
215    
216     The parameter should be an instance of the Menu class but it
217     should not contain submenus.
218     """
219     toolbar = self.CreateToolBar(wxTB_3DBUTTONS)
220    
221     # set the size of the tools' bitmaps. Not needed on wxGTK, but
222     # on Windows, although it doesn't work very well there. It seems
223     # that only 16x16 icons are really supported on windows.
224     # We probably shouldn't hardwire the bitmap size here.
225     toolbar.SetToolBitmapSize(wxSize(24, 24))
226    
227     for item in toolbardesc.items:
228     if item is None:
229     toolbar.AddSeparator()
230     else:
231     # assume it's a string.
232     self.add_toolbar_command(toolbar, item)
233    
234     return toolbar
235    
236 bh 6 def add_menu_command(self, menu, name):
237     """Add the command with name name to the menu menu.
238    
239     If name is None, add a separator.
240     """
241     if name is None:
242     menu.AppendSeparator()
243     else:
244     command = registry.Command(name)
245     if command is not None:
246     ID = self.get_id(name)
247     menu.Append(ID, command.Title(), command.HelpText(),
248     command.IsCheckCommand())
249 bh 193 self.bind_command_events(command, ID)
250 bh 6 else:
251 jan 374 print _("Unknown command %s") % name
252 bh 6
253     def add_toolbar_command(self, toolbar, name):
254     """Add the command with name name to the toolbar toolbar.
255    
256     If name is None, add a separator.
257     """
258     # Assume that all toolbar commands are also menu commmands so
259     # that we don't have to add the event handlers here
260     if name is None:
261     toolbar.AddSeparator()
262     else:
263     command = registry.Command(name)
264     if command is not None:
265     ID = self.get_id(name)
266 jonathan 653 bitmap = resource.GetBitmapResource(command.Icon(),
267     wxBITMAP_TYPE_XPM)
268 bh 6 toolbar.AddTool(ID, bitmap,
269     shortHelpString = command.HelpText(),
270     isToggle = command.IsCheckCommand())
271 bh 193 self.bind_command_events(command, ID)
272 bh 6 else:
273 jan 374 print _("Unknown command %s") % name
274 bh 6
275 bh 281 def Context(self):
276     """Return the context object for a command invoked from this window
277     """
278     return Context(self.application, self.application.Session(), self)
279    
280 bh 6 def invoke_command(self, event):
281     name = self.id_to_name.get(event.GetId())
282     if name is not None:
283     command = registry.Command(name)
284 bh 281 command.Execute(self.Context())
285 bh 6 else:
286 jan 374 print _("Unknown command ID %d") % event.GetId()
287 bh 6
288     def update_command_ui(self, event):
289     #print "update_command_ui", self.id_to_name[event.GetId()]
290 bh 281 context = self.Context()
291 bh 6 command = registry.Command(self.id_to_name[event.GetId()])
292     if command is not None:
293 bh 357 sensitive = command.Sensitive(context)
294     event.Enable(sensitive)
295     if command.IsTool() and not sensitive and command.Checked(context):
296     # When a checked tool command is disabled deselect all
297     # tools. Otherwise the tool would remain active but it
298     # might lead to errors if the tools stays active. This
299     # problem occurred in GREAT-ER and this fixes it, but
300     # it's not clear to me whether this is really the best
301     # way to do it (BH, 20021206).
302     self.canvas.SelectTool(None)
303 bh 222 event.SetText(command.DynText(context))
304 bh 13 if command.IsCheckCommand():
305 bh 357 event.Check(command.Checked(context))
306 bh 6
307 bh 20 def RunMessageBox(self, title, text, flags = wxOK | wxICON_INFORMATION):
308 bh 181 """Run a modal message box with the given text, title and flags
309 bh 20 and return the result"""
310     dlg = wxMessageDialog(self, text, title, flags)
311 bh 316 dlg.CenterOnParent()
312 bh 20 result = dlg.ShowModal()
313     dlg.Destroy()
314     return result
315    
316 bh 31 def init_dialogs(self):
317     """Initialize the dialog handling"""
318     # The mainwindow maintains a dict mapping names to open
319     # non-modal dialogs. The dialogs are put into this dict when
320     # they're created and removed when they're closed
321     self.dialogs = {}
322    
323     def add_dialog(self, name, dialog):
324     if self.dialogs.has_key(name):
325 jan 374 raise RuntimeError(_("The Dialog named %s is already open") % name)
326 bh 31 self.dialogs[name] = dialog
327    
328     def dialog_open(self, name):
329     return self.dialogs.has_key(name)
330    
331     def remove_dialog(self, name):
332     del self.dialogs[name]
333    
334     def get_open_dialog(self, name):
335     return self.dialogs.get(name)
336    
337 bh 123 def view_position_changed(self):
338 bernhard 2531 """Put current position in status bar after a mouse move."""
339 bh 123 pos = self.canvas.CurrentPosition()
340     if pos is not None:
341     text = "(%10.10g, %10.10g)" % pos
342     else:
343     text = ""
344 bernhard 2531 # XXX This is a hack until we find a better place for this code.
345     # (BER 20050120)
346     # BH wrote (20050120):
347     # this branch is only executed when the mouse
348     # leaves the canvas window, so it's not that often [..]
349     # [Here] not the right place to put this code.
350     # I don't have a better solution at hand,
351     # but the view_position_changed is only there to update
352     # the current position. If other information is to
353     # be shown in the status bar it should
354     # be handled in a different way and
355     # by other methods.
356     #
357     # The status bar widget supports some kind of stack of texts.
358     # maybe that can be used to distinguis
359     # between short-lived information such as the mouse position
360     # and more permanent information such as the hint
361     # about the projections.
362 russell 2529 map = self.canvas.Map()
363     for layer in map.layers:
364     bbox = layer.LatLongBoundingBox()
365     if bbox:
366     left, bottom, right, top = bbox
367     if not (-180 <= left <= 180 and
368     -180 <= right <= 180 and
369     -90 <= top <= 90 and
370     -90 <= bottom <= 90):
371     text = ("Select '"+layer.title+"' and pick a " +
372     "projection using Layer/Projection...")
373     break
374    
375 bh 321 self.set_position_text(text)
376    
377     def set_position_text(self, text):
378     """Set the statusbar text showing the current position.
379    
380     By default the text is shown in field 0 of the status bar.
381     Override this method in derived classes to put it into a
382     different field of the statusbar.
383     """
384 bh 123 self.SetStatusText(text)
385    
386 joey 2364 def OpenOrRaiseDialog(self, name, dialog_class, *args, **kw):
387     """
388     Open or raise a dialog.
389    
390     If a dialog with the denoted name does already exist it is
391     raised. Otherwise a new dialog, an instance of dialog_class,
392     is created, inserted into the main list and displayed.
393     """
394     dialog = self.get_open_dialog(name)
395    
396     if dialog is None:
397     dialog = dialog_class(self, name, *args, **kw)
398     self.add_dialog(name, dialog)
399     dialog.Show(True)
400     else:
401     dialog.Raise()
402    
403 bh 58 def save_modified_session(self, can_veto = 1):
404     """If the current session has been modified, ask the user
405     whether to save it and do so if requested. Return the outcome of
406     the dialog (either wxID_OK, wxID_CANCEL or wxID_NO). If the
407     dialog wasn't run return wxID_NO.
408    
409     If the can_veto parameter is true (default) the dialog includes
410     a cancel button, otherwise not.
411     """
412 bh 227 if self.application.session.WasModified():
413 bh 58 flags = wxYES_NO | wxICON_QUESTION
414     if can_veto:
415     flags = flags | wxCANCEL
416 jan 374 result = self.RunMessageBox(_("Exit"),
417     _("The session has been modified."
418 bh 58 " Do you want to save it?"),
419     flags)
420     if result == wxID_YES:
421     self.SaveSession()
422     else:
423     result = wxID_NO
424     return result
425    
426 bh 6 def NewSession(self):
427 jonathan 937 if self.save_modified_session() != wxID_CANCEL:
428     self.application.SetSession(create_empty_session())
429 bh 6
430     def OpenSession(self):
431 jonathan 937 if self.save_modified_session() != wxID_CANCEL:
432 frank 2051 dlg = wxFileDialog(self, _("Open Session"),
433     self.application.Path("data"), "",
434 jonathan 937 "Thuban Session File (*.thuban)|*.thuban",
435     wxOPEN)
436     if dlg.ShowModal() == wxID_OK:
437 bh 1648 self.application.OpenSession(dlg.GetPath(),
438     self.run_db_param_dialog)
439 frank 2051 self.application.SetPath("data", dlg.GetPath())
440 jonathan 937 dlg.Destroy()
441 bh 6
442 bh 1648 def run_db_param_dialog(self, parameters, message):
443     dlg = DBDialog(self, _("DB Connection Parameters"), parameters,
444     message)
445     return dlg.RunDialog()
446    
447 bh 6 def SaveSession(self):
448 bh 227 if self.application.session.filename == None:
449 jan 102 self.SaveSessionAs()
450 jonathan 487 else:
451     self.application.SaveSession()
452 bh 6
453     def SaveSessionAs(self):
454 frank 2051 dlg = wxFileDialog(self, _("Save Session As"),
455     self.application.Path("data"), "",
456 jonathan 879 "Thuban Session File (*.thuban)|*.thuban",
457     wxSAVE|wxOVERWRITE_PROMPT)
458 bh 6 if dlg.ShowModal() == wxID_OK:
459 bh 227 self.application.session.SetFilename(dlg.GetPath())
460     self.application.SaveSession()
461 frank 2051 self.application.SetPath("data",dlg.GetPath())
462 bh 6 dlg.Destroy()
463    
464     def Exit(self):
465 jonathan 621 self.Close(False)
466 bh 6
467 frank 951 def OnClose(self, event):
468 bh 58 result = self.save_modified_session(can_veto = event.CanVeto())
469     if result == wxID_CANCEL:
470 bh 6 event.Veto()
471     else:
472 bh 307 # FIXME: it would be better to tie the unsubscription to
473     # wx's destroy event, but that isn't implemented for wxGTK
474     # yet.
475     self.canvas.Unsubscribe(VIEW_POSITION, self.view_position_changed)
476 jonathan 974 DockFrame.OnClose(self, event)
477 frank 1056 for dlg in self.dialogs.values():
478     dlg.Destroy()
479     self.canvas.Destroy()
480 bh 6 self.Destroy()
481    
482     def SetMap(self, map):
483     self.canvas.SetMap(map)
484 bh 1782 self.update_title()
485 bh 6
486 jonathan 768 dialog = self.FindRegisteredDock("legend")
487     if dialog is not None:
488     dialog.GetPanel().SetMap(self.Map())
489    
490 bh 310 def Map(self):
491     """Return the map displayed by this mainwindow"""
492 jonathan 563
493 bh 310 return self.canvas.Map()
494    
495 bh 622 def ToggleSessionTree(self):
496     """If the session tree is shown close it otherwise create a new tree"""
497 bh 37 name = "session_tree"
498     dialog = self.get_open_dialog(name)
499     if dialog is None:
500 bh 227 dialog = tree.SessionTreeView(self, self.application, name)
501 bh 37 self.add_dialog(name, dialog)
502 jonathan 512 dialog.Show(True)
503 bh 37 else:
504 bh 622 dialog.Close()
505 bh 37
506 bh 622 def SessionTreeShown(self):
507     """Return true iff the session tree is currently shown"""
508     return self.get_open_dialog("session_tree") is not None
509 jonathan 517
510 bh 6 def About(self):
511 jonathan 1309 dlg = About(self)
512     dlg.ShowModal()
513     dlg.Destroy()
514 bh 6
515 bh 1620 def DatabaseManagement(self):
516     name = "dbmanagement"
517     dialog = self.get_open_dialog(name)
518     if dialog is None:
519     map = self.canvas.Map()
520     dialog = DBFrame(self, name, self.application.Session())
521     self.add_dialog(name, dialog)
522     dialog.Show()
523     dialog.Raise()
524    
525 bh 6 def AddLayer(self):
526 frank 2051 dlg = wxFileDialog(self, _("Select one or more data files"),
527     self.application.Path("data"), "",
528 bh 1995 _("Shapefiles (*.shp)") + "|*.shp;*.SHP|" +
529     _("All Files (*.*)") + "|*.*",
530 jan 1622 wxOPEN | wxMULTIPLE)
531 bh 6 if dlg.ShowModal() == wxID_OK:
532 jan 1622 filenames = dlg.GetPaths()
533     for filename in filenames:
534     title = os.path.splitext(os.path.basename(filename))[0]
535     map = self.canvas.Map()
536     has_layers = map.HasLayers()
537     try:
538     store = self.application.Session().OpenShapefile(filename)
539     except IOError:
540     # the layer couldn't be opened
541     self.RunMessageBox(_("Add Layer"),
542     _("Can't open the file '%s'.")%filename)
543     else:
544     layer = Layer(title, store)
545     map.AddLayer(layer)
546     if not has_layers:
547     # if we're adding a layer to an empty map, fit the
548     # new map to the window
549     self.canvas.FitMapToWindow()
550 frank 2051 self.application.SetPath("data",filename)
551 bh 6 dlg.Destroy()
552    
553 jonathan 937 def AddRasterLayer(self):
554 frank 2051 dlg = wxFileDialog(self, _("Select an image file"),
555     self.application.Path("data"), "", "*.*",
556 jonathan 937 wxOPEN)
557     if dlg.ShowModal() == wxID_OK:
558     filename = dlg.GetPath()
559     title = os.path.splitext(os.path.basename(filename))[0]
560     map = self.canvas.Map()
561     has_layers = map.HasLayers()
562     try:
563 jonathan 963 layer = RasterLayer(title, filename)
564 jonathan 937 except IOError:
565     # the layer couldn't be opened
566     self.RunMessageBox(_("Add Image Layer"),
567     _("Can't open the file '%s'.") % filename)
568     else:
569 jonathan 963 map.AddLayer(layer)
570 jonathan 937 if not has_layers:
571     # if we're adding a layer to an empty map, fit the
572     # new map to the window
573     self.canvas.FitMapToWindow()
574 frank 2051 self.application.SetPath("data", filename)
575 jonathan 937 dlg.Destroy()
576    
577 bh 1620 def AddDBLayer(self):
578     """Add a layer read from a database"""
579     session = self.application.Session()
580 bh 1695 dlg = ChooseDBTableDialog(self, self.application.Session())
581 bh 1620
582     if dlg.ShowModal() == wxID_OK:
583 bh 2102 dbconn, dbtable, id_column, geo_column = dlg.GetTable()
584 bh 1620 try:
585     title = str(dbtable)
586    
587     # Chose the correct Interface for the database type
588 bh 2102 store = session.OpenDBShapeStore(dbconn, dbtable,
589     id_column = id_column,
590     geometry_column = geo_column)
591 bh 1620 layer = Layer(title, store)
592     except:
593     # Some error occured while initializing the layer
594     self.RunMessageBox(_("Add Layer from database"),
595     _("Can't open the database table '%s'")
596     % dbtable)
597 bh 2061 return
598 bh 1620
599     map = self.canvas.Map()
600    
601     has_layers = map.HasLayers()
602     map.AddLayer(layer)
603     if not has_layers:
604     self.canvas.FitMapToWindow()
605    
606     dlg.Destroy()
607    
608 bh 6 def RemoveLayer(self):
609     layer = self.current_layer()
610     if layer is not None:
611     self.canvas.Map().RemoveLayer(layer)
612    
613 bh 299 def CanRemoveLayer(self):
614     """Return true if the currently selected layer can be deleted.
615    
616 jonathan 621 If no layer is selected return False.
617 bh 299
618     The return value of this method determines whether the remove
619     layer command is sensitive in menu.
620     """
621     layer = self.current_layer()
622     if layer is not None:
623     return self.canvas.Map().CanRemoveLayer(layer)
624 jonathan 621 return False
625 bh 299
626 jan 2186 def LayerToTop(self):
627     layer = self.current_layer()
628     if layer is not None:
629     self.canvas.Map().MoveLayerToTop(layer)
630    
631 bh 6 def RaiseLayer(self):
632     layer = self.current_layer()
633     if layer is not None:
634     self.canvas.Map().RaiseLayer(layer)
635 bh 222
636 bh 6 def LowerLayer(self):
637     layer = self.current_layer()
638     if layer is not None:
639     self.canvas.Map().LowerLayer(layer)
640    
641 jan 2186 def LayerToBottom(self):
642     layer = self.current_layer()
643     if layer is not None:
644     self.canvas.Map().MoveLayerToBottom(layer)
645    
646 bh 6 def current_layer(self):
647     """Return the currently selected layer.
648    
649     If no layer is selected, return None
650     """
651 bh 535 return self.canvas.SelectedLayer()
652 bh 6
653     def has_selected_layer(self):
654     """Return true if a layer is currently selected"""
655 bh 535 return self.canvas.HasSelectedLayer()
656 bh 6
657 bh 2019 def has_selected_shape_layer(self):
658     """Return true if a shape layer is currently selected"""
659     return isinstance(self.current_layer(), Layer)
660    
661 jonathan 829 def has_selected_shapes(self):
662     """Return true if a shape is currently selected"""
663     return self.canvas.HasSelectedShapes()
664    
665 bh 6 def HideLayer(self):
666     layer = self.current_layer()
667     if layer is not None:
668 jan 2186 layer.SetVisible(False)
669 bh 1094
670 bh 6 def ShowLayer(self):
671     layer = self.current_layer()
672     if layer is not None:
673 jan 2186 layer.SetVisible(True)
674 bh 6
675 jan 2186 def ToggleLayerVisibility(self):
676     layer = self.current_layer()
677     layer.SetVisible(not layer.Visible())
678    
679 bh 1094 def DuplicateLayer(self):
680     """Ceate a new layer above the selected layer with the same shapestore
681     """
682     layer = self.current_layer()
683     if layer is not None and hasattr(layer, "ShapeStore"):
684     new_layer = Layer(_("Copy of `%s'") % layer.Title(),
685     layer.ShapeStore(),
686     projection = layer.GetProjection())
687     new_classification = copy.deepcopy(layer.GetClassification())
688 frank 2520 new_layer.SetClassificationColumn(
689     layer.GetClassificationColumn())
690 bh 1094 new_layer.SetClassification(new_classification)
691     self.Map().AddLayer(new_layer)
692    
693     def CanDuplicateLayer(self):
694     """Return whether the DuplicateLayer method can create a duplicate"""
695     layer = self.current_layer()
696     return layer is not None and hasattr(layer, "ShapeStore")
697    
698 bh 6 def LayerShowTable(self):
699 jan 2184 """
700     Present a TableView Window for the current layer.
701     In case the window is already open, bring it to the front.
702     In case, there is no active layer, do nothing.
703 jan 2186 In case, the layer has no ShapeStore, do nothing.
704 jan 2184 """
705 bh 6 layer = self.current_layer()
706     if layer is not None:
707 jan 2184 if not hasattr(layer, "ShapeStore"):
708     return
709 bh 1219 table = layer.ShapeStore().Table()
710 bh 31 name = "table_view" + str(id(table))
711     dialog = self.get_open_dialog(name)
712     if dialog is None:
713 bh 535 dialog = tableview.LayerTableFrame(self, name,
714 jan 1023 _("Layer Table: %s") % layer.Title(),
715     layer, table)
716 bh 31 self.add_dialog(name, dialog)
717 jan 1035 dialog.Show(True)
718 bh 31 else:
719 joey 2163 dialog.Raise()
720 bh 6
721 jonathan 729 def MapProjection(self):
722 bh 6
723 jonathan 729 name = "map_projection"
724 jonathan 713 dialog = self.get_open_dialog(name)
725    
726     if dialog is None:
727     map = self.canvas.Map()
728 jonathan 750 dialog = projdialog.ProjFrame(self, name,
729     _("Map Projection: %s") % map.Title(), map)
730 jonathan 713 self.add_dialog(name, dialog)
731     dialog.Show()
732     dialog.Raise()
733    
734 jonathan 729 def LayerProjection(self):
735    
736     layer = self.current_layer()
737    
738     name = "layer_projection" + str(id(layer))
739     dialog = self.get_open_dialog(name)
740    
741     if dialog is None:
742     map = self.canvas.Map()
743 jonathan 750 dialog = projdialog.ProjFrame(self, name,
744     _("Layer Projection: %s") % layer.Title(), layer)
745 jonathan 729 self.add_dialog(name, dialog)
746     dialog.Show()
747     dialog.Raise()
748    
749 jonathan 640 def LayerEditProperties(self):
750 jonathan 363
751 jonathan 487 #
752     # the menu option for this should only be available if there
753     # is a current layer, so we don't need to check if the
754     # current layer is None
755     #
756    
757     layer = self.current_layer()
758 jonathan 640 self.OpenLayerProperties(layer)
759 jonathan 550
760 jonathan 640 def OpenLayerProperties(self, layer, group = None):
761 joey 2364 """
762     Open or raise the properties dialog.
763    
764     This method opens or raises the properties dialog for the
765     currently selected layer if one is defined for this layer
766     type.
767     """
768 joey 2365 dialog_class = layer_properties_dialogs.get(layer)
769    
770     if dialog_class is not None:
771     name = "layer_properties" + str(id(layer))
772     self.OpenOrRaiseDialog(name, dialog_class, layer, group = group)
773    
774 jonathan 879 def LayerJoinTable(self):
775 bh 1072 layer = self.canvas.SelectedLayer()
776     if layer is not None:
777     dlg = JoinDialog(self, _("Join Layer with Table"),
778     self.application.session,
779     layer = layer)
780     dlg.ShowModal()
781 jonathan 550
782 jonathan 879 def LayerUnjoinTable(self):
783 bh 1074 layer = self.canvas.SelectedLayer()
784     if layer is not None:
785     orig_store = layer.ShapeStore().OrigShapeStore()
786     if orig_store:
787     layer.SetShapeStore(orig_store)
788 jonathan 879
789 jonathan 621 def ShowLegend(self):
790 bh 622 if not self.LegendShown():
791     self.ToggleLegend()
792    
793     def ToggleLegend(self):
794     """Show the legend if it's not shown otherwise hide it again"""
795 jonathan 550 name = "legend"
796 jonathan 573 dialog = self.FindRegisteredDock(name)
797 jonathan 550
798     if dialog is None:
799 jonathan 640 dialog = self.CreateDock(name, -1, _("Legend"), wxLAYOUT_LEFT)
800 jonathan 573 legend.LegendPanel(dialog, None, self)
801 jonathan 580 dialog.Dock()
802 bh 622 dialog.GetPanel().SetMap(self.Map())
803     dialog.Show()
804     else:
805     dialog.Show(not dialog.IsShown())
806 jonathan 563
807 bh 622 def LegendShown(self):
808     """Return true iff the legend is currently open"""
809     dialog = self.FindRegisteredDock("legend")
810     return dialog is not None and dialog.IsShown()
811 jonathan 563
812 jonathan 879 def TableOpen(self):
813 frank 2051 dlg = wxFileDialog(self, _("Open Table"),
814     self.application.Path("data"), "",
815 bh 1037 _("DBF Files (*.dbf)") + "|*.dbf|" +
816     #_("CSV Files (*.csv)") + "|*.csv|" +
817     _("All Files (*.*)") + "|*.*",
818 jonathan 879 wxOPEN)
819     if dlg.ShowModal() == wxID_OK:
820 bh 1054 filename = dlg.GetPath()
821     dlg.Destroy()
822     try:
823     table = self.application.session.OpenTableFile(filename)
824     except IOError:
825     # the layer couldn't be opened
826     self.RunMessageBox(_("Open Table"),
827     _("Can't open the file '%s'.") % filename)
828     else:
829     self.ShowTableView(table)
830 frank 2051 self.application.SetPath("data",filename)
831 jonathan 879
832     def TableClose(self):
833 bh 1068 tables = self.application.session.UnreferencedTables()
834 jonathan 879
835 jan 1084 lst = [(t.Title(), t) for t in tables]
836     lst.sort()
837     titles = [i[0] for i in lst]
838 bh 1068 dlg = wxMultipleChoiceDialog(self, _("Pick the tables to close:"),
839 jan 1084 _("Close Table"), titles,
840     size = (400, 300),
841     style = wxDEFAULT_DIALOG_STYLE |
842     wxRESIZE_BORDER)
843 bh 1068 if dlg.ShowModal() == wxID_OK:
844     for i in dlg.GetValue():
845 jan 1084 self.application.session.RemoveTable(lst[i][1])
846 bh 1068
847    
848 jonathan 879 def TableShow(self):
849 jan 1014 """Offer a multi-selection dialog for tables to be displayed
850 bh 1054
851 jan 1014 The windows for the selected tables are opened or brought to
852     the front.
853     """
854     tables = self.application.session.Tables()
855 jonathan 879
856 jan 1084 lst = [(t.Title(), t) for t in tables]
857     lst.sort()
858     titles = [i[0] for i in lst]
859 jan 1014 dlg = wxMultipleChoiceDialog(self, _("Pick the table to show:"),
860 jan 1084 _("Show Table"), titles,
861 frank 1076 size = (400,300),
862     style = wxDEFAULT_DIALOG_STYLE |
863     wxRESIZE_BORDER)
864 jan 1014 if (dlg.ShowModal() == wxID_OK):
865     for i in dlg.GetValue():
866 jan 1023 # XXX: if the table belongs to a layer, open a
867     # LayerTableFrame instead of QueryTableFrame
868 jan 1084 self.ShowTableView(lst[i][1])
869 jan 1014
870 jonathan 879 def TableJoin(self):
871     dlg = JoinDialog(self, _("Join Tables"), self.application.session)
872 frank 1001 dlg.ShowModal()
873 jonathan 879
874 bh 1054 def ShowTableView(self, table):
875     """Open a table view for the table and optionally"""
876     name = "table_view%d" % id(table)
877     dialog = self.get_open_dialog(name)
878     if dialog is None:
879     dialog = tableview.QueryTableFrame(self, name,
880     _("Table: %s") % table.Title(),
881     table)
882     self.add_dialog(name, dialog)
883     dialog.Show(True)
884 jonathan 1393 dialog.Raise()
885 bh 1054
886 bh 1126 def TableRename(self):
887     """Let the user rename a table"""
888    
889     # First, let the user select a table
890     tables = self.application.session.Tables()
891     lst = [(t.Title(), t) for t in tables]
892     lst.sort()
893     titles = [i[0] for i in lst]
894     dlg = wxMultipleChoiceDialog(self, _("Pick the table to rename:"),
895     _("Rename Table"), titles,
896     size = (400,300),
897     style = wxDEFAULT_DIALOG_STYLE |
898     wxRESIZE_BORDER)
899     if (dlg.ShowModal() == wxID_OK):
900     to_rename = [lst[i][1] for i in dlg.GetValue()]
901     dlg.Destroy()
902     else:
903     to_rename = []
904    
905     # Second, let the user rename the layers
906     for table in to_rename:
907 bh 2040 dlg = wxTextEntryDialog(self, _("Table Title:"), _("Rename Table"),
908 bh 1126 table.Title())
909     try:
910     if dlg.ShowModal() == wxID_OK:
911     title = dlg.GetValue()
912     if title != "":
913     table.SetTitle(title)
914    
915     # Make sure the session is marked as modified.
916     # FIXME: This should be handled automatically,
917     # but that requires more changes to the tables
918     # than I have time for currently.
919     self.application.session.changed()
920     finally:
921     dlg.Destroy()
922    
923    
924 bh 6 def ZoomInTool(self):
925     self.canvas.ZoomInTool()
926    
927     def ZoomOutTool(self):
928     self.canvas.ZoomOutTool()
929    
930     def PanTool(self):
931     self.canvas.PanTool()
932    
933     def IdentifyTool(self):
934     self.canvas.IdentifyTool()
935 bh 49 self.identify_view_on_demand(None, None)
936 bh 6
937     def LabelTool(self):
938     self.canvas.LabelTool()
939    
940     def FullExtent(self):
941     self.canvas.FitMapToWindow()
942    
943 jonathan 821 def FullLayerExtent(self):
944     self.canvas.FitLayerToWindow(self.current_layer())
945    
946 jonathan 829 def FullSelectionExtent(self):
947     self.canvas.FitSelectedToWindow()
948    
949 frank 911 def ExportMap(self):
950     self.canvas.Export()
951    
952 bh 6 def PrintMap(self):
953     self.canvas.Print()
954    
955 jonathan 653 def RenameMap(self):
956 bh 2040 dlg = wxTextEntryDialog(self, _("Map Title:"), _("Rename Map"),
957 jonathan 653 self.Map().Title())
958     if dlg.ShowModal() == wxID_OK:
959     title = dlg.GetValue()
960     if title != "":
961     self.Map().SetTitle(title)
962    
963     dlg.Destroy()
964    
965 bh 1126 def RenameLayer(self):
966     """Let the user rename the currently selected layer"""
967     layer = self.current_layer()
968     if layer is not None:
969 bh 2040 dlg = wxTextEntryDialog(self, _("Layer Title:"), _("Rename Layer"),
970 bh 1126 layer.Title())
971     try:
972     if dlg.ShowModal() == wxID_OK:
973     title = dlg.GetValue()
974     if title != "":
975     layer.SetTitle(title)
976     finally:
977     dlg.Destroy()
978    
979 bh 535 def identify_view_on_demand(self, layer, shapes):
980 bh 787 """Subscribed to the canvas' SHAPES_SELECTED message
981    
982     If the current tool is the identify tool, at least one shape is
983     selected and the identify dialog is not shown, show the dialog.
984     """
985     # If the selection has become empty we don't need to do
986     # anything. Otherwise it could happen that the dialog was popped
987     # up when the selection became empty, e.g. when a new selection
988     # is opened while the identify tool is active and dialog had
989     # been closed
990     if not shapes:
991     return
992    
993 bh 31 name = "identify_view"
994     if self.canvas.CurrentTool() == "IdentifyTool":
995     if not self.dialog_open(name):
996 bh 535 dialog = identifyview.IdentifyView(self, name)
997 bh 31 self.add_dialog(name, dialog)
998 jonathan 563 dialog.Show(True)
999 bh 31 else:
1000 bh 33 # FIXME: bring dialog to front?
1001 bh 31 pass
1002 bh 6
1003 bh 1782 def title_changed(self, map):
1004     """Subscribed to the canvas' TITLE_CHANGED messages"""
1005     self.update_title()
1006 jonathan 653
1007 bh 1782 def update_title(self):
1008     """Update the window's title according to it's current state.
1009    
1010     In this default implementation the title is 'Thuban - ' followed
1011     by the map's title or simply 'Thuban' if there is not map.
1012     Derived classes should override this method to get different
1013     titles.
1014    
1015     This method is called automatically by other methods when the
1016     title may have to change. For the methods implemented in this
1017     class this usually only means that a different map has been set
1018     or the current map's title has changed.
1019     """
1020     map = self.Map()
1021     if map is not None:
1022     title = _("Thuban - %s") % (map.Title(),)
1023     else:
1024     title = _("Thuban")
1025     self.SetTitle(title)
1026    
1027    
1028 bh 6 #
1029     # Define all the commands available in the main window
1030     #
1031    
1032    
1033     # Helper functions to define common command implementations
1034     def call_method(context, methodname, *args):
1035 bh 222 """Call the mainwindow's method methodname with args *args"""
1036     apply(getattr(context.mainwindow, methodname), args)
1037 bh 6
1038 jan 110 def _method_command(name, title, method, helptext = "",
1039 bh 622 icon = "", sensitive = None, checked = None):
1040 bh 222 """Add a command implemented by a method of the mainwindow object"""
1041 bh 6 registry.Add(Command(name, title, call_method, args=(method,),
1042 jan 110 helptext = helptext, icon = icon,
1043 bh 622 sensitive = sensitive, checked = checked))
1044 jan 110
1045 bh 270 def make_check_current_tool(toolname):
1046     """Return a function that tests if the currently active tool is toolname
1047    
1048     The returned function can be called with the context and returns
1049     true iff the currently active tool's name is toolname. It's directly
1050     usable as the 'checked' callback of a command.
1051     """
1052     def check_current_tool(context, name=toolname):
1053     return context.mainwindow.canvas.CurrentTool() == name
1054     return check_current_tool
1055    
1056 bh 6 def _tool_command(name, title, method, toolname, helptext = "",
1057 bh 310 icon = "", sensitive = None):
1058 bh 6 """Add a tool command"""
1059 bh 357 registry.Add(ToolCommand(name, title, call_method, args=(method,),
1060     helptext = helptext, icon = icon,
1061     checked = make_check_current_tool(toolname),
1062     sensitive = sensitive))
1063 bh 6
1064     def _has_selected_layer(context):
1065     """Return true if a layer is selected in the context"""
1066 bh 222 return context.mainwindow.has_selected_layer()
1067 bh 6
1068 jan 2186 def _has_selected_layer_visible(context):
1069     """Return true if a layer is selected in the context which is
1070     visible."""
1071     if context.mainwindow.has_selected_layer():
1072     layer = context.mainwindow.current_layer()
1073     if layer.Visible(): return True
1074     return False
1075    
1076 bh 2019 def _has_selected_shape_layer(context):
1077     """Return true if a shape layer is selected in the context"""
1078     return context.mainwindow.has_selected_shape_layer()
1079    
1080 jonathan 829 def _has_selected_shapes(context):
1081     """Return true if a layer is selected in the context"""
1082     return context.mainwindow.has_selected_shapes()
1083    
1084 bh 299 def _can_remove_layer(context):
1085     return context.mainwindow.CanRemoveLayer()
1086    
1087 jan 264 def _has_tree_window_shown(context):
1088     """Return true if the tree window is shown"""
1089 bh 622 return context.mainwindow.SessionTreeShown()
1090 jan 264
1091 bh 310 def _has_visible_map(context):
1092     """Return true iff theres a visible map in the mainwindow.
1093    
1094     A visible map is a map with at least one visible layer."""
1095     map = context.mainwindow.Map()
1096     if map is not None:
1097     for layer in map.Layers():
1098     if layer.Visible():
1099 jan 2186 return True
1100     return False
1101 bh 310
1102 jonathan 550 def _has_legend_shown(context):
1103     """Return true if the legend window is shown"""
1104 bh 622 return context.mainwindow.LegendShown()
1105 bh 310
1106 jonathan 1164 def _has_gdal_support(context):
1107     """Return True if the GDAL is available"""
1108     return Thuban.Model.resource.has_gdal_support()
1109 jonathan 550
1110 bh 1620 def _has_dbconnections(context):
1111     """Return whether the the session has database connections"""
1112     return context.session.HasDBConnections()
1113    
1114 bh 1625 def _has_postgis_support(context):
1115     return has_postgis_support()
1116    
1117    
1118 bh 6 # File menu
1119 jan 1140 _method_command("new_session", _("&New Session"), "NewSession",
1120     helptext = _("Start a new session"))
1121     _method_command("open_session", _("&Open Session..."), "OpenSession",
1122     helptext = _("Open a session file"))
1123     _method_command("save_session", _("&Save Session"), "SaveSession",
1124     helptext =_("Save this session to the file it was opened from"))
1125     _method_command("save_session_as", _("Save Session &As..."), "SaveSessionAs",
1126     helptext = _("Save this session to a new file"))
1127 bh 622 _method_command("toggle_session_tree", _("Session &Tree"), "ToggleSessionTree",
1128 jan 1140 checked = _has_tree_window_shown,
1129     helptext = _("Toggle on/off the session tree analysis window"))
1130 bh 622 _method_command("toggle_legend", _("Legend"), "ToggleLegend",
1131 jan 1140 checked = _has_legend_shown,
1132     helptext = _("Toggle Legend on/off"))
1133 bh 1620 _method_command("database_management", _("&Database Connections..."),
1134 bh 1625 "DatabaseManagement",
1135     sensitive = _has_postgis_support)
1136 jan 1140 _method_command("exit", _("E&xit"), "Exit",
1137     helptext = _("Finish working with Thuban"))
1138 bh 6
1139     # Help menu
1140 jan 1140 _method_command("help_about", _("&About..."), "About",
1141     helptext = _("Info about Thuban authors, version and modules"))
1142 bh 6
1143    
1144     # Map menu
1145 jan 1140 _method_command("map_projection", _("Pro&jection..."), "MapProjection",
1146     helptext = _("Set or change the map projection"))
1147 bh 6
1148 jan 374 _tool_command("map_zoom_in_tool", _("&Zoom in"), "ZoomInTool", "ZoomInTool",
1149     helptext = _("Switch to map-mode 'zoom-in'"), icon = "zoom_in",
1150 bh 310 sensitive = _has_visible_map)
1151 jan 374 _tool_command("map_zoom_out_tool", _("Zoom &out"), "ZoomOutTool", "ZoomOutTool",
1152     helptext = _("Switch to map-mode 'zoom-out'"), icon = "zoom_out",
1153 bh 310 sensitive = _has_visible_map)
1154 jan 374 _tool_command("map_pan_tool", _("&Pan"), "PanTool", "PanTool",
1155     helptext = _("Switch to map-mode 'pan'"), icon = "pan",
1156 bh 310 sensitive = _has_visible_map)
1157 jan 374 _tool_command("map_identify_tool", _("&Identify"), "IdentifyTool",
1158     "IdentifyTool",
1159     helptext = _("Switch to map-mode 'identify'"), icon = "identify",
1160 bh 310 sensitive = _has_visible_map)
1161 jan 374 _tool_command("map_label_tool", _("&Label"), "LabelTool", "LabelTool",
1162     helptext = _("Add/Remove labels"), icon = "label",
1163 bh 310 sensitive = _has_visible_map)
1164 jan 374 _method_command("map_full_extent", _("&Full extent"), "FullExtent",
1165 jan 1140 helptext = _("Zoom to the full map extent"), icon = "fullextent",
1166 bh 310 sensitive = _has_visible_map)
1167 jonathan 821 _method_command("layer_full_extent", _("&Full layer extent"), "FullLayerExtent",
1168 jan 1140 helptext = _("Zoom to the full layer extent"),
1169     icon = "fulllayerextent", sensitive = _has_selected_layer)
1170     _method_command("selected_full_extent", _("&Full selection extent"),
1171     "FullSelectionExtent",
1172     helptext = _("Zoom to the full selection extent"),
1173     icon = "fullselextent", sensitive = _has_selected_shapes)
1174 frank 911 _method_command("map_export", _("E&xport"), "ExportMap",
1175 jan 1140 helptext = _("Export the map to file"))
1176 jan 374 _method_command("map_print", _("Prin&t"), "PrintMap",
1177     helptext = _("Print the map"))
1178 jonathan 815 _method_command("map_rename", _("&Rename..."), "RenameMap",
1179 jonathan 653 helptext = _("Rename the map"))
1180 jonathan 815 _method_command("layer_add", _("&Add Layer..."), "AddLayer",
1181 jan 1140 helptext = _("Add a new layer to the map"))
1182 jonathan 937 _method_command("rasterlayer_add", _("&Add Image Layer..."), "AddRasterLayer",
1183 jonathan 1164 helptext = _("Add a new image layer to the map"),
1184     sensitive = _has_gdal_support)
1185 bh 1620 _method_command("layer_add_db", _("Add &Database Layer..."), "AddDBLayer",
1186     helptext = _("Add a new database layer to active map"),
1187     sensitive = _has_dbconnections)
1188 jan 374 _method_command("layer_remove", _("&Remove Layer"), "RemoveLayer",
1189 jan 1140 helptext = _("Remove selected layer"),
1190 bh 299 sensitive = _can_remove_layer)
1191 jonathan 729
1192     # Layer menu
1193 jonathan 815 _method_command("layer_projection", _("Pro&jection..."), "LayerProjection",
1194 jan 1140 sensitive = _has_selected_layer,
1195     helptext = _("Specify projection for selected layer"))
1196 bh 1094 _method_command("layer_duplicate", _("&Duplicate"), "DuplicateLayer",
1197 jan 1140 helptext = _("Duplicate selected layer"),
1198 bh 1094 sensitive = lambda context: context.mainwindow.CanDuplicateLayer())
1199 bh 1126 _method_command("layer_rename", _("Re&name ..."), "RenameLayer",
1200     helptext = _("Rename selected layer"),
1201     sensitive = _has_selected_layer)
1202 jan 374 _method_command("layer_raise", _("&Raise"), "RaiseLayer",
1203 jan 1140 helptext = _("Raise selected layer"),
1204 bh 6 sensitive = _has_selected_layer)
1205 jan 374 _method_command("layer_lower", _("&Lower"), "LowerLayer",
1206 jan 1140 helptext = _("Lower selected layer"),
1207 bh 6 sensitive = _has_selected_layer)
1208 jan 374 _method_command("layer_show", _("&Show"), "ShowLayer",
1209 jan 1140 helptext = _("Make selected layer visible"),
1210 bh 6 sensitive = _has_selected_layer)
1211 jan 374 _method_command("layer_hide", _("&Hide"), "HideLayer",
1212 jan 1140 helptext = _("Make selected layer unvisible"),
1213 bh 6 sensitive = _has_selected_layer)
1214 jan 374 _method_command("layer_show_table", _("Show Ta&ble"), "LayerShowTable",
1215     helptext = _("Show the selected layer's table"),
1216 bh 2019 sensitive = _has_selected_shape_layer)
1217 jonathan 815 _method_command("layer_properties", _("&Properties..."), "LayerEditProperties",
1218 jan 1140 sensitive = _has_selected_layer,
1219     helptext = _("Edit the properties of the selected layer"))
1220 jonathan 879 _method_command("layer_jointable", _("&Join Table..."), "LayerJoinTable",
1221 bh 2019 sensitive = _has_selected_shape_layer,
1222 jan 1140 helptext = _("Join and attach a table to the selected layer"))
1223 bh 1074
1224 jan 2186 # further layer methods:
1225     _method_command("layer_to_top", _("&Top"), "LayerToTop",
1226     helptext = _("Put selected layer to the top"),
1227     sensitive = _has_selected_layer)
1228     _method_command("layer_to_bottom", _("&Bottom"), "LayerToBottom",
1229     helptext = _("Put selected layer to the bottom"),
1230     sensitive = _has_selected_layer)
1231     _method_command("layer_visibility", _("&Visible"), "ToggleLayerVisibility",
1232     checked = _has_selected_layer_visible,
1233     helptext = _("Toggle visibility of selected layer"),
1234     sensitive = _has_selected_layer)
1235    
1236 bh 1074 def _can_unjoin(context):
1237 bh 1080 """Return whether the Layer/Unjoin command can be executed.
1238    
1239     This is the case if a layer is selected and that layer has a
1240     shapestore that has an original shapestore.
1241     """
1242 bh 1074 layer = context.mainwindow.SelectedLayer()
1243 bh 1080 if layer is None:
1244     return 0
1245     getstore = getattr(layer, "ShapeStore", None)
1246     if getstore is not None:
1247     return getstore().OrigShapeStore() is not None
1248     else:
1249     return 0
1250 jonathan 879 _method_command("layer_unjointable", _("&Unjoin Table..."), "LayerUnjoinTable",
1251 jan 1140 sensitive = _can_unjoin,
1252     helptext = _("Undo the last join operation"))
1253 bh 188
1254 bh 1126
1255     def _has_tables(context):
1256     return bool(context.session.Tables())
1257    
1258 jonathan 879 # Table menu
1259 jan 1140 _method_command("table_open", _("&Open..."), "TableOpen",
1260     helptext = _("Open a DBF-table from a file"))
1261     _method_command("table_close", _("&Close..."), "TableClose",
1262     sensitive = lambda context: bool(context.session.UnreferencedTables()),
1263     helptext = _("Close one or more tables from a list"))
1264 bh 1126 _method_command("table_rename", _("&Rename..."), "TableRename",
1265 jan 1140 sensitive = _has_tables,
1266     helptext = _("Rename one or more tables"))
1267     _method_command("table_show", _("&Show..."), "TableShow",
1268     sensitive = _has_tables,
1269     helptext = _("Show one or more tables in a dialog"))
1270 bh 1126 _method_command("table_join", _("&Join..."), "TableJoin",
1271 jan 1140 sensitive = _has_tables,
1272     helptext = _("Join two tables creating a new one"))
1273 jonathan 879
1274 frank 911 # Export only under Windows ...
1275 bh 1620 map_menu = ["layer_add", "layer_add_db", "rasterlayer_add", "layer_remove",
1276 bh 188 None,
1277 jonathan 1164 "map_rename",
1278 bh 188 "map_projection",
1279     None,
1280     "map_zoom_in_tool", "map_zoom_out_tool",
1281 bh 1620 "map_pan_tool",
1282     "map_full_extent",
1283 jonathan 829 "layer_full_extent",
1284     "selected_full_extent",
1285 bh 188 None,
1286 jonathan 815 "map_identify_tool", "map_label_tool",
1287 bh 188 None,
1288 bh 624 "toggle_legend",
1289 frank 911 None]
1290     if wxPlatform == '__WXMSW__':
1291     map_menu.append("map_export")
1292     map_menu.append("map_print")
1293    
1294     # the menu structure
1295     main_menu = Menu("<main>", "<main>",
1296     [Menu("file", _("&File"),
1297     ["new_session", "open_session", None,
1298     "save_session", "save_session_as", None,
1299 bh 1620 "database_management", None,
1300 frank 911 "toggle_session_tree", None,
1301     "exit"]),
1302     Menu("map", _("&Map"), map_menu),
1303 jan 374 Menu("layer", _("&Layer"),
1304 bh 1126 ["layer_rename", "layer_duplicate",
1305 bh 188 None,
1306 bh 1126 "layer_raise", "layer_lower",
1307     None,
1308 bh 188 "layer_show", "layer_hide",
1309     None,
1310 jonathan 879 "layer_projection",
1311     None,
1312 jonathan 363 "layer_show_table",
1313 jonathan 879 "layer_jointable",
1314     "layer_unjointable",
1315 jonathan 363 None,
1316 jonathan 815 "layer_properties"]),
1317 jonathan 879 Menu("table", _("&Table"),
1318 bh 1126 ["table_open", "table_close", "table_rename",
1319 jonathan 879 None,
1320 bh 1052 "table_show",
1321 jonathan 879 None,
1322     "table_join"]),
1323 jan 374 Menu("help", _("&Help"),
1324 bh 188 ["help_about"])])
1325 bh 191
1326     # the main toolbar
1327    
1328     main_toolbar = Menu("<toolbar>", "<toolbar>",
1329 frank 351 ["map_zoom_in_tool", "map_zoom_out_tool", "map_pan_tool",
1330 jonathan 829 "map_full_extent",
1331     "layer_full_extent",
1332     "selected_full_extent",
1333     None,
1334 frank 351 "map_identify_tool", "map_label_tool"])
1335 jonathan 1293

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26