updated on Tue Jan 17 12:00:36 UTC 2012
[aur-mirror.git] / compiz-deskmenu / compiz-deskmenu-editor
blobc5c16d1ac4f1aad447125ef21e3717d0a57c0b13
1 #!/usr/bin/env python2
2 #TODO: upcoming pipemenu stuff
3 import gtk, os
4 from lxml import etree
5 from xdg import BaseDirectory
6 import re #This is to autoset file mode for *.desktop icons
7 import ConfigParser
9 try:
10         import dbus
11 except ImportError:
12         dbus = None
14 class DeskmenuEditor(gtk.Window):
16         def __init__(self):
17                 gtk.Window.__init__(self)
18                 
19                 self.props.title = 'Compiz Deskmenu Editor'
20                 self.props.icon_name = 'gtk-edit'
21                 self.props.border_width = 12
22                 self.set_size_request(400, 400)
23                 self.model = gtk.TreeStore(object)
24                 self.add_menu(menu)
26                 vbox = gtk.VBox(spacing=12)
28                 scrolled = gtk.ScrolledWindow()
29                 scrolled.props.hscrollbar_policy = gtk.POLICY_NEVER
30                 scrolled.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
31                 treeview = gtk.TreeView(self.model)
32                 treeview.set_reorderable(True)
33                 cell = gtk.CellRendererText()
34                 elements = gtk.TreeViewColumn('Item', cell)
35                 elements.set_cell_data_func(cell, self.get_type)
36                 treeview.append_column(elements)
38                 name = gtk.TreeViewColumn('Name')
40                 cell = gtk.CellRendererPixbuf()
41                 name.pack_start(cell, False)
42                 name.set_cell_data_func(cell, self.get_icon)
44                 cell = gtk.CellRendererText()
45                 name.pack_start(cell)
46                 name.set_cell_data_func(cell, self.get_name)
48                 treeview.append_column(name)
49                 scrolled.add(treeview)
50                 vbox.pack_start(scrolled, True, True)
51                 targets = [
52                         ('deskmenu-element', gtk.TARGET_SAME_WIDGET, 0),
53                         ('text/uri-list', 0, 1),
54                 ]
55                 treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, targets, gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_MOVE)
56                 treeview.enable_model_drag_dest(targets, gtk.gdk.ACTION_MOVE)
58                 treeview.connect('drag-data-get', self.on_drag_data_get)
59                 treeview.connect('drag-data-received', self.on_drag_data_received)
61                 treeview.connect('row-activated', self.on_row_activated)
63                 treeview.connect('button-press-event', self.on_treeview_button_press_event)
64                 treeview.expand_all()
66                 self.selection = treeview.get_selection()
67                 self.selection.connect('changed', self.on_selection_changed)
69                 buttonbox = gtk.HButtonBox()
70                 vbox.pack_end(buttonbox, False, False)
72                 new = gtk.Button(stock=gtk.STOCK_NEW)
73                 new.connect('clicked', self.on_new_clicked)
74                 buttonbox.pack_start(new)
75                 self.edit = gtk.Button(stock=gtk.STOCK_EDIT)
76                 self.edit.connect('clicked', self.on_edit_clicked)
77                 buttonbox.pack_start(self.edit)
78                 self.delete = gtk.Button(stock=gtk.STOCK_DELETE)
79                 self.delete.connect('clicked', self.on_delete_clicked)
80                 buttonbox.pack_start(self.delete)
81                 close = gtk.Button(stock=gtk.STOCK_CLOSE)
82                 close.connect('clicked', self.on_close_clicked)
83                 buttonbox.pack_end(close)
85                 self.add(vbox)
87                 self.popup = gtk.Menu()
88                 self.edit_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_EDIT)
89                 self.edit_menu.connect('activate', self.on_edit_clicked)
90                 self.popup.append(self.edit_menu)
91                 self.delete_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_DELETE)
92                 self.delete_menu.connect('activate', self.on_delete_clicked)
93                 self.popup.append(self.delete_menu)
94                 self.popup.show_all()
96                 self.connect('destroy', self.on_close_clicked)
98                 self.show_all()
100         def add_menu(self, m, parent=None):
101                 for item in m.children:
102                         iter = self.model.append(parent, [item])
103                         if item.node.tag == 'menu':
104                                 self.add_menu(item, iter)
106         def get_name(self, column, cell, model, iter):
107                 name = model.get_value(iter, 0).get_name()
108                 if name is None:
109                         name = ''
110                 cell.set_property('text', name)
112         def get_type(self, column, cell, model, iter):
113                 typ = model.get_value(iter, 0).get_type()
114                 if typ is None:
115                         typ = ''
116                 cell.set_property('text', typ)
118         def get_icon(self, column, cell, model, iter):
119                 icon = model.get_value(iter, 0).get_icon()
120                 icon_mode = model.get_value(iter, 0).get_icon_mode()
121                 if icon is not None:
122                         if icon_mode is not None:
123                                 w = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
124                                 cell.set_property('pixbuf', gtk.gdk.pixbuf_new_from_file_at_size(icon, w[0], w[0]))
125                                 cell.set_property('icon-name', None) #possibly reduntant safety measure
126                         else:
127                                 cell.set_property('icon-name', icon)
128                                 cell.set_property('pixbuf', None) #possibly reduntant safety measure
129                 else:
130                         cell.set_property('icon-name', None)
131                         cell.set_property('pixbuf', None)
133         def on_new_clicked(self, widget):
135                 NewItemDialog(*self.selection.get_selected())
137         def on_edit_clicked(self, widget):
139                 EditItemDialog(*self.selection.get_selected())
140                 
141         def on_delete_clicked(self, widget):
143                 model, row = self.selection.get_selected()
145                 parent = None
146                 if row:
147                         current = model[row][0].node
149                         if current.tag == 'menu' and len(current):
150                                 warning = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE, 'Delete menu element with %s children?' %len(current))
151                                 warning.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_DELETE, gtk.RESPONSE_ACCEPT)
152                                 if warning.run() != gtk.RESPONSE_ACCEPT:
153                                         warning.destroy()
154                                         return
155                                 warning.destroy()
157                         parent = model[row].parent
158                         if parent is not None:
159                                 parent = parent[0].node
160                         else:
161                                 parent = menu.node
162                         parent.remove(current)
163                         model.remove(row)
165                 write_menu()
167         def on_close_clicked(self, widget):
169                 write_menu()
170                 gtk.main_quit()
172         def on_drag_data_get(self, treeview, context, selection, target_id,
173                                                    etime):
174                 treeselection = treeview.get_selection()
175                 model, iter = treeselection.get_selected()
176                 data = model.get_string_from_iter(iter)
177                 selection.set(selection.target, 8, data)
179         def on_drag_data_received(self, treeview, context, x, y, selection,
180                                                                 info, etime):
181                 model = treeview.get_model()
182                 data = selection.data
184                 drop_info = treeview.get_dest_row_at_pos(x, y)
185                 if selection.type == 'deskmenu-element':
186                         source = model[data][0]
187                         if drop_info:
188                                 path, position = drop_info
189                                 siter = model.get_iter(data)
190                                 diter = model.get_iter(path)
192                                 if model.get_path(model.get_iter_from_string(data)) == path:
193                                         return
195                                 dest = model[path][0]
196                                 if context.action == gtk.gdk.ACTION_MOVE:
197                                         source.node.getparent().remove(source.node)
199                                 if dest.node.tag == 'menu' and position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
200                                         gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
201                                         dest.node.append(source.node)
202                                         fiter = model.append(diter, row=(source,))
203                                 else:
204                                         i = dest.node.getparent().index(dest.node)
205                                         if position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE, 
206                                                 gtk.TREE_VIEW_DROP_BEFORE):
207                                                 dest.node.getparent().insert(i, source.node)
208                                                 fiter = model.insert_before(None, diter, row=(source,))
209                                         else:
210                                                 dest.node.getparent().insert(i+1, source.node)
211                                                 fiter = model.insert_after(None, diter, row=(source,))
213                                 if model.iter_has_child(siter):
214                                         citer = model.iter_children(siter)
215                                         while citer is not None:
216                                                 model.append(fiter, row=(model[citer][0],))
217                                                 citer = model.iter_next(citer)
218                                 if context.action == gtk.gdk.ACTION_MOVE:
219                                         context.finish(True, True, etime)
221                 elif selection.type == 'text/uri-list':
222                         if drop_info:
223                                 path, position = drop_info
224                                 uri = selection.data.replace('file:///', '/').strip()
225                                 entry = ConfigParser.ConfigParser()
226                                 entry.read(uri)
227                                 launcher = Launcher()
228                                 launcher.name = etree.SubElement(launcher.node, 'name')
229                                 launcher.icon = etree.SubElement(launcher.node, 'icon')
230                                 launcher.command = etree.SubElement(launcher.node, 'command')
231                                 try:
232                                         launcher.name.text = entry.get('Desktop Entry', 'Name')
233                                         if re.search("/", entry.get('Desktop Entry', 'Icon')):
234                                                 launcher.icon.attrib['mode1'] = 'file'
235                                                 launcher.icon.text = entry.get('Desktop Entry', 'Icon')
236                                         #launcher.icon.text = entry.get('Desktop Entry', 'Icon').split('.')[0]
237                                         launcher.command.text = entry.get('Desktop Entry', 'Exec').split('%')[0]
238                                 except ConfigParser.Error:
239                                         return
240                                 dest = model[path][0]
241                                 diter = model.get_iter(path)
242                                 if dest.node.tag == 'menu' and position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
243                                         gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
244                                         dest.node.append(launcher.node)
245                                         fiter = model.append(diter, row=(launcher,))
246                                 else:
247                                         i = dest.node.getparent().index(dest.node)
248                                         if position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE, 
249                                                 gtk.TREE_VIEW_DROP_BEFORE):
250                                                 dest.node.getparent().insert(i, launcher.node)
251                                                 fiter = model.insert_before(None, diter, row=(launcher,))
252                                         else:
253                                                 dest.node.getparent().insert(i+1, launcher.node)
254                                                 fiter = model.insert_after(None, diter, row=(launcher,))
255                                 if context.action == gtk.gdk.ACTION_MOVE:
256                                         context.finish(True, True, etime)
258                 write_menu()
260                 return
262         def on_selection_changed(self, selection):
264                 model, row = selection.get_selected()
266                 sensitive = row and model.get_value(row, 0).editable
268                 self.edit.props.sensitive = sensitive
269                 self.edit_menu.props.sensitive = sensitive
270                 self.delete.props.sensitive = row
271                 self.delete_menu.props.sensitive = row
273         def on_row_activated(self, treeview, path, view_column):
275                 model = treeview.get_model()
276                 EditItemDialog(model, model.get_iter(path))
277                 
278         def on_treeview_button_press_event(self, treeview, event):
279                 if event.button == 3:
280                         pthinfo = treeview.get_path_at_pos(int(event.x), int(event.y))
281                         if pthinfo is not None:
282                                 path, col, cellx, celly = pthinfo
283                                 treeview.grab_focus()
284                                 treeview.set_cursor(path, col, 0)
285                                 self.popup.popup(None, None, None, event.button, event.time)
286                         return 1
289 class NewItemDialog(gtk.Dialog):
291         elementlist = ['Launcher', 'Menu', 'Separator', 'Windows List',
292                 'Viewports List', 'Reload']
294         def __init__(self, model, row):
295                 gtk.Dialog.__init__(self, 'New Item', None, 0, None)
297                 self.set_size_request(250, 250)
299                 self.props.border_width = 6
300                 self.vbox.props.spacing = 6
301                 self.set_has_separator(False)
302                 self.treeview = self.make_treeview()
304                 scroll = gtk.ScrolledWindow()
305                 scroll.add(self.treeview)
306                 scroll.props.hscrollbar_policy = gtk.POLICY_NEVER
307                 scroll.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
308                 self.vbox.pack_start(scroll, True, True)
310                 self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
311                         gtk.STOCK_NEW, gtk.RESPONSE_ACCEPT)
313                 self.action_area.props.border_width = 0
315                 self.show_all()
317                 element = None
319                 if self.run() == gtk.RESPONSE_ACCEPT:
320                         m, r = self.treeview.get_selection().get_selected()
321                         if r:
322                                 elementname = m[r][0]
323                         else:
324                                 self.destroy()
325                                 return
326                         parent = sibling = None
327                         if row:
328                                 current = model[row][0]
329                                 if current.node.tag == 'menu':
330                                         parent = row
331                                 else:
332                                         parent = model[row].parent
333                                         if parent is not None:
334                                                 parent = parent.iter
335                                         sibling = row 
336                         if parent:
337                                 parentelement = model[parent][0]
338                         else:
339                                 parentelement = menu
341                         element = elementsbyname[elementname]()
342                         if sibling:
343                                 position = parentelement.node.index(current.node) + 1
344                                 parentelement.node.insert(position, element.node)
345                                 model.insert_after(parent, sibling, row=(element,))
346                         else:
347                                 model.append(parent, row=(element,))
348                                 parentelement.node.append(element.node)
350                 self.destroy()
351                 write_menu()
353                 if element and element.editable:
354                         EditItemDialog(element=element)
356         def make_treeview(self):
357                 model = gtk.ListStore(str)  
358                 for el in self.elementlist:
359                         model.append([el])
361                 treeview = gtk.TreeView(model)
362                 column = gtk.TreeViewColumn(None, gtk.CellRendererText(), text=0)
363                 treeview.set_headers_visible(False)
364                 treeview.append_column(column)
366                 treeview.connect('row-activated', self.on_row_activated)
367                 return treeview
369         def on_row_activated(self, treeview, path, view_column):
370                 self.response(gtk.RESPONSE_ACCEPT)
373 class EditItemDialog(gtk.Dialog):
375         def __init__(self, model=None, row=None, element=None):
376                 gtk.Dialog.__init__(self, 'Edit Item', None, 0, None)
378                 self.set_size_request(300, -1)
380                 self.props.border_width = 6
381                 self.vbox.props.spacing = 6
382                 self.set_has_separator(False)
384                 if element is None:
385                         if not row:
386                                 return
387                         element = model.get_value(row, 0)
389                 if not element.editable:
390                         return
392                 for widget in element.get_options():
393                         self.vbox.pack_start(widget, False, False)
395                 self.add_buttons(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
397                 self.show_all()
398                 self.run()
399                 self.destroy()
400                 write_menu()
404 class Item(object):
405         
406         def __init__(self, node=None, parent=None, type=None):
408                 self.editable = False
410                 if node is None:
411                         self.node = etree.Element('item')
412                         if type is not None:
413                                 self.node.attrib['type'] = type
414                 
415                 else:
416                         self.node = node
418         def get_name(self):
419                 return None
421         def get_type(self):
422                 return 'Item'
424         def get_icon(self):
425                 return None
427         def get_icon_mode(self):
428                 return None
430 class Launcher(Item):
431         
432         def __init__(self, node=None):
434                 Item.__init__(self, node)
436                 if node is None:
437                         self.node = etree.Element('item', type='launcher') 
438                 
439                 self.editable = True
441         def get_name(self):
443                 subnode = self.node.find('name')
444                 if subnode is not None:
445                         name = subnode.text
446                         if subnode.attrib.get('mode') == 'exec':
447                                 name = 'exec: %s' %name
448                         return name
449                 else:
450                         return None
452         def get_type(self):
453                 return 'Launcher'
455         def get_icon(self):
456                 iconnode = self.node.find('icon')
457                 if iconnode is not None:
458                         return iconnode.text
459                 else:
460                         return None
462         def get_icon_mode(self):
463                 iconnode = self.node.find('icon')
464                 if iconnode is not None:
465                         if iconnode.attrib.get('mode1') == 'file':
466                                 return iconnode.attrib.get('mode1')
467                 else:
468                         return None
470         def get_options(self):
472                 retlist = []
473                 sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
475                 label = gtk.Label()
476                 label.set_alignment(0, 0.5)
477                 sgroup.add_widget(label)
478                 label.set_markup('<b>Name:</b>')
479                 widget = gtk.Entry()
480                 
481                 namenode = self.node.find('name')
482                 if namenode is not None:
483                         name = namenode.text
484                 else:
485                         name = ''
486                 widget.props.text = name
487                 widget.connect('changed', self.on_subnode_changed, 'name')
489                 hbox = gtk.HBox()
490                 hbox.pack_start(label)
491                 hbox.pack_start(widget, True, True)
492                 retlist.append(hbox)
494                 label = gtk.Label()
495                 label.set_alignment(0, 0.5)
496                 sgroup.add_widget(label)
497                 label.set_markup('<b>Name mode:</b>')
498                 widget = gtk.combo_box_new_text()
499                 widget.append_text('Normal')
500                 widget.append_text('Execute')
501                 widget.props.active = namenode is not None and namenode.attrib.get('mode') == 'exec'
502                 widget.connect('changed', self.on_name_mode_changed)
504                 hbox = gtk.HBox()
505                 hbox.pack_start(label)
506                 hbox.pack_start(widget, True, True)
507                 retlist.append(hbox)
509                 label = gtk.Label()
510                 label.set_alignment(0, 0.5)
511                 sgroup.add_widget(label)
512                 label.set_markup('<b>Icon:</b>')
513                 widget = gtk.Entry()
514                 
515                 iconnode = self.node.find('icon')
516                 if iconnode is not None:
517                         icon = iconnode.text
518                 else:
519                         icon = ''
520                 widget.props.text = icon
521                 widget.connect('changed', self.on_subnode_changed, 'icon')
523                 hbox = gtk.HBox()
524                 hbox.pack_start(label)
525                 hbox.pack_start(widget, True, True)
526                 retlist.append(hbox)
527                 
528                 label = gtk.Label()
529                 label.set_alignment(0, 0.5)
530                 sgroup.add_widget(label)
531                 label.set_markup('<b>Icon mode:</b>')
532                 widget = gtk.combo_box_new_text()
533                 widget.append_text('Normal')
534                 widget.append_text('File Path')
535                 widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
536                 widget.connect('changed', self.on_icon_mode_changed)
538                 hbox = gtk.HBox()
539                 hbox.pack_start(label)
540                 hbox.pack_start(widget, True, True)
541                 retlist.append(hbox)
543                 label = gtk.Label()
544                 label.set_alignment(0, 0.5)
545                 sgroup.add_widget(label)
546                 label.set_markup('<b>Command:</b>')
547                 widget = gtk.Entry()
548                 commandnode = self.node.find('command')
549                 if commandnode is not None:
550                         command = commandnode.text
551                 else:
552                         command = ''
553                 widget.props.text = command
554                 widget.connect('changed', self.on_subnode_changed, 'command')
556                 hbox = gtk.HBox()
557                 hbox.pack_start(label)
558                 hbox.pack_start(widget, True, True)
559                 retlist.append(hbox)
561                 return retlist
563         def on_subnode_changed(self, widget, tag):
564                 text = widget.props.text
565                 subnode = self.node.find(tag)
566                 if text:
567                         if subnode is None:
568                                 subnode = etree.SubElement(self.node, tag)
569                         subnode.text = text
570                 else:
571                         if subnode is not None:
572                                 self.node.remove(subnode)
573                 
574         def on_name_mode_changed(self, widget):
575                 namenode = self.node.find('name')
576                 if widget.props.active:
577                         if namenode is None:
578                                 namenode = etree.SubElement(self.node, 'name')
579                         namenode.attrib['mode'] = 'exec'
580                 elif 'mode' in namenode.attrib:
581                         del namenode.attrib['mode']
582                         
583         def on_icon_mode_changed(self, widget):
584                 iconnode = self.node.find('icon')
585                 if widget.props.active:
586                         if iconnode is None:
587                                 iconnode = etree.SubElement(self.node, 'icon')
588                         iconnode.attrib['mode1'] = 'file'
589                 elif 'mode1' in iconnode.attrib:
590                         del iconnode.attrib['mode1']
592 class Windowlist(Item):
593         
594         def __init__(self, node=None):
595                 Item.__init__(self, node, type='windowlist')
596                 self.editable = True
597         
598         def get_icon(self):
599                 iconnode = self.node.find('icon')
600                 if iconnode is not None:
601                         return iconnode.text
602                 else:
603                         return None
605         def get_icon_mode(self):
606                 iconnode = self.node.find('icon')
607                 if iconnode is not None:
608                         if iconnode.attrib.get('mode1') == 'file':
609                                 return iconnode.attrib.get('mode1')
610                 else:
611                         return None
613         def get_options(self):
614                 retlist = []
615                 sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
617                 label = gtk.Label()
618                 label.set_alignment(0, 0.5)
619                 sgroup.add_widget(label)
620                 label.set_markup('<b>Icon:</b>')
621                 widget = gtk.Entry()
622                 
623                 iconnode = self.node.find('icon')
624                 if iconnode is not None:
625                         icon = iconnode.text
626                 else:
627                         icon = ''
628                 widget.props.text = icon
629                 widget.connect('changed', self.on_subnode_changed, 'icon')
631                 hbox = gtk.HBox()
632                 hbox.pack_start(label)
633                 hbox.pack_start(widget, True, True)
634                 retlist.append(hbox)
635                 
636                 label = gtk.Label()
637                 label.set_alignment(0, 0.5)
638                 sgroup.add_widget(label)
639                 label.set_markup('<b>Icon mode:</b>')
640                 widget = gtk.combo_box_new_text()
641                 widget.append_text('Normal')
642                 widget.append_text('File Path')
643                 widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
644                 widget.connect('changed', self.on_icon_mode_changed)
646                 hbox = gtk.HBox()
647                 hbox.pack_start(label)
648                 hbox.pack_start(widget, True, True)
649                 retlist.append(hbox)
651                 return retlist
653         def on_subnode_changed(self, widget, tag):
654                 text = widget.props.text
655                 subnode = self.node.find(tag)
656                 if text:
657                         if subnode is None:
658                                 subnode = etree.SubElement(self.node, tag)
659                         subnode.text = text
660                 else:
661                         if subnode is not None:
662                                 self.node.remove(subnode)
664         def on_icon_mode_changed(self, widget):
665                 iconnode = self.node.find('icon')
666                 if widget.props.active:
667                         if iconnode is None:
668                                 iconnode = etree.SubElement(self.node, 'icon')
669                         iconnode.attrib['mode1'] = 'file'
670                 elif 'mode1' in iconnode.attrib:
671                         del iconnode.attrib['mode1']
673         def get_type(self):
674                 return 'Windows list'
676 class Viewportlist(Item):
677         
678         def __init__(self, node=None): #change to be an attribute of viewportlist
679                 Item.__init__(self, node, type='viewportlist')
680                 self.editable = True
681                 self.wrap = self.node.find('wrap')
683         def get_type(self):
684                 return 'Viewports list'
685         
686         def get_icon(self):
687                 iconnode = self.node.find('icon')
688                 if iconnode is not None:
689                         return iconnode.text
690                 else:
691                         return None
693         def get_icon_mode(self):
694                 iconnode = self.node.find('icon')
695                 if iconnode is not None:
696                         if iconnode.attrib.get('mode1') == 'file':
697                                 return iconnode.attrib.get('mode1')
698                 else:
699                         return None
702         def get_options(self):
703                 retlist = []
704                 sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
705                 widget = gtk.CheckButton("Wrap Viewports")
706                 widget.props.active = self.get_wrap()
707                 widget.connect('toggled', self.on_wrap_changed)
708                 retlist.append(widget)
710                 label = gtk.Label()
711                 label.set_alignment(0, 0.5)
712                 sgroup.add_widget(label)
713                 label.set_markup('<b>Icon:</b>')
714                 widget = gtk.Entry()
715                 
716                 iconnode = self.node.find('icon')
717                 if iconnode is not None:
718                         icon = iconnode.text
719                 else:
720                         icon = ''
721                 widget.props.text = icon
722                 widget.connect('changed', self.on_subnode_changed, 'icon')
724                 hbox = gtk.HBox()
725                 hbox.pack_start(label)
726                 hbox.pack_start(widget, True, True)
727                 retlist.append(hbox)
728                 
729                 label = gtk.Label()
730                 label.set_alignment(0, 0.5)
731                 sgroup.add_widget(label)
732                 label.set_markup('<b>Icon mode:</b>')
733                 widget = gtk.combo_box_new_text()
734                 widget.append_text('Normal')
735                 widget.append_text('File Path')
736                 widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
737                 widget.connect('changed', self.on_icon_mode_changed)
739                 hbox = gtk.HBox()
740                 hbox.pack_start(label)
741                 hbox.pack_start(widget, True, True)
742                 retlist.append(hbox)
744                 return retlist
746         def get_wrap(self):
747                 if self.wrap is not None:
748                         return self.wrap.text == 'true'
749                 return False
751         def on_subnode_changed(self, widget, tag):
752                 text = widget.props.text
753                 subnode = self.node.find(tag)
754                 if text:
755                         if subnode is None:
756                                 subnode = etree.SubElement(self.node, tag)
757                         subnode.text = text
758                 else:
759                         if subnode is not None:
760                                 self.node.remove(subnode)
762         def on_wrap_changed(self, widget):
763                 if self.wrap is None:
764                         self.wrap = etree.SubElement(self.node, 'wrap')
765                 if widget.props.active:
766                         text = 'true'
767                 else:
768                         text = 'false'
769                 self.wrap.text = text
771         def on_icon_mode_changed(self, widget):
772                 iconnode = self.node.find('icon')
773                 if widget.props.active:
774                         if iconnode is None:
775                                 iconnode = etree.SubElement(self.node, 'icon')
776                         iconnode.attrib['mode1'] = 'file'
777                 elif 'mode1' in iconnode.attrib:
778                         del iconnode.attrib['mode1']
780 class Reload(Item):
781         
782         def __init__(self, node=None):
783                 Item.__init__(self, node, type='reload')
784                 self.editable = True
786         def get_type(self):
787                 return 'Reload'
789         def get_icon(self):
790                 iconnode = self.node.find('icon')
791                 if iconnode is not None:
792                         return iconnode.text
793                 else:
794                         return None
796         def get_icon_mode(self):
797                 iconnode = self.node.find('icon')
798                 if iconnode is not None:
799                         if iconnode.attrib.get('mode1') == 'file':
800                                 return iconnode.attrib.get('mode1')
801                 else:
802                         return None
805         def get_options(self):
806                 retlist = []
807                 sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
809                 label = gtk.Label()
810                 label.set_alignment(0, 0.5)
811                 sgroup.add_widget(label)
812                 label.set_markup('<b>Icon:</b>')
813                 widget = gtk.Entry()
814                 
815                 iconnode = self.node.find('icon')
816                 if iconnode is not None:
817                         icon = iconnode.text
818                 else:
819                         icon = ''
820                 widget.props.text = icon
821                 widget.connect('changed', self.on_subnode_changed, 'icon')
823                 hbox = gtk.HBox()
824                 hbox.pack_start(label)
825                 hbox.pack_start(widget, True, True)
826                 retlist.append(hbox)
827                 
828                 label = gtk.Label()
829                 label.set_alignment(0, 0.5)
830                 sgroup.add_widget(label)
831                 label.set_markup('<b>Icon mode:</b>')
832                 widget = gtk.combo_box_new_text()
833                 widget.append_text('Normal')
834                 widget.append_text('File Path')
835                 widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
836                 widget.connect('changed', self.on_icon_mode_changed)
838                 hbox = gtk.HBox()
839                 hbox.pack_start(label)
840                 hbox.pack_start(widget, True, True)
841                 retlist.append(hbox)
842                 hbox = gtk.HBox()
843                 hbox.pack_start(label)
844                 hbox.pack_start(widget, True, True)
845                 retlist.append(hbox)
847                 return retlist
849         def on_subnode_changed(self, widget, tag):
850                 text = widget.props.text
851                 subnode = self.node.find(tag)
852                 if text:
853                         if subnode is None:
854                                 subnode = etree.SubElement(self.node, tag)
855                         subnode.text = text
856                 else:
857                         if subnode is not None:
858                                 self.node.remove(subnode)
860         def on_icon_mode_changed(self, widget):
861                 iconnode = self.node.find('icon')
862                 if widget.props.active:
863                         if iconnode is None:
864                                 iconnode = etree.SubElement(self.node, 'icon')
865                         iconnode.attrib['mode1'] = 'file'
866                 elif 'mode1' in iconnode.attrib:
867                         del iconnode.attrib['mode1']
869 class Separator(object):
870         
871         def __init__(self, node=None, parent=None):
872                 self.node = node
873                 self.editable = False
874                 if self.node is None:
875                         self.node = etree.Element('separator')
877         def get_name(self):
878                 return '---'
880         def get_type(self):
881                 return 'Separator'
883         def get_icon(self):
884                 return None
885                 
886         def get_icon_mode(self):
887                 return None
889 class Menu(object):
891         def __init__(self, node=None):
892                 self.node = node
893                 self.children = []
894                 self.editable = True
896                 if node is None:
897                         self.node = etree.Element('menu', name='')
899                 for child in self.node.getchildren():
900                         try:
901                                 self.children.append(self.make_child(child))
902                         except KeyError:
903                                 pass
904                 
906         def make_child(self, child):
907                 return elements[child.tag](child)
909         def get_name(self):
910                 name = self.node.attrib.get('name', '')
911                 if self.node.attrib.get('mode') == 'exec':
912                         name = 'exec: %s' %name
913                 return name
915 #TODO: make this say exec: $name when it detects mode
916         def get_icon(self):
917                 return self.node.attrib.get('icon', '')
919         def get_icon_mode(self):
920                 if self.node.attrib.get('mode1') == 'file':
921                         return self.node.attrib.get('mode1')
922                 else:
923                         return None
925         def get_type(self):
926                 return 'Menu'
928         def get_options(self):
930                 retlist = []
931                 sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
933                 label = gtk.Label()
934                 label.set_alignment(0, 0.5)
935                 label.set_markup('<b>Name:</b>')
936                 widget = gtk.Entry()
937                 widget.props.text = self.node.attrib.get('name', '')
938                 widget.connect('changed', self.on_name_changed)
939                 
940                 hbox = gtk.HBox()
941                 hbox.pack_start(label)
942                 hbox.pack_start(widget, True, True)
943                 retlist.append(hbox)
945                 label = gtk.Label()
946                 label.set_alignment(0, 0.5)
947                 sgroup.add_widget(label)
948                 label.set_markup('<b>Name mode:</b>')
949                 widget = gtk.combo_box_new_text()
950                 widget.append_text('Normal')
951                 widget.append_text('Execute')
952                 widget.props.active = self.node.attrib.get('name') is not None and self.node.attrib.get('mode') == 'exec'
953                 widget.connect('changed', self.on_name_mode_changed)
955                 hbox = gtk.HBox()
956                 hbox.pack_start(label)
957                 hbox.pack_start(widget, True, True)
958                 retlist.append(hbox)
960                 #TODO:Make this actually edit icon
961                 label = gtk.Label()
962                 label.set_alignment(0, 0.5)
963                 sgroup.add_widget(label)
964                 label.set_markup('<b>Icon:</b>')
965                 widget = gtk.Entry()
966                 if self.node.attrib.get('icon') is not None:
967                         widget.props.text = self.node.attrib.get('icon')
968                 else:
969                         widget.props.text = ''
970                 #widget.props.text = self.node.attrib.get('icon')
971                 widget.connect('changed', self.on_icon_changed)
972                 
973                 hbox = gtk.HBox()
974                 hbox.pack_start(label)
975                 hbox.pack_start(widget, True, True)
976                 retlist.append(hbox)
977                 
978                 label = gtk.Label()
979                 label.set_alignment(0, 0.5)
980                 sgroup.add_widget(label)
981                 label.set_markup('<b>Icon mode:</b>')
982                 widget = gtk.combo_box_new_text()
983                 widget.append_text('Normal')
984                 widget.append_text('File Path')
985                 widget.props.active = self.node.attrib.get('icon') is not None and self.node.attrib.get('mode1') == 'file'
986                 widget.connect('changed', self.on_icon_mode_changed)
988                 hbox = gtk.HBox()
989                 hbox.pack_start(label)
990                 hbox.pack_start(widget, True, True)
991                 retlist.append(hbox)
993                 return retlist
995         def on_name_changed(self, widget):
996                 self.node.attrib['name'] = widget.props.text
997         def on_name_mode_changed(self, widget): #EDIT THIS
998                 if widget.props.active:
999                         self.node.attrib['mode'] = 'exec'
1000                 elif 'mode' in self.node.attrib:
1001                         del self.node.attrib['mode']
1002         def on_icon_changed(self, widget):
1003                 if widget.props.text != '':
1004                         self.node.attrib['icon'] = widget.props.text
1005                 else:
1006                         del self.node.attrib['icon']
1007         def on_icon_mode_changed(self, widget): #EDIT THIS
1008                 if widget.props.active:
1009                         self.node.attrib['mode1'] = 'file'
1010                 elif 'mode1' in self.node.attrib:
1011                         del self.node.attrib['mode1']
1013 itemtypes = {
1014         'launcher': Launcher,
1015         'windowlist': Windowlist,
1016         'viewportlist': Viewportlist,
1017         'reload': Reload,
1021 def make_item(node):
1023         itemtype = node.attrib.get('type')
1024         return itemtypes.get(itemtype, Item)(node)
1027 def indent(elem, level=0):
1028         i = "\n" + level*"  "
1029         if len(elem):
1030                 if not elem.text or not elem.text.strip():
1031                         elem.text = i + "  "
1032                 for e in elem:
1033                         indent(e, level+1)
1034                         if not e.tail or not e.tail.strip():
1035                                 e.tail = i + "  "
1036                 if not e.tail or not e.tail.strip():
1037                         e.tail = i
1038         else:
1039                 if level and (not elem.tail or not elem.tail.strip()):
1040                         elem.tail = i
1042 def write_menu():
1044         indent(menu.node)
1045         menufile.write(open(os.path.join(configdir, 'menu.xml'), 'w'))
1047         if bus is not None:
1048                 try:
1049                         bus.get_object('org.compiz_fusion.deskmenu',
1050                                 '/org/compiz_fusion/deskmenu/menu').reload()
1051                 except dbus.DBusException:
1052                         return
1054 elements = {'menu': Menu, 'item': make_item, 'separator': Separator}
1056 elementsbyname = {
1057         'Launcher': Launcher,
1058         'Windows List': Windowlist,
1059         'Viewports List': Viewportlist,
1060         'Reload': Reload,
1061         'Separator': Separator,
1062         'Menu': Menu,
1066 if __name__ == '__main__':
1068         if dbus:
1069                 try:
1070                         bus = dbus.SessionBus()
1071                 except dbus.DBusException:
1072                         bus = None
1073         else:
1074                 bus = None
1076         filename = BaseDirectory.load_first_config('compiz/deskmenu/menu.xml')
1077         menufile = etree.parse(filename)
1078         root = menufile.getroot()
1079         menu = Menu(root)
1080         configdir = BaseDirectory.save_config_path('compiz/deskmenu')
1081         DeskmenuEditor()
1082         gtk.main()