Files
mailnag/Mailnag/configuration/configwindow.py
2019-10-26 19:30:47 +02:00

400 lines
13 KiB
Python

# Copyright 2011 - 2019 Patrick Ulbrich <zulu99@gmx.net>
# Copyright 2011 Ralf Hersel <ralf.hersel@gmx.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
import gi
gi.require_version('Gtk', '3.0')
import os
import shutil
import xdg.BaseDirectory as bd
from gi.repository import Gtk
from Mailnag.common.dist_cfg import PACKAGE_NAME, APP_VERSION, BIN_DIR, DESKTOP_FILE_DIR
from Mailnag.common.i18n import _
from Mailnag.common.utils import get_data_file
from Mailnag.common.config import read_cfg, write_cfg
from Mailnag.common.accounts import Account, AccountManager
from Mailnag.common.credentialstore import CredentialStore
from Mailnag.common.plugins import Plugin
from Mailnag.configuration.accountdialog import AccountDialog
from Mailnag.configuration.plugindialog import PluginDialog
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 = [s for s in [s.strip() for s in enabled_lst] if s != '']
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.items():
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",
"razer",
"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()