Edouard@2334: #!/usr/bin/env python Edouard@2334: # -*- coding: utf-8 -*- Edouard@2334: Edouard@2334: # See COPYING file for copyrights details. Edouard@2334: Edouard@2334: from __future__ import absolute_import Edouard@2334: import os Edouard@2334: import wx Edouard@2334: import wx.dataview as dv Edouard@2339: import PSKManagement as PSK Edouard@2428: from PSKManagement import * Edouard@2428: from dialogs.IDMergeDialog import IDMergeDialog Edouard@2334: Edouard@2336: class IDBrowserModel(dv.PyDataViewIndexListModel): Edouard@2340: def __init__(self, project_path, columncount): Edouard@2340: self.project_path = project_path Edouard@2334: self.columncount = columncount Edouard@2340: self.data = PSK.GetData(project_path) Edouard@2334: dv.PyDataViewIndexListModel.__init__(self, len(self.data)) Edouard@2334: Edouard@2334: def _saveData(self): Edouard@2340: PSK.SaveData(self.project_path, self.data) Edouard@2334: Edouard@2334: def GetColumnType(self, col): Edouard@2334: return "string" Edouard@2334: Edouard@2334: def GetValueByRow(self, row, col): Edouard@2334: return self.data[row][col] Edouard@2334: Edouard@2334: def SetValueByRow(self, value, row, col): Edouard@2334: self.data[row][col] = value Edouard@2334: self._saveData() Edouard@2334: Edouard@2334: def GetColumnCount(self): Edouard@2334: return len(self.data[0]) if self.data else self.columncount Edouard@2334: Edouard@2334: def GetCount(self): Edouard@2334: return len(self.data) Edouard@2334: Edouard@2334: def GetAttrByRow(self, row, col, attr): Edouard@2334: if col == 3: Edouard@2334: attr.SetColour('blue') Edouard@2334: attr.SetBold(True) Edouard@2334: return True Edouard@2334: return False Edouard@2334: Edouard@2334: Edouard@2334: def Compare(self, item1, item2, col, ascending): Edouard@2334: if not ascending: # swap sort order? Edouard@2334: item2, item1 = item1, item2 Edouard@2334: row1 = self.GetRow(item1) Edouard@2334: row2 = self.GetRow(item2) Edouard@2334: if col == 0: Edouard@2334: return cmp(int(self.data[row1][col]), int(self.data[row2][col])) Edouard@2334: else: Edouard@2334: return cmp(self.data[row1][col], self.data[row2][col]) Edouard@2334: Edouard@2334: def DeleteRows(self, rows): Edouard@2334: rows = list(rows) Edouard@2334: rows.sort(reverse=True) Edouard@2334: Edouard@2334: for row in rows: Edouard@2340: PSK.DeleteID(self.project_path, self.data[row][COL_ID]) Edouard@2334: del self.data[row] Edouard@2334: self.RowDeleted(row) Edouard@2334: self._saveData() Edouard@2334: Edouard@2334: def AddRow(self, value): Edouard@2334: self.data.append(value) Edouard@2334: self.RowAppended() Edouard@2334: self._saveData() Edouard@2334: Edouard@2340: def Import(self, filepath, sircb): Edouard@2428: data = PSK.ImportIDs(self.project_path, filepath, sircb) Edouard@2428: if data is not None: Edouard@2428: self.data = data Edouard@2428: self.Reset(len(self.data)) Edouard@2340: Edouard@2340: def Export(self, filepath): Edouard@2340: PSK.ExportIDs(self.project_path, filepath) Edouard@2340: Edouard@2334: colflags = dv.DATAVIEW_COL_RESIZABLE|dv.DATAVIEW_COL_SORTABLE Edouard@2334: Edouard@2336: class IDBrowser(wx.Panel): Edouard@2335: def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs): Edouard@2340: big = self.isManager = SelectURICallBack is None and SelectIDCallBack is None Edouard@2340: wx.Panel.__init__(self, parent, -1, size=(800 if big else 400, Edouard@2340: 600 if big else 200)) Edouard@2334: Edouard@2334: self.SelectURICallBack = SelectURICallBack Edouard@2335: self.SelectIDCallBack = SelectIDCallBack Edouard@2334: Edouard@2334: dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES Edouard@2334: if self.isManager : Edouard@2334: # no multiple selection in selector mode Edouard@2334: dvStyle |= dv.DV_MULTIPLE Edouard@2334: self.dvc = dv.DataViewCtrl(self, style = dvStyle) Edouard@2334: Edouard@2334: args = lambda *a,**k:(a,k) Edouard@2334: Edouard@2334: ColumnsDesc = [ Edouard@2335: args(_("ID"), COL_ID, width = 100), Edouard@2340: args(_("Last URI"), COL_URI, width = 160 if big else 80), Edouard@2335: args(_("Description"), COL_DESC, width = 200, Edouard@2334: mode = dv.DATAVIEW_CELL_EDITABLE Edouard@2334: if self.isManager Edouard@2334: else dv.DATAVIEW_CELL_INERT), Edouard@2340: args(_("Last connection"), COL_LAST, width = 120), Edouard@2334: ] Edouard@2334: Edouard@2339: self.model = IDBrowserModel(ctr.ProjectPath, len(ColumnsDesc)) Edouard@2334: self.dvc.AssociateModel(self.model) Edouard@2334: Edouard@2334: for a,k in ColumnsDesc: Edouard@2334: self.dvc.AppendTextColumn(*a,**dict(k, flags = colflags)) Edouard@2334: Edouard@2334: # TODO : when select, Edouard@2334: # - update ID field of scheme editor Edouard@2334: # - enable use URI button Edouard@2334: Edouard@2334: self.Sizer = wx.BoxSizer(wx.VERTICAL) Edouard@2334: self.Sizer.Add(self.dvc, 1, wx.EXPAND) Edouard@2334: Edouard@2334: btnbox = wx.BoxSizer(wx.HORIZONTAL) Edouard@2334: if self.isManager : Edouard@2334: Edouard@2334: # deletion of secret and metadata Edouard@2334: deleteButton = wx.Button(self, label=_("Delete ID")) Edouard@2334: self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton) Edouard@2334: btnbox.Add(deleteButton, 0, wx.LEFT|wx.RIGHT, 5) Edouard@2334: Edouard@2334: # export all Edouard@2334: exportButton = wx.Button(self, label=_("Export all")) Edouard@2334: self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton) Edouard@2334: btnbox.Add(exportButton, 0, wx.LEFT|wx.RIGHT, 5) Edouard@2334: Edouard@2334: # import with a merge -> duplicates are asked for Edouard@2334: importButton = wx.Button(self, label=_("Import")) Edouard@2334: self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton) Edouard@2334: btnbox.Add(importButton, 0, wx.LEFT|wx.RIGHT, 5) Edouard@2334: Edouard@2334: else : Edouard@2334: # selector mode Edouard@2334: # use last known URI button Edouard@2334: # TODO : disable use URI button until something selected Edouard@2335: self.useURIButton = wx.Button(self, label=_("Use last URI")) Edouard@2335: self.Bind(wx.EVT_BUTTON, self.OnUseURIButton, self.useURIButton) Edouard@2335: self.useURIButton.Disable() Edouard@2335: btnbox.Add(self.useURIButton, 0, wx.LEFT|wx.RIGHT, 5) Edouard@2334: Edouard@2334: self.Sizer.Add(btnbox, 0, wx.TOP|wx.BOTTOM, 5) Edouard@2335: self.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.OnSelectionChanged, self.dvc) Edouard@2335: Edouard@2334: Edouard@2334: def OnDeleteButton(self, evt): Edouard@2334: items = self.dvc.GetSelections() Edouard@2334: rows = [self.model.GetRow(item) for item in items] Edouard@2334: Edouard@2334: # Ask if user really wants to delete Edouard@2334: if wx.MessageBox(_('Are you sure to delete selected IDs?'), Edouard@2334: _('Delete IDs'), Edouard@2334: wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES: Edouard@2334: return Edouard@2334: Edouard@2334: self.model.DeleteRows(rows) Edouard@2334: Edouard@2335: def OnSelectionChanged(self, evt): Edouard@2335: if not self.isManager : Edouard@2335: items = self.dvc.GetSelections() Edouard@2335: somethingSelected = len(items) > 0 Edouard@2335: self.useURIButton.Enable(somethingSelected) Edouard@2335: if somethingSelected: Edouard@2335: row = self.model.GetRow(items[0]) Edouard@2335: ID = self.model.GetValueByRow(row, COL_ID) Edouard@2335: self.SelectIDCallBack(ID) Edouard@2335: Edouard@2335: Edouard@2335: def OnUseURIButton(self, evt): Edouard@2335: row = self.model.GetRow(self.dvc.GetSelections()[0]) Edouard@2335: URI = self.model.GetValueByRow(row, COL_URI) Edouard@2335: if URI: Edouard@2335: self.SelectURICallBack(URI) Edouard@2334: Edouard@2334: def OnExportButton(self, evt): Edouard@2340: dialog = wx.FileDialog(self, _("Choose a file"), Edouard@2340: wildcard = _("PSK ZIP files (*.zip)|*.zip"), Edouard@2340: style = wx.SAVE | wx.OVERWRITE_PROMPT) Edouard@2340: if dialog.ShowModal() == wx.ID_OK: Edouard@2340: self.model.Export(dialog.GetPath()) Edouard@2340: Edouard@2428: def ShouldIReplaceCallback(self,existing,replacement): Edouard@2428: ID,URI,DESC,LAST = existing Edouard@2428: _ID,_URI,_DESC,_LAST = replacement Edouard@2428: dlg = IDMergeDialog(self, Edouard@2428: _("Import IDs"), Edouard@2428: (_("Replace information for ID {ID} ?") + "\n\n" + Edouard@2428: _("Existing:") + "\n " + Edouard@2428: _("Description:") + " {DESC}\n " + Edouard@2428: _("Last known URI:") + " {URI}\n " + Edouard@2428: _("Last connection:") + " {LAST}\n\n" + Edouard@2428: _("Replacement:") + "\n " + Edouard@2428: _("Description:") + " {_DESC}\n " + Edouard@2428: _("Last known URI:") + " {_URI}\n " + Edouard@2428: _("Last connection:") + " {_LAST}\n").format(**locals()), Edouard@2428: _("Do the same for following IDs"), Edouard@2428: [_("Replace"), _("Keep"),_("Cancel")]) Edouard@2428: Edouard@2428: answer = dlg.ShowModal() # return value ignored as we have "Ok" only anyhow Edouard@2428: if answer == wx.ID_CANCEL: Edouard@2428: return CANCEL Edouard@2428: Edouard@2428: if dlg.OptionChecked(): Edouard@2428: if answer == wx.ID_YES: Edouard@2428: return REPLACE_ALL Edouard@2428: return KEEP_ALL Edouard@2428: else: Edouard@2428: if answer == wx.ID_YES: Edouard@2428: return REPLACE Edouard@2428: return KEEP Edouard@2334: Edouard@2334: def OnImportButton(self, evt): Edouard@2340: dialog = wx.FileDialog(self, _("Choose a file"), Edouard@2340: wildcard = _("PSK ZIP files (*.zip)|*.zip"), Edouard@2340: style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) Edouard@2340: if dialog.ShowModal() == wx.ID_OK: Edouard@2340: self.model.Import(dialog.GetPath(), Edouard@2340: self.ShouldIReplaceCallback) Edouard@2334: