← Back to team overview

clicompanion-devs team mailing list archive

[Merge] lp:~dcaro/clicompanion/fix-910355 into lp:clicompanion

 

David Caro has proposed merging lp:~dcaro/clicompanion/fix-910355 into lp:clicompanion.

Requested reviews:
  CLI Companion Development Team (clicompanion-devs)
Related bugs:
  Bug #611141 in CLI Companion: "It is not obvious that "q" must be pressed to exit help (man pages)"
  https://bugs.launchpad.net/clicompanion/+bug/611141
  Bug #801906 in CLI Companion: "clicompanion crashes on start with ValueError in _get()"
  https://bugs.launchpad.net/clicompanion/+bug/801906
  Bug #909894 in CLI Companion: "Double click execution with user input drags command"
  https://bugs.launchpad.net/clicompanion/+bug/909894
  Bug #910249 in CLI Companion: "Warning window (when wrong params issued) not working properly"
  https://bugs.launchpad.net/clicompanion/+bug/910249
  Bug #910355 in CLI Companion: "Properties are not applied to the current terminal"
  https://bugs.launchpad.net/clicompanion/+bug/910355
  Bug #910360 in CLI Companion: "Incorrect .clicompanion2 file causes the program to crash"
  https://bugs.launchpad.net/clicompanion/+bug/910360
  Bug #910370 in CLI Companion: "Drag and drop when searching breaks the drag and drop"
  https://bugs.launchpad.net/clicompanion/+bug/910370
  Bug #910531 in CLI Companion: "Allowed empty user input"
  https://bugs.launchpad.net/clicompanion/+bug/910531
  Bug #910533 in CLI Companion: "In a fresh start, when executting the first command, the second gets executed instead"
  https://bugs.launchpad.net/clicompanion/+bug/910533

For more details, see:
https://code.launchpad.net/~dcaro/clicompanion/fix-910355/+merge/87218

Done a lot of improvements and code refactoring.

Also solved a lot of bugs.

The problem was that the bugs that I resolved one by one, where easily resolved together (also I resolved some other bugs that seemed easy).

Please do a full testing of the program, because a lot of things changed, and is possible that new bug may have been created. I tested myself and I will be testing it from now on, but two (or more than one) heads are better for doing so.


Also, taken back all the conflicting merge proposals, to avoid double merging of fixes.

Thanks,
David
-- 
https://code.launchpad.net/~dcaro/clicompanion/fix-910355/+merge/87218
Your team CLI Companion Development Team is requested to review the proposed merge of lp:~dcaro/clicompanion/fix-910355 into lp:clicompanion.
=== modified file 'clicompanion'
--- clicompanion	2011-12-31 12:33:51 +0000
+++ clicompanion	2012-01-02 01:28:26 +0000
@@ -6,7 +6,6 @@
 import sys
 from optparse import OptionParser
 
-from clicompanionlib.view import run
 
 share_dirs = os.environ.get('XDG_BASE_PDATA', 
                             '/usr/local/share:/usr/share').split(os.pathsep)
@@ -27,10 +26,12 @@
 
 parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.1")
 parser.add_option("-f", "--file", dest="filename",
-                  help="write report to FILE", metavar="FILE")
+                  help="Write report to FILE", metavar="FILE")
+parser.add_option("-c", "--cheatsheet", dest="cheatsheet",
+                  help="Read cheatsheet from FILE", metavar="FILE")
 parser.add_option("-q", "--quiet",
                   action="store_false", dest="verbose", default=True,
-                  help="don't print status messages to stdout")
+                  help="Don't print status messages to stdout")
 
 (options, args) = parser.parse_args()
 
@@ -62,4 +63,5 @@
 
 
 if __name__ == "__main__":
-    run()
+    from clicompanionlib.view import run
+    run( options )

=== modified file 'clicompanionlib/config.py'
--- clicompanionlib/config.py	2011-03-12 16:08:24 +0000
+++ clicompanionlib/config.py	2012-01-02 01:28:26 +0000
@@ -20,33 +20,244 @@
 #
 import os
 import ConfigParser
+import clicompanionlib.utils as utils
+from clicompanionlib.utils import dbg
+import collections
 
+CHEATSHEET = os.path.expanduser("~/.clicompanion2")
 CONFIGDIR = os.path.expanduser("~/.config/clicompanion/")
 CONFIGFILE = os.path.expanduser("~/.config/clicompanion/config")
-
-class Config(object):
-
-    ''' 
-    create configuration file
-    '''
-
-    def create_config(self):
-    
-        if not os.path.exists(CONFIGFILE):
-            os.makedirs(CONFIGDIR)
-            config = ConfigParser.ConfigParser()
-            # set a number of parameters
+CONFIG_ORIG = "/etc/clicompanion.d/clicompanion2.config"
+DEFAULTS = { "scrollb": '500',
+             "colorf": '#FFFFFF',
+             "colorb": '#000000',
+             "encoding": 'UTF-8',
+             "debug": 'False'}
+
+## To avoid parsing the config file each time, we store the loaded config here
+CONFIG = None
+
+def create_config(conffile=CONFIGFILE):
+    global CONFIG
+    config = CONFIG
+    configdir = conffile.rsplit(os.sep,1)[0]
+    if not os.path.exists(configdir):
+        try:
+            os.makedirs(configdir)
+        except Exception, e:
+            print _('Unable to create config at dir %s (%s)')%(configdir,e)
+            return False
+    # reuse the config if able
+    if not config:
+        config = ConfigParser.SafeConfigParser(DEFAULTS)
+        # set a number of parameters
+        if os.path.isfile(conffile):
+            config.read([conffile])
+        else:
             config.add_section("terminal")
-            config.set("terminal", "scrollb", 500)
-            config.set("terminal", "colorf", '#FFFFFF')
-            config.set("terminal", "colorb", '#000000')
-            config.set("terminal", "encoding", 'utf-8')
-            # Writing our configuration file
-            with open(CONFIGFILE, 'wb') as f:
-                config.write(f)
-            
-        else:
-            pass
-
-
-
+            for option, value in DEFAULTS.items():
+                config.set("terminal", option, value)
+        CONFIG = config
+    # Writing our configuration file
+    save_config(config, conffile)
+    print _("INFO: Created config file at %s.")%conffile
+    return config
+        
+
+def get_config_copy(config=None):
+    global CONFIG
+    if not config:
+        config = CONFIG
+    new_cfg = ConfigParser.SafeConfigParser(DEFAULTS)
+    for section in config.sections():
+        new_cfg.add_section(section)
+        for option in config.options(section):
+            new_cfg.set(section, option, config.get(section, option))
+    return new_cfg
+    
+
+def get_config(conffile=CONFIGFILE, confdir=CONFIGDIR):
+    global CONFIG
+    config = CONFIG
+    if not config:
+        dbg('Loading new config')
+        if not os.path.isfile(conffile):
+            config = create_config(conffile)
+        config = ConfigParser.SafeConfigParser(DEFAULTS)
+        config.add_section("terminal")
+        config.read([conffile])
+        CONFIG = config
+    else:
+        dbg('Reusing already loaded config')
+    return config
+
+
+def save_config(config, conffile=CONFIGFILE):
+    global CONFIG
+    dbg('Saving conffile at %s'%conffile)
+    with open(CONFIGFILE, 'wb') as f:
+        config.write(f)
+    CONFIG = config
+
+class Cheatsheet:
+    '''
+    comtainer class for the cheatsheet
+
+    Example of usage:
+    >>> c = config.Cheatsheet()
+    >>> c.load('/home/cascara/.clicompanion2')
+    >>> c[3]
+    ['uname -a', '', 'What kernel am I running\n']
+    >>> c.file
+    '/home/cascara/.clicompanion2'
+    >>> c[2]=[ 'mycmd', 'userui', 'desc' ]
+    >>> c[2]
+    ['mycmd', 'userui', 'desc']
+    >>> del c[2]
+    >>> c[2]
+    ['ps aux | grep ?', 'search string', 'Search active processes for search string\n']
+    >>> c.insert('cmd2','ui2','desc2',2)
+    >>> c[2]
+    ['cmd2', 'ui2', 'desc2']
+
+    '''
+    def __init__(self):
+        self.file = CHEATSHEET
+        self.commands = []
+
+    def __repr__(self):
+        return 'Config: %s - %s'%(self.file, self.commands)
+    
+    def load(self, cheatfile=None):
+        if not cheatfile:
+            self.file = CHEATSHEET
+            if not os.path.exists(CHEATSHEET):
+                if os.path.exists(CONFIG_ORIG):
+                    os.system ("cp %s %s" % (CONFIG_ORIG, CHEATSHEET))
+                else:
+                    # Oops! Looks like there's no default cheatsheet.
+                    # Then, create an empty cheatsheet.
+                    open(CHEATSHEET, 'w').close()
+        else:
+            self.file = cheatfile
+        try:
+            dbg('Reading cheatsheet from file %s'%self.file)
+            with open(self.file, 'r') as ch_fd:
+                ## try to detect if the line is a old fashines config line
+                ## (separated by ':'), when saved will rewrite it
+                no_tabs = True
+                some_colon = False
+                for line in ch_fd:
+                    line = line.strip()
+                    if not line:
+                        continue
+                    cmd, ui, desc = [ l.strip() for l in line.split('\t',2)] \
+                                        + ['',]*(3-len(line.split('\t',2)))
+                    if ':' in cmd:
+                        some_colon = True
+                    if ui or desc:
+                        no_tabs = False
+                    if cmd and [ cmd, ui, desc ] not in self.commands:
+                        self.commands.append([cmd, ui, desc])
+                        dbg('Adding command %s'%[cmd, ui, desc])
+                if no_tabs and some_colon:
+                    ## None of the commands had tabs, and all had ':' in the 
+                    ## cmd... most probably old config style
+                    print _("Detected old cheatsheet style at")\
+                            +" %s"%self.file+_(", parsing to new one.")
+                    for i in range(len(self.commands)):
+                        cmd, ui, desc = self.commands[i]
+                        cmd, ui, desc = [ l.strip() for l in cmd.split(':',2)] \
+                                        + ['',]*(3-len(cmd.split(':',2)))
+                        self.commands[i] = [cmd, ui, desc]
+                    self.save()
+        except IOError, e:
+            print _("Error while loading cheatfile")+" %s: %s"%(self.file, e)
+
+    def save(self, cheatfile=None):
+        '''
+        Saves the current config to the file cheatfile, or the file that was 
+        loaded.
+        NOTE: It does not overwrite the value self.file, that points to the file 
+        that was loaded
+        '''
+        if not cheatfile and self.file:
+            cheatfile = self.file
+        elif not cheatfile:
+            return False
+        try:
+            with open(cheatfile, 'wb') as ch_fd:
+                for command in self.commands:
+                    ch_fd.write('\t'.join(command)+'\n')
+        except IOError, e:
+            print _("Error writing cheatfile")+" %s: %s"%(cheatfile, e)
+            return False
+        return True
+
+    def __len__(self):
+        return len(self.commands)
+
+    def __getitem__(self, key):
+        return self.commands[key]
+
+    def __setitem__(self, key, value):
+        if not isinstance(value, collections.Iterable) or len(value) < 3:
+            raise ValueError('Value must be a container with three items, but got %s'%value)
+        if key < len(self.commands):
+            self.commands[key]=list(value)
+        else:
+            try:
+                self.insert(*value, pos=key)
+            except ValueError, e:
+                raise ValueError('Value must be a container with three items, but got %s'%value)
+
+    def __iter__(self):
+        for command in self.commands:
+            yield command
+
+    def insert(self, cmd, ui, desc, pos=None):
+        if not [cmd, ui, desc] in self.commands:
+            if not pos:
+                self.commands.append([cmd, ui, desc])
+            else:
+                self.commands.insert(pos, [cmd, ui, desc])
+
+    def append(self, cmd, ui, desc):
+        self.insert(cmd, ui, desc)
+
+    def index(self, cmd, ui, value):
+        return self.commands.index([cmd, ui, desc])
+                    
+    def __delitem__(self, key):
+        del self.commands[key]
+    
+    def pop(self, key):
+        return self.commands.pop(key)
+
+    def del_by_value(self, cmd, ui, desc):
+        if [cmd, ui, desc] in self.commands:
+            return self.commands.pop(self.commands.index([cmd, ui, desc]))
+
+    def drag_n_drop(self, cmd1, cmd2, before=True):
+        if cmd1 in self.commands:
+            dbg('Dropping command from inside %s'%'_\t_'.join(cmd1))
+            i1 = self.commands.index(cmd1)
+            del self.commands[i1]
+            if cmd2:
+                i2 = self.commands.index(cmd2)
+                if before:
+                    self.commands.insert(i2, cmd1)
+                else:
+                    self.commands.insert(i2+1, cmd1)
+            else:
+                self.commands.append(cmd1)
+        else:
+            dbg('Dropping command from outside %s'%'_\t_'.join(cmd1))
+            if cmd2:
+                i2 = self.commands.index(cmd2)
+                if before:
+                    self.commands.insert(i2, cmd1)
+                else:
+                    self.commands.insert(i2+1, cmd1)
+            else:
+                self.commands.append(cmd1)

