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