/[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 2551 - (hide annotations)
Thu Jan 27 14:19:41 2005 UTC (20 years, 1 month ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 53657 byte(s)
Add a new dialog box for raster layers. The dialog box allows
the user to toggle a mask that is generated by ProjectRasterFile
and is used to only draw the real parts of the projected image.

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