~ubuntu-branches/ubuntu/vivid/grass/vivid-proposed

« back to all changes in this revision

Viewing changes to .pc/wxpy3.0-compat.patch/gui/wxpython/dbmgr/manager.py

  • Committer: Package Import Robot
  • Author(s): Bas Couwenberg
  • Date: 2015-02-20 23:12:08 UTC
  • mfrom: (8.2.6 experimental)
  • Revision ID: package-import@ubuntu.com-20150220231208-1u6qvqm84v430b10
Tags: 7.0.0-1~exp1
* New upstream release.
* Update python-ctypes-ternary.patch to use if/else instead of and/or.
* Drop check4dev patch, rely on upstream check.
* Add build dependency on libpq-dev to grass-dev for libpq-fe.h.
* Drop patches applied upstream, refresh remaining patches.
* Update symlinks for images switched from jpg to png.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""!
2
 
@package dbmgr.manager
3
 
 
4
 
@brief GRASS Attribute Table Manager
5
 
 
6
 
This program is based on FileHunter, published in 'The wxPython Linux
7
 
Tutorial' on wxPython WIKI pages.
8
 
 
9
 
It also uses some functions at
10
 
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/426407
11
 
 
12
 
@code
13
 
python dbm.py vector@mapset
14
 
@endcode
15
 
 
16
 
List of classes:
17
 
 - manager::Log
18
 
 - manager::VirtualAttributeList
19
 
 - manager::AttributeManager
20
 
 - manager::TableListCtrl
21
 
 - manager::LayerListCtrl
22
 
 - manager::LayerBook
23
 
 
24
 
(C) 2007-2009, 2011 by the GRASS Development Team
25
 
 
26
 
This program is free software under the GNU General Public License
27
 
(>=v2). Read the file COPYING that comes with GRASS for details.
28
 
 
29
 
@author Jachym Cepicky <jachym.cepicky gmail.com>
30
 
@author Martin Landa <landa.martin gmail.com>
31
 
"""
32
 
 
33
 
import sys
34
 
import os
35
 
import locale
36
 
import tempfile
37
 
import copy
38
 
import types
39
 
 
40
 
if __name__ == "__main__":
41
 
    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython'))
42
 
from core import globalvar
43
 
import wx
44
 
import wx.lib.mixins.listctrl as listmix
45
 
import wx.lib.flatnotebook    as FN
46
 
 
47
 
import grass.script as grass
48
 
 
49
 
from dbmgr.sqlbuilder import SQLFrame
50
 
from core.gcmd        import RunCommand, GException, GError, GMessage, GWarning
51
 
from core.utils       import ListOfCatsToRange
52
 
from gui_core.dialogs import CreateNewVector
53
 
from dbmgr.vinfo      import VectorDBInfo, unicodeValue, createDbInfoDesc
54
 
from core.debug       import Debug
55
 
from dbmgr.dialogs    import ModifyTableRecord
56
 
from core.settings    import UserSettings
57
 
from gui_core.widgets import GNotebook
58
 
 
59
 
class Log:
60
 
    """
61
 
    The log output is redirected to the status bar of the containing frame.
62
 
    """
63
 
    def __init__(self, parent):
64
 
        self.parent = parent
65
 
 
66
 
    def write(self, text_string):
67
 
        """!Update status bar"""
68
 
        self.parent.SetStatusText(text_string.strip())
69
 
 
70
 
 
71
 
class VirtualAttributeList(wx.ListCtrl,
72
 
                           listmix.ListCtrlAutoWidthMixin,
73
 
                           listmix.ColumnSorterMixin):
74
 
    """
75
 
    Support virtual list class
76
 
    """
77
 
    def __init__(self, parent, log, mapDBInfo, layer):
78
 
        #
79
 
        # initialize variables
80
 
        #
81
 
        self.parent  = parent
82
 
        self.log     = log
83
 
        self.mapDBInfo = mapDBInfo
84
 
        self.layer   = layer
85
 
        
86
 
        self.columns = {} # <- LoadData()
87
 
 
88
 
        wx.ListCtrl.__init__(self, parent = parent, id = wx.ID_ANY,
89
 
                             style = wx.LC_REPORT | wx.LC_HRULES |
90
 
                             wx.LC_VRULES | wx.LC_VIRTUAL | wx.LC_SORT_ASCENDING)
91
 
        
92
 
        try:
93
 
            keyColumn = self.LoadData(layer)
94
 
        except GException, e:
95
 
            GError(parent = self,
96
 
                   message = e.value)
97
 
            return
98
 
        
99
 
        #
100
 
        # add some attributes (colourful background for each item rows)
101
 
        #
102
 
        self.attr1 = wx.ListItemAttr()
103
 
        self.attr1.SetBackgroundColour(wx.Colour(238,238,238))
104
 
        self.attr2 = wx.ListItemAttr()
105
 
        self.attr2.SetBackgroundColour("white")
106
 
        self.il = wx.ImageList(16, 16)
107
 
        self.sm_up = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_UP,   wx.ART_TOOLBAR,
108
 
                                                          (16,16)))
109
 
        self.sm_dn = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR,
110
 
                                                          (16,16)))
111
 
        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
112
 
        
113
 
        # setup mixins
114
 
        listmix.ListCtrlAutoWidthMixin.__init__(self)
115
 
        listmix.ColumnSorterMixin.__init__(self, len(self.columns))
116
 
 
117
 
        # sort item by category (id)
118
 
        if keyColumn > -1:
119
 
            self.SortListItems(col = keyColumn, ascending = True) 
120
 
        elif keyColumn:
121
 
            self.SortListItems(col = 0, ascending = True) 
122
 
        
123
 
        # events
124
 
        self.Bind(wx.EVT_LIST_ITEM_SELECTED,   self.OnItemSelected)
125
 
        self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
126
 
        self.Bind(wx.EVT_LIST_COL_CLICK,       self.OnColumnSort)     
127
 
        self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColumnMenu)     
128
 
        
129
 
    def Update(self, mapDBInfo):
130
 
        """!Update list according new mapDBInfo description"""
131
 
        self.mapDBInfo = mapDBInfo
132
 
        self.LoadData(self.layer)
133
 
 
134
 
    def LoadData(self, layer, columns = None, where = None, sql = None):
135
 
        """!Load data into list
136
 
 
137
 
        @param layer layer number
138
 
        @param columns list of columns for output (-> v.db.select)
139
 
        @param where where statement (-> v.db.select)
140
 
        @param sql full sql statement (-> db.select)
141
 
        
142
 
        @return id of key column 
143
 
        @return -1 if key column is not displayed
144
 
        """
145
 
        self.log.write(_("Loading data..."))
146
 
        
147
 
        tableName    = self.mapDBInfo.layers[layer]['table']
148
 
        keyColumn    = self.mapDBInfo.layers[layer]['key']
149
 
        try:
150
 
            self.columns = self.mapDBInfo.tables[tableName]
151
 
        except KeyError:
152
 
            raise GException(_("Attribute table <%s> not found. "
153
 
                               "For creating the table switch to "
154
 
                               "'Manage layers' tab.") % tableName)
155
 
        
156
 
        if not columns:
157
 
            columns = self.mapDBInfo.GetColumns(tableName)
158
 
        else:
159
 
            all = self.mapDBInfo.GetColumns(tableName)
160
 
            for col in columns:
161
 
                if col not in all:
162
 
                    GError(parent = self,
163
 
                           message = _("Column <%(column)s> not found in "
164
 
                                       "in the table <%(table)s>.") % \
165
 
                               { 'column' : col, 'table' : tableName })
166
 
                    return
167
 
        
168
 
        try:
169
 
            # for maps connected via v.external
170
 
            keyId = columns.index(keyColumn)
171
 
        except:
172
 
            keyId = -1
173
 
        
174
 
        #
175
 
        # read data
176
 
        #
177
 
        # FIXME: Max. number of rows, while the GUI is still usable
178
 
 
179
 
        # stdout can be very large, do not use PIPE, redirect to temp file
180
 
        # TODO: more effective way should be implemented...
181
 
        outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
182
 
 
183
 
        # split on field sep breaks if varchar() column contains the
184
 
        # values, so while sticking with ASCII we make it something
185
 
        # highly unlikely to exist naturally.
186
 
        fs = '{_sep_}'
187
 
 
188
 
        cmdParams = dict(quiet = True,
189
 
                         parent = self,
190
 
                         flags = 'c',
191
 
                         fs = fs)
192
 
 
193
 
        if sql:
194
 
            cmdParams.update(dict(sql = sql,
195
 
                                  output = outFile.name))
196
 
            ret = RunCommand('db.select',
197
 
                             **cmdParams)
198
 
        else:
199
 
            cmdParams.update(dict(map = self.mapDBInfo.map,
200
 
                                  layer = layer,
201
 
                                  where = where,
202
 
                                  stdout = outFile))
203
 
            if columns:
204
 
                cmdParams.update(dict(columns = ','.join(columns)))
205
 
 
206
 
            ret = RunCommand('v.db.select',
207
 
                             **cmdParams)
208
 
        
209
 
        # These two should probably be passed to init more cleanly
210
 
        # setting the numbers of items = number of elements in the dictionary
211
 
        self.itemDataMap  = {}
212
 
        self.itemIndexMap = []
213
 
        self.itemCatsMap  = {}
214
 
        
215
 
        self.DeleteAllItems()
216
 
        
217
 
        # self.ClearAll()
218
 
        for i in range(self.GetColumnCount()):
219
 
            self.DeleteColumn(0)
220
 
        
221
 
        i = 0
222
 
        info = wx.ListItem()
223
 
        info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
224
 
        info.m_image = -1
225
 
        info.m_format = 0
226
 
        for column in columns:
227
 
            info.m_text = column
228
 
            self.InsertColumnInfo(i, info)
229
 
            i += 1
230
 
            
231
 
            if i >= 256:
232
 
                self.log.write(_("Can display only 256 columns."))
233
 
        
234
 
        i = 0
235
 
        outFile.seek(0)
236
 
        
237
 
        while True:
238
 
            # os.linesep doesn't work here (MSYS)
239
 
            record = outFile.readline().replace('\n', '')
240
 
            
241
 
            if not record:
242
 
                break
243
 
 
244
 
            record = record.split(fs) 
245
 
            if len(columns) != len(record):
246
 
                GError(parent = self, 
247
 
                       message = _("Inconsistent number of columns " 
248
 
                                   "in the table <%(table)s>.") % \
249
 
                                {'table' : tableName })
250
 
                self.columns = {} # because of IsEmpty method
251
 
                return
252
 
 
253
 
            self.AddDataRow(i, record, columns, keyId)
254
 
 
255
 
            i += 1
256
 
            if i >= 100000:
257
 
                self.log.write(_("Viewing limit: 100000 records."))
258
 
                break
259
 
        
260
 
        self.SetItemCount(i)
261
 
        
262
 
        i = 0
263
 
        for col in columns:
264
 
            width = self.columns[col]['length'] * 6 # FIXME
265
 
            if width < 60:
266
 
                width = 60
267
 
            if width > 300:
268
 
                width = 300
269
 
            self.SetColumnWidth(col = i, width = width)
270
 
            i += 1
271
 
        
272
 
        self.SendSizeEvent()
273
 
        
274
 
        self.log.write(_("Number of loaded records: %d") % \
275
 
                           self.GetItemCount())
276
 
        
277
 
        return keyId
278
 
    
279
 
    def AddDataRow(self, i, record, columns, keyId):
280
 
        """!Add row to the data list"""
281
 
        self.itemDataMap[i] = []
282
 
        keyColumn = self.mapDBInfo.layers[self.layer]['key']
283
 
        j = 0
284
 
        cat = None
285
 
        
286
 
        if keyColumn == 'OGC_FID':
287
 
            self.itemDataMap[i].append(i+1)
288
 
            j += 1
289
 
            cat = i + 1
290
 
        
291
 
        for value in record:
292
 
            if self.columns[columns[j]]['ctype'] != types.StringType:
293
 
                try:
294
 
                    ### casting disabled (2009/03)
295
 
                    ### self.itemDataMap[i].append(self.columns[columns[j]]['ctype'](value))
296
 
                    self.itemDataMap[i].append(value)
297
 
                except ValueError:
298
 
                    self.itemDataMap[i].append(_('Unknown value'))
299
 
            else:
300
 
                # encode string values
301
 
                try:
302
 
                    self.itemDataMap[i].append(unicodeValue(value))
303
 
                except UnicodeDecodeError:
304
 
                    self.itemDataMap[i].append(_("Unable to decode value. "
305
 
                                                 "Set encoding in GUI preferences ('Attributes')."))
306
 
                
307
 
            if not cat and keyId > -1 and keyId == j:
308
 
                try:
309
 
                    cat = self.columns[columns[j]]['ctype'] (value)
310
 
                except ValueError, e:
311
 
                    cat = -1
312
 
                    GError(parent = self,
313
 
                           message = _("Error loading attribute data. "
314
 
                                       "Record number: %(rec)d. Unable to convert value '%(val)s' in "
315
 
                                       "key column (%(key)s) to integer.\n\n"
316
 
                                       "Details: %(detail)s") % \
317
 
                               { 'rec' : i + 1, 'val' : value,
318
 
                                 'key' : keyColumn, 'detail' : e})
319
 
            j += 1
320
 
        
321
 
        self.itemIndexMap.append(i)
322
 
        if keyId > -1: # load cats only when LoadData() is called first time
323
 
            self.itemCatsMap[i] = cat
324
 
        
325
 
    def OnItemSelected(self, event):
326
 
        """!Item selected. Add item to selected cats..."""
327
 
        #         cat = int(self.GetItemText(event.m_itemIndex))
328
 
        #         if cat not in self.selectedCats:
329
 
        #             self.selectedCats.append(cat)
330
 
        #             self.selectedCats.sort()
331
 
        
332
 
        event.Skip()
333
 
 
334
 
    def OnItemDeselected(self, event):
335
 
        """!Item deselected. Remove item from selected cats..."""
336
 
        #         cat = int(self.GetItemText(event.m_itemIndex))
337
 
        #         if cat in self.selectedCats:
338
 
        #             self.selectedCats.remove(cat)
339
 
        #             self.selectedCats.sort()
340
 
 
341
 
        event.Skip()
342
 
 
343
 
    def GetSelectedItems(self):
344
 
        """!Return list of selected items (category numbers)"""
345
 
        cats = []
346
 
        item = self.GetFirstSelected()
347
 
        while item != -1:
348
 
            cats.append(self.GetItemText(item))
349
 
            item = self.GetNextSelected(item)
350
 
 
351
 
        return cats
352
 
 
353
 
    def GetColumnText(self, index, col):
354
 
        """!Return column text"""
355
 
        item = self.GetItem(index, col)
356
 
        return item.GetText()
357
 
 
358
 
    def GetListCtrl(self):
359
 
        """!Returt list"""
360
 
        return self
361
 
 
362
 
    def OnGetItemText(self, item, col):
363
 
        """!Get item text"""
364
 
        index = self.itemIndexMap[item]
365
 
        s = self.itemDataMap[index][col]
366
 
        return s
367
 
 
368
 
    def OnGetItemAttr(self, item):
369
 
        """!Get item attributes"""
370
 
        if ( item % 2) == 0:
371
 
            return self.attr2
372
 
        else:
373
 
            return self.attr1
374
 
 
375
 
    def OnColumnMenu(self, event):
376
 
        """!Column heading right mouse button -> pop-up menu"""
377
 
        self._col = event.GetColumn()
378
 
        
379
 
        popupMenu = wx.Menu()
380
 
 
381
 
        if not hasattr (self, "popupID1"):
382
 
            self.popupID1 = wx.NewId()
383
 
            self.popupID2 = wx.NewId()
384
 
            self.popupID3 = wx.NewId()
385
 
            self.popupID4 = wx.NewId()
386
 
            self.popupID5 = wx.NewId()
387
 
            self.popupID6 = wx.NewId()
388
 
            self.popupID7 = wx.NewId()
389
 
            self.popupID8 = wx.NewId()
390
 
            self.popupID9 = wx.NewId()
391
 
            self.popupID10 = wx.NewId()
392
 
            self.popupID11 = wx.NewId()
393
 
            self.popupID12 = wx.NewId()
394
 
        
395
 
        popupMenu.Append(self.popupID1, text = _("Sort ascending"))
396
 
        popupMenu.Append(self.popupID2, text = _("Sort descending"))
397
 
        popupMenu.AppendSeparator()
398
 
        subMenu = wx.Menu()
399
 
        popupMenu.AppendMenu(self.popupID3, _("Calculate (only numeric columns)"),
400
 
                             subMenu)
401
 
        if not self.log.parent.editable or \
402
 
                self.columns[self.GetColumn(self._col).GetText()]['ctype'] not in (types.IntType, types.FloatType):
403
 
            popupMenu.Enable(self.popupID3, False)
404
 
        
405
 
        subMenu.Append(self.popupID4,  text = _("Area size"))
406
 
        subMenu.Append(self.popupID5,  text = _("Line length"))
407
 
        subMenu.Append(self.popupID6,  text = _("Compactness of an area"))
408
 
        subMenu.Append(self.popupID7,  text = _("Fractal dimension of boundary defining a polygon"))
409
 
        subMenu.Append(self.popupID8,  text = _("Perimeter length of an area"))
410
 
        subMenu.Append(self.popupID9,  text = _("Number of features for each category"))
411
 
        subMenu.Append(self.popupID10, text = _("Slope steepness of 3D line"))
412
 
        subMenu.Append(self.popupID11, text = _("Line sinuousity"))
413
 
        subMenu.Append(self.popupID12, text = _("Line azimuth"))
414
 
        
415
 
        self.Bind (wx.EVT_MENU, self.OnColumnSortAsc,  id = self.popupID1)
416
 
        self.Bind (wx.EVT_MENU, self.OnColumnSortDesc, id = self.popupID2)
417
 
        for id in (self.popupID4, self.popupID5, self.popupID6,
418
 
                   self.popupID7, self.popupID8, self.popupID9,
419
 
                   self.popupID10, self.popupID11, self.popupID12):
420
 
            self.Bind(wx.EVT_MENU, self.OnColumnCompute, id = id)
421
 
        
422
 
        self.PopupMenu(popupMenu)
423
 
        popupMenu.Destroy()
424
 
 
425
 
    def OnColumnSort(self, event):
426
 
        """!Column heading left mouse button -> sorting"""
427
 
        self._col = event.GetColumn()
428
 
        
429
 
        self.ColumnSort()
430
 
        
431
 
        event.Skip()
432
 
 
433
 
    def OnColumnSortAsc(self, event):
434
 
        """!Sort values of selected column (ascending)"""
435
 
        self.SortListItems(col = self._col, ascending = True)
436
 
        event.Skip()
437
 
 
438
 
    def OnColumnSortDesc(self, event):
439
 
        """!Sort values of selected column (descending)"""
440
 
        self.SortListItems(col = self._col, ascending = False)
441
 
        event.Skip()
442
 
        
443
 
    def OnColumnCompute(self, event):
444
 
        """!Compute values of selected column"""
445
 
        id = event.GetId()
446
 
        
447
 
        option = None
448
 
        if id == self.popupID4:
449
 
            option = 'area'
450
 
        elif id == self.popupID5:
451
 
            option = 'length'
452
 
        elif id == self.popupID6:
453
 
            option = 'compact'
454
 
        elif id == self.popupID7:
455
 
            option = 'fd'
456
 
        elif id == self.popupID8:
457
 
            option = 'perimeter'
458
 
        elif id == self.popupID9:
459
 
            option = 'count'
460
 
        elif id == self.popupID10:
461
 
            option = 'slope'
462
 
        elif id == self.popupID11:
463
 
            option = 'sinuous'
464
 
        elif id == self.popupID12:
465
 
            option = 'azimuth'
466
 
        
467
 
        if not option:
468
 
            return
469
 
        
470
 
        RunCommand('v.to.db',
471
 
                   parent = self.parent,
472
 
                   map = self.mapDBInfo.map,
473
 
                   layer = self.layer, 
474
 
                   option = option,
475
 
                   columns = self.GetColumn(self._col).GetText())
476
 
        
477
 
        self.LoadData(self.layer)
478
 
        
479
 
    def ColumnSort(self):
480
 
        """!Sort values of selected column (self._col)"""
481
 
        # remove duplicated arrow symbol from column header
482
 
        # FIXME: should be done automatically
483
 
        info = wx.ListItem()
484
 
        info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE
485
 
        info.m_image = -1
486
 
        for column in range(self.GetColumnCount()):
487
 
            info.m_text = self.GetColumn(column).GetText()
488
 
            self.SetColumn(column, info)
489
 
        
490
 
    def SortItems(self, sorter = cmp):
491
 
        """!Sort items"""
492
 
        items = list(self.itemDataMap.keys())
493
 
        items.sort(self.Sorter)
494
 
        self.itemIndexMap = items
495
 
 
496
 
        # redraw the list
497
 
        self.Refresh()
498
 
        
499
 
    def Sorter(self, key1, key2):
500
 
        colName = self.GetColumn(self._col).GetText()
501
 
        ascending = self._colSortFlag[self._col]
502
 
        try:
503
 
            item1 = self.columns[colName]["ctype"](self.itemDataMap[key1][self._col])
504
 
            item2 = self.columns[colName]["ctype"](self.itemDataMap[key2][self._col])
505
 
        except ValueError:
506
 
            item1 = self.itemDataMap[key1][self._col]
507
 
            item2 = self.itemDataMap[key2][self._col]
508
 
 
509
 
        if type(item1) == types.StringType or type(item2) == types.StringTypes:
510
 
            cmpVal = locale.strcoll(str(item1), str(item2))
511
 
        else:
512
 
            cmpVal = cmp(item1, item2)
513
 
 
514
 
 
515
 
        # If the items are equal then pick something else to make the sort value unique
516
 
        if cmpVal == 0:
517
 
            cmpVal = apply(cmp, self.GetSecondarySortValues(self._col, key1, key2))
518
 
        
519
 
        if ascending:
520
 
            return cmpVal
521
 
        else:
522
 
            return -cmpVal
523
 
 
524
 
    def GetSortImages(self):
525
 
        """!Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py"""
526
 
        return (self.sm_dn, self.sm_up)
527
 
 
528
 
    def IsEmpty(self):
529
 
        """!Check if list if empty"""
530
 
        if self.columns:
531
 
            return False
532
 
        
533
 
        return True
534
 
    
535
 
class AttributeManager(wx.Frame):
536
 
    def __init__(self, parent, id = wx.ID_ANY,
537
 
                 title = None, vectorName = None, item = None, log = None,
538
 
                 selection = None, **kwargs):
539
 
        """!GRASS Attribute Table Manager window
