← Back to team overview

clicompanion-devs team mailing list archive

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

 

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

Requested reviews:
  CLI Companion Development Team (clicompanion-devs)
Related bugs:
  Bug #748888 in CLI Companion: "Reordering in listview should be saved"
  https://bugs.launchpad.net/clicompanion/+bug/748888
  Bug #909893 in CLI Companion: "Reordering commands does not work properly"
  https://bugs.launchpad.net/clicompanion/+bug/909893

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

Now the drag and drop feature works properly and allows you to:
  - reorder list
  - add commands from external source (on for each line)
  - saves all the changes made so they are not lost
  - when dropping also removes duplicates (practically they seem moved from the previous position to the new)


-- 
https://code.launchpad.net/~dcaro/clicompanion/fix-909893/+merge/87131
Your team CLI Companion Development Team is requested to review the proposed merge of lp:~dcaro/clicompanion/fix-909893 into lp:clicompanion.
=== modified file 'clicompanion' (properties changed: -x to +x)
=== modified file 'clicompanionlib/controller.py'
--- clicompanionlib/controller.py	2011-11-18 23:56:17 +0000
+++ clicompanionlib/controller.py	2011-12-30 12:12:24 +0000
@@ -141,7 +141,13 @@
         ## cancel button
         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)
@@ -283,7 +289,6 @@
 
     ## Remove command from command file and GUI
     def remove_command(self, widget, liststore):
-		
         row_int_x = int(view.ROW[0][0])
         row_int = 0
 		## TODO: Not implemented with filted yet
@@ -353,8 +358,7 @@
         #clear CMNDS list then populate it with the filteredlist of commands
         view.CMNDS = []
         for line in modelfilter:
-            linelist = line
-            filteredcommandplus = linelist[0], linelist[1], linelist[2]
+            filteredcommandplus = tuple(line[0:2])
             view.CMNDS.append(filteredcommandplus)
 
 
@@ -369,6 +373,7 @@
         pagenum = notebook.get_current_page()
         widget = notebook.get_nth_page(pagenum)
         page_widget = widget.get_child()
+
         ## view.CMNDS is where commands are stored
         cmnd = view.CMNDS[row_int][0]
             
@@ -687,4 +692,10 @@
         ## 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/view.py'
--- clicompanionlib/view.py	2011-11-20 16:51:59 +0000
+++ clicompanionlib/view.py	2011-12-30 12:12:24 +0000
@@ -323,23 +323,23 @@
         ## instantiate tabs
         tabs = clicompanionlib.tabs.Tabs()
         ## instantiate controller.Actions, where all the button actions are
-        actions = clicompanionlib.controller.Actions()
+        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(actions, self.notebook, self.liststore)
+        menu_bar = bar.the_menu(self.actions, self.notebook, self.liststore)
         
 
         ## get row of a selection
         def mark_selected(self, treeselection):
             global ROW
-            (model, pathlist)=treeselection.get_selected_rows()
+            (model, pathlist) = treeselection.get_selected_rows()
             ROW = pathlist
             
             
         ## double click to run a command    
         def treeview_clicked(widget, event):
             if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
-                actions.run_command(self, self.notebook, self.liststore)
+                self.actions.run_command(self, self.notebook, self.liststore)
 
         ## press enter to run a command                   
         def treeview_button(widget, event):
@@ -347,7 +347,7 @@
             #print keyname ##debug
             if event.type == gtk.gdk.KEY_PRESS:
                 if keyname == 'RETURN':
-                    actions.run_command(self, self.notebook, self.liststore)
+                    self.actions.run_command(self, self.notebook, self.liststore)
                     
                     
 
@@ -367,7 +367,7 @@
         search_label = gtk.Label(_("Search:"))
         search_label.set_alignment(xalign=-1, yalign=0)
         self.search_box = gtk.Entry()
-        self.search_box.connect("changed", actions._filter_commands, self.liststore, self.treeview)
+        self.search_box.connect("changed", self.actions._filter_commands, self.liststore, self.treeview)
         ## search box tooltip
         self.search_box.set_tooltip_text(_("Search your list of commands"))
         ## Set the search box sensitive OFF at program start, because
@@ -405,7 +405,7 @@
         
         global button_box
         ## buttons at bottom of main window [menus_buttons.py]
-        button_box = bar.buttons(actions, 10, gtk.BUTTONBOX_END, self.notebook, self.liststore)
+        button_box = bar.buttons(self.actions, 10, gtk.BUTTONBOX_END, self.notebook, self.liststore)
 
         ## vbox for search, notebook, buttonbar
         vbox = gtk.VBox()
@@ -423,13 +423,123 @@
         self.window.connect("key-press-event", self.key_clicked)
         add_tab_button.connect("clicked", tabs.add_tab, self.notebook)
         ## right click menu event capture
-        self.treeview.connect ("button_press_event", bar.right_click, actions, self.treeview, self.notebook, self.liststore)
+        self.treeview.connect ("button_press_event", bar.right_click, self.actions, self.treeview, self.notebook, self.liststore)
+
+        TARGETS = [
+           ('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
+           ('text/plain', 0, 1),
+           ('TEXT', 0, 2),
+           ('STRING', 0, 3),
+           ]
+        # Allow enable drag and drop of rows including row move
+        self.treeview.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
+                                                TARGETS,
+                                                gtk.gdk.ACTION_DEFAULT |
+                                                gtk.gdk.ACTION_COPY)
+        self.treeview.enable_model_drag_dest(TARGETS,
+                                                gtk.gdk.ACTION_DEFAULT)
+
+        self.treeview.connect ("drag_data_get", self.drag_data_get_event)
+        self.treeview.connect ("drag_data_received", self.drag_data_received_event)
 
 
         #self.vte.grab_focus()
         self.window.show_all()
         return
 
+    def drag_data_get_event(self, treeview, context, selection, target_id, 
+                            etime):
+        """
+        Executed on dragging
+        """
+        treeselection = treeview.get_selection()
+        model, iter = treeselection.get_selected()
+        data = model.get(iter, 0, 1, 2)
+        selection.set(selection.target, 8, '\t'.join(data))
+
+    def drag_data_received_event(self, treeview, context, x, y, selection, info,
+                            etime):
+        """
+        Executed when dropping.
+        """
+        global CMNDS
+        model = treeview.get_model()
+        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 ])
+            # 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]
+
+            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)
+                else:
+                    model.insert_after(iter, orig)
+                    self.drag_cmnd(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()
+        
+    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()