# HG changeset patch
# User Edouard Tisserant
# Date 1542926394 -3600
# Node ID e0f16317668ec0533715cfb3d719c3cf9c60e0fb
# Parent  decf52efb7f7b0daa00b6d19bf37c40162baccb9
IDManager : finished Import/Export. Added merging capability to import (asks if particular ID is replaced during import). Added ESC as closing shortcut to IDManager dialog, and adjusted its size.

diff -r decf52efb7f7 -r e0f16317668e PSKManagement.py
--- a/PSKManagement.py	Wed Nov 21 14:10:51 2018 +0100
+++ b/PSKManagement.py	Thu Nov 22 23:39:54 2018 +0100
@@ -11,7 +11,8 @@
 
 # PSK Management Data model :
 # [[ID,Desc, LastKnownURI, LastConnect]]
-COL_ID,COL_URI,COL_DESC,COL_LAST = range(4)
+COL_ID, COL_URI, COL_DESC, COL_LAST = range(4)
+REPLACE, REPLACE_ALL, KEEP, KEEP_ALL, CANCEL = range(5)
 
 def _pskpath(project_path):
     return os.path.join(project_path, 'psk')
@@ -93,14 +94,18 @@
                 zf.write(os.path.join(path, nm), nm)
 
 def ImportIDs(project_path, import_zip, should_I_replace_callback):
+    zf = ZipFile(import_zip, 'r')
     data = GetData(project_path)
 
     zip_loaded_data = json.loads(zf.open('management.json').read())
     name_list = zf.namelist()
-    zip_filtered_data = _filterData(name_list, loaded_data)
+    zip_filtered_data = _filterData(name_list, zip_loaded_data)
 
     idata = _dataByID(data)
 
+    keys_to_import = []
+    result = None
+
     for imported_row in zip_filtered_data:
         ID = imported_row[COL_ID]
         existing_row = idata.get(ID, None)
@@ -108,16 +113,23 @@
             data.append(imported_row)
         else:
             # callback returns the selected list for merge or none if canceled
-            result = should_I_replace_callback(existing_row, imported_row)
-            if result is None:
-                break
+            if result not in [REPLACE_ALL, KEEP_ALL]:
+                result = should_I_replace_callback(existing_row, imported_row)
+
+            if result == CANCEL:
+                return
             
-            if result: 
+            if result in [REPLACE_ALL, REPLACE]:
                 # replace with imported
                 existing_row[:] = imported_row
                 # copy the key of selected
-                self.extract(ID+".secret", _pskpath(project_path))
+                keys_to_import.append(ID)
+    
+    for ID in keys_to_import:
+        zf.extract(ID+".secret", _pskpath(project_path))
 
     SaveData(project_path, data)
 
+    return data
 
+
diff -r decf52efb7f7 -r e0f16317668e controls/IDBrowser.py
--- a/controls/IDBrowser.py	Wed Nov 21 14:10:51 2018 +0100
+++ b/controls/IDBrowser.py	Thu Nov 22 23:39:54 2018 +0100
@@ -8,7 +8,8 @@
 import wx
 import wx.dataview as dv
 import PSKManagement as PSK
-from PSKManagement import COL_ID,COL_URI,COL_DESC,COL_LAST
+from PSKManagement import *
+from dialogs.IDMergeDialog import IDMergeDialog
 
 class IDBrowserModel(dv.PyDataViewIndexListModel):
     def __init__(self, project_path, columncount):
@@ -70,7 +71,10 @@
         self._saveData()
 
     def Import(self, filepath, sircb):
-        PSK.ImportIDs(self.project_path, filepath, sircb)
+        data = PSK.ImportIDs(self.project_path, filepath, sircb)
+        if data is not None:
+            self.data = data
+            self.Reset(len(self.data)) 
 
     def Export(self, filepath):
         PSK.ExportIDs(self.project_path, filepath)
@@ -184,10 +188,35 @@
         if dialog.ShowModal() == wx.ID_OK:
             self.model.Export(dialog.GetPath())
 
-    def ShouldIReplaceCallback(self,some,stuff):
-        # TODO
-        wx.MessageBox("TODO : ShouldIReplaceCallback")
-        return True
+    def ShouldIReplaceCallback(self,existing,replacement):
+        ID,URI,DESC,LAST = existing
+        _ID,_URI,_DESC,_LAST = replacement
+        dlg = IDMergeDialog(self, 
+            _("Import IDs"), 
+            (_("Replace information for ID {ID} ?") + "\n\n" +
+             _("Existing:") + "\n    " +
+             _("Description:") + " {DESC}\n    " +
+             _("Last known URI:") + " {URI}\n    " +
+             _("Last connection:") + " {LAST}\n\n" +
+             _("Replacement:") + "\n    " +
+             _("Description:") + " {_DESC}\n    " +
+             _("Last known URI:") + " {_URI}\n    " +
+             _("Last connection:") + " {_LAST}\n").format(**locals()),
+            _("Do the same for following IDs"),
+            [_("Replace"), _("Keep"),_("Cancel")])
+
+        answer = dlg.ShowModal() # return value ignored as we have "Ok" only anyhow
+        if answer == wx.ID_CANCEL:
+            return CANCEL
+
+        if dlg.OptionChecked():
+            if answer == wx.ID_YES:
+                return REPLACE_ALL
+            return KEEP_ALL
+        else:
+            if answer == wx.ID_YES:
+                return REPLACE
+            return KEEP
 
     def OnImportButton(self, evt):
         dialog = wx.FileDialog(self, _("Choose a file"),
diff -r decf52efb7f7 -r e0f16317668e dialogs/IDManager.py
--- a/dialogs/IDManager.py	Wed Nov 21 14:10:51 2018 +0100
+++ b/dialogs/IDManager.py	Thu Nov 22 23:39:54 2018 +0100
@@ -11,8 +11,17 @@
         wx.Dialog.__init__(self,
                            name='IDManager', parent=parent,
                            title=_('URI Editor'),
-                           style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
+                           style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+                           size=(800,600))
         # start IDBrowser in manager mode
         self.browser = IDBrowser(self, ctr)
+        self.Bind(wx.EVT_CHAR_HOOK, self.OnEscapeKey)
+
+    def OnEscapeKey(self, event):
+        keycode = event.GetKeyCode()
+        if keycode == wx.WXK_ESCAPE:
+            self.EndModal(wx.ID_CANCEL)
+        else:
+            event.Skip()
 
 
diff -r decf52efb7f7 -r e0f16317668e dialogs/IDMergeDialog.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dialogs/IDMergeDialog.py	Thu Nov 22 23:39:54 2018 +0100
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# See COPYING file for copyrights details.
+
+from __future__ import absolute_import
+import wx
+
+# class RichMessageDialog is still not available in wxPython 3.0.2 
+class IDMergeDialog(wx.Dialog):
+    def __init__(self, parent, title, question, optiontext, button_texts):
+        wx.Dialog.__init__(self, parent, title=title)
+
+        main_sizer = wx.BoxSizer(wx.VERTICAL)
+
+        message = wx.StaticText(self, label=question)
+        main_sizer.AddWindow(message, border=20,
+                             flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.LEFT | wx.RIGHT)
+
+        self.check = wx.CheckBox(self, label=optiontext)
+        main_sizer.AddWindow(self.check, border=20,
+                             flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL)
+
+        buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        for label,wxID in zip(button_texts, [wx.ID_YES, wx.ID_NO, wx.ID_CANCEL]):
+            Button = wx.Button(self, label=label)
+            def OnButtonFactory(_wxID):
+                return lambda event: self.EndModal(_wxID)
+            self.Bind(wx.EVT_BUTTON, OnButtonFactory(wxID), Button)
+            buttons_sizer.AddWindow(Button)
+
+        main_sizer.AddSizer(buttons_sizer, border=20,
+                            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_RIGHT)
+
+        self.SetSizer(main_sizer)
+        self.Fit()
+
+        self.Bind(wx.EVT_CHAR_HOOK, self.OnEscapeKey)
+
+    def OnEscapeKey(self, event):
+        keycode = event.GetKeyCode()
+        if keycode == wx.WXK_ESCAPE:
+            self.EndModal(wx.ID_CANCEL)
+        else:
+            event.Skip()
+
+    def OptionChecked(self):
+        return self.check.GetValue()