540
 
 
541
 
        @param parent parent window
542
 
        @parem id window id
543
 
        @param title window title or None for default title
544
 
        @param vetorName name of vector map
545
 
        @param item item from Layer Tree
546
 
        @param log log window
547
 
        @param selection name of page to be selected
548
 
        @param kwagrs other wx.Frame's arguments
549
 
        """
550
 
        self.vectorName = vectorName
551
 
        self.parent     = parent # GMFrame
552
 
        self.treeItem   = item   # item in layer tree
553
 
        if self.parent and self.parent.GetName() == "LayerManager" and \
554
 
                self.treeItem and not self.vectorName:
555
 
            maptree = self.parent.curr_page.maptree
556
 
            name = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetName()
557
 
            self.vectorName = name
558
 
        
559
 
        # vector attributes can be changed only if vector map is in
560
 
        # the current mapset
561
 
        if grass.find_file(name = self.vectorName, element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
562
 
            self.editable = True
563
 
        else:
564
 
            self.editable = False
565
 
        
566
 
        self.cmdLog = log    # self.parent.goutput
567
 
        
568
 
        wx.Frame.__init__(self, parent, id, *kwargs)
569
 
 
570
 
        # title
571
 
        if not title:
572
 
            self.SetTitle("%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
573
 
                                         self.vectorName))
574
 
        else:
575
 
            self.SetTitle(title)
576
 
        
577
 
        # icon
578
 
        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'), wx.BITMAP_TYPE_ICO))
579
 
 
580
 
        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
581
 
 
582
 
        try:
583
 
            self.map        = self.parent.curr_page.maptree.Map
584
 
            self.mapdisplay = self.parent.curr_page.maptree.mapdisplay
585
 
        except:
586
 
            self.map = self.mapdisplay = None
587
 
        
588
 
        # status bar log class
589
 
        self.log = Log(self) # -> statusbar
590
 
 
591
 
        # query map layer (if parent (GMFrame) is given)
592
 
        self.qlayer = None
593
 
 
594
 
        # -> layers / tables description
595
 
        self.mapDBInfo = VectorDBInfo(self.vectorName)
596
 
 
597
 
        # sqlbuilder
598
 
        self.builder = None
599
 
        
600
 
        if len(self.mapDBInfo.layers.keys()) == 0:
601
 
            GMessage(parent = self.parent,
602
 
                     message = _("Database connection for vector map <%s> "
603
 
                                 "is not defined in DB file. "
604
 
                                 "You can define new connection in "
605
 
                                 "'Manage layers' tab.") % self.vectorName)
606
 
        
607
 
        #
608
 
        # list of command/SQL statements to be performed
609
 
        #
610
 
        self.listOfCommands      = []
611
 
        self.listOfSQLStatements = []
612
 
 
613
 
        self.CreateStatusBar(number = 1)
614
 
 
615
 
        # set up virtual lists (each layer)
616
 
        ### {layer: list, widgets...}
617
 
        self.layerPage = {}
618
 
 
619
 
        self.notebook = GNotebook(self.panel, style = globalvar.FNPageDStyle)
620
 
        
621
 
        if globalvar.hasAgw:
622
 
            dbmStyle = { 'agwStyle' : globalvar.FNPageStyle }
623
 
        else:
624
 
            dbmStyle = { 'style' : globalvar.FNPageStyle }
625
 
        
626
 
        self.browsePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
627
 
                                          **dbmStyle)
628
 
        self.notebook.AddPage(page = self.browsePage, text = _("Browse data"),
629
 
                              name = 'browse')
630
 
        self.browsePage.SetTabAreaColour(globalvar.FNPageColor)
631
 
 
632
 
        self.manageTablePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
633
 
                                               **dbmStyle)
634
 
        self.notebook.AddPage(page = self.manageTablePage, text = _("Manage tables"),
635
 
                              name = 'table')
636
 
        if not self.editable:
637
 
            self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
638
 
        self.manageTablePage.SetTabAreaColour(globalvar.FNPageColor)
639
 
 
640
 
        self.manageLayerPage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
641
 
                                               **dbmStyle)
642
 
        self.notebook.AddPage(page = self.manageLayerPage, text = _("Manage layers"),
643
 
                              name = 'layers')
644
 
        self.manageLayerPage.SetTabAreaColour(globalvar.FNPageColor)
645
 
        if not self.editable:
646
 
            self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
647
 
        
648
 
        self._createBrowsePage()
649
 
        self._createManageTablePage()
650
 
        self._createManageLayerPage()
651
 
 
652
 
        if selection:
653
 
            wx.CallAfter(self.notebook.SetSelectionByName, selection)
654
 
        else:
655
 
            wx.CallAfter(self.notebook.SetSelection, 0) # select browse tab
656
 
        
657
 
        # buttons
658
 
        self.btnQuit   = wx.Button(parent = self.panel, id = wx.ID_EXIT)
659
 
        self.btnQuit.SetToolTipString(_("Close Attribute Table Manager"))
660
 
        self.btnReload = wx.Button(parent = self.panel, id = wx.ID_REFRESH)
661
 
        self.btnReload.SetToolTipString(_("Reload attribute data (selected layer only)"))
662
 
 
663
 
        # events
664
 
        self.btnQuit.Bind(wx.EVT_BUTTON,   self.OnCloseWindow)
665
 
        self.btnReload.Bind(wx.EVT_BUTTON, self.OnDataReload)
666
 
        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
667
 
        self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.browsePage)
668
 
        self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.manageTablePage)
669
 
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
670
 
 
671
 
        # do layout
672
 
        self._layout()
673
 
 
674
 
        # self.SetMinSize(self.GetBestSize())
675
 
        self.SetSize((700, 550)) # FIXME hard-coded size
676
 
        self.SetMinSize(self.GetSize())
677
 
 
678
 
    def _createBrowsePage(self, onlyLayer = -1):
679
 
        """!Create browse tab page"""
680
 
        for layer in self.mapDBInfo.layers.keys():
681
 
            if onlyLayer > 0 and layer != onlyLayer:
682
 
                continue
683
 
 
684
 
            panel = wx.Panel(parent = self.browsePage, id = wx.ID_ANY)
685
 
            
686
 
            #IMPORTANT NOTE: wx.StaticBox MUST be defined BEFORE any of the 
687
 
            #   controls that are placed IN the wx.StaticBox, or it will freeze
688
 
            #   on the Mac
689
 
            
690
 
            listBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
691
 
                                   label = " %s " % _("Attribute data - right-click to edit/manage records"))
692
 
            listSizer = wx.StaticBoxSizer(listBox, wx.VERTICAL)
693
 
            
694
 
            win = VirtualAttributeList(panel, self.log,
695
 
                                       self.mapDBInfo, layer)
696
 
            if win.IsEmpty():
697
 
                panel.Destroy()
698
 
                continue
699
 
            
700
 
            win.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnDataItemActivated)
701
 
 
702
 
            self.layerPage[layer] = {'browsePage': panel.GetId()}
703
 
            
704
 
            label = _("Table")
705
 
            if not self.editable:
706
 
                label += _(" (readonly)")
707
 
            self.browsePage.AddPage(page = panel, text = " %d / %s %s" % \
708
 
                                        (layer, label, self.mapDBInfo.layers[layer]['table']))
709
 
            
710
 
            pageSizer = wx.BoxSizer(wx.VERTICAL)
711
 
 
712
 
            # attribute data            
713
 
            sqlBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
714
 
                                  label = " %s " % _("SQL Query"))
715
 
 
716
 
            sqlSizer = wx.StaticBoxSizer(sqlBox, wx.VERTICAL)
717
 
 
718
 
            win.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnDataRightUp) #wxMSW
719
 
            win.Bind(wx.EVT_RIGHT_UP,            self.OnDataRightUp) #wxGTK
720
 
            if UserSettings.Get(group = 'atm', key = 'leftDbClick', subkey = 'selection') == 0:
721
 
                win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataItemEdit)
722
 
                win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataItemEdit)
723
 
            else:
724
 
                win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataDrawSelected)
725
 
                win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataDrawSelected)
726
 
            
727
 
            listSizer.Add(item = win, proportion = 1,
728
 
                          flag = wx.EXPAND | wx.ALL,
729
 
                          border = 3)
730
 
 
731
 
            # sql statement box
732
 
            btnApply = wx.Button(parent = panel, id = wx.ID_APPLY)
733
 
            btnApply.SetToolTipString(_("Apply SELECT statement and reload data records"))
734
 
            btnApply.Bind(wx.EVT_BUTTON, self.OnApplySqlStatement)
735
 
            btnSqlBuilder = wx.Button(parent = panel, id = wx.ID_ANY, label = _("SQL Builder"))
736
 
            btnSqlBuilder.Bind(wx.EVT_BUTTON, self.OnBuilder)
737
 
 
738
 
            sqlSimple = wx.RadioButton(parent = panel, id = wx.ID_ANY,
739
 
                                       label = _("Simple"))
740
 
            sqlSimple.SetValue(True)
741
 
            sqlAdvanced = wx.RadioButton(parent = panel, id = wx.ID_ANY,
742
 
                                         label = _("Advanced"))
743
 
            sqlSimple.Bind(wx.EVT_RADIOBUTTON,   self.OnChangeSql)
744
 
            sqlAdvanced.Bind(wx.EVT_RADIOBUTTON, self.OnChangeSql)
745
 
 
746
 
            sqlWhereColumn = wx.ComboBox(parent = panel, id = wx.ID_ANY,
747
 
                                         size = (100,-1),
748
 
                                         style = wx.CB_SIMPLE | wx.CB_READONLY,
749
 
                                         choices = self.mapDBInfo.GetColumns(self.mapDBInfo.layers[layer]['table']))
750
 
            sqlWhereColumn.SetSelection(0)
751
 
            sqlWhereCond = wx.Choice(parent = panel, id = wx.ID_ANY,
752
 
                                     size = (55,-1),
753
 
                                     choices = ['=', '!=', '<', '<=', '>', '>='])
754
 
            sqlWhereValue = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = "",
755
 
                                        style = wx.TE_PROCESS_ENTER)
756
 
            sqlWhereValue.SetToolTipString(_("Example: %s") % "MULTILANE = 'no' AND OBJECTID < 10")
757
 
 
758
 
            sqlStatement = wx.TextCtrl(parent = panel, id = wx.ID_ANY,
759
 
                                       value = "SELECT * FROM %s" % \
760
 
                                           self.mapDBInfo.layers[layer]['table'],
761
 
                                       style = wx.TE_PROCESS_ENTER)
762
 
            sqlStatement.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' AND OBJECTID < 10")
763
 
            sqlWhereValue.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
764
 
            sqlStatement.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
765
 
 
766
 
            sqlLabel = wx.StaticText(parent = panel, id = wx.ID_ANY,
767
 
                                     label = "SELECT * FROM %s WHERE " % \
768
 
                                         self.mapDBInfo.layers[layer]['table'])
769
 
            label_query = wx.StaticText(parent = panel, id = wx.ID_ANY,
770
 
                                        label = "")
771
 
 
772
 
            sqlFlexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
773
 
 
774
 
            sqlFlexSizer.Add(item = sqlSimple,
775
 
                             flag = wx.ALIGN_CENTER_VERTICAL)
776
 
            sqlSimpleSizer = wx.BoxSizer(wx.HORIZONTAL)
777
 
            sqlSimpleSizer.Add(item = sqlLabel,
778
 
                               flag = wx.ALIGN_CENTER_VERTICAL)
779
 
            sqlSimpleSizer.Add(item = sqlWhereColumn,
780
 
                               flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
781
 
            sqlSimpleSizer.Add(item = sqlWhereCond,
782
 
                               flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
783
 
                               border = 3)
784
 
            sqlSimpleSizer.Add(item = sqlWhereValue, proportion = 1,
785
 
                               flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
786
 
            sqlFlexSizer.Add(item = sqlSimpleSizer,
787
 
                             flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
788
 
            sqlFlexSizer.Add(item = btnApply,
789
 
                             flag = wx.ALIGN_RIGHT)
790
 
            sqlFlexSizer.Add(item = sqlAdvanced,
791
 
                             flag = wx.ALIGN_CENTER_VERTICAL)
792
 
            sqlFlexSizer.Add(item = sqlStatement,
793
 
                             flag = wx.EXPAND)
794
 
            sqlFlexSizer.Add(item = btnSqlBuilder,
795
 
                             flag = wx.ALIGN_RIGHT)
796
 
            sqlFlexSizer.AddGrowableCol(1)
797
 
 
798
 
            sqlSizer.Add(item = sqlFlexSizer,
799
 
                         flag = wx.ALL | wx.EXPAND,
800
 
                         border = 3)
801
 
 
802
 
            pageSizer.Add(item = listSizer,
803
 
                          proportion = 1,
804
 
                          flag = wx.ALL | wx.EXPAND,
805
 
                          border = 5)
806
 
 
807
 
            pageSizer.Add(item = sqlSizer,
808
 
                          proportion = 0,
809
 
                          flag = wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.EXPAND,
810
 
                          border = 5)
811
 
 
812
 
            panel.SetSizer(pageSizer)
813
 
 
814
 
            self.layerPage[layer]['data']      = win.GetId()
815
 
            self.layerPage[layer]['simple']    = sqlSimple.GetId()
816
 
            self.layerPage[layer]['advanced']  = sqlAdvanced.GetId()
817
 
            self.layerPage[layer]['whereColumn'] = sqlWhereColumn.GetId()
818
 
            self.layerPage[layer]['whereOperator'] = sqlWhereCond.GetId()
819
 
            self.layerPage[layer]['where']     = sqlWhereValue.GetId()
820
 
            self.layerPage[layer]['builder']   = btnSqlBuilder.GetId()
821
 
            self.layerPage[layer]['statement'] = sqlStatement.GetId()
822
 
 
823
 
 
824
 
        self.browsePage.SetSelection(0) # select first layer
825
 
        try:
826
 
            self.layer = self.mapDBInfo.layers.keys()[0]
827
 
            self.OnChangeSql(None)
828
 
            self.log.write(_("Number of loaded records: %d") % \
829
 
                           self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
830
 
        except (IndexError, KeyError):
831
 
            self.layer = None
832
 
        
833
 
    def _createManageTablePage(self, onlyLayer = -1):
834
 
        """!Create manage page (create/link and alter tables)"""
835
 
        for layer in self.mapDBInfo.layers.keys():
836
 
            if onlyLayer > 0 and layer != onlyLayer:
837
 
                continue
838
 
            
839
 
            if not layer in self.layerPage:
840
 
                continue
841
 
            
842
 
            panel = wx.Panel(parent = self.manageTablePage, id = wx.ID_ANY)
843
 
            self.layerPage[layer]['tablePage'] = panel.GetId()
844
 
            label = _("Table")
845
 
            if not self.editable:
846
 
                label += _(" (readonly)")
847
 
            self.manageTablePage.AddPage(page = panel,
848
 
                                         text = " %d / %s %s" % (layer, label,
849
 
                                                               self.mapDBInfo.layers[layer]['table']))
850
 
            
851
 
            pageSizer = wx.BoxSizer(wx.VERTICAL)
852
 
            
853
 
            #
854
 
            # dbInfo
855
 
            #
856
 
            dbBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
857
 
                                          label = " %s " % _("Database connection"))
858
 
            dbSizer = wx.StaticBoxSizer(dbBox, wx.VERTICAL)
859
 
            dbSizer.Add(item = createDbInfoDesc(panel, self.mapDBInfo, layer),
860
 
                        proportion = 1,
861
 
                        flag = wx.EXPAND | wx.ALL,
862
 
                        border = 3)
863
 
            
864
 
            #
865
 
            # table description
866
 
            #
867
 
            table = self.mapDBInfo.layers[layer]['table']
868
 
            tableBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
869
 
                                    label = " %s " % _("Table <%s> - right-click to delete column(s)") % table)
870
 
            
871
 
            tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
872
 
            
873
 
            tlist = self._createTableDesc(panel, table)
874
 
            tlist.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnTableRightUp) #wxMSW
875
 
            tlist.Bind(wx.EVT_RIGHT_UP,            self.OnTableRightUp) #wxGTK
876
 
            self.layerPage[layer]['tableData'] = tlist.GetId()
877
 
            
878
 
            # manage columns (add)
879
 
            addBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
880
 
                                  label = " %s " % _("Add column"))
881
 
            addSizer = wx.StaticBoxSizer(addBox, wx.HORIZONTAL)
882
 
            
883
 
            column = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '',
884
 
                                 size = (150, -1), style = wx.TE_PROCESS_ENTER)
885
 
            column.Bind(wx.EVT_TEXT,       self.OnTableAddColumnName)
886
 
            column.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemAdd)
887
 
            self.layerPage[layer]['addColName'] = column.GetId()
888
 
            addSizer.Add(item =  wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Column")),
889
 
                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
890
 
                         border = 5)
891
 
            addSizer.Add(item = column, proportion = 1,
892
 
                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
893
 
                         border = 5)
894
 
            
895
 
            ctype = wx.Choice (parent = panel, id = wx.ID_ANY,
896
 
                               choices = ["integer",
897
 
                                          "double",
898
 
                                          "varchar",
899
 
                                          "date"]) # FIXME
900
 
            ctype.SetSelection(0)
901
 
            ctype.Bind(wx.EVT_CHOICE, self.OnTableChangeType)
902
 
            self.layerPage[layer]['addColType'] = ctype.GetId()
903
 
            addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Type")), 
904
 
                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
905
 
                         border = 5)
906
 
            addSizer.Add(item = ctype,
907
 
                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
908
 
                         border = 5)
909
 
            
910
 
            length = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
911
 
                                 initial = 250,
912
 
                                 min = 1, max = 1e6)
913
 
            length.Enable(False)
914
 
            self.layerPage[layer]['addColLength'] = length.GetId()
915
 
            addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Length")),
916
 
                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
917
 
                         border = 5)
918
 
            addSizer.Add(item = length,
919
 
                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
920
 
                         border = 5)
921
 
            
922
 
            btnAddCol = wx.Button(parent = panel, id = wx.ID_ADD)
923
 
            btnAddCol.Bind(wx.EVT_BUTTON, self.OnTableItemAdd)
924
 
            btnAddCol.Enable(False)
925
 
            self.layerPage[layer]['addColButton'] = btnAddCol.GetId()
926
 
            addSizer.Add(item = btnAddCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND,
927
 
                         border = 3)
928
 
            
929
 
            # manage columns (rename)
930
 
            renameBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
931
 
                                     label = " %s " % _("Rename column"))
932
 
            renameSizer = wx.StaticBoxSizer(renameBox, wx.HORIZONTAL)
933
 
            
934
 
            column = wx.ComboBox(parent = panel, id = wx.ID_ANY, size = (150, -1),
935
 
                                 style = wx.CB_SIMPLE | wx.CB_READONLY,
936
 
                                 choices = self.mapDBInfo.GetColumns(table))
937
 
            column.SetSelection(0)
938
 
            self.layerPage[layer]['renameCol'] = column.GetId()
939
 
            renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Column")),
940
 
                            flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
941
 
                            border = 5)
942
 
            renameSizer.Add(item = column, proportion = 1,
943
 
                            flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
944
 
                            border = 5)
945
 
            
946
 
            columnTo = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '',
947
 
                                   size = (150, -1), style = wx.TE_PROCESS_ENTER)
948
 
            columnTo.Bind(wx.EVT_TEXT,       self.OnTableRenameColumnName)
949
 
            columnTo.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemChange)
950
 
            self.layerPage[layer]['renameColTo'] = columnTo.GetId()
951
 
            renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("To")),
952
 
                            flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
953
 
                            border = 5)
954
 
            renameSizer.Add(item = columnTo, proportion = 1,
955
 
                            flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
956
 
                            border = 5)
957
 
            
958
 
            btnRenameCol = wx.Button(parent = panel, id = wx.ID_ANY, label = _("&Rename"))
959
 
            btnRenameCol.Bind(wx.EVT_BUTTON, self.OnTableItemChange)
960
 
            btnRenameCol.Enable(False)
961
 
            self.layerPage[layer]['renameColButton'] = btnRenameCol.GetId()
962
 
            renameSizer.Add(item = btnRenameCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND,
963
 
                            border = 3)
964
 
            
965
 
            tableSizer.Add(item = tlist,
966
 
                           flag = wx.ALL | wx.EXPAND,
967
 
                           proportion = 1,
968
 
                           border = 3)
969
 
            
970
 
            pageSizer.Add(item=dbSizer,
971
 
                          flag = wx.ALL | wx.EXPAND,
972
 
                          proportion = 0,
973
 
                          border = 3)
974
 
            
975
 
            pageSizer.Add(item = tableSizer,
976
 
                          flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
977
 
                          proportion = 1,
978
 
                          border = 3)
979
 
            
980
 
            pageSizer.Add(item = addSizer,
981
 
                          flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
982
 
                          proportion = 0,
983
 
                          border = 3)
984
 
            pageSizer.Add(item = renameSizer,
985
 
                          flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
986
 
                          proportion = 0,
987
 
                          border = 3)
988
 
            
989
 
            panel.SetSizer(pageSizer)
990
 
        
991
 
        self.manageTablePage.SetSelection(0) # select first layer
992
 
        try:
993
 
            self.layer = self.mapDBInfo.layers.keys()[0]
994
 
        except IndexError:
995
 
            self.layer = None
996
 
        
997
 
    def _createTableDesc(self, parent, table):
998
 
        """!Create list with table description"""
999
 
        tlist = TableListCtrl(parent = parent, id = wx.ID_ANY,
1000
 
                             table = self.mapDBInfo.tables[table],
1001
 
                             columns = self.mapDBInfo.GetColumns(table))
1002
 
        tlist.Populate()
1003
 
        # sorter
1004
 
        # itemDataMap = list.Populate()
1005
 
        # listmix.ColumnSorterMixin.__init__(self, 2)
1006
 
 
1007
 
        return tlist
1008
 
 
1009
 
    def _createManageLayerPage(self):
1010
 
        """!Create manage page"""
1011
 
        splitterWin = wx.SplitterWindow(parent = self.manageLayerPage, id = wx.ID_ANY)
1012
 
        splitterWin.SetMinimumPaneSize(100)
1013
 
        
1014
 
        label = _("Layers of vector map")
1015
 
        if not self.editable:
1016
 
            label += _(" (readonly)")
1017
 
        self.manageLayerPage.AddPage(page = splitterWin,
1018
 
                                     text = label) # dummy page
1019
 
        
1020
 
        #
1021
 
        # list of layers
1022
 
        #
1023
 
        panelList = wx.Panel(parent = splitterWin, id = wx.ID_ANY)
1024
 
 
1025
 
        panelListSizer  = wx.BoxSizer(wx.VERTICAL)
1026
 
        layerBox = wx.StaticBox(parent = panelList, id = wx.ID_ANY,
1027
 
                                label = " %s " % _("List of layers"))
1028
 
        layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
1029
 
 
1030
 
        self.layerList = self._createLayerDesc(panelList)
1031
 
        self.layerList.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnLayerRightUp) #wxMSW
1032
 
        self.layerList.Bind(wx.EVT_RIGHT_UP,            self.OnLayerRightUp) #wxGTK
1033
 
        
1034
 
        layerSizer.Add(item = self.layerList,
1035
 
                       flag = wx.ALL | wx.EXPAND,
1036
 
                       proportion = 1,
1037
 
                       border = 3)
1038
 
 
1039
 
        panelListSizer.Add(item = layerSizer,
1040
 
                           flag = wx.ALL | wx.EXPAND,
1041
 
                           proportion = 1,
1042
 
                           border = 3)
1043
 
 
1044
 
        panelList.SetSizer(panelListSizer)
1045
 
 
1046
 
        #
1047
 
        # manage part
1048
 
        #
1049
 
        panelManage = wx.Panel(parent = splitterWin, id = wx.ID_ANY)
1050
 
         
1051
 
        manageSizer = wx.BoxSizer(wx.VERTICAL)
1052
 
 
1053
 
        self.manageLayerBook = LayerBook(parent = panelManage, id = wx.ID_ANY,
1054
 
                                         parentDialog = self)
1055
 
 
1056
 
        manageSizer.Add(item = self.manageLayerBook,
1057
 
                        proportion = 1,
1058
 
                        flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
1059
 
                        border = 5)
1060
 
 
1061
 
        panelManage.SetSizer(manageSizer)
1062
 
        splitterWin.SplitHorizontally(panelList, panelManage, 100) 
1063
 
        splitterWin.Fit()
1064
 
 
1065
 
    def _createLayerDesc(self, parent):
1066
 
        """!Create list of linked layers"""
1067
 
        tlist = LayerListCtrl(parent = parent, id = wx.ID_ANY,
1068
 
                             layers = self.mapDBInfo.layers)
1069
 
        
1070
 
        tlist.Populate()
1071
 
        # sorter
1072
 
        # itemDataMap = list.Populate()
1073
 
        # listmix.ColumnSorterMixin.__init__(self, 2)
1074
 
 
1075
 
        return tlist
1076
 
 
1077
 
    def _layout(self):
1078
 
        """!Do layout"""
1079
 
        # frame body
1080
 
        mainSizer = wx.BoxSizer(wx.VERTICAL)
1081
 
 
1082
 
        # buttons
1083
 
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1084
 
        btnSizer.Add(item = self.btnReload, proportion = 1,
1085
 
                     flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
1086
 
        btnSizer.Add(item = self.btnQuit, proportion = 1,
1087
 
                     flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
1088
 
 
1089
 
        mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND)
1090
 
        mainSizer.Add(item = btnSizer, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
1091
 
 
1092
 
        self.panel.SetAutoLayout(True)
1093
 
        self.panel.SetSizer(mainSizer)
1094
 
        mainSizer.Fit(self.panel)
1095
 
        self.Layout()
1096
 
        
1097
 
    def OnDataRightUp(self, event):
1098
 
        """!Table description area, context menu"""
1099
 
        if not hasattr(self, "popupDataID1"):
1100
 
            self.popupDataID1 = wx.NewId()
1101
 
            self.popupDataID2 = wx.NewId()
1102
 
            self.popupDataID3 = wx.NewId()
1103
 
            self.popupDataID4 = wx.NewId()
1104
 
            self.popupDataID5 = wx.NewId()
1105
 
            self.popupDataID6 = wx.NewId()
1106
 
            self.popupDataID7 = wx.NewId()
1107
 
            self.popupDataID8 = wx.NewId()
1108
 
            self.popupDataID9 = wx.NewId()
1109
 
            self.popupDataID10 = wx.NewId()
1110
 
            self.popupDataID11 = wx.NewId()
1111
 
 
1112
 
            self.Bind(wx.EVT_MENU, self.OnDataItemEdit,       id = self.popupDataID1)
1113
 
            self.Bind(wx.EVT_MENU, self.OnDataItemAdd,        id = self.popupDataID2)
1114
 
            self.Bind(wx.EVT_MENU, self.OnDataItemDelete,     id = self.popupDataID3)
1115
 
            self.Bind(wx.EVT_MENU, self.OnDataItemDeleteAll,  id = self.popupDataID4)
1116
 
            self.Bind(wx.EVT_MENU, self.OnDataSelectAll,      id = self.popupDataID5)
1117
 
            self.Bind(wx.EVT_MENU, self.OnDataSelectNone,     id = self.popupDataID6)
1118
 
            self.Bind(wx.EVT_MENU, self.OnDataDrawSelected,   id = self.popupDataID7)
1119
 
            self.Bind(wx.EVT_MENU, self.OnDataDrawSelectedZoom, id = self.popupDataID8)
1120
 
            self.Bind(wx.EVT_MENU, self.OnExtractSelected,    id = self.popupDataID9)
1121
 
            self.Bind(wx.EVT_MENU, self.OnDeleteSelected,     id = self.popupDataID11)
1122
 
            self.Bind(wx.EVT_MENU, self.OnDataReload,         id = self.popupDataID10)
1123
 
 
1124
 
        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1125
 
        # generate popup-menu
1126
 
        menu = wx.Menu()
1127
 
        menu.Append(self.popupDataID1, _("Edit selected record"))
1128
 
        selected = tlist.GetFirstSelected()
1129
 
        if not self.editable or selected == -1 or tlist.GetNextSelected(selected) != -1:
1130
 
            menu.Enable(self.popupDataID1, False)
1131
 
        menu.Append(self.popupDataID2, _("Insert new record"))
1132
 
        menu.Append(self.popupDataID3, _("Delete selected record(s)"))
1133
 
        menu.Append(self.popupDataID4, _("Delete all records"))
1134
 
        if not self.editable:
1135
 
            menu.Enable(self.popupDataID2, False)
1136
 
            menu.Enable(self.popupDataID3, False)
1137
 
            menu.Enable(self.popupDataID4, False)
1138
 
        menu.AppendSeparator()
1139
 
        menu.Append(self.popupDataID5, _("Select all"))
1140
 
        menu.Append(self.popupDataID6, _("Deselect all"))
1141
 
        menu.AppendSeparator()
1142
 
        menu.Append(self.popupDataID7, _("Highlight selected features"))
1143
 
        menu.Append(self.popupDataID8, _("Highlight selected features and zoom"))
1144
 
        if not self.map or len(tlist.GetSelectedItems()) == 0:
1145
 
            menu.Enable(self.popupDataID7, False)
1146
 
            menu.Enable(self.popupDataID8, False)
1147
 
        menu.Append(self.popupDataID9, _("Extract selected features"))
1148
 
        menu.Append(self.popupDataID11, _("Delete selected features"))
1149
 
        if not self.editable:
1150
 
            menu.Enable(self.popupDataID11, False)
1151
 
        if tlist.GetFirstSelected() == -1:
1152
 
            menu.Enable(self.popupDataID3, False)
1153
 
            menu.Enable(self.popupDataID9, False)
1154
 
            menu.Enable(self.popupDataID11, False)
1155
 
        menu.AppendSeparator()
1156
 
        menu.Append(self.popupDataID10, _("Reload"))
1157
 
 
1158
 
        self.PopupMenu(menu)
1159
 
        menu.Destroy()
1160
 
 
1161
 
        # update statusbar
1162
 
        self.log.write(_("Number of loaded records: %d") % \
1163
 
                           tlist.GetItemCount())
1164
 
 
1165
 
    def OnDataItemDelete(self, event):
1166
 
        """!Delete selected item(s) from the tlist (layer/category pair)"""
1167
 
        dlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1168
 
        item = dlist.GetFirstSelected()
1169
 
        
1170
 
        table    = self.mapDBInfo.layers[self.layer]["table"]
1171
 
        key      = self.mapDBInfo.layers[self.layer]["key"]
1172
 
        
1173
 
        indeces = []
1174
 
        # collect SQL statements
1175
 
        while item != -1:
1176
 
            index = dlist.itemIndexMap[item]
1177
 
            indeces.append(index)
1178
 
            
1179
 
            cat = dlist.itemCatsMap[index]
1180
 
            
1181
 
            self.listOfSQLStatements.append('DELETE FROM %s WHERE %s=%d' % \
1182
 
                                                (table, key, cat))
1183
 
            
1184
 
            item = dlist.GetNextSelected(item)
1185
 
        
1186
 
        if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
1187
 
            deleteDialog = wx.MessageBox(parent = self,
1188
 
                                         message = _("Selected data records (%d) will be permanently deleted "
1189
 
                                                   "from table. Do you want to delete them?") % \
1190
 
                                             (len(self.listOfSQLStatements)),
1191
 
                                         caption = _("Delete records"),
1192
 
                                         style = wx.YES_NO | wx.CENTRE)
1193
 
            if deleteDialog != wx.YES:
1194
 
                self.listOfSQLStatements = []
1195
 
                return False
1196
 
        
1197
 
        # restore maps
1198
 
        i = 0
1199
 
        indexTemp = copy.copy(dlist.itemIndexMap)
1200
 
        dlist.itemIndexMap = []
1201
 
        dataTemp = copy.deepcopy(dlist.itemDataMap)
1202
 
        dlist.itemDataMap = {}
1203
 
        catsTemp = copy.deepcopy(dlist.itemCatsMap)
1204
 
        dlist.itemCatsMap = {}
1205
 
        
1206
 
        i = 0
1207
 
        for index in indexTemp:
1208
 
            if index in indeces:
1209
 
                continue
1210
 
            dlist.itemIndexMap.append(i)
1211
 
            dlist.itemDataMap[i] = dataTemp[index]
1212
 
            dlist.itemCatsMap[i] = catsTemp[index]
1213
 
            
1214
 
            i += 1
1215
 
            
1216
 
        dlist.SetItemCount(len(dlist.itemIndexMap))
1217
 
        
1218
 
        # deselect items
1219
 
        item = dlist.GetFirstSelected()
1220
 
        while item != -1:
1221
 
            dlist.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
1222
 
            item = dlist.GetNextSelected(item)
1223
 
        
1224
 
        # submit SQL statements
1225
 
        self.ApplyCommands()
1226
 
        
1227
 
        return True
1228
 
 
1229
 
    def OnDataItemDeleteAll(self, event):
1230
 
        """!Delete all items from the list"""
1231
 
        dlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1232
 
        if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
1233
 
            deleteDialog = wx.MessageBox(parent = self,
1234
 
                                         message = _("All data records (%d) will be permanently deleted "
1235
 
                                                   "from table. Do you want to delete them?") % \
1236
 
                                             (len(dlist.itemIndexMap)),
1237
 
                                         caption = _("Delete records"),
1238
 
                                         style = wx.YES_NO | wx.CENTRE)
1239
 
            if deleteDialog != wx.YES:
1240
 
                return
1241
 
 
1242
 
        dlist.DeleteAllItems()
1243
 
        dlist.itemDataMap  = {}
1244
 
        dlist.itemIndexMap = []
1245
 
        dlist.SetItemCount(0)
1246
 
 
1247
 
        table = self.mapDBInfo.layers[self.layer]["table"]
1248
 
        self.listOfSQLStatements.append('DELETE FROM %s' % table)
1249
 
 
1250
 
        self.ApplyCommands()
1251
 
        
1252
 
        event.Skip()
1253
 
 
1254
 
    def _drawSelected(self, zoom):
1255
 
        """!Highlight selected features"""
1256
 
        if not self.map or not self.mapdisplay:
1257
 
            return
1258
 
        
1259
 
        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1260
 
        cats = map(int, tlist.GetSelectedItems())
1261
 
 
1262
 
        digitToolbar = self.mapdisplay.GetToolbar('vdigit')
1263
 
        if digitToolbar and digitToolbar.GetLayer() and \
1264
 
                digitToolbar.GetLayer().GetName() == self.vectorName:
1265
 
            display = self.mapdisplay.GetMapWindow().GetDisplay()
1266
 
            display.SetSelected(cats, layer = self.layer)
1267
 
            if zoom:
1268
 
                n, s, w, e = display.GetRegionSelected()
1269
 
                self.mapdisplay.Map.GetRegion(n = n, s = s, w = w, e = e,
1270
 
                                              update = True)
1271
 
        else:
1272
 
            # add map layer with higlighted vector features
1273
 
            self.AddQueryMapLayer() # -> self.qlayer
1274
 
 
1275
 
            # set opacity based on queried layer
1276
 
            if self.parent and self.parent.GetName() == "LayerManager" and \
1277
 
                    self.treeItem:
1278
 
                maptree = self.parent.curr_page.maptree
1279
 
                opacity = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetOpacity(float = True)
1280
 
                self.qlayer.SetOpacity(opacity)
1281
 
            if zoom:
1282
 
                keyColumn = self.mapDBInfo.layers[self.layer]['key']
1283
 
                where = ''
1284
 
                for range in ListOfCatsToRange(cats).split(','):
1285
 
                    if '-' in range:
1286
 
                        min, max = range.split('-')
1287
 
                        where += '%s >= %d and %s <= %d or ' % \
1288
 
                            (keyColumn, int(min),
1289
 
                             keyColumn, int(max))
1290
 
                    else:
1291
 
                        where += '%s = %d or ' % (keyColumn, int(range))
1292
 
                where = where.rstrip('or ')
1293
 
                
1294
 
                select = RunCommand('v.db.select',
1295
 
                                    parent = self,
1296
 
                                    read = True,
1297
 
                                    quiet = True,
1298
 
                                    flags = 'r',
1299
 
                                    map = self.mapDBInfo.map,
1300
 
                                    layer = int(self.layer),
1301
 
                                    where = where)
1302
 
                
1303
 
                region = {}
1304
 
                for line in select.splitlines():
1305
 
                    key, value = line.split('=')
1306
 
                    region[key.strip()] = float(value.strip())
1307
 
 
1308
 
                nsdist = ewdist = 0
1309
 
                renderer = self.mapdisplay.GetMap()
1310
 
                nsdist = 10 * ((renderer.GetCurrentRegion()['n'] - renderer.GetCurrentRegion()['s']) /
1311
 
                        renderer.height)
1312
 
                ewdist = 10 * ((renderer.GetCurrentRegion()['e'] - renderer.GetCurrentRegion()['w']) /
1313
 
                        renderer.width)
1314
 
                north = region['n'] + nsdist
1315
 
                south = region['s'] - nsdist
1316
 
                west = region['w'] - ewdist
1317
 
                east = region['e'] + ewdist
1318
 
                renderer.GetRegion(n = north, s = south, w = west, e = east, update = True)
1319
 
                self.mapdisplay.GetMapWindow().ZoomHistory(n = north, s = south, w = west, e = east)
1320
 
        
1321
 
        if zoom:
1322
 
            self.mapdisplay.Map.AdjustRegion()           # adjust resolution
1323
 
            self.mapdisplay.Map.AlignExtentFromDisplay() # adjust extent
1324
 
            self.mapdisplay.MapWindow.UpdateMap(render = True,  renderVector = True)
1325
 
        else:
1326
 
            self.mapdisplay.MapWindow.UpdateMap(render = False, renderVector = True)
1327
 
        
1328
 
    def OnDataDrawSelected(self, event):
1329
 
        """!Reload table description"""
1330
 
        self._drawSelected(zoom = False)
1331
 
        event.Skip()
1332
 
 
1333
 
    def OnDataDrawSelectedZoom(self, event):
1334
 
        self._drawSelected(zoom = True)
1335
 
        event.Skip()
1336
 
        
1337
 
    def OnDataItemAdd(self, event):
1338
 
        """!Add new record to the attribute table"""
1339
 
        tlist      = self.FindWindowById(self.layerPage[self.layer]['data'])
1340
 
        table     = self.mapDBInfo.layers[self.layer]['table']
1341
 
        keyColumn = self.mapDBInfo.layers[self.layer]['key']
1342
 
        
1343
 
        # (column name, value)
1344
 
        data = []
1345
 
 
1346
 
        # collect names of all visible columns
1347
 
        columnName = []
1348
 
        for i in range(tlist.GetColumnCount()): 
1349
 
            columnName.append(tlist.GetColumn(i).GetText())
1350
 
 
1351
 
        # maximal category number
1352
 
        if len(tlist.itemCatsMap.values()) > 0:
1353
 
            maxCat = max(tlist.itemCatsMap.values())
1354
 
        else:
1355
 
            maxCat = 0 # starting category '1'
1356
 
        
1357
 
        # key column must be always presented
1358
 
        if keyColumn not in columnName:
1359
 
            columnName.insert(0, keyColumn) # insert key column on first position
1360
 
            data.append((keyColumn, str(maxCat + 1)))
1361
 
            missingKey = True
1362
 
        else:
1363
 
            missingKey = False
1364
 
            
1365
 
        # add other visible columns
1366
 
        colIdx = 0
1367
 
        keyId = -1
1368
 
        for col in columnName:
1369
 
            ctype = self.mapDBInfo.tables[table][col]['ctype']
1370
 
            ctypeStr = self.mapDBInfo.tables[table][col]['type']
1371
 
            if col == keyColumn: # key 
1372
 
                if missingKey is False: 
1373
 
                    data.append((col, ctype, ctypeStr, str(maxCat + 1)))
1374
 
                    keyId = colIdx
1375
 
            else:
1376
 
                data.append((col, ctype, ctypeStr, ''))
1377
 
            
1378
 
            colIdx += 1
1379
 
                
1380
 
        dlg = ModifyTableRecord(parent = self,
1381
 
                                title = _("Insert new record"),
1382
 
                                data = data, keyEditable = (keyId, True))
1383
 
 
1384
 
        if dlg.ShowModal() == wx.ID_OK:
1385
 
            try: # get category number
1386
 
                cat = int(dlg.GetValues(columns = [keyColumn])[0])
1387
 
            except:
1388
 
                cat = -1
1389
 
 
1390
 
            try:
1391
 
                if cat in tlist.itemCatsMap.values():
1392
 
                    raise ValueError(_("Record with category number %d "
1393
 
                                       "already exists in the table.") % cat)
1394
 
 
1395
 
                values = dlg.GetValues() # values (need to be casted)
1396
 
                columnsString = ''
1397
 
                valuesString   = ''
1398
 
                
1399
 
                for i in range(len(values)):
1400
 
                    if len(values[i]) == 0: # NULL
1401
 
                        if columnName[i] == keyColumn:
1402
 
                            raise ValueError(_("Category number (column %s)"
1403
 
                                               " is missing.") % keyColumn)
1404
 
                        else:
1405
 
                            continue
1406
 
 
1407
 
                    try:
1408
 
                        if tlist.columns[columnName[i]]['ctype'] == int:
1409
 
                            # values[i] is stored as text. 
1410
 
                            value = float(values[i])
1411
 
                        else:
1412
 
                            value = values[i]
1413
 
                        values[i] = tlist.columns[columnName[i]]['ctype'] (value)
1414
 
 
1415
 
                    except:
1416
 
                        raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % 
1417
 
                                         {'value' : str(values[i]),
1418
 
                                          'type' : tlist.columns[columnName[i]]['type']})
1419
 
                    columnsString += '%s,' % columnName[i]
1420
 
                    if tlist.columns[columnName[i]]['ctype'] == str:
1421
 
                        valuesString += "'%s'," % values[i]
1422
 
                    else:
1423
 
                        valuesString += "%s," % values[i]
1424
 
 
1425
 
            except ValueError, err:
1426
 
                GError(parent = self,
1427
 
                       message = _("Unable to insert new record.\n%s") % err,
1428
 
                       showTraceback = False)
1429
 
                self.OnDataItemAdd(event)
1430
 
                return
1431
 
            
1432
 
            # remove category if need 
1433
 
            if missingKey is True:
1434
 
                del values[0]
1435
 
                
1436
 
            # add new item to the tlist
1437
 
            if len(tlist.itemIndexMap) > 0:
1438
 
                index = max(tlist.itemIndexMap) + 1
1439
 
            else:
1440
 
                index = 0
1441
 
            
1442
 
            tlist.itemIndexMap.append(index)
1443
 
            tlist.itemDataMap[index] = values
1444
 
            tlist.itemCatsMap[index] = cat
1445
 
            tlist.SetItemCount(tlist.GetItemCount() + 1)
1446
 
 
1447
 
            self.listOfSQLStatements.append('INSERT INTO %s (%s) VALUES(%s)' % \
1448
 
                                                (table,
1449
 
                                                 columnsString.strip(','),
1450
 
                                                 valuesString.strip(',')))
1451
 
            self.ApplyCommands()
1452
 
            
1453
 
    def OnDataItemEdit(self, event):
1454
 
        """!Edit selected record of the attribute table"""
1455
 
        tlist      = self.FindWindowById(self.layerPage[self.layer]['data'])
1456
 
        item      = tlist.GetFirstSelected()
1457
 
        if item == -1:
1458
 
            return
1459
 
 
1460
 
        table     = self.mapDBInfo.layers[self.layer]['table']
1461
 
        keyColumn = self.mapDBInfo.layers[self.layer]['key']
1462
 
        cat       = tlist.itemCatsMap[tlist.itemIndexMap[item]]
1463
 
 
1464
 
        # (column name, value)
1465
 
        data = []
1466
 
 
1467
 
        # collect names of all visible columns
1468
 
        columnName = []
1469
 
        for i in range(tlist.GetColumnCount()): 
1470
 
            columnName.append(tlist.GetColumn(i).GetText())
1471
 
 
1472
 
 
1473
 
        # key column must be always presented
1474
 
        if keyColumn not in columnName:
1475
 
            columnName.insert(0, keyColumn) # insert key column on first position
1476
 
            data.append((keyColumn, str(cat)))
1477
 
            keyId = 0
1478
 
            missingKey = True
1479
 
        else:
1480
 
            missingKey = False
1481
 
            
1482
 
        # add other visible columns
1483
 
        for i in range(len(columnName)):
1484
 
            ctype = self.mapDBInfo.tables[table][columnName[i]]['ctype']
1485
 
            ctypeStr = self.mapDBInfo.tables[table][columnName[i]]['type']
1486
 
            if columnName[i] == keyColumn: # key 
1487
 
                if missingKey is False: 
1488
 
                    data.append((columnName[i], ctype, ctypeStr, str(cat)))
1489
 
                    keyId = i
1490
 
            else:
1491
 
                if missingKey is True:
1492
 
                    value = tlist.GetItem(item, i-1).GetText()
1493
 
                else:
1494
 
                    value = tlist.GetItem(item, i).GetText()
1495
 
                data.append((columnName[i], ctype, ctypeStr, value))
1496
 
 
1497
 
        dlg = ModifyTableRecord(parent = self, 
1498
 
                                title = _("Update existing record"),
1499
 
                                data = data, keyEditable = (keyId, False))
1500
 
 
1501
 
        if dlg.ShowModal() == wx.ID_OK:
1502
 
            values = dlg.GetValues() # string
1503
 
            updateString = ''
1504
 
            try:
1505
 
                for i in range(len(values)): 
1506
 
                    if i == keyId: # skip key column
1507
 
                        continue
1508
 
                    if tlist.GetItem(item, i).GetText() != values[i]:
1509
 
                        if len(values[i]) > 0:
1510
 
                            try:
1511
 
                                if missingKey is True:
1512
 
                                    idx = i - 1
1513
 
                                else:
1514
 
                                    idx = i
1515
 
                                if tlist.columns[columnName[i]]['ctype'] != types.StringType:
1516
 
                                    if tlist.columns[columnName[i]]['ctype'] == int:
1517
 
                                        value = float(values[i])
1518
 
                                    else:
1519
 
                                        value = values[i]
1520
 
                                    tlist.itemDataMap[item][idx] = \
1521
 
                                        tlist.columns[columnName[i]]['ctype'] (value)
1522
 
                                else:
1523
 
                                    tlist.itemDataMap[item][idx] = values[i]
1524
 
                            except:
1525
 
                                raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % \
1526
 
                                                     {'value' : str(values[i]),
1527
 
                                                      'type' : tlist.columns[columnName[i]]['type']})
1528
 
 
1529
 
                            if tlist.columns[columnName[i]]['ctype'] == str:
1530
 
                                updateString += "%s='%s'," % (columnName[i], values[i])
1531
 
                            else:
1532
 
                                updateString += "%s=%s," % (columnName[i], values[i])
1533
 
                        else: # NULL
1534
 
                            updateString += "%s=NULL," % (columnName[i])
1535
 
                            
1536
 
            except ValueError, err:
1537
 
                GError(parent = self,
1538
 
                       message = _("Unable to update existing record.\n%s") % err,
1539
 
                       showTraceback = False)
1540
 
                self.OnDataItemEdit(event)
1541
 
                return
1542
 
            
1543
 
            if len(updateString) > 0:
1544
 
                self.listOfSQLStatements.append('UPDATE %s SET %s WHERE %s=%d' % \
1545
 
                                                    (table, updateString.strip(','),
1546
 
                                                     keyColumn, cat))
1547
 
                self.ApplyCommands()
1548
 
 
1549
 
            tlist.Update(self.mapDBInfo)
1550
 
                        
1551
 
    def OnDataReload(self, event):
1552
 
        """!Reload tlist of records"""
1553
 
        self.OnApplySqlStatement(None)
1554
 
        self.listOfSQLStatements = []
1555
 
 
1556
 
    def OnDataSelectAll(self, event):
1557
 
        """!Select all items"""
1558
 
        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1559
 
        item = -1
1560
 
 
1561
 
        while True:
1562
 
            item = tlist.GetNextItem(item)
1563
 
            if item == -1:
1564
 
                break
1565
 
            tlist.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
1566
 
 
1567
 
        event.Skip()
1568
 
 
1569
 
    def OnDataSelectNone(self, event):
1570
 
        """!Deselect items"""
1571
 
        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1572
 
        item = -1
1573
 
 
1574
 
        while True:
1575
 
            item = tlist.GetNextItem(item, wx.LIST_STATE_SELECTED)
1576
 
            if item == -1:
1577
 
                break
1578
 
            tlist.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
1579
 
 
1580
 
        event.Skip()
1581
 
 
1582
 
 
1583
 
    def OnTableChangeType(self, event):
1584
 
        """!Data type for new column changed. Enable or disable
