/[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 2040 - (hide annotations)
Tue Dec 23 11:37:04 2003 UTC (21 years, 2 months ago) by bh
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 46908 byte(s)
* Thuban/UI/join.py (JoinDialog.__init__): Mark one more string
for translation

* Thuban/UI/mainwindow.py (MainWindow.TableRename)
(MainWindow.RenameMap, MainWindow.RenameLayer): Mark some more
strings for translation

* po/de.po: Update with the newly marked strings.

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