controls/IDBrowser.py
changeset 2336 869a61616b42
parent 2335 4262256e1d28
child 2339 48b4eba13064
equal deleted inserted replaced
2335:4262256e1d28 2336:869a61616b42
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 # See COPYING file for copyrights details.
       
     5 
       
     6 from __future__ import absolute_import
       
     7 import os
       
     8 import wx
       
     9 import wx.dataview as dv
       
    10 import json
       
    11 
       
    12 def _GetInitialData(psk_path):
       
    13     # [(ID, Desc, LastKnownURI, LastConnect)
       
    14     data = []
       
    15 
       
    16     data_path = os.path.join(psk_path, 'management.json')
       
    17 
       
    18     if os.path.isdir(psk_path):
       
    19         # load known keys metadata
       
    20         # {ID:(Desc, LastKnownURI, LastConnect)}
       
    21         recovered_data = json.loads(open(data_path).read()) \
       
    22                          if os.path.exists(data_path) else {}
       
    23 
       
    24         # go through all secret files available an build data
       
    25         # out of data recoverd from json and list of secret.
       
    26         # this implicitly filters IDs out of metadata who's
       
    27         # secret is missing
       
    28         psk_files = os.listdir(psk_path)
       
    29         for filename in psk_files:
       
    30            if filename.endswith('.secret'):
       
    31                ID = filename[:-7]  # strip filename extension
       
    32                meta = recovered_data.get(ID, 
       
    33                    ['', # default description
       
    34                     None, # last known URI
       
    35                     None])  # last connection date
       
    36                    
       
    37                data.append([ID]+meta)
       
    38     return data
       
    39 
       
    40 def _DeleteID(psk_path, ID):
       
    41     secret_path = os.path.join(psk_path, ID+'.secret')
       
    42     os.remove(secret_path)
       
    43 
       
    44 def _SaveData(psk_path, data):
       
    45     if not os.path.isdir(psk_path):
       
    46         os.mkdir(psk_path)
       
    47     data_path = os.path.join(psk_path, 'management.json')
       
    48     to_store = {row[0]:row[1:] for row in data}
       
    49     with open(data_path, 'w') as f:
       
    50         f.write(json.dumps(to_store))
       
    51 
       
    52 class IDBrowserModel(dv.PyDataViewIndexListModel):
       
    53     def __init__(self, psk_path, columncount):
       
    54         self.psk_path = psk_path
       
    55         self.columncount = columncount
       
    56         self.data = _GetInitialData(psk_path)
       
    57         dv.PyDataViewIndexListModel.__init__(self, len(self.data))
       
    58 
       
    59     def _saveData(self):
       
    60         _SaveData(self.psk_path, self.data)
       
    61 
       
    62     def GetColumnType(self, col):
       
    63         return "string"
       
    64 
       
    65     def GetValueByRow(self, row, col):
       
    66         return self.data[row][col]
       
    67 
       
    68     def SetValueByRow(self, value, row, col):
       
    69         self.data[row][col] = value
       
    70         self._saveData()
       
    71 
       
    72     def GetColumnCount(self):
       
    73         return len(self.data[0]) if self.data else self.columncount
       
    74 
       
    75     def GetCount(self):
       
    76         return len(self.data)
       
    77     
       
    78     def GetAttrByRow(self, row, col, attr):
       
    79         if col == 3:
       
    80             attr.SetColour('blue')
       
    81             attr.SetBold(True)
       
    82             return True
       
    83         return False
       
    84 
       
    85 
       
    86     def Compare(self, item1, item2, col, ascending):
       
    87         if not ascending: # swap sort order?
       
    88             item2, item1 = item1, item2
       
    89         row1 = self.GetRow(item1)
       
    90         row2 = self.GetRow(item2)
       
    91         if col == 0:
       
    92             return cmp(int(self.data[row1][col]), int(self.data[row2][col]))
       
    93         else:
       
    94             return cmp(self.data[row1][col], self.data[row2][col])
       
    95 
       
    96     def DeleteRows(self, rows):
       
    97         rows = list(rows)
       
    98         rows.sort(reverse=True)
       
    99         
       
   100         for row in rows:
       
   101             del self.data[row]
       
   102             _DeleteID(self.psk_path, ID)
       
   103             self.RowDeleted(row)
       
   104         self._saveData()
       
   105             
       
   106     def AddRow(self, value):
       
   107         self.data.append(value)
       
   108         self.RowAppended()
       
   109         self._saveData()
       
   110 
       
   111 colflags = dv.DATAVIEW_COL_RESIZABLE|dv.DATAVIEW_COL_SORTABLE
       
   112 COL_ID,COL_URI,COL_DESC,COL_LAST = range(4)
       
   113 
       
   114 class IDBrowser(wx.Panel):
       
   115     def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs):
       
   116         wx.Panel.__init__(self, parent, -1, size=(400,200))
       
   117 
       
   118         self.isManager = SelectURICallBack is None and SelectIDCallBack is None 
       
   119         self.SelectURICallBack = SelectURICallBack
       
   120         self.SelectIDCallBack = SelectIDCallBack
       
   121 
       
   122         dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES
       
   123         if self.isManager :
       
   124             # no multiple selection in selector mode
       
   125             dvStyle |= dv.DV_MULTIPLE
       
   126         self.dvc = dv.DataViewCtrl(self, style = dvStyle)
       
   127                     
       
   128         args = lambda *a,**k:(a,k)
       
   129 
       
   130         ColumnsDesc = [
       
   131             args(_("ID"), COL_ID, width = 100),
       
   132             args(_("Last URI"), COL_URI, width = 80),
       
   133             args(_("Description"), COL_DESC, width = 200, 
       
   134                 mode = dv.DATAVIEW_CELL_EDITABLE 
       
   135                        if self.isManager 
       
   136                        else dv.DATAVIEW_CELL_INERT),
       
   137             args(_("Last connection"),  COL_LAST, width = 100),
       
   138         ]
       
   139 
       
   140         self.model = IDBrowserModel(
       
   141             os.path.join(str(ctr.ProjectPath), 'psk'),
       
   142             len(ColumnsDesc))
       
   143         self.dvc.AssociateModel(self.model)
       
   144 
       
   145         for a,k in ColumnsDesc:
       
   146             self.dvc.AppendTextColumn(*a,**dict(k, flags = colflags))
       
   147 
       
   148         # TODO : when select,
       
   149         #  - update ID field of scheme editor
       
   150         #  - enable use URI button
       
   151 
       
   152         self.Sizer = wx.BoxSizer(wx.VERTICAL) 
       
   153         self.Sizer.Add(self.dvc, 1, wx.EXPAND)
       
   154 
       
   155         btnbox = wx.BoxSizer(wx.HORIZONTAL)
       
   156         if self.isManager :
       
   157 
       
   158             # deletion of secret and metadata
       
   159             deleteButton = wx.Button(self, label=_("Delete ID"))
       
   160             self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton)
       
   161             btnbox.Add(deleteButton, 0, wx.LEFT|wx.RIGHT, 5)
       
   162 
       
   163             # export all
       
   164             exportButton = wx.Button(self, label=_("Export all"))
       
   165             self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton)
       
   166             btnbox.Add(exportButton, 0, wx.LEFT|wx.RIGHT, 5)
       
   167 
       
   168             # import with a merge -> duplicates are asked for
       
   169             importButton = wx.Button(self, label=_("Import"))
       
   170             self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton)
       
   171             btnbox.Add(importButton, 0, wx.LEFT|wx.RIGHT, 5)
       
   172 
       
   173         else :
       
   174             # selector mode
       
   175             # use last known URI button
       
   176             # TODO : disable use URI button until something selected
       
   177             self.useURIButton = wx.Button(self, label=_("Use last URI"))
       
   178             self.Bind(wx.EVT_BUTTON, self.OnUseURIButton, self.useURIButton)
       
   179             self.useURIButton.Disable()
       
   180             btnbox.Add(self.useURIButton, 0, wx.LEFT|wx.RIGHT, 5)
       
   181 
       
   182         self.Sizer.Add(btnbox, 0, wx.TOP|wx.BOTTOM, 5)
       
   183         self.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.OnSelectionChanged, self.dvc)
       
   184 
       
   185 
       
   186     def OnDeleteButton(self, evt):
       
   187         items = self.dvc.GetSelections()
       
   188         rows = [self.model.GetRow(item) for item in items]
       
   189 
       
   190         # Ask if user really wants to delete
       
   191         if wx.MessageBox(_('Are you sure to delete selected IDs?'),
       
   192                          _('Delete IDs'),
       
   193                              wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES:
       
   194             return
       
   195 
       
   196         self.model.DeleteRows(rows)
       
   197 
       
   198     def OnSelectionChanged(self, evt):
       
   199         if not self.isManager :
       
   200             items = self.dvc.GetSelections()
       
   201             somethingSelected = len(items) > 0
       
   202             self.useURIButton.Enable(somethingSelected)
       
   203             if somethingSelected:
       
   204                 row = self.model.GetRow(items[0])
       
   205                 ID = self.model.GetValueByRow(row, COL_ID)
       
   206                 self.SelectIDCallBack(ID)
       
   207 
       
   208 
       
   209     def OnUseURIButton(self, evt):
       
   210         row = self.model.GetRow(self.dvc.GetSelections()[0])
       
   211         URI = self.model.GetValueByRow(row, COL_URI)
       
   212         if URI:
       
   213             self.SelectURICallBack(URI)
       
   214 
       
   215     def OnExportButton(self, evt):
       
   216         # TODO 
       
   217         wx.MessageBox(_('?'),
       
   218                       _('Mhe'),
       
   219                       wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT)
       
   220 
       
   221     def OnImportButton(self, evt):
       
   222         # TODO 
       
   223         wx.MessageBox(_('?'),
       
   224                       _('Mhe'),
       
   225                       wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT)
       
   226