1585
 
        data length widget"""
1586
 
        win = self.FindWindowById(self.layerPage[self.layer]['addColLength'])
1587
 
        if event.GetString() == "varchar":
1588
 
            win.Enable(True)
1589
 
        else:
1590
 
            win.Enable(False)
1591
 
 
1592
 
    def OnTableRenameColumnName(self, event):
1593
 
        """!Editing column name to be added to the table"""
1594
 
        btn  = self.FindWindowById(self.layerPage[self.layer]['renameColButton'])
1595
 
        col  = self.FindWindowById(self.layerPage[self.layer]['renameCol'])
1596
 
        colTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo'])
1597
 
        if len(col.GetValue()) > 0 and len(colTo.GetValue()) > 0:
1598
 
            btn.Enable(True)
1599
 
        else:
1600
 
            btn.Enable(False)
1601
 
 
1602
 
        event.Skip()
1603
 
 
1604
 
    def OnTableAddColumnName(self, event):
1605
 
        """!Editing column name to be added to the table"""
1606
 
        btn = self.FindWindowById(self.layerPage[self.layer]['addColButton'])
1607
 
        if len(event.GetString()) > 0:
1608
 
            btn.Enable(True)
1609
 
        else:
1610
 
            btn.Enable(False)
1611
 
 
1612
 
        event.Skip()
1613
 
 
1614
 
    def OnTableItemChange(self, event):
1615
 
        """!Rename column in the table"""
1616
 
        tlist   = self.FindWindowById(self.layerPage[self.layer]['tableData'])
1617
 
        name   = self.FindWindowById(self.layerPage[self.layer]['renameCol']).GetValue()
1618
 
        nameTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo']).GetValue()
1619
 
 
1620
 
        table = self.mapDBInfo.layers[self.layer]["table"]
1621
 
 
1622
 
        if not name or not nameTo:
1623
 
            GError(parent = self,
1624
 
                   message = _("Unable to rename column. "
1625
 
                               "No column name defined."))
1626
 
            return
1627
 
        else:
1628
 
            item = tlist.FindItem(start = -1, str = name)
1629
 
            if item > -1:
1630
 
                if tlist.FindItem(start = -1, str = nameTo) > -1:
1631
 
                    GError(parent = self,
1632
 
                           message = _("Unable to rename column <%(column)s> to "
1633
 
                                       "<%(columnTo)s>. Column already exists "
1634
 
                                       "in the table <%(table)s>.") % \
1635
 
                               {'column' : name, 'columnTo' : nameTo,
1636
 
                                'table' : table})
1637
 
                    return
1638
 
                else:
1639
 
                    tlist.SetItemText(item, nameTo)
1640
 
 
1641
 
                    self.listOfCommands.append(('v.db.renamecol',
1642
 
                                                { 'map'    : self.vectorName,
1643
 
                                                  'layer'  : self.layer,
1644
 
                                                  'column' : '%s,%s' % (name, nameTo) }
1645
 
                                                ))
1646
 
            else:
1647
 
                GError(parent = self,
1648
 
                       message = _("Unable to rename column. "
1649
 
                                   "Column <%(column)s> doesn't exist in the table <%(table)s>.") % 
1650
 
                       {'column' : name, 'table' : table})
1651
 
                return
1652
 
            
1653
 
        # apply changes
1654
 
        self.ApplyCommands()
1655
 
 
1656
 
        # update widgets
1657
 
        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
1658
 
        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
1659
 
        self.FindWindowById(self.layerPage[self.layer]['renameColTo']).SetValue('')
1660
 
 
1661
 
        event.Skip()
1662
 
 
1663
 
    def OnTableRightUp(self, event):
1664
 
        """!Table description area, context menu"""
1665
 
        if not hasattr(self, "popupTableID"):
1666
 
            self.popupTableID1 = wx.NewId()
1667
 
            self.popupTableID2 = wx.NewId()
1668
 
            self.popupTableID3 = wx.NewId()
1669
 
            self.Bind(wx.EVT_MENU, self.OnTableItemDelete,    id = self.popupTableID1)
1670
 
            self.Bind(wx.EVT_MENU, self.OnTableItemDeleteAll, id = self.popupTableID2)
1671
 
            self.Bind(wx.EVT_MENU, self.OnTableReload,        id = self.popupTableID3)
1672
 
 
1673
 
        # generate popup-menu
1674
 
        menu = wx.Menu()
1675
 
        menu.Append(self.popupTableID1, _("Drop selected column"))
1676
 
        if self.FindWindowById(self.layerPage[self.layer]['tableData']).GetFirstSelected() == -1:
1677
 
            menu.Enable(self.popupTableID1, False)
1678
 
        menu.Append(self.popupTableID2, _("Drop all columns"))
1679
 
        menu.AppendSeparator()
1680
 
        menu.Append(self.popupTableID3, _("Reload"))
1681
 
 
1682
 
        self.PopupMenu(menu)
1683
 
        menu.Destroy()
1684
 
 
1685
 
    def OnTableItemDelete(self, event):
1686
 
        """!Delete selected item(s) from the list"""
1687
 
        tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
1688
 
        
1689
 
        item = tlist.GetFirstSelected()
1690
 
        
1691
 
        if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
1692
 
            deleteDialog = wx.MessageBox(parent = self,
1693
 
                                         message = _("Selected column '%s' will PERMANENTLY removed "
1694
 
                                                   "from table. Do you want to drop the column?") % \
1695
 
                                             (tlist.GetItemText(item)),
1696
 
                                         caption = _("Drop column(s)"),
1697
 
                                         style = wx.YES_NO | wx.CENTRE)
1698
 
            if deleteDialog != wx.YES:
1699
 
                return False
1700
 
        
1701
 
        while item != -1:
1702
 
            self.listOfCommands.append(('v.db.dropcol',
1703
 
                                        { 'map' : self.vectorName,
1704
 
                                          'layer' : self.layer,
1705
 
                                          'column' : tlist.GetItemText(item) }
1706
 
                                        ))
1707
 
            tlist.DeleteItem(item)
1708
 
            item = tlist.GetFirstSelected()
1709
 
        
1710
 
        # apply changes
1711
 
        self.ApplyCommands()
1712
 
        
1713
 
        # update widgets
1714
 
        table = self.mapDBInfo.layers[self.layer]['table']
1715
 
        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
1716
 
        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
1717
 
        
1718
 
        event.Skip()
1719
 
 
1720
 
    def OnTableItemDeleteAll(self, event):
1721
 
        """!Delete all items from the list"""
1722
 
        table     = self.mapDBInfo.layers[self.layer]['table']
1723
 
        cols      = self.mapDBInfo.GetColumns(table)
1724
 
        keyColumn = self.mapDBInfo.layers[self.layer]['key']
1725
 
        if keyColumn in cols:
1726
 
            cols.remove(keyColumn)
1727
 
        
1728
 
        if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
1729
 
            deleteDialog = wx.MessageBox(parent = self,
1730
 
                                         message = _("Selected columns\n%s\nwill PERMANENTLY removed "
1731
 
                                                   "from table. Do you want to drop the columns?") % \
1732
 
                                             ('\n'.join(cols)),
1733
 
                                         caption = _("Drop column(s)"),
1734
 
                                         style = wx.YES_NO | wx.CENTRE)
1735
 
            if deleteDialog != wx.YES:
1736
 
                return False
1737
 
        
1738
 
        for col in cols:
1739
 
            self.listOfCommands.append(('v.db.dropcol',
1740
 
                                        { 'map' : self.vectorName,
1741
 
                                          'layer' : self.layer,
1742
 
                                          'column' : col }
1743
 
                                        ))
1744
 
        self.FindWindowById(self.layerPage[self.layer]['tableData']).DeleteAllItems()
1745
 
 
1746
 
        # apply changes
1747
 
        self.ApplyCommands()
1748
 
 
1749
 
        # update widgets
1750
 
        table = self.mapDBInfo.layers[self.layer]['table']
1751
 
        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
1752
 
        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
1753
 
 
1754
 
        event.Skip()
1755
 
 
1756
 
    def OnTableReload(self, event = None):
1757
 
        """!Reload table description"""
1758
 
        self.FindWindowById(self.layerPage[self.layer]['tableData']).Populate(update = True)
1759
 
        self.listOfCommands = []
1760
 
 
1761
 
    def OnTableItemAdd(self, event):
1762
 
        """!Add new column to the table"""
1763
 
        table = self.mapDBInfo.layers[self.layer]['table']
1764
 
        name = self.FindWindowById(self.layerPage[self.layer]['addColName']).GetValue()
1765
 
        
1766
 
        if not name:
1767
 
            GError(parent = self,
1768
 
                   message = _("Unable to add column to the table. "
1769
 
                               "No column name defined."))
1770
 
            return
1771
 
        
1772
 
        ctype = self.FindWindowById(self.layerPage[self.layer]['addColType']). \
1773
 
            GetStringSelection()
1774
 
        
1775
 
        # cast type if needed
1776
 
        if ctype == 'double':
1777
 
            ctype = 'double precision'
1778
 
        if ctype == 'varchar':
1779
 
            length = int(self.FindWindowById(self.layerPage[self.layer]['addColLength']). \
1780
 
                             GetValue())
1781
 
        else:
1782
 
            length = '' # FIXME
1783
 
        
1784
 
        # add item to the list of table columns
1785
 
        tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
1786
 
        # check for duplicate items
1787
 
        if tlist.FindItem(start = -1, str = name) > -1:
1788
 
            GError(parent = self,
1789
 
                   message = _("Column <%(column)s> already exists in table <%(table)s>.") % \
1790
 
                       {'column' : name, 'table' : self.mapDBInfo.layers[self.layer]["table"]})
1791
 
            return
1792
 
        index = tlist.InsertStringItem(sys.maxint, str(name))
1793
 
        tlist.SetStringItem(index, 0, str(name))
1794
 
        tlist.SetStringItem(index, 1, str(ctype))
1795
 
        tlist.SetStringItem(index, 2, str(length))
1796
 
        
1797
 
        # add v.db.addcol command to the list
1798
 
        if ctype == 'varchar':
1799
 
            ctype += ' (%d)' % length
1800
 
        self.listOfCommands.append(('v.db.addcol',
1801
 
                                    { 'map'     : self.vectorName,
1802
 
                                      'layer'   : self.layer,
1803
 
                                      'columns' : '%s %s' % (name, ctype) }
1804
 
                                    ))
1805
 
        # apply changes
1806
 
        self.ApplyCommands()
1807
 
        
1808
 
        # update widgets
1809
 
        self.FindWindowById(self.layerPage[self.layer]['addColName']).SetValue('')
1810
 
        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
1811
 
        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
1812
 
        
1813
 
        event.Skip()
1814
 
        
1815
 
    def OnLayerPageChanged(self, event):
1816
 
        """!Layer tab changed"""
1817
 
        pageNum = event.GetSelection()
1818
 
        self.layer = self.mapDBInfo.layers.keys()[pageNum]
1819
 
        
1820
 
        try:
1821
 
            idCol = self.layerPage[self.layer]['whereColumn']
1822
 
        except KeyError:
1823
 
            idCol = None
1824
 
        
1825
 
        try:
1826
 
            self.OnChangeSql(None)
1827
 
            # update statusbar
1828
 
            self.log.write(_("Number of loaded records: %d") % \
1829
 
                               self.FindWindowById(self.layerPage[self.layer]['data']).\
1830
 
                               GetItemCount())
1831
 
        except:
1832
 
            pass
1833
 
        
1834
 
        if idCol:
1835
 
            winCol = self.FindWindowById(idCol)
1836
 
            table = self.mapDBInfo.layers[self.layer]["table"]
1837
 
            self.mapDBInfo.GetColumns(table)
1838
 
        
1839
 
        event.Skip()
1840
 
        
1841
 
    def OnPageChanged(self, event):
1842
 
        try:
1843
 
            id = self.layerPage[self.layer]['data']
1844
 
        except KeyError:
1845
 
            id = None
1846
 
        
1847
 
        if event.GetSelection() == 0 and id:
1848
 
            win = self.FindWindowById(id)
1849
 
            if win:
1850
 
                self.log.write(_("Number of loaded records: %d") % win.GetItemCount())
1851
 
            else:
1852
 
                self.log.write("")
1853
 
            self.btnReload.Enable()
1854
 
        else:
1855
 
            self.log.write("")
1856
 
            self.btnReload.Enable(False)
1857
 
        
1858
 
        event.Skip()
1859
 
        
1860
 
    def OnLayerRightUp(self, event):
1861
 
        """!Layer description area, context menu"""
1862
 
        pass
1863
 
 
1864
 
    def OnChangeSql(self, event):
1865
 
        """!Switch simple/advanced sql statement"""
1866
 
        if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue():
1867
 
            self.FindWindowById(self.layerPage[self.layer]['where']).Enable(True)
1868
 
            self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(False)
1869
 
            self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(False)
1870
 
        else:
1871
 
            self.FindWindowById(self.layerPage[self.layer]['where']).Enable(False)
1872
 
            self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(True)
1873
 
            self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(True)
1874
 
 
1875
 
    def ApplyCommands(self):
1876
 
        """!Apply changes"""
1877
 
        # perform GRASS commands (e.g. v.db.addcol)
1878
 
        wx.BeginBusyCursor()
1879
 
        
1880
 
        if len(self.listOfCommands) > 0:
1881
 
            for cmd in self.listOfCommands:
1882
 
                RunCommand(prog = cmd[0],
1883
 
                           quiet = True,
1884
 
                           parent = self,
1885
 
                           **cmd[1])
1886
 
            
1887
 
            self.mapDBInfo = VectorDBInfo(self.vectorName)
1888
 
            table = self.mapDBInfo.layers[self.layer]['table']
1889
 
 
1890
 
            # update table description
1891
 
            tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
1892
 
            tlist.Update(table = self.mapDBInfo.tables[table],
1893
 
                        columns = self.mapDBInfo.GetColumns(table))
1894
 
            self.OnTableReload(None)
1895
 
 
1896
 
            # update data tlist
1897
 
            tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
1898
 
            tlist.Update(self.mapDBInfo)
1899
 
 
1900
 
            # reset list of commands
1901
 
            self.listOfCommands = []
1902
 
        
1903
 
        # perform SQL non-select statements (e.g. 'delete from table where cat=1')
1904
 
        if len(self.listOfSQLStatements) > 0:
1905
 
            fd, sqlFilePath = tempfile.mkstemp(text=True)
1906
 
            sqlFile = open(sqlFilePath, 'w')
1907
 
            for sql in self.listOfSQLStatements:
1908
 
                enc = UserSettings.Get(group = 'atm', key = 'encoding', subkey = 'value')
1909
 
                if not enc and 'GRASS_DB_ENCODING' in os.environ:
1910
 
                    enc = os.environ['GRASS_DB_ENCODING']
1911
 
                if enc:
1912
 
                    sqlFile.write(sql.encode(enc) + ';')
1913
 
                else:
1914
 
                    sqlFile.write(sql + ';')
1915
 
                sqlFile.write(os.linesep)
1916
 
                sqlFile.close()
1917
 
 
1918
 
            driver   = self.mapDBInfo.layers[self.layer]["driver"]
1919
 
            database = self.mapDBInfo.layers[self.layer]["database"]
1920
 
            
1921
 
            Debug.msg(3, 'AttributeManger.ApplyCommands(): %s' %
1922
 
                      ';'.join(["%s" % s for s in self.listOfSQLStatements]))
1923
 
            
1924
 
            RunCommand('db.execute',
1925
 
                       parent = self,
1926
 
                       input = sqlFilePath,
1927
 
                       driver = driver,
1928
 
                       database = database)
1929
 
            
1930
 
            os.close(fd)
1931
 
            os.remove(sqlFilePath)
1932
 
            # reset list of statements
1933
 
            self.listOfSQLStatements = []
1934
 
            
1935
 
        wx.EndBusyCursor()
1936
 
        
1937
 
    def OnApplySqlStatement(self, event):
1938
 
        """!Apply simple/advanced sql statement"""
1939
 
        keyColumn = -1 # index of key column
1940
 
        listWin = self.FindWindowById(self.layerPage[self.layer]['data'])
1941
 
        sql = None
1942
 
        win = self.FindWindowById(self.layerPage[self.layer]['simple'])
1943
 
        if not win:
1944
 
            return
1945
 
        
1946
 
        wx.BeginBusyCursor()
1947
 
        if win.GetValue():
1948
 
            # simple sql statement
1949
 
            whereCol = self.FindWindowById(self.layerPage[self.layer]['whereColumn']).GetStringSelection()
1950
 
            whereOpe = self.FindWindowById(self.layerPage[self.layer]['whereOperator']).GetStringSelection()
1951
 
            whereVal = self.FindWindowById(self.layerPage[self.layer]['where']).GetValue().strip()
1952
 
            try:
1953
 
                if len(whereVal) > 0:
1954
 
                    keyColumn = listWin.LoadData(self.layer, where = whereCol + whereOpe + whereVal)
1955
 
                else:
1956
 
                    keyColumn = listWin.LoadData(self.layer)
1957
 
            except GException, e:
1958
 
                GError(parent = self,
1959
 
                       message = _("Loading attribute data failed.\n\n%s") % e.value)
1960
 
                self.FindWindowById(self.layerPage[self.layer]['where']).SetValue('')
1961
 
        else:
1962
 
            # advanced sql statement
1963
 
            win = self.FindWindowById(self.layerPage[self.layer]['statement'])
1964
 
            try:
1965
 
                cols, where = self.ValidateSelectStatement(win.GetValue())
1966
 
                if cols is None and where is None:
1967
 
                    sql = win.GetValue()
1968
 
            except TypeError:
1969
 
                GError(parent = self,
1970
 
                       message = _("Loading attribute data failed.\n"
1971
 
                                   "Invalid SQL select statement.\n\n%s") % win.GetValue())
1972
 
                win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
1973
 
                cols = None
1974
 
                where = None
1975
 
            
1976
 
            if cols or where or sql:
1977
 
                try:
1978
 
                    keyColumn = listWin.LoadData(self.layer, columns = cols,
1979
 
                                                 where = where, sql = sql)
1980
 
                except GException, e:
1981
 
                    GError(parent = self,
1982
 
                           message = _("Loading attribute data failed.\n\n%s") % e.value)
1983
 
                    win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
1984
 
        
1985
 
        # sort by key column
1986
 
        if sql and 'order by' in sql.lower():
1987
 
            pass # don't order by key column
1988
 
        else:
1989
 
            if keyColumn > -1:
1990
 
                listWin.SortListItems(col = keyColumn, ascending = True)
1991
 
            else:
1992
 
                listWin.SortListItems(col = 0, ascending = True) 
1993
 
        
1994
 
        wx.EndBusyCursor()
1995
 
        
1996
 
        # update statusbar
1997
 
        self.log.write(_("Number of loaded records: %d") % \
1998
 
                           self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
1999
 
 
2000
 
    def ValidateSelectStatement(self, statement):
2001
 
        """!Validate SQL select statement
