summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjvoisin2018-07-19 00:11:30 +0200
committerjvoisin2018-07-19 00:11:30 +0200
commit052a356750541d5f985cfa7f995afc4083f42a13 (patch)
tree7b079fc51f3acd8035132e400025703dd5ae3c6a
parent2f670651cf9307a1d8ae7b4cb83a8f2d03ad0ef7 (diff)
Implement a much better Nautilus extension thanks to @atenart
Co-authored-by: Antoine Tenart <antoine.tenart@ack.tf> Co-authored-by: jvoisin <julien.voisin@dustri.org>
-rw-r--r--nautilus/nautilus_mat2.py157
1 files changed, 70 insertions, 87 deletions
diff --git a/nautilus/nautilus_mat2.py b/nautilus/nautilus_mat2.py
index 48f2261..9e8d4db 100644
--- a/nautilus/nautilus_mat2.py
+++ b/nautilus/nautilus_mat2.py
@@ -1,5 +1,10 @@
1#!/usr/bin/env python3 1#!/usr/bin/env python3
2 2
3# TODO:
4# - Test with a large amount of files.
5# - Show a progression bar when the removal takes time.
6# - Improve the MessageDialog list for failed items.
7
3import os 8import os
4from urllib.parse import unquote 9from urllib.parse import unquote
5 10
@@ -8,46 +13,65 @@ gi.require_version('Nautilus', '3.0')
8gi.require_version('Gtk', '3.0') 13gi.require_version('Gtk', '3.0')
9from gi.repository import Nautilus, GObject, Gtk, Gio 14from gi.repository import Nautilus, GObject, Gtk, Gio
10 15
11
12from libmat2 import parser_factory 16from libmat2 import parser_factory
13 17
14class Mat2Wrapper(): 18class Mat2Wrapper():
15 def __init__(self, filepath): 19 def __init__(self, filepath):
16 self.filepath = filepath 20 self.__filepath = filepath
17 21
18class StatusWindow(Gtk.Window): 22 def remove_metadata(self):
19 def __init__(self, items): 23 parser, mtype = parser_factory.get_parser(self.__filepath)
20 self.window = Gtk.Window() 24 if parser is None:
21 self.window.set_border_width(10) 25 return False, mtype
26 return parser.remove_all(), mtype
22 27
23 self.items = items 28class ColumnExtension(GObject.GObject, Nautilus.MenuProvider, Nautilus.LocationWidgetProvider):
24 self.confirm_window() 29 def notify(self):
30 self.infobar_msg.set_text("Failed to clean some items")
31 self.infobar.show_all()
25 32
26 def confirm_window(self): 33 def get_widget(self, uri, window):
27 # Header bar 34 self.infobar = Gtk.InfoBar()
28 hb = Gtk.HeaderBar() 35 self.infobar.set_message_type(Gtk.MessageType.ERROR)
29 self.window.set_titlebar(hb) 36 self.infobar.set_show_close_button(True)
30 hb.props.title = "Remove metadata" 37 self.infobar.connect("response", self.__cb_infobar_response)
38
39 hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
40 self.infobar.get_content_area().pack_start(hbox, False, False, 0)
41
42 btn = Gtk.Button("Show")
43 btn.connect("clicked", self.__cb_show_failed)
44 self.infobar.get_content_area().pack_end(btn, False, False, 0)
45
46 self.infobar_msg = Gtk.Label()
47 hbox.pack_start(self.infobar_msg, False, False, 0)
48
49 return self.infobar
50
51 def __cb_infobar_response(self, infobar, response):
52 if response == Gtk.ResponseType.CLOSE:
53 self.infobar.hide()
31 54
32 cancel = Gtk.Button("Cancel") 55 def __cb_show_failed(self, button):
33 cancel.connect("clicked", self.cancel_btn) 56 self.infobar.hide()
34 hb.pack_start(cancel)
35 57
36 self.remove = Gtk.Button("Remove") 58 window = Gtk.Window()
37 self.remove.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION) 59 hb = Gtk.HeaderBar()
38 self.remove.connect("clicked", self.remove_btn) 60 window.set_titlebar(hb)
39 hb.pack_end(self.remove) 61 hb.props.title = "Metadata removal failed"
62
63 exit_buton = Gtk.Button("Exit")
64 exit_buton.connect("clicked", lambda _: window.close())
65 hb.pack_end(exit_buton)
40 66
41 self.main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) 67 box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
42 self.window.add(self.main_box) 68 window.add(box)
43 69
44 # List of files to clean
45 listbox = Gtk.ListBox() 70 listbox = Gtk.ListBox()
46 self.main_box.pack_start(listbox, True, True, 0)
47 listbox.set_selection_mode(Gtk.SelectionMode.NONE) 71 listbox.set_selection_mode(Gtk.SelectionMode.NONE)
48 for i in self.items: 72 box.pack_start(listbox, True, True, 0)
49 p, mtype = parser_factory.get_parser(i)
50 73
74 for i, mtype in self.failed_items:
51 row = Gtk.ListBoxRow() 75 row = Gtk.ListBoxRow()
52 hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) 76 hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
53 row.add(hbox) 77 row.add(hbox)
@@ -56,78 +80,37 @@ class StatusWindow(Gtk.Window):
56 select_image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON) 80 select_image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
57 hbox.pack_start(select_image, False, False, 0) 81 hbox.pack_start(select_image, False, False, 0)
58 82
59 image = Gtk.Image()
60 image.set_from_stock(Gtk.STOCK_NO if not p else Gtk.STOCK_YES, Gtk.IconSize.BUTTON)
61 hbox.pack_start(image, False, False, 0)
62
63 label = Gtk.Label(os.path.basename(i)) 83 label = Gtk.Label(os.path.basename(i))
64 hbox.pack_start(label, True, False, 0) 84 hbox.pack_start(label, True, False, 0)
65 85
66 listbox.add(row) 86 listbox.add(row)
67 listbox.show_all()
68
69 # Options
70 separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
71 self.main_box.pack_start(separator, True, True, 5)
72
73 hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
74 self.main_box.pack_start(hbox, True, True, 0)
75 label = Gtk.Label(xalign=0)
76 label.set_markup("Lightweight mode (only remove <i>some</i> metadata)")
77 hbox.pack_start(label, False, True, 0)
78 hbox.pack_start(Gtk.Switch(), False, True, 0)
79 87
80 self.window.show_all()
81
82 def error_window(self, items):
83 self.window.remove(self.main_box)
84
85 box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
86 self.window.add(box)
87
88 # Disclaimer
89 box.pack_start(Gtk.Label("Could not remove metadata from the following items:",
90 xalign=0), True, True, 0)
91
92 # List of failed files
93 listbox = Gtk.ListBox()
94 box.pack_start(listbox, True, True, 0)
95 listbox.set_selection_mode(Gtk.SelectionMode.NONE)
96 for i in items:
97 listbox.add(Gtk.Label(os.path.basename(i), xalign=0))
98 listbox.show_all() 88 listbox.show_all()
89 window.show_all()
99 90
100 self.window.show_all()
101 self.remove.hide()
102 91
103 def cancel_btn(self, button): 92 @staticmethod
104 self.window.close() 93 def __validate(f):
105 94 if f.get_uri_scheme() != "file" or f.is_directory():
106 def remove_btn(self, button):
107 failed = []
108 for i in self.items:
109 p, _ = parser_factory.get_parser(i)
110 if p is not None and p.remove_all():
111 continue
112 failed.append(i)
113
114 # Everything went the right way, exit
115 if not len(failed):
116 self.window.close()
117
118 self.error_window(failed)
119
120class ColumnExtension(GObject.GObject, Nautilus.MenuProvider):
121 def __validate(self, file):
122 if file.get_uri_scheme() != "file" or file.is_directory():
123 return False 95 return False
124 if not file.can_write(): 96 elif not f.can_write():
125 return False 97 return False
126 return True 98 return True
127 99
128 def menu_activate_cb(self, menu, files): 100 def __cb_menu_activate(self, menu, files):
129 items = list(map(lambda x: unquote(x.get_uri()[7:]), files)) 101 self.failed_items = list()
130 StatusWindow(items) 102 for f in files:
103 if not self.__validate(f):
104 self.failed_items.append((f.get_name(), None))
105 continue
106
107 fname = unquote(f.get_uri()[7:])
108 ret, mtype = Mat2Wrapper(fname).remove_metadata()
109 if not ret:
110 self.failed_items.append((f.get_name(), mtype))
111
112 if len(self.failed_items):
113 self.notify()
131 114
132 def get_background_items(self, window, file): 115 def get_background_items(self, window, file):
133 """ https://bugzilla.gnome.org/show_bug.cgi?id=784278 """ 116 """ https://bugzilla.gnome.org/show_bug.cgi?id=784278 """
@@ -144,6 +127,6 @@ class ColumnExtension(GObject.GObject, Nautilus.MenuProvider):
144 label="Remove metadata", 127 label="Remove metadata",
145 tip="Remove metadata" 128 tip="Remove metadata"
146 ) 129 )
147 item.connect('activate', self.menu_activate_cb, files) 130 item.connect('activate', self.__cb_menu_activate, files)
148 131
149 return [item] 132 return [item]