Exemplo n.º 1
0
def test_imap_config_values_should_be_stored():
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('imap')
	options = {
		'user': '******',
		'password': '',
		'server': 'imap.example.org',
		'port': '',
		'ssl': True,
		'imap': True,
		'idle': True,
		'folders': ['a', 'b'],
	}
	config = RawConfigParser()
	config.add_section('account1')
	am._set_cfg_options(config, 'account1', options, option_spec)
	expected_config_items = [
		('user', 'you'),
		('password', ''),
		('server', 'imap.example.org'),
		('port', ''),
		('ssl', '1'),
		('imap', '1'),
		('idle', '1'),
		('folder', '["a", "b"]'),
	]
	assert set(expected_config_items) == set(config.items('account1'))
Exemplo n.º 2
0
	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
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
def test_imap_config_values_should_be_stored():
    am = AccountManager()
    option_spec = get_mailbox_parameter_specs('imap')
    options = {
        'user': '******',
        'password': '',
        'server': 'imap.example.org',
        'port': '',
        'ssl': True,
        'imap': True,
        'idle': True,
        'folders': ['a', 'b'],
    }
    config = RawConfigParser()
    config.add_section('account1')
    am._set_cfg_options(config, 'account1', options, option_spec)
    expected_config_items = [
        ('user', 'you'),
        ('password', ''),
        ('server', 'imap.example.org'),
        ('port', ''),
        ('ssl', '1'),
        ('imap', '1'),
        ('idle', '1'),
        ('folder', '["a", "b"]'),
    ]
    assert set(expected_config_items) == set(config.items('account1'))
Exemplo n.º 5
0
def test_pop3_config_defaults(config):
    am = AccountManager()
    option_spec = get_mailbox_parameter_specs('pop3')
    options = am._get_cfg_options(config, 'account3', option_spec)
    expected_options = {
        'user': '',
        'password': '',
        'server': '',
        'port': '',
        'ssl': True,
        'imap': False,
        'idle': False,
    }
    assert expected_options == options
Exemplo n.º 6
0
def test_pop3_config_defaults(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('pop3')
	options = am._get_cfg_options(config, 'account3', option_spec)
	expected_options = {
		'user': '',
		'password': '',
		'server': '',
		'port': '',
		'ssl': True,
		'imap': False,
		'idle': False,
	}
	assert expected_options == options
Exemplo n.º 7
0
def test_imap_config_options(config):
    am = AccountManager()
    option_spec = get_mailbox_parameter_specs('imap')
    options = am._get_cfg_options(config, 'account1', option_spec)
    expected_options = {
        'user': '******',
        'password': '******',
        'server': 'imap.example.org',
        'port': '',
        'ssl': True,
        'imap': True,
        'idle': True,
        'folders': [],
    }
    assert expected_options == options
Exemplo n.º 8
0
def test_imap_config_options(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('imap')
	options = am._get_cfg_options(config, 'account1', option_spec)
	expected_options = {
		'user': '******',
		'password': '',
		'server': 'imap.example.org',
		'port': '',
		'ssl': True,
		'imap': True,
		'idle': True,
		'folders': [],
	}
	assert expected_options == options
Exemplo n.º 9
0
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 == {}
Exemplo n.º 10
0
    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()
Exemplo n.º 11
0
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'
Exemplo n.º 12
0
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'
Exemplo n.º 13
0
def test_save_zero_accounts_to_config(config):
    am = AccountManager()
    am.save_to_cfg(config)
    assert len(config.sections()) == 0
Exemplo n.º 14
0
def test_imap_new_folder_option(config):
	am = AccountManager()
	option_spec = get_mailbox_parameter_specs('imap')
	options = am._get_cfg_options(config, 'account6', option_spec)
	assert options['folders'] == ['folderA', 'folderB', 'folderC']
Exemplo n.º 15
0
	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()
Exemplo n.º 16
0
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()
Exemplo n.º 17
0
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()
Exemplo n.º 18
0
    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()
Exemplo n.º 19
0
	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()
Exemplo n.º 20
0
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()
Exemplo n.º 21
0
def test_imap_new_folder_option(config):
    am = AccountManager()
    option_spec = get_mailbox_parameter_specs('imap')
    options = am._get_cfg_options(config, 'account6', option_spec)
    assert options['folders'] == ['folderA', 'folderB', 'folderC']
Exemplo n.º 22
0
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
Exemplo n.º 23
0
def test_save_zero_accounts_to_config_with_credential_store(config):
    cs = FakeCredentialStore()
    am = AccountManager(cs)
    am.save_to_cfg(config)
    assert len(config.sections()) == 0
    assert cs.secrets == {}
Exemplo n.º 24
0
    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()
Exemplo n.º 25
0
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()