2002
 
 
2003
 
        @return (columns, where)
2004
 
        @return None on error
2005
 
        """
2006
 
        if statement[0:7].lower() != 'select ':
2007
 
            return None
2008
 
        
2009
 
        cols = ''
2010
 
        index = 7
2011
 
        for c in statement[index:]:
2012
 
            if c == ' ':
2013
 
                break
2014
 
            cols += c
2015
 
            index += 1
2016
 
        if cols == '*':
2017
 
            cols = None
2018
 
        else:
2019
 
            cols = cols.split(',')
2020
 
        
2021
 
        tablelen = len(self.mapDBInfo.layers[self.layer]['table'])
2022
 
        
2023
 
        if statement[index+1:index+6].lower() != 'from ' or \
2024
 
                statement[index+6:index+6+tablelen] != '%s' % \
2025
 
                (self.mapDBInfo.layers[self.layer]['table']):
2026
 
            return None
2027
 
        
2028
 
        if len(statement[index+7+tablelen:]) > 0:
2029
 
            index = statement.lower().find('where ')
2030
 
            if index > -1:
2031
 
                where = statement[index+6:]
2032
 
            else:
2033
 
                where = None
2034
 
        else:
2035
 
            where = None
2036
 
        
2037
 
        return (cols, where)
2038
 
    
2039
 
    def OnCloseWindow(self, event):
2040
 
        """!Cancel button pressed"""
2041
 
        if self.parent and self.parent.GetName() == 'LayerManager':
2042
 
            # deregister ATM
2043
 
            self.parent.dialogs['atm'].remove(self)
2044
 
                    
2045
 
        if not isinstance(event, wx.CloseEvent):
2046
 
            self.Destroy()
2047
 
        
2048
 
        event.Skip()
2049
 
 
2050
 
    def OnBuilder(self,event):
2051
 
        """!SQL Builder button pressed -> show the SQLBuilder dialog"""
2052
 
        if not self.builder:
2053
 
            self.builder = SQLFrame(parent = self, id = wx.ID_ANY,
2054
 
                                    title = _("SQL Builder"),
2055
 
                                    vectmap = self.vectorName,
2056
 
                                    evtHandler = self.OnBuilderEvt)
2057
 
            self.builder.Show()
2058
 
        else:
2059
 
            self.builder.Raise()
2060
 
        
2061
 
    def OnBuilderEvt(self, event):
2062
 
        if event == 'apply':
2063
 
            sqlstr = self.builder.GetSQLStatement()
2064
 
            self.FindWindowById(self.layerPage[self.layer]['statement']).SetValue(sqlstr)
2065
 
            # apply query
2066
 
            self.listOfSQLStatements.append(sqlstr)
2067
 
            self.OnApplySqlStatement(None)
2068
 
            # close builder on apply
2069
 
            if self.builder.CloseOnApply():
2070
 
                self.builder = None
2071
 
        elif event == 'close':
2072
 
            self.builder = None
2073
 
        
2074
 
    def OnTextEnter(self, event):
2075
 
        pass
2076
 
    
2077
 
    def OnDataItemActivated(self, event):
2078
 
        """!Item activated, highlight selected item"""
2079
 
        self.OnDataDrawSelected(event)
2080
 
 
2081
 
        event.Skip()
2082
 
 
2083
 
    def OnExtractSelected(self, event):
2084
 
        """!Extract vector objects selected in attribute browse window
2085
 
        to new vector map
2086
 
        """
2087
 
        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
2088
 
        # cats = tlist.selectedCats[:]
2089
 
        cats = tlist.GetSelectedItems()
2090
 
        if len(cats) == 0:
2091
 
            GMessage(parent = self,
2092
 
                     message = _('Nothing to extract.'))
2093
 
            return
2094
 
        else:
2095
 
            # dialog to get file name
2096
 
            dlg = CreateNewVector(parent = self, title = _('Extract selected features'),
2097
 
                                  log = self.cmdLog,
2098
 
                                  cmd = (('v.extract',
2099
 
                                          { 'input' : self.vectorName,
2100
 
                                            'list' : ListOfCatsToRange(cats) },
2101
 
                                          'output')),
2102
 
                                  disableTable = True)
2103
 
            if not dlg:
2104
 
                return
2105
 
            
2106
 
            name = dlg.GetName(full = True)
2107
 
            if name and dlg.IsChecked('add'):
2108
 
                # add layer to map layer tree
2109
 
                self.parent.curr_page.maptree.AddLayer(ltype = 'vector',
2110
 
                                                       lname = name,
2111
 
                                                       lcmd = ['d.vect', 'map=%s' % name])
2112
 
            dlg.Destroy()
2113
 
            
2114
 
    def OnDeleteSelected(self, event):
2115
 
        """!Delete vector objects selected in attribute browse window