=== modified file 'clicompanionlib/controller.py'
--- clicompanionlib/controller.py	2011-12-31 14:49:46 +0000
+++ clicompanionlib/controller.py	2012-01-02 01:28:26 +0000
@@ -19,17 +19,21 @@
 #
 #
 
-from clicompanionlib.utils import get_user_shell
-
-import clicompanionlib.tabs
-import view
-
+import os
 import pygtk
 pygtk.require('2.0')
 import re
 import webbrowser
-import ConfigParser
-import os
+import view
+import copy
+import clicompanionlib.tabs
+import clicompanionlib.config as cc_config
+import clicompanionlib.utils  as utils
+from clicompanionlib.utils import get_user_shell, dbg
+
+#if cc_config.get_config().get('terminal','debug') == 'True':
+#    utils.DEBUG = True
+
 # import vte and gtk or print error
 try:
     import gtk
@@ -49,27 +53,17 @@
     
 
 
-CHEATSHEET = os.path.expanduser("~/.clicompanion2")
-CONFIG_ORIG = "/etc/clicompanion.d/clicompanion2.config"
-CONFIGFILE = os.path.expanduser("~/.config/clicompanion/config")
-
 class Actions(object):
-    #make instances of the Classes we are going to use
-    #main_window = view.MainWindow
     ## Info Dialog Box
     ## if a command needs more info EX: a package name, a path
-    def get_info(self, widget, liststore):
-
-        if not view.ROW:
-            return
-        row_int = int(view.ROW[0][0])
-
+    def get_info(self, cmd, ui, desc):
+        dbg('Got command with user input')
         ## Create Dialog object
         dialog = gtk.MessageDialog(
             None,
             gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
             gtk.MESSAGE_QUESTION,
-            gtk.BUTTONS_OK,
+            gtk.BUTTONS_OK_CANCEL,
             None)
 
         # Primary text
@@ -82,16 +76,16 @@
 
         ## create a horizontal box to pack the entry and a label
         hbox = gtk.HBox()
-        hbox.pack_start(gtk.Label(liststore[row_int][1]+":"), False, 5, 5)
+        hbox.pack_start(gtk.Label(ui+":"), False, 5, 5)
         hbox.pack_end(entry)
         ## some secondary text
-        dialog.format_secondary_markup(_("Please provide a "+liststore[row_int][1]))
+        dialog.format_secondary_markup(_("Please provide a "+ui))
         ## add it and show it
         dialog.vbox.pack_end(hbox, True, True, 0)
         dialog.show_all()
 
         ## Show the dialog
-        dialog.run()
+        response = dialog.run()
 
         ## user text assigned to a variable
         text = entry.get_text()
@@ -100,13 +94,15 @@
         ## The destroy method must be called otherwise the 'Close' button will
         ## not work.
         dialog.destroy()
+        if response != gtk.RESPONSE_OK:
+            user_input = None
         return user_input
 
     def responseToDialog(self, text, dialog, response):
         dialog.response(response)
 
     ## Add command dialog box
