From bcb21731aacc344cdcd73631346633b6c50d4ec0 Mon Sep 17 00:00:00 2001
From: Patrick Monnerat <patrick@monnerat.net>
Date: Thu, 23 May 2019 20:20:13 +0200
Subject: [PATCH] quickopen plugin: change code for Python 2 & 3 compatibility.

---
 plugins/quickopen/quickopen/__init__.py     |   2 +-
 plugins/quickopen/quickopen/popup.py        | 124 ++++++++++++--------
 plugins/quickopen/quickopen/virtualdirs.py  |   8 +-
 plugins/quickopen/quickopen/windowhelper.py |  28 +++--
 4 files changed, 94 insertions(+), 68 deletions(-)

diff --git a/plugins/quickopen/quickopen/__init__.py b/plugins/quickopen/quickopen/__init__.py
index 3ae72a4..17ccdf7 100644
--- a/plugins/quickopen/quickopen/__init__.py
+++ b/plugins/quickopen/quickopen/__init__.py
@@ -18,7 +18,7 @@
 #  Boston, MA 02110-1301, USA.
 
 from gi.repository import GObject, Peas
-from windowhelper import WindowHelper
+from .windowhelper import WindowHelper
 
 class QuickOpenPlugin(GObject.Object, Peas.Activatable):
     __gtype_name__ = "QuickOpenPlugin"
diff --git a/plugins/quickopen/quickopen/popup.py b/plugins/quickopen/quickopen/popup.py
index c6cc801..be40509 100644
--- a/plugins/quickopen/quickopen/popup.py
+++ b/plugins/quickopen/quickopen/popup.py
@@ -18,10 +18,11 @@
 #  Boston, MA 02110-1301, USA.
 
 import os
+import sys
 import fnmatch
 import xml.sax.saxutils
 from gi.repository import GObject, Gio, GLib, Gdk, Gtk, Pango, Pluma
-from virtualdirs import VirtualDirectory
+from .virtualdirs import VirtualDirectory
 
 class Popup(Gtk.Dialog):
     __gtype_name__ = "QuickOpenPopup"
@@ -30,11 +31,12 @@ class Popup(Gtk.Dialog):
         Gtk.Dialog.__init__(self,
                             title=_('Quick Open'),
                             parent=window,
-                            flags=Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL,
-                            buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL))
-
-        self._open_button = self.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT)
+                            flags=Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL)
 
+        self._add_button(_("_Cancel"), Gtk.ResponseType.CANCEL, "process-stop")
+        self._open_button = self._add_button(_("_Open"),
+                                             Gtk.ResponseType.ACCEPT,
+                                             "document-open")
         self._handler = handler
         self._build_ui()
 
@@ -48,7 +50,10 @@ class Popup(Gtk.Dialog):
         self._busy_cursor = Gdk.Cursor(Gdk.CursorType.WATCH)
 
         accel_group = Gtk.AccelGroup()
-        accel_group.connect(Gdk.KEY_l, Gdk.ModifierType.CONTROL_MASK, 0, self.on_focus_entry)
+        accel_group.connect(Gdk.keyval_from_name('l'),
+                            Gdk.ModifierType.CONTROL_MASK,
+                            0,
+                            self.on_focus_entry)
 
         self.add_accel_group(accel_group)
 
@@ -63,10 +68,11 @@ class Popup(Gtk.Dialog):
         return self._size
 
     def _build_ui(self):
+        self.set_border_width(5)
         vbox = self.get_content_area()
         vbox.set_spacing(3)
 
-        self._entry = Gtk.Entry()
+        self._entry = Gtk.SearchEntry()
 
         self._entry.connect('changed', self.on_changed)
         self._entry.connect('key-press-event', self.on_key_press_event)
@@ -78,7 +84,10 @@ class Popup(Gtk.Dialog):
         tv = Gtk.TreeView()
         tv.set_headers_visible(False)
 
-        self._store = Gtk.ListStore(Gio.Icon, str, GObject.Object, Gio.FileType)
+        self._store = Gtk.ListStore(Gio.Icon,
+                                    str,
+                                    GObject.Object,
+                                    Gio.FileType)
         tv.set_model(self._store)
 
         self._treeview = tv
