# (c) Copyright 2009-2012. CodeWeavers, Inc.
import traceback
import gobject
import gtk
import cxlog
import cxmenu
import cxguitools
import pyop
# for localization
from cxutils import cxgettext as _
_COLUMN_PATH = 0
_COLUMN_SHOW = 1
_COLUMN_DIRNAME = 2
_COLUMN_BASENAME = 3
_COLUMN_ICON_PIXBUF = 4
_COLUMN_SORTPATH = 5
class MenuEditor(gtk.VBox):
__gtype_name__ = 'MenuEditor'
bottlename = None
managed = None
prefs = None
def set_bottle(self, bottlename, managed):
if self.bottlename:
raise ValueError("bottle is already set")
self.bottlename = bottlename
self.managed = managed
if not self.managed:
self.mode_renderer.connect('toggled', self.on_show_toggle)
elif hasattr(self.mode_renderer, 'set_sensitive'):
self.mode_renderer.set_sensitive(False)
self.prefs = cxmenu.MenuPrefs(bottlename, managed)
def __init__(self):
gtk.VBox.__init__(self)
self.progbar = gtk.ProgressBar()
self.pack_start(self.progbar, expand=True, fill=False, padding=0)
self.liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN,
gobject.TYPE_STRING, gobject.TYPE_STRING, gtk.gdk.Pixbuf,
gobject.TYPE_STRING)
self.treeview = gtk.TreeView(self.liststore)
self.treeview.show()
self.scrolledview = gtk.ScrolledWindow()
self.scrolledview.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.scrolledview.add(self.treeview)
self.liststore.set_sort_column_id(_COLUMN_SORTPATH, gtk.SORT_ASCENDING)
self.mode_renderer = gtk.CellRendererToggle()
mode_column = gtk.TreeViewColumn(_("Show"), self.mode_renderer)
mode_column.add_attribute(self.mode_renderer, 'active', _COLUMN_SHOW)
mode_column.set_sort_column_id(_COLUMN_SHOW)
self.treeview.append_column(mode_column)
icon_renderer = gtk.CellRendererPixbuf()
basename_renderer = gtk.CellRendererText()
basename_column = gtk.TreeViewColumn(_("Menu Name"))
basename_column.set_resizable(True)
basename_column.pack_start(icon_renderer, False)
basename_column.pack_end(basename_renderer, True)
basename_column.add_attribute(basename_renderer, 'text', _COLUMN_BASENAME)
basename_column.add_attribute(icon_renderer, 'pixbuf', _COLUMN_ICON_PIXBUF)
basename_column.set_sort_column_id(_COLUMN_BASENAME)
self.treeview.append_column(basename_column)
dir_renderer = gtk.CellRendererText()
dir_column = gtk.TreeViewColumn(_("Path"), dir_renderer)
dir_column.set_resizable(True)
dir_column.add_attribute(dir_renderer, 'text', _COLUMN_DIRNAME)
dir_column.set_sort_column_id(_COLUMN_SORTPATH)
self.treeview.append_column(dir_column)
self.pack_start(self.scrolledview, expand=True, fill=True, padding=0)
self.pulse_timer = None
self.treeview.connect('row-activated', self.on_row_activate)
def get_selection(self):
_model, treeiter = self.treeview.get_selection().get_selected()
if treeiter is None:
return None
path = self.liststore.get_value(treeiter, _COLUMN_PATH)
return path
def _on_selection_changed(self, _treeselection, user_function):
# pylint: disable=R0201
user_function()
def connect_selection_changed(self, user_function):
self.treeview.get_selection().connect('changed', self._on_selection_changed, user_function)
def on_row_activate(self, _treeview, treepath, _view_column):
treeiter = self.liststore.get_iter(treepath)
path = self.liststore.get_value(treeiter, _COLUMN_PATH)
self.prefs[path].start()
def pulse(self):
self.progbar.pulse()
return True
def pulse_start(self, text):
self.progbar.set_text(text)
self.progbar.show()
self.pulse_timer = gobject.timeout_add(100, self.pulse)
self.scrolledview.set_sensitive(False)
def pulse_stop(self):
if self.pulse_timer:
gobject.source_remove(self.pulse_timer)
self.pulse_timer = None
self.progbar.hide()
self.scrolledview.set_sensitive(True)
def refresh(self, success_notify, failure_notify):
if self.pulse_timer:
return
self.pulse_start(_(u"Reading the menus of %(bottlename)s\u2026") % {'bottlename': self.prefs.bottlename})
operation = RefreshMenusOp(self, success_notify, failure_notify)
pyop.sharedOperationQueue.enqueue(operation)
def recreate_menus(self, success_notify, failure_notify):
if self.pulse_timer:
return
self.pulse_start(_(u"Recreating the menus in %(bottlename)s\u2026") % {'bottlename': self.prefs.bottlename})
operation = RecreateMenusOp(self, success_notify, failure_notify)
pyop.sharedOperationQueue.enqueue(operation)
def commit(self, success_notify, failure_notify):
if self.pulse_timer:
return
self.pulse_start(_(u"Updating the menus in %(bottlename)s\u2026") % {'bottlename': self.prefs.bottlename})
operation = CommitMenusOp(self, success_notify, failure_notify)
pyop.sharedOperationQueue.enqueue(operation)
def update_menulist(self):
"read the list of menus from self.prefs"
self.liststore.clear()
for menu in self.prefs.itervalues():
path = menu.path
if path.startswith('StartMenu'):
topleveldir = _("Start Menu")
elif path.startswith('Desktop'):
topleveldir = _("Desktop")
else:
cxlog.warn("Ignoring unrecognized path %s" % cxlog.to_str(path))
continue
basename = path.split('/', 1)[1]
if '/' in basename:
dirname, basename = basename.rsplit('/', 1)
dirname = '%s/%s' % (topleveldir, dirname)
else:
dirname = topleveldir
if basename.endswith('.lnk') or basename.endswith('.pif') or \
basename.endswith('.url'):
basename = basename[0:-4]
sortname = '%s/0/%s' % (dirname.replace('/', '/1/'), basename)
icon_filename = menu.iconfile
icon = None
if icon_filename:
try:
icon = gtk.gdk.pixbuf_new_from_file(icon_filename)
except gobject.GError:
cxlog.warn("couldn't load icon file %s:\n%s" % (cxlog.debug_str(icon_filename), traceback.format_exc()))
if icon is None:
icon = cxguitools.get_std_icon('crossover')
if icon is not None:
icon = icon.scale_simple(24, 24, gtk.gdk.INTERP_BILINEAR)
self.liststore.append((path, menu.mode == 'install', dirname, basename, icon, sortname))
# Don't pad the progress bar next time we show it; we want the treeview
# to fill that space.
self.set_child_packing(self.progbar, False, False, 0, gtk.PACK_START)
self.scrolledview.show()
def on_show_toggle(self, _cellrenderer, treepath):
treeiter = self.liststore.get_iter(treepath)
path = self.liststore.get_value(treeiter, _COLUMN_PATH)
prev_show = (self.prefs[path].new_mode == 'install')
if prev_show:
self.prefs[path].new_mode = 'ignore'
else:
self.prefs[path].new_mode = 'install'
self.liststore.set_value(treeiter, _COLUMN_SHOW, not prev_show)
class RefreshMenusOp(pyop.PythonOperation):
def __init__(self, editor, success_notify, failure_notify):
pyop.PythonOperation.__init__(self)
self.editor = editor
self.success_notify = success_notify
self.failure_notify = failure_notify
self.error_text = None
def main(self):
self.error_text = None
try:
self.editor.prefs.refresh()
except Exception: # pylint: disable=W0703
self.error_text = traceback.format_exc()
def finish(self):
self.editor.pulse_stop()
self.editor.update_menulist()
if self.error_text is None:
self.success_notify()
else:
self.failure_notify(self.error_text)
class RecreateMenusOp(pyop.PythonOperation):
def __init__(self, editor, success_notify, failure_notify):
pyop.PythonOperation.__init__(self)
self.editor = editor
self.success_notify = success_notify
self.failure_notify = failure_notify
self.error_text = None
def main(self):
self.error_text = None
try:
self.editor.prefs.recreate_menus()
self.editor.prefs.refresh()
except Exception: # pylint: disable=W0703
self.error_text = traceback.format_exc()
def finish(self):
self.editor.pulse_stop()
self.editor.update_menulist()
if self.error_text is None:
self.success_notify()
else:
self.failure_notify(self.error_text)
class CommitMenusOp(pyop.PythonOperation):
def __init__(self, editor, success_notify, failure_notify):
pyop.PythonOperation.__init__(self)
self.editor = editor
self.success_notify = success_notify
self.failure_notify = failure_notify
self.error_text = None
def main(self):
self.error_text = None
try:
self.editor.prefs.commit()
except Exception: # pylint: disable=W0703
self.error_text = traceback.format_exc()
def finish(self):
self.editor.pulse_stop()
if self.error_text is None:
self.success_notify()
else:
self.failure_notify(self.error_text)