GRASS Programmer's Manual  6.5.svn(2012)-r51648
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
gis_set.py
Go to the documentation of this file.
00001 """!
00002 @package gis_set
00003 
00004 GRASS start-up screen.
00005 
00006 Initialization module for wxPython GRASS GUI.
00007 Location/mapset management (selection, creation, etc.).
00008 
00009 Classes:
00010  - gis_set::GRASSStartup
00011  - gis_set::GListBox
00012  - gis_set::StartUp
00013 
00014 (C) 2006-2011 by the GRASS Development Team
00015 
00016 This program is free software under the GNU General Public License
00017 (>=v2). Read the file COPYING that comes with GRASS for details.
00018 
00019 @author Michael Barton and Jachym Cepicky (original author)
00020 @author Martin Landa <landa.martin gmail.com> (various updates)
00021 """
00022 
00023 import os
00024 import sys
00025 import shutil
00026 import copy
00027 import platform
00028 import codecs
00029 
00030 ### i18N
00031 import gettext
00032 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
00033 
00034 if __name__ == "__main__":
00035     sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
00036 from core import globalvar
00037 import wx
00038 import wx.lib.mixins.listctrl as listmix
00039 import wx.lib.scrolledpanel as scrolled
00040 
00041 from gui_core.ghelp import HelpFrame
00042 from core.gcmd      import GMessage, GError, DecodeString, RunCommand
00043 from core.utils     import GetListOfLocations, GetListOfMapsets
00044 
00045 sys.stderr = codecs.getwriter('utf8')(sys.stderr)
00046 
00047 class GRASSStartup(wx.Frame):
00048     """!GRASS start-up screen"""
00049     def __init__(self, parent = None, id = wx.ID_ANY, style = wx.DEFAULT_FRAME_STYLE):
00050 
00051         #
00052         # GRASS variables
00053         #
00054         self.gisbase  = os.getenv("GISBASE")
00055         self.grassrc  = self._readGisRC()
00056         self.gisdbase = self.GetRCValue("GISDBASE")
00057 
00058         #
00059         # list of locations/mapsets
00060         #
00061         self.listOfLocations = []
00062         self.listOfMapsets = []
00063         self.listOfMapsetsSelectable = []
00064         
00065         wx.Frame.__init__(self, parent = parent, id = id, style = style)
00066         
00067         self.locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
00068         
00069         self.panel = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
00070         
00071         # i18N
00072         import gettext
00073         gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
00074 
00075         #
00076         # graphical elements
00077         #
00078         # image
00079         try:
00080             name = os.path.join(globalvar.ETCIMGDIR, "startup_banner.gif")
00081             self.hbitmap = wx.StaticBitmap(self.panel, wx.ID_ANY,
00082                                            wx.Bitmap(name = name,
00083                                                      type = wx.BITMAP_TYPE_GIF))
00084         except:
00085             self.hbitmap = wx.StaticBitmap(self.panel, wx.ID_ANY, wx.EmptyBitmap(530,150))
00086 
00087         # labels
00088         ### crashes when LOCATION doesn't exist
00089         versionFile = open(os.path.join(globalvar.ETCDIR, "VERSIONNUMBER"))
00090         grassVersion = versionFile.readline().split(' ')[0].rstrip('\n')
00091         versionFile.close()
00092         
00093         self.select_box = wx.StaticBox (parent = self.panel, id = wx.ID_ANY,
00094                                         label = " %s " % _("Choose project location and mapset"))
00095 
00096         self.manage_box = wx.StaticBox (parent = self.panel, id = wx.ID_ANY,
00097                                         label = " %s " % _("Manage"))
00098         self.lwelcome = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00099                                       label = _("Welcome to GRASS GIS %s\n"
00100                                               "The world's leading open source GIS") % grassVersion,
00101                                       style = wx.ALIGN_CENTRE)
00102         self.ltitle = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00103                                     label = _("Select an existing project location and mapset\n"
00104                                             "or define a new location"),
00105                                     style = wx.ALIGN_CENTRE)
00106         self.ldbase = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00107                                     label = _("GIS Data Directory:"))
00108         self.llocation = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00109                                        label = _("Project location\n(projection/coordinate system)"),
00110                                        style = wx.ALIGN_CENTRE)
00111         self.lmapset = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00112                                      label = _("Accessible mapsets\n(directories of GIS files)"),
00113                                      style = wx.ALIGN_CENTRE)
00114         self.lcreate = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00115                                      label = _("Create new mapset\nin selected location"),
00116                                      style = wx.ALIGN_CENTRE)
00117         self.ldefine = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00118                                      label = _("Define new location"),
00119                                      style = wx.ALIGN_CENTRE)
00120         self.lmanageloc = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00121                                         label = _("Rename/delete selected\nmapset or location"),
00122                                         style = wx.ALIGN_CENTRE)
00123 
00124         # buttons
00125         self.bstart = wx.Button(parent = self.panel, id = wx.ID_ANY,
00126                                 label = _("Start &GRASS"))
00127         self.bstart.SetDefault()
00128         self.bexit = wx.Button(parent = self.panel, id = wx.ID_EXIT)
00129         self.bstart.SetMinSize((180, self.bexit.GetSize()[1]))
00130         self.bhelp = wx.Button(parent = self.panel, id = wx.ID_HELP)
00131         self.bbrowse = wx.Button(parent = self.panel, id = wx.ID_ANY,
00132                                  label = _("&Browse"))
00133         self.bmapset = wx.Button(parent = self.panel, id = wx.ID_ANY,
00134                                  label = _("&Create mapset"))
00135         self.bwizard = wx.Button(parent = self.panel, id = wx.ID_ANY,
00136                                  label = _("&Location wizard"))
00137         self.manageloc = wx.Choice(parent = self.panel, id = wx.ID_ANY,
00138                                    choices = [_('Rename mapset'), _('Rename location'),
00139                                             _('Delete mapset'), _('Delete location')])
00140         self.manageloc.SetSelection(0)
00141 
00142         # textinputs
00143         self.tgisdbase = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, value = "", size = (300, -1),
00144                                      style = wx.TE_PROCESS_ENTER)
00145 
00146         # Locations
00147         self.lblocations = GListBox(parent = self.panel,
00148                                     id = wx.ID_ANY, size = (180, 200),
00149                                     choices = self.listOfLocations)
00150         
00151         self.lblocations.SetColumnWidth(0, 180)
00152 
00153         # TODO: sort; but keep PERMANENT on top of list
00154         # Mapsets
00155         self.lbmapsets = GListBox(parent = self.panel,
00156                                   id = wx.ID_ANY, size = (180, 200),
00157                                   choices = self.listOfMapsets)
00158         
00159         self.lbmapsets.SetColumnWidth(0, 180)
00160 
00161         # layout & properties
00162         self._set_properties()
00163         self._do_layout()
00164 
00165         # events
00166         self.bbrowse.Bind(wx.EVT_BUTTON,      self.OnBrowse)
00167         self.bstart.Bind(wx.EVT_BUTTON,       self.OnStart)
00168         self.bexit.Bind(wx.EVT_BUTTON,        self.OnExit)
00169         self.bhelp.Bind(wx.EVT_BUTTON,        self.OnHelp)
00170         self.bmapset.Bind(wx.EVT_BUTTON,      self.OnCreateMapset)
00171         self.bwizard.Bind(wx.EVT_BUTTON,      self.OnWizard)
00172         self.manageloc.Bind(wx.EVT_CHOICE,    self.OnManageLoc)
00173         self.lblocations.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelectLocation)
00174         self.lbmapsets.Bind(wx.EVT_LIST_ITEM_SELECTED,   self.OnSelectMapset)
00175         self.lbmapsets.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnStart)
00176         self.tgisdbase.Bind(wx.EVT_TEXT_ENTER, self.OnSetDatabase)
00177         self.Bind(wx.EVT_CLOSE,               self.OnCloseWindow)
00178         
00179     def _set_properties(self):
00180         """!Set frame properties"""
00181         self.SetTitle(_("Welcome to GRASS GIS"))
00182         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, "grass.ico"),
00183                              wx.BITMAP_TYPE_ICO))
00184 
00185         self.lwelcome.SetForegroundColour(wx.Colour(35, 142, 35))
00186         self.lwelcome.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
00187 
00188         self.bstart.SetForegroundColour(wx.Colour(35, 142, 35))
00189         self.bstart.SetToolTipString(_("Enter GRASS session"))
00190         self.bstart.Enable(False)
00191         self.bmapset.Enable(False)
00192         self.manageloc.Enable(False)
00193 
00194         # set database
00195         if not self.gisdbase:
00196             # sets an initial path for gisdbase if nothing in GISRC
00197             if os.path.isdir(os.getenv("HOME")):
00198                 self.gisdbase = os.getenv("HOME")
00199             else:
00200                 self.gisdbase = os.getcwd()
00201         try:
00202             self.tgisdbase.SetValue(self.gisdbase)
00203         except UnicodeDecodeError:
00204             wx.MessageBox(parent = self, caption = _("Error"),
00205                           message = _("Unable to set GRASS database. "
00206                                       "Check your locale settings."),
00207                           style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
00208         
00209         self.OnSetDatabase(None)
00210         location = self.GetRCValue("LOCATION_NAME")
00211         if location == "<UNKNOWN>" or \
00212                 not os.path.isdir(os.path.join(self.gisdbase, location)):
00213             location = None
00214 
00215         if location:
00216             # list of locations
00217             self.UpdateLocations(self.gisdbase)
00218             try:
00219                 self.lblocations.SetSelection(self.listOfLocations.index(location),
00220                                               force = True)
00221                 self.lblocations.EnsureVisible(self.listOfLocations.index(location))
00222             except ValueError:
00223                 print >> sys.stderr, _("ERROR: Location <%s> not found") % location
00224             
00225             # list of mapsets
00226             self.UpdateMapsets(os.path.join(self.gisdbase, location))
00227             mapset = self.GetRCValue("MAPSET")
00228             if mapset:
00229                 try:
00230                     self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset),
00231                                                 force = True)
00232                     self.lbmapsets.EnsureVisible(self.listOfMapsets.index(mapset))
00233                 except ValueError:
00234                     self.lbmapsets.Clear()
00235                     print >> sys.stderr, _("ERROR: Mapset <%s> not found") % mapset
00236                     
00237     def _do_layout(self):
00238         sizer           = wx.BoxSizer(wx.VERTICAL)
00239         dbase_sizer     = wx.BoxSizer(wx.HORIZONTAL)
00240         location_sizer  = wx.BoxSizer(wx.HORIZONTAL)
00241         select_boxsizer = wx.StaticBoxSizer(self.select_box, wx.VERTICAL)
00242         select_sizer    = wx.FlexGridSizer(rows = 2, cols = 2, vgap = 4, hgap = 4)
00243         select_sizer.AddGrowableRow(1)
00244         select_sizer.AddGrowableCol(0)
00245         select_sizer.AddGrowableCol(1)
00246         manage_sizer    = wx.StaticBoxSizer(self.manage_box, wx.VERTICAL)
00247         btns_sizer      = wx.BoxSizer(wx.HORIZONTAL)
00248         
00249         # gis data directory
00250         dbase_sizer.Add(item = self.ldbase, proportion = 0,
00251                         flag = wx.ALIGN_CENTER_VERTICAL |
00252                         wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
00253                         border = 3)
00254         dbase_sizer.Add(item = self.tgisdbase, proportion = 1,
00255                         flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL,
00256                         border = 3)
00257         dbase_sizer.Add(item = self.bbrowse, proportion = 0,
00258                         flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL,
00259                         border = 3)
00260         
00261         # select sizer
00262         select_sizer.Add(item = self.llocation, proportion = 0,
00263                          flag = wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
00264                          border = 3)
00265         select_sizer.Add(item = self.lmapset, proportion = 0,
00266                          flag = wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
00267                          border = 3)
00268         select_sizer.Add(item = self.lblocations, proportion = 1,
00269                          flag = wx.EXPAND)
00270         select_sizer.Add(item = self.lbmapsets, proportion = 1,
00271                          flag = wx.EXPAND)
00272         
00273         select_boxsizer.Add(item = select_sizer, proportion = 1,
00274                             flag = wx.EXPAND)
00275         
00276         # define new location and mapset
00277         manage_sizer.Add(item = self.ldefine, proportion = 0,
00278                          flag = wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
00279                          border = 3)
00280         manage_sizer.Add(item = self.bwizard, proportion = 0,
00281                          flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM,
00282                          border = 5)
00283         manage_sizer.Add(item = self.lcreate, proportion = 0,
00284                          flag = wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
00285                          border = 3)
00286         manage_sizer.Add(item = self.bmapset, proportion = 0,
00287                          flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM,
00288                          border = 5)
00289         manage_sizer.Add(item = self.lmanageloc, proportion = 0,
00290                          flag = wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
00291                          border = 3)
00292         manage_sizer.Add(item = self.manageloc, proportion = 0,
00293                          flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM,
00294                          border = 5)
00295         
00296         # location sizer
00297         location_sizer.Add(item = select_boxsizer, proportion = 1,
00298                            flag = wx.LEFT | wx.RIGHT | wx.EXPAND,
00299                            border = 3) 
00300         location_sizer.Add(item = manage_sizer, proportion = 0,
00301                            flag = wx.RIGHT | wx.EXPAND,
00302                            border = 3)
00303         
00304         # buttons
00305         btns_sizer.Add(item = self.bstart, proportion = 0,
00306                        flag = wx.ALIGN_CENTER_HORIZONTAL |
00307                        wx.ALIGN_CENTER_VERTICAL |
00308                        wx.ALL,
00309                        border = 5)
00310         btns_sizer.Add(item = self.bexit, proportion = 0,
00311                        flag = wx.ALIGN_CENTER_HORIZONTAL |
00312                        wx.ALIGN_CENTER_VERTICAL |
00313                        wx.ALL,
00314                        border = 5)
00315         btns_sizer.Add(item = self.bhelp, proportion = 0,
00316                        flag = wx.ALIGN_CENTER_HORIZONTAL |
00317                        wx.ALIGN_CENTER_VERTICAL |
00318                        wx.ALL,
00319                        border = 5)
00320         
00321         # main sizer
00322         sizer.Add(item = self.hbitmap,
00323                   proportion = 0,
00324                   flag = wx.ALIGN_CENTER_VERTICAL |
00325                   wx.ALIGN_CENTER_HORIZONTAL |
00326                   wx.ALL,
00327                   border = 3) # image
00328         sizer.Add(item = self.lwelcome, # welcome message
00329                   proportion = 0,
00330                   flag = wx.ALIGN_CENTER_VERTICAL |
00331                   wx.ALIGN_CENTER_HORIZONTAL |
00332                   wx.BOTTOM,
00333                   border=1)
00334         sizer.Add(item = self.ltitle, # title
00335                   proportion = 0,
00336                   flag = wx.ALIGN_CENTER_VERTICAL |
00337                   wx.ALIGN_CENTER_HORIZONTAL)
00338         sizer.Add(item = dbase_sizer, proportion = 0,
00339                   flag = wx.ALIGN_CENTER_HORIZONTAL |
00340                   wx.RIGHT | wx.LEFT | wx.EXPAND,
00341                   border = 20) # GISDBASE setting
00342         sizer.Add(item = location_sizer, proportion = 1,
00343                   flag = wx.RIGHT | wx.LEFT | wx.EXPAND,
00344                   border = 1)
00345         sizer.Add(item = btns_sizer, proportion = 0,
00346                   flag = wx.ALIGN_CENTER_VERTICAL |
00347                   wx.ALIGN_CENTER_HORIZONTAL |
00348                   wx.RIGHT | wx.LEFT,
00349                   border = 1)
00350         
00351         self.panel.SetAutoLayout(True)
00352         self.panel.SetSizer(sizer)
00353         sizer.Fit(self.panel)
00354         sizer.SetSizeHints(self)
00355         
00356         self.Layout()
00357 
00358     def _readGisRC(self):
00359         """
00360         Read variables from $HOME/.grassrc6 file
00361         """
00362 
00363         grassrc = {}
00364         
00365         gisrc = os.getenv("GISRC")
00366         
00367         if gisrc and os.path.isfile(gisrc):
00368             try:
00369                 rc = open(gisrc, "r")
00370                 for line in rc.readlines():
00371                     key, val = line.split(":", 1)
00372                     grassrc[key.strip()] = DecodeString(val.strip())
00373             finally:
00374                 rc.close()
00375         
00376         return grassrc
00377 
00378     def GetRCValue(self, value):
00379         """!Return GRASS variable (read from GISRC)
00380         """
00381         if self.grassrc.has_key(value):
00382             return self.grassrc[value]
00383         else:
00384             return None
00385         
00386     def OnWizard(self, event):
00387         """!Location wizard started"""
00388         from location_wizard.wizard import LocationWizard
00389         gWizard = LocationWizard(parent = self,
00390                                  grassdatabase = self.tgisdbase.GetValue())
00391         if gWizard.location !=  None:
00392             self.OnSetDatabase(event)
00393             self.UpdateMapsets(os.path.join(self.gisdbase, gWizard.location))
00394             self.lblocations.SetSelection(self.listOfLocations.index(gWizard.location))
00395             self.lbmapsets.SetSelection(0)
00396 
00397     def OnManageLoc(self, event):
00398         """!Location management choice control handler
00399         """
00400         sel = event.GetSelection()
00401         if sel ==  0:
00402             self.RenameMapset()
00403         elif sel ==  1:
00404             self.RenameLocation()
00405         elif sel ==  2:
00406             self.DeleteMapset()
00407         elif sel ==  3:
00408             self.DeleteLocation()
00409         
00410         event.Skip()
00411         
00412     def RenameMapset(self):
00413         """!Rename selected mapset
00414         """
00415         location = self.listOfLocations[self.lblocations.GetSelection()]
00416         mapset   = self.listOfMapsets[self.lbmapsets.GetSelection()]
00417         if mapset ==  'PERMANENT':
00418             GMessage(parent = self,
00419                      message = _('Mapset <PERMANENT> is required for valid GRASS location.\n\n'
00420                                  'This mapset cannot be renamed.'))
00421             return
00422         
00423         dlg = wx.TextEntryDialog(parent = self,
00424                                  message = _('Current name: %s\n\nEnter new name:') % mapset,
00425                                  caption = _('Rename selected mapset'))
00426         
00427         if dlg.ShowModal() ==  wx.ID_OK:
00428             newmapset = dlg.GetValue()
00429             if newmapset ==  mapset:
00430                 dlg.Destroy()
00431                 return
00432             
00433             if newmapset in self.listOfMapsets:
00434                 wx.MessageBox(parent = self,
00435                               caption = _('Message'),
00436                               message = _('Unable to rename mapset.\n\n'
00437                                         'Mapset <%s> already exists in location.') % newmapset,
00438                               style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
00439             else:
00440                 try:
00441                     os.rename(os.path.join(self.gisdbase, location, mapset),
00442                               os.path.join(self.gisdbase, location, newmapset))
00443                     self.OnSelectLocation(None)
00444                     self.lbmapsets.SetSelection(self.listOfMapsets.index(newmapset))
00445                 except StandardError, e:
00446                     wx.MessageBox(parent = self,
00447                                   caption = _('Error'),
00448                                   message = _('Unable to rename mapset.\n\n%s') % e,
00449                                   style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
00450             
00451         dlg.Destroy()
00452 
00453     def RenameLocation(self):
00454         """!Rename selected location
00455         """
00456         location = self.listOfLocations[self.lblocations.GetSelection()]
00457 
00458         dlg = wx.TextEntryDialog(parent = self,
00459                                  message = _('Current name: %s\n\nEnter new name:') % location,
00460                                  caption = _('Rename selected location'))
00461 
00462         if dlg.ShowModal() ==  wx.ID_OK:
00463             newlocation = dlg.GetValue()
00464             if newlocation ==  location:
00465                 dlg.Destroy()
00466                 return
00467 
00468             if newlocation in self.listOfLocations:
00469                 wx.MessageBox(parent = self,
00470                               caption = _('Message'),
00471                               message = _('Unable to rename location.\n\n'
00472                                         'Location <%s> already exists in GRASS database.') % newlocation,
00473                               style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
00474             else:
00475                 try:
00476                     os.rename(os.path.join(self.gisdbase, location),
00477                               os.path.join(self.gisdbase, newlocation))
00478                     self.UpdateLocations(self.gisdbase)
00479                     self.lblocations.SetSelection(self.listOfLocations.index(newlocation))
00480                     self.UpdateMapsets(newlocation)
00481                 except StandardError, e:
00482                     wx.MessageBox(parent = self,
00483                                   caption = _('Error'),
00484                                   message = _('Unable to rename location.\n\n%s') % e,
00485                                   style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
00486         
00487         dlg.Destroy()
00488 
00489     def DeleteMapset(self):
00490         """!Delete selected mapset
00491         """
00492         location = self.listOfLocations[self.lblocations.GetSelection()]
00493         mapset   = self.listOfMapsets[self.lbmapsets.GetSelection()]
00494         if mapset ==  'PERMANENT':
00495             GMessage(parent = self,
00496                      message = _('Mapset <PERMANENT> is required for valid GRASS location.\n\n'
00497                                  'This mapset cannot be deleted.'))
00498             return
00499         
00500         dlg = wx.MessageDialog(parent = self, message = _("Do you want to continue with deleting mapset <%(mapset)s> "
00501                                                       "from location <%(location)s>?\n\n"
00502                                                       "ALL MAPS included in this mapset will be "
00503                                                       "PERMANENTLY DELETED!") % {'mapset' : mapset,
00504                                                                                  'location' : location},
00505                                caption = _("Delete selected mapset"),
00506                                style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
00507 
00508         if dlg.ShowModal() ==  wx.ID_YES:
00509             try:
00510                 shutil.rmtree(os.path.join(self.gisdbase, location, mapset))
00511                 self.OnSelectLocation(None)
00512                 self.lbmapsets.SetSelection(0)
00513             except:
00514                 wx.MessageBox(message = _('Unable to delete mapset'))
00515 
00516         dlg.Destroy()
00517 
00518     def DeleteLocation(self):
00519         """
00520         Delete selected location
00521         """
00522 
00523         location = self.listOfLocations[self.lblocations.GetSelection()]
00524 
00525         dlg = wx.MessageDialog(parent = self, message = _("Do you want to continue with deleting "
00526                                                       "location <%s>?\n\n"
00527                                                       "ALL MAPS included in this location will be "
00528                                                       "PERMANENTLY DELETED!") % (location),
00529                                caption = _("Delete selected location"),
00530                                style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
00531 
00532         if dlg.ShowModal() ==  wx.ID_YES:
00533             try:
00534                 shutil.rmtree(os.path.join(self.gisdbase, location))
00535                 self.UpdateLocations(self.gisdbase)
00536                 self.lblocations.SetSelection(0)
00537                 self.OnSelectLocation(None)
00538                 self.lbmapsets.SetSelection(0)
00539             except:
00540                 wx.MessageBox(message = _('Unable to delete location'))
00541 
00542         dlg.Destroy()
00543 
00544     def UpdateLocations(self, dbase):
00545         """!Update list of locations"""
00546         try:
00547             self.listOfLocations = GetListOfLocations(dbase)
00548         except UnicodeEncodeError:
00549             wx.MessageBox(parent = self, caption = _("Error"),
00550                           message = _("Unable to set GRASS database. "
00551                                       "Check your locale settings."),
00552                           style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
00553         
00554         self.lblocations.Clear()
00555         self.lblocations.InsertItems(self.listOfLocations, 0)
00556 
00557         if len(self.listOfLocations) > 0:
00558             self.lblocations.SetSelection(0)
00559         else:
00560             self.lblocations.SetSelection(wx.NOT_FOUND)
00561 
00562         return self.listOfLocations
00563 
00564     def UpdateMapsets(self, location):
00565         """!Update list of mapsets"""
00566         self.FormerMapsetSelection = wx.NOT_FOUND # for non-selectable item
00567         
00568         self.listOfMapsetsSelectable = list()
00569         self.listOfMapsets = GetListOfMapsets(self.gisdbase, location)
00570         
00571         self.lbmapsets.Clear()
00572         
00573         # disable mapset with denied permission
00574         locationName = os.path.basename(location)
00575         
00576         ret = RunCommand('g.mapset',
00577                          read = True,
00578                          flags = 'l',
00579                          location = locationName,
00580                          gisdbase = self.gisdbase)
00581             
00582         if ret:
00583             for line in ret.splitlines():
00584                 self.listOfMapsetsSelectable += line.split(' ')
00585         else:
00586             RunCommand("g.gisenv",
00587                        set = "GISDBASE=%s" % self.gisdbase)
00588             RunCommand("g.gisenv",
00589                        set = "LOCATION_NAME=%s" % locationName)
00590             RunCommand("g.gisenv",
00591                        set = "MAPSET=PERMANENT")
00592             # first run only
00593             self.listOfMapsetsSelectable = copy.copy(self.listOfMapsets)
00594         
00595         disabled = []
00596         idx = 0
00597         for mapset in self.listOfMapsets:
00598             if mapset not in self.listOfMapsetsSelectable or \
00599                     os.path.isfile(os.path.join(self.gisdbase,
00600                                                 locationName,
00601                                                 mapset, ".gislock")):
00602                 disabled.append(idx)
00603             idx +=  1
00604         
00605         self.lbmapsets.InsertItems(self.listOfMapsets, 0, disabled = disabled)
00606         
00607         return self.listOfMapsets
00608 
00609     def OnSelectLocation(self, event):
00610         """!Location selected"""
00611         if event:
00612             self.lblocations.SetSelection(event.GetIndex())
00613             
00614         if self.lblocations.GetSelection() !=  wx.NOT_FOUND:
00615             self.UpdateMapsets(os.path.join(self.gisdbase,
00616                                             self.listOfLocations[self.lblocations.GetSelection()]))
00617         else:
00618             self.listOfMapsets = []
00619         
00620         disabled = []
00621         idx = 0
00622         try:
00623             locationName = self.listOfLocations[self.lblocations.GetSelection()]
00624         except IndexError:
00625             locationName = ''
00626         
00627         for mapset in self.listOfMapsets:
00628             if mapset not in self.listOfMapsetsSelectable or \
00629                     os.path.isfile(os.path.join(self.gisdbase,
00630                                                 locationName,
00631                                                 mapset, ".gislock")):
00632                 disabled.append(idx)
00633             idx +=  1
00634 
00635         self.lbmapsets.Clear()
00636         self.lbmapsets.InsertItems(self.listOfMapsets, 0, disabled = disabled)
00637 
00638         if len(self.listOfMapsets) > 0:
00639             self.lbmapsets.SetSelection(0)
00640             if locationName:
00641                 # enable start button when location and mapset is selected
00642                 self.bstart.Enable()
00643                 self.bmapset.Enable()
00644                 self.manageloc.Enable()
00645         else:
00646             self.lbmapsets.SetSelection(wx.NOT_FOUND)
00647             self.bstart.Enable(False)
00648             self.bmapset.Enable(False)
00649             self.manageloc.Enable(False)
00650         
00651     def OnSelectMapset(self, event):
00652         """!Mapset selected"""
00653         self.lbmapsets.SetSelection(event.GetIndex())
00654 
00655         if event.GetText() not in self.listOfMapsetsSelectable:
00656             self.lbmapsets.SetSelection(self.FormerMapsetSelection)
00657         else:
00658             self.FormerMapsetSelection = event.GetIndex()
00659             event.Skip()
00660 
00661     def OnSetDatabase(self, event):
00662         """!Database set"""
00663         self.gisdbase = self.tgisdbase.GetValue()
00664         
00665         self.UpdateLocations(self.gisdbase)
00666 
00667         self.OnSelectLocation(None)
00668 
00669     def OnBrowse(self, event):
00670         """'Browse' button clicked"""
00671         grassdata = None
00672 
00673         dlg = wx.DirDialog(self, _("Choose GIS Data Directory:"),
00674                            style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
00675         if dlg.ShowModal() ==  wx.ID_OK:
00676             self.gisdbase = dlg.GetPath()
00677             self.tgisdbase.SetValue(self.gisdbase)
00678             self.OnSetDatabase(event)
00679 
00680         dlg.Destroy()
00681 
00682     def OnCreateMapset(self,event):
00683         """!Create new mapset"""
00684         self.gisdbase = self.tgisdbase.GetValue()
00685         location = self.listOfLocations[self.lblocations.GetSelection()]
00686 
00687         dlg = wx.TextEntryDialog(parent = self,
00688                                  message = _('Enter name for new mapset:'),
00689                                  caption = _('Create new mapset'))
00690 
00691         if dlg.ShowModal() ==  wx.ID_OK:
00692             mapset = dlg.GetValue()
00693             if mapset in self.listOfMapsets:
00694                 GMessage(parent = self,
00695                          message = _("Mapset <%s> already exists.") % mapset)
00696                 return
00697             
00698             try:
00699                 os.mkdir(os.path.join(self.gisdbase, location, mapset))
00700                 # copy WIND file and its permissions from PERMANENT and set permissions to u+rw,go+r
00701                 shutil.copy(os.path.join(self.gisdbase, location, 'PERMANENT', 'WIND'),
00702                             os.path.join(self.gisdbase, location, mapset))
00703                 # os.chmod(os.path.join(database,location,mapset,'WIND'), 0644)
00704                 self.OnSelectLocation(None)
00705                 self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset))
00706             except StandardError, e:
00707                 dlg = wx.MessageDialog(parent = self, message = _("Unable to create new mapset: %s") % e,
00708                                        caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
00709                 dlg.ShowModal()
00710                 dlg.Destroy()
00711                 return False
00712         
00713         self.bstart.SetFocus()
00714         
00715         return True
00716 
00717     def OnStart(self, event):
00718         """'Start GRASS' button clicked"""
00719         dbase    = self.tgisdbase.GetValue()
00720         location = self.listOfLocations[self.lblocations.GetSelection()]
00721         mapset   = self.listOfMapsets[self.lbmapsets.GetSelection()]
00722         
00723         lockfile = os.path.join(dbase, location, mapset, '.gislock')
00724         if os.path.isfile(lockfile):
00725             dlg = wx.MessageDialog(parent = self,
00726                                    message = _("GRASS is already running in selected mapset <%(mapset)s>\n"
00727                                                "(file %(lock)s found).\n\n"
00728                                                "Concurrent use not allowed.\n\n"
00729                                                "Do you want to try to remove .gislock (note that you "
00730                                                "need permission for this operation) and continue?") % 
00731                                    { 'mapset' : mapset, 'lock' : lockfile },
00732                                    caption = _("Lock file found"),
00733                                    style = wx.YES_NO | wx.NO_DEFAULT |
00734                                    wx.ICON_QUESTION | wx.CENTRE)
00735             
00736             ret = dlg.ShowModal()
00737             dlg.Destroy()
00738             if ret == wx.ID_YES:
00739                 dlg1 = wx.MessageDialog(parent = self,
00740                                         message = _("ARE YOU REALLY SURE?\n\n"
00741                                                     "If you really are running another GRASS session doing this "
00742                                                     "could corrupt your data. Have another look in the processor "
00743                                                     "manager just to be sure..."),
00744                                         caption = _("Lock file found"),
00745                                         style = wx.YES_NO | wx.NO_DEFAULT |
00746                                         wx.ICON_QUESTION | wx.CENTRE)
00747                 
00748                 ret = dlg1.ShowModal()
00749                 dlg1.Destroy()
00750                 
00751                 if ret == wx.ID_YES:
00752                     try:
00753                         os.remove(lockfile)
00754                     except IOError, e:
00755                         GError(_("Unable to remove '%(lock)s'.\n\n"
00756                                  "Details: %(reason)s") % { 'lock' : lockfile, 'reason' : e})
00757                 else:
00758                     return
00759             else:
00760                 return
00761         
00762         RunCommand("g.gisenv",
00763                    set = "GISDBASE=%s" % dbase)
00764         RunCommand("g.gisenv",
00765                    set = "LOCATION_NAME=%s" % location)
00766         RunCommand("g.gisenv",
00767                    set = "MAPSET=%s" % mapset)
00768         
00769         self.Destroy()
00770         sys.exit(0)
00771 
00772     def OnExit(self, event):
00773         """'Exit' button clicked"""
00774         self.Destroy()
00775         sys.exit (2)
00776 
00777     def OnHelp(self, event):
00778         """'Help' button clicked"""
00779         # help text in lib/init/helptext.html
00780         file = os.path.join(self.gisbase, "docs", "html", "helptext.html")
00781 
00782         helpFrame = HelpFrame(parent = self, id = wx.ID_ANY,
00783                               title = _("GRASS Quickstart"),
00784                               size = (640, 480),
00785                               file = file)
00786         helpFrame.Show(True)
00787 
00788         event.Skip()
00789 
00790     def OnCloseWindow(self, event):
00791         """!Close window event"""
00792         event.Skip()
00793         sys.exit(2)
00794 
00795 class GListBox(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
00796     """!Use wx.ListCtrl instead of wx.ListBox, different style for
00797     non-selectable items (e.g. mapsets with denied permission)"""
00798     def __init__(self, parent, id, size,
00799                  choices, disabled = []):
00800         wx.ListCtrl.__init__(self, parent, id, size = size,
00801                              style = wx.LC_REPORT | wx.LC_NO_HEADER | wx.LC_SINGLE_SEL |
00802                              wx.BORDER_SUNKEN)
00803         
00804         listmix.ListCtrlAutoWidthMixin.__init__(self)
00805         
00806         self.InsertColumn(0, '')
00807         
00808         self.selected = wx.NOT_FOUND
00809         
00810         self._LoadData(choices, disabled)
00811         
00812     def _LoadData(self, choices, disabled = []):
00813         """!Load data into list
00814         
00815         @param choices list of item
00816         @param disabled list of indeces of non-selectable items
00817         """
00818         idx = 0
00819         for item in choices:
00820             index = self.InsertStringItem(sys.maxint, item)
00821             self.SetStringItem(index, 0, item)
00822             
00823             if idx in disabled:
00824                 self.SetItemTextColour(idx, wx.Colour(150, 150, 150))
00825             idx +=  1
00826         
00827     def Clear(self):
00828         self.DeleteAllItems()
00829         
00830     def InsertItems(self, choices, pos, disabled = []):
00831         self._LoadData(choices, disabled)
00832         
00833     def SetSelection(self, item, force = False):
00834         if item !=  wx.NOT_FOUND and \
00835                 (platform.system() !=  'Windows' or force):
00836             ### Windows -> FIXME
00837             self.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
00838         
00839         self.selected = item
00840         
00841     def GetSelection(self):
00842         return self.selected
00843         
00844 class StartUp(wx.App):
00845     """!Start-up application"""
00846 
00847     def OnInit(self):
00848         wx.InitAllImageHandlers()
00849         StartUp = GRASSStartup()
00850         StartUp.CenterOnScreen()
00851         self.SetTopWindow(StartUp)
00852         StartUp.Show()
00853         
00854         if StartUp.GetRCValue("LOCATION_NAME") ==  "<UNKNOWN>":
00855             wx.MessageBox(parent = StartUp,
00856                           caption = _('Starting GRASS for the first time'),
00857                           message = _('GRASS needs a directory in which to store its data. '
00858                                     'Create one now if you have not already done so. '
00859                                     'A popular choice is "grassdata", located in '
00860                                     'your home directory.'),
00861                           style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
00862             
00863             StartUp.OnBrowse(None)
00864         
00865         return 1
00866 
00867 if __name__ ==  "__main__":
00868     if os.getenv("GISBASE") is None:
00869         sys.exit("Failed to start GUI, GRASS GIS is not running.")
00870         
00871     import gettext
00872     gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
00873     
00874     GRASSStartUp = StartUp(0)
00875     GRASSStartUp.MainLoop()