GRASS Programmer's Manual  6.4.2(2012)
dbm.py
Go to the documentation of this file.
00001 """!
00002 @package dbm.py
00003 
00004 @brief GRASS Attribute Table Manager
00005 
00006 This program is based on FileHunter, published in 'The wxPython Linux
00007 Tutorial' on wxPython WIKI pages.
00008 
00009 It also uses some functions at
00010 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/426407
00011 
00012 @code
00013 python dbm.py vector@mapset
00014 @endcode
00015 
00016 List of classes:
00017  - Log
00018  - VirtualAttributeList
00019  - AttributeManager
00020 
00021 (C) 2007-2009, 2011 by the GRASS Development Team
00022 
00023 This program is free software under the GNU General Public
00024 License (>=v2). Read the file COPYING that comes with GRASS
00025 for details.
00026 
00027 @author Jachym Cepicky <jachym.cepicky gmail.com>
00028 @author Martin Landa <landa.martin gmail.com>
00029 """
00030 
00031 import sys
00032 import os
00033 import locale
00034 import tempfile
00035 import copy
00036 import types
00037 
00038 ### i18N
00039 import gettext
00040 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
00041 
00042 import globalvar
00043 import wx
00044 import wx.lib.mixins.listctrl as listmix
00045 import wx.lib.flatnotebook as FN
00046 
00047 import grass.script as grass
00048 
00049 import sqlbuilder
00050 import gcmd
00051 import utils
00052 import gdialogs
00053 import dbm_base
00054 from debug import Debug
00055 from dbm_dialogs import ModifyTableRecord
00056 from preferences import globalSettings as UserSettings
00057 
00058 class Log:
00059     """
00060     The log output is redirected to the status bar of the containing frame.
00061     """
00062     def __init__(self, parent):
00063         self.parent = parent
00064 
00065     def write(self, text_string):
00066         """!Update status bar"""
00067         self.parent.SetStatusText(text_string.strip())
00068 
00069 
00070 class VirtualAttributeList(wx.ListCtrl,
00071                            listmix.ListCtrlAutoWidthMixin,
00072                            listmix.ColumnSorterMixin):
00073     """
00074     Support virtual list class
00075     """
00076     def __init__(self, parent, log, mapDBInfo, layer):
00077         #
00078         # initialize variables
00079         #
00080         self.parent  = parent
00081         self.log     = log
00082         self.mapDBInfo = mapDBInfo
00083         self.layer   = layer
00084         
00085         self.columns = {} # <- LoadData()
00086 
00087         wx.ListCtrl.__init__(self, parent=parent, id=wx.ID_ANY,
00088                              style=wx.LC_REPORT | wx.LC_HRULES |
00089                              wx.LC_VRULES | wx.LC_VIRTUAL | wx.LC_SORT_ASCENDING)
00090         
00091         try:
00092             keyColumn = self.LoadData(layer)
00093         except gcmd.GException, e:
00094             GError(parent = self,
00095                    message = e.value)
00096             return
00097         
00098         #
00099         # add some attributes (colourful background for each item rows)
00100         #
00101         self.attr1 = wx.ListItemAttr()
00102         self.attr1.SetBackgroundColour(wx.Colour(238,238,238))
00103         self.attr2 = wx.ListItemAttr()
00104         self.attr2.SetBackgroundColour("white")
00105         self.il = wx.ImageList(16, 16)
00106         self.sm_up = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_UP,   wx.ART_TOOLBAR,
00107                                                           (16,16)))
00108         self.sm_dn = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR,
00109                                                           (16,16)))
00110         self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
00111         
00112         # setup mixins
00113         listmix.ListCtrlAutoWidthMixin.__init__(self)
00114         listmix.ColumnSorterMixin.__init__(self, len(self.columns))
00115 
00116         # sort item by category (id)
00117         if keyColumn > -1:
00118             self.SortListItems(col=keyColumn, ascending=True) 
00119         else:
00120             self.SortListItems(col=0, ascending=True) 
00121         
00122         # events
00123         self.Bind(wx.EVT_LIST_ITEM_SELECTED,   self.OnItemSelected)
00124         self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
00125         self.Bind(wx.EVT_LIST_COL_CLICK,       self.OnColumnSort)     
00126         self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColumnMenu)     
00127         
00128     def Update(self, mapDBInfo):
00129         """!Update list according new mapDBInfo description"""
00130         self.mapDBInfo = mapDBInfo
00131         self.LoadData(self.layer)
00132 
00133     def LoadData(self, layer, columns=None, where=None, sql=None):
00134         """!Load data into list
00135 
00136         @param layer layer number
00137         @param columns list of columns for output (-> v.db.select)
00138         @param where where statement (-> v.db.select)
00139         @param sql full sql statement (-> db.select)
00140         
00141         @return id of key column 
00142         @return -1 if key column is not displayed
00143         """
00144         self.log.write(_("Loading data..."))
00145         
00146         tableName    = self.mapDBInfo.layers[layer]['table']
00147         keyColumn    = self.mapDBInfo.layers[layer]['key']
00148         try:
00149             self.columns = self.mapDBInfo.tables[tableName]
00150         except KeyError:
00151             raise gcmd.GException(_("Attribute table <%s> not found. "
00152                                     "For creating the table switch to "
00153                                     "'Manage layers' tab.") % tableName)
00154         
00155         if not columns:
00156             columns = self.mapDBInfo.GetColumns(tableName)
00157         else:
00158             all = self.mapDBInfo.GetColumns(tableName)
00159             for col in columns:
00160                 if col not in all:
00161                     wx.MessageBox(parent=self,
00162                                   message=_("Column <%(column)s> not found in "
00163                                             "in the table <%(table)s>.") % \
00164                                       { 'column' : col, 'table' : tableName },
00165                                   caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
00166                     return
00167         
00168         try:
00169             # for maps connected via v.external
00170             keyId = columns.index(keyColumn)
00171         except:
00172             keyId = -1
00173         
00174         #
00175         # read data
00176         #
00177         # FIXME: Max. number of rows, while the GUI is still usable
00178 
00179         # stdout can be very large, do not use PIPE, redirect to temp file
00180         # TODO: more effective way should be implemented...
00181         outFile = tempfile.NamedTemporaryFile(mode='w+b')
00182         
00183         if sql:
00184             ret = gcmd.RunCommand('db.select',
00185                                   quiet = True,
00186                                   parent = self,
00187                                   flags = 'c',
00188                                   sql = sql,
00189                                   output = outFile.name)
00190         else:
00191             if columns:
00192                 ret = gcmd.RunCommand('v.db.select',
00193                                       quiet = True,
00194                                       flags = 'c',
00195                                       map = self.mapDBInfo.map,
00196                                       layer = layer,
00197                                       columns = ','.join(columns),
00198                                       where = where,
00199                                       stdout=outFile)
00200             else:
00201                 ret = gcmd.RunCommand('v.db.select',
00202                                       quiet = True,
00203                                       flags = 'c',
00204                                       map = self.mapDBInfo.map,
00205                                       layer = layer,
00206                                       where = where,
00207                                       stdout=outFile) 
00208         
00209         # These two should probably be passed to init more cleanly
00210         # setting the numbers of items = number of elements in the dictionary
00211         self.itemDataMap  = {}
00212         self.itemIndexMap = []
00213         self.itemCatsMap  = {}
00214         
00215         self.DeleteAllItems()
00216         
00217         # self.ClearAll()
00218         for i in range(self.GetColumnCount()):
00219             self.DeleteColumn(0)
00220         
00221         i = 0
00222         info = wx.ListItem()
00223         info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
00224         info.m_image = -1
00225         info.m_format = 0
00226         for column in columns:
00227             info.m_text = column
00228             self.InsertColumnInfo(i, info)
00229             i += 1
00230             
00231             if i >= 256:
00232                 self.log.write(_("Can display only 256 columns."))
00233         
00234         i = 0
00235         outFile.seek(0)
00236         
00237         while True:
00238             # os.linesep doesn't work here (MSYS)
00239             record = outFile.readline().replace('\n', '')
00240             
00241             if not record:
00242                 break
00243            
00244             self.AddDataRow(i, record, columns, keyId)
00245 
00246             i += 1
00247             if i >= 100000:
00248                 self.log.write(_("Limit 100000 records."))
00249                 break
00250         
00251         self.SetItemCount(i)
00252         
00253         i = 0
00254         for col in columns:
00255             width = self.columns[col]['length'] * 6 # FIXME
00256             if width < 60:
00257                 width = 60
00258             if width > 300:
00259                 width = 300
00260             self.SetColumnWidth(col=i, width=width)
00261             i += 1
00262         
00263         self.SendSizeEvent()
00264         
00265         self.log.write(_("Number of loaded records: %d") % \
00266                            self.GetItemCount())
00267         
00268         return keyId
00269     
00270     def AddDataRow(self, i, record, columns, keyId):
00271         """!Add row to the data list"""
00272         self.itemDataMap[i] = []
00273         keyColumn = self.mapDBInfo.layers[self.layer]['key']
00274         j = 0
00275         cat = None
00276         
00277         if keyColumn == 'OGC_FID':
00278             self.itemDataMap[i].append(i+1)
00279             j += 1
00280             cat = i + 1
00281         
00282         for value in record.split('|'):
00283             if self.columns[columns[j]]['ctype'] != types.StringType:
00284                 try:
00285                     ### casting disabled (2009/03)
00286                     ### self.itemDataMap[i].append(self.columns[columns[j]]['ctype'](value))
00287                     self.itemDataMap[i].append(value)
00288                 except ValueError:
00289                     self.itemDataMap[i].append(_('Unknown value'))
00290             else:
00291                 # encode string values
00292                 try:
00293                     self.itemDataMap[i].append(dbm_base.unicodeValue(value))
00294                 except UnicodeDecodeError:
00295                     self.itemDataMap[i].append(_("Unable to decode value. "
00296                                                  "Set encoding in GUI preferences ('Attributes')."))
00297                 
00298             if not cat and keyId > -1 and keyId == j:
00299                 try:
00300                     cat = self.columns[columns[j]]['ctype'] (value)
00301                 except ValueError, e:
00302                     cat = -1
00303                     gcmd.GError(parent = self,
00304                                 message=_("Error loading attribute data. "
00305                                           "Record number: %(rec)d. Unable to convert value '%(val)s' in "
00306                                           "key column (%(key)s) to integer.\n\n"
00307                                           "Details: %(detail)s") % \
00308                                     { 'rec' : i + 1, 'val' : value,
00309                                       'key' : keyColumn, 'detail' : e})
00310             j += 1
00311         
00312         self.itemIndexMap.append(i)
00313         if keyId > -1: # load cats only when LoadData() is called first time
00314             self.itemCatsMap[i] = cat
00315         
00316     def OnItemSelected(self, event):
00317         """!Item selected. Add item to selected cats..."""
00318         #         cat = int(self.GetItemText(event.m_itemIndex))
00319         #         if cat not in self.selectedCats:
00320         #             self.selectedCats.append(cat)
00321         #             self.selectedCats.sort()
00322         
00323         event.Skip()
00324 
00325     def OnItemDeselected(self, event):
00326         """!Item deselected. Remove item from selected cats..."""
00327         #         cat = int(self.GetItemText(event.m_itemIndex))
00328         #         if cat in self.selectedCats:
00329         #             self.selectedCats.remove(cat)
00330         #             self.selectedCats.sort()
00331 
00332         event.Skip()
00333 
00334     def GetSelectedItems(self):
00335         """!Return list of selected items (category numbers)"""
00336         cats = []
00337         item = self.GetFirstSelected()
00338         while item != -1:
00339             cats.append(self.GetItemText(item))
00340             item = self.GetNextSelected(item)
00341 
00342         return cats
00343 
00344     def GetColumnText(self, index, col):
00345         """!Return column text"""
00346         item = self.GetItem(index, col)
00347         return item.GetText()
00348 
00349     def GetListCtrl(self):
00350         """!Returt list"""
00351         return self
00352 
00353     def OnGetItemText(self, item, col):
00354         """!Get item text"""
00355         index = self.itemIndexMap[item]
00356         s = self.itemDataMap[index][col]
00357         return s
00358 
00359     def OnGetItemAttr(self, item):
00360         """!Get item attributes"""
00361         if ( item % 2) == 0:
00362             return self.attr2
00363         else:
00364             return self.attr1
00365 
00366     def OnColumnMenu(self, event):
00367         """!Column heading right mouse button -> pop-up menu"""
00368         self._col = event.GetColumn()
00369         
00370         popupMenu = wx.Menu()
00371 
00372         if not hasattr (self, "popupID1"):
00373             self.popupID1 = wx.NewId()
00374             self.popupID2 = wx.NewId()
00375             self.popupID3 = wx.NewId()
00376             self.popupID4 = wx.NewId()
00377             self.popupID5 = wx.NewId()
00378             self.popupID6 = wx.NewId()
00379             self.popupID7 = wx.NewId()
00380             self.popupID8 = wx.NewId()
00381             self.popupID9 = wx.NewId()
00382             self.popupID10 = wx.NewId()
00383             self.popupID11 = wx.NewId()
00384             self.popupID12 = wx.NewId()
00385         
00386         popupMenu.Append(self.popupID1, text=_("Sort ascending"))
00387         popupMenu.Append(self.popupID2, text=_("Sort descending"))
00388         popupMenu.AppendSeparator()
00389         subMenu = wx.Menu()
00390         popupMenu.AppendMenu(self.popupID3, _("Calculate (only numeric columns)"),
00391                              subMenu)
00392         if not self.log.parent.editable or \
00393                 self.columns[self.GetColumn(self._col).GetText()]['ctype'] not in (types.IntType, types.FloatType):
00394             popupMenu.Enable(self.popupID3, False)
00395         
00396         subMenu.Append(self.popupID4,  text=_("Area size"))
00397         subMenu.Append(self.popupID5,  text=_("Line length"))
00398         subMenu.Append(self.popupID6,  text=_("Compactness of an area"))
00399         subMenu.Append(self.popupID7,  text=_("Fractal dimension of boundary defining a polygon"))
00400         subMenu.Append(self.popupID8,  text=_("Perimeter length of an area"))
00401         subMenu.Append(self.popupID9,  text=_("Number of features for each category"))
00402         subMenu.Append(self.popupID10, text=_("Slope steepness of 3D line"))
00403         subMenu.Append(self.popupID11, text=_("Line sinuousity"))
00404         subMenu.Append(self.popupID12, text=_("Line azimuth"))
00405         
00406         self.Bind (wx.EVT_MENU, self.OnColumnSortAsc,  id=self.popupID1)
00407         self.Bind (wx.EVT_MENU, self.OnColumnSortDesc, id=self.popupID2)
00408         for id in (self.popupID4, self.popupID5, self.popupID6,
00409                    self.popupID7, self.popupID8, self.popupID9,
00410                    self.popupID10, self.popupID11, self.popupID12):
00411             self.Bind(wx.EVT_MENU, self.OnColumnCompute, id = id)
00412         
00413         self.PopupMenu(popupMenu)
00414         popupMenu.Destroy()
00415 
00416     def OnColumnSort(self, event):
00417         """!Column heading left mouse button -> sorting"""
00418         self._col = event.GetColumn()
00419         
00420         self.ColumnSort()
00421         
00422         event.Skip()
00423 
00424     def OnColumnSortAsc(self, event):
00425         """!Sort values of selected column (ascending)"""
00426         self.SortListItems(col = self._col, ascending = True)
00427         event.Skip()
00428 
00429     def OnColumnSortDesc(self, event):
00430         """!Sort values of selected column (descending)"""
00431         self.SortListItems(col = self._col, ascending = False)
00432         event.Skip()
00433         
00434     def OnColumnCompute(self, event):
00435         """!Compute values of selected column"""
00436         id = event.GetId()
00437         
00438         option = None
00439         if id == self.popupID4:
00440             option = 'area'
00441         elif id == self.popupID5:
00442             option = 'length'
00443         elif id == self.popupID6:
00444             option = 'compact'
00445         elif id == self.popupID7:
00446             option = 'fd'
00447         elif id == self.popupID8:
00448             option = 'perimeter'
00449         elif id == self.popupID9:
00450             option = 'count'
00451         elif id == self.popupID10:
00452             option = 'slope'
00453         elif id == self.popupID11:
00454             option = 'sinuous'
00455         elif id == self.popupID12:
00456             option = 'azimuth'
00457         
00458         if not option:
00459             return
00460         
00461         gcmd.RunCommand('v.to.db',
00462                         parent = self.parent,
00463                         map = self.mapDBInfo.map,
00464                         layer = self.layer, 
00465                         option = option,
00466                         columns = self.GetColumn(self._col).GetText())
00467         
00468         self.LoadData(self.layer)
00469         
00470     def ColumnSort(self):
00471         """!Sort values of selected column (self._col)"""
00472         # remove duplicated arrow symbol from column header
00473         # FIXME: should be done automatically
00474         info = wx.ListItem()
00475         info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE
00476         info.m_image = -1
00477         for column in range(self.GetColumnCount()):
00478             info.m_text = self.GetColumn(column).GetText()
00479             self.SetColumn(column, info)
00480         
00481     def SortItems(self, sorter=cmp):
00482         """!Sort items"""
00483         items = list(self.itemDataMap.keys())
00484         items.sort(self.Sorter)
00485         self.itemIndexMap = items
00486 
00487         # redraw the list
00488         self.Refresh()
00489         
00490     def Sorter(self, key1, key2):
00491         colName = self.GetColumn(self._col).GetText()
00492         ascending = self._colSortFlag[self._col]
00493         try:
00494             item1 = self.columns[colName]["ctype"](self.itemDataMap[key1][self._col])
00495             item2 = self.columns[colName]["ctype"](self.itemDataMap[key2][self._col])
00496         except ValueError:
00497             item1 = self.itemDataMap[key1][self._col]
00498             item2 = self.itemDataMap[key2][self._col]
00499 
00500         if type(item1) == type('') or type(item2) == type(''):
00501             cmpVal = locale.strcoll(str(item1), str(item2))
00502         else:
00503             cmpVal = cmp(item1, item2)
00504 
00505 
00506         # If the items are equal then pick something else to make the sort value unique
00507         if cmpVal == 0:
00508             cmpVal = apply(cmp, self.GetSecondarySortValues(self._col, key1, key2))
00509         
00510         if ascending:
00511             return cmpVal
00512         else:
00513             return -cmpVal
00514 
00515     def GetSortImages(self):
00516         """!Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py"""
00517         return (self.sm_dn, self.sm_up)
00518 
00519     def IsEmpty(self):
00520         """!Check if list if empty"""
00521         if self.columns:
00522             return False
00523         
00524         return True
00525     
00526 class AttributeManager(wx.Frame):
00527     """
00528     GRASS Attribute manager main window
00529     """
00530     def __init__(self, parent, id=wx.ID_ANY,
00531                  size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE,
00532                  title=None, vectorName=None, item=None, log=None, selection = 0):
00533 
00534         self.vectorName = vectorName
00535         self.parent     = parent # GMFrame
00536         self.treeItem   = item   # item in layer tree
00537         if self.parent and self.parent.GetName() == "LayerManager" and \
00538                 self.treeItem and not self.vectorName:
00539             maptree = self.parent.curr_page.maptree
00540             name = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetName()
00541             self.vectorName = name
00542         
00543         # vector attributes can be changed only if vector map is in
00544         # the current mapset
00545         if grass.find_file(name = self.vectorName, element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
00546             self.editable = True
00547         else:
00548             self.editable = False
00549         
00550         # FIXME: editing is currently broken on wingrass (bug #1270)
00551         if sys.platform == 'win32':
00552             self.editable = False
00553 
00554         self.cmdLog     = log    # self.parent.goutput
00555         
00556         wx.Frame.__init__(self, parent, id, style=style)
00557 
00558         # title
00559         if not title:
00560             self.SetTitle("%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
00561                                          self.vectorName))
00562         else:
00563             self.SetTitle(title)
00564         
00565         # icon
00566         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'), wx.BITMAP_TYPE_ICO))
00567 
00568         self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
00569 
00570         try:
00571             self.map        = self.parent.curr_page.maptree.Map
00572             self.mapdisplay = self.parent.curr_page.maptree.mapdisplay
00573         except:
00574             self.map = self.mapdisplay = None
00575         
00576         # status bar log class
00577         self.log = Log(self) # -> statusbar
00578 
00579         # query map layer (if parent (GMFrame) is given)
00580         self.qlayer = None
00581 
00582         # -> layers / tables description
00583         self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)
00584 
00585         # sqlbuilder
00586         self.builder = None
00587         
00588         if len(self.mapDBInfo.layers.keys()) == 0:
00589             wx.MessageBox(parent=self.parent,
00590                           message=_("Database connection for vector map <%s> "
00591                                     "is not defined in DB file. "
00592                                     "You can define new connection in "
00593                                     "'Manage layers' tab.") % self.vectorName,
00594                           caption=_("Attribute Table Manager"),
00595                           style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
00596 
00597         #
00598         # list of command/SQL statements to be performed
00599         #
00600         self.listOfCommands      = []
00601         self.listOfSQLStatements = []
00602 
00603         self.CreateStatusBar(number=1)
00604 
00605         # set up virtual lists (each layer)
00606         ### {layer: list, widgets...}
00607         self.layerPage = {}
00608 
00609         # auinotebook (browse, manage, settings)
00610         #self.notebook = wx.aui.AuiNotebook(parent=self, id=wx.ID_ANY,
00611         #                                   style=wx.aui.AUI_NB_BOTTOM)
00612         # really needed (ML)
00613         # self.notebook.SetFont(wx.Font(10, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.NORMAL, 0, ''))
00614 
00615         if globalvar.hasAgw:
00616             self.notebook = FN.FlatNotebook(parent = self.panel, id = wx.ID_ANY,
00617                                             agwStyle = FN.FNB_BOTTOM |
00618                                             FN.FNB_NO_NAV_BUTTONS |
00619                                             FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON)
00620         else:
00621             self.notebook = FN.FlatNotebook(parent = self.panel, id = wx.ID_ANY,
00622                                             style = FN.FNB_BOTTOM |
00623                                             FN.FNB_NO_NAV_BUTTONS |
00624                                             FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON)
00625         
00626         if globalvar.hasAgw:
00627             dbmStyle = { 'agwStyle' : globalvar.FNPageStyle }
00628         else:
00629             dbmStyle = { 'style' : globalvar.FNPageStyle }
00630         
00631         self.browsePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
00632                                           **dbmStyle)
00633         # self.notebook.AddPage(self.browsePage, caption=_("Browse data"))
00634         self.notebook.AddPage(self.browsePage, text=_("Browse data")) # FN
00635         self.browsePage.SetTabAreaColour(globalvar.FNPageColor)
00636 
00637         self.manageTablePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
00638                                                **dbmStyle)
00639         #self.notebook.AddPage(self.manageTablePage, caption=_("Manage tables"))
00640         self.notebook.AddPage(self.manageTablePage, text=_("Manage tables")) # FN
00641         if not self.editable:
00642             self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
00643         self.manageTablePage.SetTabAreaColour(globalvar.FNPageColor)
00644 
00645         self.manageLayerPage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
00646                                                **dbmStyle)
00647         #self.notebook.AddPage(self.manageLayerPage, caption=_("Manage layers"))
00648         self.notebook.AddPage(self.manageLayerPage, text=_("Manage layers")) # FN
00649         self.manageLayerPage.SetTabAreaColour(globalvar.FNPageColor)
00650         if not self.editable:
00651             self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
00652         
00653         self.__createBrowsePage()
00654         self.__createManageTablePage()
00655         self.__createManageLayerPage()
00656         wx.CallAfter(self.notebook.SetSelection, selection)
00657         
00658         #
00659         # buttons
00660         #
00661         self.btnQuit   = wx.Button(parent=self.panel, id=wx.ID_EXIT)
00662         self.btnQuit.SetToolTipString(_("Close Attribute Table Manager"))
00663         self.btnReload = wx.Button(parent=self.panel, id=wx.ID_REFRESH)
00664         self.btnReload.SetToolTipString(_("Reload attribute data (selected layer only)"))
00665 
00666         # events
00667         self.btnQuit.Bind(wx.EVT_BUTTON,   self.OnCloseWindow)
00668         self.btnReload.Bind(wx.EVT_BUTTON, self.OnDataReload)
00669         self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
00670         self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.browsePage)
00671         self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.manageTablePage)
00672         
00673         # do layout
00674         self.__layout()
00675 
00676         # self.SetMinSize(self.GetBestSize())
00677         self.SetSize((680, 550)) # FIXME hard-coded size
00678         self.SetMinSize(self.GetSize())
00679 
00680     def __createBrowsePage(self, onlyLayer=-1):
00681         """!Create browse tab page"""
00682         for layer in self.mapDBInfo.layers.keys():
00683             if onlyLayer > 0 and layer != onlyLayer:
00684                 continue
00685 
00686             panel = wx.Panel(parent=self.browsePage, id=wx.ID_ANY)
00687             
00688             #IMPORTANT NOTE: wx.StaticBox MUST be defined BEFORE any of the 
00689             #   controls that are placed IN the wx.StaticBox, or it will freeze
00690             #   on the Mac
00691             
00692             listBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
00693                        label=" %s " % _("Attribute data - right-click to edit/manage records"))
00694             listSizer = wx.StaticBoxSizer(listBox, wx.VERTICAL)
00695             
00696             win = VirtualAttributeList(panel, self.log,
00697                                        self.mapDBInfo, layer)
00698             if win.IsEmpty():
00699                 del panel
00700                 continue
00701             
00702             win.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnDataItemActivated)
00703 
00704             self.layerPage[layer] = {'browsePage': panel.GetId()}
00705 
00706             self.browsePage.AddPage(page=panel, text=" %d / %s %s" % \
00707                                         (layer, _("Table"), self.mapDBInfo.layers[layer]['table']))
00708 
00709             pageSizer = wx.BoxSizer(wx.VERTICAL)
00710 
00711             # attribute data            
00712             sqlBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
00713                                   label=" %s " % _("SQL Query"))
00714 
00715             sqlSizer = wx.StaticBoxSizer(sqlBox, wx.VERTICAL)
00716 
00717             win.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnDataRightUp) #wxMSW
00718             win.Bind(wx.EVT_RIGHT_UP,            self.OnDataRightUp) #wxGTK
00719             if UserSettings.Get(group='atm', key='leftDbClick', subkey='selection') == 0:
00720                 win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataItemEdit)
00721                 win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataItemEdit)
00722             else:
00723                 win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataDrawSelected)
00724                 win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataDrawSelected)
00725                 
00726             
00727             listSizer.Add(item=win, proportion=1,
00728                           flag=wx.EXPAND | wx.ALL,
00729                           border=3)
00730 
00731             # sql statement box
00732             btnApply = wx.Button(parent=panel, id=wx.ID_APPLY)
00733             btnApply.SetToolTipString(_("Apply SELECT statement and reload data records"))
00734             btnApply.Bind(wx.EVT_BUTTON, self.OnApplySqlStatement)
00735             btnSqlBuilder = wx.Button(parent=panel, id=wx.ID_ANY, label=_("SQL Builder"))
00736             btnSqlBuilder.Bind(wx.EVT_BUTTON, self.OnBuilder)
00737 
00738             sqlSimple = wx.RadioButton(parent=panel, id=wx.ID_ANY,
00739                                        label=_("Simple"))
00740             sqlSimple.SetValue(True)
00741             sqlAdvanced = wx.RadioButton(parent=panel, id=wx.ID_ANY,
00742                                          label=_("Advanced"))
00743             sqlSimple.Bind(wx.EVT_RADIOBUTTON,   self.OnChangeSql)
00744             sqlAdvanced.Bind(wx.EVT_RADIOBUTTON, self.OnChangeSql)
00745 
00746             sqlWhereColumn = wx.Choice(parent=panel, id=wx.ID_ANY,
00747                                        size=(100,-1),
00748                                        choices=self.mapDBInfo.GetColumns(self.mapDBInfo.layers[layer]['table']))
00749             sqlWhereValue = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value="",
00750                                         style=wx.TE_PROCESS_ENTER)
00751             sqlWhereValue.SetToolTipString(_("Example: %s") % "MULTILANE = 'no' AND OBJECTID < 10")
00752 
00753             sqlStatement = wx.TextCtrl(parent=panel, id=wx.ID_ANY,
00754                                        value="SELECT * FROM %s" % \
00755                                            self.mapDBInfo.layers[layer]['table'],
00756                                        style=wx.TE_PROCESS_ENTER)
00757             sqlStatement.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' AND OBJECTID < 10")
00758             sqlWhereValue.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
00759             sqlStatement.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
00760 
00761             sqlLabel = wx.StaticText(parent=panel, id=wx.ID_ANY,
00762                                      label="SELECT * FROM %s WHERE " % \
00763                                          self.mapDBInfo.layers[layer]['table'])
00764             label_query = wx.StaticText(parent=panel, id=wx.ID_ANY,
00765                                         label="")
00766 
00767             sqlFlexSizer = wx.FlexGridSizer (cols=3, hgap=5, vgap=5)
00768             sqlFlexSizer.AddGrowableCol(1)
00769 
00770             sqlFlexSizer.Add(item=sqlSimple,
00771                              flag=wx.ALIGN_CENTER_VERTICAL)
00772             sqlSimpleSizer = wx.BoxSizer(wx.HORIZONTAL)
00773             sqlSimpleSizer.Add(item=sqlLabel,
00774                                flag=wx.ALIGN_CENTER_VERTICAL)
00775             sqlSimpleSizer.Add(item=sqlWhereColumn,
00776                                flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
00777             sqlSimpleSizer.Add(item=sqlWhereValue, proportion=1,
00778                                flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
00779             sqlFlexSizer.Add(item=sqlSimpleSizer,
00780                              flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
00781             sqlFlexSizer.Add(item=btnApply,
00782                              flag=wx.ALIGN_RIGHT)
00783             sqlFlexSizer.Add(item=sqlAdvanced,
00784                              flag=wx.ALIGN_CENTER_VERTICAL)
00785             sqlFlexSizer.Add(item=sqlStatement,
00786                              flag=wx.EXPAND)
00787             sqlFlexSizer.Add(item=btnSqlBuilder,
00788                              flag=wx.ALIGN_RIGHT)
00789 
00790             sqlSizer.Add(item=sqlFlexSizer,
00791                          flag=wx.ALL | wx.EXPAND,
00792                          border=3)
00793 
00794             pageSizer.Add(item=listSizer,
00795                           proportion=1,
00796                           flag=wx.ALL | wx.EXPAND,
00797                           border=5)
00798 
00799             pageSizer.Add(item=sqlSizer,
00800                           proportion=0,
00801                           flag=wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.EXPAND,
00802                           border=5)
00803 
00804             panel.SetSizer(pageSizer)
00805 
00806             self.layerPage[layer]['data']      = win.GetId()
00807             self.layerPage[layer]['simple']    = sqlSimple.GetId()
00808             self.layerPage[layer]['advanced']  = sqlAdvanced.GetId()
00809             self.layerPage[layer]['whereColumn'] = sqlWhereColumn.GetId()
00810             self.layerPage[layer]['where']     = sqlWhereValue.GetId()
00811             self.layerPage[layer]['builder']   = btnSqlBuilder.GetId()
00812             self.layerPage[layer]['statement'] = sqlStatement.GetId()
00813 
00814 
00815         self.browsePage.SetSelection(0) # select first layer
00816         try:
00817             self.layer = self.mapDBInfo.layers.keys()[0]
00818             self.OnChangeSql(None)
00819             self.log.write(_("Number of loaded records: %d") % \
00820                            self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
00821         except (IndexError, KeyError):
00822             self.layer = None
00823         
00824     def __createManageTablePage(self, onlyLayer=-1):
00825         """!Create manage page (create/link and alter tables)"""
00826         for layer in self.mapDBInfo.layers.keys():
00827             if onlyLayer > 0 and layer != onlyLayer:
00828                 continue
00829             
00830             if not layer in self.layerPage:
00831                 continue
00832             
00833             panel = wx.Panel(parent=self.manageTablePage, id=wx.ID_ANY)
00834             self.layerPage[layer]['tablePage'] = panel.GetId()
00835             self.manageTablePage.AddPage(page=panel,
00836                                          text=" %d / %s %s" % (layer,
00837                                                                _("Table"),
00838                                                                self.mapDBInfo.layers[layer]['table']))
00839             
00840             pageSizer = wx.BoxSizer(wx.VERTICAL)
00841             
00842             #
00843             # dbInfo
00844             #
00845             dbBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
00846                                           label=" %s " % _("Database connection"))
00847             dbSizer = wx.StaticBoxSizer(dbBox, wx.VERTICAL)
00848             dbSizer.Add(item=dbm_base.createDbInfoDesc(panel, self.mapDBInfo, layer),
00849                         proportion=1,
00850                         flag=wx.EXPAND | wx.ALL,
00851                         border=3)
00852             
00853             #
00854             # table description
00855             #
00856             table = self.mapDBInfo.layers[layer]['table']
00857             tableBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
00858                                     label=" %s " % _("Table <%s> - right-click to delete column(s)") % table)
00859             
00860             tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
00861             
00862             list = self.__createTableDesc(panel, table)
00863             list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnTableRightUp) #wxMSW
00864             list.Bind(wx.EVT_RIGHT_UP,            self.OnTableRightUp) #wxGTK
00865             self.layerPage[layer]['tableData'] = list.GetId()
00866             
00867             # manage columns (add)
00868             addBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
00869                                   label = " %s " % _("Add column"))
00870             addSizer = wx.StaticBoxSizer(addBox, wx.HORIZONTAL)
00871             
00872             column = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '',
00873                                  size=(150, -1), style = wx.TE_PROCESS_ENTER)
00874             column.Bind(wx.EVT_TEXT,       self.OnTableAddColumnName)
00875             column.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemAdd)
00876             self.layerPage[layer]['addColName'] = column.GetId()
00877             addSizer.Add(item= wx.StaticText(parent = panel, id = wx.ID_ANY, label=_("Column")),
00878                          flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00879                          border = 5)
00880             addSizer.Add(item = column, proportion = 1,
00881                          flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00882                          border = 5)
00883             
00884             ctype = wx.Choice (parent=panel, id=wx.ID_ANY,
00885                                choices = ["integer",
00886                                           "double",
00887                                           "varchar",
00888                                           "date"]) # FIXME
00889             ctype.SetSelection(0)
00890             ctype.Bind(wx.EVT_CHOICE, self.OnTableChangeType)
00891             self.layerPage[layer]['addColType'] = ctype.GetId()
00892             addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label=_("Type")), 
00893                          flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00894                          border = 5)
00895             addSizer.Add(item = ctype,
00896                          flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00897                          border = 5)
00898             
00899             length = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
00900                                  initial = 250,
00901                                  min = 1, max = 1e6)
00902             length.Enable(False)
00903             self.layerPage[layer]['addColLength'] = length.GetId()
00904             addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Length")),
00905                          flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00906                          border = 5)
00907             addSizer.Add(item = length,
00908                          flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00909                          border = 5)
00910             
00911             btnAddCol = wx.Button(parent = panel, id = wx.ID_ADD)
00912             btnAddCol.Bind(wx.EVT_BUTTON, self.OnTableItemAdd)
00913             btnAddCol.Enable(False)
00914             self.layerPage[layer]['addColButton'] = btnAddCol.GetId()
00915             addSizer.Add(item = btnAddCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND,
00916                          border = 3)
00917             
00918             # manage columns (rename)
00919             renameBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
00920                                      label = " %s " % _("Rename column"))
00921             renameSizer = wx.StaticBoxSizer(renameBox, wx.HORIZONTAL)
00922             
00923             column = wx.ComboBox(parent = panel, id = wx.ID_ANY, size = (150, -1),
00924                                  style = wx.CB_SIMPLE | wx.CB_READONLY,
00925                                  choices = self.mapDBInfo.GetColumns(table))
00926             column.SetSelection(0)
00927             self.layerPage[layer]['renameCol'] = column.GetId()
00928             renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Column")),
00929                             flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00930                             border = 5)
00931             renameSizer.Add(item = column, proportion = 1,
00932                             flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00933                             border = 5)
00934             
00935             columnTo = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '',
00936                                    size = (150, -1), style = wx.TE_PROCESS_ENTER)
00937             columnTo.Bind(wx.EVT_TEXT,       self.OnTableRenameColumnName)
00938             columnTo.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemChange)
00939             self.layerPage[layer]['renameColTo'] = columnTo.GetId()
00940             renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("To")),
00941                             flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00942                             border = 5)
00943             renameSizer.Add(item = columnTo, proportion = 1,
00944                             flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
00945                             border = 5)
00946             
00947             btnRenameCol = wx.Button(parent = panel, id = wx.ID_ANY, label = _("&Rename"))
00948             btnRenameCol.Bind(wx.EVT_BUTTON, self.OnTableItemChange)
00949             btnRenameCol.Enable(False)
00950             self.layerPage[layer]['renameColButton'] = btnRenameCol.GetId()
00951             renameSizer.Add(item = btnRenameCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND,
00952                             border = 3)
00953             
00954             tableSizer.Add(item = list,
00955                            flag = wx.ALL | wx.EXPAND,
00956                            proportion = 1,
00957                            border = 3)
00958             
00959             pageSizer.Add(item=dbSizer,
00960                           flag = wx.ALL | wx.EXPAND,
00961                           proportion = 0,
00962                           border = 3)
00963             
00964             pageSizer.Add(item = tableSizer,
00965                           flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
00966                           proportion = 1,
00967                           border = 3)
00968             
00969             pageSizer.Add(item = addSizer,
00970                           flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
00971                           proportion = 0,
00972                           border = 3)
00973             pageSizer.Add(item = renameSizer,
00974                           flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
00975                           proportion = 0,
00976                           border = 3)
00977             
00978             panel.SetSizer(pageSizer)
00979         
00980         self.manageTablePage.SetSelection(0) # select first layer
00981         try:
00982             self.layer = self.mapDBInfo.layers.keys()[0]
00983         except IndexError:
00984             self.layer = None
00985         
00986     def __createTableDesc(self, parent, table):
00987         """!Create list with table description"""
00988         list = TableListCtrl(parent=parent, id=wx.ID_ANY,
00989                              table=self.mapDBInfo.tables[table],
00990                              columns=self.mapDBInfo.GetColumns(table))
00991         list.Populate()
00992         # sorter
00993         # itemDataMap = list.Populate()
00994         # listmix.ColumnSorterMixin.__init__(self, 2)
00995 
00996         return list
00997 
00998     def __createManageLayerPage(self):
00999         """!Create manage page"""
01000         splitterWin = wx.SplitterWindow(parent=self.manageLayerPage, id=wx.ID_ANY)
01001         splitterWin.SetMinimumPaneSize(100)
01002         
01003         self.manageLayerPage.AddPage(page=splitterWin,
01004                                      text=_("Layers of vector map")) # dummy page
01005         
01006         #
01007         # list of layers
01008         #
01009         panelList = wx.Panel(parent=splitterWin, id=wx.ID_ANY)
01010 
01011         panelListSizer  = wx.BoxSizer(wx.VERTICAL)
01012         layerBox = wx.StaticBox(parent=panelList, id=wx.ID_ANY,
01013                                 label=" %s " % _("List of layers"))
01014         layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
01015 
01016         self.layerList = self.__createLayerDesc(panelList)
01017         self.layerList.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnLayerRightUp) #wxMSW
01018         self.layerList.Bind(wx.EVT_RIGHT_UP,            self.OnLayerRightUp) #wxGTK
01019         
01020         layerSizer.Add(item=self.layerList,
01021                        flag=wx.ALL | wx.EXPAND,
01022                        proportion=1,
01023                        border=3)
01024 
01025         panelListSizer.Add(item=layerSizer,
01026                            flag=wx.ALL | wx.EXPAND,
01027                            proportion=1,
01028                            border=3)
01029 
01030         panelList.SetSizer(panelListSizer)
01031 
01032         #
01033         # manage part
01034         #
01035         panelManage = wx.Panel(parent=splitterWin, id=wx.ID_ANY)
01036          
01037         manageSizer = wx.BoxSizer(wx.VERTICAL)
01038 
01039         self.manageLayerBook = LayerBook(parent=panelManage, id=wx.ID_ANY,
01040                                          parentDialog=self)
01041 
01042         manageSizer.Add(item=self.manageLayerBook,
01043                         proportion=1,
01044                         flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
01045                         border=5)
01046 
01047         panelManage.SetSizer(manageSizer)
01048         splitterWin.SplitHorizontally(panelList, panelManage, 100) 
01049         splitterWin.Fit()
01050 
01051     def __createLayerDesc(self, parent):
01052         """!Create list of linked layers"""
01053         list = LayerListCtrl(parent=parent, id=wx.ID_ANY,
01054                              layers=self.mapDBInfo.layers)
01055         
01056         list.Populate()
01057         # sorter
01058         # itemDataMap = list.Populate()
01059         # listmix.ColumnSorterMixin.__init__(self, 2)
01060 
01061         return list
01062 
01063     def __layout(self):
01064         """!Do layout"""
01065         # frame body
01066         mainSizer = wx.BoxSizer(wx.VERTICAL)
01067 
01068         # buttons
01069         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
01070         btnSizer.Add(item=self.btnReload, proportion=1,
01071                      flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
01072         btnSizer.Add(item=self.btnQuit, proportion=1,
01073                      flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
01074 
01075         mainSizer.Add(item=self.notebook, proportion=1, flag=wx.EXPAND)
01076         mainSizer.Add(item=btnSizer, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
01077 
01078         self.panel.SetAutoLayout(True)
01079         self.panel.SetSizer(mainSizer)
01080         mainSizer.Fit(self.panel)
01081         self.Layout()
01082         
01083     def OnDataRightUp(self, event):
01084         """!Table description area, context menu"""
01085         if not hasattr(self, "popupDataID1"):
01086             self.popupDataID1 = wx.NewId()
01087             self.popupDataID2 = wx.NewId()
01088             self.popupDataID3 = wx.NewId()
01089             self.popupDataID4 = wx.NewId()
01090             self.popupDataID5 = wx.NewId()
01091             self.popupDataID6 = wx.NewId()
01092             self.popupDataID7 = wx.NewId()
01093             self.popupDataID8 = wx.NewId()
01094             self.popupDataID9 = wx.NewId()
01095             self.popupDataID10 = wx.NewId()
01096             self.popupDataID11 = wx.NewId()
01097 
01098             self.Bind(wx.EVT_MENU, self.OnDataItemEdit,       id=self.popupDataID1)
01099             self.Bind(wx.EVT_MENU, self.OnDataItemAdd,        id=self.popupDataID2)
01100             self.Bind(wx.EVT_MENU, self.OnDataItemDelete,     id=self.popupDataID3)
01101             self.Bind(wx.EVT_MENU, self.OnDataItemDeleteAll,  id=self.popupDataID4)
01102             self.Bind(wx.EVT_MENU, self.OnDataSelectAll,      id=self.popupDataID5)
01103             self.Bind(wx.EVT_MENU, self.OnDataSelectNone,     id=self.popupDataID6)
01104             self.Bind(wx.EVT_MENU, self.OnDataDrawSelected,   id=self.popupDataID7)
01105             self.Bind(wx.EVT_MENU, self.OnDataDrawSelectedZoom, id=self.popupDataID8)
01106             self.Bind(wx.EVT_MENU, self.OnExtractSelected,    id=self.popupDataID9)
01107             self.Bind(wx.EVT_MENU, self.OnDeleteSelected,     id=self.popupDataID11)
01108             self.Bind(wx.EVT_MENU, self.OnDataReload,         id=self.popupDataID10)
01109 
01110         list = self.FindWindowById(self.layerPage[self.layer]['data'])
01111         # generate popup-menu
01112         menu = wx.Menu()
01113         menu.Append(self.popupDataID1, _("Edit selected record"))
01114         selected = list.GetFirstSelected()
01115         if not self.editable or selected == -1 or list.GetNextSelected(selected) != -1:
01116             menu.Enable(self.popupDataID1, False)
01117         menu.Append(self.popupDataID2, _("Insert new record"))
01118         menu.Append(self.popupDataID3, _("Delete selected record(s)"))
01119         menu.Append(self.popupDataID4, _("Delete all records"))
01120         if not self.editable:
01121             menu.Enable(self.popupDataID2, False)
01122             menu.Enable(self.popupDataID3, False)
01123             menu.Enable(self.popupDataID4, False)
01124         menu.AppendSeparator()
01125         menu.Append(self.popupDataID5, _("Select all"))
01126         menu.Append(self.popupDataID6, _("Deselect all"))
01127         menu.AppendSeparator()
01128         menu.Append(self.popupDataID7, _("Highlight selected features"))
01129         menu.Append(self.popupDataID8, _("Highlight selected features and zoom"))
01130         if not self.map or len(list.GetSelectedItems()) == 0:
01131             menu.Enable(self.popupDataID7, False)
01132             menu.Enable(self.popupDataID8, False)
01133         menu.Append(self.popupDataID9, _("Extract selected features"))
01134         menu.Append(self.popupDataID11, _("Delete selected features"))
01135         if not self.editable:
01136             menu.Enable(self.popupDataID11, False)
01137         if list.GetFirstSelected() == -1:
01138             menu.Enable(self.popupDataID3, False)
01139             menu.Enable(self.popupDataID9, False)
01140             menu.Enable(self.popupDataID11, False)
01141         menu.AppendSeparator()
01142         menu.Append(self.popupDataID10, _("Reload"))
01143 
01144         self.PopupMenu(menu)
01145         menu.Destroy()
01146 
01147         # update statusbar
01148         self.log.write(_("Number of loaded records: %d") % \
01149                            list.GetItemCount())
01150 
01151     def OnDataItemDelete(self, event):
01152         """!Delete selected item(s) from the list (layer/category pair)"""
01153         list = self.FindWindowById(self.layerPage[self.layer]['data'])
01154         item = list.GetFirstSelected()
01155 
01156         table    = self.mapDBInfo.layers[self.layer]["table"]
01157         key      = self.mapDBInfo.layers[self.layer]["key"]
01158 
01159         indeces = []
01160         # collect SQL statements
01161         while item != -1:
01162             index = list.itemIndexMap[item]
01163             indeces.append(index)
01164 
01165             cat = list.itemCatsMap[index]
01166             
01167             self.listOfSQLStatements.append('DELETE FROM %s WHERE %s=%d' % \
01168                                                 (table, key, cat))
01169 
01170             item = list.GetNextSelected(item)
01171 
01172         if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'):
01173             deleteDialog = wx.MessageBox(parent=self,
01174                                          message=_("Selected data records (%d) will be permanently deleted "
01175                                                    "from table. Do you want to delete them?") % \
01176                                              (len(self.listOfSQLStatements)),
01177                                          caption=_("Delete records"),
01178                                          style=wx.YES_NO | wx.CENTRE)
01179             if deleteDialog != wx.YES:
01180                 self.listOfSQLStatements = []
01181                 return False
01182 
01183         # restore maps
01184         i = 0
01185         indexTemp = copy.copy(list.itemIndexMap)
01186         list.itemIndexMap = []
01187         dataTemp = copy.deepcopy(list.itemDataMap)
01188         list.itemDataMap = {}
01189         catsTemp = copy.deepcopy(list.itemCatsMap)
01190         list.itemCatsMap = {}
01191 
01192         i = 0
01193         for index in indexTemp:
01194             if index in indeces:
01195                 continue
01196             list.itemIndexMap.append(i)
01197             list.itemDataMap[i] = dataTemp[index]
01198             list.itemCatsMap[i] = catsTemp[index]
01199 
01200             i += 1
01201             
01202         list.SetItemCount(len(list.itemIndexMap))
01203 
01204         # deselect items
01205         item = list.GetFirstSelected()
01206         while item != -1:
01207             list.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
01208             item = list.GetNextSelected(item)
01209         
01210         # submit SQL statements
01211         self.ApplyCommands()
01212         
01213         return True
01214 
01215     def OnDataItemDeleteAll(self, event):
01216         """!Delete all items from the list"""
01217         list = self.FindWindowById(self.layerPage[self.layer]['data'])
01218         if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'):
01219             deleteDialog = wx.MessageBox(parent=self,
01220                                          message=_("All data records (%d) will be permanently deleted "
01221                                                    "from table. Do you want to delete them?") % \
01222                                              (len(list.itemIndexMap)),
01223                                          caption=_("Delete records"),
01224                                          style=wx.YES_NO | wx.CENTRE)
01225             if deleteDialog != wx.YES:
01226                 return
01227 
01228         list.DeleteAllItems()
01229         list.itemDataMap  = {}
01230         list.itemIndexMap = []
01231         list.SetItemCount(0)
01232 
01233         table = self.mapDBInfo.layers[self.layer]["table"]
01234         self.listOfSQLStatements.append('DELETE FROM %s' % table)
01235 
01236         self.ApplyCommands()
01237         
01238         event.Skip()
01239 
01240     def _drawSelected(self, zoom):
01241         """!Highlight selected features"""
01242         if not self.map or not self.mapdisplay:
01243             return
01244         
01245         list = self.FindWindowById(self.layerPage[self.layer]['data'])
01246         cats = map(int, list.GetSelectedItems())
01247 
01248         digitToolbar = self.mapdisplay.toolbars['vdigit']
01249         if digitToolbar and digitToolbar.GetLayer() and \
01250                 digitToolbar.GetLayer().GetName() == self.vectorName:
01251 
01252             self.mapdisplay.digit.driver.SetSelected(cats, field=self.layer)
01253             if zoom:
01254                 n, s, w, e = self.mapdisplay.digit.driver.GetRegionSelected()
01255                 self.mapdisplay.Map.GetRegion(n=n, s=s, w=w, e=e,
01256                                               update=True)
01257         else:
01258             # add map layer with higlighted vector features
01259             self.AddQueryMapLayer() # -> self.qlayer
01260 
01261             # set opacity based on queried layer
01262             if self.parent and self.parent.GetName() == "LayerManager" and \
01263                     self.treeItem:
01264                 maptree = self.parent.curr_page.maptree
01265                 opacity = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetOpacity(float=True)
01266                 self.qlayer.SetOpacity(opacity)
01267             if zoom:
01268                 keyColumn = self.mapDBInfo.layers[self.layer]['key']
01269                 where = ''
01270                 for range in utils.ListOfCatsToRange(cats).split(','):
01271                     if '-' in range:
01272                         min, max = range.split('-')
01273                         where += '%s >= %d and %s <= %d or ' % \
01274                             (keyColumn, int(min),
01275                              keyColumn, int(max))
01276                     else:
01277                         where += '%s = %d or ' % (keyColumn, int(range))
01278                 where = where.rstrip('or ')
01279                 
01280                 select = gcmd.RunCommand('v.db.select',
01281                                          parent = self,
01282                                          read = True,
01283                                          quiet = True,
01284                                          flags = 'r',
01285                                          map = self.mapDBInfo.map,
01286                                          layer = int(self.layer),
01287                                          where = where)
01288                 
01289                 region = {}
01290                 for line in select.splitlines():
01291                     key, value = line.split('=')
01292                     region[key.strip()] = float(value.strip())
01293                 
01294                 self.mapdisplay.Map.GetRegion(n=region['n'], s=region['s'],
01295                                               w=region['w'], e=region['e'],
01296                                               update=True)
01297         
01298         if zoom:
01299             self.mapdisplay.Map.AdjustRegion() # adjust resolution
01300             self.mapdisplay.Map.AlignExtentFromDisplay() # adjust extent
01301             self.mapdisplay.MapWindow.UpdateMap(render=True, renderVector=True)
01302         else:
01303             self.mapdisplay.MapWindow.UpdateMap(render=False, renderVector=True)
01304         
01305     def OnDataDrawSelected(self, event):
01306         """!Reload table description"""
01307         self._drawSelected(zoom=False)
01308         event.Skip()
01309 
01310     def OnDataDrawSelectedZoom(self, event):
01311         self._drawSelected(zoom=True)
01312         event.Skip()
01313         
01314     def OnDataItemAdd(self, event):
01315         """!Add new record to the attribute table"""
01316         list      = self.FindWindowById(self.layerPage[self.layer]['data'])
01317         table     = self.mapDBInfo.layers[self.layer]['table']
01318         keyColumn = self.mapDBInfo.layers[self.layer]['key']
01319         
01320         # (column name, value)
01321         data = []
01322 
01323         # collect names of all visible columns
01324         columnName = []
01325         for i in range(list.GetColumnCount()): 
01326             columnName.append(list.GetColumn(i).GetText())
01327 
01328         # maximal category number
01329         if len(list.itemCatsMap.values()) > 0:
01330             maxCat = max(list.itemCatsMap.values())
01331         else:
01332             maxCat = 0 # starting category '1'
01333         
01334         # key column must be always presented
01335         if keyColumn not in columnName:
01336             columnName.insert(0, keyColumn) # insert key column on first position
01337             data.append((keyColumn, str(maxCat + 1)))
01338             missingKey = True
01339         else:
01340             missingKey = False
01341             
01342         # add other visible columns
01343         colIdx = 0
01344         keyId = -1
01345         for col in columnName:
01346             if col == keyColumn: # key 
01347                 if missingKey is False: 
01348                     data.append((col, str(maxCat + 1)))
01349                     keyId = colIdx
01350             else:
01351                 data.append((col, ''))
01352             colIdx += 1
01353                 
01354         dlg = ModifyTableRecord(parent = self,
01355                                 title = _("Insert new record"),
01356                                 data = data, keyEditable = (keyId, True))
01357 
01358         if dlg.ShowModal() == wx.ID_OK:
01359             try: # get category number
01360                 cat = int(dlg.GetValues(columns=[keyColumn])[0])
01361             except:
01362                 cat = -1
01363 
01364             try:
01365                 if cat in list.itemCatsMap.values():
01366                     raise ValueError(_("Record with category number %d "
01367                                        "already exists in the table.") % cat)
01368 
01369                 values = dlg.GetValues() # values (need to be casted)
01370                 columnsString = ''
01371                 valuesString   = ''
01372                 
01373                 for i in range(len(values)):
01374                     if len(values[i]) == 0: # NULL
01375                         if columnName[i] == keyColumn:
01376                             raise ValueError(_("Category number (column %s)"
01377                                                " is missing.") % keyColumn)
01378                         else:
01379                             continue
01380 
01381                     try:
01382                         if list.columns[columnName[i]]['ctype'] == int:
01383                             # values[i] is stored as text. 
01384                             value = float(values[i])
01385                         else:
01386                             value = values[i]
01387                         values[i] = list.columns[columnName[i]]['ctype'] (value)
01388 
01389                     except:
01390                         raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % 
01391                                          {'value' : str(values[i]),
01392                                           'type' : list.columns[columnName[i]]['type']})
01393                     columnsString += '%s,' % columnName[i]
01394                     if list.columns[columnName[i]]['ctype'] == str:
01395                         valuesString += "'%s'," % values[i]
01396                     else:
01397                         valuesString += "%s," % values[i]
01398 
01399             except ValueError, err:
01400                 wx.MessageBox(parent=self,
01401                               message="%s%s%s" % (_("Unable to insert new record."),
01402                                                     os.linesep, err),
01403                               caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
01404                 return
01405 
01406             # remove category if need 
01407             if missingKey is True:
01408                 del values[0]
01409                 
01410             # add new item to the list
01411             if len(list.itemIndexMap) > 0:
01412                 index = max(list.itemIndexMap) + 1
01413             else:
01414                 index = 0
01415             
01416             list.itemIndexMap.append(index)
01417             list.itemDataMap[index] = values
01418             list.itemCatsMap[index] = cat
01419             list.SetItemCount(list.GetItemCount() + 1)
01420 
01421             self.listOfSQLStatements.append('INSERT INTO %s (%s) VALUES(%s)' % \
01422                                                 (table,
01423                                                  columnsString.strip(','),
01424                                                  valuesString.strip(',')))
01425             self.ApplyCommands()
01426             
01427     def OnDataItemEdit(self, event):
01428         """!Edit selected record of the attribute table"""
01429         list      = self.FindWindowById(self.layerPage[self.layer]['data'])
01430         item      = list.GetFirstSelected()
01431         if item == -1:
01432             return
01433 
01434         table     = self.mapDBInfo.layers[self.layer]['table']
01435         keyColumn = self.mapDBInfo.layers[self.layer]['key']
01436         cat       = list.itemCatsMap[list.itemIndexMap[item]]
01437 
01438         # (column name, value)
01439         data = []
01440 
01441         # collect names of all visible columns
01442         columnName = []
01443         for i in range(list.GetColumnCount()): 
01444             columnName.append(list.GetColumn(i).GetText())
01445 
01446 
01447         # key column must be always presented
01448         if keyColumn not in columnName:
01449             columnName.insert(0, keyColumn) # insert key column on first position
01450             data.append((keyColumn, str(cat)))
01451             keyId = 0
01452             missingKey = True
01453         else:
01454             missingKey = False
01455             
01456         # add other visible columns
01457         for i in range(len(columnName)):
01458             if columnName[i] == keyColumn: # key 
01459                 if missingKey is False: 
01460                     data.append((columnName[i], str(cat)))
01461                     keyId = i
01462             else:
01463                 if missingKey is True:
01464                     value = list.GetItem(item, i-1).GetText()
01465                 else:
01466                     value = list.GetItem(item, i).GetText()
01467                 data.append((columnName[i], value))
01468 
01469         dlg = ModifyTableRecord(parent = self, 
01470                                 title = _("Update existing record"),
01471                                 data = data, keyEditable = (keyId, False))
01472 
01473         if dlg.ShowModal() == wx.ID_OK:
01474             values = dlg.GetValues() # string
01475             updateString = ''
01476             try:
01477                 for i in range(len(values)): 
01478                     if i == keyId: # skip key column
01479                         continue
01480                     if list.GetItem(item, i).GetText() != values[i]:
01481                         if len(values[i]) > 0:
01482                             try:
01483                                 if missingKey is True:
01484                                     idx = i - 1
01485                                 else:
01486                                     idx = i
01487                                 if list.columns[columnName[i]]['ctype'] != type(''):
01488                                     if list.columns[columnName[i]]['ctype'] == int:
01489                                         value = float(values[i])
01490                                     else:
01491                                         value = values[i]
01492                                     list.itemDataMap[item][idx] = \
01493                                         list.columns[columnName[i]]['ctype'] (value)
01494                                 else:
01495                                     list.itemDataMap[item][idx] = values[i]
01496                             except:
01497                                 raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % \
01498                                                      {'value' : str(values[i]),
01499                                                       'type' : list.columns[columnName[i]]['type']})
01500 
01501                             if list.columns[columnName[i]]['ctype'] == str:
01502                                 updateString += "%s='%s'," % (columnName[i], values[i])
01503                             else:
01504                                 updateString += "%s=%s," % (columnName[i], values[i])
01505                         else: # NULL
01506                             updateString += "%s=NULL," % (columnName[i])
01507                             
01508             except ValueError, err:
01509                 wx.MessageBox(parent=self,
01510                               message="%s%s%s" % (_("Unable to update existing record."),
01511                                                   os.linesep, err),
01512                               caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
01513                 return
01514             
01515             if len(updateString) > 0:
01516                 self.listOfSQLStatements.append('UPDATE %s SET %s WHERE %s=%d' % \
01517                                                     (table, updateString.strip(','),
01518                                                      keyColumn, cat))
01519                 self.ApplyCommands()
01520 
01521             list.Update(self.mapDBInfo)
01522                         
01523     def OnDataReload(self, event):
01524         """!Reload list of records"""
01525         self.OnApplySqlStatement(None)
01526         self.listOfSQLStatements = []
01527 
01528     def OnDataSelectAll(self, event):
01529         """!Select all items"""
01530         list = self.FindWindowById(self.layerPage[self.layer]['data'])
01531         item = -1
01532 
01533         while True:
01534             item = list.GetNextItem(item)
01535             if item == -1:
01536                 break
01537             list.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
01538 
01539         event.Skip()
01540 
01541     def OnDataSelectNone(self, event):
01542         """!Deselect items"""
01543         list = self.FindWindowById(self.layerPage[self.layer]['data'])
01544         item = -1
01545 
01546         while True:
01547             item = list.GetNextItem(item, wx.LIST_STATE_SELECTED)
01548             if item == -1:
01549                 break
01550             list.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
01551 
01552         event.Skip()
01553 
01554 
01555     def OnTableChangeType(self, event):
01556         """!Data type for new column changed. Enable or disable
01557         data length widget"""
01558         win = self.FindWindowById(self.layerPage[self.layer]['addColLength'])
01559         if event.GetString() == "varchar":
01560             win.Enable(True)
01561         else:
01562             win.Enable(False)
01563 
01564     def OnTableRenameColumnName(self, event):
01565         """!Editing column name to be added to the table"""
01566         btn  = self.FindWindowById(self.layerPage[self.layer]['renameColButton'])
01567         col  = self.FindWindowById(self.layerPage[self.layer]['renameCol'])
01568         colTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo'])
01569         if len(col.GetValue()) > 0 and len(colTo.GetValue()) > 0:
01570             btn.Enable(True)
01571         else:
01572             btn.Enable(False)
01573 
01574         event.Skip()
01575 
01576     def OnTableAddColumnName(self, event):
01577         """!Editing column name to be added to the table"""
01578         btn = self.FindWindowById(self.layerPage[self.layer]['addColButton'])
01579         if len(event.GetString()) > 0:
01580             btn.Enable(True)
01581         else:
01582             btn.Enable(False)
01583 
01584         event.Skip()
01585 
01586     def OnTableItemChange(self, event):
01587         """!Rename column in the table"""
01588         list   = self.FindWindowById(self.layerPage[self.layer]['tableData'])
01589         name   = self.FindWindowById(self.layerPage[self.layer]['renameCol']).GetValue()
01590         nameTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo']).GetValue()
01591 
01592         table = self.mapDBInfo.layers[self.layer]["table"]
01593 
01594         if not name or not nameTo:
01595             wx.MessageBox(self=self,
01596                           message=_("Unable to rename column. "
01597                                     "No column name defined."),
01598                           caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
01599             return
01600         else:
01601             item = list.FindItem(start=-1, str=name)
01602             if item > -1:
01603                 if list.FindItem(start=-1, str=nameTo) > -1:
01604                     wx.MessageBox(parent=self,
01605                                   message=_("Unable to rename column <%(column)s> to "
01606                                             "<%(columnTo)s>. Column already exists "
01607                                             "in the table <%(table)s>.") % \
01608                                       {'column' : name, 'columnTo' : nameTo,
01609                                        'table' : table},
01610                                   caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
01611                     return
01612                 else:
01613                     list.SetItemText(item, nameTo)
01614 
01615                     self.listOfCommands.append(('v.db.renamecol',
01616                                                 { 'map'    : self.vectorName,
01617                                                   'layer'  : self.layer,
01618                                                   'column' : '%s,%s' % (name, nameTo) }
01619                                                 ))
01620             else:
01621                 wx.MessageBox(parent=self,
01622                               message=_("Unable to rename column. "
01623                                         "Column <%(column)s> doesn't exist in the table <%(table)s>.") % 
01624                               {'column' : name, 'table' : table},
01625                               caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
01626                 return
01627             
01628         # apply changes
01629         self.ApplyCommands()
01630 
01631         # update widgets
01632         self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
01633         self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
01634         self.FindWindowById(self.layerPage[self.layer]['renameColTo']).SetValue('')
01635 
01636         event.Skip()
01637 
01638     def OnTableRightUp(self, event):
01639         """!Table description area, context menu"""
01640         if not hasattr(self, "popupTableID"):
01641             self.popupTableID1 = wx.NewId()
01642             self.popupTableID2 = wx.NewId()
01643             self.popupTableID3 = wx.NewId()
01644             self.Bind(wx.EVT_MENU, self.OnTableItemDelete,    id=self.popupTableID1)
01645             self.Bind(wx.EVT_MENU, self.OnTableItemDeleteAll, id=self.popupTableID2)
01646             self.Bind(wx.EVT_MENU, self.OnTableReload,        id=self.popupTableID3)
01647 
01648         # generate popup-menu
01649         menu = wx.Menu()
01650         menu.Append(self.popupTableID1, _("Drop selected column"))
01651         if self.FindWindowById(self.layerPage[self.layer]['tableData']).GetFirstSelected() == -1:
01652             menu.Enable(self.popupTableID1, False)
01653         menu.Append(self.popupTableID2, _("Drop all columns"))
01654         menu.AppendSeparator()
01655         menu.Append(self.popupTableID3, _("Reload"))
01656 
01657         self.PopupMenu(menu)
01658         menu.Destroy()
01659 
01660     def OnTableItemDelete(self, event):
01661         """!Delete selected item(s) from the list"""
01662         list = self.FindWindowById(self.layerPage[self.layer]['tableData'])
01663         
01664         item = list.GetFirstSelected()
01665         
01666         if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'):
01667             deleteDialog = wx.MessageBox(parent=self,
01668                                          message=_("Selected column '%s' will PERMANENTLY removed "
01669                                                    "from table. Do you want to drop the column?") % \
01670                                              (list.GetItemText(item)),
01671                                          caption=_("Drop column(s)"),
01672                                          style=wx.YES_NO | wx.CENTRE)
01673             if deleteDialog != wx.YES:
01674                 return False
01675         
01676         while item != -1:
01677             self.listOfCommands.append(('v.db.dropcol',
01678                                         { 'map' : self.vectorName,
01679                                           'layer' : self.layer,
01680                                           'column' : list.GetItemText(item) }
01681                                         ))
01682             list.DeleteItem(item)
01683             item = list.GetFirstSelected()
01684         
01685         # apply changes
01686         self.ApplyCommands()
01687         
01688         # update widgets
01689         table = self.mapDBInfo.layers[self.layer]['table']
01690         self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
01691         self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
01692         
01693         event.Skip()
01694 
01695     def OnTableItemDeleteAll(self, event):
01696         """!Delete all items from the list"""
01697         table     = self.mapDBInfo.layers[self.layer]['table']
01698         cols      = self.mapDBInfo.GetColumns(table)
01699         keyColumn = self.mapDBInfo.layers[self.layer]['key']
01700         if keyColumn in cols:
01701             cols.remove(keyColumn)
01702         
01703         if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'):
01704             deleteDialog = wx.MessageBox(parent=self,
01705                                          message=_("Selected columns\n%s\nwill PERMANENTLY removed "
01706                                                    "from table. Do you want to drop the columns?") % \
01707                                              ('\n'.join(cols)),
01708                                          caption=_("Drop column(s)"),
01709                                          style=wx.YES_NO | wx.CENTRE)
01710             if deleteDialog != wx.YES:
01711                 return False
01712         
01713         for col in cols:
01714             self.listOfCommands.append(('v.db.dropcol',
01715                                         { 'map' : self.vectorName,
01716                                           'layer' : self.layer,
01717                                           'column' : col }
01718                                         ))
01719         self.FindWindowById(self.layerPage[self.layer]['tableData']).DeleteAllItems()
01720 
01721         # apply changes
01722         self.ApplyCommands()
01723 
01724         # update widgets
01725         table = self.mapDBInfo.layers[self.layer]['table']
01726         self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
01727         self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
01728 
01729         event.Skip()
01730 
01731     def OnTableReload(self, event=None):
01732         """!Reload table description"""
01733         self.FindWindowById(self.layerPage[self.layer]['tableData']).Populate(update=True)
01734         self.listOfCommands = []
01735 
01736     def OnTableItemAdd(self, event):
01737         """!Add new column to the table"""
01738         table = self.mapDBInfo.layers[self.layer]['table']
01739         name = self.FindWindowById(self.layerPage[self.layer]['addColName']).GetValue()
01740         
01741         if not name:
01742             gcmd.GError(parent = self,
01743                         message = _("Unable to add column to the table. "
01744                                     "No column name defined."))
01745             return
01746         
01747         ctype = self.FindWindowById(self.layerPage[self.layer]['addColType']). \
01748             GetStringSelection()
01749         
01750         # cast type if needed
01751         if ctype == 'double':
01752             ctype = 'double precision'
01753         if ctype == 'varchar':
01754             length = int(self.FindWindowById(self.layerPage[self.layer]['addColLength']). \
01755                              GetValue())
01756         else:
01757             length = '' # FIXME
01758         
01759         # add item to the list of table columns
01760         tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
01761         # check for duplicate items
01762         if tlist.FindItem(start=-1, str=name) > -1:
01763             gcmd.GError(parent = self,
01764                         message = _("Column <%(column)s> already exists in table <%(table)s>.") % \
01765                             {'column' : name, 'table' : self.mapDBInfo.layers[self.layer]["table"]}
01766                         )
01767             return
01768         index = tlist.InsertStringItem(sys.maxint, str(name))
01769         tlist.SetStringItem(index, 0, str(name))
01770         tlist.SetStringItem(index, 1, str(ctype))
01771         tlist.SetStringItem(index, 2, str(length))
01772         
01773         # add v.db.addcol command to the list
01774         if ctype == 'varchar':
01775             ctype += ' (%d)' % length
01776         self.listOfCommands.append(('v.db.addcol',
01777                                     { 'map'     : self.vectorName,
01778                                       'layer'   : self.layer,
01779                                       'columns' : '%s %s' % (name, ctype) }
01780                                     ))
01781         # apply changes
01782         self.ApplyCommands()
01783         
01784         # update widgets
01785         self.FindWindowById(self.layerPage[self.layer]['addColName']).SetValue('')
01786         self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
01787         self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
01788         
01789         event.Skip()
01790         
01791     def OnLayerPageChanged(self, event):
01792         """!Layer tab changed"""
01793         pageNum = event.GetSelection()
01794         self.layer = self.mapDBInfo.layers.keys()[pageNum]
01795         
01796         try:
01797             idCol = self.layerPage[self.layer]['whereColumn']
01798         except KeyError:
01799             idCol = None
01800         
01801         try:
01802             self.OnChangeSql(None)
01803             # update statusbar
01804             self.log.write(_("Number of loaded records: %d") % \
01805                                self.FindWindowById(self.layerPage[self.layer]['data']).\
01806                                GetItemCount())
01807         except:
01808             pass
01809         
01810         if idCol:
01811             winCol = self.FindWindowById(idCol)
01812             table = self.mapDBInfo.layers[self.layer]["table"]
01813             self.mapDBInfo.GetColumns(table)
01814         
01815         event.Skip()
01816         
01817     def OnPageChanged(self, event):
01818         try:
01819             id = self.layerPage[self.layer]['data']
01820         except KeyError:
01821             id = None
01822         
01823         if event.GetSelection() == 0 and id:
01824             win = self.FindWindowById(id)
01825             if win:
01826                 self.log.write(_("Number of loaded records: %d") % win.GetItemCount())
01827             else:
01828                 self.log.write("")
01829             self.btnReload.Enable()
01830         else:
01831             self.log.write("")
01832             self.btnReload.Enable(False)
01833         
01834         event.Skip()
01835         
01836     def OnLayerRightUp(self, event):
01837         """!Layer description area, context menu"""
01838         pass
01839 
01840     def OnChangeSql(self, event):
01841         """!Switch simple/advanced sql statement"""
01842         if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue():
01843             self.FindWindowById(self.layerPage[self.layer]['where']).Enable(True)
01844             self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(False)
01845             self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(False)
01846         else:
01847             self.FindWindowById(self.layerPage[self.layer]['where']).Enable(False)
01848             self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(True)
01849             self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(True)
01850 
01851     def ApplyCommands(self):
01852         """!Apply changes"""
01853         # perform GRASS commands (e.g. v.db.addcol)
01854         wx.BeginBusyCursor()
01855         
01856         if len(self.listOfCommands) > 0:
01857             for cmd in self.listOfCommands:
01858                 gcmd.RunCommand(prog = cmd[0],
01859                                 quiet = True,
01860                                 parent = self,
01861                                 **cmd[1])
01862             
01863             self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)
01864             table = self.mapDBInfo.layers[self.layer]['table']
01865 
01866             # update table description
01867             list = self.FindWindowById(self.layerPage[self.layer]['tableData'])
01868             list.Update(table=self.mapDBInfo.tables[table],
01869                         columns=self.mapDBInfo.GetColumns(table))
01870             self.OnTableReload(None)
01871 
01872             # update data list
01873             list = self.FindWindowById(self.layerPage[self.layer]['data'])
01874             list.Update(self.mapDBInfo)
01875 
01876             # reset list of commands
01877             self.listOfCommands = []
01878         
01879         # perform SQL non-select statements (e.g. 'delete from table where cat=1')
01880         if len(self.listOfSQLStatements) > 0:
01881             sqlFile = tempfile.NamedTemporaryFile(mode="wt")
01882             for sql in self.listOfSQLStatements:
01883                 enc = UserSettings.Get(group='atm', key='encoding', subkey='value')
01884                 if not enc and 'GRASS_DB_ENCODING' in os.environ:
01885                     enc = os.environ['GRASS_DB_ENCODING']
01886                 if enc:
01887                     sqlFile.file.write(sql.encode(enc) + ';')
01888                 else:
01889                     sqlFile.file.write(sql + ';')
01890                 sqlFile.file.write(os.linesep)
01891                 sqlFile.file.flush()
01892 
01893             driver   = self.mapDBInfo.layers[self.layer]["driver"]
01894             database = self.mapDBInfo.layers[self.layer]["database"]
01895             
01896             Debug.msg(3, 'AttributeManger.ApplyCommands(): %s' %
01897                       ';'.join(["%s" % s for s in self.listOfSQLStatements]))
01898             
01899             gcmd.RunCommand('db.execute',
01900                             parent = self,
01901                             input = sqlFile.name,
01902                             driver = driver,
01903                             database = database)
01904             
01905             # reset list of statements
01906             self.listOfSQLStatements = []
01907             
01908         wx.EndBusyCursor()
01909         
01910     def OnApplySqlStatement(self, event):
01911         """!Apply simple/advanced sql statement"""
01912         keyColumn = -1 # index of key column
01913         listWin = self.FindWindowById(self.layerPage[self.layer]['data'])
01914         sql = None
01915         
01916         wx.BeginBusyCursor()
01917         
01918         if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue():
01919             # simple sql statement
01920             whereCol = self.FindWindowById(self.layerPage[self.layer]['whereColumn']).GetStringSelection()
01921             whereVal = self.FindWindowById(self.layerPage[self.layer]['where']).GetValue().strip()
01922             try:
01923                 if len(whereVal) > 0:
01924                     keyColumn = listWin.LoadData(self.layer, where=whereCol + whereVal)
01925                 else:
01926                     keyColumn = listWin.LoadData(self.layer)
01927             except gcmd.GException, e:
01928                 gcmd.GError(parent = self,
01929                             message = _("Loading attribute data failed.\n\n%s") % e.value)
01930                 self.FindWindowById(self.layerPage[self.layer]['where']).SetValue('')
01931         else:
01932             # advanced sql statement
01933             win = self.FindWindowById(self.layerPage[self.layer]['statement'])
01934             try:
01935                 cols, where = self.ValidateSelectStatement(win.GetValue())
01936                 if cols is None and where is None:
01937                     sql = win.GetValue()
01938             except TypeError:
01939                 wx.MessageBox(parent=self,
01940                               message=_("Loading attribute data failed.\n"
01941                                         "Invalid SQL select statement.\n\n%s") % win.GetValue(),
01942                               caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
01943                 win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
01944                 cols = None
01945                 where = None
01946             
01947             if cols or where or sql:
01948                 try:
01949                     keyColumn = listWin.LoadData(self.layer, columns=cols,
01950                                                  where=where, sql=sql)
01951                 except gcmd.GException, e:
01952                     gcmd.GError(parent = self,
01953                                 message = _("Loading attribute data failed.\n\n%s") % e.value)
01954                     win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
01955         
01956         # sort by key column
01957         if sql and 'order by' in sql.lower():
01958             pass # don't order by key column
01959         else:
01960             if keyColumn > -1:
01961                 listWin.SortListItems(col=keyColumn, ascending=True)
01962             else:
01963                 listWin.SortListItems(col=0, ascending=True) 
01964         
01965         wx.EndBusyCursor()
01966         
01967         # update statusbar
01968         self.log.write(_("Number of loaded records: %d") % \
01969                            self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
01970 
01971     def ValidateSelectStatement(self, statement):
01972         """!Validate SQL select statement
01973 
01974         @return (columns, where)
01975         @return None on error
01976         """
01977         if statement[0:7].lower() != 'select ':
01978             return None
01979         
01980         cols = ''
01981         index = 7
01982         for c in statement[index:]:
01983             if c == ' ':
01984                 break
01985             cols += c
01986             index += 1
01987         if cols == '*':
01988             cols = None
01989         else:
01990             cols = cols.split(',')
01991         
01992         tablelen = len(self.mapDBInfo.layers[self.layer]['table'])
01993         
01994         if statement[index+1:index+6].lower() != 'from ' or \
01995                 statement[index+6:index+6+tablelen] != '%s' % \
01996                 (self.mapDBInfo.layers[self.layer]['table']):
01997             return None
01998         
01999         if len(statement[index+7+tablelen:]) > 0:
02000             index = statement.lower().find('where ')
02001             if index > -1:
02002                 where = statement[index+6:]
02003             else:
02004                 where = None
02005         else:
02006             where = None
02007         
02008         return (cols, where)
02009     
02010     def OnCloseWindow(self, event):
02011         """!Cancel button pressed"""
02012         self.Close()
02013         if self.parent and self.parent.GetName() == 'LayerManager':
02014             # deregister ATM
02015             self.parent.dialogs['atm'].remove(self)
02016         
02017         event.Skip()
02018 
02019     def OnBuilder(self,event):
02020         """!SQL Builder button pressed -> show the SQLBuilder dialog"""
02021         if not self.builder:
02022             self.builder = sqlbuilder.SQLFrame(parent = self, id = wx.ID_ANY,
02023                                                title = _("SQL Builder"),
02024                                                vectmap = self.vectorName,
02025                                                evtheader = self.OnBuilderEvt)
02026             self.builder.Show()
02027         else:
02028             self.builder.Raise()
02029         
02030     def OnBuilderEvt(self, event):
02031         if event == 'apply':
02032             sqlstr = self.builder.GetSQLStatement()
02033             self.FindWindowById(self.layerPage[self.layer]['statement']).SetValue(sqlstr)
02034             if self.builder.CloseOnApply():
02035                 self.builder = None
02036         elif event == 'close':
02037             self.builder = None
02038         
02039     def OnTextEnter(self, event):
02040         pass
02041     
02042     def OnDataItemActivated(self, event):
02043         """!Item activated, highlight selected item"""
02044         self.OnDataDrawSelected(event)
02045 
02046         event.Skip()
02047 
02048     def OnExtractSelected(self, event):
02049         """!Extract vector objects selected in attribute browse window
02050         to new vector map
02051         """
02052         list = self.FindWindowById(self.layerPage[self.layer]['data'])
02053         # cats = list.selectedCats[:]
02054         cats = list.GetSelectedItems()
02055         if len(cats) == 0:
02056             wx.MessageBox(parent=self,
02057                           message=_('Nothing to extract.'),
02058                           caption=_('Message'), style=wx.CENTRE)
02059             return
02060         else:
02061             # dialog to get file name
02062             dlg = gdialogs.CreateNewVector(parent = self, title = _('Extract selected features'),
02063                                            log = self.cmdLog,
02064                                            cmd = (('v.extract',
02065                                                    { 'input' : self.vectorName,
02066                                                      'list' : utils.ListOfCatsToRange(cats) },
02067                                                    'output')),
02068                                            disableTable = True)
02069             if not dlg:
02070                 return
02071             
02072             name = dlg.GetName(full = True)
02073             if name and dlg.IsChecked('add'):
02074                 # add layer to map layer tree
02075                 self.parent.curr_page.maptree.AddLayer(ltype = 'vector',
02076                                                        lname = name,
02077                                                        lcmd = ['d.vect', 'map=%s' % name])
02078             dlg.Destroy()
02079             
02080     def OnDeleteSelected(self, event):
02081         """
02082         Delete vector objects selected in attribute browse window
02083         (attribures and geometry)
02084         """
02085         list = self.FindWindowById(self.layerPage[self.layer]['data'])
02086         cats = list.GetSelectedItems()
02087         if len(cats) == 0:
02088             wx.MessageBox(parent=self,
02089                           message=_('Nothing to delete.'),
02090                           caption=_('Message'), style=wx.CENTRE)
02091         
02092         if self.OnDataItemDelete(None):
02093             digitToolbar = self.mapdisplay.toolbars['vdigit']
02094             if digitToolbar and digitToolbar.GetLayer() and \
02095                     digitToolbar.GetLayer().GetName() == self.vectorName:
02096                 self.mapdisplay.digit.driver.SetSelected(map(int, cats), field=self.layer)
02097                 self.mapdisplay.digit.DeleteSelectedLines()
02098             else:
02099                 gcmd.RunCommand('v.edit',
02100                                 parent = self,
02101                                 quiet = True,
02102                                 map = self.vectorName,
02103                                 tool = 'delete',
02104                                 cats = utils.ListOfCatsToRange(cats))
02105                 
02106             self.mapdisplay.MapWindow.UpdateMap(render=True, renderVector=True)
02107         
02108     def AddQueryMapLayer(self):
02109         """!Redraw a map
02110 
02111         Return True if map has been redrawn, False if no map is given
02112         """
02113         list = self.FindWindowById(self.layerPage[self.layer]['data'])
02114         cats = { 
02115             self.layer : list.GetSelectedItems()
02116             }
02117         
02118         if self.mapdisplay.Map.GetLayerIndex(self.qlayer) < 0:
02119             self.qlayer = None
02120             
02121         if self.qlayer:
02122             self.qlayer.SetCmd(self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats, addLayer=False))
02123         else:
02124             self.qlayer = self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats)
02125 
02126         return self.qlayer
02127     
02128     def UpdateDialog(self, layer):
02129         """!Updates dialog layout for given layer"""
02130         #
02131         # delete page
02132         #
02133         if layer in self.mapDBInfo.layers.keys():
02134             # delete page
02135             # draging pages disallowed
02136             # if self.browsePage.GetPageText(page).replace('Layer ', '').strip() == str(layer):
02137             # self.browsePage.DeletePage(page)
02138             # break
02139             self.browsePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
02140             self.manageTablePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
02141             # set current page selection
02142             self.notebook.SetSelection(2)
02143             
02144         # fetch fresh db info
02145         self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)    
02146 
02147         #
02148         # add new page
02149         #
02150         if layer in self.mapDBInfo.layers.keys():
02151             # 'browse data' page
02152             self.__createBrowsePage(layer)
02153             # 'manage tables' page
02154             self.__createManageTablePage(layer)
02155             # set current page selection
02156             self.notebook.SetSelection(2)
02157             
02158         #
02159         # 'manage layers' page
02160         #
02161         # update list of layers
02162         self.layerList.Update(self.mapDBInfo.layers)
02163         self.layerList.Populate(update=True)
02164         # update selected widgets
02165         listOfLayers = map(str, self.mapDBInfo.layers.keys())
02166         ### delete layer page
02167         self.manageLayerBook.deleteLayer.SetItems(listOfLayers)
02168         if len(listOfLayers) > 0:
02169             self.manageLayerBook.deleteLayer.SetStringSelection(listOfLayers[0])
02170             tableName = self.mapDBInfo.layers[int(listOfLayers[0])]['table']
02171             maxLayer = max(self.mapDBInfo.layers.keys())
02172         else:
02173             tableName = ''
02174             maxLayer = 0
02175         self.manageLayerBook.deleteTable.SetLabel( \
02176             _('Drop also linked attribute table (%s)') % \
02177                 tableName)
02178         ### add layer page
02179         self.manageLayerBook.addLayerWidgets['layer'][1].SetValue(\
02180             maxLayer+1)
02181         ### modify layer
02182         self.manageLayerBook.modifyLayerWidgets['layer'][1].SetItems(listOfLayers)
02183         self.manageLayerBook.OnChangeLayer(event=None)
02184 
02185     def GetVectorName(self):
02186         """!Get vector name"""
02187         return self.vectorName
02188     
02189     def LoadData(self, layer, columns=None, where=None, sql=None):
02190         """!Load data into list
02191 
02192         @param layer layer number
02193         @param columns list of columns for output
02194         @param where where statement
02195         @param sql full sql statement
02196 
02197         @return id of key column 
02198         @return -1 if key column is not displayed
02199         """
02200         listWin = self.FindWindowById(self.layerPage[layer]['data'])
02201         return listWin.LoadData(layer, columns, where, sql)
02202     
02203 class TableListCtrl(wx.ListCtrl,
02204                     listmix.ListCtrlAutoWidthMixin):
02205                     #                    listmix.TextEditMixin):
02206     """!Table description list"""
02207 
02208     def __init__(self, parent, id, table, columns, pos=wx.DefaultPosition,
02209                  size=wx.DefaultSize):
02210 
02211         self.parent  = parent
02212         self.table   = table
02213         self.columns = columns
02214         wx.ListCtrl.__init__(self, parent, id, pos, size,
02215                              style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
02216                              wx.BORDER_NONE)
02217 
02218         listmix.ListCtrlAutoWidthMixin.__init__(self)
02219         # listmix.TextEditMixin.__init__(self)
02220 
02221     def Update(self, table, columns):
02222         """!Update column description"""
02223         self.table   = table
02224         self.columns = columns
02225 
02226     def Populate(self, update=False):
02227         """!Populate the list"""
02228         itemData = {} # requested by sorter
02229 
02230         if not update:
02231             headings = [_("Column name"), _("Data type"), _("Data length")]
02232             i = 0
02233             for h in headings:
02234                 self.InsertColumn(col=i, heading=h)
02235                 self.SetColumnWidth(col=i, width=150)
02236                 i += 1
02237         else:
02238             self.DeleteAllItems()
02239 
02240         i = 0
02241         for column in self.columns:
02242             index = self.InsertStringItem(sys.maxint, str(column))
02243             self.SetStringItem(index, 0, str(column))
02244             self.SetStringItem(index, 1, str(self.table[column]['type']))
02245             self.SetStringItem(index, 2, str(self.table[column]['length']))
02246             self.SetItemData(index, i)
02247             itemData[i] = (str(column),
02248                            str(self.table[column]['type']),
02249                            int(self.table[column]['length']))
02250             i = i + 1
02251 
02252         self.SendSizeEvent()
02253         
02254         return itemData
02255 
02256 class LayerListCtrl(wx.ListCtrl,
02257                     listmix.ListCtrlAutoWidthMixin):
02258                     # listmix.ColumnSorterMixin):
02259                     # listmix.TextEditMixin):
02260     """!Layer description list"""
02261 
02262     def __init__(self, parent, id, layers,
02263                  pos=wx.DefaultPosition,
02264                  size=wx.DefaultSize):
02265 
02266         self.parent = parent
02267         self.layers = layers
02268         wx.ListCtrl.__init__(self, parent, id, pos, size,
02269                              style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
02270                              wx.BORDER_NONE)
02271 
02272         listmix.ListCtrlAutoWidthMixin.__init__(self)
02273         # listmix.TextEditMixin.__init__(self)
02274 
02275     def Update(self, layers):
02276         """!Update description"""
02277         self.layers = layers
02278 
02279     def Populate(self, update=False):
02280         """!Populate the list"""
02281         itemData = {} # requested by sorter
02282 
02283         if not update:
02284             headings = [_("Layer"),  _("Driver"), _("Database"), _("Table"), _("Key")]
02285             i = 0
02286             for h in headings:
02287                 self.InsertColumn(col=i, heading=h)
02288                 i += 1
02289         else:
02290             self.DeleteAllItems()
02291 
02292         i = 0
02293         for layer in self.layers.keys():
02294             index = self.InsertStringItem(sys.maxint, str(layer))
02295             self.SetStringItem(index, 0, str(layer))
02296             database = str(self.layers[layer]['database'])
02297             driver   = str(self.layers[layer]['driver'])
02298             table    = str(self.layers[layer]['table'])
02299             key      = str(self.layers[layer]['key'])
02300             self.SetStringItem(index, 1, driver)
02301             self.SetStringItem(index, 2, database)
02302             self.SetStringItem(index, 3, table)
02303             self.SetStringItem(index, 4, key)
02304             self.SetItemData(index, i)
02305             itemData[i] = (str(layer),
02306                            driver,
02307                            database,
02308                            table,
02309                            key)
02310             i += 1
02311 
02312         for i in range(self.GetColumnCount()):
02313             self.SetColumnWidth(col=i, width=wx.LIST_AUTOSIZE)
02314             if self.GetColumnWidth(col=i) < 60:
02315                 self.SetColumnWidth(col=i, width=60)
02316 
02317         self.SendSizeEvent()
02318         
02319         return itemData
02320 
02321 class LayerBook(wx.Notebook):
02322     """!Manage layers (add, delete, modify)"""
02323     def __init__(self, parent, id,
02324                  parentDialog,
02325                  style=wx.BK_DEFAULT):
02326         wx.Notebook.__init__(self, parent, id, style=style)
02327 
02328         self.parent       = parent
02329         self.parentDialog = parentDialog
02330         self.mapDBInfo    = self.parentDialog.mapDBInfo
02331 
02332         #
02333         # drivers
02334         #
02335         drivers = gcmd.RunCommand('db.drivers',
02336                                   quiet = True,
02337                                   read = True,
02338                                   flags = 'p')
02339         
02340         self.listOfDrivers = []
02341         for drv in drivers.splitlines():
02342             self.listOfDrivers.append(drv.strip())
02343         
02344         #
02345         # get default values
02346         #
02347         self.defaultConnect = {}
02348         connect = gcmd.RunCommand('db.connect',
02349                                   flags = 'p',
02350                                   read = True,
02351                                   quiet = True)
02352         
02353         for line in connect.splitlines():
02354             item, value = line.split(':', 1)
02355             self.defaultConnect[item.strip()] = value.strip()
02356         
02357         if len(self.defaultConnect['driver']) == 0 or \
02358                len(self.defaultConnect['database']) == 0:
02359             wx.MessageBox(parent=self.parent,
02360                           message=_("Unknown default DB connection. "
02361                                     "Please define DB connection using db.connect module."),
02362                           caption=_("Warning"),
02363                           style=wx.OK | wx.ICON_WARNING | wx.CENTRE)
02364         
02365         self.defaultTables = self.__getTables(self.defaultConnect['driver'],
02366                                               self.defaultConnect['database'])
02367         try:
02368             self.defaultColumns = self.__getColumns(self.defaultConnect['driver'],
02369                                                     self.defaultConnect['database'],
02370                                                     self.defaultTables[0])
02371         except IndexError:
02372             self.defaultColumns = []
02373 
02374         self.__createAddPage()
02375         self.__createDeletePage()
02376         self.__createModifyPage()
02377 
02378     def __createAddPage(self):
02379         """!Add new layer"""
02380         self.addPanel = wx.Panel(parent=self, id=wx.ID_ANY)
02381         self.AddPage(page=self.addPanel, text=_("Add layer"))
02382         
02383         try:
02384             maxLayer = max(self.mapDBInfo.layers.keys())
02385         except ValueError:
02386             maxLayer = 0
02387 
02388         # layer description
02389         
02390         layerBox = wx.StaticBox (parent=self.addPanel, id=wx.ID_ANY,
02391                                  label=" %s " % (_("Layer description")))
02392         layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
02393         
02394         #
02395         # list of layer widgets (label, value)
02396         #
02397         self.addLayerWidgets = {'layer':
02398                                     (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
02399                                                    label='%s:' % _("Layer")),
02400                                      wx.SpinCtrl(parent=self.addPanel, id=wx.ID_ANY, size=(65, -1),
02401                                                  initial=maxLayer+1,
02402                                                  min=1, max=1e6)),
02403                                 'driver':
02404                                     (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
02405                                                    label='%s:' % _("Driver")),
02406                                      wx.Choice(parent=self.addPanel, id=wx.ID_ANY, size=(200, -1),
02407                                                choices=self.listOfDrivers)),
02408                                 'database':
02409                                     (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
02410                                                    label='%s:' % _("Database")),
02411                                      wx.TextCtrl(parent=self.addPanel, id=wx.ID_ANY,
02412                                                  value='',
02413                                                  style=wx.TE_PROCESS_ENTER)),
02414                                 'table':
02415                                     (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
02416                                                    label='%s:' % _("Table")),
02417                                      wx.Choice(parent=self.addPanel, id=wx.ID_ANY, size=(200, -1),
02418                                                choices=self.defaultTables)),
02419                                 'key':
02420                                     (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
02421                                                    label='%s:' % _("Key column")),
02422                                      wx.Choice(parent=self.addPanel, id=wx.ID_ANY, size=(200, -1),
02423                                                choices=self.defaultColumns)),
02424                                 'addCat':
02425                                     (wx.CheckBox(parent=self.addPanel, id=wx.ID_ANY,
02426                                                  label=_("Insert record for each category into table")),
02427                                      None),
02428                                 }
02429         
02430         # set default values for widgets
02431         self.addLayerWidgets['driver'][1].SetStringSelection(self.defaultConnect['driver'])
02432         self.addLayerWidgets['database'][1].SetValue(self.defaultConnect['database'])
02433         self.addLayerWidgets['table'][1].SetSelection(0)
02434         self.addLayerWidgets['key'][1].SetSelection(0)
02435         # events
02436         self.addLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
02437         self.addLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
02438         self.addLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
02439         
02440         # tooltips
02441         self.addLayerWidgets['addCat'][0].SetToolTipString(_("You need to add categories "
02442                                                              "by v.category module."))
02443         #
02444         # list of table widgets
02445         #
02446         keyCol = UserSettings.Get(group='atm', key='keycolumn', subkey='value')
02447         self.tableWidgets = {'table': (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
02448                                                      label='%s:' % _("Table name")),
02449                                        wx.TextCtrl(parent=self.addPanel, id=wx.ID_ANY,
02450                                                    value='',
02451                                                    style=wx.TE_PROCESS_ENTER)),
02452                              'key': (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
02453                                                    label='%s:' % _("Key column")),
02454                                      wx.TextCtrl(parent=self.addPanel, id=wx.ID_ANY,
02455                                                  value=keyCol,
02456                                                  style=wx.TE_PROCESS_ENTER))}
02457         # events
02458         self.tableWidgets['table'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
02459         self.tableWidgets['key'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
02460         
02461         btnTable   = wx.Button(self.addPanel, wx.ID_ANY, _("&Create table"),
02462                              size=(125,-1))
02463         btnTable.Bind(wx.EVT_BUTTON, self.OnCreateTable)
02464         
02465         btnLayer   = wx.Button(self.addPanel, wx.ID_ANY, _("&Add layer"),
02466                              size=(125,-1))
02467         btnLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer)
02468         
02469         btnDefault = wx.Button(self.addPanel, wx.ID_ANY, _("&Set default"),
02470                                size=(125,-1))
02471         btnDefault.Bind(wx.EVT_BUTTON, self.OnSetDefault)
02472         
02473         # do layout
02474         
02475         pageSizer = wx.BoxSizer(wx.HORIZONTAL)
02476                 
02477         # data area
02478         dataSizer = wx.GridBagSizer(hgap=5, vgap=5)
02479         dataSizer.AddGrowableCol(1)
02480         row = 0
02481         for key in ('layer', 'driver', 'database', 'table', 'key', 'addCat'):
02482             label, value = self.addLayerWidgets[key]
02483             if not value:
02484                 span = (1, 2)
02485             else:
02486                 span = (1, 1)
02487             dataSizer.Add(item=label,
02488                           flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0),
02489                           span=span)
02490             
02491             if not value:
02492                 row += 1
02493                 continue
02494 
02495             if label.GetLabel() == "Layer:":
02496                 style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT
02497             else:
02498                 style = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND
02499             
02500             dataSizer.Add(item=value,
02501                           flag=style, pos=(row, 1))
02502             
02503             row += 1
02504         
02505         layerSizer.Add(item=dataSizer,
02506                        proportion=1,
02507                        flag=wx.ALL | wx.EXPAND,
02508                        border=5)
02509         
02510         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
02511         btnSizer.Add(item=btnDefault,
02512                      proportion=0,
02513                      flag=wx.ALL | wx.ALIGN_LEFT,
02514                      border=5)
02515         
02516         btnSizer.Add(item=(5, 5),
02517                      proportion=1,
02518                      flag=wx.ALL | wx.EXPAND,
02519                      border=5)
02520         
02521         btnSizer.Add(item=btnLayer,
02522                      proportion=0,
02523                      flag=wx.ALL | wx.ALIGN_RIGHT,
02524                      border=5)
02525         
02526         layerSizer.Add(item=btnSizer,
02527                        proportion=0,
02528                        flag=wx.ALL | wx.EXPAND,
02529                        border=0)
02530                 
02531         # table description
02532         tableBox = wx.StaticBox (parent=self.addPanel, id=wx.ID_ANY,
02533                                  label=" %s " % (_("Table description")))
02534         tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
02535         
02536         # data area
02537         dataSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
02538         dataSizer.AddGrowableCol(1)
02539         for key in ['table', 'key']:
02540             label, value = self.tableWidgets[key]
02541             dataSizer.Add(item=label,
02542                           flag=wx.ALIGN_CENTER_VERTICAL)
02543             dataSizer.Add(item=value,
02544                           flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
02545 
02546         tableSizer.Add(item=dataSizer,
02547                        proportion=1,
02548                        flag=wx.ALL | wx.EXPAND,
02549                        border=5)
02550 
02551         tableSizer.Add(item=btnTable,
02552                        proportion=0,
02553                        flag=wx.ALL | wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT,
02554                        border=5)
02555 
02556         pageSizer.Add(item=layerSizer,
02557                       proportion=3,
02558                       flag=wx.ALL | wx.EXPAND,
02559                       border=3)
02560         
02561         pageSizer.Add(item=tableSizer,
02562                       proportion=2,
02563                       flag=wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND,
02564                       border=3)
02565         
02566         layerSizer.SetVirtualSizeHints(self.addPanel)
02567         self.addPanel.SetAutoLayout(True)
02568         self.addPanel.SetSizer(pageSizer)
02569         pageSizer.Fit(self.addPanel)
02570         
02571     def __createDeletePage(self):
02572         """!Delete layer"""
02573         self.deletePanel = wx.Panel(parent=self, id=wx.ID_ANY)
02574         self.AddPage(page=self.deletePanel, text=_("Remove layer"))
02575 
02576         label = wx.StaticText(parent=self.deletePanel, id=wx.ID_ANY,
02577                               label='%s:' % _("Layer to remove"))
02578 
02579         self.deleteLayer = wx.ComboBox(parent=self.deletePanel, id=wx.ID_ANY, size=(100, -1),
02580                                        style=wx.CB_SIMPLE | wx.CB_READONLY,
02581                                        choices=map(str, self.mapDBInfo.layers.keys()))
02582         self.deleteLayer.SetSelection(0)           
02583         self.deleteLayer.Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
02584 
02585         try:
02586             tableName = self.mapDBInfo.layers[int(self.deleteLayer.GetStringSelection())]['table']
02587         except ValueError:
02588             tableName = ''
02589             
02590         self.deleteTable = wx.CheckBox(parent=self.deletePanel, id=wx.ID_ANY,
02591                                        label=_('Drop also linked attribute table (%s)') % \
02592                                        tableName)
02593 
02594         if tableName == '':
02595             self.deleteLayer.Enable(False)
02596             self.deleteTable.Enable(False)
02597             
02598         btnDelete   = wx.Button(self.deletePanel, wx.ID_DELETE, _("&Remove layer"),
02599                                 size=(125,-1))
02600         btnDelete.Bind(wx.EVT_BUTTON, self.OnDeleteLayer)
02601 
02602         #
02603         # do layout
02604         #
02605         pageSizer = wx.BoxSizer(wx.VERTICAL)
02606 
02607         dataSizer = wx.BoxSizer(wx.VERTICAL)
02608 
02609         flexSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
02610         flexSizer.AddGrowableCol(2)
02611 
02612         flexSizer.Add(item=label,
02613                       flag=wx.ALIGN_CENTER_VERTICAL)
02614         flexSizer.Add(item=self.deleteLayer,
02615                       flag=wx.ALIGN_CENTER_VERTICAL)
02616 
02617         dataSizer.Add(item=flexSizer,
02618                       proportion=0,
02619                       flag=wx.ALL | wx.EXPAND,
02620                       border=1)
02621 
02622         dataSizer.Add(item=self.deleteTable,
02623                       proportion=0,
02624                       flag=wx.ALL | wx.EXPAND,
02625                       border=1)
02626 
02627         pageSizer.Add(item=dataSizer,
02628                       proportion=1,
02629                       flag=wx.ALL | wx.EXPAND,
02630                       border=5)
02631 
02632         pageSizer.Add(item=btnDelete,
02633                       proportion=0,
02634                       flag=wx.ALL | wx.ALIGN_RIGHT,
02635                       border=5)
02636 
02637         self.deletePanel.SetSizer(pageSizer)
02638 
02639     def __createModifyPage(self):
02640         """!Modify layer"""
02641         self.modifyPanel = wx.Panel(parent=self, id=wx.ID_ANY)
02642         self.AddPage(page=self.modifyPanel, text=_("Modify layer"))
02643 
02644         #
02645         # list of layer widgets (label, value)
02646         #
02647         self.modifyLayerWidgets = {'layer':
02648                                        (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
02649                                                       label='%s:' % _("Layer")),
02650                                         wx.ComboBox(parent=self.modifyPanel, id=wx.ID_ANY,
02651                                                     size=(100, -1),
02652                                                     style=wx.CB_SIMPLE | wx.CB_READONLY,
02653                                                     choices=map(str, 
02654                                                                 self.mapDBInfo.layers.keys()))),
02655                                    'driver':
02656                                        (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
02657                                                       label='%s:' % _("Driver")),
02658                                         wx.Choice(parent=self.modifyPanel, id=wx.ID_ANY,
02659                                                   size=(200, -1),
02660                                                   choices=self.listOfDrivers)),
02661                                    'database':
02662                                        (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
02663                                                       label='%s:' % _("Database")),
02664                                         wx.TextCtrl(parent=self.modifyPanel, id=wx.ID_ANY,
02665                                                     value='', size=(350, -1),
02666                                                     style=wx.TE_PROCESS_ENTER)),
02667                                    'table':
02668                                        (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
02669                                                       label='%s:' % _("Table")),
02670                                         wx.Choice(parent=self.modifyPanel, id=wx.ID_ANY,
02671                                                   size=(200, -1),
02672                                                   choices=self.defaultTables)),
02673                                    'key':
02674                                        (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
02675                                                       label='%s:' % _("Key column")),
02676                                         wx.Choice(parent=self.modifyPanel, id=wx.ID_ANY,
02677                                                   size=(200, -1),
02678                                                   choices=self.defaultColumns))}
02679         
02680         # set default values for widgets
02681         self.modifyLayerWidgets['layer'][1].SetSelection(0)
02682         try:
02683             layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
02684         except ValueError:
02685             layer = None
02686             for label in self.modifyLayerWidgets.keys():
02687                 self.modifyLayerWidgets[label][1].Enable(False)
02688 
02689         if layer:
02690             driver   = self.mapDBInfo.layers[layer]['driver']
02691             database = self.mapDBInfo.layers[layer]['database']
02692             table    = self.mapDBInfo.layers[layer]['table']
02693 
02694             listOfColumns = self.__getColumns(driver, database, table)
02695             self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
02696             self.modifyLayerWidgets['database'][1].SetValue(database)
02697             if table in self.modifyLayerWidgets['table'][1].GetItems():
02698                 self.modifyLayerWidgets['table'][1].SetStringSelection(table)
02699             else:
02700                 if self.defaultConnect['schema'] != '':
02701                     table = self.defaultConnect['schema'] + table # try with default schema
02702                 else:
02703                     table = 'public.' + table # try with 'public' schema
02704                 self.modifyLayerWidgets['table'][1].SetStringSelection(table)
02705             self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
02706             self.modifyLayerWidgets['key'][1].SetSelection(0)
02707 
02708         # events
02709         self.modifyLayerWidgets['layer'][1].Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
02710         # self.modifyLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
02711         # self.modifyLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
02712         # self.modifyLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
02713 
02714         btnModify = wx.Button(self.modifyPanel, wx.ID_DELETE, _("&Modify layer"),
02715                               size=(125,-1))
02716         btnModify.Bind(wx.EVT_BUTTON, self.OnModifyLayer)
02717 
02718         #
02719         # do layout
02720         #
02721         pageSizer = wx.BoxSizer(wx.VERTICAL)
02722 
02723         # data area
02724         dataSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
02725         dataSizer.AddGrowableCol(1)
02726         for key in ('layer', 'driver', 'database', 'table', 'key'):
02727             label, value = self.modifyLayerWidgets[key]
02728             dataSizer.Add(item=label,
02729                           flag=wx.ALIGN_CENTER_VERTICAL)
02730             if label.GetLabel() == "Layer:":
02731                 dataSizer.Add(item=value,
02732                               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
02733             else:
02734                 dataSizer.Add(item=value,
02735                               flag=wx.ALIGN_CENTER_VERTICAL)
02736 
02737         pageSizer.Add(item=dataSizer,
02738                       proportion=1,
02739                       flag=wx.ALL | wx.EXPAND,
02740                       border=5)
02741 
02742         pageSizer.Add(item=btnModify,
02743                       proportion=0,
02744                       flag=wx.ALL | wx.ALIGN_RIGHT,
02745                       border=5)
02746 
02747         self.modifyPanel.SetSizer(pageSizer)
02748 
02749     def __getTables(self, driver, database):
02750         """!Get list of tables for given driver and database"""
02751         tables = []
02752 
02753         ret = gcmd.RunCommand('db.tables',
02754                               parent = self,
02755                               read = True,
02756                               flags = 'p',
02757                               driver = driver,
02758                               database = database)
02759         
02760         if ret is None:
02761             wx.MessageBox(parent=self,
02762                           message=_("Unable to get list of tables.\n"
02763                                     "Please use db.connect to set database parameters."),
02764                           caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
02765             
02766             return tables
02767         
02768         for table in ret.splitlines():
02769             tables.append(table)
02770         
02771         return tables
02772 
02773     def __getColumns(self, driver, database, table):
02774         """!Get list of column of given table"""
02775         columns = []
02776 
02777         ret = gcmd.RunCommand('db.columns',
02778                               parent = self,
02779                               quiet = True,
02780                               read = True,
02781                               driver = driver,
02782                               database = database,
02783                               table = table)
02784         
02785         if ret == None:
02786             return columns
02787         
02788         for column in ret.splitlines():
02789             columns.append(column)
02790         
02791         return columns
02792 
02793     def OnDriverChanged(self, event):
02794         """!Driver selection changed, update list of tables"""
02795         driver = event.GetString()
02796         database = self.addLayerWidgets['database'][1].GetValue()
02797 
02798         winTable = self.addLayerWidgets['table'][1]
02799         winKey   = self.addLayerWidgets['key'][1]
02800         tables   = self.__getTables(driver, database)
02801 
02802         winTable.SetItems(tables)
02803         winTable.SetSelection(0)
02804 
02805         if len(tables) == 0:
02806             winKey.SetItems([])
02807 
02808         event.Skip()
02809 
02810     def OnDatabaseChanged(self, event):
02811         """!Database selection changed, update list of tables"""
02812         event.Skip()
02813 
02814     def OnTableChanged(self, event):
02815         """!Table name changed, update list of columns"""
02816         driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
02817         database = self.addLayerWidgets['database'][1].GetValue()
02818         table    = event.GetString()
02819 
02820         win  = self.addLayerWidgets['key'][1]
02821         cols = self.__getColumns(driver, database, table)
02822         win.SetItems(cols)
02823         win.SetSelection(0)
02824 
02825         event.Skip()
02826 
02827     def OnSetDefault(self, event):
02828         """!Set default values"""
02829         driver   = self.addLayerWidgets['driver'][1]
02830         database = self.addLayerWidgets['database'][1]
02831         table    = self.addLayerWidgets['table'][1]
02832         key      = self.addLayerWidgets['key'][1]
02833 
02834         driver.SetStringSelection(self.defaultConnect['driver'])
02835         database.SetValue(self.defaultConnect['database'])
02836         tables = self.__getTables(self.defaultConnect['driver'],
02837                                   self.defaultConnect['database'])
02838         table.SetItems(tables)
02839         table.SetSelection(0)
02840         if len(tables) == 0:
02841             key.SetItems([])
02842         else:
02843             cols = self.__getColumns(self.defaultConnect['driver'],
02844                                      self.defaultConnect['database'],
02845                                      tables[0])
02846             key.SetItems(cols)
02847             key.SetSelection(0)
02848 
02849         event.Skip()
02850 
02851     def OnCreateTable(self, event):
02852         """!Create new table (name and key column given)"""
02853         driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
02854         database = self.addLayerWidgets['database'][1].GetValue()
02855         table    = self.tableWidgets['table'][1].GetValue()
02856         key      = self.tableWidgets['key'][1].GetValue()
02857         
02858         if not table or not key:
02859             wx.MessageBox(parent=self,
02860                           message=_("Unable to create new table. "
02861                                     "Table name or key column name is missing."),
02862                           caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
02863             return
02864 
02865         if table in self.addLayerWidgets['table'][1].GetItems():
02866             wx.MessageBox(parent=self,
02867                           message=_("Unable to create new table. "
02868                                     "Table <%s> already exists in the database.") % table,
02869                           caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
02870             return
02871         
02872         # create table
02873         sql = 'CREATE TABLE %s (%s INTEGER)' % (table, key)
02874 
02875         gcmd.RunCommand('db.execute',
02876                         quiet = True,
02877                         parent = self,
02878                         stdin = sql,
02879                         driver = driver,
02880                         database = database)
02881         
02882         # update list of tables
02883         tableList = self.addLayerWidgets['table'][1]
02884         tableList.SetItems(self.__getTables(driver, database))
02885         tableList.SetStringSelection(table)
02886 
02887         # update key column selection
02888         keyList = self.addLayerWidgets['key'][1]
02889         keyList.SetItems(self.__getColumns(driver, database, table))
02890         keyList.SetStringSelection(key)
02891         
02892         event.Skip()
02893 
02894     def OnAddLayer(self, event):
02895         """!Add new layer to vector map"""
02896         layer    = int(self.addLayerWidgets['layer'][1].GetValue())
02897         layerWin = self.addLayerWidgets['layer'][1]
02898         driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
02899         database = self.addLayerWidgets['database'][1].GetValue()
02900         table    = self.addLayerWidgets['table'][1].GetStringSelection()
02901         key      = self.addLayerWidgets['key'][1].GetStringSelection()
02902         
02903         if layer in self.mapDBInfo.layers.keys():
02904             wx.MessageBox(parent=self,
02905                           message=_("Unable to add new layer to vector map <%(vector)s>. "
02906                                     "Layer %(layer)d already exists.") % 
02907                           {'vector' : self.mapDBInfo.map, 'layer' : layer},
02908                           caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
02909             return
02910 
02911         # add new layer
02912         ret = gcmd.RunCommand('v.db.connect',
02913                               parent = self,
02914                               quiet = True,
02915                               map = self.mapDBInfo.map,
02916                               driver = driver,
02917                               database = database,
02918                               table = table,
02919                               key = key,
02920                               layer = layer)
02921         
02922         # insert records into table if required
02923         if self.addLayerWidgets['addCat'][0].IsChecked():
02924             gcmd.RunCommand('v.to.db',
02925                             parent = self,
02926                             quiet = True,
02927                             map = self.mapDBInfo.map,
02928                             layer = layer,
02929                             qlayer = layer,
02930                             option = 'cat',
02931                             columns = key)
02932 
02933         if ret == 0:
02934             # update dialog (only for new layer)
02935             self.parentDialog.UpdateDialog(layer=layer) 
02936             # update db info
02937             self.mapDBInfo = self.parentDialog.mapDBInfo
02938             # increase layer number
02939             layerWin.SetValue(layer+1)
02940 
02941         if len(self.mapDBInfo.layers.keys()) == 1:
02942             # first layer add --- enable previously disabled widgets
02943             self.deleteLayer.Enable()
02944             self.deleteTable.Enable()
02945             for label in self.modifyLayerWidgets.keys():
02946                 self.modifyLayerWidgets[label][1].Enable()
02947             
02948     def OnDeleteLayer(self, event):
02949         """!Delete layer"""
02950         try:
02951             layer = int(self.deleteLayer.GetValue())
02952         except:
02953             return
02954 
02955         gcmd.RunCommand('v.db.connect',
02956                         parent = self,
02957                         flags = 'd',
02958                         map = self.mapDBInfo.map,
02959                         layer = layer)
02960 
02961         # drop also table linked to layer which is deleted
02962         if self.deleteTable.IsChecked():
02963             driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
02964             database = self.addLayerWidgets['database'][1].GetValue()
02965             table    = self.mapDBInfo.layers[layer]['table']
02966             sql      = 'DROP TABLE %s' % (table)
02967 
02968             gcmd.RunCommand('db.execute',
02969                             parent = self,
02970                             stdin = sql,
02971                             quiet = True,
02972                             driver = driver,
02973                             database = database)
02974             
02975             # update list of tables
02976             tableList = self.addLayerWidgets['table'][1]
02977             tableList.SetItems(self.__getTables(driver, database))
02978             tableList.SetStringSelection(table)
02979         
02980         # update dialog
02981         self.parentDialog.UpdateDialog(layer=layer) 
02982         # update db info
02983         self.mapDBInfo = self.parentDialog.mapDBInfo
02984 
02985         if len(self.mapDBInfo.layers.keys()) == 0:
02986             # disable selected widgets
02987             self.deleteLayer.Enable(False)
02988             self.deleteTable.Enable(False)
02989             for label in self.modifyLayerWidgets.keys():
02990                 self.modifyLayerWidgets[label][1].Enable(False)
02991             
02992         event.Skip()
02993 
02994     def OnChangeLayer(self, event):
02995         """!Layer number of layer to be deleted is changed"""
02996         try:
02997             layer = int(event.GetString())
02998         except:
02999             try:
03000                 layer = self.mapDBInfo.layers.keys()[0]
03001             except:
03002                 return
03003 
03004         if self.GetCurrentPage() == self.modifyPanel:
03005             driver   = self.mapDBInfo.layers[layer]['driver']
03006             database = self.mapDBInfo.layers[layer]['database']
03007             table    = self.mapDBInfo.layers[layer]['table']
03008             listOfColumns = self.__getColumns(driver, database, table)
03009             self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
03010             self.modifyLayerWidgets['database'][1].SetValue(database)
03011             self.modifyLayerWidgets['table'][1].SetStringSelection(table)
03012             self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
03013             self.modifyLayerWidgets['key'][1].SetSelection(0)
03014         else:
03015             self.deleteTable.SetLabel(_('Drop also linked attribute table (%s)') % \
03016                                           self.mapDBInfo.layers[layer]['table'])
03017         if event:
03018             event.Skip()
03019 
03020     def OnModifyLayer(self, event):
03021         """!Modify layer connection settings"""
03022 
03023         layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
03024 
03025         modify = False
03026         if self.modifyLayerWidgets['driver'][1].GetStringSelection() != \
03027                 self.mapDBInfo.layers[layer]['driver'] or \
03028                 self.modifyLayerWidgets['database'][1].GetStringSelection() != \
03029                 self.mapDBInfo.layers[layer]['database'] or \
03030                 self.modifyLayerWidgets['table'][1].GetStringSelection() != \
03031                 self.mapDBInfo.layers[layer]['table'] or \
03032                 self.modifyLayerWidgets['key'][1].GetStringSelection() != \
03033                 self.mapDBInfo.layers[layer]['key']:
03034             modify = True
03035 
03036         if modify:
03037             # delete layer
03038             gcmd.RunCommand('v.db.connect',
03039                             parent = self,
03040                             quiet = True,
03041                             flag = 'd',
03042                             map = self.mapDBInfo.map,
03043                             layer = layer)
03044 
03045             # add modified layer
03046             gcmd.RunCommand('v.db.connect',
03047                             quiet = True,
03048                             map = self.mapDBInfo.map,
03049                             driver = self.modifyLayerWidgets['driver'][1].GetStringSelection(),
03050                             database = self.modifyLayerWidgets['database'][1].GetValue(),
03051                             table = self.modifyLayerWidgets['table'][1].GetStringSelection(),
03052                             key = self.modifyLayerWidgets['key'][1].GetStringSelection(),
03053                             layer = int(layer))
03054             
03055             # update dialog (only for new layer)
03056             self.parentDialog.UpdateDialog(layer=layer) 
03057             # update db info
03058             self.mapDBInfo = self.parentDialog.mapDBInfo
03059 
03060         event.Skip()
03061 
03062 def main(argv=None):
03063     if argv is None:
03064         argv = sys.argv
03065 
03066     if len(argv) != 2:
03067         print >> sys.stderr, __doc__
03068         sys.exit()
03069 
03070     # Command line arguments of the script to be run are preserved by the
03071     # hotswap.py wrapper but hotswap.py and its options are removed that
03072     # sys.argv looks as if no wrapper was present.
03073     #print "argv:", `argv`
03074 
03075     #some applications might require image handlers
03076     wx.InitAllImageHandlers()
03077 
03078     app = wx.PySimpleApp()
03079     f = AttributeManager(parent=None, id=wx.ID_ANY,
03080                          title="%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
03081                                               argv[1]),
03082                          size=(900,600), vectorName=argv[1])
03083     f.Show()
03084 
03085     app.MainLoop()
03086 
03087 if __name__ == '__main__':
03088     main()
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines