#!/usr/bin/python
#
# Openbox Menu Editor 1.0 beta
#
# Copyright 2005 Manuel Colmenero
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import obxml, gtk, gtk.glade, gobject, random, time, os, sys
class App:
def reconfigureOpenbox(self):
lines = os.popen("ps aux").read().splitlines()
ob = os.popen("which openbox").read().strip()
for line in lines:
if ob in " ".join(line.split()[10:]):
os.kill(int(line.split()[1]), 12)
break
# Recursively creates the treeview model
def createTree(self, padre, menuid):
for it in self.menu.getMenu(menuid):
if it["type"] == "item":
self.treemodel.append(padre, (it["label"], 'item',it["action"],it["execute"],it["parent"],""))
elif it["type"] == "separator":
self.treemodel.append(padre, ("", "separator", "", "", it["parent"], ""))
elif it["type"] == "menu":
hijo=self.treemodel.append(padre, (it["label"], 'menu',it["action"],it["execute"], it["parent"], it["id"]))
if self.menu.getMenu(it["id"]) and it["action"] == "":
self.createTree(hijo, it["id"])
def deleteTree(self):
# treemodel.iter(label, type, [action], [execute], parent, [menu-id])
self.treemodel=gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
self.treeview.set_model(self.treemodel)
# Sets the state of "menu modified"
# Refreshes the window's title
def _sth_changed(self, op):
self.sth_changed = op
if op: s = "(*)"
else: s = ""
self.arbol.get_widget("window1").set_title("Obmenu: %s %s" % (self.menu_path, s))
# Auxiliary function for model.foreach()
def _change_id(self, model, path, it,(old_id, new_id)):
mid = model.get_value(it,5)
if mid == old_id:
model.set(it, 5, new_id)
parent = model.get_value(it,4)
if parent == old_id:
model.set(it, 4, new_id)
def clear_fields(self):
self.auto_change = True
self.label_entry
for each in (self.label_entry, self.id_entry, self.execute_entry):
each.set_sensitive(False)
each.set_text("")
self.action_entry.set_sensitive(False)
self.auto_change = False
def confirm(self, message):
dlg = gtk.MessageDialog(None,0,gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO,message)
res = dlg.run()
dlg.destroy()
if res == -8: return True
else: return False
def ask_for_path(self, title, op):
if op == 0:
action = gtk.FILE_CHOOSER_ACTION_OPEN
else:
action = gtk.FILE_CHOOSER_ACTION_SAVE
dlg = gtk.FileChooserDialog(title,None,action,(gtk.STOCK_OK, 1, gtk.STOCK_CANCEL, 3))
dlg.set_default_response(1)
res = dlg.run()
flnm = dlg.get_filename()
dlg.destroy()
if res == 1: return flnm
# New file clicked
def new(self, bt):
if self.sth_changed:
res = self.confirm("Changes in %s will be lost. Continue?" % (self.menu_path))
if not res: return
self.deleteTree()
self.menu.newMenu()
self.unsaved_menu = True
self.menu_path = "Untitled"
self._sth_changed(True)
self.clear_fields()
def open(self, bt):
if self.sth_changed:
res = self.confirm("Changes in %s will be lost. Continue?" % (self.menu_path))
if not res: return
path = self.ask_for_path("Open", 0)
if not path: return
self.menu_path = path
self.unsaved_menu = False
self._sth_changed(False)
# Load in memory the real xml menu
self.menu = obxml.ObMenu()
self.menu.loadMenu(self.menu_path)
self.deleteTree() #Not really deleting this time, just initialiting
self.createTree(None, None)
self.clear_fields()
# save the menu
def save(self, bt):
if self.unsaved_menu:
path = self.ask_for_path("Save as...", 1)
if not path: return
self.menu_path = path
self.unsaved_menu = False
self.menu.saveMenu(self.menu_path)
self.reconfigureOpenbox()
self._sth_changed(False)
# save as...
def save_as(self, bt):
path = self.ask_for_path("Save as...", 1)
if not path: return
self.menu_path = path
self.unsaved_menu = False
self._sth_changed(False)
self.menu.saveMenu(self.menu_path)
self.reconfigureOpenbox()
# quit signal
def quit (self, bt, arg2):
if self.sth_changed:
res = self.confirm("There are unsaved changes. Exit anyway?")
if not res: return True
gtk.main_quit()
# file->quit menu signal
def mnu_quit (self, bt):
self.quit(None,None)
# id_entry changed signal
def change_id(self, pa):
if self.auto_change: return
self._sth_changed(True)
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
if model.get_value(ite, 1).lower() != "menu": return
old_id = model.get_value(ite, 5)
new_id = self.id_entry.get_text()
if model.get_value(ite, 2).lower() == "link":
self.auto_change = True
if self.menu.getMenuLabel(new_id):
model.set(ite, 0, self.menu.getMenuLabel(new_id), 5, new_id)
self.label_entry.set_text(self.menu.getMenuLabel(new_id))
else:
model.set(ite, 0, new_id, 5, new_id)
self.label_entry.set_text(new_id)
self.auto_change = False
self.menu.setRefId( model.get_value(ite, 4), old_id, new_id)
else:
if old_id != new_id and not self.menu.isMenu(new_id):
self.menu.replaceId(old_id, new_id)
model.foreach(self._change_id, (old_id, new_id))
# label_entry changed signal
def change_label(self, pa):
if self.auto_change: return
self._sth_changed(True)
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
(label, tipe, aaa, eee, menu, mid) = model.get(ite,0,1,2,3,4,5)
lb = self.label_entry.get_text()
p = model.get_path(ite)
n = p[len(p)-1]
if tipe == "item":
self.menu.setItemProps(menu, n, lb, aaa, eee)
elif tipe == "menu":
if aaa == "":
self.menu.setMenuLabel(mid, lb)
else:
self.menu.setRefLabel(menu, mid, lb)
model.set(ite, 0, lb)
# action_combo_box changed signal
def change_action(self, pa):
if self.auto_change: return
self._sth_changed(True)
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
(label, tipe, aaa, eee, menu, mid) = model.get(ite,0,1,2,3,4,5)
p = model.get_path(ite)
n = p[len(p)-1]
if tipe == "item":
ac = self.action_entry.get_active()
self.execute_entry.set_sensitive(False)
self.execute_srch.set_sensitive(False)
if ac == 0:
action = "Execute"
self.execute_entry.set_sensitive(True)
self.execute_srch.set_sensitive(True)
elif ac == 1:
action = "Reconfigure"
eee = ""
elif ac == 2:
action = "Restart"
eee = ""
elif ac == 3:
action = "Exit"
eee = ""
self.menu.setItemProps(menu, n, label, action, eee)
model.set(ite, 2, action, 3, eee)
# execute_entry changed signal
def change_execute(self, pa):
if self.auto_change: return
self._sth_changed(True)
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
(label, tipe, aaa, eee, menu, mid) = model.get(ite,0,1,2,3,4,5)
p = model.get_path(ite)
n = p[len(p)-1]
ex = self.execute_entry.get_text()
if tipe == "item":
p = model.get_path(ite)
n = p[len(p)-1]
self.menu.setItemProps(menu, n, label, aaa, ex)
elif tipe == "menu":
self.menu.setMenuExecute(menu, mid, ex)
model.set(ite, 3, ex)
# buton [...] clicked signal
def search_clicked(self, arg):
path = self.ask_for_path("Select file", 0)
if path:
self.execute_entry.set_text(path)
# treeview key pressed signal
def tree_key_pressed(self, treeview, ev):
# if Delete key has been pressed
if ev.keyval == 65535:
self.remove(None)
# treeview clicked signal
def treeview_changed(self, param):
(model, ite) = param.get_selection().get_selected()
if not ite: return
(label, tipe, action, exe, menu, mid) = model.get(ite,0,1,2,3,4,5)
self.auto_change = True
self.label_entry.set_text(label)
self.execute_entry.set_text(exe)
self.label_entry.set_sensitive(tipe == "item" or (tipe == "menu" and action != "Link"))
self.action_entry.set_sensitive(tipe == "item")
self.id_entry.set_sensitive(tipe == "menu")
self.id_entry.set_text(mid)
if tipe == "item":
self.execute_entry.set_sensitive(False)
self.execute_srch.set_sensitive(False)
if action.lower() == "execute":
self.action_entry.set_active(0)
self.execute_entry.set_sensitive(True)
self.execute_srch.set_sensitive(True)
elif action.lower() == "reconfigure":
self.action_entry.set_active(1)
elif action.lower() == "restart":
self.action_entry.set_active(2)
elif action.lower() == "exit":
self.action_entry.set_active(3)
else:
self.action_entry.set_sensitive(False)
elif tipe == "menu":
self.execute_entry.set_sensitive(action == "Pipemenu")
self.execute_srch.set_sensitive(action == "Pipemenu")
else:
self.execute_entry.set_sensitive(False)
self.execute_srch.set_sensitive(False)
self.auto_change = False
# new menu button clicked
def new_menu(self, param):
(model, ite) = self.treeview.get_selection().get_selected()
if ite:
(label, tipe, action, exe, menu, mid) = model.get(ite,0,1,2,3,4,5)
parent = model.iter_parent(ite)
n = model.get_path(ite)[-1]
else:
menu = None
parent = model.get_iter_root()
n = 0
nmid="%s-%d%d%d" % (menu, random.randint(0,99), time.gmtime()[4], time.gmtime()[5])
n_menu = self.treemodel.insert_before(parent, ite,("New Menu", "menu", "", "", menu, nmid))
itm = self.treemodel.append(n_menu, ("New Item", "item", "Execute", "command", nmid, ""))
self.menu.createMenu(menu, "New Menu", nmid, n)
self.menu.createItem(nmid, "New Item", "Execute", "command")
path = model.get_path(n_menu)
self.treeview.set_cursor(path, None, False)
self.label_entry.select_region(0, -1)
self.label_entry.grab_focus()
self._sth_changed(True)
# new item button clicked
def new_item(self,param):
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
(label, tipe, action, exe, menu, mid) = model.get(ite,0,1,2,3,4,5)
if menu == None: return
n = model.get_path(ite)[-1]
parent = model.iter_parent(ite)
self.menu.createItem(menu, "New Item", "Execute", "command", n)
itm = self.treemodel.insert_before(parent, ite, ("New Item", 'item','Execute','command', menu, ""))
path = model.get_path(itm)
self.treeview.set_cursor(path, None, False)
self.label_entry.select_region(0, -1)
self.label_entry.grab_focus()
self._sth_changed(True)
# new separator button clicked
def new_separator(self,param):
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
(label, tipe, action, exe, menu, mid) = model.get(ite,0,1,2,3,4,5)
if menu == None: return
n = model.get_path(ite)[-1]
parent = model.iter_parent(ite)
self.menu.createSep(menu, n)
itm = self.treemodel.insert_before(parent, ite, ("", 'separator', '','', menu, ""))
path = model.get_path(itm)
self.treeview.set_cursor(path, None, False)
self.treeview.grab_focus()
self._sth_changed(True)
# new link button clicked
def new_link(self,param):
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
(label, tipe, action, exe, menu, mid) = model.get(ite,0,1,2,3,4,5)
if menu == None: return
n = model.get_path(ite)[-1]
parent = model.iter_parent(ite)
nmid="link-%d%d%d" % (random.randint(0,99), time.gmtime()[4], time.gmtime()[5])
self.menu.createLink(menu, nmid, n)
itm = self.treemodel.insert_before(parent, ite, (nmid, 'menu', 'Link','', menu, nmid))
path = model.get_path(itm)
self.treeview.set_cursor(path, None, False)
self.id_entry.select_region(0, -1)
self.id_entry.grab_focus()
self._sth_changed(True)
# new pipe button clicked
def new_pipe(self,param):
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
(label, tipe, action, exe, menu, mid) = model.get(ite,0,1,2,3,4,5)
if menu == None: return
n = model.get_path(ite)[-1]
parent = model.iter_parent(ite)
nmid="pipe-%d%d%d" % (random.randint(0,99), time.gmtime()[4], time.gmtime()[5])
self.menu.createPipe(menu, nmid, "Newpipe", "command", n)
itm = self.treemodel.insert_before(parent, ite, ("Newpipe", 'menu', 'Pipemenu','command', menu, nmid))
path = model.get_path(itm)
self.treeview.set_cursor(path, None, False)
self.label_entry.select_region(0, -1)
self.label_entry.grab_focus()
self._sth_changed(True)
# up button clicked
def up(self,param):
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
(label, tipe, action, exe, menu, mid) = model.get(ite,0,1,2,3,4,5)
if menu == "None": menu = None
p = model.get_path(ite)
n = p[len(p)-1]
if n > 0:
self._sth_changed(True)
self.menu.interchange(menu, n, n-1)
l = list(p)
l[len(l)-1] -= 1
p = tuple(l)
upper = model.get_iter(p)
model.move_before(ite,upper)
# down button clicked
def down(self,param):
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
(label, tipe, action, exe, menu, mid) = model.get(ite,0,1,2,3,4,5)
p = model.get_path(ite)
n = p[len(p)-1]
if n < self.menu._get_menu_len(menu) -1:
self._sth_changed(True)
self.menu.interchange(menu, n, n+1)
l = list(p)
l[len(l)-1] += 1
p = tuple(l)
upper = model.get_iter(p)
model.move_after(ite,upper)
# remove button clicked
def remove(self, param):
(model, ite) = self.treeview.get_selection().get_selected()
if not ite: return
self._sth_changed(True)
(label, tipe, action, exe, menu, mid) = model.get(ite,0,1,2,3,4,5)
path = model.get_path(ite)
n = path[len(path)-1]
if tipe == "menu":
if model.iter_has_child(ite):
self.menu.removeMenu(mid)
else:
self.menu.removeItem(menu, n)
else:
self.menu.removeItem(menu, n)
model.remove(ite)
self.clear_fields()
def mnu_remove(self, param):
if self.treeview.is_focus():
self.remove(None)
def show_about(self, args):
#gtk.glade.XML("/usr/local/share/obmenu/obmenu.glade", "aboutdialog1")
gtk.glade.XML(self.gladefile, "aboutdialog1")
# application init
def init(self):
if len(sys.argv) == 2:
# must be a path to a menu
self.menu_path = sys.argv[1]
elif len(sys.argv) == 1:
self.menu_path = os.getenv("HOME") + "/.config/openbox/menu.xml"
else:
print "Error: Wrong number of arguments"
print "Usage: obmenu /path/to/menu.xml"
print "\tOr just obmenu, to edit the default file"
return
if not os.path.isfile(self.menu_path):
print "Error: \"%s\" not found" % self.menu_path
return
self.unsaved_menu = False
# Load in memory the real xml menu
self.menu = obxml.ObMenu()
self.menu.loadMenu(self.menu_path)
# Look for my glade file!
if os.path.isfile("obmenu.glade"):
# It's here.. looks like a pre-installation execution
self.gladefile = "obmenu.glade"
elif os.path.isfile("/usr/local/share/obmenu/obmenu.glade"):
self.gladefile = "/usr/local/share/obmenu/obmenu.glade"
elif os.path.isfile("/usr/share/obmenu/obmenu.glade"):
self.gladefile = "/usr/share/obmenu/obmenu.glade"
else:
print "ERROR: obmenu.glade not found!"
print " check that everything was installed all right"
sys.exit(1)
# Set the basics for GTK
self.arbol = gtk.glade.XML(self.gladefile, "window1")
self.treeview=self.arbol.get_widget("treeview1")
self.label_entry = self.arbol.get_widget("entry1")
self.action_entry = self.arbol.get_widget("combobox1")
self.execute_entry = self.arbol.get_widget("entry2")
self.execute_srch = self.arbol.get_widget("button1")
self.id_entry = self.arbol.get_widget("entry3")
# treemodel.iter(label, type, [action], [execute], parent, [menu-id])
self.treemodel=gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
self.treeview.set_model(self.treemodel)
# Signals
self.arbol.signal_autoconnect ({
"new_clicked": self.new,
"open_clicked": self.open,
"save_clicked": self.save,
"save_as_clicked": self.save_as,
"mnu_quit": self.mnu_quit,
"mnu_remove": self.mnu_remove,
"label_changed": self.change_label,
"id_changed": self.change_id,
"action_changed": self.change_action,
"execute_changed": self.change_execute,
"action_changed": self.change_action,
"search_clicked": self.search_clicked,
"on_treeview1": self.treeview_changed,
"tree_key_pressed": self.tree_key_pressed,
"menu_clicked": self.new_menu,
"item_clicked": self.new_item,
"separator_clicked": self.new_separator,
"pipe_clicked": self.new_pipe,
"link_clicked": self.new_link,
"remove_clicked": self.remove,
"up_clicked": self.up,
"down_clicked": self.down,
"show_about": self.show_about,
"exit": self.quit })
# Set the columns
self.treeview.set_headers_visible(True)
renderer=gtk.CellRendererText()
column=gtk.TreeViewColumn("Label",renderer, text=0)
column.set_resizable(True)
self.treeview.append_column(column)
renderer=gtk.CellRendererText()
column=gtk.TreeViewColumn("Type",renderer,text=1)
column.set_resizable(True)
self.treeview.append_column(column)
renderer=gtk.CellRendererText()
column=gtk.TreeViewColumn("Action",renderer,text=2)
column.set_resizable(True)
self.treeview.append_column(column)
renderer=gtk.CellRendererText()
column=gtk.TreeViewColumn("Execute",renderer,text=3)
column.set_resizable(True)
self.treeview.append_column(column)
# Some states of the app
self.auto_change = False
self._sth_changed(False)
# Create the menu tree
self.createTree(None, None)
# Let's roll!
gtk.main()
if __name__ == "__main__":
app = App()
app.init()
syntax highlighted by Code2HTML, v. 0.9.1