/[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 2529 - (hide annotations)
Thu Jan 20 17:55:23 2005 UTC (20 years, 1 month ago) by russell
Original Path: trunk/thuban/Thuban/UI/mainwindow.py
File MIME type: text/x-python
File size: 50742 byte(s)
Warn user about misprojected layers when their lat/lon bounding
box exceeds rational lat/lon values.

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