controls/IDBrowser.py
changeset 2492 7dd551ac2fa0
parent 2458 2a70d5240300
child 2537 eb4a4cc41914
equal deleted inserted replaced
2491:362039519454 2492:7dd551ac2fa0
     2 # -*- coding: utf-8 -*-
     2 # -*- coding: utf-8 -*-
     3 
     3 
     4 # See COPYING file for copyrights details.
     4 # See COPYING file for copyrights details.
     5 
     5 
     6 from __future__ import absolute_import
     6 from __future__ import absolute_import
     7 import os
       
     8 import wx
     7 import wx
     9 import wx.dataview as dv
     8 import wx.dataview as dv
    10 import PSKManagement as PSK
     9 import PSKManagement as PSK
    11 from PSKManagement import *
    10 from PSKManagement import *
    12 from dialogs.IDMergeDialog import IDMergeDialog
    11 from dialogs.IDMergeDialog import IDMergeDialog
       
    12 
    13 
    13 
    14 class IDBrowserModel(dv.PyDataViewIndexListModel):
    14 class IDBrowserModel(dv.PyDataViewIndexListModel):
    15     def __init__(self, project_path, columncount):
    15     def __init__(self, project_path, columncount):
    16         self.project_path = project_path
    16         self.project_path = project_path
    17         self.columncount = columncount
    17         self.columncount = columncount
    34     def GetColumnCount(self):
    34     def GetColumnCount(self):
    35         return len(self.data[0]) if self.data else self.columncount
    35         return len(self.data[0]) if self.data else self.columncount
    36 
    36 
    37     def GetCount(self):
    37     def GetCount(self):
    38         return len(self.data)
    38         return len(self.data)
    39     
    39 
    40     def Compare(self, item1, item2, col, ascending):
    40     def Compare(self, item1, item2, col, ascending):
    41         if not ascending: # swap sort order?
    41         if not ascending:  # swap sort order?
    42             item2, item1 = item1, item2
    42             item2, item1 = item1, item2
    43         row1 = self.GetRow(item1)
    43         row1 = self.GetRow(item1)
    44         row2 = self.GetRow(item2)
    44         row2 = self.GetRow(item2)
    45         if col == 0:
    45         if col == 0:
    46             return cmp(int(self.data[row1][col]), int(self.data[row2][col]))
    46             return cmp(int(self.data[row1][col]), int(self.data[row2][col]))
    48             return cmp(self.data[row1][col], self.data[row2][col])
    48             return cmp(self.data[row1][col], self.data[row2][col])
    49 
    49 
    50     def DeleteRows(self, rows):
    50     def DeleteRows(self, rows):
    51         rows = list(rows)
    51         rows = list(rows)
    52         rows.sort(reverse=True)
    52         rows.sort(reverse=True)
    53         
    53 
    54         for row in rows:
    54         for row in rows:
    55             PSK.DeleteID(self.project_path, self.data[row][COL_ID])
    55             PSK.DeleteID(self.project_path, self.data[row][COL_ID])
    56             del self.data[row]
    56             del self.data[row]
    57             self.RowDeleted(row)
    57             self.RowDeleted(row)
    58         self._saveData()
    58         self._saveData()
    59             
    59 
    60     def AddRow(self, value):
    60     def AddRow(self, value):
    61         self.data.append(value)
    61         self.data.append(value)
    62         self.RowAppended()
    62         self.RowAppended()
    63         self._saveData()
    63         self._saveData()
    64 
    64 
    65     def Import(self, filepath, sircb):
    65     def Import(self, filepath, sircb):
    66         data = PSK.ImportIDs(self.project_path, filepath, sircb)
    66         data = PSK.ImportIDs(self.project_path, filepath, sircb)
    67         if data is not None:
    67         if data is not None:
    68             self.data = data
    68             self.data = data
    69             self.Reset(len(self.data)) 
    69             self.Reset(len(self.data))
    70 
    70 
    71     def Export(self, filepath):
    71     def Export(self, filepath):
    72         PSK.ExportIDs(self.project_path, filepath)
    72         PSK.ExportIDs(self.project_path, filepath)
    73 
    73 
    74 colflags = dv.DATAVIEW_COL_RESIZABLE|dv.DATAVIEW_COL_SORTABLE
    74 
       
    75 colflags = dv.DATAVIEW_COL_RESIZABLE | dv.DATAVIEW_COL_SORTABLE
       
    76 
    75 
    77 
    76 class IDBrowser(wx.Panel):
    78 class IDBrowser(wx.Panel):
    77     def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs):
    79     def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs):
    78         big = self.isManager = SelectURICallBack is None and SelectIDCallBack is None 
    80         big = self.isManager = SelectURICallBack is None and SelectIDCallBack is None
    79         wx.Panel.__init__(self, parent, -1, size=(800 if big else 450,
    81         wx.Panel.__init__(self, parent, -1, size=(800 if big else 450,
    80                                                   600 if big else 200))
    82                                                   600 if big else 200))
    81 
    83 
    82         self.SelectURICallBack = SelectURICallBack
    84         self.SelectURICallBack = SelectURICallBack
    83         self.SelectIDCallBack = SelectIDCallBack
    85         self.SelectIDCallBack = SelectIDCallBack
    84 
    86 
    85         dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES
    87         dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES
    86         if self.isManager :
    88         if self.isManager:
    87             # no multiple selection in selector mode
    89             # no multiple selection in selector mode
    88             dvStyle |= dv.DV_MULTIPLE
    90             dvStyle |= dv.DV_MULTIPLE
    89         self.dvc = dv.DataViewCtrl(self, style = dvStyle)
    91         self.dvc = dv.DataViewCtrl(self, style=dvStyle)
    90                     
    92 
    91         args = lambda *a,**k:(a,k)
    93         def args(*a, **k):
       
    94             return (a, k)
    92 
    95 
    93         ColumnsDesc = [
    96         ColumnsDesc = [
    94             args(_("ID"), COL_ID, width = 70),
    97             args(_("ID"), COL_ID, width=70),
    95             args(_("Last URI"), COL_URI, width = 300 if big else 80),
    98             args(_("Last URI"), COL_URI, width=300 if big else 80),
    96             args(_("Description"), COL_DESC, width = 300 if big else 200, 
    99             args(_("Description"), COL_DESC, width=300 if big else 200,
    97                 mode = dv.DATAVIEW_CELL_EDITABLE 
   100                  mode=dv.DATAVIEW_CELL_EDITABLE
    98                        if self.isManager 
   101                       if self.isManager
    99                        else dv.DATAVIEW_CELL_INERT),
   102                       else dv.DATAVIEW_CELL_INERT),
   100             args(_("Last connection"),  COL_LAST, width = 120),
   103             args(_("Last connection"), COL_LAST, width=120),
   101         ]
   104         ]
   102 
   105 
   103         self.model = IDBrowserModel(ctr.ProjectPath, len(ColumnsDesc))
   106         self.model = IDBrowserModel(ctr.ProjectPath, len(ColumnsDesc))
   104         self.dvc.AssociateModel(self.model)
   107         self.dvc.AssociateModel(self.model)
   105 
   108 
   106         col_list = []
   109         col_list = []
   107         for a,k in ColumnsDesc:
   110         for a, k in ColumnsDesc:
   108             col_list.append(
   111             col_list.append(
   109                 self.dvc.AppendTextColumn(*a,**dict(k, flags = colflags)))
   112                 self.dvc.AppendTextColumn(*a, **dict(k, flags=colflags)))
   110         col_list[COL_LAST].SetSortOrder(False)
   113         col_list[COL_LAST].SetSortOrder(False)
   111 
   114 
   112         # TODO : sort by last bvisit by default
   115         # TODO : sort by last bvisit by default
   113 
   116 
   114         self.Sizer = wx.BoxSizer(wx.VERTICAL) 
   117         self.Sizer = wx.BoxSizer(wx.VERTICAL)
   115         self.Sizer.Add(self.dvc, 1, wx.EXPAND)
   118         self.Sizer.Add(self.dvc, 1, wx.EXPAND)
   116 
   119 
   117         btnbox = wx.BoxSizer(wx.HORIZONTAL)
   120         btnbox = wx.BoxSizer(wx.HORIZONTAL)
   118         if self.isManager :
   121         if self.isManager:
   119 
   122 
   120             # deletion of secret and metadata
   123             # deletion of secret and metadata
   121             deleteButton = wx.Button(self, label=_("Delete ID"))
   124             deleteButton = wx.Button(self, label=_("Delete ID"))
   122             self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton)
   125             self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton)
   123             btnbox.Add(deleteButton, 0, wx.LEFT|wx.RIGHT, 5)
   126             btnbox.Add(deleteButton, 0, wx.LEFT | wx.RIGHT, 5)
   124 
   127 
   125             # export all
   128             # export all
   126             exportButton = wx.Button(self, label=_("Export all"))
   129             exportButton = wx.Button(self, label=_("Export all"))
   127             self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton)
   130             self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton)
   128             btnbox.Add(exportButton, 0, wx.LEFT|wx.RIGHT, 5)
   131             btnbox.Add(exportButton, 0, wx.LEFT | wx.RIGHT, 5)
   129 
   132 
   130             # import with a merge -> duplicates are asked for
   133             # import with a merge -> duplicates are asked for
   131             importButton = wx.Button(self, label=_("Import"))
   134             importButton = wx.Button(self, label=_("Import"))
   132             self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton)
   135             self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton)
   133             btnbox.Add(importButton, 0, wx.LEFT|wx.RIGHT, 5)
   136             btnbox.Add(importButton, 0, wx.LEFT | wx.RIGHT, 5)
   134 
   137 
   135         else :
   138         else:
   136             # selector mode
   139             # selector mode
   137             self.useURIButton = wx.Button(self, label=_("Use last URI"))
   140             self.useURIButton = wx.Button(self, label=_("Use last URI"))
   138             self.Bind(wx.EVT_BUTTON, self.OnUseURIButton, self.useURIButton)
   141             self.Bind(wx.EVT_BUTTON, self.OnUseURIButton, self.useURIButton)
   139             self.useURIButton.Disable()
   142             self.useURIButton.Disable()
   140             btnbox.Add(self.useURIButton, 0, wx.LEFT|wx.RIGHT, 5)
   143             btnbox.Add(self.useURIButton, 0, wx.LEFT | wx.RIGHT, 5)
   141 
   144 
   142         self.Sizer.Add(btnbox, 0, wx.TOP|wx.BOTTOM, 5)
   145         self.Sizer.Add(btnbox, 0, wx.TOP | wx.BOTTOM, 5)
   143         self.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.OnSelectionChanged, self.dvc)
   146         self.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.OnSelectionChanged, self.dvc)
   144 
       
   145 
   147 
   146     def OnDeleteButton(self, evt):
   148     def OnDeleteButton(self, evt):
   147         items = self.dvc.GetSelections()
   149         items = self.dvc.GetSelections()
   148         rows = [self.model.GetRow(item) for item in items]
   150         rows = [self.model.GetRow(item) for item in items]
   149 
   151 
   150         # Ask if user really wants to delete
   152         # Ask if user really wants to delete
   151         if wx.MessageBox(_('Are you sure to delete selected IDs?'),
   153         if wx.MessageBox(_('Are you sure to delete selected IDs?'),
   152                          _('Delete IDs'),
   154                          _('Delete IDs'),
   153                              wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES:
   155                          wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES:
   154             return
   156             return
   155 
   157 
   156         self.model.DeleteRows(rows)
   158         self.model.DeleteRows(rows)
   157 
   159 
   158     def OnSelectionChanged(self, evt):
   160     def OnSelectionChanged(self, evt):
   159         if not self.isManager :
   161         if not self.isManager:
   160             items = self.dvc.GetSelections()
   162             items = self.dvc.GetSelections()
   161             somethingSelected = len(items) > 0
   163             somethingSelected = len(items) > 0
   162             self.useURIButton.Enable(somethingSelected)
   164             self.useURIButton.Enable(somethingSelected)
   163             if somethingSelected:
   165             if somethingSelected:
   164                 row = self.model.GetRow(items[0])
   166                 row = self.model.GetRow(items[0])
   165                 ID = self.model.GetValueByRow(row, COL_ID)
   167                 ID = self.model.GetValueByRow(row, COL_ID)
   166                 self.SelectIDCallBack(ID)
   168                 self.SelectIDCallBack(ID)
   167 
   169 
   168 
       
   169     def OnUseURIButton(self, evt):
   170     def OnUseURIButton(self, evt):
   170         row = self.model.GetRow(self.dvc.GetSelections()[0])
   171         row = self.model.GetRow(self.dvc.GetSelections()[0])
   171         URI = self.model.GetValueByRow(row, COL_URI)
   172         URI = self.model.GetValueByRow(row, COL_URI)
   172         if URI:
   173         if URI:
   173             self.SelectURICallBack(URI)
   174             self.SelectURICallBack(URI)
   174 
   175 
   175     def OnExportButton(self, evt):
   176     def OnExportButton(self, evt):
   176         dialog = wx.FileDialog(self, _("Choose a file"),
   177         dialog = wx.FileDialog(self, _("Choose a file"),
   177                                wildcard = _("PSK ZIP files (*.zip)|*.zip"), 
   178                                wildcard=_("PSK ZIP files (*.zip)|*.zip"),
   178                                style = wx.SAVE | wx.OVERWRITE_PROMPT)
   179                                style=wx.SAVE | wx.OVERWRITE_PROMPT)
   179         if dialog.ShowModal() == wx.ID_OK:
   180         if dialog.ShowModal() == wx.ID_OK:
   180             self.model.Export(dialog.GetPath())
   181             self.model.Export(dialog.GetPath())
   181 
   182 
   182     def ShouldIReplaceCallback(self,existing,replacement):
   183     # pylint: disable=unused-variable
   183         ID,URI,DESC,LAST = existing
   184     def ShouldIReplaceCallback(self, existing, replacement):
   184         _ID,_URI,_DESC,_LAST = replacement
   185         ID, URI, DESC, LAST = existing
   185         dlg = IDMergeDialog(self, 
   186         _ID, _URI, _DESC, _LAST = replacement
   186             _("Import IDs"), 
   187         dlg = IDMergeDialog(
       
   188             self,
       
   189             _("Import IDs"),
   187             (_("Replace information for ID {ID} ?") + "\n\n" +
   190             (_("Replace information for ID {ID} ?") + "\n\n" +
   188              _("Existing:") + "\n    " +
   191              _("Existing:") + "\n    " +
   189              _("Description:") + " {DESC}\n    " +
   192              _("Description:") + " {DESC}\n    " +
   190              _("Last known URI:") + " {URI}\n    " +
   193              _("Last known URI:") + " {URI}\n    " +
   191              _("Last connection:") + " {LAST}\n\n" +
   194              _("Last connection:") + " {LAST}\n\n" +
   192              _("Replacement:") + "\n    " +
   195              _("Replacement:") + "\n    " +
   193              _("Description:") + " {_DESC}\n    " +
   196              _("Description:") + " {_DESC}\n    " +
   194              _("Last known URI:") + " {_URI}\n    " +
   197              _("Last known URI:") + " {_URI}\n    " +
   195              _("Last connection:") + " {_LAST}\n").format(**locals()),
   198              _("Last connection:") + " {_LAST}\n").format(**locals()),
   196             _("Do the same for following IDs"),
   199             _("Do the same for following IDs"),
   197             [_("Replace"), _("Keep"),_("Cancel")])
   200             [_("Replace"), _("Keep"), _("Cancel")])
   198 
   201 
   199         answer = dlg.ShowModal() # return value ignored as we have "Ok" only anyhow
   202         answer = dlg.ShowModal()  # return value ignored as we have "Ok" only anyhow
   200         if answer == wx.ID_CANCEL:
   203         if answer == wx.ID_CANCEL:
   201             return CANCEL
   204             return CANCEL
   202 
   205 
   203         if dlg.OptionChecked():
   206         if dlg.OptionChecked():
   204             if answer == wx.ID_YES:
   207             if answer == wx.ID_YES:
   209                 return REPLACE
   212                 return REPLACE
   210             return KEEP
   213             return KEEP
   211 
   214 
   212     def OnImportButton(self, evt):
   215     def OnImportButton(self, evt):
   213         dialog = wx.FileDialog(self, _("Choose a file"),
   216         dialog = wx.FileDialog(self, _("Choose a file"),
   214                                wildcard = _("PSK ZIP files (*.zip)|*.zip"), 
   217                                wildcard=_("PSK ZIP files (*.zip)|*.zip"),
   215                                style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
   218                                style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
   216         if dialog.ShowModal() == wx.ID_OK:
   219         if dialog.ShowModal() == wx.ID_OK:
   217             self.model.Import(dialog.GetPath(),
   220             self.model.Import(dialog.GetPath(),
   218                               self.ShouldIReplaceCallback)
   221                               self.ShouldIReplaceCallback)
   219