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 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@2492: 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@2492: Edouard@2334: def Compare(self, item1, item2, col, ascending): edouard@2492: 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@2492: 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@2492: 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@2492: self.Reset(len(self.data)) Edouard@2340: Edouard@2340: def Export(self, filepath): Edouard@2340: PSK.ExportIDs(self.project_path, filepath) Edouard@2340: edouard@2492: edouard@2492: colflags = dv.DATAVIEW_COL_RESIZABLE | dv.DATAVIEW_COL_SORTABLE edouard@2492: Edouard@2334: Edouard@2336: class IDBrowser(wx.Panel): Edouard@2335: def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs): edouard@2492: big = self.isManager = SelectURICallBack is None and SelectIDCallBack is None Edouard@2458: wx.Panel.__init__(self, parent, -1, size=(800 if big else 450, 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@2492: if self.isManager: Edouard@2334: # no multiple selection in selector mode Edouard@2334: dvStyle |= dv.DV_MULTIPLE edouard@2492: self.dvc = dv.DataViewCtrl(self, style=dvStyle) edouard@2492: edouard@2492: def args(*a, **k): edouard@2492: return (a, k) Edouard@2334: Edouard@2334: ColumnsDesc = [ edouard@2492: args(_("ID"), COL_ID, width=70), edouard@2492: args(_("Last URI"), COL_URI, width=300 if big else 80), edouard@2492: args(_("Description"), COL_DESC, width=300 if big else 200, edouard@2492: mode=dv.DATAVIEW_CELL_EDITABLE andrej@2537: if self.isManager andrej@2537: else dv.DATAVIEW_CELL_INERT), edouard@2492: 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@2458: col_list = [] edouard@2492: for a, k in ColumnsDesc: Edouard@2458: col_list.append( edouard@2492: self.dvc.AppendTextColumn(*a, **dict(k, flags=colflags))) Edouard@2458: col_list[COL_LAST].SetSortOrder(False) Edouard@2458: Edouard@2458: # TODO : sort by last bvisit by default Edouard@2334: edouard@2492: 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@2492: 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@2492: 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@2492: 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@2492: btnbox.Add(importButton, 0, wx.LEFT | wx.RIGHT, 5) edouard@2492: edouard@2492: else: Edouard@2334: # selector mode 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@2492: btnbox.Add(self.useURIButton, 0, wx.LEFT | wx.RIGHT, 5) edouard@2492: edouard@2492: 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: 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@2492: 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@2492: 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: 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@2492: wildcard=_("PSK ZIP files (*.zip)|*.zip"), edouard@2492: style=wx.SAVE | wx.OVERWRITE_PROMPT) Edouard@2340: if dialog.ShowModal() == wx.ID_OK: Edouard@2340: self.model.Export(dialog.GetPath()) Edouard@2340: edouard@2492: # pylint: disable=unused-variable edouard@2492: def ShouldIReplaceCallback(self, existing, replacement): edouard@2492: ID, URI, DESC, LAST = existing edouard@2492: _ID, _URI, _DESC, _LAST = replacement edouard@2492: dlg = IDMergeDialog( edouard@2492: self, edouard@2492: _("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@2492: [_("Replace"), _("Keep"), _("Cancel")]) edouard@2492: edouard@2492: 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@2492: wildcard=_("PSK ZIP files (*.zip)|*.zip"), edouard@2492: 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)