MainWindow: work when deselecting things
[alacarte.git] / Alacarte / MainWindow.py
blob42fa5099c8d9cb2e2b83d124fda0dc1002618b2e
1 # -*- coding: utf-8 -*-
2 # vim: set noexpandtab:
3 # Alacarte Menu Editor - Simple fd.o Compliant Menu Editor
4 # Copyright (C) 2006 Travis Watkins
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Library General Public
8 # License as published by the Free Software Foundation; either
9 # version 2 of the License, or (at your option) any later version.
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Library General Public License for more details.
16 # You should have received a copy of the GNU Library General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 from gi.repository import Gtk, GObject, Gio, GdkPixbuf, Gdk, GMenu, GLib
21 import cgi, os
22 import gettext
23 import subprocess
24 import urllib
26 from Alacarte import config
27 gettext.bindtextdomain(config.GETTEXT_PACKAGE, config.localedir)
28 gettext.textdomain(config.GETTEXT_PACKAGE)
30 _ = gettext.gettext
31 from Alacarte.MenuEditor import MenuEditor
32 from Alacarte import util
34 class MainWindow(object):
35 timer = None
36 #hack to make editing menu properties work
37 allow_update = True
38 #drag-and-drop stuff
39 dnd_items = [('ALACARTE_ITEM_ROW', Gtk.TargetFlags.SAME_APP, 0), ('text/plain', 0, 1)]
40 dnd_menus = [('ALACARTE_MENU_ROW', Gtk.TargetFlags.SAME_APP, 0)]
41 dnd_both = [dnd_items[0],] + dnd_menus
42 drag_data = None
43 edit_pool = []
45 def __init__(self, datadir, version):
46 self.file_path = datadir
47 self.version = version
48 self.editor = MenuEditor()
49 Gtk.Window.set_default_icon_name('alacarte')
50 self.tree = Gtk.Builder()
51 self.tree.set_translation_domain(config.GETTEXT_PACKAGE)
52 self.tree.add_from_file(os.path.join(self.file_path, 'alacarte.ui'))
53 self.tree.connect_signals(self)
54 self.setupMenuTree()
55 self.setupItemTree()
56 self.tree.get_object('edit_delete').set_sensitive(False)
57 self.tree.get_object('edit_revert_to_original').set_sensitive(False)
58 self.tree.get_object('edit_properties').set_sensitive(False)
59 self.tree.get_object('move_up_button').set_sensitive(False)
60 self.tree.get_object('move_down_button').set_sensitive(False)
61 self.tree.get_object('new_separator_button').set_sensitive(False)
62 accelgroup = Gtk.AccelGroup()
63 keyval, modifier = Gtk.accelerator_parse('<Ctrl>Z')
64 accelgroup.connect(keyval, modifier, Gtk.AccelFlags.VISIBLE, self.on_mainwindow_undo)
65 keyval, modifier = Gtk.accelerator_parse('<Ctrl><Shift>Z')
66 accelgroup.connect(keyval, modifier, Gtk.AccelFlags.VISIBLE, self.on_mainwindow_redo)
67 keyval, modifier = Gtk.accelerator_parse('F1')
68 accelgroup.connect(keyval, modifier, Gtk.AccelFlags.VISIBLE, self.on_help_button_clicked)
69 self.tree.get_object('mainwindow').add_accel_group(accelgroup)
71 def run(self):
72 self.loadMenus()
73 self.editor.applications.tree.connect("changed", self.menuChanged)
74 self.tree.get_object('mainwindow').show_all()
75 Gtk.main()
77 def menuChanged(self, *a):
78 self.loadUpdates()
80 def loadUpdates(self):
81 self.editor.reloadMenus()
82 self.editor.applications.tree.connect("changed", self.menuChanged)
84 if not self.allow_update:
85 return False
86 menu_tree = self.tree.get_object('menu_tree')
87 item_tree = self.tree.get_object('item_tree')
88 items, iter = item_tree.get_selection().get_selected()
89 update_items = False
90 update_type = None
91 item_id = None
92 if iter:
93 update_items = True
94 if isinstance(items[iter][3], GMenu.TreeEntry):
95 item_id = items[iter][3].get_desktop_file_id()
96 update_type = GMenu.TreeItemType.ENTRY
97 elif isinstance(items[iter][3], GMenu.TreeDirectory):
98 item_id = os.path.split(items[iter][3].get_desktop_file_path())[1]
99 update_type = GMenu.TreeItemType.DIRECTORY
100 elif isinstance(items[iter][3], GMenu.Tree.Separator):
101 item_id = items.get_path(iter)
102 update_type = GMenu.TreeItemType.SEPARATOR
103 menus, iter = menu_tree.get_selection().get_selected()
104 update_menus = False
105 menu_id = None
106 if iter:
107 if menus[iter][2].get_desktop_file_path():
108 menu_id = os.path.split(menus[iter][2].get_desktop_file_path())[1]
109 else:
110 menu_id = menus[iter][2].get_menu_id()
111 update_menus = True
112 self.loadMenus()
113 #find current menu in new tree
114 if update_menus:
115 menu_tree.get_model().foreach(self.findMenu, menu_id)
116 menus, iter = menu_tree.get_selection().get_selected()
117 if iter:
118 self.on_menu_tree_cursor_changed(menu_tree)
119 #find current item in new list
120 if update_items:
121 i = 0
122 for item in item_tree.get_model():
123 found = False
124 if update_type != GMenu.TreeItemType.SEPARATOR:
125 if isinstance (item[3], GMenu.TreeEntry) and item[3].get_desktop_file_id() == item_id:
126 found = True
127 if isinstance (item[3], GMenu.TreeDirectory) and item[3].get_desktop_file_path() and update_type == GMenu.TreeItemType.DIRECTORY:
128 if os.path.split(item[3].get_desktop_file_path())[1] == item_id:
129 found = True
130 if isinstance(item[3], GMenu.TreeSeparator):
131 if not isinstance(item_id, tuple):
132 #we may not skip the increment via "continue"
133 i += 1
134 continue
135 #separators have no id, have to find them manually
136 #probably won't work with two separators together
137 if (item_id[0] - 1,) == (i,):
138 found = True
139 elif (item_id[0] + 1,) == (i,):
140 found = True
141 elif (item_id[0],) == (i,):
142 found = True
143 if found:
144 item_tree.get_selection().select_path((i,))
145 self.on_item_tree_cursor_changed(item_tree)
146 break
147 i += 1
148 return False
150 def findMenu(self, menus, path, iter, menu_id):
151 if not menus[path][2].get_desktop_file_path():
152 if menu_id == menus[path][2].get_menu_id():
153 menu_tree = self.tree.get_object('menu_tree')
154 menu_tree.expand_to_path(path)
155 menu_tree.get_selection().select_path(path)
156 return True
157 return False
158 if os.path.split(menus[path][2].get_desktop_file_path())[1] == menu_id:
159 menu_tree = self.tree.get_object('menu_tree')
160 menu_tree.expand_to_path(path)
161 menu_tree.get_selection().select_path(path)
162 return True
164 def setupMenuTree(self):
165 self.menu_store = Gtk.TreeStore(GdkPixbuf.Pixbuf, str, object)
166 menus = self.tree.get_object('menu_tree')
167 column = Gtk.TreeViewColumn(_('Name'))
168 column.set_spacing(4)
169 cell = Gtk.CellRendererPixbuf()
170 column.pack_start(cell, False)
171 column.add_attribute(cell, 'pixbuf', 0)
172 cell = Gtk.CellRendererText()
173 column.pack_start(cell, True)
174 column.add_attribute(cell, 'markup', 1)
175 menus.append_column(column)
176 menus.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, self.dnd_menus, Gdk.DragAction.COPY)
177 menus.enable_model_drag_dest(self.dnd_both, Gdk.DragAction.PRIVATE)
178 menus.get_selection().set_mode(Gtk.SelectionMode.BROWSE)
180 def setupItemTree(self):
181 items = self.tree.get_object('item_tree')
182 column = Gtk.TreeViewColumn(_('Show'))
183 cell = Gtk.CellRendererToggle()
184 cell.connect('toggled', self.on_item_tree_show_toggled)
185 column.pack_start(cell, True)
186 column.add_attribute(cell, 'active', 0)
187 #hide toggle for separators
188 column.set_cell_data_func(cell, self._cell_data_toggle_func)
189 items.append_column(column)
190 column = Gtk.TreeViewColumn(_('Item'))
191 column.set_spacing(4)
192 cell = Gtk.CellRendererPixbuf()
193 column.pack_start(cell, False)
194 column.add_attribute(cell, 'pixbuf', 1)
195 cell = Gtk.CellRendererText()
196 column.pack_start(cell, True)
197 column.add_attribute(cell, 'markup', 2)
198 items.append_column(column)
199 self.item_store = Gtk.ListStore(bool, GdkPixbuf.Pixbuf, str, object)
200 items.set_model(self.item_store)
201 items.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, self.dnd_items, Gdk.DragAction.COPY)
202 items.enable_model_drag_dest(self.dnd_items, Gdk.DragAction.PRIVATE)
204 def _cell_data_toggle_func(self, tree_column, renderer, model, treeiter, data=None):
205 if isinstance(model[treeiter][3], GMenu.TreeSeparator):
206 renderer.set_property('visible', False)
207 else:
208 renderer.set_property('visible', True)
210 def loadMenus(self):
211 self.menu_store.clear()
212 for menu in self.editor.getMenus():
213 iters = [None]*20
214 self.loadMenu(iters, menu)
215 menu_tree = self.tree.get_object('menu_tree')
216 menu_tree.set_model(self.menu_store)
217 for menu in self.menu_store:
218 menu_tree.expand_to_path(menu.path)
219 menu_tree.get_selection().select_path((0,))
220 self.on_menu_tree_cursor_changed(menu_tree)
222 def loadMenu(self, iters, parent, depth=0):
223 if depth == 0:
224 icon = util.getIcon(parent)
225 iters[depth] = self.menu_store.append(None, (icon, cgi.escape(parent.get_name()), parent))
226 depth += 1
227 for menu, show in self.editor.getMenus(parent):
228 if show:
229 name = cgi.escape(menu.get_name())
230 else:
231 name = '<small><i>' + cgi.escape(menu.get_name()) + '</i></small>'
232 icon = util.getIcon(menu)
233 iters[depth] = self.menu_store.append(iters[depth-1], (icon, name, menu))
234 self.loadMenu(iters, menu, depth)
235 depth -= 1
237 def loadItems(self, menu, menu_path):
238 self.item_store.clear()
239 for item, show in self.editor.getItems(menu):
240 menu_icon = None
241 if isinstance(item, GMenu.TreeSeparator):
242 name = '---'
243 icon = None
244 elif isinstance(item, GMenu.TreeEntry):
245 app_info = item.get_app_info()
246 if show:
247 name = cgi.escape(app_info.get_display_name())
248 else:
249 name = '<small><i>' + cgi.escape(app_info.get_display_name()) + '</i></small>'
250 icon = util.getIcon(item)
251 else:
252 if show:
253 name = cgi.escape(item.get_name())
254 else:
255 name = '<small><i>' + cgi.escape(item.get_name()) + '</i></small>'
256 icon = util.getIcon(item)
257 self.item_store.append((show, icon, name, item))
259 #this is a little timeout callback to insert new items after
260 #gnome-desktop-item-edit has finished running
261 def waitForNewItemProcess(self, process, parent_id, file_path):
262 if process.poll() != None:
263 if os.path.isfile(file_path):
264 self.editor.insertExternalItem(os.path.split(file_path)[1], parent_id)
265 return False
266 return True
268 def waitForNewMenuProcess(self, process, parent_id, file_path):
269 if process.poll() != None:
270 #hack for broken gnome-desktop-item-edit
271 broken_path = os.path.join(os.path.split(file_path)[0], '.directory')
272 if os.path.isfile(broken_path):
273 os.rename(broken_path, file_path)
274 if os.path.isfile(file_path):
275 self.editor.insertExternalMenu(os.path.split(file_path)[1], parent_id)
276 return False
277 return True
279 #this callback keeps you from editing the same item twice
280 def waitForEditProcess(self, process, file_path):
281 if process.poll() != None:
282 self.edit_pool.remove(file_path)
283 return False
284 return True
286 def on_new_menu_button_clicked(self, button):
287 menu_tree = self.tree.get_object('menu_tree')
288 menus, iter = menu_tree.get_selection().get_selected()
289 if not iter:
290 parent = menus[(0,)][2]
291 menu_tree.expand_to_path((0,))
292 menu_tree.get_selection().select_path((0,))
293 else:
294 parent = menus[iter][2]
295 file_path = os.path.join(util.getUserDirectoryPath(), util.getUniqueFileId('alacarte-made', '.directory'))
296 process = subprocess.Popen(['gnome-desktop-item-edit', file_path], env=os.environ)
297 GObject.timeout_add(100, self.waitForNewMenuProcess, process, parent.get_menu_id(), file_path)
299 def on_new_item_button_clicked(self, button):
300 menu_tree = self.tree.get_object('menu_tree')
301 menus, iter = menu_tree.get_selection().get_selected()
302 if not iter:
303 parent = menus[(0,)][2]
304 menu_tree.expand_to_path((0,))
305 menu_tree.get_selection().select_path((0,))
306 else:
307 parent = menus[iter][2]
308 file_path = os.path.join(util.getUserItemPath(), util.getUniqueFileId('alacarte-made', '.desktop'))
309 process = subprocess.Popen(['gnome-desktop-item-edit', file_path], env=os.environ)
310 GObject.timeout_add(100, self.waitForNewItemProcess, process, parent.get_menu_id(), file_path)
312 def on_new_separator_button_clicked(self, button):
313 item_tree = self.tree.get_object('item_tree')
314 items, iter = item_tree.get_selection().get_selected()
315 if not iter:
316 return
317 else:
318 after = items[iter][3]
319 menu_tree = self.tree.get_object('menu_tree')
320 menus, iter = menu_tree.get_selection().get_selected()
321 parent = menus[iter][2]
322 self.editor.createSeparator(parent, after=after)
324 def on_edit_delete_activate(self, menu):
325 item_tree = self.tree.get_object('item_tree')
326 items, iter = item_tree.get_selection().get_selected()
327 if not iter:
328 return
329 item = items[iter][3]
330 if isinstance(item, GMenu.TreeEntry):
331 self.editor.deleteItem(item)
332 elif isinstance(item, GMenu.TreeDirectory):
333 self.editor.deleteMenu(item)
334 elif isinstance(item, GMenu.TreeSeparator):
335 self.editor.deleteSeparator(item)
337 def on_edit_revert_to_original_activate(self, menu):
338 item_tree = self.tree.get_object('item_tree')
339 items, iter = item_tree.get_selection().get_selected()
340 if not iter:
341 return
342 item = items[iter][3]
343 if isinstance(item, GMenu.TreeEntry):
344 self.editor.revertItem(item)
345 elif isinstance(item, GMenu.TreeDirectory):
346 self.editor.revertMenu(item)
348 def on_edit_properties_activate(self, menu):
349 item_tree = self.tree.get_object('item_tree')
350 items, iter = item_tree.get_selection().get_selected()
351 if not iter:
352 return
353 item = items[iter][3]
354 if not isinstance(item, GMenu.TreeEntry) and not isinstance(item, GMenu.TreeDirectory):
355 return
357 if isinstance(item, GMenu.TreeEntry):
358 file_path = os.path.join(util.getUserItemPath(), item.get_desktop_file_id())
359 file_type = 'Item'
360 elif isinstance(item, GMenu.TreeDirectory):
361 file_path = os.path.join(util.getUserDirectoryPath(), os.path.split(item.get_desktop_file_path())[1])
362 file_type = 'Menu'
364 if not os.path.isfile(file_path):
365 data = open(item.get_desktop_file_path()).read()
366 open(file_path, 'w').write(data)
367 self.editor._MenuEditor__addUndo([(file_type, os.path.split(file_path)[1]),])
368 else:
369 self.editor._MenuEditor__addUndo([item,])
370 if file_path not in self.edit_pool:
371 self.edit_pool.append(file_path)
372 process = subprocess.Popen(['gnome-desktop-item-edit', file_path], env=os.environ)
373 GObject.timeout_add(100, self.waitForEditProcess, process, file_path)
375 def on_menu_tree_cursor_changed(self, treeview):
376 menus, iter = treeview.get_selection().get_selected()
377 if iter is None:
378 return
379 menu_path = menus.get_path(iter)
380 item_tree = self.tree.get_object('item_tree')
381 item_tree.get_selection().unselect_all()
382 self.loadItems(self.menu_store[menu_path][2], menu_path)
383 self.tree.get_object('edit_delete').set_sensitive(False)
384 self.tree.get_object('edit_revert_to_original').set_sensitive(False)
385 self.tree.get_object('edit_properties').set_sensitive(False)
386 self.tree.get_object('move_up_button').set_sensitive(False)
387 self.tree.get_object('move_down_button').set_sensitive(False)
388 self.tree.get_object('new_separator_button').set_sensitive(False)
389 self.tree.get_object('properties_button').set_sensitive(False)
390 self.tree.get_object('delete_button').set_sensitive(False)
392 def on_menu_tree_drag_data_get(self, treeview, context, selection, target_id, etime):
393 menus, iter = treeview.get_selection().get_selected()
394 self.drag_data = menus[iter][2]
396 def on_menu_tree_drag_data_received(self, treeview, context, x, y, selection, info, etime):
397 menus = treeview.get_model()
398 drop_info = treeview.get_dest_row_at_pos(x, y)
399 types = (Gtk.TreeViewDropPosition.BEFORE, Gtk.TreeViewDropPosition.INTO_OR_BEFORE)
400 if drop_info:
401 path, position = drop_info
402 if position not in types:
403 context.finish(False, False, etime)
404 return False
405 if selection.target in ('ALACARTE_ITEM_ROW', 'ALACARTE_MENU_ROW'):
406 if self.drag_data == None:
407 return False
408 item = self.drag_data
409 new_parent = menus[path][2]
410 treeview.get_selection().select_path(path)
411 if isinstance(item, GMenu.TreeEntry):
412 self.editor.copyItem(item, new_parent)
413 elif isinstance(item, GMenu.TreeDirectory):
414 if self.editor.moveMenu(item, new_parent) == False:
415 self.loadUpdates()
416 elif isinstance(item, GMenu.TreeSeparator):
417 self.editor.moveSeparator(item, new_parent)
418 else:
419 context.finish(False, False, etime)
420 context.finish(True, True, etime)
421 self.drag_data = None
423 def on_item_tree_show_toggled(self, cell, path):
424 item = self.item_store[path][3]
425 if isinstance(item, GMenu.TreeSeparator):
426 return
427 if self.item_store[path][0]:
428 self.editor.setVisible(item, False)
429 else:
430 self.editor.setVisible(item, True)
431 self.item_store[path][0] = not self.item_store[path][0]
433 def on_item_tree_cursor_changed(self, treeview):
434 items, iter = treeview.get_selection().get_selected()
435 if iter is None:
436 return
437 item = items[iter][3]
438 self.tree.get_object('edit_delete').set_sensitive(True)
439 self.tree.get_object('new_separator_button').set_sensitive(True)
440 self.tree.get_object('delete_button').set_sensitive(True)
441 if self.editor.canRevert(item):
442 self.tree.get_object('edit_revert_to_original').set_sensitive(True)
443 else:
444 self.tree.get_object('edit_revert_to_original').set_sensitive(False)
445 if not isinstance(item, GMenu.TreeSeparator):
446 self.tree.get_object('edit_properties').set_sensitive(True)
447 self.tree.get_object('properties_button').set_sensitive(True)
448 else:
449 self.tree.get_object('edit_properties').set_sensitive(False)
450 self.tree.get_object('properties_button').set_sensitive(False)
452 # If first item...
453 if items.get_path(iter).get_indices()[0] == 0:
454 self.tree.get_object('move_up_button').set_sensitive(False)
455 else:
456 self.tree.get_object('move_up_button').set_sensitive(True)
458 # If last item...
459 if items.get_path(iter).get_indices()[0] == (len(items)-1):
460 self.tree.get_object('move_down_button').set_sensitive(False)
461 else:
462 self.tree.get_object('move_down_button').set_sensitive(True)
464 def on_item_tree_row_activated(self, treeview, path, column):
465 self.on_edit_properties_activate(None)
467 def on_item_tree_popup_menu(self, item_tree, event=None):
468 model, iter = item_tree.get_selection().get_selected()
469 if event:
470 #don't show if it's not the right mouse button
471 if event.button != 3:
472 return
473 button = event.button
474 event_time = event.time
475 info = item_tree.get_path_at_pos(int(event.x), int(event.y))
476 if info != None:
477 path, col, cellx, celly = info
478 item_tree.grab_focus()
479 item_tree.set_cursor(path, col, 0)
480 else:
481 path = model.get_path(iter)
482 button = 0
483 event_time = 0
484 item_tree.grab_focus()
485 item_tree.set_cursor(path, item_tree.get_columns()[0], 0)
486 popup = self.tree.get_object('edit_menu')
487 popup.popup(None, None, None, None, button, event_time)
488 #without this shift-f10 won't work
489 return True
491 def on_item_tree_drag_data_get(self, treeview, context, selection, target_id, etime):
492 items, iter = treeview.get_selection().get_selected()
493 self.drag_data = items[iter][3]
495 def on_item_tree_drag_data_received(self, treeview, context, x, y, selection, info, etime):
496 items = treeview.get_model()
497 types_into = (Gtk.TreeViewDropPosition.INTO_OR_BEFORE, Gtk.TreeViewDropPosition.INTO_OR_AFTER)
498 types_before = (Gtk.TreeViewDropPosition.BEFORE, Gtk.TreeViewDropPosition.INTO_OR_BEFORE)
499 types_after = (Gtk.TreeViewDropPosition.AFTER, Gtk.TreeViewDropPosition.INTO_OR_AFTER)
500 if selection.target == 'ALACARTE_ITEM_ROW':
501 drop_info = treeview.get_dest_row_at_pos(x, y)
502 before = None
503 after = None
504 if self.drag_data == None:
505 return False
506 item = self.drag_data
507 # by default we assume, that the items stays in the same menu
508 destination = item.get_parent()
509 if drop_info:
510 path, position = drop_info
511 target = items[path][3]
512 # move the item to the directory, if the item was dropped into it
513 if isinstance(target, GMenu.TreeDirectory) and (position in types_into):
514 # append the selected item to the choosen menu
515 destination = target
516 elif position in types_before:
517 before = target
518 elif position in types_after:
519 after = target
520 else:
521 # this does not happen
522 pass
523 else:
524 path = (len(items) - 1,)
525 after = items[path][3]
526 if isinstance(item, GMenu.TreeEntry):
527 self.editor.moveItem(item, destination, before, after)
528 elif isinstance(item, GMenu.TreeDirectory):
529 if self.editor.moveMenu(item, destination, before, after) == False:
530 self.loadUpdates()
531 elif isinstance(item, GMenu.TreeSeparator):
532 self.editor.moveSeparator(item, destination, before, after)
533 context.finish(True, True, etime)
534 elif selection.target == 'text/plain':
535 if selection.data == None:
536 return False
537 menus, iter = self.tree.get_object('menu_tree').get_selection().get_selected()
538 parent = menus[iter][2]
539 drop_info = treeview.get_dest_row_at_pos(x, y)
540 before = None
541 after = None
542 if drop_info:
543 path, position = drop_info
544 if position in types_before:
545 before = items[path][3]
546 else:
547 after = items[path][3]
548 else:
549 path = (len(items) - 1,)
550 after = items[path][3]
551 file_path = urllib.unquote(selection.data).strip()
552 if not file_path.startswith('file:'):
553 return
554 myfile = Gio.File(uri=file_path)
555 file_info = myfile.query_info(Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)
556 content_type = file_info.get_content_type()
557 if content_type == 'application/x-desktop':
558 input_stream = myfile.read()
559 keyfile = GLib.KeyFile()
560 keyfile.load_from_data(input_stream.read())
561 self.editor.createItem(parent, before, after, KeyFile=keyfile)
562 elif content_type in ('application/x-shellscript', 'application/x-executable'):
563 self.editor.createItem(parent, before, after,
564 Name=os.path.split(file_path)[1].strip(),
565 Exec=file_path.replace('file://', '').strip(),
566 Terminal=False)
567 self.drag_data = None
569 def on_item_tree_key_press_event(self, item_tree, event):
570 if event.keyval == Gdk.KEY_Delete:
571 self.on_edit_delete_activate(item_tree)
573 def on_move_up_button_clicked(self, button):
574 item_tree = self.tree.get_object('item_tree')
575 items, iter = item_tree.get_selection().get_selected()
576 if not iter:
577 return
578 path = items.get_path(iter)
579 #at top, can't move up
580 if path.get_indices()[0] == 0:
581 return
582 item = items[path][3]
583 before = items[(path.get_indices()[0] - 1,)][3]
584 if isinstance(item, GMenu.TreeEntry):
585 self.editor.moveItem(item, item.get_parent(), before=before)
586 elif isinstance(item, GMenu.TreeDirectory):
587 self.editor.moveMenu(item, item.get_parent(), before=before)
588 elif isinstance(item, GMenu.TreeSeparator):
589 self.editor.moveSeparator(item, item.get_parent(), before=before)
591 def on_move_down_button_clicked(self, button):
592 item_tree = self.tree.get_object('item_tree')
593 items, iter = item_tree.get_selection().get_selected()
594 if not iter:
595 return
596 path = items.get_path(iter)
597 #at bottom, can't move down
598 if path.get_indices()[0] == (len(items) - 1):
599 return
600 item = items[path][3]
601 after = items[path][3]
602 if isinstance(item, GMenu.TreeEntry):
603 self.editor.moveItem(item, item.get_parent(), after=after)
604 elif isinstance(item, GMenu.TreeDirectory):
605 self.editor.moveMenu(item, item.get_parent(), after=after)
606 elif isinstance(item, GMenu.TreeSeparator):
607 self.editor.moveSeparator(item, item.get_parent(), after=after)
609 def on_mainwindow_undo(self, accelgroup, window, keyval, modifier):
610 self.editor.undo()
612 def on_mainwindow_redo(self, accelgroup, window, keyval, modifier):
613 self.editor.redo()
615 def on_help_button_clicked(self, *args):
616 Gtk.show_uri(Gdk.Screen.get_default(), "ghelp:user-guide#menu-editor", Gtk.get_current_event_time())
618 def on_revert_button_clicked(self, button):
619 dialog = self.tree.get_object('revertdialog')
620 dialog.set_transient_for(self.tree.get_object('mainwindow'))
621 dialog.show_all()
622 if dialog.run() == Gtk.ResponseType.YES:
623 self.editor.revert()
624 dialog.hide()
626 def on_close_button_clicked(self, button):
627 self.quit()
629 def on_properties_button_clicked(self, button):
630 self.on_edit_properties_activate(None)
631 def on_delete_button_clicked(self, button):
632 self.on_edit_delete_activate(None)
634 def on_style_set(self, *args):
635 self.loadUpdates()
637 def quit(self):
638 self.editor.quit()
639 Gtk.main_quit()