Updated translation
[findit] / src / files / search.py
index 0df7faf..9510194 100755 (executable)
@@ -3,30 +3,40 @@
 # vim: sw=4 ts=4 expandtab ai
 
 from os import walk
-from os.path import join, abspath, normcase, basename, isdir, getsize
+from os.path import join, abspath, normcase, isdir, getsize
 from heapq import nlargest
+from fnmatch import fnmatch
 
 from misc import size_hum_read, _
+from config import config
+
+OUTTYPES = [
+    ('out_table',  _('Table')),
+    ('out_diabar', _('Bar chart')),
+    ('out_diapie', _('Pie chart')),
+    ('out_diaold', _('Old chart')),
+]
 
 #==============================================================================
 
 class Control(object):
 
-    def __init__(self, ui, config):
-        self.config = config
-
-        self.present = eval(ui + '_Presentation(config, self.start_search)')
-        self.abstrac = Abstraction(self.config, self.present)
+    def __init__(self, ui, params):
+        self.present = eval(ui + '_Presentation(self.start_search, params)')
+        self.abstrac = Abstraction(self.present)
 
         self.toplevel = self.present.toplevel
 
-    def start_search(self, get_data, get_stopit):
+    def start_search(self, get_criteria, get_stopit):
         filelist = []
-        outtype, start_path, count = get_data()
-        search_func = self.abstrac.filegetter(start_path, get_stopit)
+        outtype, start_path, count, file_filter = get_criteria()
+        search_func = self.abstrac.filegetter(start_path, file_filter, get_stopit)
+
         for fsize, fpath in nlargest(count, search_func):
             filelist.append([int(fsize), fpath, size_hum_read(fsize)])
-        self.present.show_out_toplevel(None, outtype, filelist)
+
+        results = [filelist, start_path]
+        self.present.show_out_toplevel(outtype, results)
 
     def run(self):
         self.present.run()
@@ -35,13 +45,12 @@ class Control(object):
 
 class Abstraction(object):
 
-    def __init__(self, config, presentation):
-        self.ignore_dirs = config['ignore_dirs']
+    def __init__(self, presentation):
+        self.ignore_dirs = config['files']['ignore_dirs']
         self.presentation = presentation
 
-    def filegetter(self, startdir, get_stopit):
+    def filegetter(self, startdir, file_filter, get_stopit):
         """Generator of file sizes and paths based on os.walk."""
-
         # Walk across directory tree
         for dirpath, dirnames, fnames in walk(startdir):
             # Eliminate unnecessary directories
@@ -53,42 +62,48 @@ class Abstraction(object):
                         ignore_dirs.remove(ign_dir)
 
             for fname in fnames:
-                flpath = abspath(join(dirpath, fname))
-                self.presentation.show_current_status(flpath)
-
-                # Stop search via 'stopit' signal
-                stopit = get_stopit()
-                if stopit:
-                    stopit = False
-                    print 'Stopped'
-                    raise StopIteration
-                # Query only valid files
-                try:
-                    # Return results (bytesize, path)
-                    yield getsize(flpath), flpath
-                except OSError:
-                    continue
+                # Store only necessary files
+                for mask in file_filter:
+                    if fnmatch(fname, mask):
+                        # Crutch for non-unicode names
+                        #flpath = unicode(join(dirpath, fname), '1251')
+                        flpath = join(dirpath, fname)
+                        # Show current path
+                        self.presentation.show_current_status(flpath)
+                        # Stop search via 'stopit' signal
+                        stopit = get_stopit()
+                        if stopit:
+                            stopit = False
+                            print 'Stopped'
+                            raise StopIteration
+                        # Query only valid files
+                        try:
+                            # Return results (bytesize, path)
+                            yield getsize(flpath), flpath
+                        except OSError:
+                            continue
 
 #==============================================================================
 
 class Cli_Presentation(object):
-    def __init__(self, config, start_func):
+    def __init__(self, start_func, params):
         self.start_func = start_func
 
-        self.outtype = config['outtype']
-        self.start_path = config['start_path']
-        self.count = config['count']
+        self.outtype = params['outtype']
+        self.start_path = params['start_path']
+        self.count = params['count']
+        self.file_filter = params['file_filter'].split(';')
         self.stopit = False
 
         self.toplevel = None
 
     def get_data(self):
-        return self.outtype, self.start_path, int(self.count)
+        return self.outtype, self.start_path, int(self.count), self.file_filter
 
     def get_stopit(self):
         return False
 
-    def show_out_toplevel(self, _, outtype, results):
+    def show_out_toplevel(self, outtype, results):
         out_submodule = __import__('files.' + outtype, None, None, outtype)
         out_submodule.Cli_Presentation(results).toplevel
 