@@ -106,7 +115,7 @@ class Popup(Gtk.Dialog):
         vbox.pack_start(sw, True, True, 0)
 
         lbl = Gtk.Label()
-        lbl.set_alignment(0, 0.5)
+        lbl.set_halign(Gtk.Align.START)
         lbl.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
         self._info_label = lbl
 
@@ -129,19 +138,22 @@ class Popup(Gtk.Dialog):
             cell.set_property('cell-background-set', False)
             cell.set_property('style-set', False)
 
-    def _icon_from_stock(self, stock):
-        theme = Gtk.icon_theme_get_default()
-        size = Gtk.icon_size_lookup(Gtk.IconSize.MENU)
-        pixbuf = theme.load_icon(stock, size[0], Gtk.IconLookupFlags.USE_BUILTIN)
-
-        return pixbuf
+    def _add_button(self, label, response, icon=None):
+        button = self.add_button(label, response)
+        if icon:
+            image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON)
+            button.set_image(image)
+            button.set_property("always-show-image", True)
+        return button
 
     def _list_dir(self, gfile):
         entries = []
 
         try:
-            ret = gfile.enumerate_children("standard::*", Gio.FileQueryInfoFlags.NONE, None)
-        except GLib.GError:
+            ret = gfile.enumerate_children("standard::*",
+                                           Gio.FileQueryInfoFlags.NONE,
+                                           None)
+        except GLib.Error:
             pass
 
         if isinstance(ret, Gio.FileEnumerator):
@@ -151,27 +163,23 @@ class Popup(Gtk.Dialog):
                 if not entry:
                     break
 
-                entries.append((gfile.get_child(entry.get_name()), entry))
+                if not entry.get_is_backup():
+                    entries.append((gfile.get_child(entry.get_name()), entry))
         else:
             entries = ret
 
         children = []
 
         for entry in entries:
-            children.append((entry[0], entry[1].get_name(), entry[1].get_file_type(), entry[1].get_icon()))
+            children.append((entry[0],
+                             entry[1].get_name(),
+                             entry[1].get_file_type(),
+                             entry[1].get_icon()))
 
         return children
 
-    def _compare_entries(self, a, b, lpart):
-        if lpart in a:
-            if lpart in b:
-                return cmp(a.index(lpart), b.index(lpart))
-            else:
-                return -1
-        elif lpart in b:
-            return 1
-        else:
-            return 0
+    def _key_entries(self, pos):
+        return pos if pos >= 0 else sys.maxsize
 
     def _match_glob(self, s, glob):
         if glob:
@@ -185,8 +193,7 @@ class Popup(Gtk.Dialog):
 
         if not d in self._cache:
             entries = self._list_dir(d)
-            entries.sort(lambda x, y: cmp(x[1].lower(), y[1].lower()))
-
+            entries.sort(key=lambda x: x[1].lower())
             self._cache[d] = entries
         else:
             entries = self._cache[d]
@@ -212,7 +219,7 @@ class Popup(Gtk.Dialog):
                         (not lpart or len(parts) == 1):
                     found.append(entry)
 
-        found.sort(lambda a, b: self._compare_entries(a[1].lower(), b[1].lower(), lpart))
+        found.sort(key=lambda x: self._key_entries(x[1].lower().find(lpart)))
 
         if lpart == '..':
             newdirs.append(d.get_parent())
@@ -251,7 +258,9 @@ class Popup(Gtk.Dialog):
         return os.sep.join(out)
 
     def _get_icon(self, f):
-        query = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None)
+        query = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON,
+                             Gio.FileQueryInfoFlags.NONE,
+                             None)
 
         if not query:
             return None
@@ -304,7 +313,10 @@ class Popup(Gtk.Dialog):
         for d in self._dirs:
             if isinstance(d, VirtualDirectory):
                 for entry in d.enumerate_children("standard::*", 0, None):
-                    self._append_to_store((entry[1].get_icon(), xml.sax.saxutils.escape(entry[1].get_name()), entry[0], entry[1].get_file_type()))
+                    self._append_to_store((entry[1].get_icon(),
+                                           xml.sax.saxutils.escape(entry[1].get_name()),
+                                           entry[0],
+                                           entry[1].get_file_type()))
 
     def _set_busy(self, busy):
         if busy:
@@ -337,14 +349,17 @@ class Popup(Gtk.Dialog):
             for d in self._dirs:
                 for entry in self.do_search_dir(parts, d):
                     pathparts = self._make_parts(d, entry[0], parts)
-                    self._append_to_store((entry[3], self.make_markup(parts, pathparts), entry[0], entry[2]))
+                    self._append_to_store((entry[3],
+                                           self.make_markup(parts, pathparts),
+                                           entry[0],
+                                           entry[2]))
 
         piter = self._store.get_iter_first()
 
         if piter:
-            self._treeview.get_selection().select_path(self._store.get_path(piter))
+            path = self._store.get_path(piter)
+            self._treeview.get_selection().select_path(path)
 
-        self.get_window().set_cursor(None)
         self._set_busy(False)
 
     def do_show(self):
@@ -366,7 +381,7 @@ class Popup(Gtk.Dialog):
             model, rows = selection.get_selected_rows()
             start = rows[0]
 
-            self._shift_start = Gtk.TreeRowReference(self._store, start)
+            self._shift_start = Gtk.TreeRowReference.new(self._store, start)
         else:
             start = self._shift_start.get_path()
 
@@ -419,7 +434,7 @@ class Popup(Gtk.Dialog):
             else:
                 self._select_index(num - 1, hasctrl, hasshift)
         else:
-            idx = path[0]
+            idx = path.get_indices()[0]
 
             if idx + howmany < 0:
                 self._select_index(0, hasctrl, hasshift)
@@ -461,19 +476,22 @@ class Popup(Gtk.Dialog):
                     if text[i] == os.sep:
                         break
 
-                self._entry.set_text(os.path.join(text[:i], os.path.basename(info[0].get_uri())) + os.sep)
+                self._entry.set_text(os.path.join(text[:i],
+                                  os.path.basename(info[0].get_uri())) + os.sep)
                 self._entry.set_position(-1)
                 self._entry.grab_focus()
                 return True
 
         if rows and ret:
-            self.destroy()
+            # We destroy the popup in an idle callback to work around a crash that happens with
+            # GTK_IM_MODULE=xim.  See https://bugzilla.gnome.org/show_bug.cgi?id=737711 .
+            GLib.idle_add(self.destroy)
 
         if not rows:
             gfile = self._direct_file()
 
             if gfile and self._handler(gfile):
-                self.destroy()
+                GLib.idle_add(self.destroy)
             else:
                 ret = False
         else:
@@ -495,20 +513,24 @@ class Popup(Gtk.Dialog):
 
     def on_key_press_event(self, widget, event):
         move_mapping = {
-            Gdk.KEY_Down: 1,
-            Gdk.KEY_Up: -1,
-            Gdk.KEY_Page_Down: 5,
-            Gdk.KEY_Page_Up: -5
+            "Down": 1,
+            "Up": -1,
+            "Page_Down": 5,
+            "Page_Up": -5
         }
 
-        if event.keyval == Gdk.KEY_Escape:
+        keyname = Gdk.keyval_name(event.keyval)
+
+        if keyname == "Escape":
             self.destroy()
             return True
-        elif event.keyval in move_mapping:
-            return self._move_selection(move_mapping[event.keyval], event.state & Gdk.ModifierType.CONTROL_MASK, event.state & Gdk.ModifierType.SHIFT_MASK)
-        elif event.keyval in [Gdk.KEY_Return, Gdk.KEY_KP_Enter, Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab]:
+        elif keyname in move_mapping:
+            return self._move_selection(move_mapping[keyname],
+                                   event.state & Gdk.ModifierType.CONTROL_MASK,
+                                   event.state & Gdk.ModifierType.SHIFT_MASK)
+        elif keyname in ["Return", "KP_Enter", "Tab", "ISO_Left_Tab"]:
             return self._activate()
-        elif event.keyval == Gdk.KEY_space and event.state & Gdk.ModifierType.CONTROL_MASK:
+        elif keyname == "space" and event.state & Gdk.ModifierType.CONTROL_MASK:
             self.toggle_cursor()
 
         return False
diff --git a/plugins/quickopen/quickopen/virtualdirs.py b/plugins/quickopen/quickopen/virtualdirs.py
index 53d716a..7bf66b8 100644
--- a/plugins/quickopen/quickopen/virtualdirs.py
+++ b/plugins/quickopen/quickopen/virtualdirs.py
@@ -38,7 +38,9 @@ class VirtualDirectory(object):
             return
 
         try:
