diff --git a/Mailnag/common/utils.py b/Mailnag/common/utils.py index 28b4aaa..bc20f6f 100644 --- a/Mailnag/common/utils.py +++ b/Mailnag/common/utils.py @@ -3,7 +3,7 @@ # # utils.py # -# Copyright 2011 - 2016 Patrick Ulbrich +# Copyright 2011 - 2019 Patrick Ulbrich # Copyright 2007 Marco Ferragina # # This program is free software; you can redistribute it and/or modify @@ -109,7 +109,7 @@ def try_call(f, err_retval = None): return err_retval -def shutdown_existing_instance(): +def shutdown_existing_instance(wait_for_completion = True): bus = dbus.SessionBus() if bus.name_has_owner(DBUS_BUS_NAME): @@ -122,8 +122,9 @@ def shutdown_existing_instance(): shutdown() - while bus.name_has_owner(DBUS_BUS_NAME): - time.sleep(2) + if wait_for_completion: + while bus.name_has_owner(DBUS_BUS_NAME): + time.sleep(2) print 'OK' except: diff --git a/Mailnag/configuration/accountdialog.py b/Mailnag/configuration/accountdialog.py index f103842..297501f 100644 --- a/Mailnag/configuration/accountdialog.py +++ b/Mailnag/configuration/accountdialog.py @@ -3,7 +3,7 @@ # # accountdialog.py # -# Copyright 2011 - 2017 Patrick Ulbrich +# Copyright 2011 - 2019 Patrick Ulbrich # Copyright 2016 Timo Kankare # # This program is free software; you can redistribute it and/or modify @@ -22,6 +22,7 @@ # MA 02110-1301, USA. # +import os import gi gi.require_version('Gtk', '3.0') gi.require_version('GLib', '2.0') @@ -56,17 +57,24 @@ class AccountDialog: builder = Gtk.Builder() builder.set_translation_domain(PACKAGE_NAME) - builder.add_from_file(get_data_file("account_dialog.ui")) + builder.add_from_file(get_data_file("account_widget.ui")) builder.connect_signals({ \ "account_type_changed" : self._on_cmb_account_type_changed, \ "entry_changed" : self._on_entry_changed, \ - "expander_folders_activate" : self._on_expander_folders_activate, \ - "btn_cancel_clicked" : self._on_btn_cancel_clicked, \ - "btn_save_clicked" : self._on_btn_save_clicked \ + "expander_folders_activate" : self._on_expander_folders_activate \ }) - self._window = builder.get_object("account_dialog") - self._window.set_transient_for(parent) + self._window = Gtk.Dialog(title = _('Mail Account'), parent = parent, use_header_bar = True, \ + buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK)) + + self._window.set_default_response(Gtk.ResponseType.OK) + self._window.set_default_size(400, 0) + + self._box = self._window.get_content_area() + self._box.set_border_width(12) + self._box.set_spacing(12) + + self._box.pack_start(builder.get_object("account_widget"), True, True, 0) self._cmb_account_type = builder.get_object("cmb_account_type") self._label_account_name = builder.get_object("label_account_name") @@ -89,7 +97,9 @@ class AccountDialog: self._liststore_folders = builder.get_object("liststore_folders") self._chk_account_push = builder.get_object("chk_account_push") self._chk_account_ssl = builder.get_object("chk_account_ssl") - self._button_save = builder.get_object("button_save") + + self._button_ok = self._window.get_widget_for_response(Gtk.ResponseType.OK) + self._button_ok.set_sensitive(False) self._error_label = None self._folders_received = False @@ -115,7 +125,7 @@ class AccountDialog: res = self._window.run() - if res == 1: + if res == Gtk.ResponseType.OK: self._configure_account(self._acc) self._window.destroy() @@ -143,9 +153,8 @@ class AccountDialog: self._chk_account_push.set_sensitive(len(config['folders']) < 2) if 'ssl' in config: self._chk_account_ssl.set_active(config['ssl']) - if 'path' in config: + if ('path' in config) and os.path.exists(config.get('path')): self._chooser_account_file_path.set_filename(config.get('path')) - if 'path' in config: self._chooser_account_directory_path.set_filename(config.get('path')) @@ -265,14 +274,6 @@ class AccountDialog: else: is_type_change_allowed = True self._cmb_account_type.set_sensitive(is_type_change_allowed) - - - def _on_btn_cancel_clicked(self, widget): - pass - - - def _on_btn_save_clicked(self, widget): - pass def _on_entry_changed(self, widget): @@ -296,7 +297,7 @@ class AccountDialog: len(self._entry_account_password.get_text()) > 0 self._expander_folders.set_sensitive(self._folders_received or ok) - self._button_save.set_sensitive(ok) + self._button_ok.set_sensitive(ok) def _on_expander_folders_activate(self, widget): diff --git a/Mailnag/configuration/configwindow.py b/Mailnag/configuration/configwindow.py index 0056b52..46d5e46 100644 --- a/Mailnag/configuration/configwindow.py +++ b/Mailnag/configuration/configwindow.py @@ -3,7 +3,7 @@ # # configwindow.py # -# Copyright 2011 - 2016 Patrick Ulbrich +# Copyright 2011 - 2019 Patrick Ulbrich # Copyright 2011 Ralf Hersel # # This program is free software; you can redistribute it and/or modify @@ -24,16 +24,15 @@ import gi gi.require_version('Gtk', '3.0') -gi.require_version('GLib', '2.0') import os import shutil import xdg.BaseDirectory as bd -from gi.repository import GLib, GdkPixbuf, Gdk, Gtk, GObject +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, get_data_paths +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 @@ -43,13 +42,13 @@ from Mailnag.configuration.plugindialog import PluginDialog class ConfigWindow: - def __init__(self): + 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_page_toggled" : self._on_btn_page_toggled, \ + "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, \ @@ -61,37 +60,13 @@ class ConfigWindow: "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._window.set_application(app) self._cfg = read_cfg() - self.daemon_enabled = False + 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("Mailnag\nVersion %s" % str(APP_VERSION)) self._switch_daemon_enabled = builder.get_object("switch_daemon_enabled") # @@ -147,17 +122,17 @@ class ConfigWindow: # load config self._load_config() - self._window.show() + self._window.show_all() + + + def get_gtk_window(self): + return self._window + + + def get_daemon_enabled(self): + return self._daemon_enabled - 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')))) @@ -246,7 +221,7 @@ class ConfigWindow: if iter != None: d = AccountDialog(self._window, acc) - if d.run() == 1: + if d.run() == Gtk.ResponseType.OK: model.set_value(iter, 2, acc.name) @@ -301,19 +276,40 @@ class ConfigWindow: if os.path.exists(autostart_file): os.remove(autostart_file) - - def _on_btn_page_toggled(self, button): - if not button.get_active(): return + + 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()) - 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 - - + 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) @@ -327,7 +323,7 @@ class ConfigWindow: acc = Account(enabled = True) d = AccountDialog(self._window, acc) - if d.run() == 1: + if d.run() == Gtk.ResponseType.OK: self._accountman.add(acc) row = [acc, acc.enabled, acc.name] @@ -397,14 +393,9 @@ class ConfigWindow: 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() + self._save_config() + self._daemon_enabled = self._switch_daemon_enabled.get_active() diff --git a/Mailnag/configuration/plugindialog.py b/Mailnag/configuration/plugindialog.py index 1afa776..d0d89f3 100644 --- a/Mailnag/configuration/plugindialog.py +++ b/Mailnag/configuration/plugindialog.py @@ -3,7 +3,7 @@ # # plugindialog.py # -# Copyright 2013 - 2016 Patrick Ulbrich +# Copyright 2013 - 2019 Patrick Ulbrich # # 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 @@ -32,20 +32,22 @@ class PluginDialog: def __init__(self, parent, plugin): self._plugin = plugin - flags = Gtk.DialogFlags.MODAL # | Gtk.DialogFlags.USE_HEADER_BAR - self._window = Gtk.Dialog(_('Plugin Configuration'), parent, flags, \ - (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK)) + self._window = Gtk.Dialog(title = _('Plugin Configuration'), parent = parent, use_header_bar = True, \ + buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK)) + + self._window.set_default_response(Gtk.ResponseType.OK) + self._window.set_default_size(480, 0) self._box = self._window.get_content_area() - self._box.set_border_width(6) - self._box.set_spacing(6) + self._box.set_border_width(12) + self._box.set_spacing(12) def run(self): widget = self._plugin.get_config_ui() if widget != None: - self._box.add(widget) + self._box.pack_start(widget, True, True, 0) widget.show_all() self._plugin.load_ui_from_config(widget) diff --git a/data/account_dialog.ui b/data/account_dialog.ui deleted file mode 100644 index e92adb5..0000000 --- a/data/account_dialog.ui +++ /dev/null @@ -1,372 +0,0 @@ - - - - - - - - - - - - - - False - 5 - Mail Account - False - True - dialog - - - False - vertical - 18 - - - False - end - - - gtk-cancel - True - True - True - True - - - - True - True - 0 - - - - - gtk-ok - True - False - True - True - True - - - - True - True - 1 - - - - - False - True - end - 0 - - - - - True - False - 6 - 6 - 6 - 6 - 6 - - - True - True - True - - number - - - 1 - 5 - - - - - True - True - True - - - - - 1 - 4 - - - - - True - True - True - False - - - - - 1 - 3 - - - - - True - True - True - - - - - 1 - 2 - - - - - True - True - True - - - - - 1 - 1 - - - - - Enable Push-IMAP - True - True - False - 0 - True - - - 0 - 8 - 2 - - - - - True - False - - - - 1 - 0 - - - - - Enable SSL encryption - True - True - False - 0 - True - - - 0 - 9 - 2 - - - - - True - False - Accountname: - 0 - - - 0 - 1 - - - - - True - False - Account type: - 0 - - - 0 - 0 - - - - - True - False - User: - 0 - - - 0 - 2 - - - - - True - False - Password: - 0 - - - 0 - 3 - - - - - True - False - Server: - 0 - - - 0 - 4 - - - - - True - False - Port: - 0 - - - 0 - 5 - - - - - True - False - True - - - - True - False - - - True - True - in - 100 - - - True - True - liststore_folders - False - - - - - - - - - - - - True - False - Folders (optional) - - - - - 0 - 10 - 2 - - - - - True - False - File path: - 0 - - - 0 - 6 - - - - - True - False - False - False - True - False - - - - - 1 - 6 - - - - - True - False - Directory: - 0 - - - 0 - 7 - - - - - True - False - select-folder - False - False - True - False - - - - - 1 - 7 - - - - - True - True - 1 - - - - - - button_cancel - button_save - - - diff --git a/data/account_widget.ui b/data/account_widget.ui new file mode 100644 index 0000000..e67f2fe --- /dev/null +++ b/data/account_widget.ui @@ -0,0 +1,305 @@ + + + + + + True + False + 6 + 6 + 6 + + + True + True + True + + number + + + 1 + 5 + + + + + True + True + True + + + + + 1 + 4 + + + + + True + True + True + False + + + + + 1 + 3 + + + + + True + True + True + + + + + 1 + 2 + + + + + True + True + True + + + + + 1 + 1 + + + + + Enable Push-IMAP + True + True + False + 0 + True + + + 0 + 8 + 2 + + + + + True + False + + + + 1 + 0 + + + + + Enable SSL encryption + True + True + False + 0 + True + + + 0 + 9 + 2 + + + + + True + False + Accountname: + 0 + + + 0 + 1 + + + + + True + False + Account type: + 0 + + + 0 + 0 + + + + + True + False + User: + 0 + + + 0 + 2 + + + + + True + False + Password: + 0 + + + 0 + 3 + + + + + True + False + Server: + 0 + + + 0 + 4 + + + + + True + False + Port: + 0 + + + 0 + 5 + + + + + True + False + True + + + + True + False + + + True + True + in + 100 + + + True + True + liststore_folders + False + 0 + + + + + + + + -1 + + + + + + + True + False + Folders (optional) + + + + + 0 + 10 + 2 + + + + + True + False + File path: + 0 + + + 0 + 6 + + + + + True + False + False + False + True + False + + + + + 1 + 6 + + + + + True + False + Directory: + 0 + + + 0 + 7 + + + + + True + False + select-folder + False + False + True + False + + + + + 1 + 7 + + + + + + + + + + + + diff --git a/data/config_window.css b/data/config_window.css deleted file mode 100644 index a18f7c4..0000000 --- a/data/config_window.css +++ /dev/null @@ -1,6 +0,0 @@ -.bg-gradient { - background-image: linear-gradient(to bottom, - @theme_bg_color, - shade(@theme_bg_color, 0.94) - 12%); -} diff --git a/data/config_window.ui b/data/config_window.ui index 5ceeff0..c578022 100644 --- a/data/config_window.ui +++ b/data/config_window.ui @@ -1,7 +1,7 @@ - + - + @@ -24,45 +24,130 @@ - + False - Mailnag Configuration + center + 460 + 320 - - + + True False - vertical - - + 12 + True + + True False - 18 - 18 - 12 - 12 - True + stack1 + + + + + True + True + True + popovermenu - - General + + True + False + open-menu + + + + + end + + + + + + + True + False + slide-left-right + + + True + False + vertical + + True True - True - + in + + + True + True + liststore_accounts + False + True + False + 1 + + + + + + - False + True True 0 - - Accounts + True - True - True - + False + icons + 1 + + + True + False + Add Account + list-add-symbolic + + + + False + True + + + + + True + False + Remove Account + list-remove-symbolic + + + + False + True + + + + + True + False + Edit Account + text-editor-symbolic + + + + False + True + + + False @@ -70,295 +155,154 @@ 1 - - - Plugins - True - True - True - - - - False - True - 2 - - - - False - True - 0 + page0 + Accounts - + True - True - False - False + False + vertical - + True - False + True + in - - True - False - 12 - 12 - 12 - vertical - 12 - - - True - False - vertical - - - - False - False - 0 - - - - - True - False - APP_DESC - True - - - False - False - 1 - - - - - False - False - 0 - - - - - True - True - <a href="https://github.com/pulb/mailnag">Mailnag</a> - An extensible mail notification daemon. -Copyright (c) 2011 - 2017 Patrick Ulbrich -and contributors. - True - - - False - False - 1 - - - - - True - True - center - - - False - False - 2 - - - - - - - - - - - - - True - False - vertical - - + True True - in - - - True - True - liststore_accounts - False - True - False - 1 - - - - - + liststore_plugins + False + True + False + 1 + + + + - - True - True - 0 - - - - - True - False - icons - 1 - - - True - False - Add Account - list-add-symbolic - - - - False - True - - - - - True - False - Remove Account - list-remove-symbolic - - - - False - True - - - - - True - False - Edit Account - text-editor-symbolic - - - - False - True - - - - - - False - True - 1 - + True + True + 0 + + + + + True + False + icons + 1 + + + True + False + Edit Plugin + text-editor-symbolic + + + + False + True + + + + + + False + True 1 - - - - - - True - False - vertical - - - True - True - in - - - True - True - liststore_plugins - False - True - False - 1 - - - - - - - - - - True - True - 0 - - - - - True - False - icons - 1 - - - True - False - Edit Plugin - text-editor-symbolic - - - - False - True - - - - - - False - True - 1 - - - - - 2 - - - - - - True - True + page1 + Plugins 1 + + False + 6 + menubutton1 + + + True + False + vertical + + + True + True + Enable/disable Mailnag daemon + center + center + + + False + True + 6 + 0 + + + + + True + False + + + False + True + 1 + + + + + Donate + True + True + True + none + https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8F5FNJ3U4N7AW + + + False + True + 2 + + + + + True + True + True + Info + True + + + + False + True + 3 + + + + + submenu0 + 1 + + + diff --git a/mailnag-config b/mailnag-config index 2b91a78..e8c4d88 100755 --- a/mailnag-config +++ b/mailnag-config @@ -3,8 +3,7 @@ # # mailnag-config # -# Copyright 2011 - 2016 Patrick Ulbrich -# Copyright 2011 Ralf Hersel +# Copyright 2011 - 2019 Patrick Ulbrich # # 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 @@ -29,37 +28,66 @@ import os import subprocess import logging from gi.repository import Gtk -from dbus.mainloop.glib import DBusGMainLoop from Mailnag.common.utils import fix_cwd, init_logging fix_cwd() -from Mailnag.common.utils import set_procname, shutdown_existing_instance +from Mailnag.common.i18n import _ +from Mailnag.common.utils import set_procname, shutdown_existing_instance, get_data_file, get_data_paths from Mailnag.common.dist_cfg import BIN_DIR from Mailnag.configuration.configwindow import ConfigWindow LOG_LEVEL = logging.DEBUG +class App(Gtk.Application): + def __init__(self): + Gtk.Application.__init__(self, application_id = 'com.github.pulp.Mailnag') + self.win = None + + + def do_startup(self): + Gtk.Application.do_startup(self) + + # 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")) + + + def do_activate(self): + Gtk.Application.do_activate(self) + + if not self.win: + self.win = ConfigWindow(self) + self.win.get_gtk_window().present() + + + def do_shutdown(self): + Gtk.Application.do_shutdown(self) + + if self.win.get_daemon_enabled(): + try: + # the launched daemon shuts down + # an already running daemon + print "Launching Mailnag daemon." + subprocess.Popen(os.path.join(BIN_DIR, "mailnag")) + except: + print "ERROR: Failed to launch Mailnag daemon." + else: + # shutdown running Mailnag daemon + shutdown_existing_instance(wait_for_completion = False) + + def main(): set_procname("mailnag-config") init_logging(enable_stdout = True, enable_syslog = False, log_level = LOG_LEVEL) - confwin = ConfigWindow() - Gtk.main() - - if confwin.daemon_enabled: - try: - # the launched daemon shuts down - # an already running daemon - print "Launching Mailnag daemon." - subprocess.Popen(os.path.join(BIN_DIR, "mailnag")) - except: - print "ERROR: Failed to launch Mailnag daemon." - else: - DBusGMainLoop(set_as_default = True) - # shutdown running Mailnag daemon - shutdown_existing_instance() + app = App() + app.run(None) + if __name__ == "__main__": main()