@@ -107,122 +122,184 @@ class Cli_Presentation(object):
 
 class Gtk_Presentation(object):
 
-    def __init__(self, config, start_func):
+    def __init__(self, start_func, __):
         import gtk
+        global gtk  # for show_current_status()
+        from misc import NotebookWCloseBtns
+
+        self.nb = NotebookWCloseBtns()
+        self.nb.notebook.set_scrollable(True)
+        self.nb.notebook.set_border_width(2)
 
-        self.config = config
+        #====================
+        # Notebook
+        #====================
 
+        # "Start path" label
+        self.path_label = gtk.Label(_('Path'))
         # "Start path" entry
         self.path_entry = gtk.Entry()
-        self.path_entry.set_text(self.config['start_path'])
+        self.path_entry.set_text(config['files']['start_path'])
+        # "Browse" button
+        self.browse_btn = gtk.Button('Browse...')
+        self.browse_btn.connect('clicked', self.browse_btn_clicked)
 
         # "Files quantity" label
         qty_label = gtk.Label(_('Files quantity'))
-
         # "Files quantity" spin
         self.qty_spin = gtk.SpinButton()
         self.qty_spin.set_numeric(True)
         self.qty_spin.set_range(0, 65536)
         self.qty_spin.set_increments(1, 10)
-        self.qty_spin.set_value(self.config['count'])
+        self.qty_spin.set_value(config['files']['count'])
+
+        # "Filter" label
+        filter_label = gtk.Label(_('Filter (semicolon separated)'))
+        # "Filter" entry
+        self.filter_entry = gtk.Entry()
+        self.filter_entry.set_text(config['files']['filter'])
+
+        # "Output" label
+        out_label = gtk.Label(_('Output'))
+        # Output selection
+        btn = gtk.RadioButton(None, OUTTYPES[0][1])
+        btn.set_name(OUTTYPES[0][0])
+        self.out_rbtns = []
+        self.out_rbtns.append(btn)
+        for name, label in OUTTYPES[1:]:
+            btn = gtk.RadioButton(self.out_rbtns[0], label)
+            btn.set_name(name)
+            self.out_rbtns.append(btn)
 
         # "Start" button
         self.start_btn = gtk.Button(_('Start'))
         self.start_btn.connect('released', self.start_btn_released, start_func)
-
         # "Stop" button
         self.stop_btn = gtk.Button(_('Stop'))
         self.stop_btn.set_sensitive(False)
         self.stop_btn.connect('clicked', self.stop_btn_clicked)
 
-        # Output selection
-        outtable_rbtn = gtk.RadioButton(None, _('Table'))
-        outtable_rbtn.set_name('outtable')
-        outdiagram_rbtn = gtk.RadioButton(outtable_rbtn, _('Diagram'))
-        outdiagram_rbtn.set_name('outdiagram')
-        out1_rbtn = gtk.RadioButton(outtable_rbtn, 'Another 1')
-        out1_rbtn.set_name('outanother1')
-        self.out_rbtns = [outtable_rbtn, outdiagram_rbtn, out1_rbtn]
-
-        hbox = gtk.HBox(False, 4)
-        hbox.pack_start(qty_label, False, False, 0)
-        hbox.pack_start(self.qty_spin, False, False, 0)
-        hbox.pack_start(self.start_btn, False, False, 0)
-        hbox.pack_start(self.stop_btn, False, False, 0)
-        for btn in reversed(self.out_rbtns):
-            hbox.pack_end(btn, False, False, 0)
+        path_hbox = gtk.HBox(False, 2)
+        path_hbox.pack_start(self.path_label, False, False, 4)
+        path_hbox.pack_start(self.path_entry, True, True, 0)
+        path_hbox.pack_start(self.browse_btn, False, False, 0)
+
+        qty_hbox = gtk.HBox(False, 2)
+        qty_hbox.pack_start(qty_label, False, False, 4)
+        qty_hbox.pack_start(self.qty_spin, False, False, 0)
+
+        filter_hbox = gtk.HBox(False, 2)
+        filter_hbox.pack_start(filter_label, False, False, 4)
+        filter_hbox.pack_start(self.filter_entry, True, True, 0)
+
+        out_hbox = gtk.HBox(False, 2)
+        out_hbox.pack_start(out_label, False, False, 4)
+        for btn in self.out_rbtns:
+            out_hbox.pack_start(btn, False, False, 0)
             # Activate radio button
-            if btn.get_name() == self.config['outtype']:
+            if btn.get_name() == config['outtype']:
                 btn.set_active(True)
 