2116
 
        (attribures and geometry)
2117
 
        """
2118
 
        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
2119
 
        cats = tlist.GetSelectedItems()
2120
 
        if len(cats) == 0:
2121
 
            GMessage(parent = self,
2122
 
                     message = _('Nothing to delete.'))
2123
 
        
2124
 
        display = None
2125
 
        if 'vdigit' in self.mapdisplay.toolbars:
2126
 
            digitToolbar = self.mapdisplay.toolbars['vdigit']
2127
 
            if digitToolbar and digitToolbar.GetLayer() and \
2128
 
                    digitToolbar.GetLayer().GetName() == self.vectorName:
2129
 
                display = self.mapdisplay.GetMapWindow().GetDisplay()
2130
 
                display.SetSelected(map(int, cats), layer = self.layer)
2131
 
                self.mapdisplay.MapWindow.UpdateMap(render = True, renderVector = True)
2132
 
        
2133
 
        if self.OnDataItemDelete(None):
2134
 
            if display:
2135
 
                self.mapdisplay.GetMapWindow().digit.DeleteSelectedLines()
2136
 
            else:
2137
 
                RunCommand('v.edit',
2138
 
                           parent = self,
2139
 
                           quiet = True,
2140
 
                           map = self.vectorName,
2141
 
                           tool = 'delete',
2142
 
                           cats = ListOfCatsToRange(cats))
2143
 
            
2144
 
            self.mapdisplay.MapWindow.UpdateMap(render = True, renderVector = True)
2145
 
        
2146
 
    def AddQueryMapLayer(self):
2147
 
        """!Redraw a map
2148
 
 
2149
 
        Return True if map has been redrawn, False if no map is given
2150
 
        """
2151
 
        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
2152
 
        cats = { 
2153
 
            self.layer : tlist.GetSelectedItems()
2154
 
            }
2155
 
        
2156
 
        if self.mapdisplay.Map.GetLayerIndex(self.qlayer) < 0:
2157
 
            self.qlayer = None
2158
 
            
2159
 
        if self.qlayer:
2160
 
            self.qlayer.SetCmd(self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats, addLayer = False))
2161
 
        else:
2162
 
            self.qlayer = self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats)
2163
 
 
2164
 
        return self.qlayer
2165
 
    
2166
 
    def UpdateDialog(self, layer):
2167
 
        """!Updates dialog layout for given layer"""
2168
 
        # delete page
2169
 
        if layer in self.mapDBInfo.layers.keys():
2170
 
            # delete page
2171
 
            # draging pages disallowed
2172
 
            # if self.browsePage.GetPageText(page).replace('Layer ', '').strip() == str(layer):
2173
 
            # self.browsePage.DeletePage(page)
2174
 
            # break
2175
 
            self.browsePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
2176
 
            self.manageTablePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
2177
 
            # set current page selection
2178
 
            self.notebook.SetSelectionByName('layers')
2179
 
            
2180
 
        # fetch fresh db info
2181
 
        self.mapDBInfo = VectorDBInfo(self.vectorName)    
2182
 
 
2183
 
        #
2184
 
        # add new page
2185
 
        #
2186
 
        if layer in self.mapDBInfo.layers.keys():
2187
 
            # 'browse data' page
2188
 
            self._createBrowsePage(layer)
2189
 
            # 'manage tables' page
2190
 
            self._createManageTablePage(layer)
2191
 
            # set current page selection
2192
 
            self.notebook.SetSelectionByName('layers')
2193
 
            
2194
 
        #
2195
 
        # 'manage layers' page
2196
 
        #
2197
 
        # update list of layers
2198
 
        self.layerList.Update(self.mapDBInfo.layers)
2199
 
        self.layerList.Populate(update = True)
2200
 
        # update selected widgets
2201
 
        listOfLayers = map(str, self.mapDBInfo.layers.keys())
2202
 
        ### delete layer page
2203
 
        self.manageLayerBook.deleteLayer.SetItems(listOfLayers)
2204
 
        if len(listOfLayers) > 0:
2205
 
            self.manageLayerBook.deleteLayer.SetStringSelection(listOfLayers[0])
2206
 
            tableName = self.mapDBInfo.layers[int(listOfLayers[0])]['table']
2207
 
            maxLayer = max(self.mapDBInfo.layers.keys())
2208
 
        else:
2209
 
            tableName = ''
2210
 
            maxLayer = 0
2211
 
        self.manageLayerBook.deleteTable.SetLabel( \
2212
 
            _('Drop also linked attribute table (%s)') % \
2213
 
                tableName)
2214
 
        ### add layer page
2215
 
        self.manageLayerBook.addLayerWidgets['layer'][1].SetValue(\
2216
 
            maxLayer+1)
2217
 
        ### modify layer
2218
 
        self.manageLayerBook.modifyLayerWidgets['layer'][1].SetItems(listOfLayers)
2219
 
        self.manageLayerBook.OnChangeLayer(event = None)
2220
 
 
2221
 
    def GetVectorName(self):
2222
 
        """!Get vector name"""
2223
 
        return self.vectorName
2224
 
    
2225
 
    def LoadData(self, layer, columns = None, where = None, sql = None):
2226
 
        """!Load data into list
2227
 
 
2228
 
        @param layer layer number
2229
 
        @param columns list of columns for output
2230
 
        @param where where statement
2231
 
        @param sql full sql statement
2232
 
 
2233
 
        @return id of key column 
2234
 
        @return -1 if key column is not displayed
