#!/usr/local/bin/python3.12
#
#  Openbox Menu Editor 1.1.1
#
#  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 gi, obxml, random, time, os, sys

# Try setting OBMENU_GTK_VERSION if PyGObject loads the wrong GTK version.
if os.getenv('OBMENU_GTK_VERSION'):
	gtk_version = os.getenv('OBMENU_GTK_VERSION')
# These versions are incompatible with GTK 4, so force GTK 3.
elif gi.version_info < (3, 40, 0):
	gtk_version = "3.0"
else:
	# Don't specify the version; PyGObject should use the highest possible
	# version, though it will give a warning.
	gtk_version = ""
if gtk_version:
	gi.require_version('Gtk', gtk_version)
from gi.repository import Gtk, Gio, GLib
gtk_version = None

try:
	import locale
	from locale import textdomain, bindtextdomain, dgettext, gettext as _
	locale.setlocale(locale.LC_ALL, "")
except:
	import gettext
	from gettext import textdomain, bindtextdomain, dgettext, gettext as _

class App(Gtk.Application):
	# 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(str, str, str, str, str, str)
		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 = ""
		if self.menu_path:
			self.appwindow.set_title("Obmenu: %s %s" % (self.menu_path, s))
		else:
			self.appwindow.set_title("Obmenu")

	# Auxiliary function for model.foreach()
	def _change_id(self, model, path, it, id_pair):
		(old_id, new_id) = id_pair
		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_combo.set_sensitive(False)
		self.auto_change = False

	def clear_view(self):
		self.deleteTree()
		self.menu.newMenu()
		self.unsaved_menu = True
		self.menu_path = _("Untitled")
		self._sth_changed(True)
		self.clear_fields()

	def on_confirm(self, dialog, response):
		dialog.destroy()
		if response == Gtk.ResponseType.YES:
			if self.confirm_action == "exit":
				app.quit()
			elif self.confirm_action == "new":
				self.clear_view()
			elif self.confirm_action == "open":
				self.ask_for_path(_("Open"), Gtk.FileChooserAction.OPEN)
				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 initializing
				self.createTree(None, None)
				self.clear_fields()

	def confirm(self, message, action):
		dlg = Gtk.MessageDialog(message_type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.YES_NO, text=message)
		self.confirm_action = action
		dlg.set_transient_for(self.appwindow);
		dlg.connect("response", self.on_confirm)
		dlg.show()

	def on_file_chosen(self, dialog, response):
		if response == Gtk.ResponseType.OK:
			self.menu_path = dialog.get_file().get_path()

			if dialog.get_action() == Gtk.FileChooserAction.OPEN:
				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 initializing
				self.createTree(None, None)
				self.clear_fields()
			else:
				self.unsaved_menu = False
				self.menu.saveMenu(self.menu_path)
				self._sth_changed(False)

		dialog.destroy()

	def on_search_clicked(self, dialog, response):
		if response == Gtk.ResponseType.OK:
			self.execute_entry.set_text(dialog.get_file().get_path())
		dialog.destroy()

	def ask_for_path(self, title, action, cb=None):
		dlg = Gtk.FileChooserDialog(title=title, action=action)
		dlg.add_button(dgettext(self.gtk_domain, "_OK"), Gtk.ResponseType.OK)
		dlg.add_button(dgettext(self.gtk_domain, "_Cancel"), Gtk.ResponseType.CANCEL)
		dlg.set_default_response(Gtk.ResponseType.OK)
		dlg.set_transient_for(self.appwindow)
		dlg.connect("response", cb or self.on_file_chosen)
		dlg.show()

	#  New file clicked
	def new(self, params, data):
		if self.sth_changed:
			self.confirm(_("Changes in %s will be lost. Continue?") % (self.menu_path), "new")
		else:
			self.clear_view()

	def open(self, params, data):
		if self.sth_changed:
			self.confirm(_("Changes in %s will be lost. Continue?") % (self.menu_path), "open")
		else:
			self.ask_for_path(_("Open"), Gtk.FileChooserAction.OPEN)

	# save the menu
	# It takes three parameters from the menu, but only two from the
	# toolbar, so make the third argument default to None
	def save(self, params, data=None):
		if self.unsaved_menu:
			self.ask_for_path(_("Save as..."), Gtk.FileChooserAction.SAVE)
		else:
			self.menu.saveMenu(self.menu_path)
		self._sth_changed(False)

	# save as
	def save_as(self, params, data):
		self.ask_for_path(_("Save as..."), Gtk.FileChooserAction.SAVE)

	def reconfigure_openbox(self, param, data):
		import signal
		lines = os.popen("pgrep -x openbox").read().splitlines()
		if lines:
			os.kill(int(lines[0]), signal.SIGUSR2)

	# quit signal
	# event is required by GTK 3's delete-event but not by GTK 4's
	# close-request
	def exit (self, bt, event=None):
		if self.settings:
			self.settings.set_value("last-opened-menu", GLib.Variant.new_string(self.menu_path))
		if self.sth_changed:
			self.confirm(_("There are unsaved changes. Exit anyway?"), "exit")
		else:
			self.quit()

	# File->Quit menu signal
	def mnu_quit (self, params, data):
		self.exit(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_combo.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)

	# button [...] clicked signal
	def search_clicked(self, arg):
		dlg = self.ask_for_path(_("Select File"), Gtk.FileChooserAction.OPEN, self.on_search_clicked)

	# 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_combo.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_combo.set_active(0)
				self.execute_entry.set_sensitive(True)
				self.execute_srch.set_sensitive(True)
			elif action.lower() == "reconfigure":
				self.action_combo.set_active(1)
			elif action.lower() == "restart":
				self.action_combo.set_active(2)
			elif action.lower() == "exit":
				self.action_combo.set_active(3)
			else:
				self.action_combo.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, data=None):
		(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
			# If there's no root menu available, allow the creation of a new one
			try:
				parent = model.get_iter_root()
			except:
				parent = None
			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, data=None):
		(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, data=None):
		(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, data):
		(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, data):
		(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, _("New Pipe"), _("command"), n)
		itm = self.treemodel.insert_before(parent, ite, (_("New Pipe"), '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, data=None):
		(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, data=None):
		(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, data=None):
		(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, data):
		if self.treeview.is_focus():
			self.remove(None)

	# event is required by GTK 3's delete-event but not by GTK 4's
	# close-request
	def hide_about(self, widget, event=None):
		widget.hide()
		return True

	def show_about(self, param, data):
		self.builder.get_object("aboutdialog").show()

	def do_startup(self):
		Gtk.Application.do_startup(self)

		# Look for the data files!
		if os.path.isfile("obmenu.ui"):
			# They're here.. looks like a pre-installation execution
			self.datapath = os.getcwd()
		elif os.path.isdir(os.path.abspath(os.path.dirname(__file__) + "/../share/obmenu")):
			self.datapath = os.path.abspath(os.path.dirname(__file__) + "/../share/obmenu")
		elif os.path.isdir("%s/share/obmenu" % sys.prefix):
			self.datatpath = "%s/share/obmenu" % sys.prefix
		elif os.path.isdir("/usr/local/share/obmenu"):
			self.datapath = "/usr/local/share/obmenu"
		elif os.path.isdir("/usr/share/obmenu"):
			self.datapath = "/usr/share/obmenu"
		elif os.getenv("XDG_DATA_HOME") and os.path.isdir("%s/%s" % (os.getenv("XDG_DATA_HOME"), "obmenu")):
			self.datapath = "%s/%s" % (os.getenv("XDG_DATA_HOME"), "obmenu")
		elif os.path.isdir(os.path.expanduser("~/.local/share/obmenu")):
			self.datapath = os.path.expanduser("~/.local/share/obmenu")
		else:
			# These will be untranslated, as things weren't installed correctly
			print("ERROR: the data files were not found!")
			print("       check that everything was installed all right")
			sys.exit(1)

		if os.getenv("TEXTDOMAINDIR"):
			textdomaindir = os.getenv("TEXTDOMAINDIR")
		else:
			textdomaindir = os.path.abspath("../share/locale")
		textdomain("obmenu")
		bindtextdomain("obmenu", textdomaindir)
		self.gtk_domain = "gtk%d0" % Gtk.get_major_version()

		signals = {
			"new": self.new,
			"open": self.open,
			"save": self.save,
			"save.as": self.save_as,
			"reconfigure": self.reconfigure_openbox,
			"quit": self.mnu_quit,
			"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,
			"treeview_changed": self.treeview_changed,
			"add.menu": self.new_menu,
			"add.item": self.new_item,
			"add.separator": self.new_separator,
			"add.pipe": self.new_pipe,
			"add.link": self.new_link,
			"remove": self.remove,
			"up": self.up,
			"down": self.down,
			"about": self.show_about,
			"hide_about": self.hide_about,
			"exit": self.exit }
		for signal in signals:
			action = Gio.SimpleAction.new(signal, None)
			action.connect("activate", signals[signal])
			self.add_action(action)

		if Gtk.get_major_version() >= 4:
			self.builder = Gtk.Builder(signals)
			self.builder.add_from_file(self.datapath + "/obmenu.ui")
		else:
			self.builder = Gtk.Builder()
			self.builder.add_from_file(self.datapath + "/obmenu.glade")
			self.set_menubar(self.builder.get_object("menubar"))
			self.builder.connect_signals(signals)

		# PyGObject seems to lack an equivalent of the C GTK_CHECK_VERSION macro.
		# Gtk.check_version() checks for ABI compatibility with the requested version.
		if Gtk.get_major_version() > 3 or Gtk.get_minor_version() >= 12:
			accels = {
				"app.new": ["<Control>n"],
				"app.open": ["<Control>o"],
				"app.save": ["<Control>s"],
				"app.save.as": ["<Control><Shift>s"],
				"app.reconfigure": ["<Control>c"],
				"app.quit": ["<Control>q"],
				"app.up": ["<Control>Up"],
				"app.down": ["<Control>Down"],
				"app.add.menu": ["<Control>m"],
				"app.add.item": ["<Control>i"],
				"app.add.separator": ["<Control>r"],
				"app.add.pipe": ["<Control>p"],
				"app.add.link": ["<Control>l"],
				"app.remove": ["Delete"],
				"app.about": ["F1"]
			}
			for accel in accels:
				app.set_accels_for_action(accel, accels[accel])

	# Application activation
	def do_activate(self):
		# Set the basics for GTK
		self.appwindow = self.builder.get_object("appwindow")
		self.treeview = self.builder.get_object("treeview")
		self.label_entry = self.builder.get_object("label_entry")
		self.action_combo = self.builder.get_object("action_combo")
		self.execute_entry = self.builder.get_object("execute_entry")
		self.execute_srch = self.builder.get_object("execute_search_button")
		self.id_entry = self.builder.get_object("id_entry")

		self.menu = obxml.Obmenu()
		self.menu_path = ""
		self.settings = None
		try:
			if self.menu_file.query_exists():
				self.menu_path = self.menu_file.get_path()
		except:
			# No files specified on the command line
			#
			# If the GSettings schema isn't installed, don't go kaboom
			if Gio.SettingsSchemaSource.lookup(Gio.SettingsSchemaSource.get_default(), self.appid, False):
				self.settings = Gio.Settings.new(self.appid)
				self.menu_path = GLib.Variant.get_string(self.settings.get_value("last-opened-menu"))

		if self.menu_path:
			# Load in memory the real xml menu
			self.menu.loadMenu(self.menu_path)
			self.unsaved_menu = False
		else:
			self.unsaved_menu = True

		# treemodel.iter(label, type, [action], [execute], parent, [menu-id])
		self.treemodel=Gtk.TreeStore(str, str, str, str, str, str)
		self.treeview.set_model(self.treemodel)

		# Set the columns
		self.treeview.set_headers_visible(True)

		renderer=Gtk.CellRendererText()
		column=Gtk.TreeViewColumn(dgettext(self.gtk_domain, "Label"),renderer, text=0)
		column.set_resizable(True)
		self.treeview.append_column(column)

		renderer=Gtk.CellRendererText()
		column=Gtk.TreeViewColumn(dgettext(self.gtk_domain, "Type"),renderer,text=1)
		column.set_resizable(True)
		self.treeview.append_column(column)

		renderer=Gtk.CellRendererText()
		column=Gtk.TreeViewColumn(dgettext(self.gtk_domain, "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!
		self.add_window(self.appwindow)
		self.appwindow.show()

	# Callback called when command-line arguments are provided
	def do_open(self, files, n_files, hint):
		if n_files > 1:
			print(_("Error: Obmenu can presently open only one file at a time."))
			print(_("       Only the first specified will be opened."))
		self.menu_file = files[0]
		self.do_activate()

	# Application init
	def __init__(self):
		self.appid = "io.sourceforge.obmenu"
		Gtk.Application.__init__(self, application_id=self.appid,
				flags=Gio.ApplicationFlags.HANDLES_OPEN | Gio.ApplicationFlags.NON_UNIQUE)
		if Gtk.get_major_version() < 4:
			GLib.set_prgname(self.appid)

if __name__ == "__main__":
	app = App()
	app.run(sys.argv)