+        control_hbox = gtk.HBox(True, 2)
+        control_hbox.pack_start(self.start_btn, True, True, 0)
+        control_hbox.pack_start(self.stop_btn, True, True, 0)
+
+        cr_vbox = gtk.VBox(False, 2)
+        cr_vbox.set_border_width(2)
+        cr_vbox.pack_start(path_hbox, False, False, 0)
+        cr_vbox.pack_start(qty_hbox, False, False, 0)
+        cr_vbox.pack_start(filter_hbox, False, False, 0)
+        cr_vbox.pack_start(out_hbox, False, False, 0)
+        cr_vbox.pack_end(control_hbox, False, False, 0)
+
+        self.nb.new_tab(cr_vbox, _('Criteria'), noclose=True)
+
+        #====================
+        # Others
+        #====================
+
         self.statusbar = gtk.Statusbar()
+        self.statusbar.set_has_resize_grip(False)
         self.context_id = self.statusbar.get_context_id('Current walked file')
 
-        self.vbox = gtk.VBox(False, 4)
-        self.vbox.pack_start(self.path_entry, False, False, 0)
-        self.vbox.pack_start(hbox, False, False, 0)
+        self.vbox = gtk.VBox()
+        self.vbox.pack_start(self.nb.notebook, True, True, 0)
         self.vbox.pack_end(self.statusbar, False, False, 0)
 
-        self.toplevel = self.vbox
-
-        # For importing gtk only once (lambda not work)
-        def show_current_status(current_path):
-            self.statusbar.push(self.context_id, current_path)
-            gtk.main_iteration()
-        self.show_current_status = show_current_status
+#        self.show_out_toplevel(config['outtype'], [(1, 'path', 'bytesize')])
 
-        self.show_out_toplevel(None, self.config['outtype'], [(1, 'path', 'bytesize')])
+        self.toplevel = self.vbox
 
     #=== Functions ============================================================
+    def browse_btn_clicked(self, btn):
+        """Open directory browser. "Browse" button clicked callback."""
+        dialog = gtk.FileChooserDialog(title=_('Choose directory'),
+                                       action='select-folder',
+                                       buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
+                                                gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
+        path = abspath(self.path_entry.get_text())
+        dialog.set_current_folder(path)
+        dialog.show_all()
+        response = dialog.run()
+        if response == gtk.RESPONSE_OK:
+            self.path_entry.set_text(dialog.get_filename())
+        dialog.destroy()
+
     def start_btn_released(self, btn, start_func):
+        """Start file search. Button "Go" activate callback."""
         self.stopit = False
         self.stop_btn.set_sensitive(True)
         self.start_btn.set_sensitive(False)
-        start_func(self.get_data, self.get_stopit)
+        start_func(self.get_criteria, self.get_stopit)
         self.stop_btn.set_sensitive(False)
         self.start_btn.set_sensitive(True)
 
     def stop_btn_clicked(self, widget):
+        """Stop search. "Stop" button clicked callback."""
         self.stopit = True
         self.stop_btn.set_sensitive(False)
         self.start_btn.set_sensitive(True)
 
-    def get_data(self):
+    def get_criteria(self):
+        """Pick search criteria from window."""
         for btn in self.out_rbtns:
             if btn.get_active():
-                out = btn.get_name()
-        return out, self.path_entry.get_text(), int(self.qty_spin.get_value())
+                out = {}
+                out['name'] = btn.get_name()
+                out['label'] = btn.get_label()
+        file_filter = self.filter_entry.get_text().split(';')
+        # If no filter - show all files
+        if file_filter == ['']:
+            file_filter = ['*.*']
+        return out, \
+            self.path_entry.get_text(), int(self.qty_spin.get_value()), \
+            file_filter
 
     def get_stopit(self):
         return self.stopit
 
+    def show_current_status(self, current_path):
+        """Show current walked path in statusbar and update window."""
+        self.statusbar.push(self.context_id, current_path)
+        gtk.main_iteration()
+
     def run(self):
         pass
 
     #=== Output type selecting ================================================
-    def show_out_toplevel(self, btn, outtype, results):
-        print 'Entering <' + outtype + '> output mode...'
-        out_submodule = __import__('files.' + outtype, None, None, outtype)
-
-        try:
-            self.out_toplevel.destroy()
-        except:
-            pass
-
+    def show_out_toplevel(self, outtype, results):
+        out_submodule = __import__('files.' + outtype['name'], None, None, outtype)
         self.out_toplevel = out_submodule.Gtk_Presentation(results).toplevel
-        self.vbox.add(self.out_toplevel)
-        self.out_toplevel.show_all()
+        self.nb.new_tab(self.out_toplevel, outtype['label'])
 ###        out_submodule.Gtk_Presentation().show_results(results)
 
 #==============================================================================
 
 class Hildon_Presentation(object):
 
-    def __init__(self, config, start_func):
+    def __init__(self, start_func):
         import gtk
         import hildon
 
-        self.config = config
-
     def run(self):
         pass