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

Proposed by David Caro
Status: Merged
Approved by: Marek Bardoński
Approved revision: 97
Merged at revision: 101
Proposed branch: lp:~dcaro/clicompanion/fix-910355
Merge into: lp:clicompanion
Diff against target: 2007 lines (+779/-533)
7 files modified
clicompanion (+6/-4)
clicompanionlib/config.py (+237/-26)
clicompanionlib/controller.py (+206/-252)
clicompanionlib/menus_buttons.py (+27/-25)
clicompanionlib/tabs.py (+77/-42)
clicompanionlib/utils.py (+142/-3)
clicompanionlib/view.py (+84/-181)
To merge this branch: bzr merge lp:~dcaro/clicompanion/fix-910355
Reviewer Review Type Date Requested Status
Marek Bardoński Approve
Review via email: mp+87218@code.launchpad.net

Description of the change

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

To post a comment you must log in.
Revision history for this message
Marek Bardoński (bdfhjk) wrote :

I checked Clicompanion both statically and dynamically. No errors in code observed.

    Bug #611141: It is not obvious that "q" must be pressed to exit help (man pages) [OK]

    Bug #801906: clicompanion crashes on start with ValueError in _get() [Look good]

    Bug #909894: Double click execution with user input drags command [OK]

    Bug #910249: Warning window (when wrong params issued) not working properly [OK]

    Bug #910355: Properties are not applied to the current terminal [OK]

    Bug #910360: Incorrect .clicompanion2 file causes the program to crash [OK]

    Bug #910370: Drag and drop when searching breaks the drag and drop [OK]

    Bug #910531: Allowed empty user input [OK]

    Bug #910533: In a fresh start, when executting the first command, the second gets executed instead [OK]

Regression bugs:
1. Low importance. Everytime, when I open new instance of Clicompanion, I got a message "INFO: Created config file at /home/user/.config/clicompanion/config", but that isn't true - my configuration is stored correctly and no new file was created.

