mirror of
https://github.com/pulb/mailnag.git
synced 2026-01-13 17:32:03 +01:00
400 lines
13 KiB
Python
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()
|
|
|
|
|