-    def add_command(self, widget, liststore):
+    def add_command(self, mw):
 
         ## Create Dialog object
         dialog = gtk.MessageDialog(
@@ -141,15 +137,16 @@
         hbox2.pack_start(entry3, True, 5, 5)
 
         ## cancel button
-        dialog.add_button('Cancel', gtk.RESPONSE_DELETE_EVENT)
+        dialog.add_button(_('Cancel'), gtk.RESPONSE_DELETE_EVENT)
         ## some secondary text
-        dialog.format_secondary_markup(_('When entering a command use question '
-                'marks(?) as placeholders if user input is required when the '
-                'command runs. Example: ls /any/directory would be entered as, '
-                'ls ? .For each question mark(?) in your command, if any, use '
-                'the User Input field to provide a hint for each variable. '
-                'Using our example ls ? you could put directory as the User '
-                'Input. Lastly provide a brief Description.'))
+        dialog.format_secondary_markup(
+            _("When entering a command use question marks(?) as placeholders if"
+              " user input is required when the command runs. Example: ls "
+              "/any/directory would be entered as, ls ? .For each question "
+              "mark(?) in your command, if any, use the User Input field to "
+              "provide a hint for each variable. Using our example ls ? you "
+              "could put directory as the User Input. Lastly provide a brief "
+              "Description."))
 
         ## add it and show it
         dialog.vbox.pack_end(hbox2, True, True, 0)
@@ -163,24 +160,10 @@
             text1 = entry1.get_text()
             text2 = entry2.get_text()
             text3 = entry3.get_text()
-            '''open flat file, add the new command, update CMNDS variable
-            ## update commands in liststore (on screen) '''
-            if text1 != "":
-                with open(CHEATSHEET, "r") as cheatfile:
-                    cheatlines = cheatfile.readlines()
-                    cheatlines.append(text1+"\t"+text2+"\t"+text3+'\n')
-                    cheatfile.close()
-                with open(CHEATSHEET, "w") as cheatfile2:
-                    cheatfile2.writelines(cheatlines)
-                    cheatfile2.close()
-                    l = str(text1+"\t"+text2+"\t"+text3)
-                    #ls = l.split(':',2)
-                    ## update view.CMNDS variable
-                    filteredcommandplus = text1, text2, text3
-                    view.CMNDS.append(filteredcommandplus)
-                    ## update the command list on screen
-                    liststore.append([text1,text2,text3])
-
+            ## update commandsand sync with screen '''
+            view.CMNDS.append(text1, text2, text3)
+            mw.sync_cmnds()
+            view.CMNDS.save()
 
         ## The destroy method must be called otherwise the 'Close' button will
         ## not work.
@@ -188,32 +171,14 @@
         #return text
 
     ## This the edit function
-    def edit_command(self, widget , liststore):
-
+    def edit_command(self, mw):
         if not view.ROW:
             return
-        row_int_x = int(view.ROW[0][0])
-        row_int = 0
-		## TODO: Not implemented with filted yet
-        if view.FILTER == 1:
-            with open(CHEATSHEET, "r") as cheatfile:
-                cheatlines = cheatfile.readlines()
-                for i in range(len(cheatlines)):
-			    	if view.CMNDS[row_int_x][0] in cheatlines[i] and view.CMNDS[row_int_x][1] in cheatlines[i] :
-				    	row_int = i 
-                cheatfile.close()
-        else:
-			row_int = row_int_x
-
-         
-        row_obj1 = view.MainWindow.liststore[row_int][0]
-        text1 = "".join(row_obj1)
-
-        row_obj2 = view.MainWindow.liststore[row_int][1]
-        text2 = "".join(row_obj2)
-
-        row_obj3 = view.MainWindow.liststore[row_int][2]
-        text3 = "".join(row_obj3)
+        lst_index = int(view.ROW[0][0])
+        model = mw.treeview.get_model()
+        cmd = ''.join(model[lst_index][0])
+        ui = ''.join(model[lst_index][1])
+        desc = ''.join(model[lst_index][2])
 
         ## Create Dialog object
         dialog = gtk.MessageDialog(
@@ -228,11 +193,11 @@
 
         ## create the text input fields
         entry1 = gtk.Entry()
-        entry1.set_text(text1)
+        entry1.set_text(cmd)
         entry2 = gtk.Entry()
-        entry2.set_text(text2)
+        entry2.set_text(ui)
         entry3 = gtk.Entry()
-        entry3.set_text(text3)
+        entry3.set_text(desc)
         ## allow the user to press enter to do ok
         entry1.connect("activate", self.responseToDialog, dialog, gtk.RESPONSE_OK)
 
@@ -249,7 +214,7 @@
         hbox2.pack_start(entry3, True, 5, 5)
 
         ## cancel button
-        dialog.add_button('Cancel', gtk.RESPONSE_DELETE_EVENT)
+        dialog.add_button(_('Cancel'), gtk.RESPONSE_DELETE_EVENT)
         ## some secondary text
         dialog.format_secondary_markup(_("Please provide a command, description, and what type of user variable, if any, is required."))
 
@@ -262,64 +227,35 @@
 
         if result == gtk.RESPONSE_OK:
             ## user text assigned to a variable
-            text1 = entry1.get_text()
-            text2 = entry2.get_text()
-            text3 = entry3.get_text()
+            cmd = entry1.get_text()
+            ui = entry2.get_text()
+            desc = entry3.get_text()
 
-            if text1 != "":
-                self.remove_command(widget, liststore)
-                '''open flat file, add the new command, update CMNDS variable
-                ## update commands in liststore (on screen) '''
-                
-                with open(CHEATSHEET, "r") as cheatfile:
-                    cheatlines = cheatfile.readlines()
-                    cheatlines.append(text1+":"+text2+":"+text3+'\n')
-                    cheatfile.close()
-                with open(CHEATSHEET, "w") as cheatfile2:
-                    cheatfile2.writelines(cheatlines)
-                    cheatfile2.close()
-                    l = str(text1+":"+text2+":"+text3)
-                    #ls = l.split(':',2)
-                    ## update view.CMNDS variable
-                    filteredcommandplus = text1, text2, text3
-                    view.CMNDS.append(filteredcommandplus)
-                    ## update the command list on screen
-                    liststore.append([text1,text2,text3])
-                
+            if cmd != "":
+                cmd_index = model[lst_index][3]
+                dbg('Got index %d for command at pos %d'%(cmd_index, lst_index))
+                view.CMNDS[cmd_index] = [cmd, ui, desc]
+            mw.sync_cmnds()
+            view.CMNDS.save()
         ## The destroy method must be called otherwise the 'Close' button will
         ## not work.
         dialog.destroy()
 
 
     ## Remove command from command file and GUI
-    def remove_command(self, widget, liststore):
-		
+    def remove_command(self, mw):
         if not view.ROW:
             return
-        row_int_x = int(view.ROW[0][0])
-        row_int = 0
-		## TODO: Not implemented with filted yet
-        if view.FILTER == 1:
-            with open(CHEATSHEET, "r") as cheatfile:
-                cheatlines = cheatfile.readlines()
-                for i in range(len(cheatlines)):
-			    	if view.CMNDS[row_int_x][0] in cheatlines[i] and view.CMNDS[row_int_x][1] in cheatlines[i]:
-				    	row_int = i 
-                cheatfile.close()
-        else:
-			row_int = row_int_x
-
-        del view.CMNDS[row_int_x]
-        del liststore[row_int]
-
-        ## open command file and delete line so the change is persistent
-        with open(CHEATSHEET, "r") as cheatfile:
-            cheatlines = cheatfile.readlines()
-            del cheatlines[row_int]
-            cheatfile.close()
-        with open(CHEATSHEET, "w") as cheatfile2:
-            cheatfile2.writelines(cheatlines)
-            cheatfile2.close()
+        ## get selected row
+        lst_index = int(view.ROW[0][0])
+        ## get selected element index, even from search filter
+        model = mw.treeview.get_model()
+        cmd_index = model[lst_index][3]
+        ## delete element from liststore and CMNDS
+        del view.CMNDS[cmd_index]
+        mw.sync_cmnds()
+        ## save changes
+        view.CMNDS.save()
 
 
     def _filter_commands(self, widget, liststore, treeview):
@@ -330,11 +266,13 @@
         Pretty straight-forward.
         """
         search_term = widget.get_text().lower()
+        ## If the search term is empty, restore the liststore
         if search_term == "":
-		    view.FILTER = 0
-        else:
-		    view.FILTER = 1
-        
+            view.FILTER = 0
+            treeview.set_model(liststore)
+            return
+
+        view.FILTER = 1
         ## Create a TreeModelFilter object which provides auxiliary functions for
         ## filtering data.
         ## http://www.pygtk.org/pygtk2tutorial/sec-TreeModelSortAndTreeModelFilter.html
@@ -356,39 +294,31 @@
                 ## Python raises a AttributeError if row data was modified . Catch
                 ## that and fail silently.
                 pass
-
-
         modelfilter.set_visible_func(search, search_term)
+        ## save the old liststore and cmnds
         treeview.set_model(modelfilter)
-        
-
-        #clear CMNDS list then populate it with the filteredlist of commands
-        view.CMNDS = []
-        for line in modelfilter:
-            filteredcommandplus = tuple(modelfilter.get(line.iter, 0, 1, 2))
-            view.CMNDS.append(filteredcommandplus)
-
-
 
     ## send the command to the terminal
-    def run_command(self, widget, notebook, liststore):
+    def run_command(self, mw):
 
         ## if called without selecting a command from the list return
         if not view.ROW:
             return
         text = ""
-        row_int = int(view.ROW[0][0]) ## removes everything but number from [5,]
+        lst_index = int(view.ROW[0][0]) ## removes everything but number from [5,]
 
         ## get the current notebook page so the function knows which terminal to run the command in.
-        pagenum = notebook.get_current_page()
-        widget = notebook.get_nth_page(pagenum)
+        pagenum = mw.notebook.get_current_page()
+        widget = mw.notebook.get_nth_page(pagenum)
         page_widget = widget.get_child()
 
-        ## view.CMNDS is where commands are stored
-        cmnd = view.CMNDS[row_int][0]
+        model = mw.treeview.get_model()
+        cmd = ''.join(model[lst_index][0])
+        ui = ''.join(model[lst_index][1])
+        desc = ''.join(model[lst_index][2])
             
         ## find how many ?(user arguments) are in command
-        match = re.findall('\?', cmnd) 
+        match = re.findall('\?', cmd) 
         '''
         Make sure user arguments were found. Replace ? with something
         .format can read. This is done so the user can just enter ?, when
@@ -400,23 +330,33 @@
         else:
             num = len(match)
             ran = 0
-            new_cmnd = self.replace(cmnd, num, ran)
-
-
-        if not view.CMNDS[row_int][1] == "": # command with user input
-            c = ""
-            try:
-                text = self.get_info(self, liststore)
-                c = new_cmnd.format(text)
-            except: 
-                error = gtk.MessageDialog (None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
-                    _("You need to enter full input. Click on [x] to continue."))
-                error.run()
-            page_widget.feed_child(c+"\n") #send command w/ input
+            new_cmnd = self.replace(cmd, num, ran)
+
+        if len(match) > 0: # command with user input
+            dbg('command with ui')
+            f_cmd = ""
+            while True:
+                try:
+                    ui_text = self.get_info(cmd, ui, desc)
+                    if ui_text == None:
+                        return
+                    dbg('Got ui "%s"'%' '.join(ui_text))
+                    if ''.join(ui_text) == '':
+                        raise IndexError
+                    f_cmd = new_cmnd.format(ui_text)
+                except IndexError, e: 
+                    error = gtk.MessageDialog (None, gtk.DIALOG_MODAL, \
+                        gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
+                        _("You need to enter full input. Space separated."))
+                    error.connect('response', lambda err, *x: err.destroy())
+                    error.run()
+                    continue
+                break
+            page_widget.feed_child(f_cmd+"\n") #send command w/ input
             page_widget.show()
             page_widget.grab_focus()
         else: ## command that has no user input
-            page_widget.feed_child(cmnd+"\n") #send command
+            page_widget.feed_child(cmd+"\n") #send command
             page_widget.show()
             page_widget.grab_focus()
             
@@ -432,7 +372,9 @@
         return cmnd
         
     ## open the man page for selected command
-    def man_page(self, widget, notebook):
+    def man_page(self, notebook):
+        import subprocess as sp
+        import shlex
         try:
             row_int = int(view.ROW[0][0]) # removes everything but number from EX: [5,]
         except IndexError:  
@@ -443,21 +385,67 @@
                 gtk.MESSAGE_QUESTION,
                 gtk.BUTTONS_OK,
                 None)
-            dialog.set_markup('You must choose row to view help')
+            dialog.set_markup(_('You must choose a row to view the help'))
             dialog.show_all()
             dialog.run()
             dialog.destroy()     
             return 
+        ## get the manpage for the command
         cmnd = view.CMNDS[row_int][0] #CMNDS is where commands are store
-        splitcommand = self._filter_sudo_from(cmnd.split(" "))
-        ## get current notebook tab to use in function
-        pagenum = notebook.get_current_page()
-        widget = notebook.get_nth_page(pagenum)
-        page_widget = widget.get_child()
-        #send command to Terminal
-        page_widget.feed_child("man "+splitcommand[0]+"| most \n") 
-        page_widget.grab_focus()
-        page_widget.show()
+        ## get each command for each pipe, It's not 100 accurate, but good 
+        ## enough (by now)
+        commands = []
+        next_part = True
+        found_sudo = False
+        for part in shlex.split(cmnd):
+            if next_part:
+                if part == 'sudo' and not found_sudo:
+                    found_sudo = True
+                    commands.append('sudo') 
+                else:
+                    if part not in commands:
+                        commands.append(part)
+                    next_part = False
+            else:
+                if part in [ '||', '&&', '&', '|']:
+                    next_part = True
+           
+        notebook = gtk.Notebook()
+        notebook.set_scrollable(True)
+        notebook.popup_enable()
+        notebook.set_properties(group_id=0, tab_vborder=0, tab_hborder=1, tab_pos=gtk.POS_TOP)
+        ## create a tab for each command
+        for command in commands:
+            scrolled_page = gtk.ScrolledWindow()
+            scrolled_page.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+            tab = gtk.HBox()
+            tab_label = gtk.Label(command)
+            tab_label.show()
+            tab.pack_start(tab_label)
+            page = gtk.TextView()
+            page.set_wrap_mode(gtk.WRAP_WORD)
+            page.set_editable(False)
+            page.set_cursor_visible(False)
+            try:
+                manpage = sp.check_output(["man",command])
+            except sp.CalledProcessError, e:
+                manpage =  _('Failed to get manpage for command "%s"\nReason:\n%s')%(
+                        command, e)
+            textbuffer = page.get_buffer()
+            textbuffer.set_text(manpage)
+            scrolled_page.add(page)
+            notebook.append_page(scrolled_page, tab)
+        
+        help_win = gtk.Dialog()
+        help_win.set_title(_("Man page for %s")%cmnd)
+        help_win.vbox.pack_start(notebook, True, True, 0)
+        button = gtk.Button("close")
+        button.connect_object("clicked", lambda self: self.destroy(), help_win)
+        button.set_flags(gtk.CAN_DEFAULT)
+        help_win.action_area.pack_start( button, True, True, 0)
+        button.grab_default()
+        help_win.set_default_size(500,600)
+        help_win.show_all()
 
 
     @staticmethod
@@ -471,7 +459,6 @@
         return command
 
 
-
     #TODO: Move to menus_buttons
     def copy_paste(self, vte, event, data=None):
         if event.button == 3:
@@ -517,11 +504,11 @@
 
         # Add a short comment about the application, this appears below the application
         # name in the dialog
-        dialog.set_comments('This is a CLI Companion program.')
+        dialog.set_comments(_('This is a CLI Companion program.'))
 
         # Add license information, this is connected to the 'License' button
         # and is displayed in a new window.
-        dialog.set_license('Distributed under the GNU license. You can see it at <http://www.gnu.org/licenses/>.')
+        dialog.set_license(_('Distributed under the GNU license. You can see it at <http://www.gnu.org/licenses/>.'))
 
         # Show the dialog
         dialog.run()
@@ -529,11 +516,13 @@
         # The destroy method must be called otherwise the 'Close' button will
         # not work.
         dialog.destroy()
+
+
     def help_event(self, widget, data=None):
-        webbrowser.open("http://okiebuntu.homelinux.com/okwiki/clicompanion";)
+        webbrowser.open("http://launchpad.net/clicompanion";)
         
+
     def usage_event(self, widget, data=None):
-        mw = view.MainWindow
         dialog = gtk.Dialog("Usage",
             None,
             gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
@@ -573,139 +562,104 @@
         
     ## File --> Preferences    
     def changed_cb(self, combobox, config):
-        config.read(CONFIGFILE)
+        dbg('Changed encoding')
         model = combobox.get_model()
         index = combobox.get_active()
-        if index:
+        if index>=0:
             text_e = model[index][0]
-            config.set("terminal", "encoding", text_e)
-            # Writing our configuration file
-            with open(CONFIGFILE, 'wb') as f:
-                config.write(f)
+            encoding = text_e.split(':',1)[0].strip()
+            dbg('Setting encoding to "%s"'%encoding)
+            config.set("terminal", "encoding", encoding)
 
         
-    def color_set_fg_cb(self, colorbutton_fg, config):
-        config.read(CONFIGFILE)
-        #colorf16 = colorbutton_fg.get_color()
+    def color_set_fg_cb(self, colorbutton_fg, config, tabs):
+        dbg('Changing fg color')
         colorf = self.color2hex(colorbutton_fg)
         config.set("terminal", "colorf", str(colorf))          
-        # Writing our configuration file
-        with open(CONFIGFILE, 'wb') as f:
-            config.write(f)
-
-
-
-    def color_set_bg_cb(self, colorbutton_bg, config):
-        config.read(CONFIGFILE)
-        #colorb16 = colorbutton_bg.get_color()
+        tabs.update_all_term_config(config)
+
+
+    def color_set_bg_cb(self, colorbutton_bg, config, tabs):
+        dbg('Changing bg color')
         colorb = self.color2hex(colorbutton_bg)
         config.set("terminal", "colorb", str(colorb))
-        # Writing our configuration file
-        with open(CONFIGFILE, 'wb') as f:
-            config.write(f)
-
+        tabs.update_all_term_config(config)
         
         
     def color2hex(self, widget):
         """Pull the colour values out of a Gtk ColorPicker widget and return them
         as 8bit hex values, sinces its default behaviour is to give 16bit values"""
         widcol = widget.get_color()
-        print widcol
         return('#%02x%02x%02x' % (widcol.red>>8, widcol.green>>8, widcol.blue>>8))
         
-    def preferences(self, widget, data=None):
+    def preferences(self, tabs, data=None):
         '''
         Preferences window
         '''
-        mw = view.MainWindow
-        dialog = gtk.Dialog("User Preferences",
+        dialog = gtk.Dialog(_("User Preferences"),
             None,
             gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
             (gtk.STOCK_CANCEL, gtk.RESPONSE_CLOSE,
             gtk.STOCK_OK, gtk.RESPONSE_OK))
             
-        config = ConfigParser.RawConfigParser()
-        config.read(CONFIGFILE)
-            
+        config = cc_config.get_config_copy()
+           
         ##create the text input fields
         entry1 = gtk.Entry()
         entry1.set_text(config.get('terminal', 'scrollb'))
-
         
         ##combobox for selecting encoding
         combobox = gtk.combo_box_new_text()
-        combobox.append_text('Select encoding:')
-        combobox.append_text('UTF-8')
-        combobox.append_text('ISO-8859-1')
-        combobox.append_text('ISO-8859-15')
-
+        i=0
+        for encoding, desc in utils.encodings:
+            combobox.append_text(encoding + ': '+desc)
+            if encoding.strip().upper() == config.get('terminal','encoding').upper():
+                active = i
+            i=i+1
+        combobox.set_active(active)
         combobox.connect('changed', self.changed_cb, config)
-        combobox.set_active(0)
         
         ##colorbox for selecting text and background color
-        colorbutton_fg = gtk.ColorButton(gtk.gdk.color_parse('white'))
-        colorbutton_bg = gtk.ColorButton(gtk.gdk.color_parse('black'))
-
-        colorbutton_fg.connect('color-set', self.color_set_fg_cb, config)
-        colorbutton_bg.connect('color-set', self.color_set_bg_cb, config)
-
-        #dialog.show_all()
+        colorbutton_fg = gtk.ColorButton(
+            gtk.gdk.color_parse(config.get('terminal','colorf')))
+        colorbutton_bg = gtk.ColorButton(
+            gtk.gdk.color_parse(config.get('terminal','colorb')))
+
+        colorbutton_fg.connect('color-set', self.color_set_fg_cb, config, tabs)
+        colorbutton_bg.connect('color-set', self.color_set_bg_cb, config, tabs)
         
         ## allow the user to press enter to do ok
         entry1.connect("activate", self.responseToDialog, dialog, gtk.RESPONSE_OK)
 
-
-
-        ## create three labels
+        ## create the labels
         hbox1 = gtk.HBox()
         hbox1.pack_start(gtk.Label(_("Scrollback")), False, 5, 5)
         hbox1.pack_start(entry1, False, 5, 5)
 
-        hbox1.pack_start(gtk.Label(_("encoding")), False, 5, 5)
+        hbox1.pack_start(gtk.Label(_("Encoding")), False, 5, 5)
         hbox1.pack_start(combobox, False, 5, 5)
 
-
         hbox2 = gtk.HBox()
-        hbox2.pack_start(gtk.Label(_("font color")), False, 5, 5)
+        hbox2.pack_start(gtk.Label(_("Font color")), False, 5, 5)
         hbox2.pack_start(colorbutton_fg, True, 5, 5)
         
-        hbox2.pack_start(gtk.Label(_("background color")), False, 5, 5)
+        hbox2.pack_start(gtk.Label(_("Background color")), False, 5, 5)
         hbox2.pack_start(colorbutton_bg, True, 5, 5)
         
-        
         ## add it and show it
         dialog.vbox.pack_end(hbox2, True, True, 0)
         dialog.vbox.pack_end(hbox1, True, True, 0)
         dialog.show_all()
-        
+
         result = dialog.run()
-
         if result == gtk.RESPONSE_OK:
-
             ## user text assigned to a variable
             text_sb = entry1.get_text()
-            
-            config.read(CONFIGFILE)
             config.set("terminal", "scrollb", text_sb)
-
-
-
-            # Writing our configuration file
-            with open(CONFIGFILE, 'wb') as f:
-                config.write(f)
-   
-
-            ## instantiate tabs
-            tabs = clicompanionlib.tabs.Tabs()
-            tabs.update_term_config
+            cc_config.save_config(config)
+        tabs.update_all_term_config()
 
         ## The destroy method must be called otherwise the 'Close' button will
         ## not work.
         dialog.destroy()
 
-    def save_cmnds(self):
-        with open(CHEATSHEET, "w") as cheatfile:
-            for command in  view.CMNDS:
-                cheatfile.write('\t'.join(command)+'\n')
-       
-        

=== modified file 'clicompanionlib/menus_buttons.py'
--- clicompanionlib/menus_buttons.py	2011-11-16 10:48:50 +0000
+++ clicompanionlib/menus_buttons.py	2012-01-02 01:28:26 +0000
@@ -26,7 +26,11 @@
 
 class FileMenu(object):
 
-    def the_menu(self, actions, notebook, liststore):
+    def the_menu(self, mw):
+        actions = mw.actions
+        liststore = mw.liststore
+        tabs = mw.tabs
+        notebook = mw.notebook
         menu = gtk.Menu()
         #color = gtk.gdk.Color(65555, 62000, 65555)
         #menu.modify_bg(gtk.STATE_NORMAL, color)
@@ -43,33 +47,31 @@
         ## Make 'Run' menu entry
         menu_item1 = gtk.MenuItem(_("Run Command [F4]"))
         menu.append(menu_item1)
-        menu_item1.connect("activate", actions.run_command, notebook, liststore)
+        menu_item1.connect("activate", lambda *x: actions.run_command(mw))
         menu_item1.show()
 
         ## Make 'Add' file menu entry
         menu_item2 = gtk.MenuItem(_("Add Command [F5]"))
         menu.append(menu_item2)
-        menu_item2.connect("activate", actions.add_command, liststore)
+        menu_item2.connect("activate", lambda *x: actions.add_command(mw))
         menu_item2.show()
         
         ## Make 'Remove' file menu entry
         menu_item3 = gtk.MenuItem(_("Remove Command [F6]"))
         menu.append(menu_item3)
-        menu_item3.connect("activate", actions.remove_command, liststore)
+        menu_item3.connect("activate", lambda *x: actions.remove_command(mw))
         menu_item3.show()
         
         ## Make 'Add Tab' file menu entry
-        tab = tabs.Tabs()
         menu_item4 = gtk.MenuItem(_("Add Tab [F7]"))
         menu.append(menu_item4)
-        menu_item4.connect("activate", tab.add_tab, notebook)
+        menu_item4.connect("activate", lambda *x: tabs.add_tab(notebook))
         menu_item4.show()
         
         ## Make 'User Preferences' file menu entry
-        #tab = tabs.Tabs()
         menu_item5 = gtk.MenuItem(_("Preferences"))
         menu.append(menu_item5)
-        menu_item5.connect("activate", actions.preferences)
+        menu_item5.connect("activate", lambda *x: actions.preferences(tabs))
         menu_item5.show()
 
         ## Make 'Quit' file menu entry
@@ -113,7 +115,7 @@
         
         
         
-    def buttons(self, actions,  spacing, layout, notebook, liststore):
+    def buttons(self, mw,  spacing, layout):
         #button box at bottom of main window
         frame = gtk.Frame()
         bbox = gtk.HButtonBox()
@@ -125,9 +127,9 @@
         bbox.set_layout(layout)
         bbox.set_spacing(spacing)
         # Run button
-        buttonRun = gtk.Button(_("Run"))
+        buttonRun = gtk.Button('_'+_("Run"))
         bbox.add(buttonRun)
-        buttonRun.connect("clicked", actions.run_command, notebook, liststore)
+        buttonRun.connect("clicked", lambda *x: mw.actions.run_command(mw))
         buttonRun.set_tooltip_text(_("Click to run a highlighted command"))
         #buttonRun.modify_bg(gtk.STATE_NORMAL, color)        
         #buttonRun.modify_bg(gtk.STATE_PRELIGHT, color)        
@@ -135,15 +137,15 @@
         # Add button
         buttonAdd = gtk.Button(stock=gtk.STOCK_ADD)
         bbox.add(buttonAdd)
-        buttonAdd.connect("clicked", actions.add_command, liststore)
+        buttonAdd.connect("clicked", lambda *x: mw.actions.add_command(mw))
         buttonAdd.set_tooltip_text(_("Click to add a command to your command list"))
         #buttonAdd.modify_bg(gtk.STATE_NORMAL, color)        
         #buttonAdd.modify_bg(gtk.STATE_PRELIGHT, color)        
         #buttonAdd.modify_bg(gtk.STATE_INSENSITIVE, color)
         # Edit button
-        buttonEdit = gtk.Button(_("Edit"))
+        buttonEdit = gtk.Button('_'+_("Edit"))
         bbox.add(buttonEdit)
-        buttonEdit.connect("clicked", actions.edit_command, liststore)
+        buttonEdit.connect("clicked", lambda *x: mw.actions.edit_command(mw))
         buttonEdit.set_tooltip_text(_("Click to edit a command in your command list"))
         #buttonEdit.modify_bg(gtk.STATE_NORMAL, color)        
         #buttonEdit.modify_bg(gtk.STATE_PRELIGHT, color)        
@@ -151,7 +153,7 @@
         # Delete button
         buttonDelete = gtk.Button(stock=gtk.STOCK_DELETE)
         bbox.add(buttonDelete)
-        buttonDelete.connect("clicked", actions.remove_command, liststore)
+        buttonDelete.connect("clicked", lambda *x: mw.actions.remove_command(mw))
         buttonDelete.set_tooltip_text(_("Click to delete a command in your command list"))
         #buttonDelete.modify_bg(gtk.STATE_NORMAL, color)        
         #buttonDelete.modify_bg(gtk.STATE_PRELIGHT, color)        
@@ -159,7 +161,7 @@
         #Help Button
         buttonHelp = gtk.Button(stock=gtk.STOCK_HELP)
         bbox.add(buttonHelp)
-        buttonHelp.connect("clicked", actions.man_page, notebook)
+        buttonHelp.connect("clicked", lambda *x: mw.actions.man_page(mw.notebook))
         buttonHelp.set_tooltip_text(_("Click to get help with a command in your command list"))
         #buttonHelp.modify_bg(gtk.STATE_NORMAL, color)        
         #buttonHelp.modify_bg(gtk.STATE_PRELIGHT, color)        
@@ -167,7 +169,7 @@
         # Cancel button
         buttonCancel = gtk.Button(stock=gtk.STOCK_QUIT)
         bbox.add(buttonCancel)
-        buttonCancel.connect("clicked", actions.delete_event)
+        buttonCancel.connect("clicked", mw.actions.delete_event)
         buttonCancel.set_tooltip_text(_("Click to quit CLI Companion"))
         #buttonCancel.modify_bg(gtk.STATE_NORMAL, color)        
         #buttonCancel.modify_bg(gtk.STATE_PRELIGHT, color)        
@@ -176,34 +178,34 @@
         
         
     #right-click popup menu for the Liststore(command list)
-    def right_click(self, widget, event, actions, treeview, notebook, liststore):
+    def right_click(self, widget, event, mw):
         if event.button == 3:
             x = int(event.x)
             y = int(event.y)
             time = event.time
-            pthinfo = treeview.get_path_at_pos(x, y)
+            pthinfo = mw.treeview.get_path_at_pos(x, y)
             if pthinfo is not None:
                 path, col, cellx, celly = pthinfo
-                treeview.grab_focus()
-                treeview.set_cursor( path, col, 0)
+                mw.treeview.grab_focus()
+                mw.treeview.set_cursor( path, col, 0)
                 
                 # right-click popup menu Apply(run)
                 popupMenu = gtk.Menu()
                 menuPopup1 = gtk.ImageMenuItem (gtk.STOCK_APPLY)
                 popupMenu.add(menuPopup1)
-                menuPopup1.connect("activate", actions.run_command, notebook, liststore)
+                menuPopup1.connect("activate", lambda self, *x: mw.actions.run_command(mw))
                 # right-click popup menu Edit        
                 menuPopup2 = gtk.ImageMenuItem (gtk.STOCK_EDIT)
                 popupMenu.add(menuPopup2)
-                menuPopup2.connect("activate", actions.edit_command, liststore)
+                menuPopup2.connect("activate", lambda self, *x: mw.actions.edit_command(mw))
                 # right-click popup menu Delete                 
                 menuPopup3 = gtk.ImageMenuItem (gtk.STOCK_DELETE)
                 popupMenu.add(menuPopup3)
-                menuPopup3.connect("activate", actions.remove_command, liststore)
+                menuPopup3.connect("activate", lambda self, *x: mw.actions.remove_command(mw))
                 # right-click popup menu Help                
                 menuPopup4 = gtk.ImageMenuItem (gtk.STOCK_HELP)
                 popupMenu.add(menuPopup4)
-                menuPopup4.connect("activate", actions.man_page, notebook)
+                menuPopup4.connect("activate", lambda self, *x: mw.actions.man_page(mw.notebook))
                 # Show popup menu
                 popupMenu.show_all()
                 popupMenu.popup( None, None, None, event.button, time)

=== modified file 'clicompanionlib/tabs.py'
--- clicompanionlib/tabs.py	2011-03-29 17:31:11 +0000
+++ clicompanionlib/tabs.py	2012-01-02 01:28:26 +0000
@@ -22,33 +22,33 @@
 pygtk.require('2.0')
 import gtk
 import vte
-import ConfigParser
+import clicompanionlib.config as cc_config
 
-from clicompanionlib.utils import get_user_shell
+from clicompanionlib.utils import get_user_shell, dbg
 import clicompanionlib.controller
+import clicompanionlib.utils as utils
 import view
 
-CONFIGFILE = os.path.expanduser("~/.config/clicompanion/config")
-
-
-#definition gcp - how many pages is visible
-gcp=0;
-
-#definition nop - (no of pages) reflects no of terminal tabs left (some may be closed by the user)
-nop=0;
 
 class Tabs(object):
     '''
     add a new terminal in a tab above the current terminal
     '''
-    def add_tab(self,widget, notebook):
+    def __init__(self):
+        #definition nop - (no of pages) reflects no of terminal tabs left (some may be closed by the user)
+        self.nop = 0
+        #definition gcp - how many pages is visible
+        self.gcp = 0
+
+    def add_tab(self, notebook):
+        dbg('Adding a new tab')
         _vte = vte.Terminal()
         if view.NETBOOKMODE == 1:
             _vte.set_size_request(700, 120)
         else:
 		    _vte.set_size_request(700, 220) 
        
-        _vte.connect ("child-exited", lambda term: gtk.main_quit())
+        _vte.connect("child-exited", lambda term: gtk.main_quit())
         _vte.fork_command(get_user_shell()) # Get the user's default shell
         
         self.update_term_config(_vte)
@@ -58,19 +58,16 @@
         #notebook.set_show_tabs(True)
         #notebook.set_show_border(True)
         
-        global gcp
-        global nop
-        nop += 1
-        gcp += 1
-        pagenum = ('Tab %d') % gcp
-        if nop > 1:
+        self.nop += 1
+        self.gcp += 1
+        pagenum = ('Tab %d') % self.gcp
+        if self.nop > 1:
+            dbg('More than one tab, showing them.')
             view.MainWindow.notebook.set_show_tabs(True)
         box = gtk.HBox()
         label = gtk.Label(pagenum)
         box.pack_start(label, True, True)
         
-
-
         
         ## x image for tab close button
         close_image = gtk.image_new_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
@@ -87,10 +84,10 @@
         view.MainWindow.notebook.prepend_page(vte_tab, box) # add tab
         view.MainWindow.notebook.set_scrollable(True)
         actions = clicompanionlib.controller.Actions()
-        _vte.connect ("button_press_event", actions.copy_paste, None)
+        _vte.connect("button_press_event", actions.copy_paste, None)
         vte_tab.grab_focus()
         # signal handler for tab
-        closebtn.connect("clicked", self.close_tab, vte_tab, notebook)
+        closebtn.connect("clicked", lambda *x: self.close_tab(vte_tab, notebook))
                 
         vte_tab.show_all()
 
@@ -98,29 +95,42 @@
 
 
     ## Remove a page from the notebook
-    def close_tab(self, sender, widget, notebook):
+    def close_tab(self, vte_tab, notebook):
         ## get the page number of the tab we wanted to close
-        pagenum = view.MainWindow.notebook.page_num(widget)
+        pagenum = view.MainWindow.notebook.page_num(vte_tab)
         ## and close it
         view.MainWindow.notebook.remove_page(pagenum)
-        global nop
-        nop -= 1
-        if nop <= 1:
+        self.nop -= 1
+        if self.nop <= 1:
             view.MainWindow.notebook.set_show_tabs(False)
         
         # check if the focus does not go to the last page (ie with only a + sign)
-        if view.MainWindow.notebook.get_current_page() == nop:
+        if view.MainWindow.notebook.get_current_page() == self.nop:
             view.MainWindow.notebook.prev_page()
         
-        
-        
-    def update_term_config(self, _vte):
-        ##read config file
-        config = ConfigParser.RawConfigParser()
-        config.read(CONFIGFILE)
-
+    def update_all_term_config(self, config=None):
+        for pagenum in range(view.MainWindow.notebook.get_n_pages()):
+            page = view.MainWindow.notebook.get_nth_page(pagenum)
+            dbg(page)
+            if isinstance(page, gtk.ScrolledWindow):
+                for grandson in page.get_children():
+                    dbg(grandson)
+                    if isinstance(grandson,vte.Terminal):
+                        self.update_term_config(grandson, config)
+        
+    def update_term_config(self, _vte, config=None):
         ##set terminal preferences from conig file data
-        config_scrollback = config.getint('terminal', 'scrollb')
+        if not config:
+            config = cc_config.get_config()
+        try:
+            config_scrollback = config.getint('terminal', 'scrollb')
+        except ValueError:
+            print _("WARNING: Invalid value for property 'terminal', int expected:"
+                    " got '%s', using default '%s'")%(
+                        config.get('terminal', 'scrollb'),
+                        config.get('DEFAULT', 'scrollb'))
+            config.set('terminal','scrollb',config.get('DEFAULT', 'scrollb'))
+            config_scrollback = config.getint('DEFAULT', 'scrollb')
         _vte.set_scrollback_lines(config_scrollback)
         
         color = '#2e3436:#cc0000:#4e9a06:#c4a000:#3465a4:#75507b:#06989a:#d3d7cf:#555753:#ef2929:#8ae234:#fce94f:#729fcf:#ad7fa8:#34e2e2:#eeeeec'
@@ -130,14 +140,39 @@
             if color:
                 palette.append(gtk.gdk.color_parse(color))
         
-        config_color_fore = gtk.gdk.color_parse(config.get('terminal', 'colorf'))
-        #_vte.set_color_foreground(config_color_fore)
-        
-        config_color_back = gtk.gdk.color_parse(config.get('terminal', 'colorb'))
-        #_vte.set_color_background( config_color_back)
-        
+        try:
+            config_color_fore = gtk.gdk.color_parse(config.get('terminal', 'colorf'))
+        except ValueError, e:
+            print _("WARNING: Invalid value for property '%s':"
+                    " got '%s', using default '%s'.")%(
+                        'colorf',
+                        config.get('terminal', 'colorf'),
+                        config.get('DEFAULT', 'colorf'))
+            config.set('terminal','colorf',config.get('DEFAULT', 'colorf'))
+            config_color_fore = gtk.gdk.color_parse(config.get('DEFAULT', 'colorf'))
+
+        try:
+            config_color_back = gtk.gdk.color_parse(config.get('terminal', 'colorb'))
+        except ValueError, e:
+            print _("WARNING: Invalid value for property '%s':"
+                    " got '%s', using default '%s'.")%(
+                        'colorb',
+                        config.get('terminal', 'colorb'),
+                        config.get('DEFAULT', 'colorb'))
+            config.set('terminal','colorb',config.get('DEFAULT', 'colorb'))
+            config_color_back = gtk.gdk.color_parse(config.get('DEFAULT', 'colorb'))
         _vte.set_colors(config_color_fore, config_color_back, palette)
         
         config_encoding = config.get('terminal', 'encoding')
+        if config_encoding.upper() not in [ enc.upper() for enc, desc in utils.encodings]:
+            print _("WARNING: Invalid value for property '%s':"
+                    " got '%s', using default '%s'")%(
+                        'encoding',
+                        config_encoding,
+                        config.get('DEFAULT', 'encoding'))
+            config.set('terminal','encoding',config.get('DEFAULT', 'encoding'))
+            config_encoding = config.get('DEFAULT', 'encoding')
         _vte.set_encoding(config_encoding)
+
+
         

=== modified file 'clicompanionlib/utils.py'
--- clicompanionlib/utils.py	2011-03-29 17:31:11 +0000
+++ clicompanionlib/utils.py	2012-01-02 01:28:26 +0000
@@ -25,9 +25,148 @@
 
 import getpass
 import os
-
-CHEATSHEET = os.path.expanduser("~/.clicompanion2")
-#CONFIG_ORIG = "/etc/clicompanion.d/clicompanion2.config"
+import sys 
+import gtk 
+import pwd 
+import inspect
+
+
+## set to True if you want to see more logs
+DEBUG = False
+DEBUGFILES = False
+DEBUGCLASSES = []
+DEBUGMETHODS = []
+
+## list gotten from terminator (https://launchpad.net/terminator)
+encodings = [
+    ["ISO-8859-1", _("Western")],
+    ["ISO-8859-2", _("Central European")],
+    ["ISO-8859-3", _("South European") ],
+    ["ISO-8859-4", _("Baltic") ],
+    ["ISO-8859-5", _("Cyrillic") ],
+    ["ISO-8859-6", _("Arabic") ],
+    ["ISO-8859-7", _("Greek") ],
+    ["ISO-8859-8", _("Hebrew Visual") ],
+    ["ISO-8859-8-I", _("Hebrew") ],
+    ["ISO-8859-9", _("Turkish") ],
+    ["ISO-8859-10", _("Nordic") ],
+    ["ISO-8859-13", _("Baltic") ],
+    ["ISO-8859-14", _("Celtic") ],
+    ["ISO-8859-15", _("Western") ],
+    ["ISO-8859-16", _("Romanian") ],
+    #    ["UTF-7", _("Unicode") ],
+    ["UTF-8", _("Unicode") ],
+    #    ["UTF-16", _("Unicode") ],
+    #    ["UCS-2", _("Unicode") ],
+    #    ["UCS-4", _("Unicode") ],
+    ["ARMSCII-8", _("Armenian") ],
+    ["BIG5", _("Chinese Traditional") ],
+    ["BIG5-HKSCS", _("Chinese Traditional") ],
+    ["CP866", _("Cyrillic/Russian") ],
+    ["EUC-JP", _("Japanese") ],
+    ["EUC-KR", _("Korean") ],
+    ["EUC-TW", _("Chinese Traditional") ],
+    ["GB18030", _("Chinese Simplified") ],
+    ["GB2312", _("Chinese Simplified") ],
+    ["GBK", _("Chinese Simplified") ],
+    ["GEORGIAN-PS", _("Georgian") ],
+    ["HZ", _("Chinese Simplified") ],
+    ["IBM850", _("Western") ],
+    ["IBM852", _("Central European") ],
+    ["IBM855", _("Cyrillic") ],
+    ["IBM857", _("Turkish") ],
+    ["IBM862", _("Hebrew") ],
+    ["IBM864", _("Arabic") ],
+    ["ISO-2022-JP", _("Japanese") ],
+    ["ISO-2022-KR", _("Korean") ],
+    ["EUC-TW", _("Chinese Traditional") ],
+    ["GB18030", _("Chinese Simplified") ],
+    ["GB2312", _("Chinese Simplified") ],
+    ["GBK", _("Chinese Simplified") ],
+    ["GEORGIAN-PS", _("Georgian") ],
+    ["HZ", _("Chinese Simplified") ],
+    ["IBM850", _("Western") ],
+    ["IBM852", _("Central European") ],
+    ["IBM855", _("Cyrillic") ],
+    ["IBM857", _("Turkish") ],
+    ["IBM862", _("Hebrew") ],
+    ["IBM864", _("Arabic") ],
+    ["ISO-2022-JP", _("Japanese") ],
+    ["ISO-2022-KR", _("Korean") ],
+    ["ISO-IR-111", _("Cyrillic") ],
+    #    ["JOHAB", _("Korean") ],
+    ["KOI8-R", _("Cyrillic") ],
+    ["KOI8-U", _("Cyrillic/Ukrainian") ],
+    ["MAC_ARABIC", _("Arabic") ],
+    ["MAC_CE", _("Central European") ],
+    ["MAC_CROATIAN", _("Croatian") ],
+    ["MAC-CYRILLIC", _("Cyrillic") ],
+    ["MAC_DEVANAGARI", _("Hindi") ],
+    ["MAC_FARSI", _("Persian") ],
+    ["MAC_GREEK", _("Greek") ],
+    ["MAC_GUJARATI", _("Gujarati") ],
+    ["MAC_GURMUKHI", _("Gurmukhi") ],
+    ["MAC_HEBREW", _("Hebrew") ],
+    ["MAC_ICELANDIC", _("Icelandic") ],
+    ["MAC_ROMAN", _("Western") ],
+    ["MAC_ROMANIAN", _("Romanian") ],
+    ["MAC_TURKISH", _("Turkish") ],
+    ["MAC_UKRAINIAN", _("Cyrillic/Ukrainian") ],
+    ["SHIFT-JIS", _("Japanese") ],
+    ["TCVN", _("Vietnamese") ],
+    ["TIS-620", _("Thai") ],
+    ["UHC", _("Korean") ],
+    ["VISCII", _("Vietnamese") ],
+    ["WINDOWS-1250", _("Central European") ],
+    ["WINDOWS-1251", _("Cyrillic") ],
+    ["WINDOWS-1252", _("Western") ],
+    ["WINDOWS-1253", _("Greek") ],
+    ["WINDOWS-1254", _("Turkish") ],
+    ["WINDOWS-1255", _("Hebrew") ],
+    ["WINDOWS-1256", _("Arabic") ],
+    ["WINDOWS-1257", _("Baltic") ],
+    ["WINDOWS-1258", _("Vietnamese") ]
+    ]
+
+
+def dbg(log):
+    if DEBUG:
+        stack = inspect.stack()
+        method = None
+        for stackitem in stack:
+            parent_frame = stackitem[0]
+            names, varargs, keywords, local_vars = inspect.getargvalues(parent_frame)
+            ## little trick to get the second stackline method, in case we do 
+            ## not find self
+            if not method and method != None:
+                method = stackitem[3]
+            elif not method:
+                method = ''
+            try:
+                self_name = names[0]
+                if self_name != 'self':
+                    continue
+                classname = local_vars[self_name].__class__.__name__
+            except IndexError:
+                classname = "noclass"
+            ## in case self is found, get the method
+            method = stackitem[3]
+            break
+        if DEBUGFILES:
+            line = stackitem[2]
+            filename = parent_frame.f_code.co_filename
+            extra = " (%s:%s)" % (filename, line)
+        else:
+            extra = ""
+        if DEBUGCLASSES != [] and classname not in DEBUGCLASSES:
+            return
+        if DEBUGMETHODS != [] and method not in DEBUGMETHODS:
+            return
+        try:
+            print >> sys.stderr, "%s::%s: %s%s" % (classname, method, log, extra)
+        except IOError:
+            pass
+    
 
 #TODO: Move this to controller.py
 def get_user_shell():

=== modified file 'clicompanionlib/view.py'
--- clicompanionlib/view.py	2011-12-30 12:22:17 +0000
+++ clicompanionlib/view.py	2012-01-02 01:28:26 +0000
@@ -22,7 +22,6 @@
 import pygtk
 pygtk.require('2.0')
 import os
-import ConfigParser
 
 # import vte and gtk or print error
 try:
@@ -43,18 +42,20 @@
     
 import clicompanionlib.menus_buttons
 import clicompanionlib.controller
-from clicompanionlib.utils import get_user_shell , Borg
+from clicompanionlib.utils import get_user_shell , Borg, dbg
 import clicompanionlib.tabs
-from clicompanionlib.config import Config
-
-
-CONFIGFILE = os.path.expanduser("~/.config/clicompanion/config")
-CHEATSHEET = os.path.expanduser("~/.clicompanion2")
-CONFIG_ORIG = "/etc/clicompanion.d/clicompanion2.config"
+import clicompanionlib.utils as utils
+import clicompanionlib.config as cc_config
+
 
 ## Changed two->three columns
-CMNDS = [] ## will hold the commands. Actually the first three columns
-ROW = '1' ## holds the currently selected row
+CMNDS = cc_config.Cheatsheet() 
+## will hold the commands. Actually the first three columns
+## note that this commands list will not change with searchers and filters, 
+## instead, when adding a command to the liststore, we will add also the index 
+## of the command in the CMND list
+
+ROW = '0' ## holds the currently selected row
 TARGETS = [
     ('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
     ('text/plain', 0, 1),
@@ -67,7 +68,6 @@
 HIDEUI = 0
 FULLSCREEN = 0
 
-
 menu_search_hbox = ''
 button_box = ''
 
@@ -76,90 +76,32 @@
     window = gtk.Window(gtk.WINDOW_TOPLEVEL) 
     #color = gtk.gdk.Color(60000, 65533, 60000)
     #window.modify_bg(gtk.STATE_NORMAL, color)
-    liststore = gtk.ListStore(str, str, str)	
+    liststore = gtk.ListStore(str, str, str, int)	
     treeview = gtk.TreeView()
     expander = gtk.Expander()
     scrolledwindow = gtk.ScrolledWindow()
     notebook = gtk.Notebook()
 
-
     screen = gtk.gdk.display_get_default().get_default_screen()
     screen_size = screen.get_monitor_geometry(0)
     height =  screen.get_height() ## screen height ##
     global NETBOOKMODE
     if height < 750:
 		NETBOOKMODE = 1
-    ## open file containing command list and put it in a variable
-    def update(self, liststore):
-        try:
-            with open(CHEATSHEET, "r") as cheatfile:
-                bugdata=cheatfile.read()
-                cheatfile.close()
-        except IOError:
-            ## CHEATSHEET is not there. Oh, no!
-            ## So, run self.setup() again.
-            self.setup()
-            ## Then, run me again.
-            self.update(self.liststore)
-
-        ## add bug data from .clicompanion --> bugdata --> to the liststore
-        for line in bugdata.splitlines(): 
-            l = line.split('\t',2) 
-            if len(l) < 2:
-                """
-                If for any reason we have a old file, we must
-                replace it by new one
-                """
-                print "PLEASE RESTART APPLICATION TO FINISH UPDATE"
-                self.setup()
-                return
-            commandplus = l[0], l[1], l[2]
-            CMNDS.append(commandplus)
-            self.liststore.append([l[0],l[1],l[2]])
-
-          
-    #copy config file to user $HOME if does not exist
-    def setup(self):
-        """
-        Check if ~/.clicompanion2 exists. If not check for original
-        installed in /etc/clicompanion.d/. If origianl exists copy to $HOME.
-        if not create a new, blank ~/.clicompanion2 so program will not crash
-        """
-
-        if not os.path.exists(CHEATSHEET):
-            if os.path.exists(CONFIG_ORIG):
-                os.system ("cp %s %s" % (CONFIG_ORIG, CHEATSHEET))
-            else:
-                # Oops! Looks like there's no cheatsheet in CHEATSHEET.
-                # Then, create an empty cheatsheet.
-                open(CHEATSHEET, 'w').close()
-        """
-        If we have old file, we must replace it by fresh list
-        """ 
-        cheatlines = []
-        try:
-            with open(CHEATSHEET, "r") as cheatfile:
-                bugdata=cheatfile.read()
-                cheatfile.close()
-                for line in bugdata.splitlines():
-                    l = line.split('\t', 2)
-                    if len(l) < 2:
-                        l = line.split(':', 2)
-                        p = str(l[0] + "\t"+ l[1] +"\t"+ l[2] + "\n")
-                        cheatlines.append(p)
-                    else:
-                        cheatlines.append(str(l[0] + "\t"+ l[1] +"\t"+ l[2] + "\n"))
-                        
-            with open(CHEATSHEET, "w") as cheatfile2:
-                cheatfile2.writelines(cheatlines)
-                cheatfile2.close()      
-                                     
-        except IOError:
-            ## CHEATSHEET is not there. Oh, no!
-            ## So, run self.setup() again.
-            self.setup()
-            ## Then, run me again.
-            self.update(self.liststore)
+
+
+    def sync_cmnds(self, rld=False):
+        global CMNDS
+        dbg('syncing commands')
+        if rld:
+            ## reload the commands list from the file
+            CMNDS.load()
+        self.liststore.clear()
+        ## Store also the index of the command in the CMNDS list
+        i = 0
+        for cmd, ui, desc in CMNDS:
+            self.liststore.append((cmd, ui, desc, i))
+            i = i +1
 
     
     #liststore in a scrolled window in an expander
@@ -185,13 +127,11 @@
         
     def key_clicked(self, widget, event):
         actions = clicompanionlib.controller.Actions()
-        tabs = clicompanionlib.tabs.Tabs()
         global HIDEUI
         global FULLSCREEN
         global menu_search_hbox
         global button_box
         keyname = gtk.gdk.keyval_name(event.keyval).upper()
-        #print keyname ##debug
         if keyname == "F12":
             HIDEUI = 1 - HIDEUI
         if HIDEUI == 1:
@@ -215,13 +155,13 @@
             pwin = button_box.get_window()
             pwin.unfullscreen()
         if keyname == "F4":
-			actions.run_command(self, self.notebook, self.liststore)
+			actions.run_command(self)
         if keyname == "F5":
-			actions.add_command(self, self.liststore)
+			actions.add_command(self)
         if keyname == "F6":
-			actions.remove_command(self, self.liststore)
+			actions.remove_command(self)
         if keyname == "F7":
-			tabs.add_tab(self, self.notebook)
+			self.tabs.add_tab(self)
   
     def __init__(self):
         #import pdb  ##debug
@@ -231,14 +171,7 @@
         ##in libvte in Ubuntu Maverick
         os.putenv('TERM', 'xterm')
 
-        ## copy command list to user $HOME if does not exist
-        self.setup()
 
-        ##create the config file
-        conf_mod = Config()
-        conf_mod.create_config()
-        
-        
         ## style to reduce padding around tabs
         ## TODO: Find a better place for this? 
     	gtk.rc_parse_string ("style \"tab-close-button-style\"\n"
@@ -257,7 +190,6 @@
         ##attach the style to the widget
         self.notebook.set_name ("tab-close-button")
 
-
         ## set sizes and borders
         global NETBOOKMODE
         if NETBOOKMODE == 1:
@@ -272,19 +204,15 @@
         ## Allow user to resize window
         self.window.set_resizable(True)
         
-        
         ## set Window title and icon
         self.window.set_title("CLI Companion")
         icon = gtk.gdk.pixbuf_new_from_file("/usr/share/pixmaps/clicompanion.16.png")
         self.window.set_icon(icon)
-        
-        
 	   
-        # get commands and put in liststore
-        self.update(self.liststore) 
+        # sync liststore with commands
+        self.sync_cmnds()
         
         ## set renderer and colors
-
         #color2 = gtk.gdk.Color(5000,5000,65000)
         renderer = gtk.CellRendererText()
         #renderer.set_property("cell-background-gdk", color)
@@ -308,7 +236,7 @@
                                                 True)
             ## set the cell attributes to the appropriate liststore column
             self.treeview.columns[n].set_attributes(
-            self.treeview.columns[n].cell, text=n)   
+                    self.treeview.columns[n].cell, text=n)   
             self.treeview.columns[n].set_resizable(True)  
         
         ''' set treeview model and put treeview in the scrolled window
@@ -321,12 +249,12 @@
         #self.window.show_all()
 
         ## instantiate tabs
-        tabs = clicompanionlib.tabs.Tabs()
+        self.tabs = clicompanionlib.tabs.Tabs()
         ## instantiate controller.Actions, where all the button actions are
         self.actions = clicompanionlib.controller.Actions()
         ## instantiate 'File' and 'Help' Drop Down Menu [menus_buttons.py]
         bar = clicompanionlib.menus_buttons.FileMenu()
-        menu_bar = bar.the_menu(self.actions, self.notebook, self.liststore)
+        menu_bar = bar.the_menu(self)
         
 
         ## get row of a selection
@@ -337,17 +265,17 @@
             
             
         ## double click to run a command    
-        def treeview_clicked(widget, event):
-            if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
-                self.actions.run_command(self, self.notebook, self.liststore)
+        def treeview_clicked(widget, path, column):
+            self.actions.run_command(self)
+
 
         ## press enter to run a command                   
         def treeview_button(widget, event):
             keyname = gtk.gdk.keyval_name(event.keyval).upper()
-            #print keyname ##debug
+            dbg('Key %s pressed'%keyname)
             if event.type == gtk.gdk.KEY_PRESS:
                 if keyname == 'RETURN':
-                    self.actions.run_command(self, self.notebook, self.liststore)
+                    self.actions.run_command(self)
                     
                     
 
@@ -357,7 +285,7 @@
         selection.select_path(0) 
         selection.connect("changed", mark_selected, selection)
         ## double-click
-        self.treeview.connect("button-press-event", treeview_clicked)
+        self.treeview.connect("row-activated", treeview_clicked)
         #press enter to run command
         self.treeview.connect("key-press-event", treeview_button)
                 
@@ -393,7 +321,7 @@
         self.expander.set_label_widget(expander_hbox)
 
         ## Add the first tab with the Terminal
-        tabs.add_tab(self, self.notebook)
+        self.tabs.add_tab(self.notebook)
         self.notebook.set_tab_pos(2)
 
         ## The "Add Tab" tab
@@ -405,7 +333,7 @@
         
         global button_box
         ## buttons at bottom of main window [menus_buttons.py]
-        button_box = bar.buttons(self.actions, 10, gtk.BUTTONBOX_END, self.notebook, self.liststore)
+        button_box = bar.buttons(self, 10, gtk.BUTTONBOX_END)
 
         ## vbox for search, notebook, buttonbar
         vbox = gtk.VBox()
@@ -421,9 +349,9 @@
         self.expander.connect('notify::expanded', self.expanded_cb, self.window, self.search_box)
         self.window.connect("delete_event", self.delete_event)
         self.window.connect("key-press-event", self.key_clicked)
-        add_tab_button.connect("clicked", tabs.add_tab, self.notebook)
+        add_tab_button.connect("clicked", lambda *x: self.tabs.add_tab(self.notebook))
         ## right click menu event capture
-        self.treeview.connect ("button_press_event", bar.right_click, self.actions, self.treeview, self.notebook, self.liststore)
+        self.treeview.connect("button_press_event", bar.right_click, self)
 
         # Allow enable drag and drop of rows including row move
         self.treeview.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
@@ -435,12 +363,20 @@
 
         self.treeview.connect ("drag_data_get", self.drag_data_get_event)
         self.treeview.connect ("drag_data_received", self.drag_data_received_event)
+        self.treeview.connect("drag_drop", self.on_drag_drop )
 
 
         #self.vte.grab_focus()
         self.window.show_all()
         return
 
+    def on_drag_drop(self, treeview, *x):
+        '''
+        Stop the signal when in search mode
+        '''
+        if FILTER:
+            treeview.stop_emission('drag_drop')
+
     def drag_data_get_event(self, treeview, context, selection, target_id, 
                             etime):
         """
@@ -457,90 +393,57 @@
         Executed when dropping.
         """
         global CMNDS
+        global FILTER
+        ## if we are in a search, do nothing
+        if FILTER == 1:
+            return
         model = treeview.get_model()
+        ## get the destination
+        drop_info = treeview.get_dest_row_at_pos(x, y)
+        if drop_info: 
+            path, position = drop_info
+            iter = model.get_iter(path)
+            dest = list(model.get(iter, 0, 1, 2))
+
+        ## parse all the incoming commands
         for data in selection.data.split('\n'):
             # if we got an empty line skip it
             if not data.replace('\r',''): continue
             # format the incoming string
             orig = data.replace('\r','').split('\t',2)
-            orig = tuple([ fld.strip() for fld in orig ])
+            orig = [ fld.strip() for fld in orig ]
             # fill the empty fields
             if len(orig) < 3: orig = orig + ('',)*(3-len(orig))
-            # if the element already exists delete it (dragged from clicompanion)
-            olditer = self.find_iter_by_tuple(orig, model)
-            if olditer: del model[olditer]
+            dbg('Got drop of command %s'%'_\t_'.join(orig))
 
-            drop_info = treeview.get_dest_row_at_pos(x, y)
             if drop_info:
-                path, position = drop_info
-                iter = model.get_iter(path)
-                dest = tuple(model.get(iter, 0, 1, 2))
                 if (position == gtk.TREE_VIEW_DROP_BEFORE
                         or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
-                    model.insert_before(iter, orig)
-                    self.drag_cmnd(orig, dest, before=True)
+                    dbg('\t to before dest %s'%'_\t_'.join(dest))
+                    CMNDS.drag_n_drop(orig, dest, before=True)
                 else:
-                    model.insert_after(iter, orig)
-                    self.drag_cmnd(orig, dest, before=False)
+                    dbg('\t to after dest %s'%'_\t_'.join(dest))
+                    CMNDS.drag_n_drop(orig, dest, before=False)
             else:
-                if len(model) > 0:
-                    iter = model[-1].iter
-                    model.insert_after(iter, orig)
-                else:
-                    model.insert(0, orig)
-                    return
-                dest = tuple(model.get(iter, 0, 1, 2))
-                self.drag_cmnd(orig, dest, before=False)
-            if context.action == gtk.gdk.ACTION_MOVE:
-                context.finish(True, True, etime)
-        self.actions.save_cmnds()
+                dbg('\t to the end')
+                CMNDS[len(CMNDS)] = orig
+        if context.action == gtk.gdk.ACTION_MOVE:
+            context.finish(True, True, etime)
+        self.sync_cmnds()
+        CMNDS.save()
         
-    def find_iter_by_tuple(self, data, model):
-        for row in model:
-            if tuple(model.get(row.iter, 0, 1, 2)) == data:
-                return row.iter
-        return None
-    
-    def drag_cmnd(self, orig, dest, before=True):
-        """
-        Sync the CMNDS array with the drag and drop of the treeview.
-        """
-        global CMNDS
-        i = j = None
-        pos = 0
-        for cmnd in CMNDS:
-            if cmnd == orig: 
-                i = pos
-            elif cmnd == dest: 
-                j = pos
-            pos += 1
-        ## both from clicompanion
-        if i != None and j != None:
-            cmnd = CMNDS.pop(i)
-            if before and i<=j:
-                CMNDS.insert(j-1, cmnd)
-            elif before and i>j:
-                CMNDS.insert(j, cmnd)
-            elif i<=j:
-                CMNDS.insert(j, cmnd)
-            else:
-                CMNDS.insert(j+1, cmnd)
-        ## origin unknown
-        elif j != None:
-            cmnd = orig
-            if before:
-                CMNDS.insert(j, cmnd)
-            else:
-                CMNDS.insert(j+1, cmnd)
-    
-
     def main(self):
         try:
             gtk.main()
         except KeyboardInterrupt:
             pass
         
-def run():
-    
+def run( options=None ):
+    ##create the config file
+    config = cc_config.create_config()
+    if config.get('terminal','debug') == 'True':
+        utils.DEBUG = True
+    CMNDS.load(options and options.cheatsheet or None)
+    dbg('Loaded commands %s'%CMNDS)
     main_window = MainWindow()
     main_window.main()