def init(self): with self._lock: if self._disposed: raise InvalidOperationException("Daemon has been disposed") if self._initialized: raise InvalidOperationException( "Daemon has already been initialized") self._cfg = read_cfg() accountman = AccountManager() accountman.load_from_cfg(self._cfg, enabled_only=True) self._accounts = accountman.to_list() self._hookreg = HookRegistry() self._conntest = ConnectivityTest(testmode_mapping[self._cfg.get( 'core', 'connectivity_test')]) memorizer = Memorizer() memorizer.load() self._mailchecker = MailChecker(self._cfg, memorizer, self._hookreg, self._conntest) # Note: all code following _load_plugins() should be executed # asynchronously because the dbus plugin requires an active mainloop # (usually started in the programs main function). self._load_plugins(self._cfg, self._hookreg, memorizer) # Start checking for mails asynchronously. self._start_thread = threading.Thread(target=self._start) self._start_thread.start() self._initialized = True
def init(self): with self._lock: if self._disposed: raise InvalidOperationException("Daemon has been disposed") if self._initialized: raise InvalidOperationException("Daemon has already been initialized") self._cfg = read_cfg() accountman = AccountManager(CredentialStore.from_string(self._cfg.get('core', 'credentialstore'))) accountman.load_from_cfg(self._cfg, enabled_only = True) self._accounts = accountman.to_list() self._hookreg = HookRegistry() self._conntest = ConnectivityTest(testmode_mapping[self._cfg.get('core', 'connectivity_test')]) memorizer = Memorizer() memorizer.load() self._mailchecker = MailChecker(self._cfg, memorizer, self._hookreg, self._conntest) # Note: all code following _load_plugins() should be executed # asynchronously because the dbus plugin requires an active mainloop # (usually started in the programs main function). self._load_plugins(self._cfg, self._hookreg, memorizer) # Start checking for mails asynchronously. self._start_thread = threading.Thread(target = self._start) self._start_thread.start() self._initialized = True
def test_load_from_config(config): am = AccountManager() am.load_from_cfg(config, enabled_only=False) accounts = am.to_list() assert len(accounts) == 6 imap_account = get_account(am.to_list(), 'IMAP mailbox config') pop3_account = get_account(am.to_list(), 'POP3 mailbox config') assert imap_account.get_config()['password'] == 'drowssap' assert pop3_account.get_config()['password'] == 'poppoppop'
def test_save_removed_accounts_to_config_with_credential_store(config): cs = FakeCredentialStore() cs.set('Mailnag password for imap://[email protected]', 'verry seecret') cs.set('Mailnag password for pop://[email protected]', 'seecret too') am = AccountManager(cs) am.load_from_cfg(config, enabled_only=False) am.clear() am.save_to_cfg(config) assert len(config.sections()) == 0 assert cs.secrets == {}
def test_load_from_config_with_credential_store(config): cs = FakeCredentialStore() cs.set('Mailnag password for imap://[email protected]', 'verry seecret') cs.set('Mailnag password for pop://[email protected]', 'seecret too') am = AccountManager(cs) am.load_from_cfg(config, enabled_only=False) accounts = am.to_list() assert len(accounts) == 6 imap_account = get_account(am.to_list(), 'IMAP mailbox config') pop3_account = get_account(am.to_list(), 'POP3 mailbox config') assert imap_account.get_config()['password'] == 'verry seecret' assert pop3_account.get_config()['password'] == 'seecret too'
def __init__(self, fatal_error_handler=None, shutdown_request_handler=None): self._fatal_error_handler = fatal_error_handler self._shutdown_request_handler = shutdown_request_handler self._plugins = [] self._poll_thread = None self._poll_thread_stop = threading.Event() self._idlrunner = None self._disposed = False self._cfg = read_cfg() accountman = AccountManager() accountman.load_from_cfg(self._cfg, enabled_only=True) self._accounts = accountman.to_list() self._hookreg = HookRegistry() self._conntest = ConnectivityTest(testmode_mapping[self._cfg.get( 'core', 'connectivity_test')]) self._memorizer = Memorizer() self._memorizer.load() dbus_service = DBusService(self) self._mailchecker = MailChecker(self._cfg, self._memorizer, self._hookreg, self._conntest, dbus_service) # Note: all code following _load_plugins() should be executed # asynchronously because the dbus plugin requires an active mainloop # (usually started in the programs main function). self._load_plugins(self._cfg) # Start checking for mails asynchronously. self._start_thread = threading.Thread(target=self._start) self._start_thread.start()
class ConfigWindow: def __init__(self, app): builder = Gtk.Builder() builder.set_translation_domain(PACKAGE_NAME) builder.add_from_file(get_data_file("config_window.ui")) builder.connect_signals({ \ "config_window_deleted" : self._on_config_window_deleted, \ "btn_info_clicked" : self._on_btn_info_clicked, \ "btn_add_account_clicked" : self._on_btn_add_account_clicked, \ "btn_edit_account_clicked" : self._on_btn_edit_account_clicked, \ "btn_remove_account_clicked" : self._on_btn_remove_account_clicked, \ "treeview_accounts_row_activated" : self._on_treeview_accounts_row_activated, \ "liststore_accounts_row_deleted" : self._on_liststore_accounts_row_deleted, \ "liststore_accounts_row_inserted" : self._on_liststore_accounts_row_inserted, \ "btn_edit_plugin_clicked" : self._on_btn_edit_plugin_clicked, \ "treeview_plugins_row_activated" : self._on_treeview_plugins_row_activated, \ "treeview_plugins_cursor_changed" : self._on_treeview_plugins_cursor_changed, \ }) self._window = builder.get_object("config_window") self._window.set_icon_name("mailnag") self._window.set_application(app) self._cfg = read_cfg() self._daemon_enabled = False self._switch_daemon_enabled = builder.get_object( "switch_daemon_enabled") # # accounts page # self._accountman = AccountManager( CredentialStore.from_string( self._cfg.get('core', 'credentialstore'))) self._treeview_accounts = builder.get_object("treeview_accounts") self._liststore_accounts = builder.get_object("liststore_accounts") self._button_edit_account = builder.get_object("btn_edit_account") self._button_remove_account = builder.get_object("btn_remove_account") self._button_edit_account.set_sensitive(False) self._button_remove_account.set_sensitive(False) renderer_acc_enabled = Gtk.CellRendererToggle() renderer_acc_enabled.connect("toggled", self._on_account_toggled) column_acc_enabled = Gtk.TreeViewColumn(_('Enabled'), renderer_acc_enabled) column_acc_enabled.add_attribute(renderer_acc_enabled, "active", 1) column_acc_enabled.set_alignment(0.5) self._treeview_accounts.append_column(column_acc_enabled) renderer_acc_name = Gtk.CellRendererText() column_acc_name = Gtk.TreeViewColumn(_('Name'), renderer_acc_name, text=2) self._treeview_accounts.append_column(column_acc_name) # # plugins page # self._treeview_plugins = builder.get_object("treeview_plugins") self._liststore_plugins = builder.get_object("liststore_plugins") self._button_edit_plugin = builder.get_object("btn_edit_plugin") self._button_edit_plugin.set_sensitive(False) def renderer_plugin_enabled_func(column, cell_renderer, model, iter, data): plugin = model.get_value(iter, 0) name, desc, ver, author, mandatory = plugin.get_manifest() cell_renderer.set_sensitive(not mandatory) renderer_plugin_enabled = Gtk.CellRendererToggle() renderer_plugin_enabled.connect("toggled", self._on_plugin_toggled) column_plugin_enabled = Gtk.TreeViewColumn(_('Enabled'), renderer_plugin_enabled) column_plugin_enabled.add_attribute(renderer_plugin_enabled, "active", 1) column_plugin_enabled.set_alignment(0.5) column_plugin_enabled.set_cell_data_func(renderer_plugin_enabled, renderer_plugin_enabled_func) self._treeview_plugins.append_column(column_plugin_enabled) renderer_plugin_name = Gtk.CellRendererText() column_plugin_name = Gtk.TreeViewColumn(_('Name'), renderer_plugin_name, markup=2) self._treeview_plugins.append_column(column_plugin_name) # load config self._load_config() self._window.show_all() def get_gtk_window(self): return self._window def get_daemon_enabled(self): return self._daemon_enabled def _load_config(self): self._switch_daemon_enabled.set_active( bool(int(self._cfg.get('core', 'autostart')))) self._accountman.load_from_cfg(self._cfg) # load accounts for acc in self._accountman: row = [acc, acc.enabled, acc.name] self._liststore_accounts.append(row) self._select_account_path((0, )) # load plugins enabled_lst = self._cfg.get('core', 'enabled_plugins').split(',') enabled_lst = filter(lambda s: s != '', map(lambda s: s.strip(), enabled_lst)) plugins = Plugin.load_plugins(self._cfg) plugins.sort( key=lambda p: (not p.get_manifest()[4], p.get_manifest()[0])) for plugin in plugins: name, desc, ver, author, mandatory = plugin.get_manifest() enabled = True if (plugin.get_modname() in enabled_lst) or mandatory else False description = '<b>%s</b> (%s)\n%s' % (name, ver, desc) row = [plugin, enabled, description] self._liststore_plugins.append(row) self._select_plugin_path((0, )) def _save_config(self): autostart = self._switch_daemon_enabled.get_active() self._cfg.set('core', 'autostart', int(autostart)) self._accountman.save_to_cfg(self._cfg) enabled_plugins = '' for row in self._liststore_plugins: plugin = row[0] modname = plugin.get_modname() if row[1] == True: if len(enabled_plugins) > 0: enabled_plugins += ', ' enabled_plugins += modname config = plugin.get_config() if len(config) > 0: if not self._cfg.has_section(modname): self._cfg.add_section(modname) for k, v in config.iteritems(): self._cfg.set(modname, k, v) self._cfg.set('core', 'enabled_plugins', enabled_plugins) write_cfg(self._cfg) if autostart: self._create_autostart() else: self._delete_autostart() def _show_confirmation_dialog(self, text): message = Gtk.MessageDialog(self._window, Gtk.DialogFlags.MODAL, \ Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, text) resp = message.run() message.destroy() if resp == Gtk.ResponseType.YES: return True else: return False def _get_selected_account(self): treeselection = self._treeview_accounts.get_selection() selection = treeselection.get_selected() model, iter = selection # get account object from treeviews 1st column if iter != None: acc = model.get_value(iter, 0) else: acc = None return acc, model, iter def _select_account_path(self, path): treeselection = self._treeview_accounts.get_selection() treeselection.select_path(path) self._treeview_accounts.grab_focus() def _edit_account(self): acc, model, iter = self._get_selected_account() if iter != None: d = AccountDialog(self._window, acc) if d.run() == Gtk.ResponseType.OK: model.set_value(iter, 2, acc.name) def _get_selected_plugin(self): treeselection = self._treeview_plugins.get_selection() selection = treeselection.get_selected() model, iter = selection # get plugin object from treeviews 1st column if iter != None: plugin = model.get_value(iter, 0) else: plugin = None return plugin, model, iter def _select_plugin_path(self, path): treeselection = self._treeview_plugins.get_selection() treeselection.select_path(path) self._treeview_plugins.grab_focus() def _edit_plugin(self): plugin, model, iter = self._get_selected_plugin() if (iter != None) and plugin.has_config_ui(): d = PluginDialog(self._window, plugin) d.run() def _create_autostart(self): autostart_folder = os.path.join(bd.xdg_config_home, "autostart") src = os.path.join(DESKTOP_FILE_DIR, "mailnag.desktop") dst = os.path.join(autostart_folder, "mailnag.desktop") if not os.path.exists(autostart_folder): os.makedirs(autostart_folder) shutil.copyfile(src, dst) # If mailag-config was started from a local directory, # patch the exec path of the autostart .desktop file accordingly. if not os.path.isabs(DESKTOP_FILE_DIR): exec_file = os.path.join(os.path.abspath(BIN_DIR), "mailnag") with open(dst, 'r') as f: strn = f.read() strn = strn.replace('/usr/bin/mailnag', exec_file) with open(dst, 'w') as f: f.write(strn) def _delete_autostart(self): autostart_folder = os.path.join(bd.xdg_config_home, "autostart") autostart_file = os.path.join(autostart_folder, "mailnag.desktop") if os.path.exists(autostart_file): os.remove(autostart_file) def _on_btn_info_clicked(self, widget): aboutdialog = Gtk.AboutDialog() aboutdialog.set_title(_("About %s") % PACKAGE_NAME.title()) aboutdialog.set_version(APP_VERSION) aboutdialog.set_program_name(PACKAGE_NAME.title()) aboutdialog.set_comments(_("An extensible mail notification daemon.")) aboutdialog.set_copyright( _("Copyright (c) 2011 - 2019 Patrick Ulbrich and contributors.")) aboutdialog.set_logo_icon_name("mailnag") aboutdialog.set_website("https://github.com/pulb/mailnag") aboutdialog.set_website_label(_("Homepage")) aboutdialog.set_license_type(Gtk.License.GPL_2_0) aboutdialog.set_authors([ "Patrick Ulbrich (maintainer)", "Edwin Smulders", "Freeroot", "Leighton Earl", "Matthias Mailänder", "Oleg", "Ralf Hersel", "Taylor Braun-Jones", "Thomas Haider", "Timo Kankare", "Vincent Cheng" ]) aboutdialog.set_translator_credits(_("translator-credits")) aboutdialog.set_artists(["Reda Lazri"]) aboutdialog.connect("response", lambda w, r: aboutdialog.destroy()) aboutdialog.set_modal(True) aboutdialog.set_transient_for(self._window) aboutdialog.show() def _on_account_toggled(self, cell, path): model = self._liststore_accounts iter = model.get_iter(path) acc = model.get_value(iter, 0) acc.enabled = not acc.enabled self._liststore_accounts.set_value(iter, 1, not cell.get_active()) def _on_btn_add_account_clicked(self, widget): acc = Account(enabled=True) d = AccountDialog(self._window, acc) if d.run() == Gtk.ResponseType.OK: self._accountman.add(acc) row = [acc, acc.enabled, acc.name] iter = self._liststore_accounts.append(row) model = self._treeview_accounts.get_model() path = model.get_path(iter) self._treeview_accounts.set_cursor(path, None, False) self._treeview_accounts.grab_focus() def _on_btn_edit_account_clicked(self, widget): self._edit_account() def _on_btn_remove_account_clicked(self, widget): acc, model, iter = self._get_selected_account() if iter != None: if self._show_confirmation_dialog(_('Delete this account:') + \ '\n\n' + acc.name): # select prev/next account p = model.get_path(iter) if not p.prev(): p.next() self._select_account_path(p) # remove from treeview model.remove(iter) # remove from account manager self._accountman.remove(acc) def _on_treeview_accounts_row_activated(self, treeview, path, view_column): self._edit_account() def _on_liststore_accounts_row_deleted(self, model, path): self._button_edit_account.set_sensitive(len(model) > 0) self._button_remove_account.set_sensitive(len(model) > 0) def _on_liststore_accounts_row_inserted(self, model, path, user_param): self._button_edit_account.set_sensitive(len(model) > 0) self._button_remove_account.set_sensitive(len(model) > 0) def _on_plugin_toggled(self, cell, path): model = self._liststore_plugins iter = model.get_iter(path) self._liststore_plugins.set_value(iter, 1, not cell.get_active()) def _on_btn_edit_plugin_clicked(self, widget): self._edit_plugin() def _on_treeview_plugins_row_activated(self, treeview, path, view_column): self._edit_plugin() def _on_treeview_plugins_cursor_changed(self, treeview): # Workaround for a bug in GTK < 3.8, # see http://permalink.gmane.org/gmane.comp.gnome.svn/694089 if not self._window.get_visible(): return plugin, model, iter = self._get_selected_plugin() if iter != None: self._button_edit_plugin.set_sensitive(plugin.has_config_ui()) def _on_config_window_deleted(self, widget, event): self._save_config() self._daemon_enabled = self._switch_daemon_enabled.get_active()
class ConfigWindow: def __init__(self): builder = Gtk.Builder() builder.set_translation_domain(PACKAGE_NAME) builder.add_from_file(get_data_file("config_window.ui")) builder.connect_signals({ \ "config_window_deleted" : self._on_config_window_deleted, \ "btn_page_toggled" : self._on_btn_page_toggled, \ "btn_add_account_clicked" : self._on_btn_add_account_clicked, \ "btn_edit_account_clicked" : self._on_btn_edit_account_clicked, \ "btn_remove_account_clicked" : self._on_btn_remove_account_clicked, \ "treeview_accounts_row_activated" : self._on_treeview_accounts_row_activated, \ "liststore_accounts_row_deleted" : self._on_liststore_accounts_row_deleted, \ "liststore_accounts_row_inserted" : self._on_liststore_accounts_row_inserted, \ "btn_edit_plugin_clicked" : self._on_btn_edit_plugin_clicked, \ "treeview_plugins_row_activated" : self._on_treeview_plugins_row_activated, \ "treeview_plugins_cursor_changed" : self._on_treeview_plugins_cursor_changed, \ }) # Add icons in alternative data paths (e.g. ./data/icons) # to the icon search path in case Mailnag is launched # from a local directory (without installing). icon_theme = Gtk.IconTheme.get_default() for path in get_data_paths(): icon_theme.append_search_path(os.path.join(path, "icons")) self._window = builder.get_object("config_window") self._window.set_icon_name("mailnag") self._load_stylesheet('config_window.css') self._cfg = read_cfg() self.daemon_enabled = False # # toggle buttons / notebook # self._notebook = builder.get_object("notebook") self._box_navigation = builder.get_object("box_navigation") self._box_navigation.get_children()[0].set_active(True) # # general page # # The dimension of the png is expected to be 180x180 px pb = GdkPixbuf.Pixbuf.new_from_file(get_data_file("mailnag.png")) pb = pb.new_subpixbuf(0, 10, 180, 146) # crop whitespace at the bottom self._image_logo = builder.get_object("image_logo") self._image_logo.set_from_pixbuf(pb) self._label_app_desc = builder.get_object("label_app_desc") self._label_app_desc.set_markup("<span font=\"24\"><b>Mailnag</b></span>\nVersion %s" % str(APP_VERSION)) self._switch_daemon_enabled = builder.get_object("switch_daemon_enabled") # # accounts page # self._accountman = AccountManager(CredentialStore.from_string(self._cfg.get('core', 'credentialstore'))) self._treeview_accounts = builder.get_object("treeview_accounts") self._liststore_accounts = builder.get_object("liststore_accounts") self._button_edit_account = builder.get_object("btn_edit_account") self._button_remove_account = builder.get_object("btn_remove_account") self._button_edit_account.set_sensitive(False) self._button_remove_account.set_sensitive(False) renderer_acc_enabled = Gtk.CellRendererToggle() renderer_acc_enabled.connect("toggled", self._on_account_toggled) column_acc_enabled = Gtk.TreeViewColumn(_('Enabled'), renderer_acc_enabled) column_acc_enabled.add_attribute(renderer_acc_enabled, "active", 1) column_acc_enabled.set_alignment(0.5) self._treeview_accounts.append_column(column_acc_enabled) renderer_acc_name = Gtk.CellRendererText() column_acc_name = Gtk.TreeViewColumn(_('Name'), renderer_acc_name, text = 2) self._treeview_accounts.append_column(column_acc_name) # # plugins page # self._treeview_plugins = builder.get_object("treeview_plugins") self._liststore_plugins = builder.get_object("liststore_plugins") self._button_edit_plugin = builder.get_object("btn_edit_plugin") self._button_edit_plugin.set_sensitive(False) def renderer_plugin_enabled_func(column, cell_renderer, model, iter, data): plugin = model.get_value(iter, 0) name, desc, ver, author, mandatory = plugin.get_manifest() cell_renderer.set_sensitive(not mandatory) renderer_plugin_enabled = Gtk.CellRendererToggle() renderer_plugin_enabled.connect("toggled", self._on_plugin_toggled) column_plugin_enabled = Gtk.TreeViewColumn(_('Enabled'), renderer_plugin_enabled) column_plugin_enabled.add_attribute(renderer_plugin_enabled, "active", 1) column_plugin_enabled.set_alignment(0.5) column_plugin_enabled.set_cell_data_func(renderer_plugin_enabled, renderer_plugin_enabled_func) self._treeview_plugins.append_column(column_plugin_enabled) renderer_plugin_name = Gtk.CellRendererText() column_plugin_name = Gtk.TreeViewColumn(_('Name'), renderer_plugin_name, markup = 2) self._treeview_plugins.append_column(column_plugin_name) # load config self._load_config() self._window.show() def _load_stylesheet(self, stylesheet): provider = Gtk.CssProvider() provider.load_from_path(get_data_file(stylesheet)) Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) def _load_config(self): self._switch_daemon_enabled.set_active(bool(int(self._cfg.get('core', 'autostart')))) self._accountman.load_from_cfg(self._cfg) # load accounts for acc in self._accountman: row = [acc, acc.enabled, acc.name] self._liststore_accounts.append(row) self._select_account_path((0,)) # load plugins enabled_lst = self._cfg.get('core', 'enabled_plugins').split(',') enabled_lst = filter(lambda s: s != '', map(lambda s: s.strip(), enabled_lst)) plugins = Plugin.load_plugins(self._cfg) plugins.sort(key = lambda p : (not p.get_manifest()[4], p.get_manifest()[0])) for plugin in plugins: name, desc, ver, author, mandatory = plugin.get_manifest() enabled = True if (plugin.get_modname() in enabled_lst) or mandatory else False description = '<b>%s</b> (%s)\n%s' % (name, ver, desc) row = [plugin, enabled, description] self._liststore_plugins.append(row) self._select_plugin_path((0,)) def _save_config(self): autostart = self._switch_daemon_enabled.get_active() self._cfg.set('core', 'autostart', int(autostart)) self._accountman.save_to_cfg(self._cfg) enabled_plugins = '' for row in self._liststore_plugins: plugin = row[0] modname = plugin.get_modname() if row[1] == True: if len(enabled_plugins) > 0: enabled_plugins += ', ' enabled_plugins += modname config = plugin.get_config() if len(config) > 0: if not self._cfg.has_section(modname): self._cfg.add_section(modname) for k, v in config.iteritems(): self._cfg.set(modname, k, v) self._cfg.set('core', 'enabled_plugins', enabled_plugins) write_cfg(self._cfg) if autostart: self._create_autostart() else: self._delete_autostart() def _show_confirmation_dialog(self, text): message = Gtk.MessageDialog(self._window, Gtk.DialogFlags.MODAL, \ Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, text) resp = message.run() message.destroy() if resp == Gtk.ResponseType.YES: return True else: return False def _get_selected_account(self): treeselection = self._treeview_accounts.get_selection() selection = treeselection.get_selected() model, iter = selection # get account object from treeviews 1st column if iter != None: acc = model.get_value(iter, 0) else: acc = None return acc, model, iter def _select_account_path(self, path): treeselection = self._treeview_accounts.get_selection() treeselection.select_path(path) self._treeview_accounts.grab_focus() def _edit_account(self): acc, model, iter = self._get_selected_account() if iter != None: d = AccountDialog(self._window, acc) if d.run() == 1: model.set_value(iter, 2, acc.name) def _get_selected_plugin(self): treeselection = self._treeview_plugins.get_selection() selection = treeselection.get_selected() model, iter = selection # get plugin object from treeviews 1st column if iter != None: plugin = model.get_value(iter, 0) else: plugin = None return plugin, model, iter def _select_plugin_path(self, path): treeselection = self._treeview_plugins.get_selection() treeselection.select_path(path) self._treeview_plugins.grab_focus() def _edit_plugin(self): plugin, model, iter = self._get_selected_plugin() if (iter != None) and plugin.has_config_ui(): d = PluginDialog(self._window, plugin) d.run() def _create_autostart(self): autostart_folder = os.path.join(bd.xdg_config_home, "autostart") src = os.path.join(DESKTOP_FILE_DIR, "mailnag.desktop") dst = os.path.join(autostart_folder, "mailnag.desktop") if not os.path.exists(autostart_folder): os.makedirs(autostart_folder) shutil.copyfile(src, dst) # If mailag-config was started from a local directory, # patch the exec path of the autostart .desktop file accordingly. if not os.path.isabs(DESKTOP_FILE_DIR): exec_file = os.path.join(os.path.abspath(BIN_DIR), "mailnag") with open(dst, 'r') as f: strn = f.read() strn = strn.replace('/usr/bin/mailnag', exec_file) with open(dst, 'w') as f: f.write(strn) def _delete_autostart(self): autostart_folder = os.path.join(bd.xdg_config_home, "autostart") autostart_file = autostart_folder + "mailnag.desktop" if os.path.exists(autostart_file): os.remove(autostart_file) def _on_btn_page_toggled(self, button): if not button.get_active(): return page = 0 for btn in self._box_navigation.get_children(): if btn == button: self._notebook.set_current_page(page) else: btn.set_active(False) page += 1 def _on_account_toggled(self, cell, path): model = self._liststore_accounts iter = model.get_iter(path) acc = model.get_value(iter, 0) acc.enabled = not acc.enabled self._liststore_accounts.set_value(iter, 1, not cell.get_active()) def _on_btn_add_account_clicked(self, widget): acc = Account(enabled = True) d = AccountDialog(self._window, acc) if d.run() == 1: self._accountman.add(acc) row = [acc, acc.enabled, acc.name] iter = self._liststore_accounts.append(row) model = self._treeview_accounts.get_model() path = model.get_path(iter) self._treeview_accounts.set_cursor(path, None, False) self._treeview_accounts.grab_focus() def _on_btn_edit_account_clicked(self, widget): self._edit_account() def _on_btn_remove_account_clicked(self, widget): acc, model, iter = self._get_selected_account() if iter != None: if self._show_confirmation_dialog(_('Delete this account:') + \ '\n\n' + acc.name): # select prev/next account p = model.get_path(iter) if not p.prev(): p.next() self._select_account_path(p) # remove from treeview model.remove(iter) # remove from account manager self._accountman.remove(acc) def _on_treeview_accounts_row_activated(self, treeview, path, view_column): self._edit_account() def _on_liststore_accounts_row_deleted(self, model, path): self._button_edit_account.set_sensitive(len(model) > 0) self._button_remove_account.set_sensitive(len(model) > 0) def _on_liststore_accounts_row_inserted(self, model, path, user_param): self._button_edit_account.set_sensitive(len(model) > 0) self._button_remove_account.set_sensitive(len(model) > 0) def _on_plugin_toggled(self, cell, path): model = self._liststore_plugins iter = model.get_iter(path) self._liststore_plugins.set_value(iter, 1, not cell.get_active()) def _on_btn_edit_plugin_clicked(self, widget): self._edit_plugin() def _on_treeview_plugins_row_activated(self, treeview, path, view_column): self._edit_plugin() def _on_treeview_plugins_cursor_changed(self, treeview): # Workaround for a bug in GTK < 3.8, # see http://permalink.gmane.org/gmane.comp.gnome.svn/694089 if not self._window.get_visible(): return plugin, model, iter = self._get_selected_plugin() if iter != None: self._button_edit_plugin.set_sensitive(plugin.has_config_ui()) def _save_and_quit(self): self._save_config() self.daemon_enabled = self._switch_daemon_enabled.get_active() Gtk.main_quit() def _on_config_window_deleted(self, widget, event): self._save_and_quit()
def test_save_all_accounts_to_config(config): am = AccountManager() am.load_from_cfg(config, enabled_only=False) am.save_to_cfg(config) assert len(config.sections()) == 6
class ConfigWindow: def __init__(self): builder = Gtk.Builder() builder.set_translation_domain(PACKAGE_NAME) builder.add_from_file(get_data_file("config_window.ui")) builder.connect_signals({ \ "config_window_deleted" : self._on_config_window_deleted, \ "btn_page_toggled" : self._on_btn_page_toggled, \ "btn_add_account_clicked" : self._on_btn_add_account_clicked, \ "btn_edit_account_clicked" : self._on_btn_edit_account_clicked, \ "btn_remove_account_clicked" : self._on_btn_remove_account_clicked, \ "treeview_accounts_row_activated" : self._on_treeview_accounts_row_activated, \ "liststore_accounts_row_deleted" : self._on_liststore_accounts_row_deleted, \ "liststore_accounts_row_inserted" : self._on_liststore_accounts_row_inserted, \ "btn_edit_plugin_clicked" : self._on_btn_edit_plugin_clicked, \ "treeview_plugins_row_activated" : self._on_treeview_plugins_row_activated, \ "treeview_plugins_cursor_changed" : self._on_treeview_plugins_cursor_changed, \ }) # Add icons in alternative data paths (e.g. ./data/icons) # to the icon search path in case Mailnag is launched # from a local directory (without installing). icon_theme = Gtk.IconTheme.get_default() for path in get_data_paths(): icon_theme.append_search_path(os.path.join(path, "icons")) self._window = builder.get_object("config_window") self._window.set_icon_name("mailnag") self._load_stylesheet('config_window.css') self._cfg = read_cfg() self.daemon_enabled = False # # toggle buttons / notebook # self._notebook = builder.get_object("notebook") self._box_navigation = builder.get_object("box_navigation") self._box_navigation.get_children()[0].set_active(True) # # general page # # The dimension of the png is expected to be 180x180 px pb = GdkPixbuf.Pixbuf.new_from_file(get_data_file("mailnag.png")) pb = pb.new_subpixbuf(0, 10, 180, 146) # crop whitespace at the bottom self._image_logo = builder.get_object("image_logo") self._image_logo.set_from_pixbuf(pb) self._label_app_desc = builder.get_object("label_app_desc") self._label_app_desc.set_markup( "<span font=\"24\"><b>Mailnag</b></span>\nVersion %s" % str(APP_VERSION)) self._switch_daemon_enabled = builder.get_object( "switch_daemon_enabled") # # accounts page # self._accountman = AccountManager( CredentialStore.from_string( self._cfg.get('core', 'credentialstore'))) self._treeview_accounts = builder.get_object("treeview_accounts") self._liststore_accounts = builder.get_object("liststore_accounts") self._button_edit_account = builder.get_object("btn_edit_account") self._button_remove_account = builder.get_object("btn_remove_account") self._button_edit_account.set_sensitive(False) self._button_remove_account.set_sensitive(False) renderer_acc_enabled = Gtk.CellRendererToggle() renderer_acc_enabled.connect("toggled", self._on_account_toggled) column_acc_enabled = Gtk.TreeViewColumn(_('Enabled'), renderer_acc_enabled) column_acc_enabled.add_attribute(renderer_acc_enabled, "active", 1) column_acc_enabled.set_alignment(0.5) self._treeview_accounts.append_column(column_acc_enabled) renderer_acc_name = Gtk.CellRendererText() column_acc_name = Gtk.TreeViewColumn(_('Name'), renderer_acc_name, text=2) self._treeview_accounts.append_column(column_acc_name) # # plugins page # self._treeview_plugins = builder.get_object("treeview_plugins") self._liststore_plugins = builder.get_object("liststore_plugins") self._button_edit_plugin = builder.get_object("btn_edit_plugin") self._button_edit_plugin.set_sensitive(False) def renderer_plugin_enabled_func(column, cell_renderer, model, iter, data): plugin = model.get_value(iter, 0) name, desc, ver, author, mandatory = plugin.get_manifest() cell_renderer.set_sensitive(not mandatory) renderer_plugin_enabled = Gtk.CellRendererToggle() renderer_plugin_enabled.connect("toggled", self._on_plugin_toggled) column_plugin_enabled = Gtk.TreeViewColumn(_('Enabled'), renderer_plugin_enabled) column_plugin_enabled.add_attribute(renderer_plugin_enabled, "active", 1) column_plugin_enabled.set_alignment(0.5) column_plugin_enabled.set_cell_data_func(renderer_plugin_enabled, renderer_plugin_enabled_func) self._treeview_plugins.append_column(column_plugin_enabled) renderer_plugin_name = Gtk.CellRendererText() column_plugin_name = Gtk.TreeViewColumn(_('Name'), renderer_plugin_name, markup=2) self._treeview_plugins.append_column(column_plugin_name) # load config self._load_config() self._window.show() def _load_stylesheet(self, stylesheet): provider = Gtk.CssProvider() provider.load_from_path(get_data_file(stylesheet)) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) def _load_config(self): self._switch_daemon_enabled.set_active( bool(int(self._cfg.get('core', 'autostart')))) self._accountman.load_from_cfg(self._cfg) # load accounts for acc in self._accountman: row = [acc, acc.enabled, acc.name] self._liststore_accounts.append(row) self._select_account_path((0, )) # load plugins enabled_lst = self._cfg.get('core', 'enabled_plugins').split(',') enabled_lst = filter(lambda s: s != '', map(lambda s: s.strip(), enabled_lst)) plugins = Plugin.load_plugins(self._cfg) plugins.sort( key=lambda p: (not p.get_manifest()[4], p.get_manifest()[0])) for plugin in plugins: name, desc, ver, author, mandatory = plugin.get_manifest() enabled = True if (plugin.get_modname() in enabled_lst) or mandatory else False description = '<b>%s</b> (%s)\n%s' % (name, ver, desc) row = [plugin, enabled, description] self._liststore_plugins.append(row) self._select_plugin_path((0, )) def _save_config(self): autostart = self._switch_daemon_enabled.get_active() self._cfg.set('core', 'autostart', int(autostart)) self._accountman.save_to_cfg(self._cfg) enabled_plugins = '' for row in self._liststore_plugins: plugin = row[0] modname = plugin.get_modname() if row[1] == True: if len(enabled_plugins) > 0: enabled_plugins += ', ' enabled_plugins += modname config = plugin.get_config() if len(config) > 0: if not self._cfg.has_section(modname): self._cfg.add_section(modname) for k, v in config.iteritems(): self._cfg.set(modname, k, v) self._cfg.set('core', 'enabled_plugins', enabled_plugins) write_cfg(self._cfg) if autostart: self._create_autostart() else: self._delete_autostart() def _show_confirmation_dialog(self, text): message = Gtk.MessageDialog(self._window, Gtk.DialogFlags.MODAL, \ Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, text) resp = message.run() message.destroy() if resp == Gtk.ResponseType.YES: return True else: return False def _get_selected_account(self): treeselection = self._treeview_accounts.get_selection() selection = treeselection.get_selected() model, iter = selection # get account object from treeviews 1st column if iter != None: acc = model.get_value(iter, 0) else: acc = None return acc, model, iter def _select_account_path(self, path): treeselection = self._treeview_accounts.get_selection() treeselection.select_path(path) self._treeview_accounts.grab_focus() def _edit_account(self): acc, model, iter = self._get_selected_account() if iter != None: d = AccountDialog(self._window, acc) if d.run() == 1: model.set_value(iter, 2, acc.name) def _get_selected_plugin(self): treeselection = self._treeview_plugins.get_selection() selection = treeselection.get_selected() model, iter = selection # get plugin object from treeviews 1st column if iter != None: plugin = model.get_value(iter, 0) else: plugin = None return plugin, model, iter def _select_plugin_path(self, path): treeselection = self._treeview_plugins.get_selection() treeselection.select_path(path) self._treeview_plugins.grab_focus() def _edit_plugin(self): plugin, model, iter = self._get_selected_plugin() if (iter != None) and plugin.has_config_ui(): d = PluginDialog(self._window, plugin) d.run() def _create_autostart(self): autostart_folder = os.path.join(bd.xdg_config_home, "autostart") src = os.path.join(DESKTOP_FILE_DIR, "mailnag.desktop") dst = os.path.join(autostart_folder, "mailnag.desktop") if not os.path.exists(autostart_folder): os.makedirs(autostart_folder) shutil.copyfile(src, dst) # If mailag-config was started from a local directory, # patch the exec path of the autostart .desktop file accordingly. if not os.path.isabs(DESKTOP_FILE_DIR): exec_file = os.path.join(os.path.abspath(BIN_DIR), "mailnag") with open(dst, 'r') as f: strn = f.read() strn = strn.replace('/usr/bin/mailnag', exec_file) with open(dst, 'w') as f: f.write(strn) def _delete_autostart(self): autostart_folder = os.path.join(bd.xdg_config_home, "autostart") autostart_file = os.path.join(autostart_folder, "mailnag.desktop") if os.path.exists(autostart_file): os.remove(autostart_file) def _on_btn_page_toggled(self, button): if not button.get_active(): return page = 0 for btn in self._box_navigation.get_children(): if btn == button: self._notebook.set_current_page(page) else: btn.set_active(False) page += 1 def _on_account_toggled(self, cell, path): model = self._liststore_accounts iter = model.get_iter(path) acc = model.get_value(iter, 0) acc.enabled = not acc.enabled self._liststore_accounts.set_value(iter, 1, not cell.get_active()) def _on_btn_add_account_clicked(self, widget): acc = Account(enabled=True) d = AccountDialog(self._window, acc) if d.run() == 1: self._accountman.add(acc) row = [acc, acc.enabled, acc.name] iter = self._liststore_accounts.append(row) model = self._treeview_accounts.get_model() path = model.get_path(iter) self._treeview_accounts.set_cursor(path, None, False) self._treeview_accounts.grab_focus() def _on_btn_edit_account_clicked(self, widget): self._edit_account() def _on_btn_remove_account_clicked(self, widget): acc, model, iter = self._get_selected_account() if iter != None: if self._show_confirmation_dialog(_('Delete this account:') + \ '\n\n' + acc.name): # select prev/next account p = model.get_path(iter) if not p.prev(): p.next() self._select_account_path(p) # remove from treeview model.remove(iter) # remove from account manager self._accountman.remove(acc) def _on_treeview_accounts_row_activated(self, treeview, path, view_column): self._edit_account() def _on_liststore_accounts_row_deleted(self, model, path): self._button_edit_account.set_sensitive(len(model) > 0) self._button_remove_account.set_sensitive(len(model) > 0) def _on_liststore_accounts_row_inserted(self, model, path, user_param): self._button_edit_account.set_sensitive(len(model) > 0) self._button_remove_account.set_sensitive(len(model) > 0) def _on_plugin_toggled(self, cell, path): model = self._liststore_plugins iter = model.get_iter(path) self._liststore_plugins.set_value(iter, 1, not cell.get_active()) def _on_btn_edit_plugin_clicked(self, widget): self._edit_plugin() def _on_treeview_plugins_row_activated(self, treeview, path, view_column): self._edit_plugin() def _on_treeview_plugins_cursor_changed(self, treeview): # Workaround for a bug in GTK < 3.8, # see http://permalink.gmane.org/gmane.comp.gnome.svn/694089 if not self._window.get_visible(): return plugin, model, iter = self._get_selected_plugin() if iter != None: self._button_edit_plugin.set_sensitive(plugin.has_config_ui()) def _save_and_quit(self): self._save_config() self.daemon_enabled = self._switch_daemon_enabled.get_active() Gtk.main_quit() def _on_config_window_deleted(self, widget, event): self._save_and_quit()
class ConfigWindow: def __init__(self, app): builder = Gtk.Builder() builder.set_translation_domain(PACKAGE_NAME) builder.add_from_file(get_data_file("config_window.ui")) builder.connect_signals({ \ "config_window_deleted" : self._on_config_window_deleted, \ "btn_info_clicked" : self._on_btn_info_clicked, \ "btn_add_account_clicked" : self._on_btn_add_account_clicked, \ "btn_edit_account_clicked" : self._on_btn_edit_account_clicked, \ "btn_remove_account_clicked" : self._on_btn_remove_account_clicked, \ "treeview_accounts_row_activated" : self._on_treeview_accounts_row_activated, \ "liststore_accounts_row_deleted" : self._on_liststore_accounts_row_deleted, \ "liststore_accounts_row_inserted" : self._on_liststore_accounts_row_inserted, \ "btn_edit_plugin_clicked" : self._on_btn_edit_plugin_clicked, \ "treeview_plugins_row_activated" : self._on_treeview_plugins_row_activated, \ "treeview_plugins_cursor_changed" : self._on_treeview_plugins_cursor_changed, \ }) self._window = builder.get_object("config_window") self._window.set_icon_name("mailnag") self._window.set_application(app) self._cfg = read_cfg() self._daemon_enabled = False self._switch_daemon_enabled = builder.get_object("switch_daemon_enabled") # # accounts page # self._accountman = AccountManager(CredentialStore.from_string(self._cfg.get('core', 'credentialstore'))) self._treeview_accounts = builder.get_object("treeview_accounts") self._liststore_accounts = builder.get_object("liststore_accounts") self._button_edit_account = builder.get_object("btn_edit_account") self._button_remove_account = builder.get_object("btn_remove_account") self._button_edit_account.set_sensitive(False) self._button_remove_account.set_sensitive(False) renderer_acc_enabled = Gtk.CellRendererToggle() renderer_acc_enabled.connect("toggled", self._on_account_toggled) column_acc_enabled = Gtk.TreeViewColumn(_('Enabled'), renderer_acc_enabled) column_acc_enabled.add_attribute(renderer_acc_enabled, "active", 1) column_acc_enabled.set_alignment(0.5) self._treeview_accounts.append_column(column_acc_enabled) renderer_acc_name = Gtk.CellRendererText() column_acc_name = Gtk.TreeViewColumn(_('Name'), renderer_acc_name, text = 2) self._treeview_accounts.append_column(column_acc_name) # # plugins page # self._treeview_plugins = builder.get_object("treeview_plugins") self._liststore_plugins = builder.get_object("liststore_plugins") self._button_edit_plugin = builder.get_object("btn_edit_plugin") self._button_edit_plugin.set_sensitive(False) def renderer_plugin_enabled_func(column, cell_renderer, model, iter, data): plugin = model.get_value(iter, 0) name, desc, ver, author, mandatory = plugin.get_manifest() cell_renderer.set_sensitive(not mandatory) renderer_plugin_enabled = Gtk.CellRendererToggle() renderer_plugin_enabled.connect("toggled", self._on_plugin_toggled) column_plugin_enabled = Gtk.TreeViewColumn(_('Enabled'), renderer_plugin_enabled) column_plugin_enabled.add_attribute(renderer_plugin_enabled, "active", 1) column_plugin_enabled.set_alignment(0.5) column_plugin_enabled.set_cell_data_func(renderer_plugin_enabled, renderer_plugin_enabled_func) self._treeview_plugins.append_column(column_plugin_enabled) renderer_plugin_name = Gtk.CellRendererText() column_plugin_name = Gtk.TreeViewColumn(_('Name'), renderer_plugin_name, markup = 2) self._treeview_plugins.append_column(column_plugin_name) # load config self._load_config() self._window.show_all() def get_gtk_window(self): return self._window def get_daemon_enabled(self): return self._daemon_enabled def _load_config(self): self._switch_daemon_enabled.set_active(bool(int(self._cfg.get('core', 'autostart')))) self._accountman.load_from_cfg(self._cfg) # load accounts for acc in self._accountman: row = [acc, acc.enabled, acc.name] self._liststore_accounts.append(row) self._select_account_path((0,)) # load plugins enabled_lst = self._cfg.get('core', 'enabled_plugins').split(',') enabled_lst = filter(lambda s: s != '', map(lambda s: s.strip(), enabled_lst)) plugins = Plugin.load_plugins(self._cfg) plugins.sort(key = lambda p : (not p.get_manifest()[4], p.get_manifest()[0])) for plugin in plugins: name, desc, ver, author, mandatory = plugin.get_manifest() enabled = True if (plugin.get_modname() in enabled_lst) or mandatory else False description = '<b>%s</b> (%s)\n%s' % (name, ver, desc) row = [plugin, enabled, description] self._liststore_plugins.append(row) self._select_plugin_path((0,)) def _save_config(self): autostart = self._switch_daemon_enabled.get_active() self._cfg.set('core', 'autostart', int(autostart)) self._accountman.save_to_cfg(self._cfg) enabled_plugins = '' for row in self._liststore_plugins: plugin = row[0] modname = plugin.get_modname() if row[1] == True: if len(enabled_plugins) > 0: enabled_plugins += ', ' enabled_plugins += modname config = plugin.get_config() if len(config) > 0: if not self._cfg.has_section(modname): self._cfg.add_section(modname) for k, v in config.iteritems(): self._cfg.set(modname, k, v) self._cfg.set('core', 'enabled_plugins', enabled_plugins) write_cfg(self._cfg) if autostart: self._create_autostart() else: self._delete_autostart() def _show_confirmation_dialog(self, text): message = Gtk.MessageDialog(self._window, Gtk.DialogFlags.MODAL, \ Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, text) resp = message.run() message.destroy() if resp == Gtk.ResponseType.YES: return True else: return False def _get_selected_account(self): treeselection = self._treeview_accounts.get_selection() selection = treeselection.get_selected() model, iter = selection # get account object from treeviews 1st column if iter != None: acc = model.get_value(iter, 0) else: acc = None return acc, model, iter def _select_account_path(self, path): treeselection = self._treeview_accounts.get_selection() treeselection.select_path(path) self._treeview_accounts.grab_focus() def _edit_account(self): acc, model, iter = self._get_selected_account() if iter != None: d = AccountDialog(self._window, acc) if d.run() == Gtk.ResponseType.OK: model.set_value(iter, 2, acc.name) def _get_selected_plugin(self): treeselection = self._treeview_plugins.get_selection() selection = treeselection.get_selected() model, iter = selection # get plugin object from treeviews 1st column if iter != None: plugin = model.get_value(iter, 0) else: plugin = None return plugin, model, iter def _select_plugin_path(self, path): treeselection = self._treeview_plugins.get_selection() treeselection.select_path(path) self._treeview_plugins.grab_focus() def _edit_plugin(self): plugin, model, iter = self._get_selected_plugin() if (iter != None) and plugin.has_config_ui(): d = PluginDialog(self._window, plugin) d.run() def _create_autostart(self): autostart_folder = os.path.join(bd.xdg_config_home, "autostart") src = os.path.join(DESKTOP_FILE_DIR, "mailnag.desktop") dst = os.path.join(autostart_folder, "mailnag.desktop") if not os.path.exists(autostart_folder): os.makedirs(autostart_folder) shutil.copyfile(src, dst) # If mailag-config was started from a local directory, # patch the exec path of the autostart .desktop file accordingly. if not os.path.isabs(DESKTOP_FILE_DIR): exec_file = os.path.join(os.path.abspath(BIN_DIR), "mailnag") with open(dst, 'r') as f: strn = f.read() strn = strn.replace('/usr/bin/mailnag', exec_file) with open(dst, 'w') as f: f.write(strn) def _delete_autostart(self): autostart_folder = os.path.join(bd.xdg_config_home, "autostart") autostart_file = os.path.join(autostart_folder, "mailnag.desktop") if os.path.exists(autostart_file): os.remove(autostart_file) def _on_btn_info_clicked(self, widget): aboutdialog = Gtk.AboutDialog() aboutdialog.set_title(_("About %s") % PACKAGE_NAME.title()) aboutdialog.set_version(APP_VERSION) aboutdialog.set_program_name(PACKAGE_NAME.title()) aboutdialog.set_comments(_("An extensible mail notification daemon.")) aboutdialog.set_copyright(_("Copyright (c) 2011 - 2019 Patrick Ulbrich and contributors.")) aboutdialog.set_logo_icon_name("mailnag") aboutdialog.set_website("https://github.com/pulb/mailnag") aboutdialog.set_website_label(_("Homepage")) aboutdialog.set_license_type(Gtk.License.GPL_2_0) aboutdialog.set_authors([ "Patrick Ulbrich (maintainer)", "Balló György", "Edwin Smulders", "Freeroot", "James Powell", "Leighton Earl", "Matthias Mailänder", "Oleg", "Ralf Hersel", "Taylor Braun-Jones", "Thomas Haider", "Timo Kankare", "Vincent Cheng" ]) aboutdialog.set_translator_credits(_("translator-credits")) aboutdialog.set_artists([ "Reda Lazri" ]) aboutdialog.connect("response", lambda w, r: aboutdialog.destroy()) aboutdialog.set_modal(True) aboutdialog.set_transient_for(self._window) aboutdialog.show() def _on_account_toggled(self, cell, path): model = self._liststore_accounts iter = model.get_iter(path) acc = model.get_value(iter, 0) acc.enabled = not acc.enabled self._liststore_accounts.set_value(iter, 1, not cell.get_active()) def _on_btn_add_account_clicked(self, widget): acc = Account(enabled = True) d = AccountDialog(self._window, acc) if d.run() == Gtk.ResponseType.OK: self._accountman.add(acc) row = [acc, acc.enabled, acc.name] iter = self._liststore_accounts.append(row) model = self._treeview_accounts.get_model() path = model.get_path(iter) self._treeview_accounts.set_cursor(path, None, False) self._treeview_accounts.grab_focus() def _on_btn_edit_account_clicked(self, widget): self._edit_account() def _on_btn_remove_account_clicked(self, widget): acc, model, iter = self._get_selected_account() if iter != None: if self._show_confirmation_dialog(_('Delete this account:') + \ '\n\n' + acc.name): # select prev/next account p = model.get_path(iter) if not p.prev(): p.next() self._select_account_path(p) # remove from treeview model.remove(iter) # remove from account manager self._accountman.remove(acc) def _on_treeview_accounts_row_activated(self, treeview, path, view_column): self._edit_account() def _on_liststore_accounts_row_deleted(self, model, path): self._button_edit_account.set_sensitive(len(model) > 0) self._button_remove_account.set_sensitive(len(model) > 0) def _on_liststore_accounts_row_inserted(self, model, path, user_param): self._button_edit_account.set_sensitive(len(model) > 0) self._button_remove_account.set_sensitive(len(model) > 0) def _on_plugin_toggled(self, cell, path): model = self._liststore_plugins iter = model.get_iter(path) self._liststore_plugins.set_value(iter, 1, not cell.get_active()) def _on_btn_edit_plugin_clicked(self, widget): self._edit_plugin() def _on_treeview_plugins_row_activated(self, treeview, path, view_column): self._edit_plugin() def _on_treeview_plugins_cursor_changed(self, treeview): # Workaround for a bug in GTK < 3.8, # see http://permalink.gmane.org/gmane.comp.gnome.svn/694089 if not self._window.get_visible(): return plugin, model, iter = self._get_selected_plugin() if iter != None: self._button_edit_plugin.set_sensitive(plugin.has_config_ui()) def _on_config_window_deleted(self, widget, event): self._save_config() self._daemon_enabled = self._switch_daemon_enabled.get_active()