diff options
| author | jvoisin | 2013-04-27 16:39:45 +0200 |
|---|---|---|
| committer | jvoisin | 2013-04-27 16:39:45 +0200 |
| commit | df3a1b246031fa06adecf7dbf6e54600b20b10ba (patch) | |
| tree | 4697db0080ef9a72b8506579e8440d8bb332c36c /mat-gui | |
| parent | 3e2670ea9a87ce55f5308b0d5343bd49aab25c0b (diff) | |
The GUI now use glade !
Warning: this is a pretty huge commit
Not everything is ported now, but this is
already a big step.
Diffstat (limited to 'mat-gui')
| -rwxr-xr-x | mat-gui | 573 |
1 files changed, 211 insertions, 362 deletions
| @@ -5,12 +5,10 @@ | |||
| 5 | Metadata anonymisation toolkit - GUI edition | 5 | Metadata anonymisation toolkit - GUI edition |
| 6 | ''' | 6 | ''' |
| 7 | 7 | ||
| 8 | import gi | 8 | from gi.repository import GObject, Gtk |
| 9 | from gi.repository import GObject | 9 | from gi.repository import Gdk, GdkPixbuf |
| 10 | from gi.repository import Gtk, Gdk, GdkPixbuf | ||
| 11 | 10 | ||
| 12 | import gettext | 11 | import gettext |
| 13 | #import locale | ||
| 14 | import logging | 12 | import logging |
| 15 | import os | 13 | import os |
| 16 | import sys | 14 | import sys |
| @@ -21,24 +19,21 @@ import urllib2 | |||
| 21 | from MAT import mat | 19 | from MAT import mat |
| 22 | from MAT import strippers | 20 | from MAT import strippers |
| 23 | 21 | ||
| 24 | |||
| 25 | logging.basicConfig(level=mat.LOGGING_LEVEL) | 22 | logging.basicConfig(level=mat.LOGGING_LEVEL) |
| 26 | 23 | ||
| 27 | 24 | ||
| 28 | class CFile(object): | 25 | class CFile(GObject.Object): |
| 29 | ''' | 26 | ''' |
| 30 | Contain the "parser" class of the file "filename" | 27 | Contain the "parser" class of the file "filename" |
| 31 | This class exist just to be "around" my parser.Generic_parser class, | 28 | This class exist just to be "around" my parser.Generic_parser class, |
| 32 | since the Gtk.ListStore does not accept it. | 29 | since the Gtk.ListStore does not accept it because it does not |
| 30 | extends Gobject.Object | ||
| 33 | ''' | 31 | ''' |
| 34 | def __init__(self, filename, backup, **kwargs): | 32 | def __init__(self, filename, backup, **kwargs): |
| 35 | try: | 33 | self.file = mat.create_class_file(filename, backup, **kwargs) |
| 36 | self.file = mat.create_class_file(filename, backup, **kwargs) | ||
| 37 | except: | ||
| 38 | self.file = None | ||
| 39 | 34 | ||
| 40 | 35 | ||
| 41 | class GUI: | 36 | class GUI(object): |
| 42 | ''' | 37 | ''' |
| 43 | Main GUI class | 38 | Main GUI class |
| 44 | ''' | 39 | ''' |
| @@ -50,204 +45,101 @@ class GUI: | |||
| 50 | self.pdf_quality = False | 45 | self.pdf_quality = False |
| 51 | 46 | ||
| 52 | # Main window | 47 | # Main window |
| 53 | self.window = Gtk.Window() | 48 | self.builder = Gtk.Builder() |
| 54 | self.window.set_title('Metadata Anonymisation Toolkit') | 49 | self.builder.add_from_file(os.path.join(mat.get_datadir(), 'mat.ui')) |
| 55 | self.window.connect('destroy', Gtk.main_quit) | 50 | self.builder.connect_signals(self) |
| 56 | self.window.set_default_size(800, 600) | 51 | |
| 57 | self.logo = mat.get_logo() | 52 | self.logo = mat.get_logo() |
| 58 | icon = GdkPixbuf.Pixbuf.new_from_file_at_size(self.logo, 50, 50) | 53 | icon = GdkPixbuf.Pixbuf.new_from_file_at_size(self.logo, 50, 50) |
| 59 | self.window.set_icon(icon) | ||
| 60 | |||
| 61 | self.accelerator = Gtk.AccelGroup() | ||
| 62 | self.window.add_accel_group(self.accelerator) | ||
| 63 | |||
| 64 | vbox = Gtk.VBox() | ||
| 65 | self.window.add(vbox) | ||
| 66 | |||
| 67 | menubar = self.__create_menu() | ||
| 68 | toolbar = self.__create_toolbar() | ||
| 69 | content = Gtk.ScrolledWindow() | ||
| 70 | content.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) | ||
| 71 | vbox.pack_start(menubar, False, True, 0) | ||
| 72 | vbox.pack_start(toolbar, False, True, 0) | ||
| 73 | vbox.pack_start(content, True, True, 0) | ||
| 74 | |||
| 75 | # parser.class - name - path - type - cleaned | ||
| 76 | self.liststore = Gtk.ListStore(object, str, str, str, str, str) | ||
| 77 | |||
| 78 | self.treeview = Gtk.TreeView(model=self.liststore) | ||
| 79 | self.treeview.set_search_column(1) # filename column is searchable | ||
| 80 | self.treeview.set_rules_hint(True) # alternate colors for rows | ||
| 81 | self.treeview.set_rubber_banding(True) # mouse selection | ||
| 82 | self.treeview.connect("key_press_event", self.treeview_keyboard_event) | ||
| 83 | self.treeview.connect('row-activated', self.__popup_metadata) | ||
| 84 | self.treeview.connect('drag_data_received', | ||
| 85 | self.__on_drag_data_received) | ||
| 86 | self.treeview.drag_dest_set(Gtk.DestDefaults.MOTION | | ||
| 87 | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, | ||
| 88 | [], Gdk.DragAction.COPY) | ||
| 89 | targets = Gtk.TargetList.new([]) | ||
| 90 | targets.add_uri_targets(80) | ||
| 91 | self.treeview.drag_dest_set_target_list(targets) | ||
| 92 | self.__add_columns() | ||
| 93 | self.selection = self.treeview.get_selection() | ||
| 94 | self.selection.set_mode(Gtk.SelectionMode.MULTIPLE) | ||
| 95 | |||
| 96 | content.add(self.treeview) | ||
| 97 | |||
| 98 | self.statusbar = Gtk.Statusbar() | ||
| 99 | self.statusbar.push(1, _('Ready')) | ||
| 100 | vbox.pack_start(self.statusbar, False, False, 0) | ||
| 101 | 54 | ||
| 102 | self.window.show_all() | 55 | self.window = self.builder.get_object('MainWindow') |
| 103 | 56 | self.window.set_icon(icon) | |
| 104 | def __create_toolbar(self): | 57 | self.liststore = self.builder.get_object('Liststore') |
| 105 | ''' | 58 | ''' The liststore contains: |
| 106 | Returns a vbox object, which contains a toolbar with buttons | 59 | 0: a CFile instance which represent the file |
| 60 | 1: the file's path | ||
| 61 | 2: the file's name | ||
| 62 | 3: the file's mimetype | ||
| 63 | 4: the file's state (translated string) | ||
| 64 | 5: the cleaned copy's path | ||
| 65 | 6: the file's state (-1: unknown, 0: clean, 1: dirty) | ||
| 107 | ''' | 66 | ''' |
| 108 | toolbar = Gtk.Toolbar() | ||
| 109 | 67 | ||
| 110 | toolbutton = Gtk.ToolButton(Gtk.STOCK_ADD) | 68 | self.treeview = self.builder.get_object('Treeview') |
| 111 | toolbutton.set_label(_('Add')) | 69 | self.treeview.get_column(4).set_visible(self.backup) |
| 112 | toolbutton.connect('clicked', self.__add_files) | ||
| 113 | toolbutton.set_tooltip_text(_('Add files')) | ||
| 114 | toolbar.add(toolbutton) | ||
| 115 | 70 | ||
| 116 | toolbutton = Gtk.ToolButton(Gtk.STOCK_CLEAR) | 71 | self.statusbar = self.builder.get_object('Statusbar') |
| 117 | toolbutton.set_label(_('Clean')) | 72 | self.statusbar.push(1, _('Ready')) |
| 118 | toolbutton.connect('clicked', self.__process_files, self.__mat_clean) | ||
| 119 | toolbutton.set_tooltip_text(_('Clean selected files')) | ||
| 120 | toolbar.add(toolbutton) | ||
| 121 | |||
| 122 | toolbutton = Gtk.ToolButton(Gtk.STOCK_FIND) | ||
| 123 | toolbutton.set_label(_('Check')) | ||
| 124 | toolbutton.connect('clicked', self.__process_files, self.__mat_check) | ||
| 125 | toolbutton.set_tooltip_text(_('Check selected files for harmful meta')) | ||
| 126 | toolbar.add(toolbutton) | ||
| 127 | 73 | ||
| 128 | toolbutton = Gtk.ToolButton(stock_id=Gtk.STOCK_REMOVE) | 74 | self.__init_supported_popup() |
| 129 | toolbutton.set_label(_('Clear the filelist')) | 75 | self.__set_drag_treeview() |
| 130 | toolbutton.connect('clicked', lambda x: self.liststore.clear()) | ||
| 131 | toolbutton.set_tooltip_text(_('Clear the filelist')) | ||
| 132 | toolbar.add(toolbutton) | ||
| 133 | 76 | ||
| 134 | toolbutton = Gtk.ToolButton(stock_id=Gtk.STOCK_QUIT) | 77 | self.window.show_all() |
| 135 | toolbutton.set_label(_('Quit')) | ||
| 136 | toolbutton.connect('clicked', Gtk.main_quit) | ||
| 137 | toolbar.add(toolbutton) | ||
| 138 | 78 | ||
| 139 | vbox = Gtk.VBox(spacing=3) | 79 | def __init_supported_popup(self): |
| 140 | vbox.pack_start(toolbar, False, False, 0) | 80 | ''' Initialize the "supported formats" popup ''' |
| 141 | return vbox | 81 | self.supported_dict = mat.XMLParser() |
| 82 | parser = xml.sax.make_parser() | ||
| 83 | parser.setContentHandler(self.supported_dict) | ||
| 84 | path = os.path.join(mat.get_datadir(), 'FORMATS') | ||
| 85 | with open(path, 'r') as xmlfile: | ||
| 86 | parser.parse(xmlfile) | ||
| 142 | 87 | ||
| 143 | def __add_columns(self): | 88 | supported_cbox = self.builder.get_object('supported_cbox') |
| 144 | ''' | 89 | store = Gtk.ListStore(int, str) |
| 145 | Create the columns, and add them to the treeview | 90 | for i, j in enumerate(self.supported_dict.list): |
| 146 | ''' | 91 | store.append([i, j['name']]) |
| 147 | colname = [_('Path'), _('Filename'), _('Mimetype'), _('State'), | 92 | supported_cbox.set_model(store) |
| 148 | _('Cleaned file')] | 93 | supported_cbox.set_active(0) |
| 149 | 94 | ||
| 150 | for i, j in enumerate(colname, 1): | 95 | self.builder.get_object('supported_metadata').set_buffer(Gtk.TextBuffer()) |
| 151 | filename_column = Gtk.CellRendererText() | 96 | self.builder.get_object('supported_remaining').set_buffer(Gtk.TextBuffer()) |
| 152 | column = Gtk.TreeViewColumn(j, filename_column, text=i) | 97 | self.builder.get_object('supported_method').set_buffer(Gtk.TextBuffer()) |
| 153 | column.set_sort_column_id(i) | ||
| 154 | column.set_resizable(True) # column is resizeable | ||
| 155 | self.treeview.append_column(column) | ||
| 156 | 98 | ||
| 157 | def __create_menu_item(self, name, func, menu, pix, shortcut): | 99 | self.cb_update_supported_popup(supported_cbox) # to initially fill the dialog |
| 158 | ''' | ||
| 159 | Create a MenuItem() like Preferences, Quit, Add, Clean, ... | ||
| 160 | ''' | ||
| 161 | item = Gtk.ImageMenuItem() | ||
| 162 | if shortcut: | ||
| 163 | key, mod = Gtk.accelerator_parse(shortcut) | ||
| 164 | item.add_accelerator('activate', self.accelerator, | ||
| 165 | key, mod, Gtk.AccelFlags.VISIBLE) | ||
| 166 | picture = Gtk.Image() | ||
| 167 | picture.set_from_stock(pix, Gtk.IconSize.MENU) | ||
| 168 | item.set_image(picture) | ||
| 169 | item.set_label('_' + name) | ||
| 170 | item.set_use_underline(True) | ||
| 171 | item.connect('activate', func) | ||
| 172 | menu.append(item) | ||
| 173 | 100 | ||
| 174 | def __create_sub_menu(self, name, menubar): | 101 | def __set_drag_treeview(self): |
| 175 | ''' | 102 | ''' Setup the drag'n'drop handling by the treeview ''' |
| 176 | Create a submenu like File, Edit, Clean, ... | 103 | self.treeview.drag_dest_set(Gtk.DestDefaults.MOTION | |
| 177 | ''' | 104 | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, |
| 178 | submenu = Gtk.Menu() | 105 | [], Gdk.DragAction.COPY) |
| 179 | menuitem = Gtk.MenuItem() | 106 | targets = Gtk.TargetList.new([]) |
| 180 | menuitem.set_submenu(submenu) | 107 | targets.add_uri_targets(80) |
| 181 | menuitem.set_label('_' + name) | 108 | self.treeview.drag_dest_set_target_list(targets) |
| 182 | menuitem.set_use_underline(True) | ||
| 183 | menubar.append(menuitem) | ||
| 184 | return submenu | ||
| 185 | 109 | ||
| 186 | def __create_menu(self): | 110 | def cb_update_supported_popup(self, w): |
| 187 | ''' | 111 | ''' |
| 188 | Return a MenuBar | 112 | Fill GtkEntries of the supported_format_popups |
| 113 | with corresponding data. | ||
| 189 | ''' | 114 | ''' |
| 190 | menubar = Gtk.MenuBar() | 115 | i = w.get_active_iter() |
| 191 | 116 | index, _ = w.get_model()[i] | |
| 192 | file_menu = self.__create_sub_menu(_('Files'), menubar) | 117 | support = self.builder.get_object('supported_support') |
| 193 | self.__create_menu_item(_('Add files'), self.__add_files, file_menu, | 118 | support.set_text(self.supported_dict.list[index]['support']) |
| 194 | Gtk.STOCK_ADD, '<Control>O') | 119 | metadata = self.builder.get_object('supported_metadata').get_buffer() |
| 195 | self.__create_menu_item(_('Quit'), Gtk.main_quit, file_menu, | 120 | metadata.set_text(self.supported_dict.list[index]['metadata']) |
| 196 | Gtk.STOCK_QUIT, '<Control>Q') | 121 | method = self.builder.get_object('supported_method').get_buffer() |
| 122 | method.set_text(self.supported_dict.list[index]['method']) | ||
| 123 | remaining = self.builder.get_object('supported_remaining').get_buffer() | ||
| 124 | remaining.set_text(self.supported_dict.list[index]['remaining']) | ||
| 197 | 125 | ||
| 198 | edit_menu = self.__create_sub_menu(_('Edit'), menubar) | 126 | def cb_treeview_keyboard_event(self, widget, event): |
| 199 | self.__create_menu_item(_('Clear the filelist'), | ||
| 200 | lambda x: self.liststore.clear(), edit_menu, Gtk.STOCK_REMOVE, | ||
| 201 | None) | ||
| 202 | self.__create_menu_item(_('Preferences'), self.__preferences, | ||
| 203 | edit_menu, Gtk.STOCK_PREFERENCES, '<Control>P') | ||
| 204 | |||
| 205 | process_menu = self.__create_sub_menu(_('Process'), menubar) | ||
| 206 | item = Gtk.ImageMenuItem() | ||
| 207 | key, mod = Gtk.accelerator_parse('<Control>L') | ||
| 208 | item.add_accelerator('activate', self.accelerator, | ||
| 209 | key, mod, Gtk.AccelFlags.VISIBLE) | ||
| 210 | picture = Gtk.Image() | ||
| 211 | picture.set_from_stock(Gtk.STOCK_CLEAR, Gtk.IconSize.MENU) | ||
| 212 | item.set_image(picture) | ||
| 213 | item.set_label(_('Clean')) | ||
| 214 | item.connect('activate', self.__process_files, self.__mat_clean) | ||
| 215 | process_menu.append(item) | ||
| 216 | |||
| 217 | item = Gtk.ImageMenuItem() | ||
| 218 | key, mod = Gtk.accelerator_parse('<Control>h') | ||
| 219 | item.add_accelerator('activate', self.accelerator, | ||
| 220 | key, mod, Gtk.AccelFlags.VISIBLE) | ||
| 221 | picture = Gtk.Image() | ||
| 222 | picture.set_from_stock(Gtk.STOCK_FIND, Gtk.IconSize.MENU) | ||
| 223 | item.set_image(picture) | ||
| 224 | item.set_label(_('Check')) | ||
| 225 | item.connect('activate', self.__process_files, self.__mat_check) | ||
| 226 | process_menu.append(item) | ||
| 227 | |||
| 228 | help_menu = self.__create_sub_menu(_('Help'), menubar) | ||
| 229 | self.__create_menu_item(_('Supported formats'), self.__supported, | ||
| 230 | help_menu, Gtk.STOCK_INFO, False) | ||
| 231 | self.__create_menu_item(_('About'), self.__about, help_menu, | ||
| 232 | Gtk.STOCK_ABOUT, False) | ||
| 233 | |||
| 234 | return menubar | ||
| 235 | |||
| 236 | def treeview_keyboard_event(self, widget, event): | ||
| 237 | ''' | 127 | ''' |
| 238 | Remove selected files from the treeview | 128 | Remove selected files from the treeview |
| 239 | when the use hit the 'suppr' key | 129 | when the use hit the 'suppr' key |
| 240 | ''' | 130 | ''' |
| 241 | if Gdk.keyval_name(event.keyval) == "Delete": | 131 | if Gdk.keyval_name(event.keyval) == "Delete": |
| 242 | rows = [] | 132 | rows = [] |
| 243 | self.selection.selected_foreach( | 133 | self.treeview.get_selection().selected_foreach( |
| 244 | lambda model, path, iter: rows.append(iter)) | 134 | lambda model, path, iter: rows.append(iter)) |
| 245 | [self.liststore.remove(i) for i in rows] | 135 | [self.liststore.remove(i) for i in rows] |
| 246 | 136 | ||
| 247 | def __add_files(self, button): | 137 | def cb_close_application(self, _): |
| 248 | ''' | 138 | ''' Close the application ''' |
| 249 | Add the files chosed by the filechoser ("Add" button) | 139 | Gtk.main_quit() |
| 250 | ''' | 140 | |
| 141 | def cb_add_files(self, button): | ||
| 142 | ''' Add the files chosed by the filechoser ("Add" button) ''' | ||
| 251 | chooser = Gtk.FileChooserDialog(title=_('Choose files'), | 143 | chooser = Gtk.FileChooserDialog(title=_('Choose files'), |
| 252 | parent=self.window, action=Gtk.FileChooserAction.OPEN, | 144 | parent=self.window, action=Gtk.FileChooserAction.OPEN, |
| 253 | buttons=(Gtk.STOCK_OK, 0, Gtk.STOCK_CANCEL, 1)) | 145 | buttons=(Gtk.STOCK_OK, 0, Gtk.STOCK_CANCEL, 1)) |
| @@ -259,8 +151,8 @@ class GUI: | |||
| 259 | all_filter.add_pattern('*') | 151 | all_filter.add_pattern('*') |
| 260 | chooser.add_filter(all_filter) | 152 | chooser.add_filter(all_filter) |
| 261 | 153 | ||
| 262 | supported_filter = Gtk.FileFilter() | ||
| 263 | # filter that shows only supported formats | 154 | # filter that shows only supported formats |
| 155 | supported_filter = Gtk.FileFilter() | ||
| 264 | [supported_filter.add_mime_type(i) for i in strippers.STRIPPERS.keys()] | 156 | [supported_filter.add_mime_type(i) for i in strippers.STRIPPERS.keys()] |
| 265 | supported_filter.set_name(_('Supported files')) | 157 | supported_filter.set_name(_('Supported files')) |
| 266 | chooser.add_filter(supported_filter) | 158 | chooser.add_filter(supported_filter) |
| @@ -269,58 +161,24 @@ class GUI: | |||
| 269 | 161 | ||
| 270 | if not response: # Gtk.STOCK_OK | 162 | if not response: # Gtk.STOCK_OK |
| 271 | filenames = chooser.get_filenames() | 163 | filenames = chooser.get_filenames() |
| 272 | task = self.populate(filenames) | 164 | GObject.idle_add(self.populate(filenames).next) # asynchrone processing |
| 273 | GObject.idle_add(task.next) # asynchrone processing | ||
| 274 | chooser.destroy() | 165 | chooser.destroy() |
| 275 | 166 | ||
| 276 | def populate(self, filenames): | 167 | def cb_popup_metadata(self, widget, row, col): |
| 277 | ''' | ||
| 278 | Append selected files by add_file to the self.liststore | ||
| 279 | ''' | ||
| 280 | not_supported = [] | ||
| 281 | for filename in filenames: # filenames : all selected files/folders | ||
| 282 | if os.path.isdir(filename): # if "filename" is a directory | ||
| 283 | for root, dirs, files in os.walk(filename): | ||
| 284 | for item in files: | ||
| 285 | path_to_file = os.path.join(root, item) | ||
| 286 | if self.__add_file_to_treeview(path_to_file): | ||
| 287 | not_supported.append(item) | ||
| 288 | else: # filename is a regular file | ||
| 289 | if self.__add_file_to_treeview(filename): | ||
| 290 | not_supported.append(filename) | ||
| 291 | yield True | ||
| 292 | if not_supported: | ||
| 293 | self.__popup_non_supported(not_supported) | ||
| 294 | yield False | ||
| 295 | |||
| 296 | def __add_file_to_treeview(self, filename): | ||
| 297 | ''' | ||
| 298 | Add a file to the list if it's format is supported | ||
| 299 | ''' | ||
| 300 | if not os.path.isfile(filename): | ||
| 301 | # if filename does not exist | ||
| 302 | return False | ||
| 303 | |||
| 304 | cf = CFile(filename, self.backup, add2archive=self.add2archive, low_pdf_quality=self.pdf_quality) | ||
| 305 | if cf.file: # if the file is supported by the mat | ||
| 306 | self.liststore.append([cf, os.path.dirname(cf.file.filename) + os.path.sep, | ||
| 307 | cf.file.basename, cf.file.mime, _('unknow'), 'None']) | ||
| 308 | return False | ||
| 309 | return True | ||
| 310 | |||
| 311 | def __popup_metadata(self, widget, row, col): | ||
| 312 | ''' | 168 | ''' |
| 313 | Popup that display on double-clic | 169 | Popup that display on double-clic |
| 314 | metadata from a file | 170 | metadata from a file |
| 171 | |||
| 172 | FIXME: use a prettier GUI ? | ||
| 315 | ''' | 173 | ''' |
| 316 | label = '<b>%s</b>\'s metadatas:\n' % self.liststore[row][1] | 174 | label = '<b>%s</b>\'s metadatas:\n' % self.liststore[row][1] |
| 317 | meta = '' | 175 | meta = '' |
| 318 | if self.liststore[row][4] == _('Clean') or\ | 176 | if not self.liststore[row][6] or self.liststore[row][0].file.is_clean(): |
| 319 | self.liststore[row][0].file.is_clean(): | ||
| 320 | meta = 'No metadata found' | 177 | meta = 'No metadata found' |
| 321 | self.liststore[row][4] = _('Clean') | 178 | self.liststore[row][4] = _('Clean') |
| 322 | else: | 179 | else: |
| 323 | self.liststore[row][4] = _('Dirty') | 180 | self.liststore[row][4] = _('Dirty') |
| 181 | self.liststore[row][6] = 1 | ||
| 324 | iterator = self.liststore[row][0].file.get_meta().iteritems() | 182 | iterator = self.liststore[row][0].file.get_meta().iteritems() |
| 325 | for i, j in iterator: | 183 | for i, j in iterator: |
| 326 | name = '-<b>' + str(i) + '</b> : ' | 184 | name = '-<b>' + str(i) + '</b> : ' |
| @@ -345,45 +203,8 @@ class GUI: | |||
| 345 | if click: | 203 | if click: |
| 346 | w.destroy() | 204 | w.destroy() |
| 347 | 205 | ||
| 348 | def __popup_non_supported(self, filelist): | 206 | def cb_about_popup(self, button): |
| 349 | ''' | 207 | ''' About popup ''' |
| 350 | Popup that warn the user about the unsupported files | ||
| 351 | that he want to process | ||
| 352 | ''' | ||
| 353 | dialog = Gtk.Dialog(title=_('Not-supported'), parent=self.window, | ||
| 354 | flags=0, buttons=(Gtk.STOCK_OK, 0)) | ||
| 355 | vbox = Gtk.VBox(spacing=5) | ||
| 356 | dialog.get_content_area().pack_start(vbox, True, True, 0) | ||
| 357 | store = Gtk.ListStore(str, str) | ||
| 358 | |||
| 359 | # append filename - mimetype to the store | ||
| 360 | #FIXME : I'm ugly | ||
| 361 | for item in filelist: | ||
| 362 | mime = mimetypes.guess_type(item)[0] | ||
| 363 | if mime: | ||
| 364 | store.append([item, mime]) | ||
| 365 | else: | ||
| 366 | store.append([item, 'unknown']) | ||
| 367 | |||
| 368 | treeview = Gtk.TreeView(store) | ||
| 369 | vbox.pack_start(treeview, True, True, 0) | ||
| 370 | |||
| 371 | #create column | ||
| 372 | rendererText = Gtk.CellRendererText() | ||
| 373 | column = Gtk.TreeViewColumn(_('Filename'), rendererText, text=0) | ||
| 374 | treeview.append_column(column) | ||
| 375 | column = Gtk.TreeViewColumn(_('Mimetype'), rendererText, text=1) | ||
| 376 | treeview.append_column(column) | ||
| 377 | |||
| 378 | dialog.show_all() | ||
| 379 | click = dialog.run() | ||
| 380 | if not click: # Ok button | ||
| 381 | dialog.destroy() | ||
| 382 | |||
| 383 | def __about(self, button): | ||
| 384 | ''' | ||
| 385 | About popup | ||
| 386 | ''' | ||
| 387 | w = Gtk.AboutDialog() | 208 | w = Gtk.AboutDialog() |
| 388 | w.set_authors(['Julien (jvoisin) Voisin', ]) | 209 | w.set_authors(['Julien (jvoisin) Voisin', ]) |
| 389 | w.set_artists(['Marine Benoît', ]) | 210 | w.set_artists(['Marine Benoît', ]) |
| @@ -398,63 +219,28 @@ class GUI: | |||
| 398 | w.run() | 219 | w.run() |
| 399 | w.destroy() | 220 | w.destroy() |
| 400 | 221 | ||
| 401 | def __supported(self, button): | 222 | def cb_supported_popup(self, w): |
| 402 | ''' | 223 | ''' Show the "supported formats" popup''' |
| 403 | List the supported formats | 224 | dialog = self.builder.get_object('SupportedWindow') |
| 404 | ''' | ||
| 405 | dialog = Gtk.Dialog(_('Supported formats'), self.window, 0, | ||
| 406 | (Gtk.STOCK_CLOSE, 0)) | ||
| 407 | vbox = Gtk.VBox(spacing=5) | ||
| 408 | dialog.get_content_area().pack_start(vbox, True, True, 0) | ||
| 409 | |||
| 410 | label = Gtk.Label() | ||
| 411 | label.set_markup('<big><u>Supported fileformats</u></big>') | ||
| 412 | vbox.pack_start(label, True, True, 0) | ||
| 413 | |||
| 414 | #parsing xml | ||
| 415 | handler = mat.XMLParser() | ||
| 416 | parser = xml.sax.make_parser() | ||
| 417 | parser.setContentHandler(handler) | ||
| 418 | path = mat.get_formats() | ||
| 419 | with open(path, 'r') as xmlfile: | ||
| 420 | parser.parse(xmlfile) | ||
| 421 | |||
| 422 | def expander_callback(current): | ||
| 423 | ''' Close every expander except the current one ''' | ||
| 424 | for i in vbox.get_children()[1:]: # first child is a Gtk.Label | ||
| 425 | if i != current: | ||
| 426 | i.set_expanded(False) | ||
| 427 | |||
| 428 | for item in handler.list: # list of dict : one dict per format | ||
| 429 | # create one expander per format | ||
| 430 | # only if the format is supported | ||
| 431 | if item['mimetype'].split(',')[0] in strippers.STRIPPERS: | ||
| 432 | # some format have more than one mimetype | ||
| 433 | title = '%s (%s)' % (item['name'], item['extension']) | ||
| 434 | support = ('\t<b>%s</b> : %s' % ('support', item['support'])) | ||
| 435 | metadata = '\n\t<b>metadata</b> : ' + item['metadata'] | ||
| 436 | method = '\n\t<b>method</b> : ' + item['method'] | ||
| 437 | content = support + metadata + method | ||
| 438 | if item['support'] == 'partial': | ||
| 439 | content += '\n\t<b>remaining</b> : ' + item['remaining'] | ||
| 440 | |||
| 441 | expander = Gtk.Expander() | ||
| 442 | expander.set_label(title) | ||
| 443 | vbox.pack_start(expander, False, False, 0) | ||
| 444 | label = Gtk.Label() | ||
| 445 | label.set_markup(content) | ||
| 446 | expander.add(label) | ||
| 447 | expander.connect('activate', expander_callback) | ||
| 448 | |||
| 449 | dialog.show_all() | 225 | dialog.show_all() |
| 450 | click = dialog.run() | 226 | click = dialog.run() |
| 451 | if not click: # Close | 227 | if not click: # Close |
| 452 | dialog.destroy() | 228 | dialog.hide() |
| 453 | 229 | ||
| 454 | def __preferences(self, button): | 230 | def cb_clear_list(self, _): |
| 455 | ''' | 231 | ''' Clear the file list ''' |
| 456 | Preferences popup | 232 | self.liststore.clear() |
| 457 | ''' | 233 | |
| 234 | def cb_mat_check(self, button): | ||
| 235 | ''' Callback for checking files ''' | ||
| 236 | self.__process_files(self.mat_check) | ||
| 237 | |||
| 238 | def cb_mat_clean(self, button): | ||
| 239 | ''' Callback for cleaning files ''' | ||
| 240 | self.__process_files(self.mat_clean) | ||
| 241 | |||
| 242 | def cb_preferences_popup(self, button): | ||
| 243 | ''' Preferences popup ''' | ||
| 458 | dialog = Gtk.Dialog(_('Preferences'), self.window, 0, | 244 | dialog = Gtk.Dialog(_('Preferences'), self.window, 0, |
| 459 | (Gtk.STOCK_OK, 0)) | 245 | (Gtk.STOCK_OK, 0)) |
| 460 | dialog.set_resizable(False) | 246 | dialog.set_resizable(False) |
| @@ -472,8 +258,7 @@ class GUI: | |||
| 472 | force = Gtk.CheckButton(_('Force Clean'), False) | 258 | force = Gtk.CheckButton(_('Force Clean'), False) |
| 473 | force.set_active(self.force) | 259 | force.set_active(self.force) |
| 474 | force.connect('toggled', self.__invert, 'force') | 260 | force.connect('toggled', self.__invert, 'force') |
| 475 | force.set_tooltip_text(_('Do not check if already clean before \ | 261 | force.set_tooltip_text(_('Do not check if already clean before cleaning')) |
| 476 | cleaning')) | ||
| 477 | table.attach(force, 0, 1, 0, 1) | 262 | table.attach(force, 0, 1, 0, 1) |
| 478 | 263 | ||
| 479 | backup = Gtk.CheckButton(_('Backup'), False) | 264 | backup = Gtk.CheckButton(_('Backup'), False) |
| @@ -498,31 +283,14 @@ non-anonymised) file to output archive')) | |||
| 498 | 283 | ||
| 499 | hbox.show_all() | 284 | hbox.show_all() |
| 500 | if not dialog.run(): # Gtk.STOCK_OK | 285 | if not dialog.run(): # Gtk.STOCK_OK |
| 501 | for i in self.liststore: # update preferences | 286 | for file in self.liststore: # update preferences |
| 502 | i[0].backup = self.backup | 287 | file[0].backup = self.backup |
| 503 | i[0].add2archive = self.add2archive | 288 | file[0].add2archive = self.add2archive |
| 504 | if i[2].startswith('pdf'): | 289 | if file[2].startswith('pdf'): |
| 505 | i[0].pdf_quality = self.pdf_quality | 290 | file[0].pdf_quality = self.pdf_quality |
| 506 | dialog.destroy() | 291 | dialog.destroy() |
| 507 | 292 | ||
| 508 | def __invert(self, button, name): | 293 | def cb_drag_data_received(self, widget, context, |
| 509 | ''' | ||
| 510 | Invert a preference state | ||
| 511 | ''' | ||
| 512 | if name == 'force': | ||
| 513 | self.force = not self.force | ||
| 514 | elif name == 'backup': | ||
| 515 | self.backup = not self.backup | ||
| 516 | for line in xrange(len(self.liststore)): | ||
| 517 | # change the "backup" property of all files | ||
| 518 | self.liststore[line][0].file.backup = self.backup | ||
| 519 | self.treeview.get_column(4).set_visible(self.backup) | ||
| 520 | elif name == 'pdf_quality': | ||
| 521 | self.pdf_quality = not self.pdf_quality | ||
| 522 | elif name == 'add2archive': | ||
| 523 | self.add2archive = not self.add2archive | ||
| 524 | |||
| 525 | def __on_drag_data_received(self, widget, context, | ||
| 526 | x, y, selection, target_type, timestamp): | 294 | x, y, selection, target_type, timestamp): |
| 527 | ''' | 295 | ''' |
| 528 | This function is called when something is | 296 | This function is called when something is |
| @@ -530,7 +298,6 @@ non-anonymised) file to output archive')) | |||
| 530 | It basically add files. | 298 | It basically add files. |
| 531 | ''' | 299 | ''' |
| 532 | 300 | ||
| 533 | |||
| 534 | def clean_path(url): | 301 | def clean_path(url): |
| 535 | ''' | 302 | ''' |
| 536 | Since the dragged urls are ugly, | 303 | Since the dragged urls are ugly, |
| @@ -551,43 +318,126 @@ non-anonymised) file to output archive')) | |||
| 551 | task = self.populate(urls) | 318 | task = self.populate(urls) |
| 552 | GObject.idle_add(task.next) # asynchrone processing | 319 | GObject.idle_add(task.next) # asynchrone processing |
| 553 | 320 | ||
| 554 | def __process_files(self, button, func): | 321 | def __add_file_to_treeview(self, filename): |
| 322 | ''' | ||
| 323 | Add a file to the list if its format is supported | ||
| 324 | ''' | ||
| 325 | cf = CFile(filename, self.backup, add2archive=self.add2archive, | ||
| 326 | low_pdf_quality=self.pdf_quality) | ||
| 327 | if cf.file: # if the file is supported by the mat | ||
| 328 | self.liststore.append([cf, os.path.dirname(cf.file.filename) + os.path.sep, | ||
| 329 | cf.file.basename, cf.file.mime, _('unknow'), 'None', -1]) | ||
| 330 | return False | ||
| 331 | return True | ||
| 332 | |||
| 333 | def __process_files(self, func): | ||
| 555 | ''' | 334 | ''' |
| 556 | Launch the function "func" in a asynchrone way | 335 | Launch the function "func" in a asynchrone way |
| 557 | ''' | 336 | ''' |
| 558 | iterator = self.selection.get_selected_rows()[1] | 337 | iterator = self.treeview.get_selection().get_selected_rows()[1] |
| 559 | if not iterator: # if nothing is selected : select everything | 338 | if not iterator: # if nothing is selected : select everything |
| 560 | iterator = xrange(len(self.liststore)) | 339 | iterator = xrange(len(self.liststore)) |
| 561 | task = func(iterator) # launch func() in an asynchrone way | 340 | task = func(iterator) # launch func() in an asynchrone way |
| 562 | GObject.idle_add(task.next) | 341 | GObject.idle_add(task.next) |
| 563 | 342 | ||
| 564 | def __mat_check(self, iterator): | 343 | def __invert(self, button, name): |
| 344 | ''' Invert a preference state ''' | ||
| 345 | if name == 'force': | ||
| 346 | self.force = not self.force | ||
| 347 | elif name == 'pdf_quality': | ||
| 348 | self.pdf_quality = not self.pdf_quality | ||
| 349 | elif name == 'add2archive': | ||
| 350 | self.add2archive = not self.add2archive | ||
| 351 | elif name == 'backup': | ||
| 352 | self.backup = not self.backup | ||
| 353 | for line in xrange(len(self.liststore)): | ||
| 354 | # change the "backup" property of all files | ||
| 355 | self.liststore[line][0].file.backup = self.backup | ||
| 356 | self.treeview.get_column(4).set_visible(self.backup) | ||
| 357 | |||
| 358 | def populate(self, filenames): | ||
| 359 | ''' | ||
| 360 | Append selected files by add_file to the self.liststore | ||
| 361 | ''' | ||
| 362 | not_supported = [] | ||
| 363 | for filename in filenames: # filenames : all selected files/folders | ||
| 364 | if os.path.isdir(filename): # if "filename" is a directory | ||
| 365 | for root, dirs, files in os.walk(filename): | ||
| 366 | for item in files: | ||
| 367 | path_to_file = os.path.join(root, item) | ||
| 368 | if self.__add_file_to_treeview(path_to_file): | ||
| 369 | not_supported.append(item) | ||
| 370 | yield True | ||
| 371 | else: # filename is a regular file | ||
| 372 | if self.__add_file_to_treeview(filename): | ||
| 373 | not_supported.append(filename) | ||
| 374 | yield True | ||
| 375 | if not_supported: | ||
| 376 | self.__popup_non_supported(not_supported) | ||
| 377 | yield False | ||
| 378 | |||
| 379 | def __popup_non_supported(self, filelist): | ||
| 565 | ''' | 380 | ''' |
| 566 | Check if selected elements are clean | 381 | Popup that warn the user about the unsupported files |
| 382 | that he want to process | ||
| 567 | ''' | 383 | ''' |
| 384 | dialog = Gtk.Dialog(title=_('Not-supported'), parent=self.window, | ||
| 385 | flags=0, buttons=(Gtk.STOCK_OK, 0)) | ||
| 386 | vbox = Gtk.VBox(spacing=5) | ||
| 387 | dialog.get_content_area().pack_start(vbox, True, True, 0) | ||
| 388 | store = Gtk.ListStore(str, str) | ||
| 389 | |||
| 390 | # append filename - mimetype to the store | ||
| 391 | for item in filelist: | ||
| 392 | mime = mimetypes.guess_type(item)[0] | ||
| 393 | if mime: | ||
| 394 | store.append([item, mime]) | ||
| 395 | else: | ||
| 396 | store.append([item, 'unknown']) | ||
| 397 | |||
| 398 | treeview = Gtk.TreeView(store) | ||
| 399 | vbox.pack_start(treeview, True, True, 0) | ||
| 400 | |||
| 401 | #create column | ||
| 402 | rendererText = Gtk.CellRendererText() | ||
| 403 | column = Gtk.TreeViewColumn(_('Filename'), rendererText, text=0) | ||
| 404 | treeview.append_column(column) | ||
| 405 | column = Gtk.TreeViewColumn(_('Mimetype'), rendererText, text=1) | ||
| 406 | treeview.append_column(column) | ||
| 407 | |||
| 408 | dialog.show_all() | ||
| 409 | click = dialog.run() | ||
| 410 | if not click: # Ok button | ||
| 411 | dialog.destroy() | ||
| 412 | |||
| 413 | def mat_check(self, iterator): | ||
| 414 | ''' Check elements in iterator are clean ''' | ||
| 568 | for line in iterator: # for each file in selection | 415 | for line in iterator: # for each file in selection |
| 569 | self.statusbar.push(0, _('Checking %s...') % self.liststore[line][1]) | 416 | print self.liststore[line][0].file |
| 570 | if self.force or self.liststore[line][4] != _('Clean'): | 417 | logging.info('Checking %s' % self.liststore[line][2]) |
| 418 | self.statusbar.push(0, _('Checking %s...') % self.liststore[line][2]) | ||
| 419 | |||
| 420 | if self.force or self.liststore[line][6]: | ||
| 571 | if self.liststore[line][0].file.is_clean(): | 421 | if self.liststore[line][0].file.is_clean(): |
| 572 | string = _('Clean') | 422 | self.liststore[line][4] = _('Clean') |
| 423 | self.liststore[line][6] = 0 | ||
| 573 | else: | 424 | else: |
| 574 | string = _('Dirty') | 425 | self.liststore[line][4] = _('Dirty') |
| 575 | logging.info('%s is %s' % (self.liststore[line][1], string)) | 426 | self.liststore[line][6] = 1 |
| 576 | self.liststore[line][4] = string | 427 | logging.info('%s is %s' % (self.liststore[line][1], self.liststore[line][4])) |
| 577 | yield True | 428 | yield True |
| 578 | self.statusbar.push(0, _('Ready')) | 429 | self.statusbar.push(0, _('Ready')) |
| 579 | yield False | 430 | yield False |
| 580 | 431 | ||
| 581 | def __mat_clean(self, iterator): | 432 | def mat_clean(self, iterator): |
| 582 | ''' | 433 | ''' Clean elements in iterator ''' |
| 583 | Clean selected elements | ||
| 584 | ''' | ||
| 585 | for line in iterator: # for each file in selection | 434 | for line in iterator: # for each file in selection |
| 586 | logging.info('Cleaning %s' % self.liststore[line][1]) | 435 | logging.info('Cleaning %s' % self.liststore[line][2]) |
| 587 | self.statusbar.push(0, _('Cleaning %s...') % self.liststore[line][1]) | 436 | self.statusbar.push(0, _('Cleaning %s...') % self.liststore[line][2]) |
| 588 | if self.force or self.liststore[line][4] != _('Clean'): | 437 | if self.force or self.liststore[line][6]: |
| 589 | if self.liststore[line][0].file.remove_all(): | 438 | if self.liststore[line][0].file.remove_all(): |
| 590 | self.liststore[line][4] = _('Clean') | 439 | self.liststore[line][4] = _('Clean') |
| 440 | self.liststore[line][6] = 0 | ||
| 591 | if self.backup: # the backup copy state | 441 | if self.backup: # the backup copy state |
| 592 | self.liststore[line][5] = os.path.basename(self.liststore[line][0].file.output) | 442 | self.liststore[line][5] = os.path.basename(self.liststore[line][0].file.output) |
| 593 | yield True | 443 | yield True |
| @@ -597,7 +447,6 @@ non-anonymised) file to output archive')) | |||
| 597 | if __name__ == '__main__': | 447 | if __name__ == '__main__': |
| 598 | gettext.install('MAT', unicode=True) | 448 | gettext.install('MAT', unicode=True) |
| 599 | 449 | ||
| 600 | #Main | ||
| 601 | gui = GUI() | 450 | gui = GUI() |
| 602 | 451 | ||
| 603 | #Add files from command line | 452 | #Add files from command line |