2235
 
        """
2236
 
        listWin = self.FindWindowById(self.layerPage[layer]['data'])
2237
 
        return listWin.LoadData(layer, columns, where, sql)
2238
 
    
2239
 
class TableListCtrl(wx.ListCtrl,
2240
 
                    listmix.ListCtrlAutoWidthMixin):
2241
 
                    #                    listmix.TextEditMixin):
2242
 
    """!Table description list"""
2243
 
 
2244
 
    def __init__(self, parent, id, table, columns, pos = wx.DefaultPosition,
2245
 
                 size = wx.DefaultSize):
2246
 
 
2247
 
        self.parent  = parent
2248
 
        self.table   = table
2249
 
        self.columns = columns
2250
 
        wx.ListCtrl.__init__(self, parent, id, pos, size,
2251
 
                             style = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
2252
 
                             wx.BORDER_NONE)
2253
 
 
2254
 
        listmix.ListCtrlAutoWidthMixin.__init__(self)
2255
 
        # listmix.TextEditMixin.__init__(self)
2256
 
 
2257
 
    def Update(self, table, columns):
2258
 
        """!Update column description"""
2259
 
        self.table   = table
2260
 
        self.columns = columns
2261
 
 
2262
 
    def Populate(self, update = False):
2263
 
        """!Populate the list"""
2264
 
        itemData = {} # requested by sorter
2265
 
 
2266
 
        if not update:
2267
 
            headings = [_("Column name"), _("Data type"), _("Data length")]
2268
 
            i = 0
2269
 
            for h in headings:
2270
 
                self.InsertColumn(col = i, heading = h)
2271
 
                i += 1
2272
 
            self.SetColumnWidth(col = 0, width = 350)
2273
 
            self.SetColumnWidth(col = 1, width = 175)
2274
 
        else:
2275
 
            self.DeleteAllItems()
2276
 
 
2277
 
        i = 0
2278
 
        for column in self.columns:
2279
 
            index = self.InsertStringItem(sys.maxint, str(column))
2280
 
            self.SetStringItem(index, 0, str(column))
2281
 
            self.SetStringItem(index, 1, str(self.table[column]['type']))
2282
 
            self.SetStringItem(index, 2, str(self.table[column]['length']))
2283
 
            self.SetItemData(index, i)
2284
 
            itemData[i] = (str(column),
2285
 
                           str(self.table[column]['type']),
2286
 
                           int(self.table[column]['length']))
2287
 
            i = i + 1
2288
 
 
2289
 
        self.SendSizeEvent()
2290
 
        
2291
 
        return itemData
2292
 
 
2293
 
class LayerListCtrl(wx.ListCtrl,
2294
 
                    listmix.ListCtrlAutoWidthMixin):
2295
 
                    # listmix.ColumnSorterMixin):
2296
 
                    # listmix.TextEditMixin):
2297
 
    """!Layer description list"""
2298
 
 
2299
 
    def __init__(self, parent, id, layers,
2300
 
                 pos = wx.DefaultPosition,
2301
 
                 size = wx.DefaultSize):
2302
 
 
2303
 
        self.parent = parent
2304
 
        self.layers = layers
2305
 
        wx.ListCtrl.__init__(self, parent, id, pos, size,
2306
 
                             style = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
2307
 
                             wx.BORDER_NONE)
2308
 
 
2309
 
        listmix.ListCtrlAutoWidthMixin.__init__(self)
2310
 
        # listmix.TextEditMixin.__init__(self)
2311
 
 
2312
 
    def Update(self, layers):
2313
 
        """!Update description"""
2314
 
        self.layers = layers
2315
 
 
2316
 
    def Populate(self, update = False):
2317
 
        """!Populate the list"""
2318
 
        itemData = {} # requested by sorter
2319
 
 
2320
 
        if not update:
2321
 
            headings = [_("Layer"),  _("Driver"), _("Database"), _("Table"), _("Key")]
2322
 
            i = 0
2323
 
            for h in headings:
2324
 
                self.InsertColumn(col = i, heading = h)
2325
 
                i += 1
2326
 
        else:
2327
 
            self.DeleteAllItems()
2328
 
 
2329
 
        i = 0
2330
 
        for layer in self.layers.keys():
2331
 
            index = self.InsertStringItem(sys.maxint, str(layer))
2332
 
            self.SetStringItem(index, 0, str(layer))
2333
 
            database = str(self.layers[layer]['database'])
2334
 
            driver   = str(self.layers[layer]['driver'])
2335
 
            table    = str(self.layers[layer]['table'])
2336
 
            key      = str(self.layers[layer]['key'])
2337
 
            self.SetStringItem(index, 1, driver)
2338
 
            self.SetStringItem(index, 2, database)
2339
 
            self.SetStringItem(index, 3, table)
2340
 
            self.SetStringItem(index, 4, key)
2341
 
            self.SetItemData(index, i)
2342
 
            itemData[i] = (str(layer),
2343
 
                           driver,
2344
 
                           database,
2345
 
                           table,
2346
 
                           key)
2347
 
            i += 1
2348
 
 
2349
 
        for i in range(self.GetColumnCount()):
2350
 
            self.SetColumnWidth(col = i, width = wx.LIST_AUTOSIZE)
2351
 
            if self.GetColumnWidth(col = i) < 60:
2352
 
                self.SetColumnWidth(col = i, width = 60)
2353
 
 
2354
 
        self.SendSizeEvent()
2355
 
        
2356
 
        return itemData
2357
 
 
2358
 
class LayerBook(wx.Notebook):
2359
 
    """!Manage layers (add, delete, modify)"""
2360
 
    def __init__(self, parent, id,
2361
 
                 parentDialog,
2362
 
                 style = wx.BK_DEFAULT):
2363
 
        wx.Notebook.__init__(self, parent, id, style = style)
2364
 
 
2365
 
        self.parent       = parent
2366
 
        self.parentDialog = parentDialog
2367
 
        self.mapDBInfo    = self.parentDialog.mapDBInfo
2368
 
 
2369
 
        #
2370
 
        # drivers
2371
 
        #
2372
 
        drivers = RunCommand('db.drivers',
2373
 
                             quiet = True,
2374
 
                             read = True,
2375
 
                             flags = 'p')
2376
 
        
2377
 
        self.listOfDrivers = []
2378
 
        for drv in drivers.splitlines():
2379
 
            self.listOfDrivers.append(drv.strip())
2380
 
        
2381
 
        #
2382
 
        # get default values
2383
 
        #
2384
 
        self.defaultConnect = {}
2385
 
        connect = RunCommand('db.connect',
2386
 
                             flags = 'p',
2387
 
                             read = True,
2388
 
                             quiet = True)
2389
 
        
2390
 
        for line in connect.splitlines():
2391
 
            item, value = line.split(':', 1)
2392
 
            self.defaultConnect[item.strip()] = value.strip()
2393
 
        
2394
 
        if len(self.defaultConnect['driver']) == 0 or \
2395
 
               len(self.defaultConnect['database']) == 0:
2396
 
            GWarning(parent = self.parent,
2397
 
                     message = _("Unknown default DB connection. "
2398
 
                                 "Please define DB connection using db.connect module."))
2399
 
        
2400
 
        self.defaultTables = self._getTables(self.defaultConnect['driver'],
2401
 
                                             self.defaultConnect['database'])
2402
 
        try:
2403
 
            self.defaultColumns = self._getColumns(self.defaultConnect['driver'],
2404
 
                                                    self.defaultConnect['database'],
2405
 
                                                    self.defaultTables[0])
2406
 
        except IndexError:
2407
 
            self.defaultColumns = []
2408
 
 
2409
 
        self._createAddPage()
2410
 
        self._createDeletePage()
2411
 
        self._createModifyPage()
2412
 
 
2413
 
    def _createAddPage(self):
2414
 
        """!Add new layer"""
2415
 
        self.addPanel = wx.Panel(parent = self, id = wx.ID_ANY)
2416
 
        self.AddPage(page = self.addPanel, text = _("Add layer"))
2417
 
        
2418
 
        try:
2419
 
            maxLayer = max(self.mapDBInfo.layers.keys())
2420
 
        except ValueError:
2421
 
            maxLayer = 0
2422
 
 
2423
 
        # layer description
2424
 
        
2425
 
        layerBox = wx.StaticBox (parent = self.addPanel, id = wx.ID_ANY,
2426
 
                                 label = " %s " % (_("Layer description")))
2427
 
        layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
2428
 
        
2429
 
        #
2430
 
        # list of layer widgets (label, value)
2431
 
        #
2432
 
        self.addLayerWidgets = {'layer':
2433
 
                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2434
 
                                                   label = '%s:' % _("Layer")),
2435
 
                                     wx.SpinCtrl(parent = self.addPanel, id = wx.ID_ANY, size = (65, -1),
2436
 
                                                 initial = maxLayer+1,
2437
 
                                                 min = 1, max = 1e6)),
2438
 
                                'driver':
2439
 
                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2440
 
                                                   label = '%s:' % _("Driver")),
2441
 
                                     wx.Choice(parent = self.addPanel, id = wx.ID_ANY, size = (200, -1),
2442
 
                                               choices = self.listOfDrivers)),
2443
 
                                'database':
2444
 
                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2445
 
                                                   label = '%s:' % _("Database")),
2446
 
                                     wx.TextCtrl(parent = self.addPanel, id = wx.ID_ANY,
2447
 
                                                 value = '',
2448
 
                                                 style = wx.TE_PROCESS_ENTER)),
2449
 
                                'table':
2450
 
                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2451
 
                                                   label = '%s:' % _("Table")),
2452
 
                                     wx.Choice(parent = self.addPanel, id = wx.ID_ANY, size = (200, -1),
2453
 
                                               choices = self.defaultTables)),
2454
 
                                'key':
2455
 
                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2456
 
                                                   label = '%s:' % _("Key column")),
2457
 
                                     wx.Choice(parent = self.addPanel, id = wx.ID_ANY, size = (200, -1),
2458
 
                                               choices = self.defaultColumns)),
2459
 
                                'addCat':
2460
 
                                    (wx.CheckBox(parent = self.addPanel, id = wx.ID_ANY,
2461
 
                                                 label = _("Insert record for each category into table")),
2462
 
                                     None),
2463
 
                                }
2464
 
        
2465
 
        # set default values for widgets
2466
 
        self.addLayerWidgets['driver'][1].SetStringSelection(self.defaultConnect['driver'])
2467
 
        self.addLayerWidgets['database'][1].SetValue(self.defaultConnect['database'])
2468
 
        self.addLayerWidgets['table'][1].SetSelection(0)
2469
 
        self.addLayerWidgets['key'][1].SetSelection(0)
2470
 
        # events
2471
 
        self.addLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
2472
 
        self.addLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
2473
 
        self.addLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
2474
 
        
2475
 
        # tooltips
2476
 
        self.addLayerWidgets['addCat'][0].SetToolTipString(_("You need to add categories "
2477
 
                                                             "by v.category module."))
2478
 
 
2479
 
        # table description
2480
 
        tableBox = wx.StaticBox (parent = self.addPanel, id = wx.ID_ANY,
2481
 
                                 label = " %s " % (_("Table description")))
2482
 
        tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
2483
 
 
2484
 
        #
2485
 
        # list of table widgets
2486
 
        #
2487
 
        keyCol = UserSettings.Get(group = 'atm', key = 'keycolumn', subkey = 'value')
2488
 
        self.tableWidgets = {'table': (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2489
 
                                                     label = '%s:' % _("Table name")),
2490
 
                                       wx.TextCtrl(parent = self.addPanel, id = wx.ID_ANY,
2491
 
                                                   value = '',
2492
 
                                                   style = wx.TE_PROCESS_ENTER)),
2493
 
                             'key': (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
2494
 
                                                   label = '%s:' % _("Key column")),
2495
 
                                     wx.TextCtrl(parent = self.addPanel, id = wx.ID_ANY,
2496
 
                                                 value = keyCol,
2497
 
                                                 style = wx.TE_PROCESS_ENTER))}
2498
 
        # events
2499
 
        self.tableWidgets['table'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
2500
 
        self.tableWidgets['key'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
2501
 
        
2502
 
        btnTable   = wx.Button(self.addPanel, wx.ID_ANY, _("&Create table"),
2503
 
                             size = (125,-1))
2504
 
        btnTable.Bind(wx.EVT_BUTTON, self.OnCreateTable)
2505
 
        
2506
 
        btnLayer   = wx.Button(self.addPanel, wx.ID_ANY, _("&Add layer"),
2507
 
                             size = (125,-1))
2508
 
        btnLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer)
2509
 
        
2510
 
        btnDefault = wx.Button(self.addPanel, wx.ID_ANY, _("&Set default"),
2511
 
                               size = (125,-1))
2512
 
        btnDefault.Bind(wx.EVT_BUTTON, self.OnSetDefault)
2513
 
        
2514
 
        # do layout
2515
 
        
2516
 
        pageSizer = wx.BoxSizer(wx.HORIZONTAL)
2517
 
                
2518
 
        # data area
2519
 
        dataSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
2520
 
        row = 0
2521
 
        for key in ('layer', 'driver', 'database', 'table', 'key', 'addCat'):
2522
 
            label, value = self.addLayerWidgets[key]
2523
 
            if not value:
2524
 
                span = (1, 2)
2525
 
            else:
2526
 
                span = (1, 1)
2527
 
            dataSizer.Add(item = label,
2528
 
                          flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0),
2529
 
                          span = span)
2530
 
            
2531
 
            if not value:
2532
 
                row += 1
2533
 
                continue
2534
 
 
2535
 
            if key == 'layer':
2536
 
                style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT
2537
 
            else:
2538
 
                style = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND
2539
 
            
2540
 
            dataSizer.Add(item = value,
2541
 
                          flag = style, pos = (row, 1))
2542
 
            
2543
 
            row += 1
2544
 
        dataSizer.AddGrowableCol(1)
2545
 
        
2546
 
        layerSizer.Add(item = dataSizer,
2547
 
                       proportion = 1,
2548
 
                       flag = wx.ALL | wx.EXPAND,
2549
 
                       border = 5)
2550
 
        
2551
 
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2552
 
        btnSizer.Add(item = btnDefault,
2553
 
                     proportion = 0,
2554
 
                     flag = wx.ALL | wx.ALIGN_LEFT,
2555
 
                     border = 5)
2556
 
        
2557
 
        btnSizer.Add(item = (5, 5),
2558
 
                     proportion = 1,
2559
 
                     flag = wx.ALL | wx.EXPAND,
2560
 
                     border = 5)
2561
 
        
2562
 
        btnSizer.Add(item = btnLayer,
2563
 
                     proportion = 0,
2564
 
                     flag = wx.ALL | wx.ALIGN_RIGHT,
2565
 
                     border = 5)
2566
 
        
2567
 
        layerSizer.Add(item = btnSizer,
2568
 
                       proportion = 0,
2569
 
                       flag = wx.ALL | wx.EXPAND,
2570
 
                       border = 0)
2571
 
                
2572
 
        # data area
2573
 
        dataSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
2574
 
        for key in ['table', 'key']:
2575
 
            label, value = self.tableWidgets[key]
2576
 
            dataSizer.Add(item = label,
2577
 
                          flag = wx.ALIGN_CENTER_VERTICAL)
2578
 
            dataSizer.Add(item = value,
2579
 
                          flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
2580
 
        dataSizer.AddGrowableCol(1)
2581
 
 
2582
 
        tableSizer.Add(item = dataSizer,
2583
 
                       proportion = 1,
2584
 
                       flag = wx.ALL | wx.EXPAND,
2585
 
                       border = 5)
2586
 
 
2587
 
        tableSizer.Add(item = btnTable,
2588
 
                       proportion = 0,
2589
 
                       flag = wx.ALL | wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT,
2590
 
                       border = 5)
2591
 
 
2592
 
        pageSizer.Add(item = layerSizer,
2593
 
                      proportion = 3,
2594
 
                      flag = wx.ALL | wx.EXPAND,
2595
 
                      border = 3)
2596
 
        
2597
 
        pageSizer.Add(item = tableSizer,
2598
 
                      proportion = 2,
2599
 
                      flag = wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND,
2600
 
                      border = 3)
2601
 
        
2602
 
        layerSizer.SetVirtualSizeHints(self.addPanel)
2603
 
        self.addPanel.SetAutoLayout(True)
2604
 
        self.addPanel.SetSizer(pageSizer)
2605
 
        pageSizer.Fit(self.addPanel)
2606
 
        
2607
 
    def _createDeletePage(self):
2608
 
        """!Delete layer"""
2609
 
        self.deletePanel = wx.Panel(parent = self, id = wx.ID_ANY)
2610
 
        self.AddPage(page = self.deletePanel, text = _("Remove layer"))
2611
 
 
2612
 
        label = wx.StaticText(parent = self.deletePanel, id = wx.ID_ANY,
2613
 
                              label = '%s:' % _("Layer to remove"))
2614
 
 
2615
 
        self.deleteLayer = wx.ComboBox(parent = self.deletePanel, id = wx.ID_ANY, size = (100, -1),
2616
 
                                       style = wx.CB_SIMPLE | wx.CB_READONLY,
2617
 
                                       choices = map(str, self.mapDBInfo.layers.keys()))
2618
 
        self.deleteLayer.SetSelection(0)           
2619
 
        self.deleteLayer.Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
2620
 
 
2621
 
        try:
2622
 
            tableName = self.mapDBInfo.layers[int(self.deleteLayer.GetStringSelection())]['table']
2623
 
        except ValueError:
2624
 
            tableName = ''
2625
 
            
2626
 
        self.deleteTable = wx.CheckBox(parent = self.deletePanel, id = wx.ID_ANY,
2627
 
                                       label = _('Drop also linked attribute table (%s)') % \
2628
 
                                       tableName)
2629
 
 
2630
 
        if tableName == '':
2631
 
            self.deleteLayer.Enable(False)
2632
 
            self.deleteTable.Enable(False)
2633
 
            
2634
 
        btnDelete   = wx.Button(self.deletePanel, wx.ID_DELETE, _("&Remove layer"),
2635
 
                                size = (125,-1))
2636
 
        btnDelete.Bind(wx.EVT_BUTTON, self.OnDeleteLayer)
2637
 
 
2638
 
        #
2639
 
        # do layout
2640
 
        #
2641
 
        pageSizer = wx.BoxSizer(wx.VERTICAL)
2642
 
 
2643
 
        dataSizer = wx.BoxSizer(wx.VERTICAL)
2644
 
 
2645
 
        flexSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
2646
 
 
2647
 
        flexSizer.Add(item = label,
2648
 
                      flag = wx.ALIGN_CENTER_VERTICAL)
2649
 
        flexSizer.Add(item = self.deleteLayer,
2650
 
                      flag = wx.ALIGN_CENTER_VERTICAL)
2651
 
        flexSizer.AddGrowableCol(1)
2652
 
 
2653
 
        dataSizer.Add(item = flexSizer,
2654
 
                      proportion = 0,
2655
 
                      flag = wx.ALL | wx.EXPAND,
2656
 
                      border = 1)
2657
 
 
2658
 
        dataSizer.Add(item = self.deleteTable,
2659
 
                      proportion = 0,
2660
 
                      flag = wx.ALL | wx.EXPAND,
2661
 
                      border = 1)
2662
 
 
2663
 
        pageSizer.Add(item = dataSizer,
2664
 
                      proportion = 1,
2665
 
                      flag = wx.ALL | wx.EXPAND,
2666
 
                      border = 5)
2667
 
 
2668
 
        pageSizer.Add(item = btnDelete,
2669
 
                      proportion = 0,
2670
 
                      flag = wx.ALL | wx.ALIGN_RIGHT,
2671
 
                      border = 5)
2672
 
 
2673
 
        self.deletePanel.SetSizer(pageSizer)
2674
 
 
2675
 
    def _createModifyPage(self):
2676
 
        """!Modify layer"""
2677
 
        self.modifyPanel = wx.Panel(parent = self, id = wx.ID_ANY)
2678
 
        self.AddPage(page = self.modifyPanel, text = _("Modify layer"))
2679
 
 
2680
 
        #
2681
 
        # list of layer widgets (label, value)
2682
 
        #
2683
 
        self.modifyLayerWidgets = {'layer':
2684
 
                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2685
 
                                                      label = '%s:' % _("Layer")),
2686
 
                                        wx.ComboBox(parent = self.modifyPanel, id = wx.ID_ANY,
2687
 
                                                    size = (100, -1),
2688
 
                                                    style = wx.CB_SIMPLE | wx.CB_READONLY,
2689
 
                                                    choices = map(str, 
2690
 
                                                                self.mapDBInfo.layers.keys()))),
2691
 
                                   'driver':
2692
 
                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2693
 
                                                      label = '%s:' % _("Driver")),
2694
 
                                        wx.Choice(parent = self.modifyPanel, id = wx.ID_ANY,
2695
 
                                                  size = (200, -1),
2696
 
                                                  choices = self.listOfDrivers)),
2697
 
                                   'database':
2698
 
                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2699
 
                                                      label = '%s:' % _("Database")),
2700
 
                                        wx.TextCtrl(parent = self.modifyPanel, id = wx.ID_ANY,
2701
 
                                                    value = '', size = (350, -1),
2702
 
                                                    style = wx.TE_PROCESS_ENTER)),
2703
 
                                   'table':
2704
 
                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2705
 
                                                      label = '%s:' % _("Table")),
2706
 
                                        wx.Choice(parent = self.modifyPanel, id = wx.ID_ANY,
2707
 
                                                  size = (200, -1),
2708
 
                                                  choices = self.defaultTables)),
2709
 
                                   'key':
2710
 
                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
2711
 
                                                      label = '%s:' % _("Key column")),
2712
 
                                        wx.Choice(parent = self.modifyPanel, id = wx.ID_ANY,
2713
 
                                                  size = (200, -1),
2714
 
                                                  choices = self.defaultColumns))}
2715
 
        
2716
 
        # set default values for widgets
2717
 
        self.modifyLayerWidgets['layer'][1].SetSelection(0)
2718
 
        try:
2719
 
            layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
2720
 
        except ValueError:
2721
 
            layer = None
2722
 
            for label in self.modifyLayerWidgets.keys():
2723
 
                self.modifyLayerWidgets[label][1].Enable(False)
2724
 
 
2725
 
        if layer:
2726
 
            driver   = self.mapDBInfo.layers[layer]['driver']
2727
 
            database = self.mapDBInfo.layers[layer]['database']
2728
 
            table    = self.mapDBInfo.layers[layer]['table']
2729
 
 
2730
 
            listOfColumns = self._getColumns(driver, database, table)
2731
 
            self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
2732
 
            self.modifyLayerWidgets['database'][1].SetValue(database)
2733
 
            if table in self.modifyLayerWidgets['table'][1].GetItems():
2734
 
                self.modifyLayerWidgets['table'][1].SetStringSelection(table)
2735
 
            else:
2736
 
                if self.defaultConnect['schema'] != '':
2737
 
                    table = self.defaultConnect['schema'] + table # try with default schema
2738
 
                else:
2739
 
                    table = 'public.' + table # try with 'public' schema
2740
 
                self.modifyLayerWidgets['table'][1].SetStringSelection(table)
2741
 
            self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
2742
 
            self.modifyLayerWidgets['key'][1].SetSelection(0)
2743
 
 
2744
 
        # events
2745
 
        self.modifyLayerWidgets['layer'][1].Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
2746
 
        # self.modifyLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
2747
 
        # self.modifyLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
2748
 
        # self.modifyLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
2749
 
 
2750
 
        btnModify = wx.Button(self.modifyPanel, wx.ID_DELETE, _("&Modify layer"),
2751
 
                              size = (125,-1))
2752
 
        btnModify.Bind(wx.EVT_BUTTON, self.OnModifyLayer)
2753
 
 
2754
 
        #
2755
 
        # do layout
2756
 
        #
2757
 
        pageSizer = wx.BoxSizer(wx.VERTICAL)
2758
 
 
2759
 
        # data area
2760
 
        dataSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
2761
 
        for key in ('layer', 'driver', 'database', 'table', 'key'):
2762
 
            label, value = self.modifyLayerWidgets[key]
2763
 
            dataSizer.Add(item = label,
2764
 
                          flag = wx.ALIGN_CENTER_VERTICAL)
2765
 
            if key == 'layer':
2766
 
                dataSizer.Add(item = value,
2767
 
                              flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
2768
 
            else:
2769
 
                dataSizer.Add(item = value,
2770
 
                              flag = wx.ALIGN_CENTER_VERTICAL)
2771
 
        dataSizer.AddGrowableCol(1)
2772
 
 
2773
 
        pageSizer.Add(item = dataSizer,
2774
 
                      proportion = 1,
2775
 
                      flag = wx.ALL | wx.EXPAND,
2776
 
                      border = 5)
2777
 
 
2778
 
        pageSizer.Add(item = btnModify,
2779
 
                      proportion = 0,
2780
 
                      flag = wx.ALL | wx.ALIGN_RIGHT,
2781
 
                      border = 5)
2782
 
 
2783
 
        self.modifyPanel.SetSizer(pageSizer)
2784
 
 
2785
 
    def _getTables(self, driver, database):
2786
 
        """!Get list of tables for given driver and database"""
2787
 
        tables = []
2788
 
 
2789
 
        ret = RunCommand('db.tables',
2790
 
                         parent = self,
2791
 
                         read = True,
2792
 
                         flags = 'p',
2793
 
                         driver = driver,
2794
 
                         database = database)
2795
 
        
2796
 
        if ret is None:
2797
 
            GError(parent = self,
2798
 
                   message = _("Unable to get list of tables.\n"
2799
 
                               "Please use db.connect to set database parameters."))
2800
 
            
2801
 
            return tables
2802
 
        
2803
 
        for table in ret.splitlines():
2804
 
            tables.append(table)
2805
 
        
2806
 
        return tables
2807
 
 
2808
 
    def _getColumns(self, driver, database, table):
2809
 
        """!Get list of column of given table"""
2810
 
        columns = []
2811
 
 
2812
 
        ret = RunCommand('db.columns',
2813
 
                         parent = self,
2814
 
                         quiet = True,
2815
 
                         read = True,
2816
 
                         driver = driver,
2817
 
                         database = database,
2818
 
                         table = table)
2819
 
        
2820
 
        if ret == None:
2821
 
            return columns
2822
 
        
2823
 
        for column in ret.splitlines():
2824
 
            columns.append(column)
2825
 
        
2826
 
        return columns
2827
 
 
2828
 
    def OnDriverChanged(self, event):
2829
 
        """!Driver selection changed, update list of tables"""
2830
 
        driver = event.GetString()
2831
 
        database = self.addLayerWidgets['database'][1].GetValue()
2832
 
 
2833
 
        winTable = self.addLayerWidgets['table'][1]
2834
 
        winKey   = self.addLayerWidgets['key'][1]
2835
 
        tables   = self._getTables(driver, database)
2836
 
 
2837
 
        winTable.SetItems(tables)
2838
 
        winTable.SetSelection(0)
2839
 
 
2840
 
        if len(tables) == 0:
2841
 
            winKey.SetItems([])
2842
 
 
2843
 
        event.Skip()
2844
 
 
2845
 
    def OnDatabaseChanged(self, event):
2846
 
        """!Database selection changed, update list of tables"""
2847
 
        event.Skip()
2848
 
 
2849
 
    def OnTableChanged(self, event):
2850
 
        """!Table name changed, update list of columns"""
2851
 
        driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
2852
 
        database = self.addLayerWidgets['database'][1].GetValue()
2853
 
        table    = event.GetString()
2854
 
 
2855
 
        win  = self.addLayerWidgets['key'][1]
2856
 
        cols = self._getColumns(driver, database, table)
2857
 
        win.SetItems(cols)
2858
 
        win.SetSelection(0)
2859
 
 
2860
 
        event.Skip()
2861
 
 
2862
 
    def OnSetDefault(self, event):
2863
 
        """!Set default values"""
2864
 
        driver   = self.addLayerWidgets['driver'][1]
2865
 
        database = self.addLayerWidgets['database'][1]
2866
 
        table    = self.addLayerWidgets['table'][1]
2867
 
        key      = self.addLayerWidgets['key'][1]
2868
 
 
2869
 
        driver.SetStringSelection(self.defaultConnect['driver'])
2870
 
        database.SetValue(self.defaultConnect['database'])
2871
 
        tables = self._getTables(self.defaultConnect['driver'],
2872
 
                                  self.defaultConnect['database'])
2873
 
        table.SetItems(tables)
2874
 
        table.SetSelection(0)
2875
 
        if len(tables) == 0:
2876
 
            key.SetItems([])
2877
 
        else:
2878
 
            cols = self._getColumns(self.defaultConnect['driver'],
2879
 
                                     self.defaultConnect['database'],
2880
 
                                     tables[0])
2881
 
            key.SetItems(cols)
2882
 
            key.SetSelection(0)
2883
 
 
2884
 
        event.Skip()
2885
 
 
2886
 
    def OnCreateTable(self, event):
2887
 
        """!Create new table (name and key column given)"""
2888
 
        driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
2889
 
        database = self.addLayerWidgets['database'][1].GetValue()
2890
 
        table    = self.tableWidgets['table'][1].GetValue()
2891
 
        key      = self.tableWidgets['key'][1].GetValue()
2892
 
        
2893
 
        if not table or not key:
2894
 
            GError(parent = self,
2895
 
                   message = _("Unable to create new table. "
2896
 
                               "Table name or key column name is missing."))
2897
 
            return
2898
 
 
2899
 
        if table in self.addLayerWidgets['table'][1].GetItems():
2900
 
            GError(parent = self,
2901
 
                   message = _("Unable to create new table. "
2902
 
                               "Table <%s> already exists in the database.") % table)
2903
 
            return
2904
 
        
2905
 
        # create table
2906
 
        sql = 'CREATE TABLE %s (%s INTEGER)' % (table, key)
2907
 
 
2908
 
        RunCommand('db.execute',
2909
 
                   quiet = True,
2910
 
                   parent = self,
2911
 
                   stdin = sql,
2912
 
                   driver = driver,
2913
 
                   database = database)
2914
 
        
2915
 
        # update list of tables
2916
 
        tableList = self.addLayerWidgets['table'][1]
2917
 
        tableList.SetItems(self._getTables(driver, database))
2918
 
        tableList.SetStringSelection(table)
2919
 
 
2920
 
        # update key column selection
2921
 
        keyList = self.addLayerWidgets['key'][1]
2922
 
        keyList.SetItems(self._getColumns(driver, database, table))
2923
 
        keyList.SetStringSelection(key)
2924
 
        
2925
 
        event.Skip()
2926
 
 
2927
 
    def OnAddLayer(self, event):
2928
 
        """!Add new layer to vector map"""
2929
 
        layer    = int(self.addLayerWidgets['layer'][1].GetValue())
2930
 
        layerWin = self.addLayerWidgets['layer'][1]
2931
 
        driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
2932
 
        database = self.addLayerWidgets['database'][1].GetValue()
2933
 
        table    = self.addLayerWidgets['table'][1].GetStringSelection()
2934
 
        key      = self.addLayerWidgets['key'][1].GetStringSelection()
2935
 
        
2936
 
        if layer in self.mapDBInfo.layers.keys():
2937
 
            GError(parent = self,
2938
 
                   message = _("Unable to add new layer to vector map <%(vector)s>. "
2939
 
                               "Layer %(layer)d already exists.") % \
2940
 
                       {'vector' : self.mapDBInfo.map, 'layer' : layer})
2941
 
            return
2942
 
 
2943
 
        # add new layer
2944
 
        ret = RunCommand('v.db.connect',
2945
 
                         parent = self,
2946
 
                         quiet = True,
2947
 
                         map = self.mapDBInfo.map,
2948
 
                         driver = driver,
2949
 
                         database = database,
2950
 
                         table = table,
2951
 
                         key = key,
2952
 
                         layer = layer)
2953
 
        
2954
 
        # insert records into table if required
2955
 
        if self.addLayerWidgets['addCat'][0].IsChecked():
2956
 
            RunCommand('v.to.db',
2957
 
                       parent = self,
2958
 
                       quiet = True,
2959
 
                       map = self.mapDBInfo.map,
2960
 
                       layer = layer,
2961
 
                       qlayer = layer,
2962
 
                       option = 'cat',
2963
 
                       columns = key)
2964
 
 
2965
 
        if ret == 0:
2966
 
            # update dialog (only for new layer)
2967
 
            self.parentDialog.UpdateDialog(layer = layer) 
2968
 
            # update db info
2969
 
            self.mapDBInfo = self.parentDialog.mapDBInfo
2970
 
            # increase layer number
2971
 
            layerWin.SetValue(layer+1)
2972
 
 
2973
 
        if len(self.mapDBInfo.layers.keys()) == 1:
2974
 
            # first layer add --- enable previously disabled widgets
2975
 
            self.deleteLayer.Enable()
2976
 
            self.deleteTable.Enable()
2977
 
            for label in self.modifyLayerWidgets.keys():
2978
 
                self.modifyLayerWidgets[label][1].Enable()
2979
 
            
2980
 
    def OnDeleteLayer(self, event):
2981
 
        """!Delete layer"""
2982
 
        try:
2983
 
            layer = int(self.deleteLayer.GetValue())
2984
 
        except:
2985
 
            return
2986
 
 
2987
 
        RunCommand('v.db.connect',
2988
 
                   parent = self,
2989
 
                   flags = 'd',
2990
 
                   map = self.mapDBInfo.map,
2991
 
                   layer = layer)
2992
 
 
2993
 
        # drop also table linked to layer which is deleted
2994
 
        if self.deleteTable.IsChecked():
2995
 
            driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
2996
 
            database = self.addLayerWidgets['database'][1].GetValue()
2997
 
            table    = self.mapDBInfo.layers[layer]['table']
2998
 
            sql      = 'DROP TABLE %s' % (table)
2999
 
 
3000
 
            RunCommand('db.execute',
3001
 
                       parent = self,
3002
 
                       stdin = sql,
3003
 
                       quiet = True,
3004
 
                       driver = driver,
3005
 
                       database = database)
3006
 
            
3007
 
            # update list of tables
3008
 
            tableList = self.addLayerWidgets['table'][1]
3009
 
            tableList.SetItems(self._getTables(driver, database))
3010
 
            tableList.SetStringSelection(table)
3011
 
        
3012
 
        # update dialog
3013
 
        self.parentDialog.UpdateDialog(layer = layer) 
3014
 
        # update db info
3015
 
        self.mapDBInfo = self.parentDialog.mapDBInfo
3016
 
 
3017
 
        if len(self.mapDBInfo.layers.keys()) == 0:
3018
 
            # disable selected widgets
3019
 
            self.deleteLayer.Enable(False)
3020
 
            self.deleteTable.Enable(False)
3021
 
            for label in self.modifyLayerWidgets.keys():
3022
 
                self.modifyLayerWidgets[label][1].Enable(False)
3023
 
            
3024
 
        event.Skip()
3025
 
 
3026
 
    def OnChangeLayer(self, event):
3027
 
        """!Layer number of layer to be deleted is changed"""
3028
 
        try:
3029
 
            layer = int(event.GetString())
3030
 
        except:
3031
 
            try:
3032
 
                layer = self.mapDBInfo.layers.keys()[0]
3033
 
            except:
3034
 
                return
3035
 
 
3036
 
        if self.GetCurrentPage() == self.modifyPanel:
3037
 
            driver   = self.mapDBInfo.layers[layer]['driver']
3038
 
            database = self.mapDBInfo.layers[layer]['database']
3039
 
            table    = self.mapDBInfo.layers[layer]['table']
3040
 
            listOfColumns = self._getColumns(driver, database, table)
3041
 
            self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
3042
 
            self.modifyLayerWidgets['database'][1].SetValue(database)
3043
 
            self.modifyLayerWidgets['table'][1].SetStringSelection(table)
3044
 
            self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
3045
 
            self.modifyLayerWidgets['key'][1].SetSelection(0)
3046
 
        else:
3047
 
            self.deleteTable.SetLabel(_('Drop also linked attribute table (%s)') % \
3048
 
                                          self.mapDBInfo.layers[layer]['table'])
3049
 
        if event:
3050
 
            event.Skip()
3051
 
 
3052
 
    def OnModifyLayer(self, event):
3053
 
        """!Modify layer connection settings"""
3054
 
 
3055
 
        layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
3056
 
 
3057
 
        modify = False
3058
 
        if self.modifyLayerWidgets['driver'][1].GetStringSelection() != \
3059
 
                self.mapDBInfo.layers[layer]['driver'] or \
3060
 
                self.modifyLayerWidgets['database'][1].GetStringSelection() != \
3061
 
                self.mapDBInfo.layers[layer]['database'] or \
3062
 
                self.modifyLayerWidgets['table'][1].GetStringSelection() != \
3063
 
                self.mapDBInfo.layers[layer]['table'] or \
3064
 
                self.modifyLayerWidgets['key'][1].GetStringSelection() != \
3065
 
                self.mapDBInfo.layers[layer]['key']:
3066
 
            modify = True
3067
 
 
3068
 
        if modify:
3069
 
            # delete layer
3070
 
            RunCommand('v.db.connect',
3071
 
                       parent = self,
3072
 
                       quiet = True,
3073
 
                       flag = 'd',
3074
 
                       map = self.mapDBInfo.map,
3075
 
                       layer = layer)
3076
 
            
3077
 
            # add modified layer
3078
 
            RunCommand('v.db.connect',
3079
 
                       quiet = True,
3080
 
                       map = self.mapDBInfo.map,
3081
 
                       driver = self.modifyLayerWidgets['driver'][1].GetStringSelection(),
3082
 
                       database = self.modifyLayerWidgets['database'][1].GetValue(),
3083
 
                       table = self.modifyLayerWidgets['table'][1].GetStringSelection(),
3084
 
                       key = self.modifyLayerWidgets['key'][1].GetStringSelection(),
3085
 
                       layer = int(layer))
3086
 
            
3087
 
            # update dialog (only for new layer)
3088
 
            self.parentDialog.UpdateDialog(layer = layer) 
3089
 
            # update db info
3090
 
            self.mapDBInfo = self.parentDialog.mapDBInfo
3091
 
 
3092
 
        event.Skip()
3093
 
 
3094
 
def main(argv = None):
3095
 
    import gettext
3096
 
    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
3097
 
    
3098
 
    if argv is None:
3099
 
        argv = sys.argv
3100
 
    
3101
 
    if len(argv) != 2:
3102
 
        print >> sys.stderr, __doc__
3103
 
        sys.exit()
3104
 
    
3105
 
    #some applications might require image handlers
3106
 
    wx.InitAllImageHandlers()
3107
 
    
3108
 
    app = wx.PySimpleApp()
3109
 
    f = AttributeManager(parent = None, id = wx.ID_ANY,
3110
 
                         title = "%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
3111
 
                                              argv[1]),
3112
 
                         size = (900,600), vectorName = argv[1])
3113
 
    f.Show()
3114
 
    
3115
 
    app.MainLoop()
3116
 
    
3117
 
if __name__ == '__main__':
3118
 
    main()