Thank You for your work.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'clicompanion'
--- clicompanion 2011-12-31 12:33:51 +0000
+++ clicompanion 2012-01-02 01:28:26 +0000
@@ -6,7 +6,6 @@
6import sys6import sys
7from optparse import OptionParser7from optparse import OptionParser
88
9from clicompanionlib.view import run
109
11share_dirs = os.environ.get('XDG_BASE_PDATA', 10share_dirs = os.environ.get('XDG_BASE_PDATA',
12 '/usr/local/share:/usr/share').split(os.pathsep)11 '/usr/local/share:/usr/share').split(os.pathsep)
@@ -27,10 +26,12 @@
2726
28parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.1")27parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.1")
29parser.add_option("-f", "--file", dest="filename",28parser.add_option("-f", "--file", dest="filename",
30 help="write report to FILE", metavar="FILE")29 help="Write report to FILE", metavar="FILE")
30parser.add_option("-c", "--cheatsheet", dest="cheatsheet",
31 help="Read cheatsheet from FILE", metavar="FILE")
31parser.add_option("-q", "--quiet",32parser.add_option("-q", "--quiet",
32 action="store_false", dest="verbose", default=True,33 action="store_false", dest="verbose", default=True,
33 help="don't print status messages to stdout")34 help="Don't print status messages to stdout")
3435
35(options, args) = parser.parse_args()36(options, args) = parser.parse_args()
3637
@@ -62,4 +63,5 @@
6263
6364
64if __name__ == "__main__":65if __name__ == "__main__":
65 run()66 from clicompanionlib.view import run
67 run( options )
6668
=== 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 @@
20#20#
21import os21import os
22import ConfigParser22import ConfigParser
23import clicompanionlib.utils as utils
24from clicompanionlib.utils import dbg
25import collections
2326
27CHEATSHEET = os.path.expanduser("~/.clicompanion2")
24CONFIGDIR = os.path.expanduser("~/.config/clicompanion/")28CONFIGDIR = os.path.expanduser("~/.config/clicompanion/")
25CONFIGFILE = os.path.expanduser("~/.config/clicompanion/config")29CONFIGFILE = os.path.expanduser("~/.config/clicompanion/config")
2630CONFIG_ORIG = "/etc/clicompanion.d/clicompanion2.config"
27class Config(object):31DEFAULTS = { "scrollb": '500',
2832 "colorf": '#FFFFFF',
29 ''' 33 "colorb": '#000000',
30 create configuration file34 "encoding": 'UTF-8',
31 '''35 "debug": 'False'}
3236
33 def create_config(self):37## To avoid parsing the config file each time, we store the loaded config here
34 38CONFIG = None
35 if not os.path.exists(CONFIGFILE):39
36 os.makedirs(CONFIGDIR)40def create_config(conffile=CONFIGFILE):
37 config = ConfigParser.ConfigParser()41 global CONFIG
38 # set a number of parameters42 config = CONFIG
43 configdir = conffile.rsplit(os.sep,1)[0]
44 if not os.path.exists(configdir):
45 try:
46 os.makedirs(configdir)
47 except Exception, e:
48 print _('Unable to create config at dir %s (%s)')%(configdir,e)
49 return False
50 # reuse the config if able
51 if not config:
52 config = ConfigParser.SafeConfigParser(DEFAULTS)
53 # set a number of parameters
54 if os.path.isfile(conffile):
55 config.read([conffile])
56 else:
39 config.add_section("terminal")57 config.add_section("terminal")
40 config.set("terminal", "scrollb", 500)58 for option, value in DEFAULTS.items():
41 config.set("terminal", "colorf", '#FFFFFF')59 config.set("terminal", option, value)
42 config.set("terminal", "colorb", '#000000')60 CONFIG = config
43 config.set("terminal", "encoding", 'utf-8')61 # Writing our configuration file
44 # Writing our configuration file62 save_config(config, conffile)
45 with open(CONFIGFILE, 'wb') as f:63 print _("INFO: Created config file at %s.")%conffile
46 config.write(f)64 return config
47 65
48 else:66
49 pass67def get_config_copy(config=None):
5068 global CONFIG
5169 if not config:
5270 config = CONFIG
71 new_cfg = ConfigParser.SafeConfigParser(DEFAULTS)
72 for section in config.sections():
73 new_cfg.add_section(section)
74 for option in config.options(section):
75 new_cfg.set(section, option, config.get(section, option))
76 return new_cfg
77
78
79def get_config(conffile=CONFIGFILE, confdir=CONFIGDIR):
80 global CONFIG
81 config = CONFIG
82 if not config:
83 dbg('Loading new config')
84 if not os.path.isfile(conffile):
85 config = create_config(conffile)
86 config = ConfigParser.SafeConfigParser(DEFAULTS)
87 config.add_section("terminal")
88 config.read([conffile])
89 CONFIG = config
90 else:
91 dbg('Reusing already loaded config')
92 return config
93
94
95def save_config(config, conffile=CONFIGFILE):
96 global CONFIG
97 dbg('Saving conffile at %s'%conffile)
98 with open(CONFIGFILE, 'wb') as f:
99 config.write(f)
100 CONFIG = config
101
102class Cheatsheet:
103 '''
104 comtainer class for the cheatsheet
105
106 Example of usage:
107 >>> c = config.Cheatsheet()
108 >>> c.load('/home/cascara/.clicompanion2')
109 >>> c[3]
110 ['uname -a', '', 'What kernel am I running\n']
111 >>> c.file
112 '/home/cascara/.clicompanion2'
113 >>> c[2]=[ 'mycmd', 'userui', 'desc' ]
114 >>> c[2]
115 ['mycmd', 'userui', 'desc']
116 >>> del c[2]
117 >>> c[2]
118 ['ps aux | grep ?', 'search string', 'Search active processes for search string\n']
119 >>> c.insert('cmd2','ui2','desc2',2)
120 >>> c[2]
121 ['cmd2', 'ui2', 'desc2']
122
123 '''
124 def __init__(self):
125 self.file = CHEATSHEET
126 self.commands = []
127
128 def __repr__(self):
129 return 'Config: %s - %s'%(self.file, self.commands)
130
131 def load(self, cheatfile=None):
132 if not cheatfile:
133 self.file = CHEATSHEET
134 if not os.path.exists(CHEATSHEET):
135 if os.path.exists(CONFIG_ORIG):
136 os.system ("cp %s %s" % (CONFIG_ORIG, CHEATSHEET))
137 else:
138 # Oops! Looks like there's no default cheatsheet.
139 # Then, create an empty cheatsheet.
140 open(CHEATSHEET, 'w').close()
141 else:
142 self.file = cheatfile
143 try:
144 dbg('Reading cheatsheet from file %s'%self.file)
145 with open(self.file, 'r') as ch_fd:
146 ## try to detect if the line is a old fashines config line
147 ## (separated by ':'), when saved will rewrite it
148 no_tabs = True
149 some_colon = False
150 for line in ch_fd:
151 line = line.strip()
152 if not line:
153 continue
154 cmd, ui, desc = [ l.strip() for l in line.split('\t',2)] \
155 + ['',]*(3-len(line.split('\t',2)))
156 if ':' in cmd:
157 some_colon = True
158 if ui or desc:
159 no_tabs = False
160 if cmd and [ cmd, ui, desc ] not in self.commands:
161 self.commands.append([cmd, ui, desc])
162 dbg('Adding command %s'%[cmd, ui, desc])
163 if no_tabs and some_colon:
164 ## None of the commands had tabs, and all had ':' in the
165 ## cmd... most probably old config style
166 print _("Detected old cheatsheet style at")\
167 +" %s"%self.file+_(", parsing to new one.")
168 for i in range(len(self.commands)):
169 cmd, ui, desc = self.commands[i]
170 cmd, ui, desc = [ l.strip() for l in cmd.split(':',2)] \
171 + ['',]*(3-len(cmd.split(':',2)))
172 self.commands[i] = [cmd, ui, desc]
173 self.save()
174 except IOError, e:
175 print _("Error while loading cheatfile")+" %s: %s"%(self.file, e)
176
177 def save(self, cheatfile=None):
178 '''
179 Saves the current config to the file cheatfile, or the file that was
180 loaded.
181 NOTE: It does not overwrite the value self.file, that points to the file
182 that was loaded
183 '''
184 if not cheatfile and self.file:
185 cheatfile = self.file
186 elif not cheatfile:
187 return False
188 try:
189 with open(cheatfile, 'wb') as ch_fd:
190 for command in self.commands:
191 ch_fd.write('\t'.join(command)+'\n')
192 except IOError, e:
193 print _("Error writing cheatfile")+" %s: %s"%(cheatfile, e)
194 return False
195 return True
196
197 def __len__(self):
198 return len(self.commands)
199
200 def __getitem__(self, key):
201 return self.commands[key]
202
203 def __setitem__(self, key, value):
204 if not isinstance(value, collections.Iterable) or len(value) < 3:
205 raise ValueError('Value must be a container with three items, but got %s'%value)
206 if key < len(self.commands):
207 self.commands[key]=list(value)
208 else:
209 try:
210 self.insert(*value, pos=key)
211 except ValueError, e:
212 raise ValueError('Value must be a container with three items, but got %s'%value)
213
214 def __iter__(self):
215 for command in self.commands:
216 yield command
217
218 def insert(self, cmd, ui, desc, pos=None):
219 if not [cmd, ui, desc] in self.commands:
220 if not pos:
221 self.commands.append([cmd, ui, desc])
222 else:
223 self.commands.insert(pos, [cmd, ui, desc])
224
225 def append(self, cmd, ui, desc):
226 self.insert(cmd, ui, desc)
227
228 def index(self, cmd, ui, value):
229 return self.commands.index([cmd, ui, desc])
230
231 def __delitem__(self, key):
232 del self.commands[key]
233
234 def pop(self, key):
235 return self.commands.pop(key)
236
237 def del_by_value(self, cmd, ui, desc):
238 if [cmd, ui, desc] in self.commands:
239 return self.commands.pop(self.commands.index([cmd, ui, desc]))
240
241 def drag_n_drop(self, cmd1, cmd2, before=True):
242 if cmd1 in self.commands:
243 dbg('Dropping command from inside %s'%'_\t_'.join(cmd1))
244 i1 = self.commands.index(cmd1)
245 del self.commands[i1]
246 if cmd2:
247 i2 = self.commands.index(cmd2)
248 if before:
249 self.commands.insert(i2, cmd1)
250 else:
251 self.commands.insert(i2+1, cmd1)
252 else:
253 self.commands.append(cmd1)
254 else:
255 dbg('Dropping command from outside %s'%'_\t_'.join(cmd1))
256 if cmd2:
257 i2 = self.commands.index(cmd2)
258 if before:
259 self.commands.insert(i2, cmd1)
260 else:
261 self.commands.insert(i2+1, cmd1)
262 else:
263 self.commands.append(cmd1)
53264
=== 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 @@
19#19#
20#20#
2121
22from clicompanionlib.utils import get_user_shell22import os
23
24import clicompanionlib.tabs
25import view
26
27import pygtk23import pygtk
28pygtk.require('2.0')24pygtk.require('2.0')
29import re25import re
30import webbrowser26import webbrowser
31import ConfigParser27import view
32import os28import copy
29import clicompanionlib.tabs
30import clicompanionlib.config as cc_config
31import clicompanionlib.utils as utils
32from clicompanionlib.utils import get_user_shell, dbg
33
34#if cc_config.get_config().get('terminal','debug') == 'True':
35# utils.DEBUG = True
36
33# import vte and gtk or print error37# import vte and gtk or print error
34try:38try:
35 import gtk39 import gtk
@@ -49,27 +53,17 @@
49 53
5054
5155
52CHEATSHEET = os.path.expanduser("~/.clicompanion2")
53CONFIG_ORIG = "/etc/clicompanion.d/clicompanion2.config"
54CONFIGFILE = os.path.expanduser("~/.config/clicompanion/config")
55
56class Actions(object):56class Actions(object):
57 #make instances of the Classes we are going to use
58 #main_window = view.MainWindow
59 ## Info Dialog Box57 ## Info Dialog Box
60 ## if a command needs more info EX: a package name, a path58 ## if a command needs more info EX: a package name, a path
61 def get_info(self, widget, liststore):59 def get_info(self, cmd, ui, desc):
6260 dbg('Got command with user input')
63 if not view.ROW:
64 return
65 row_int = int(view.ROW[0][0])
66
67 ## Create Dialog object61 ## Create Dialog object
68 dialog = gtk.MessageDialog(62 dialog = gtk.MessageDialog(
69 None,63 None,
70 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,64 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
71 gtk.MESSAGE_QUESTION,65 gtk.MESSAGE_QUESTION,
72 gtk.BUTTONS_OK,66 gtk.BUTTONS_OK_CANCEL,
73 None)67 None)
7468
75 # Primary text69 # Primary text
@@ -82,16 +76,16 @@
8276
83 ## create a horizontal box to pack the entry and a label77 ## create a horizontal box to pack the entry and a label
84 hbox = gtk.HBox()78 hbox = gtk.HBox()
85 hbox.pack_start(gtk.Label(liststore[row_int][1]+":"), False, 5, 5)79 hbox.pack_start(gtk.Label(ui+":"), False, 5, 5)
86 hbox.pack_end(entry)80 hbox.pack_end(entry)
87 ## some secondary text81 ## some secondary text
88 dialog.format_secondary_markup(_("Please provide a "+liststore[row_int][1]))82 dialog.format_secondary_markup(_("Please provide a "+ui))
89 ## add it and show it83 ## add it and show it
90 dialog.vbox.pack_end(hbox, True, True, 0)84 dialog.vbox.pack_end(hbox, True, True, 0)
91 dialog.show_all()85 dialog.show_all()
9286
93 ## Show the dialog87 ## Show the dialog
94 dialog.run()88 response = dialog.run()
9589
96 ## user text assigned to a variable90 ## user text assigned to a variable
97 text = entry.get_text()91 text = entry.get_text()
@@ -100,13 +94,15 @@
100 ## The destroy method must be called otherwise the 'Close' button will94 ## The destroy method must be called otherwise the 'Close' button will
101 ## not work.95 ## not work.
102 dialog.destroy()96 dialog.destroy()
97 if response != gtk.RESPONSE_OK:
98 user_input = None
103 return user_input99 return user_input
104100
105 def responseToDialog(self, text, dialog, response):101 def responseToDialog(self, text, dialog, response):
106 dialog.response(response)102 dialog.response(response)
107103
108 ## Add command dialog box104 ## Add command dialog box
109 def add_command(self, widget, liststore):105 def add_command(self, mw):
110106
111 ## Create Dialog object107 ## Create Dialog object
112 dialog = gtk.MessageDialog(108 dialog = gtk.MessageDialog(
@@ -141,15 +137,16 @@
141 hbox2.pack_start(entry3, True, 5, 5)137 hbox2.pack_start(entry3, True, 5, 5)
142138
143 ## cancel button139 ## cancel button
144 dialog.add_button('Cancel', gtk.RESPONSE_DELETE_EVENT)140 dialog.add_button(_('Cancel'), gtk.RESPONSE_DELETE_EVENT)
145 ## some secondary text141 ## some secondary text
146 dialog.format_secondary_markup(_('When entering a command use question '142 dialog.format_secondary_markup(
147 'marks(?) as placeholders if user input is required when the '143 _("When entering a command use question marks(?) as placeholders if"
148 'command runs. Example: ls /any/directory would be entered as, '144 " user input is required when the command runs. Example: ls "
149 'ls ? .For each question mark(?) in your command, if any, use '145 "/any/directory would be entered as, ls ? .For each question "
150 'the User Input field to provide a hint for each variable. '146 "mark(?) in your command, if any, use the User Input field to "
151 'Using our example ls ? you could put directory as the User '147 "provide a hint for each variable. Using our example ls ? you "
152 'Input. Lastly provide a brief Description.'))148 "could put directory as the User Input. Lastly provide a brief "
149 "Description."))
153150
154 ## add it and show it151 ## add it and show it
155 dialog.vbox.pack_end(hbox2, True, True, 0)152 dialog.vbox.pack_end(hbox2, True, True, 0)
@@ -163,24 +160,10 @@
163 text1 = entry1.get_text()160 text1 = entry1.get_text()
164 text2 = entry2.get_text()161 text2 = entry2.get_text()
165 text3 = entry3.get_text()162 text3 = entry3.get_text()
166 '''open flat file, add the new command, update CMNDS variable163 ## update commandsand sync with screen '''
167 ## update commands in liststore (on screen) '''164 view.CMNDS.append(text1, text2, text3)
168 if text1 != "":165 mw.sync_cmnds()
169 with open(CHEATSHEET, "r") as cheatfile:166 view.CMNDS.save()
170 cheatlines = cheatfile.readlines()
171 cheatlines.append(text1+"\t"+text2+"\t"+text3+'\n')
172 cheatfile.close()
173 with open(CHEATSHEET, "w") as cheatfile2:
174 cheatfile2.writelines(cheatlines)
175 cheatfile2.close()
176 l = str(text1+"\t"+text2+"\t"+text3)
177 #ls = l.split(':',2)
178 ## update view.CMNDS variable
179 filteredcommandplus = text1, text2, text3
180 view.CMNDS.append(filteredcommandplus)
181 ## update the command list on screen
182 liststore.append([text1,text2,text3])
183
184167
185 ## The destroy method must be called otherwise the 'Close' button will168 ## The destroy method must be called otherwise the 'Close' button will
186 ## not work.169 ## not work.
@@ -188,32 +171,14 @@
188 #return text171 #return text
189172
190 ## This the edit function173 ## This the edit function
191 def edit_command(self, widget , liststore):174 def edit_command(self, mw):
192
193 if not view.ROW:175 if not view.ROW:
194 return176 return
195 row_int_x = int(view.ROW[0][0])177 lst_index = int(view.ROW[0][0])
196 row_int = 0178 model = mw.treeview.get_model()
197 ## TODO: Not implemented with filted yet179 cmd = ''.join(model[lst_index][0])
198 if view.FILTER == 1:180 ui = ''.join(model[lst_index][1])
199 with open(CHEATSHEET, "r") as cheatfile:181 desc = ''.join(model[lst_index][2])
200 cheatlines = cheatfile.readlines()
201 for i in range(len(cheatlines)):
202 if view.CMNDS[row_int_x][0] in cheatlines[i] and view.CMNDS[row_int_x][1] in cheatlines[i] :
203 row_int = i
204 cheatfile.close()
205 else:
206 row_int = row_int_x
207
208
209 row_obj1 = view.MainWindow.liststore[row_int][0]
210 text1 = "".join(row_obj1)
211
212 row_obj2 = view.MainWindow.liststore[row_int][1]
213 text2 = "".join(row_obj2)
214
215 row_obj3 = view.MainWindow.liststore[row_int][2]
216 text3 = "".join(row_obj3)
217182
218 ## Create Dialog object183 ## Create Dialog object
219 dialog = gtk.MessageDialog(184 dialog = gtk.MessageDialog(
@@ -228,11 +193,11 @@
228193
229 ## create the text input fields194 ## create the text input fields
230 entry1 = gtk.Entry()195 entry1 = gtk.Entry()
231 entry1.set_text(text1)196 entry1.set_text(cmd)
232 entry2 = gtk.Entry()197 entry2 = gtk.Entry()
233 entry2.set_text(text2)198 entry2.set_text(ui)
234 entry3 = gtk.Entry()199 entry3 = gtk.Entry()
235 entry3.set_text(text3)200 entry3.set_text(desc)
236 ## allow the user to press enter to do ok201 ## allow the user to press enter to do ok
237 entry1.connect("activate", self.responseToDialog, dialog, gtk.RESPONSE_OK)202 entry1.connect("activate", self.responseToDialog, dialog, gtk.RESPONSE_OK)
238203
@@ -249,7 +214,7 @@
249 hbox2.pack_start(entry3, True, 5, 5)214 hbox2.pack_start(entry3, True, 5, 5)
250215
251 ## cancel button216 ## cancel button
252 dialog.add_button('Cancel', gtk.RESPONSE_DELETE_EVENT)217 dialog.add_button(_('Cancel'), gtk.RESPONSE_DELETE_EVENT)
253 ## some secondary text218 ## some secondary text
254 dialog.format_secondary_markup(_("Please provide a command, description, and what type of user variable, if any, is required."))219 dialog.format_secondary_markup(_("Please provide a command, description, and what type of user variable, if any, is required."))
255220
@@ -262,64 +227,35 @@
262227
263 if result == gtk.RESPONSE_OK:228 if result == gtk.RESPONSE_OK:
264 ## user text assigned to a variable229 ## user text assigned to a variable
265 text1 = entry1.get_text()230 cmd = entry1.get_text()
266 text2 = entry2.get_text()231 ui = entry2.get_text()
267 text3 = entry3.get_text()232 desc = entry3.get_text()
268233
269 if text1 != "":234 if cmd != "":
270 self.remove_command(widget, liststore)235 cmd_index = model[lst_index][3]
271 '''open flat file, add the new command, update CMNDS variable236 dbg('Got index %d for command at pos %d'%(cmd_index, lst_index))
272 ## update commands in liststore (on screen) '''237 view.CMNDS[cmd_index] = [cmd, ui, desc]
273 238 mw.sync_cmnds()
274 with open(CHEATSHEET, "r") as cheatfile:239 view.CMNDS.save()
275 cheatlines = cheatfile.readlines()
276 cheatlines.append(text1+":"+text2+":"+text3+'\n')
277 cheatfile.close()
278 with open(CHEATSHEET, "w") as cheatfile2:
279 cheatfile2.writelines(cheatlines)
280 cheatfile2.close()
281 l = str(text1+":"+text2+":"+text3)
282 #ls = l.split(':',2)
283 ## update view.CMNDS variable
284 filteredcommandplus = text1, text2, text3
285 view.CMNDS.append(filteredcommandplus)
286 ## update the command list on screen
287 liststore.append([text1,text2,text3])
288
289 ## The destroy method must be called otherwise the 'Close' button will240 ## The destroy method must be called otherwise the 'Close' button will
290 ## not work.241 ## not work.
291 dialog.destroy()242 dialog.destroy()
292243
293244
294 ## Remove command from command file and GUI245 ## Remove command from command file and GUI
295 def remove_command(self, widget, liststore):246 def remove_command(self, mw):
296
297 if not view.ROW:247 if not view.ROW:
298 return248 return
299 row_int_x = int(view.ROW[0][0])249 ## get selected row
300 row_int = 0250 lst_index = int(view.ROW[0][0])
301 ## TODO: Not implemented with filted yet251 ## get selected element index, even from search filter
302 if view.FILTER == 1:252 model = mw.treeview.get_model()
303 with open(CHEATSHEET, "r") as cheatfile:253 cmd_index = model[lst_index][3]
304 cheatlines = cheatfile.readlines()254 ## delete element from liststore and CMNDS
305 for i in range(len(cheatlines)):255 del view.CMNDS[cmd_index]
306 if view.CMNDS[row_int_x][0] in cheatlines[i] and view.CMNDS[row_int_x][1] in cheatlines[i]:256 mw.sync_cmnds()
307 row_int = i 257 ## save changes
308 cheatfile.close()258 view.CMNDS.save()
309 else:
310 row_int = row_int_x
311
312 del view.CMNDS[row_int_x]
313 del liststore[row_int]
314
315 ## open command file and delete line so the change is persistent
316 with open(CHEATSHEET, "r") as cheatfile:
317 cheatlines = cheatfile.readlines()
318 del cheatlines[row_int]
319 cheatfile.close()
320 with open(CHEATSHEET, "w") as cheatfile2:
321 cheatfile2.writelines(cheatlines)
322 cheatfile2.close()
323259
324260
325 def _filter_commands(self, widget, liststore, treeview):261 def _filter_commands(self, widget, liststore, treeview):
@@ -330,11 +266,13 @@
330 Pretty straight-forward.266 Pretty straight-forward.
331 """267 """
332 search_term = widget.get_text().lower()268 search_term = widget.get_text().lower()
269 ## If the search term is empty, restore the liststore
333 if search_term == "":270 if search_term == "":
334 view.FILTER = 0271 view.FILTER = 0
335 else:272 treeview.set_model(liststore)
336 view.FILTER = 1273 return
337 274
275 view.FILTER = 1
338 ## Create a TreeModelFilter object which provides auxiliary functions for276 ## Create a TreeModelFilter object which provides auxiliary functions for
339 ## filtering data.277 ## filtering data.
340 ## http://www.pygtk.org/pygtk2tutorial/sec-TreeModelSortAndTreeModelFilter.html278 ## http://www.pygtk.org/pygtk2tutorial/sec-TreeModelSortAndTreeModelFilter.html
@@ -356,39 +294,31 @@
356 ## Python raises a AttributeError if row data was modified . Catch294 ## Python raises a AttributeError if row data was modified . Catch
357 ## that and fail silently.295 ## that and fail silently.
358 pass296 pass
359
360
361 modelfilter.set_visible_func(search, search_term)297 modelfilter.set_visible_func(search, search_term)
298 ## save the old liststore and cmnds
362 treeview.set_model(modelfilter)299 treeview.set_model(modelfilter)
363
364
365 #clear CMNDS list then populate it with the filteredlist of commands
366 view.CMNDS = []
367 for line in modelfilter:
368 filteredcommandplus = tuple(modelfilter.get(line.iter, 0, 1, 2))
369 view.CMNDS.append(filteredcommandplus)
370
371
372300
373 ## send the command to the terminal301 ## send the command to the terminal
374 def run_command(self, widget, notebook, liststore):302 def run_command(self, mw):
375303
376 ## if called without selecting a command from the list return304 ## if called without selecting a command from the list return
377 if not view.ROW:305 if not view.ROW:
378 return306 return
379 text = ""307 text = ""
380 row_int = int(view.ROW[0][0]) ## removes everything but number from [5,]308 lst_index = int(view.ROW[0][0]) ## removes everything but number from [5,]
381309
382 ## get the current notebook page so the function knows which terminal to run the command in.310 ## get the current notebook page so the function knows which terminal to run the command in.
383 pagenum = notebook.get_current_page()311 pagenum = mw.notebook.get_current_page()
384 widget = notebook.get_nth_page(pagenum)312 widget = mw.notebook.get_nth_page(pagenum)
385 page_widget = widget.get_child()313 page_widget = widget.get_child()
386314
387 ## view.CMNDS is where commands are stored315 model = mw.treeview.get_model()
388 cmnd = view.CMNDS[row_int][0]316 cmd = ''.join(model[lst_index][0])
317 ui = ''.join(model[lst_index][1])
318 desc = ''.join(model[lst_index][2])
389 319
390 ## find how many ?(user arguments) are in command320 ## find how many ?(user arguments) are in command
391 match = re.findall('\?', cmnd) 321 match = re.findall('\?', cmd)
392 '''322 '''
393 Make sure user arguments were found. Replace ? with something323 Make sure user arguments were found. Replace ? with something
394 .format can read. This is done so the user can just enter ?, when324 .format can read. This is done so the user can just enter ?, when
@@ -400,23 +330,33 @@
400 else:330 else:
401 num = len(match)331 num = len(match)
402 ran = 0332 ran = 0
403 new_cmnd = self.replace(cmnd, num, ran)333 new_cmnd = self.replace(cmd, num, ran)
404334
405335 if len(match) > 0: # command with user input
406 if not view.CMNDS[row_int][1] == "": # command with user input336 dbg('command with ui')
407 c = ""337 f_cmd = ""
408 try:338 while True:
409 text = self.get_info(self, liststore)339 try:
410 c = new_cmnd.format(text)340 ui_text = self.get_info(cmd, ui, desc)
411 except: 341 if ui_text == None:
412 error = gtk.MessageDialog (None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,342 return
413 _("You need to enter full input. Click on [x] to continue."))343 dbg('Got ui "%s"'%' '.join(ui_text))
414 error.run()344 if ''.join(ui_text) == '':
415 page_widget.feed_child(c+"\n") #send command w/ input345 raise IndexError
346 f_cmd = new_cmnd.format(ui_text)
347 except IndexError, e:
348 error = gtk.MessageDialog (None, gtk.DIALOG_MODAL, \
349 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
350 _("You need to enter full input. Space separated."))
351 error.connect('response', lambda err, *x: err.destroy())
352 error.run()
353 continue
354 break
355 page_widget.feed_child(f_cmd+"\n") #send command w/ input
416 page_widget.show()356 page_widget.show()
417 page_widget.grab_focus()357 page_widget.grab_focus()
418 else: ## command that has no user input358 else: ## command that has no user input
419 page_widget.feed_child(cmnd+"\n") #send command359 page_widget.feed_child(cmd+"\n") #send command
420 page_widget.show()360 page_widget.show()
421 page_widget.grab_focus()361 page_widget.grab_focus()
422 362
@@ -432,7 +372,9 @@
432 return cmnd372 return cmnd
433 373
434 ## open the man page for selected command374 ## open the man page for selected command
435 def man_page(self, widget, notebook):375 def man_page(self, notebook):
376 import subprocess as sp
377 import shlex
436 try:378 try:
437 row_int = int(view.ROW[0][0]) # removes everything but number from EX: [5,]379 row_int = int(view.ROW[0][0]) # removes everything but number from EX: [5,]
438 except IndexError: 380 except IndexError:
@@ -443,21 +385,67 @@
443 gtk.MESSAGE_QUESTION,385 gtk.MESSAGE_QUESTION,
444 gtk.BUTTONS_OK,386 gtk.BUTTONS_OK,
445 None)387 None)
446 dialog.set_markup('You must choose row to view help')388 dialog.set_markup(_('You must choose a row to view the help'))
447 dialog.show_all()389 dialog.show_all()
448 dialog.run()390 dialog.run()
449 dialog.destroy() 391 dialog.destroy()
450 return 392 return
393 ## get the manpage for the command
451 cmnd = view.CMNDS[row_int][0] #CMNDS is where commands are store394 cmnd = view.CMNDS[row_int][0] #CMNDS is where commands are store
452 splitcommand = self._filter_sudo_from(cmnd.split(" "))395 ## get each command for each pipe, It's not 100 accurate, but good
453 ## get current notebook tab to use in function396 ## enough (by now)
454 pagenum = notebook.get_current_page()397 commands = []
455 widget = notebook.get_nth_page(pagenum)398 next_part = True
456 page_widget = widget.get_child()399 found_sudo = False
457 #send command to Terminal400 for part in shlex.split(cmnd):
458 page_widget.feed_child("man "+splitcommand[0]+"| most \n") 401 if next_part:
459 page_widget.grab_focus()402 if part == 'sudo' and not found_sudo:
460 page_widget.show()403 found_sudo = True
404 commands.append('sudo')
405 else:
406 if part not in commands:
407 commands.append(part)
408 next_part = False
409 else:
410 if part in [ '||', '&&', '&', '|']:
411 next_part = True
412
413 notebook = gtk.Notebook()
414 notebook.set_scrollable(True)
415 notebook.popup_enable()
416 notebook.set_properties(group_id=0, tab_vborder=0, tab_hborder=1, tab_pos=gtk.POS_TOP)
417 ## create a tab for each command
418 for command in commands:
419 scrolled_page = gtk.ScrolledWindow()
420 scrolled_page.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
421 tab = gtk.HBox()
422 tab_label = gtk.Label(command)
423 tab_label.show()
424 tab.pack_start(tab_label)
425 page = gtk.TextView()
426 page.set_wrap_mode(gtk.WRAP_WORD)
427 page.set_editable(False)
428 page.set_cursor_visible(False)
429 try:
430 manpage = sp.check_output(["man",command])
431 except sp.CalledProcessError, e:
432 manpage = _('Failed to get manpage for command "%s"\nReason:\n%s')%(
433 command, e)
434 textbuffer = page.get_buffer()
435 textbuffer.set_text(manpage)
436 scrolled_page.add(page)
437 notebook.append_page(scrolled_page, tab)
438
439 help_win = gtk.Dialog()
440 help_win.set_title(_("Man page for %s")%cmnd)
441 help_win.vbox.pack_start(notebook, True, True, 0)
442 button = gtk.Button("close")
443 button.connect_object("clicked", lambda self: self.destroy(), help_win)
444 button.set_flags(gtk.CAN_DEFAULT)
445 help_win.action_area.pack_start( button, True, True, 0)
446 button.grab_default()
447 help_win.set_default_size(500,600)
448 help_win.show_all()
461449
462450
463 @staticmethod451 @staticmethod
@@ -471,7 +459,6 @@
471 return command459 return command
472460
473461
474
475 #TODO: Move to menus_buttons462 #TODO: Move to menus_buttons
476 def copy_paste(self, vte, event, data=None):463 def copy_paste(self, vte, event, data=None):
477 if event.button == 3:464 if event.button == 3:
@@ -517,11 +504,11 @@
517504
518 # Add a short comment about the application, this appears below the application505 # Add a short comment about the application, this appears below the application
519 # name in the dialog506 # name in the dialog
520 dialog.set_comments('This is a CLI Companion program.')507 dialog.set_comments(_('This is a CLI Companion program.'))
521508
522 # Add license information, this is connected to the 'License' button509 # Add license information, this is connected to the 'License' button
523 # and is displayed in a new window.510 # and is displayed in a new window.
524 dialog.set_license('Distributed under the GNU license. You can see it at <http://www.gnu.org/licenses/>.')511 dialog.set_license(_('Distributed under the GNU license. You can see it at <http://www.gnu.org/licenses/>.'))
525512
526 # Show the dialog513 # Show the dialog
527 dialog.run()514 dialog.run()
@@ -529,11 +516,13 @@
529 # The destroy method must be called otherwise the 'Close' button will516 # The destroy method must be called otherwise the 'Close' button will
530 # not work.517 # not work.
531 dialog.destroy()518 dialog.destroy()
519
520
532 def help_event(self, widget, data=None):521 def help_event(self, widget, data=None):
533 webbrowser.open("http://okiebuntu.homelinux.com/okwiki/clicompanion")522 webbrowser.open("http://launchpad.net/clicompanion")
534 523
524
535 def usage_event(self, widget, data=None):525 def usage_event(self, widget, data=None):
536 mw = view.MainWindow
537 dialog = gtk.Dialog("Usage",526 dialog = gtk.Dialog("Usage",
538 None,527 None,
539 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,528 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
@@ -573,139 +562,104 @@
573 562
574 ## File --> Preferences 563 ## File --> Preferences
575 def changed_cb(self, combobox, config):564 def changed_cb(self, combobox, config):
576 config.read(CONFIGFILE)565 dbg('Changed encoding')
577 model = combobox.get_model()566 model = combobox.get_model()
578 index = combobox.get_active()567 index = combobox.get_active()
579 if index:568 if index>=0:
580 text_e = model[index][0]569 text_e = model[index][0]
581 config.set("terminal", "encoding", text_e)570 encoding = text_e.split(':',1)[0].strip()
582 # Writing our configuration file571 dbg('Setting encoding to "%s"'%encoding)
583 with open(CONFIGFILE, 'wb') as f:572 config.set("terminal", "encoding", encoding)
584 config.write(f)
585573
586 574
587 def color_set_fg_cb(self, colorbutton_fg, config):575 def color_set_fg_cb(self, colorbutton_fg, config, tabs):
588 config.read(CONFIGFILE)576 dbg('Changing fg color')
589 #colorf16 = colorbutton_fg.get_color()
590 colorf = self.color2hex(colorbutton_fg)577 colorf = self.color2hex(colorbutton_fg)
591 config.set("terminal", "colorf", str(colorf)) 578 config.set("terminal", "colorf", str(colorf))
592 # Writing our configuration file579 tabs.update_all_term_config(config)
593 with open(CONFIGFILE, 'wb') as f:580
594 config.write(f)581
595582 def color_set_bg_cb(self, colorbutton_bg, config, tabs):
596583 dbg('Changing bg color')
597
598 def color_set_bg_cb(self, colorbutton_bg, config):
599 config.read(CONFIGFILE)
600 #colorb16 = colorbutton_bg.get_color()
601 colorb = self.color2hex(colorbutton_bg)584 colorb = self.color2hex(colorbutton_bg)
602 config.set("terminal", "colorb", str(colorb))585 config.set("terminal", "colorb", str(colorb))
603 # Writing our configuration file586 tabs.update_all_term_config(config)
604 with open(CONFIGFILE, 'wb') as f:
605 config.write(f)
606
607 587
608 588
609 def color2hex(self, widget):589 def color2hex(self, widget):
610 """Pull the colour values out of a Gtk ColorPicker widget and return them590 """Pull the colour values out of a Gtk ColorPicker widget and return them
611 as 8bit hex values, sinces its default behaviour is to give 16bit values"""591 as 8bit hex values, sinces its default behaviour is to give 16bit values"""
612 widcol = widget.get_color()592 widcol = widget.get_color()
613 print widcol
614 return('#%02x%02x%02x' % (widcol.red>>8, widcol.green>>8, widcol.blue>>8))593 return('#%02x%02x%02x' % (widcol.red>>8, widcol.green>>8, widcol.blue>>8))
615 594
616 def preferences(self, widget, data=None):595 def preferences(self, tabs, data=None):
617 '''596 '''
618 Preferences window597 Preferences window
619 '''598 '''
620 mw = view.MainWindow599 dialog = gtk.Dialog(_("User Preferences"),
621 dialog = gtk.Dialog("User Preferences",
622 None,600 None,
623 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,601 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
624 (gtk.STOCK_CANCEL, gtk.RESPONSE_CLOSE,602 (gtk.STOCK_CANCEL, gtk.RESPONSE_CLOSE,
625 gtk.STOCK_OK, gtk.RESPONSE_OK))603 gtk.STOCK_OK, gtk.RESPONSE_OK))
626 604
627 config = ConfigParser.RawConfigParser()605 config = cc_config.get_config_copy()
628 config.read(CONFIGFILE)606
629
630 ##create the text input fields607 ##create the text input fields
631 entry1 = gtk.Entry()608 entry1 = gtk.Entry()
632 entry1.set_text(config.get('terminal', 'scrollb'))609 entry1.set_text(config.get('terminal', 'scrollb'))
633
634 610
635 ##combobox for selecting encoding611 ##combobox for selecting encoding
636 combobox = gtk.combo_box_new_text()612 combobox = gtk.combo_box_new_text()
637 combobox.append_text('Select encoding:')613 i=0
638 combobox.append_text('UTF-8')614 for encoding, desc in utils.encodings:
639 combobox.append_text('ISO-8859-1')615 combobox.append_text(encoding + ': '+desc)
640 combobox.append_text('ISO-8859-15')616 if encoding.strip().upper() == config.get('terminal','encoding').upper():
641617 active = i
618 i=i+1
619 combobox.set_active(active)
642 combobox.connect('changed', self.changed_cb, config)620 combobox.connect('changed', self.changed_cb, config)
643 combobox.set_active(0)
644 621
645 ##colorbox for selecting text and background color622 ##colorbox for selecting text and background color
646 colorbutton_fg = gtk.ColorButton(gtk.gdk.color_parse('white'))623 colorbutton_fg = gtk.ColorButton(
647 colorbutton_bg = gtk.ColorButton(gtk.gdk.color_parse('black'))624 gtk.gdk.color_parse(config.get('terminal','colorf')))
648625 colorbutton_bg = gtk.ColorButton(
649 colorbutton_fg.connect('color-set', self.color_set_fg_cb, config)626 gtk.gdk.color_parse(config.get('terminal','colorb')))
650 colorbutton_bg.connect('color-set', self.color_set_bg_cb, config)627
651628 colorbutton_fg.connect('color-set', self.color_set_fg_cb, config, tabs)
652 #dialog.show_all()629 colorbutton_bg.connect('color-set', self.color_set_bg_cb, config, tabs)
653 630
654 ## allow the user to press enter to do ok631 ## allow the user to press enter to do ok
655 entry1.connect("activate", self.responseToDialog, dialog, gtk.RESPONSE_OK)632 entry1.connect("activate", self.responseToDialog, dialog, gtk.RESPONSE_OK)
656633
657634 ## create the labels
658
659 ## create three labels
660 hbox1 = gtk.HBox()635 hbox1 = gtk.HBox()
661 hbox1.pack_start(gtk.Label(_("Scrollback")), False, 5, 5)636 hbox1.pack_start(gtk.Label(_("Scrollback")), False, 5, 5)
662 hbox1.pack_start(entry1, False, 5, 5)637 hbox1.pack_start(entry1, False, 5, 5)
663638
664 hbox1.pack_start(gtk.Label(_("encoding")), False, 5, 5)639 hbox1.pack_start(gtk.Label(_("Encoding")), False, 5, 5)
665 hbox1.pack_start(combobox, False, 5, 5)640 hbox1.pack_start(combobox, False, 5, 5)
666641
667
668 hbox2 = gtk.HBox()642 hbox2 = gtk.HBox()
669 hbox2.pack_start(gtk.Label(_("font color")), False, 5, 5)643 hbox2.pack_start(gtk.Label(_("Font color")), False, 5, 5)
670 hbox2.pack_start(colorbutton_fg, True, 5, 5)644 hbox2.pack_start(colorbutton_fg, True, 5, 5)
671 645
672 hbox2.pack_start(gtk.Label(_("background color")), False, 5, 5)646 hbox2.pack_start(gtk.Label(_("Background color")), False, 5, 5)
673 hbox2.pack_start(colorbutton_bg, True, 5, 5)647 hbox2.pack_start(colorbutton_bg, True, 5, 5)
674 648
675
676 ## add it and show it649 ## add it and show it
677 dialog.vbox.pack_end(hbox2, True, True, 0)650 dialog.vbox.pack_end(hbox2, True, True, 0)
678 dialog.vbox.pack_end(hbox1, True, True, 0)651 dialog.vbox.pack_end(hbox1, True, True, 0)
679 dialog.show_all()652 dialog.show_all()
680 653
681 result = dialog.run()654 result = dialog.run()
682
683 if result == gtk.RESPONSE_OK:655 if result == gtk.RESPONSE_OK:
684
685 ## user text assigned to a variable656 ## user text assigned to a variable
686 text_sb = entry1.get_text()657 text_sb = entry1.get_text()
687
688 config.read(CONFIGFILE)
689 config.set("terminal", "scrollb", text_sb)658 config.set("terminal", "scrollb", text_sb)
690659 cc_config.save_config(config)
691660 tabs.update_all_term_config()
692
693 # Writing our configuration file
694 with open(CONFIGFILE, 'wb') as f:
695 config.write(f)
696
697
698 ## instantiate tabs
699 tabs = clicompanionlib.tabs.Tabs()
700 tabs.update_term_config
701661
702 ## The destroy method must be called otherwise the 'Close' button will662 ## The destroy method must be called otherwise the 'Close' button will
703 ## not work.663 ## not work.
704 dialog.destroy()664 dialog.destroy()
705665
706 def save_cmnds(self):
707 with open(CHEATSHEET, "w") as cheatfile:
708 for command in view.CMNDS:
709 cheatfile.write('\t'.join(command)+'\n')
710
711
712666
=== 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 @@
2626
27class FileMenu(object):27class FileMenu(object):
2828
29 def the_menu(self, actions, notebook, liststore):29 def the_menu(self, mw):
30 actions = mw.actions
31 liststore = mw.liststore
32 tabs = mw.tabs
33 notebook = mw.notebook
30 menu = gtk.Menu()34 menu = gtk.Menu()
31 #color = gtk.gdk.Color(65555, 62000, 65555)35 #color = gtk.gdk.Color(65555, 62000, 65555)
32 #menu.modify_bg(gtk.STATE_NORMAL, color)36 #menu.modify_bg(gtk.STATE_NORMAL, color)
@@ -43,33 +47,31 @@
43 ## Make 'Run' menu entry47 ## Make 'Run' menu entry
44 menu_item1 = gtk.MenuItem(_("Run Command [F4]"))48 menu_item1 = gtk.MenuItem(_("Run Command [F4]"))
45 menu.append(menu_item1)49 menu.append(menu_item1)
46 menu_item1.connect("activate", actions.run_command, notebook, liststore)50 menu_item1.connect("activate", lambda *x: actions.run_command(mw))
47 menu_item1.show()51 menu_item1.show()
4852
49 ## Make 'Add' file menu entry53 ## Make 'Add' file menu entry
50 menu_item2 = gtk.MenuItem(_("Add Command [F5]"))54 menu_item2 = gtk.MenuItem(_("Add Command [F5]"))
51 menu.append(menu_item2)55 menu.append(menu_item2)
52 menu_item2.connect("activate", actions.add_command, liststore)56 menu_item2.connect("activate", lambda *x: actions.add_command(mw))
53 menu_item2.show()57 menu_item2.show()
54 58
55 ## Make 'Remove' file menu entry59 ## Make 'Remove' file menu entry
56 menu_item3 = gtk.MenuItem(_("Remove Command [F6]"))60 menu_item3 = gtk.MenuItem(_("Remove Command [F6]"))
57 menu.append(menu_item3)61 menu.append(menu_item3)
58 menu_item3.connect("activate", actions.remove_command, liststore)62 menu_item3.connect("activate", lambda *x: actions.remove_command(mw))
59 menu_item3.show()63 menu_item3.show()
60 64
61 ## Make 'Add Tab' file menu entry65 ## Make 'Add Tab' file menu entry
62 tab = tabs.Tabs()
63 menu_item4 = gtk.MenuItem(_("Add Tab [F7]"))66 menu_item4 = gtk.MenuItem(_("Add Tab [F7]"))
64 menu.append(menu_item4)67 menu.append(menu_item4)
65 menu_item4.connect("activate", tab.add_tab, notebook)68 menu_item4.connect("activate", lambda *x: tabs.add_tab(notebook))
66 menu_item4.show()69 menu_item4.show()
67 70
68 ## Make 'User Preferences' file menu entry71 ## Make 'User Preferences' file menu entry
69 #tab = tabs.Tabs()
70 menu_item5 = gtk.MenuItem(_("Preferences"))72 menu_item5 = gtk.MenuItem(_("Preferences"))
71 menu.append(menu_item5)73 menu.append(menu_item5)
72 menu_item5.connect("activate", actions.preferences)74 menu_item5.connect("activate", lambda *x: actions.preferences(tabs))
73 menu_item5.show()75 menu_item5.show()
7476
75 ## Make 'Quit' file menu entry77 ## Make 'Quit' file menu entry
@@ -113,7 +115,7 @@
113 115
114 116
115 117
116 def buttons(self, actions, spacing, layout, notebook, liststore):118 def buttons(self, mw, spacing, layout):
117 #button box at bottom of main window119 #button box at bottom of main window
118 frame = gtk.Frame()120 frame = gtk.Frame()
119 bbox = gtk.HButtonBox()121 bbox = gtk.HButtonBox()
@@ -125,9 +127,9 @@
125 bbox.set_layout(layout)127 bbox.set_layout(layout)
126 bbox.set_spacing(spacing)128 bbox.set_spacing(spacing)
127 # Run button129 # Run button
128 buttonRun = gtk.Button(_("Run"))130 buttonRun = gtk.Button('_'+_("Run"))
129 bbox.add(buttonRun)131 bbox.add(buttonRun)
130 buttonRun.connect("clicked", actions.run_command, notebook, liststore)132 buttonRun.connect("clicked", lambda *x: mw.actions.run_command(mw))
131 buttonRun.set_tooltip_text(_("Click to run a highlighted command"))133 buttonRun.set_tooltip_text(_("Click to run a highlighted command"))
132 #buttonRun.modify_bg(gtk.STATE_NORMAL, color) 134 #buttonRun.modify_bg(gtk.STATE_NORMAL, color)
133 #buttonRun.modify_bg(gtk.STATE_PRELIGHT, color) 135 #buttonRun.modify_bg(gtk.STATE_PRELIGHT, color)
@@ -135,15 +137,15 @@
135 # Add button137 # Add button
136 buttonAdd = gtk.Button(stock=gtk.STOCK_ADD)138 buttonAdd = gtk.Button(stock=gtk.STOCK_ADD)
137 bbox.add(buttonAdd)139 bbox.add(buttonAdd)
138 buttonAdd.connect("clicked", actions.add_command, liststore)140 buttonAdd.connect("clicked", lambda *x: mw.actions.add_command(mw))
139 buttonAdd.set_tooltip_text(_("Click to add a command to your command list"))141 buttonAdd.set_tooltip_text(_("Click to add a command to your command list"))
140 #buttonAdd.modify_bg(gtk.STATE_NORMAL, color) 142 #buttonAdd.modify_bg(gtk.STATE_NORMAL, color)
141 #buttonAdd.modify_bg(gtk.STATE_PRELIGHT, color) 143 #buttonAdd.modify_bg(gtk.STATE_PRELIGHT, color)
142 #buttonAdd.modify_bg(gtk.STATE_INSENSITIVE, color)144 #buttonAdd.modify_bg(gtk.STATE_INSENSITIVE, color)
143 # Edit button145 # Edit button
144 buttonEdit = gtk.Button(_("Edit"))146 buttonEdit = gtk.Button('_'+_("Edit"))
145 bbox.add(buttonEdit)147 bbox.add(buttonEdit)
146 buttonEdit.connect("clicked", actions.edit_command, liststore)148 buttonEdit.connect("clicked", lambda *x: mw.actions.edit_command(mw))
147 buttonEdit.set_tooltip_text(_("Click to edit a command in your command list"))149 buttonEdit.set_tooltip_text(_("Click to edit a command in your command list"))
148 #buttonEdit.modify_bg(gtk.STATE_NORMAL, color) 150 #buttonEdit.modify_bg(gtk.STATE_NORMAL, color)
149 #buttonEdit.modify_bg(gtk.STATE_PRELIGHT, color) 151 #buttonEdit.modify_bg(gtk.STATE_PRELIGHT, color)
@@ -151,7 +153,7 @@
151 # Delete button153 # Delete button
152 buttonDelete = gtk.Button(stock=gtk.STOCK_DELETE)154 buttonDelete = gtk.Button(stock=gtk.STOCK_DELETE)
153 bbox.add(buttonDelete)155 bbox.add(buttonDelete)
154 buttonDelete.connect("clicked", actions.remove_command, liststore)156 buttonDelete.connect("clicked", lambda *x: mw.actions.remove_command(mw))
155 buttonDelete.set_tooltip_text(_("Click to delete a command in your command list"))157 buttonDelete.set_tooltip_text(_("Click to delete a command in your command list"))
156 #buttonDelete.modify_bg(gtk.STATE_NORMAL, color) 158 #buttonDelete.modify_bg(gtk.STATE_NORMAL, color)
157 #buttonDelete.modify_bg(gtk.STATE_PRELIGHT, color) 159 #buttonDelete.modify_bg(gtk.STATE_PRELIGHT, color)
@@ -159,7 +161,7 @@
159 #Help Button161 #Help Button
160 buttonHelp = gtk.Button(stock=gtk.STOCK_HELP)162 buttonHelp = gtk.Button(stock=gtk.STOCK_HELP)
161 bbox.add(buttonHelp)163 bbox.add(buttonHelp)
162 buttonHelp.connect("clicked", actions.man_page, notebook)164 buttonHelp.connect("clicked", lambda *x: mw.actions.man_page(mw.notebook))
163 buttonHelp.set_tooltip_text(_("Click to get help with a command in your command list"))165 buttonHelp.set_tooltip_text(_("Click to get help with a command in your command list"))
164 #buttonHelp.modify_bg(gtk.STATE_NORMAL, color) 166 #buttonHelp.modify_bg(gtk.STATE_NORMAL, color)
165 #buttonHelp.modify_bg(gtk.STATE_PRELIGHT, color) 167 #buttonHelp.modify_bg(gtk.STATE_PRELIGHT, color)
@@ -167,7 +169,7 @@
167 # Cancel button169 # Cancel button
168 buttonCancel = gtk.Button(stock=gtk.STOCK_QUIT)170 buttonCancel = gtk.Button(stock=gtk.STOCK_QUIT)
169 bbox.add(buttonCancel)171 bbox.add(buttonCancel)
170 buttonCancel.connect("clicked", actions.delete_event)172 buttonCancel.connect("clicked", mw.actions.delete_event)
171 buttonCancel.set_tooltip_text(_("Click to quit CLI Companion"))173 buttonCancel.set_tooltip_text(_("Click to quit CLI Companion"))
172 #buttonCancel.modify_bg(gtk.STATE_NORMAL, color) 174 #buttonCancel.modify_bg(gtk.STATE_NORMAL, color)
173 #buttonCancel.modify_bg(gtk.STATE_PRELIGHT, color) 175 #buttonCancel.modify_bg(gtk.STATE_PRELIGHT, color)
@@ -176,34 +178,34 @@
176 178
177 179
178 #right-click popup menu for the Liststore(command list)180 #right-click popup menu for the Liststore(command list)
179 def right_click(self, widget, event, actions, treeview, notebook, liststore):181 def right_click(self, widget, event, mw):
180 if event.button == 3:182 if event.button == 3:
181 x = int(event.x)183 x = int(event.x)
182 y = int(event.y)184 y = int(event.y)
183 time = event.time185 time = event.time
184 pthinfo = treeview.get_path_at_pos(x, y)186 pthinfo = mw.treeview.get_path_at_pos(x, y)
185 if pthinfo is not None:187 if pthinfo is not None:
186 path, col, cellx, celly = pthinfo188 path, col, cellx, celly = pthinfo
187 treeview.grab_focus()189 mw.treeview.grab_focus()
188 treeview.set_cursor( path, col, 0)190 mw.treeview.set_cursor( path, col, 0)
189 191
190 # right-click popup menu Apply(run)192 # right-click popup menu Apply(run)
191 popupMenu = gtk.Menu()193 popupMenu = gtk.Menu()
192 menuPopup1 = gtk.ImageMenuItem (gtk.STOCK_APPLY)194 menuPopup1 = gtk.ImageMenuItem (gtk.STOCK_APPLY)
193 popupMenu.add(menuPopup1)195 popupMenu.add(menuPopup1)
194 menuPopup1.connect("activate", actions.run_command, notebook, liststore)196 menuPopup1.connect("activate", lambda self, *x: mw.actions.run_command(mw))
195 # right-click popup menu Edit 197 # right-click popup menu Edit
196 menuPopup2 = gtk.ImageMenuItem (gtk.STOCK_EDIT)198 menuPopup2 = gtk.ImageMenuItem (gtk.STOCK_EDIT)
197 popupMenu.add(menuPopup2)199 popupMenu.add(menuPopup2)
198 menuPopup2.connect("activate", actions.edit_command, liststore)200 menuPopup2.connect("activate", lambda self, *x: mw.actions.edit_command(mw))
199 # right-click popup menu Delete 201 # right-click popup menu Delete
200 menuPopup3 = gtk.ImageMenuItem (gtk.STOCK_DELETE)202 menuPopup3 = gtk.ImageMenuItem (gtk.STOCK_DELETE)
201 popupMenu.add(menuPopup3)203 popupMenu.add(menuPopup3)
202 menuPopup3.connect("activate", actions.remove_command, liststore)204 menuPopup3.connect("activate", lambda self, *x: mw.actions.remove_command(mw))
203 # right-click popup menu Help 205 # right-click popup menu Help
204 menuPopup4 = gtk.ImageMenuItem (gtk.STOCK_HELP)206 menuPopup4 = gtk.ImageMenuItem (gtk.STOCK_HELP)
205 popupMenu.add(menuPopup4)207 popupMenu.add(menuPopup4)
206 menuPopup4.connect("activate", actions.man_page, notebook)208 menuPopup4.connect("activate", lambda self, *x: mw.actions.man_page(mw.notebook))
207 # Show popup menu209 # Show popup menu
208 popupMenu.show_all()210 popupMenu.show_all()
209 popupMenu.popup( None, None, None, event.button, time)211 popupMenu.popup( None, None, None, event.button, time)
210212
=== 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 @@
22pygtk.require('2.0')22pygtk.require('2.0')
23import gtk23import gtk
24import vte24import vte
25import ConfigParser25import clicompanionlib.config as cc_config
2626
27from clicompanionlib.utils import get_user_shell27from clicompanionlib.utils import get_user_shell, dbg
28import clicompanionlib.controller28import clicompanionlib.controller
29import clicompanionlib.utils as utils
29import view30import view
3031
31CONFIGFILE = os.path.expanduser("~/.config/clicompanion/config")
32
33
34#definition gcp - how many pages is visible
35gcp=0;
36
37#definition nop - (no of pages) reflects no of terminal tabs left (some may be closed by the user)
38nop=0;
3932
40class Tabs(object):33class Tabs(object):
41 '''34 '''
42 add a new terminal in a tab above the current terminal35 add a new terminal in a tab above the current terminal
43 '''36 '''
44 def add_tab(self,widget, notebook):37 def __init__(self):
38 #definition nop - (no of pages) reflects no of terminal tabs left (some may be closed by the user)
39 self.nop = 0
40 #definition gcp - how many pages is visible
41 self.gcp = 0
42
43 def add_tab(self, notebook):
44 dbg('Adding a new tab')
45 _vte = vte.Terminal()45 _vte = vte.Terminal()
46 if view.NETBOOKMODE == 1:46 if view.NETBOOKMODE == 1:
47 _vte.set_size_request(700, 120)47 _vte.set_size_request(700, 120)
48 else:48 else:
49 _vte.set_size_request(700, 220) 49 _vte.set_size_request(700, 220)
50 50
51 _vte.connect ("child-exited", lambda term: gtk.main_quit())51 _vte.connect("child-exited", lambda term: gtk.main_quit())
52 _vte.fork_command(get_user_shell()) # Get the user's default shell52 _vte.fork_command(get_user_shell()) # Get the user's default shell
53 53
54 self.update_term_config(_vte)54 self.update_term_config(_vte)
@@ -58,19 +58,16 @@
58 #notebook.set_show_tabs(True)58 #notebook.set_show_tabs(True)
59 #notebook.set_show_border(True)59 #notebook.set_show_border(True)
60 60
61 global gcp61 self.nop += 1
62 global nop62 self.gcp += 1
63 nop += 163 pagenum = ('Tab %d') % self.gcp
64 gcp += 164 if self.nop > 1:
65 pagenum = ('Tab %d') % gcp65 dbg('More than one tab, showing them.')
66 if nop > 1:
67 view.MainWindow.notebook.set_show_tabs(True)66 view.MainWindow.notebook.set_show_tabs(True)
68 box = gtk.HBox()67 box = gtk.HBox()
69 label = gtk.Label(pagenum)68 label = gtk.Label(pagenum)
70 box.pack_start(label, True, True)69 box.pack_start(label, True, True)
71 70
72
73
74 71
75 ## x image for tab close button72 ## x image for tab close button
76 close_image = gtk.image_new_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)73 close_image = gtk.image_new_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
@@ -87,10 +84,10 @@
87 view.MainWindow.notebook.prepend_page(vte_tab, box) # add tab84 view.MainWindow.notebook.prepend_page(vte_tab, box) # add tab
88 view.MainWindow.notebook.set_scrollable(True)85 view.MainWindow.notebook.set_scrollable(True)
89 actions = clicompanionlib.controller.Actions()86 actions = clicompanionlib.controller.Actions()
90 _vte.connect ("button_press_event", actions.copy_paste, None)87 _vte.connect("button_press_event", actions.copy_paste, None)
91 vte_tab.grab_focus()88 vte_tab.grab_focus()
92 # signal handler for tab89 # signal handler for tab
93 closebtn.connect("clicked", self.close_tab, vte_tab, notebook)90 closebtn.connect("clicked", lambda *x: self.close_tab(vte_tab, notebook))
94 91
95 vte_tab.show_all()92 vte_tab.show_all()
9693
@@ -98,29 +95,42 @@
9895
9996
100 ## Remove a page from the notebook97 ## Remove a page from the notebook
101 def close_tab(self, sender, widget, notebook):98 def close_tab(self, vte_tab, notebook):
102 ## get the page number of the tab we wanted to close99 ## get the page number of the tab we wanted to close
103 pagenum = view.MainWindow.notebook.page_num(widget)100 pagenum = view.MainWindow.notebook.page_num(vte_tab)
104 ## and close it101 ## and close it
105 view.MainWindow.notebook.remove_page(pagenum)102 view.MainWindow.notebook.remove_page(pagenum)
106 global nop103 self.nop -= 1
107 nop -= 1104 if self.nop <= 1:
108 if nop <= 1:
109 view.MainWindow.notebook.set_show_tabs(False)105 view.MainWindow.notebook.set_show_tabs(False)
110 106
111 # check if the focus does not go to the last page (ie with only a + sign)107 # check if the focus does not go to the last page (ie with only a + sign)
112 if view.MainWindow.notebook.get_current_page() == nop:108 if view.MainWindow.notebook.get_current_page() == self.nop:
113 view.MainWindow.notebook.prev_page()109 view.MainWindow.notebook.prev_page()
114 110
115 111 def update_all_term_config(self, config=None):
116 112 for pagenum in range(view.MainWindow.notebook.get_n_pages()):
117 def update_term_config(self, _vte):113 page = view.MainWindow.notebook.get_nth_page(pagenum)
118 ##read config file114 dbg(page)
119 config = ConfigParser.RawConfigParser()115 if isinstance(page, gtk.ScrolledWindow):
120 config.read(CONFIGFILE)116 for grandson in page.get_children():
121117 dbg(grandson)
118 if isinstance(grandson,vte.Terminal):
119 self.update_term_config(grandson, config)
120
121 def update_term_config(self, _vte, config=None):
122 ##set terminal preferences from conig file data122 ##set terminal preferences from conig file data
123 config_scrollback = config.getint('terminal', 'scrollb')123 if not config:
124 config = cc_config.get_config()
125 try:
126 config_scrollback = config.getint('terminal', 'scrollb')
127 except ValueError:
128 print _("WARNING: Invalid value for property 'terminal', int expected:"
129 " got '%s', using default '%s'")%(
130 config.get('terminal', 'scrollb'),
131 config.get('DEFAULT', 'scrollb'))
132 config.set('terminal','scrollb',config.get('DEFAULT', 'scrollb'))
133 config_scrollback = config.getint('DEFAULT', 'scrollb')
124 _vte.set_scrollback_lines(config_scrollback)134 _vte.set_scrollback_lines(config_scrollback)
125 135
126 color = '#2e3436:#cc0000:#4e9a06:#c4a000:#3465a4:#75507b:#06989a:#d3d7cf:#555753:#ef2929:#8ae234:#fce94f:#729fcf:#ad7fa8:#34e2e2:#eeeeec'136 color = '#2e3436:#cc0000:#4e9a06:#c4a000:#3465a4:#75507b:#06989a:#d3d7cf:#555753:#ef2929:#8ae234:#fce94f:#729fcf:#ad7fa8:#34e2e2:#eeeeec'
@@ -130,14 +140,39 @@
130 if color:140 if color:
131 palette.append(gtk.gdk.color_parse(color))141 palette.append(gtk.gdk.color_parse(color))
132 142
133 config_color_fore = gtk.gdk.color_parse(config.get('terminal', 'colorf'))143 try:
134 #_vte.set_color_foreground(config_color_fore)144 config_color_fore = gtk.gdk.color_parse(config.get('terminal', 'colorf'))
135 145 except ValueError, e:
136 config_color_back = gtk.gdk.color_parse(config.get('terminal', 'colorb'))146 print _("WARNING: Invalid value for property '%s':"
137 #_vte.set_color_background( config_color_back)147 " got '%s', using default '%s'.")%(
138 148 'colorf',
149 config.get('terminal', 'colorf'),
150 config.get('DEFAULT', 'colorf'))
151 config.set('terminal','colorf',config.get('DEFAULT', 'colorf'))
152 config_color_fore = gtk.gdk.color_parse(config.get('DEFAULT', 'colorf'))
153
154 try:
155 config_color_back = gtk.gdk.color_parse(config.get('terminal', 'colorb'))
156 except ValueError, e:
157 print _("WARNING: Invalid value for property '%s':"
158 " got '%s', using default '%s'.")%(
159 'colorb',
160 config.get('terminal', 'colorb'),
161 config.get('DEFAULT', 'colorb'))
162 config.set('terminal','colorb',config.get('DEFAULT', 'colorb'))
163 config_color_back = gtk.gdk.color_parse(config.get('DEFAULT', 'colorb'))
139 _vte.set_colors(config_color_fore, config_color_back, palette)164 _vte.set_colors(config_color_fore, config_color_back, palette)
140 165
141 config_encoding = config.get('terminal', 'encoding')166 config_encoding = config.get('terminal', 'encoding')
167 if config_encoding.upper() not in [ enc.upper() for enc, desc in utils.encodings]:
168 print _("WARNING: Invalid value for property '%s':"
169 " got '%s', using default '%s'")%(
170 'encoding',
171 config_encoding,
172 config.get('DEFAULT', 'encoding'))
173 config.set('terminal','encoding',config.get('DEFAULT', 'encoding'))
174 config_encoding = config.get('DEFAULT', 'encoding')
142 _vte.set_encoding(config_encoding)175 _vte.set_encoding(config_encoding)
176
177
143 178
144179
=== 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 @@
2525
26import getpass26import getpass
27import os27import os
2828import sys
29CHEATSHEET = os.path.expanduser("~/.clicompanion2")29import gtk
30#CONFIG_ORIG = "/etc/clicompanion.d/clicompanion2.config"30import pwd
31import inspect
32
33
34## set to True if you want to see more logs
35DEBUG = False
36DEBUGFILES = False
37DEBUGCLASSES = []
38DEBUGMETHODS = []
39
40## list gotten from terminator (https://launchpad.net/terminator)
41encodings = [
42 ["ISO-8859-1", _("Western")],
43 ["ISO-8859-2", _("Central European")],
44 ["ISO-8859-3", _("South European") ],
45 ["ISO-8859-4", _("Baltic") ],
46 ["ISO-8859-5", _("Cyrillic") ],
47 ["ISO-8859-6", _("Arabic") ],
48 ["ISO-8859-7", _("Greek") ],
49 ["ISO-8859-8", _("Hebrew Visual") ],
50 ["ISO-8859-8-I", _("Hebrew") ],
51 ["ISO-8859-9", _("Turkish") ],
52 ["ISO-8859-10", _("Nordic") ],
53 ["ISO-8859-13", _("Baltic") ],
54 ["ISO-8859-14", _("Celtic") ],
55 ["ISO-8859-15", _("Western") ],
56 ["ISO-8859-16", _("Romanian") ],
57 # ["UTF-7", _("Unicode") ],
58 ["UTF-8", _("Unicode") ],
59 # ["UTF-16", _("Unicode") ],
60 # ["UCS-2", _("Unicode") ],
61 # ["UCS-4", _("Unicode") ],
62 ["ARMSCII-8", _("Armenian") ],
63 ["BIG5", _("Chinese Traditional") ],
64 ["BIG5-HKSCS", _("Chinese Traditional") ],
65 ["CP866", _("Cyrillic/Russian") ],
66 ["EUC-JP", _("Japanese") ],
67 ["EUC-KR", _("Korean") ],
68 ["EUC-TW", _("Chinese Traditional") ],
69 ["GB18030", _("Chinese Simplified") ],
70 ["GB2312", _("Chinese Simplified") ],
71 ["GBK", _("Chinese Simplified") ],
72 ["GEORGIAN-PS", _("Georgian") ],
73 ["HZ", _("Chinese Simplified") ],
74 ["IBM850", _("Western") ],
75 ["IBM852", _("Central European") ],
76 ["IBM855", _("Cyrillic") ],
77 ["IBM857", _("Turkish") ],
78 ["IBM862", _("Hebrew") ],
79 ["IBM864", _("Arabic") ],
80 ["ISO-2022-JP", _("Japanese") ],
81 ["ISO-2022-KR", _("Korean") ],
82 ["EUC-TW", _("Chinese Traditional") ],
83 ["GB18030", _("Chinese Simplified") ],
84 ["GB2312", _("Chinese Simplified") ],
85 ["GBK", _("Chinese Simplified") ],
86 ["GEORGIAN-PS", _("Georgian") ],
87 ["HZ", _("Chinese Simplified") ],
88 ["IBM850", _("Western") ],
89 ["IBM852", _("Central European") ],
90 ["IBM855", _("Cyrillic") ],
91 ["IBM857", _("Turkish") ],
92 ["IBM862", _("Hebrew") ],
93 ["IBM864", _("Arabic") ],
94 ["ISO-2022-JP", _("Japanese") ],
95 ["ISO-2022-KR", _("Korean") ],
96 ["ISO-IR-111", _("Cyrillic") ],
97 # ["JOHAB", _("Korean") ],
98 ["KOI8-R", _("Cyrillic") ],
99 ["KOI8-U", _("Cyrillic/Ukrainian") ],
100 ["MAC_ARABIC", _("Arabic") ],
101 ["MAC_CE", _("Central European") ],
102 ["MAC_CROATIAN", _("Croatian") ],
103 ["MAC-CYRILLIC", _("Cyrillic") ],
104 ["MAC_DEVANAGARI", _("Hindi") ],
105 ["MAC_FARSI", _("Persian") ],
106 ["MAC_GREEK", _("Greek") ],
107 ["MAC_GUJARATI", _("Gujarati") ],
108 ["MAC_GURMUKHI", _("Gurmukhi") ],
109 ["MAC_HEBREW", _("Hebrew") ],
110 ["MAC_ICELANDIC", _("Icelandic") ],
111 ["MAC_ROMAN", _("Western") ],
112 ["MAC_ROMANIAN", _("Romanian") ],
113 ["MAC_TURKISH", _("Turkish") ],
114 ["MAC_UKRAINIAN", _("Cyrillic/Ukrainian") ],
115 ["SHIFT-JIS", _("Japanese") ],
116 ["TCVN", _("Vietnamese") ],
117 ["TIS-620", _("Thai") ],
118 ["UHC", _("Korean") ],
119 ["VISCII", _("Vietnamese") ],
120 ["WINDOWS-1250", _("Central European") ],
121 ["WINDOWS-1251", _("Cyrillic") ],
122 ["WINDOWS-1252", _("Western") ],
123 ["WINDOWS-1253", _("Greek") ],
124 ["WINDOWS-1254", _("Turkish") ],
125 ["WINDOWS-1255", _("Hebrew") ],
126 ["WINDOWS-1256", _("Arabic") ],
127 ["WINDOWS-1257", _("Baltic") ],
128 ["WINDOWS-1258", _("Vietnamese") ]
129 ]
130
131
132def dbg(log):
133 if DEBUG:
134 stack = inspect.stack()
135 method = None
136 for stackitem in stack:
137 parent_frame = stackitem[0]
138 names, varargs, keywords, local_vars = inspect.getargvalues(parent_frame)
139 ## little trick to get the second stackline method, in case we do
140 ## not find self
141 if not method and method != None:
142 method = stackitem[3]
143 elif not method:
144 method = ''
145 try:
146 self_name = names[0]
147 if self_name != 'self':
148 continue
149 classname = local_vars[self_name].__class__.__name__
150 except IndexError:
151 classname = "noclass"
152 ## in case self is found, get the method
153 method = stackitem[3]
154 break
155 if DEBUGFILES:
156 line = stackitem[2]
157 filename = parent_frame.f_code.co_filename
158 extra = " (%s:%s)" % (filename, line)
159 else:
160 extra = ""
161 if DEBUGCLASSES != [] and classname not in DEBUGCLASSES:
162 return
163 if DEBUGMETHODS != [] and method not in DEBUGMETHODS:
164 return
165 try:
166 print >> sys.stderr, "%s::%s: %s%s" % (classname, method, log, extra)
167 except IOError:
168 pass
169
31170
32#TODO: Move this to controller.py171#TODO: Move this to controller.py
33def get_user_shell():172def get_user_shell():
34173
=== 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 @@
22import pygtk22import pygtk
23pygtk.require('2.0')23pygtk.require('2.0')
24import os24import os
25import ConfigParser
2625
27# import vte and gtk or print error26# import vte and gtk or print error
28try:27try:
@@ -43,18 +42,20 @@
43 42
44import clicompanionlib.menus_buttons43import clicompanionlib.menus_buttons
45import clicompanionlib.controller44import clicompanionlib.controller
46from clicompanionlib.utils import get_user_shell , Borg45from clicompanionlib.utils import get_user_shell , Borg, dbg
47import clicompanionlib.tabs46import clicompanionlib.tabs
48from clicompanionlib.config import Config47import clicompanionlib.utils as utils
4948import clicompanionlib.config as cc_config
5049
51CONFIGFILE = os.path.expanduser("~/.config/clicompanion/config")
52CHEATSHEET = os.path.expanduser("~/.clicompanion2")
53CONFIG_ORIG = "/etc/clicompanion.d/clicompanion2.config"
5450
55## Changed two->three columns51## Changed two->three columns
56CMNDS = [] ## will hold the commands. Actually the first three columns52CMNDS = cc_config.Cheatsheet()
57ROW = '1' ## holds the currently selected row53## will hold the commands. Actually the first three columns
54## note that this commands list will not change with searchers and filters,
55## instead, when adding a command to the liststore, we will add also the index
56## of the command in the CMND list
57
58ROW = '0' ## holds the currently selected row
58TARGETS = [59TARGETS = [
59 ('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),60 ('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
60 ('text/plain', 0, 1),61 ('text/plain', 0, 1),
@@ -67,7 +68,6 @@
67HIDEUI = 068HIDEUI = 0
68FULLSCREEN = 069FULLSCREEN = 0
6970
70
71menu_search_hbox = ''71menu_search_hbox = ''
72button_box = ''72button_box = ''
7373
@@ -76,90 +76,32 @@
76 window = gtk.Window(gtk.WINDOW_TOPLEVEL) 76 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
77 #color = gtk.gdk.Color(60000, 65533, 60000)77 #color = gtk.gdk.Color(60000, 65533, 60000)
78 #window.modify_bg(gtk.STATE_NORMAL, color)78 #window.modify_bg(gtk.STATE_NORMAL, color)
79 liststore = gtk.ListStore(str, str, str) 79 liststore = gtk.ListStore(str, str, str, int)
80 treeview = gtk.TreeView()80 treeview = gtk.TreeView()
81 expander = gtk.Expander()81 expander = gtk.Expander()
82 scrolledwindow = gtk.ScrolledWindow()82 scrolledwindow = gtk.ScrolledWindow()
83 notebook = gtk.Notebook()83 notebook = gtk.Notebook()
8484
85
86 screen = gtk.gdk.display_get_default().get_default_screen()85 screen = gtk.gdk.display_get_default().get_default_screen()
87 screen_size = screen.get_monitor_geometry(0)86 screen_size = screen.get_monitor_geometry(0)
88 height = screen.get_height() ## screen height ##87 height = screen.get_height() ## screen height ##
89 global NETBOOKMODE88 global NETBOOKMODE
90 if height < 750:89 if height < 750:
91 NETBOOKMODE = 190 NETBOOKMODE = 1
92 ## open file containing command list and put it in a variable91
93 def update(self, liststore):92
94 try:93 def sync_cmnds(self, rld=False):
95 with open(CHEATSHEET, "r") as cheatfile:94 global CMNDS
96 bugdata=cheatfile.read()95 dbg('syncing commands')
97 cheatfile.close()96 if rld:
98 except IOError:97 ## reload the commands list from the file
99 ## CHEATSHEET is not there. Oh, no!98 CMNDS.load()
100 ## So, run self.setup() again.99 self.liststore.clear()
101 self.setup()100 ## Store also the index of the command in the CMNDS list
102 ## Then, run me again.101 i = 0
103 self.update(self.liststore)102 for cmd, ui, desc in CMNDS:
104103 self.liststore.append((cmd, ui, desc, i))
105 ## add bug data from .clicompanion --> bugdata --> to the liststore104 i = i +1
106 for line in bugdata.splitlines():
107 l = line.split('\t',2)
108 if len(l) < 2:
109 """
110 If for any reason we have a old file, we must
111 replace it by new one
112 """
113 print "PLEASE RESTART APPLICATION TO FINISH UPDATE"
114 self.setup()
115 return
116 commandplus = l[0], l[1], l[2]
117 CMNDS.append(commandplus)
118 self.liststore.append([l[0],l[1],l[2]])
119
120
121 #copy config file to user $HOME if does not exist
122 def setup(self):
123 """
124 Check if ~/.clicompanion2 exists. If not check for original
125 installed in /etc/clicompanion.d/. If origianl exists copy to $HOME.
126 if not create a new, blank ~/.clicompanion2 so program will not crash
127 """
128
129 if not os.path.exists(CHEATSHEET):
130 if os.path.exists(CONFIG_ORIG):
131 os.system ("cp %s %s" % (CONFIG_ORIG, CHEATSHEET))
132 else:
133 # Oops! Looks like there's no cheatsheet in CHEATSHEET.
134 # Then, create an empty cheatsheet.
135 open(CHEATSHEET, 'w').close()
136 """
137 If we have old file, we must replace it by fresh list
138 """
139 cheatlines = []
140 try:
141 with open(CHEATSHEET, "r") as cheatfile:
142 bugdata=cheatfile.read()
143 cheatfile.close()
144 for line in bugdata.splitlines():
145 l = line.split('\t', 2)
146 if len(l) < 2:
147 l = line.split(':', 2)
148 p = str(l[0] + "\t"+ l[1] +"\t"+ l[2] + "\n")
149 cheatlines.append(p)
150 else:
151 cheatlines.append(str(l[0] + "\t"+ l[1] +"\t"+ l[2] + "\n"))
152
153 with open(CHEATSHEET, "w") as cheatfile2:
154 cheatfile2.writelines(cheatlines)
155 cheatfile2.close()
156
157 except IOError:
158 ## CHEATSHEET is not there. Oh, no!
159 ## So, run self.setup() again.
160 self.setup()
161 ## Then, run me again.
162 self.update(self.liststore)
163105
164 106
165 #liststore in a scrolled window in an expander107 #liststore in a scrolled window in an expander
@@ -185,13 +127,11 @@
185 127
186 def key_clicked(self, widget, event):128 def key_clicked(self, widget, event):
187 actions = clicompanionlib.controller.Actions()129 actions = clicompanionlib.controller.Actions()
188 tabs = clicompanionlib.tabs.Tabs()
189 global HIDEUI130 global HIDEUI
190 global FULLSCREEN131 global FULLSCREEN
191 global menu_search_hbox132 global menu_search_hbox
192 global button_box133 global button_box
193 keyname = gtk.gdk.keyval_name(event.keyval).upper()134 keyname = gtk.gdk.keyval_name(event.keyval).upper()
194 #print keyname ##debug
195 if keyname == "F12":135 if keyname == "F12":
196 HIDEUI = 1 - HIDEUI136 HIDEUI = 1 - HIDEUI
197 if HIDEUI == 1:137 if HIDEUI == 1:
@@ -215,13 +155,13 @@
215 pwin = button_box.get_window()155 pwin = button_box.get_window()
216 pwin.unfullscreen()156 pwin.unfullscreen()
217 if keyname == "F4":157 if keyname == "F4":
218 actions.run_command(self, self.notebook, self.liststore)158 actions.run_command(self)
219 if keyname == "F5":159 if keyname == "F5":
220 actions.add_command(self, self.liststore)160 actions.add_command(self)
221 if keyname == "F6":161 if keyname == "F6":
222 actions.remove_command(self, self.liststore)162 actions.remove_command(self)
223 if keyname == "F7":163 if keyname == "F7":
224 tabs.add_tab(self, self.notebook)164 self.tabs.add_tab(self)
225 165
226 def __init__(self):166 def __init__(self):
227 #import pdb ##debug167 #import pdb ##debug
@@ -231,14 +171,7 @@
231 ##in libvte in Ubuntu Maverick171 ##in libvte in Ubuntu Maverick
232 os.putenv('TERM', 'xterm')172 os.putenv('TERM', 'xterm')
233173
234 ## copy command list to user $HOME if does not exist
235 self.setup()
236174
237 ##create the config file
238 conf_mod = Config()
239 conf_mod.create_config()
240
241
242 ## style to reduce padding around tabs175 ## style to reduce padding around tabs
243 ## TODO: Find a better place for this? 176 ## TODO: Find a better place for this?
244 gtk.rc_parse_string ("style \"tab-close-button-style\"\n"177 gtk.rc_parse_string ("style \"tab-close-button-style\"\n"
@@ -257,7 +190,6 @@
257 ##attach the style to the widget190 ##attach the style to the widget
258 self.notebook.set_name ("tab-close-button")191 self.notebook.set_name ("tab-close-button")
259192
260
261 ## set sizes and borders193 ## set sizes and borders
262 global NETBOOKMODE194 global NETBOOKMODE
263 if NETBOOKMODE == 1:195 if NETBOOKMODE == 1:
@@ -272,19 +204,15 @@
272 ## Allow user to resize window204 ## Allow user to resize window
273 self.window.set_resizable(True)205 self.window.set_resizable(True)
274 206
275
276 ## set Window title and icon207 ## set Window title and icon
277 self.window.set_title("CLI Companion")208 self.window.set_title("CLI Companion")
278 icon = gtk.gdk.pixbuf_new_from_file("/usr/share/pixmaps/clicompanion.16.png")209 icon = gtk.gdk.pixbuf_new_from_file("/usr/share/pixmaps/clicompanion.16.png")
279 self.window.set_icon(icon)210 self.window.set_icon(icon)
280
281
282 211
283 # get commands and put in liststore212 # sync liststore with commands
284 self.update(self.liststore) 213 self.sync_cmnds()
285 214
286 ## set renderer and colors215 ## set renderer and colors
287
288 #color2 = gtk.gdk.Color(5000,5000,65000)216 #color2 = gtk.gdk.Color(5000,5000,65000)
289 renderer = gtk.CellRendererText()217 renderer = gtk.CellRendererText()
290 #renderer.set_property("cell-background-gdk", color)218 #renderer.set_property("cell-background-gdk", color)
@@ -308,7 +236,7 @@
308 True)236 True)
309 ## set the cell attributes to the appropriate liststore column237 ## set the cell attributes to the appropriate liststore column
310 self.treeview.columns[n].set_attributes(238 self.treeview.columns[n].set_attributes(
311 self.treeview.columns[n].cell, text=n) 239 self.treeview.columns[n].cell, text=n)
312 self.treeview.columns[n].set_resizable(True) 240 self.treeview.columns[n].set_resizable(True)
313 241
314 ''' set treeview model and put treeview in the scrolled window242 ''' set treeview model and put treeview in the scrolled window
@@ -321,12 +249,12 @@
321 #self.window.show_all()249 #self.window.show_all()
322250
323 ## instantiate tabs251 ## instantiate tabs
324 tabs = clicompanionlib.tabs.Tabs()252 self.tabs = clicompanionlib.tabs.Tabs()
325 ## instantiate controller.Actions, where all the button actions are253 ## instantiate controller.Actions, where all the button actions are
326 self.actions = clicompanionlib.controller.Actions()254 self.actions = clicompanionlib.controller.Actions()
327 ## instantiate 'File' and 'Help' Drop Down Menu [menus_buttons.py]255 ## instantiate 'File' and 'Help' Drop Down Menu [menus_buttons.py]
328 bar = clicompanionlib.menus_buttons.FileMenu()256 bar = clicompanionlib.menus_buttons.FileMenu()
329 menu_bar = bar.the_menu(self.actions, self.notebook, self.liststore)257 menu_bar = bar.the_menu(self)
330 258
331259
332 ## get row of a selection260 ## get row of a selection
@@ -337,17 +265,17 @@
337 265
338 266
339 ## double click to run a command 267 ## double click to run a command
340 def treeview_clicked(widget, event):268 def treeview_clicked(widget, path, column):
341 if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:269 self.actions.run_command(self)
342 self.actions.run_command(self, self.notebook, self.liststore)270
343271
344 ## press enter to run a command 272 ## press enter to run a command
345 def treeview_button(widget, event):273 def treeview_button(widget, event):
346 keyname = gtk.gdk.keyval_name(event.keyval).upper()274 keyname = gtk.gdk.keyval_name(event.keyval).upper()
347 #print keyname ##debug275 dbg('Key %s pressed'%keyname)
348 if event.type == gtk.gdk.KEY_PRESS:276 if event.type == gtk.gdk.KEY_PRESS:
349 if keyname == 'RETURN':277 if keyname == 'RETURN':
350 self.actions.run_command(self, self.notebook, self.liststore)278 self.actions.run_command(self)
351 279
352 280
353281
@@ -357,7 +285,7 @@
357 selection.select_path(0) 285 selection.select_path(0)
358 selection.connect("changed", mark_selected, selection)286 selection.connect("changed", mark_selected, selection)
359 ## double-click287 ## double-click
360 self.treeview.connect("button-press-event", treeview_clicked)288 self.treeview.connect("row-activated", treeview_clicked)
361 #press enter to run command289 #press enter to run command
362 self.treeview.connect("key-press-event", treeview_button)290 self.treeview.connect("key-press-event", treeview_button)
363 291
@@ -393,7 +321,7 @@
393 self.expander.set_label_widget(expander_hbox)321 self.expander.set_label_widget(expander_hbox)
394322
395 ## Add the first tab with the Terminal323 ## Add the first tab with the Terminal
396 tabs.add_tab(self, self.notebook)324 self.tabs.add_tab(self.notebook)
397 self.notebook.set_tab_pos(2)325 self.notebook.set_tab_pos(2)
398326
399 ## The "Add Tab" tab327 ## The "Add Tab" tab
@@ -405,7 +333,7 @@
405 333
406 global button_box334 global button_box
407 ## buttons at bottom of main window [menus_buttons.py]335 ## buttons at bottom of main window [menus_buttons.py]
408 button_box = bar.buttons(self.actions, 10, gtk.BUTTONBOX_END, self.notebook, self.liststore)336 button_box = bar.buttons(self, 10, gtk.BUTTONBOX_END)
409337
410 ## vbox for search, notebook, buttonbar338 ## vbox for search, notebook, buttonbar
411 vbox = gtk.VBox()339 vbox = gtk.VBox()
@@ -421,9 +349,9 @@
421 self.expander.connect('notify::expanded', self.expanded_cb, self.window, self.search_box)349 self.expander.connect('notify::expanded', self.expanded_cb, self.window, self.search_box)
422 self.window.connect("delete_event", self.delete_event)350 self.window.connect("delete_event", self.delete_event)
423 self.window.connect("key-press-event", self.key_clicked)351 self.window.connect("key-press-event", self.key_clicked)
424 add_tab_button.connect("clicked", tabs.add_tab, self.notebook)352 add_tab_button.connect("clicked", lambda *x: self.tabs.add_tab(self.notebook))
425 ## right click menu event capture353 ## right click menu event capture
426 self.treeview.connect ("button_press_event", bar.right_click, self.actions, self.treeview, self.notebook, self.liststore)354 self.treeview.connect("button_press_event", bar.right_click, self)
427355
428 # Allow enable drag and drop of rows including row move356 # Allow enable drag and drop of rows including row move
429 self.treeview.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,357 self.treeview.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
@@ -435,12 +363,20 @@
435363
436 self.treeview.connect ("drag_data_get", self.drag_data_get_event)364 self.treeview.connect ("drag_data_get", self.drag_data_get_event)
437 self.treeview.connect ("drag_data_received", self.drag_data_received_event)365 self.treeview.connect ("drag_data_received", self.drag_data_received_event)
366 self.treeview.connect("drag_drop", self.on_drag_drop )
438367
439368
440 #self.vte.grab_focus()369 #self.vte.grab_focus()
441 self.window.show_all()370 self.window.show_all()
442 return371 return
443372
373 def on_drag_drop(self, treeview, *x):
374 '''
375 Stop the signal when in search mode
376 '''
377 if FILTER:
378 treeview.stop_emission('drag_drop')
379
444 def drag_data_get_event(self, treeview, context, selection, target_id, 380 def drag_data_get_event(self, treeview, context, selection, target_id,
445 etime):381 etime):
446 """382 """
@@ -457,90 +393,57 @@
457 Executed when dropping.393 Executed when dropping.
458 """394 """
459 global CMNDS395 global CMNDS
396 global FILTER
397 ## if we are in a search, do nothing
398 if FILTER == 1:
399 return
460 model = treeview.get_model()400 model = treeview.get_model()
401 ## get the destination
402 drop_info = treeview.get_dest_row_at_pos(x, y)
403 if drop_info:
404 path, position = drop_info
405 iter = model.get_iter(path)
406 dest = list(model.get(iter, 0, 1, 2))
407
408 ## parse all the incoming commands
461 for data in selection.data.split('\n'):409 for data in selection.data.split('\n'):
462 # if we got an empty line skip it410 # if we got an empty line skip it
463 if not data.replace('\r',''): continue411 if not data.replace('\r',''): continue
464 # format the incoming string412 # format the incoming string
465 orig = data.replace('\r','').split('\t',2)413 orig = data.replace('\r','').split('\t',2)
466 orig = tuple([ fld.strip() for fld in orig ])414 orig = [ fld.strip() for fld in orig ]
467 # fill the empty fields415 # fill the empty fields
468 if len(orig) < 3: orig = orig + ('',)*(3-len(orig))416 if len(orig) < 3: orig = orig + ('',)*(3-len(orig))
469 # if the element already exists delete it (dragged from clicompanion)417 dbg('Got drop of command %s'%'_\t_'.join(orig))
470 olditer = self.find_iter_by_tuple(orig, model)
471 if olditer: del model[olditer]
472418
473 drop_info = treeview.get_dest_row_at_pos(x, y)
474 if drop_info:419 if drop_info:
475 path, position = drop_info
476 iter = model.get_iter(path)
477 dest = tuple(model.get(iter, 0, 1, 2))
478 if (position == gtk.TREE_VIEW_DROP_BEFORE420 if (position == gtk.TREE_VIEW_DROP_BEFORE
479 or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):421 or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
480 model.insert_before(iter, orig)422 dbg('\t to before dest %s'%'_\t_'.join(dest))
481 self.drag_cmnd(orig, dest, before=True)423 CMNDS.drag_n_drop(orig, dest, before=True)
482 else:424 else:
483 model.insert_after(iter, orig)425 dbg('\t to after dest %s'%'_\t_'.join(dest))
484 self.drag_cmnd(orig, dest, before=False)426 CMNDS.drag_n_drop(orig, dest, before=False)
485 else:427 else:
486 if len(model) > 0:428 dbg('\t to the end')
487 iter = model[-1].iter429 CMNDS[len(CMNDS)] = orig
488 model.insert_after(iter, orig)430 if context.action == gtk.gdk.ACTION_MOVE:
489 else:431 context.finish(True, True, etime)
490 model.insert(0, orig)432 self.sync_cmnds()
491 return433 CMNDS.save()
492 dest = tuple(model.get(iter, 0, 1, 2))
493 self.drag_cmnd(orig, dest, before=False)
494 if context.action == gtk.gdk.ACTION_MOVE:
495 context.finish(True, True, etime)
496 self.actions.save_cmnds()
497 434
498 def find_iter_by_tuple(self, data, model):
499 for row in model:
500 if tuple(model.get(row.iter, 0, 1, 2)) == data:
501 return row.iter
502 return None
503
504 def drag_cmnd(self, orig, dest, before=True):
505 """
506 Sync the CMNDS array with the drag and drop of the treeview.
507 """
508 global CMNDS
509 i = j = None
510 pos = 0
511 for cmnd in CMNDS:
512 if cmnd == orig:
513 i = pos
514 elif cmnd == dest:
515 j = pos
516 pos += 1
517 ## both from clicompanion
518 if i != None and j != None:
519 cmnd = CMNDS.pop(i)
520 if before and i<=j:
521 CMNDS.insert(j-1, cmnd)
522 elif before and i>j:
523 CMNDS.insert(j, cmnd)
524 elif i<=j:
525 CMNDS.insert(j, cmnd)
526 else:
527 CMNDS.insert(j+1, cmnd)
528 ## origin unknown
529 elif j != None:
530 cmnd = orig
531 if before:
532 CMNDS.insert(j, cmnd)
533 else:
534 CMNDS.insert(j+1, cmnd)
535
536
537 def main(self):435 def main(self):
538 try:436 try:
539 gtk.main()437 gtk.main()
540 except KeyboardInterrupt:438 except KeyboardInterrupt:
541 pass439 pass
542 440
543def run():441def run( options=None ):
544 442 ##create the config file
443 config = cc_config.create_config()
444 if config.get('terminal','debug') == 'True':
445 utils.DEBUG = True
446 CMNDS.load(options and options.cheatsheet or None)
447 dbg('Loaded commands %s'%CMNDS)
545 main_window = MainWindow()448 main_window = MainWindow()
546 main_window.main()449 main_window.main()

Subscribers

People subscribed via source and target branches