-            info = child.query_info("standard::*", Gio.FileQueryInfoFlags.NONE, None)
+            info = child.query_info("standard::*",
+                                    Gio.FileQueryInfoFlags.NONE,
+                                    None)
 
             if info:
                 self._children.append((child, info))
@@ -46,7 +48,7 @@ class VirtualDirectory(object):
             pass
 
 class RecentDocumentsDirectory(VirtualDirectory):
-    def __init__(self, maxitems=10):
+    def __init__(self, maxitems=200):
         VirtualDirectory.__init__(self, 'recent')
 
         self._maxitems = maxitems
@@ -56,7 +58,7 @@ class RecentDocumentsDirectory(VirtualDirectory):
         manager = Gtk.RecentManager.get_default()
 
         items = manager.get_items()
-        items.sort(lambda a, b: cmp(b.get_visited(), a.get_visited()))
+        items.sort(key=lambda a: a.get_visited(), reverse=True)
 
         added = 0
 
diff --git a/plugins/quickopen/quickopen/windowhelper.py b/plugins/quickopen/quickopen/windowhelper.py
index 19e44cb..7f9ab12 100644
--- a/plugins/quickopen/quickopen/windowhelper.py
+++ b/plugins/quickopen/quickopen/windowhelper.py
@@ -17,11 +17,12 @@
 #  Foundation, Inc., 51 Franklin St, Fifth Floor,
 #  Boston, MA 02110-1301, USA.
 
-from popup import Popup
 import os
+import codecs
 from gi.repository import Gio, GLib, Gtk, Pluma
-from virtualdirs import RecentDocumentsDirectory
-from virtualdirs import CurrentDocumentsDirectory
+from .popup import Popup
+from .virtualdirs import RecentDocumentsDirectory
+from .virtualdirs import CurrentDocumentsDirectory
 
 ui_str = """<ui>
   <menubar name="MenuBar">
@@ -60,12 +61,13 @@ class WindowHelper:
 
     def _install_menu(self):
         manager = self._window.get_ui_manager()
+        action = Gtk.Action.new("QuickOpen",
+                                _("Quick open"),
+                                _("Quickly open documents"))
+        action.set_icon_name("document-open")
+        action.connect("activate", self.on_quick_open_activate)
         self._action_group = Gtk.ActionGroup("PlumaQuickOpenPluginActions")
-        self._action_group.add_actions([
-            ("QuickOpen", Gtk.STOCK_OPEN, _("Quick open"),
-             '<Ctrl><Alt>O', _("Quickly open documents"),
-             self.on_quick_open_activate)
-        ])
+        self._action_group.add_action_with_accel(action, "<Ctrl><Alt>O")
 
         manager.insert_action_group(self._action_group, -1)
         self._ui_id = manager.add_ui_from_string(ui_str)
@@ -95,10 +97,10 @@ class WindowHelper:
                 if uri:
                     gfile = Gio.file_new_for_uri(uri)
 
-                    if gfile.is_native():
+                    if gfile and gfile.is_native():
                         paths.append(gfile)
 
-        except StandardError:
+        except Exception:
             pass
 
         # Recent documents
@@ -128,14 +130,14 @@ class WindowHelper:
         self._popup.connect('destroy', self.on_popup_destroy)
 
     def _local_bookmarks(self):
-        filename = os.path.expanduser('~/.gtk-bookmarks')
+        filename = os.path.expanduser('~/.config/gtk-3.0/bookmarks')
 
         if not os.path.isfile(filename):
             return []
 
         paths = []
 
-        for line in file(filename, 'r').xreadlines():
+        for line in codecs.open(filename, 'r', encoding='utf-8'):
             uri = line.strip().split(" ")[0]
             f = Gio.file_new_for_uri(uri)
 
@@ -160,7 +162,7 @@ class WindowHelper:
         desktopdir = None
 
         if os.path.isfile(config):
-            for line in file(config, 'r').xreadlines():
+            for line in codecs.open(config, 'r', encoding='utf-8'):
                 line = line.strip()
 
                 if line.startswith('XDG_DESKTOP_DIR'):
-- 
2.21.0

