diff --git a/core/Services/Calendar/EventStore.vala b/core/Services/Calendar/EventStore.vala index af8fe3733..ef46ca4c2 100644 --- a/core/Services/Calendar/EventStore.vala +++ b/core/Services/Calendar/EventStore.vala @@ -74,7 +74,7 @@ public class Calendar.EventStore : Object { } } - protected EventStore () { + public EventStore () { this.week_starts_on = get_week_start (); this.month_start = Calendar.Util.datetime_get_start_of_month (get_page ()); compute_ranges (); @@ -368,7 +368,7 @@ public class Calendar.EventStore : Object { int wso = (int) week_starts_on; int offset = 0; - // offset corresponds number of days from the start of the week to + // offset corresponds number of days from the start of the week to // month_start, as seen on a displayed calendar. if (wso < dow) { offset = dow - wso; diff --git a/daemon/Daemon.vala b/daemon/Daemon.vala deleted file mode 100644 index b6c94f700..000000000 --- a/daemon/Daemon.vala +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2018 elementary, Inc. (https://elementary.io) - * 2014 Corentin Noël - * - * 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 3 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, see . - */ - -namespace Maya { - private static bool has_debug; - - const OptionEntry[] OPTIONS = { - { "debug", 'd', 0, OptionArg.NONE, out has_debug, - N_("Print debug information"), null}, - { null } - }; - - public class Daemon : GLib.Application { - private Gee.HashMultiMap event_uids; - - construct { - load_today_events (); - Timeout.add_seconds (86400, () => { - event_uids.clear (); - load_today_events (); - return true; - }); - } - - protected override void activate () { - Gtk.main (); - } - - private void load_today_events () { - event_uids = new Gee.HashMultiMap (); - var model = Calendar.EventStore.get_default (); - model.events_added.connect (on_events_added); - model.events_updated.connect (on_events_updated); - model.events_removed.connect (on_events_removed); - model.month_start = Calendar.Util.datetime_get_start_of_month (new DateTime.now_local ()); - } - - private void on_events_added (E.Source source, Gee.Collection events) { - var extension = (E.SourceAlarms)source.get_extension (E.SOURCE_EXTENSION_ALARMS); - if (extension.get_include_me () == false) { - return; - } - - Idle.add ( () => { - foreach (var event in events) - add_event (source, event); - - return false; - }); - } - - private void on_events_updated (E.Source source, Gee.Collection events) { - Idle.add ( () => { - foreach (var event in events) - update_event (source, event); - - return false; - }); - } - - private void on_events_removed (E.Source source, Gee.Collection events) { - Idle.add ( () => { - foreach (var event in events) - remove_event (source, event); - - return false; - }); - } - - private void add_event (E.Source source, ECal.Component event) { - unowned ICal.Component comp = event.get_icalcomponent (); - debug ("Event [%s, %s, %s]".printf (comp.get_summary (), source.dup_display_name (), comp.get_uid ())); - foreach (var alarm_uid in event.get_alarm_uids ()) { - ECal.ComponentAlarm e_alarm = event.get_alarm (alarm_uid); - ECal.ComponentAlarmAction action; - -#if E_CAL_2_0 - action = e_alarm.get_action (); -#else - e_alarm.get_action (out action); -#endif - switch (action) { - case (ECal.ComponentAlarmAction.DISPLAY): - ECal.ComponentAlarmTrigger trigger; -#if E_CAL_2_0 - trigger = e_alarm.get_trigger (); -#else - e_alarm.get_trigger (out trigger); -#endif - if (trigger.get_kind () == ECal.ComponentAlarmTriggerKind.RELATIVE_START) { - ICal.Duration duration = trigger.get_duration (); - var start_time = Calendar.Util.icaltime_to_datetime (comp.get_dtstart ()); - var now = new DateTime.now_local (); - if (now.compare (start_time) > 0) { - continue; - } - start_time = start_time.add_weeks (-(int)duration.get_weeks ()); //vala-lint=space-before-paren - start_time = start_time.add_days (-(int)duration.get_days ()); //vala-lint=space-before-paren - start_time = start_time.add_hours (-(int)duration.get_hours ()); //vala-lint=space-before-paren - start_time = start_time.add_minutes (-(int)duration.get_minutes ()); //vala-lint=space-before-paren - start_time = start_time.add_seconds (-(int)duration.get_seconds ()); //vala-lint=space-before-paren - if (start_time.get_year () == now.get_year () && start_time.get_day_of_year () == now.get_day_of_year ()) { - var time = time_until_now (start_time); - if (time >= 0) { - add_timeout.begin (source, event, (uint)time); - } - } - } - continue; - default: - continue; - } - } - } - - public async void add_timeout (E.Source source, ECal.Component event, uint interval) { - var uid = "%u-%u".printf (interval, GLib.Random.next_int ()); - event_uids.set (event, uid); - debug ("adding timeout uid:%s", uid); - Timeout.add_seconds (interval, () => { - var extension = (E.SourceAlarms)source.get_extension (E.SOURCE_EXTENSION_ALARMS); - if (extension != null) { - extension.set_last_notified (new DateTime.now_local ().to_string ()); - } - - queue_event_notification (event, uid); - return false; - }); - } - - public void queue_event_notification (ECal.Component event, string uid) { - if (!(uid in event_uids[event])) { - return; - } - - unowned ICal.Component comp = event.get_icalcomponent (); - var primary_text = "%s".printf (comp.get_summary ()); - var start_time = Calendar.Util.icaltime_to_datetime (comp.get_dtstart ()); - var now = new DateTime.now_local (); - string secondary_text = ""; - var h24_settings = new GLib.Settings ("org.gnome.desktop.interface"); - var format = h24_settings.get_string ("clock-format"); - var text = Granite.DateTime.get_default_time_format (format.contains ("12h")); - if (start_time.get_year () == now.get_year ()) { - if (start_time.get_day_of_year () == now.get_day_of_year ()) { - secondary_text = Granite.DateTime.get_relative_datetime (start_time); - } else { - secondary_text = start_time.format ("%s, %s".printf (Granite.DateTime.get_default_date_format (), text)); - } - } else { - secondary_text = start_time.format ("%s, %s".printf (Granite.DateTime.get_default_date_format (false, true, true), text)); - } - - var notification = new GLib.Notification (primary_text); - notification.set_body (secondary_text); - - GLib.Application.get_default ().send_notification (uid, notification); - } - - private void update_event (E.Source source, ECal.Component event) { - remove_event (source, event); -#if !E_CAL_2_0 - event.rescan (); -#endif - event.commit_sequence (); - add_event (source, event); - } - - private void remove_event (E.Source source, ECal.Component event) { - if (event in event_uids) { - event_uids.remove_all (event); - } - } - - private TimeSpan time_until_now (GLib.DateTime dt) { - var now = new DateTime.now_local (); - return dt.difference (now) / TimeSpan.SECOND; - } - } - - public static int main (string[] args) { - OptionContext context = new OptionContext (""); - context.add_main_entries (OPTIONS, null); - - try { - context.parse (ref args); - } catch (OptionError e) { - error (e.message); - } - - Granite.Services.Logger.initialize (Build.APP_NAME); - Granite.Services.Logger.DisplayLevel = Granite.Services.LogLevel.WARN; - - if (has_debug) { - Granite.Services.Logger.DisplayLevel = Granite.Services.LogLevel.DEBUG; - } - - var app = new Daemon (); - - return app.run (args); - } -} diff --git a/daemon/meson.build b/daemon/meson.build deleted file mode 100644 index 1dac2af6b..000000000 --- a/daemon/meson.build +++ /dev/null @@ -1,18 +0,0 @@ -daemon_deps = [ - core_dep, - gee_dep, - glib_dep, - libecal_dep, - libedataserver_dep, - libedataserverui_dep, - libical_dep, - m_dep -] - -executable( - meson.project_name()+'-daemon', - 'Daemon.vala', - dependencies: daemon_deps, - install: true, - install_dir: join_paths(get_option('prefix'), get_option('libexecdir')) -) diff --git a/data/io.elementary.calendar-daemon.desktop.in b/data/io.elementary.calendar-daemon.desktop.in index aa5431cbd..dd9f89d53 100644 --- a/data/io.elementary.calendar-daemon.desktop.in +++ b/data/io.elementary.calendar-daemon.desktop.in @@ -2,7 +2,7 @@ Name=Calendar Daemon Comment=View Event Reminders Icon=io.elementary.calendar -Exec=@EXEC_PATH@ +Exec=@EXEC_PATH@ --background Terminal=false Type=Application Categories=System; diff --git a/meson.build b/meson.build index 5517b705c..afc2715fb 100644 --- a/meson.build +++ b/meson.build @@ -58,7 +58,6 @@ gresource_calendar = gnome.compile_resources( subdir('data') subdir('core') -subdir('daemon') subdir('src') subdir('plugins') subdir('po') diff --git a/src/Application.vala b/src/Application.vala index a2def4f2f..cf5b08294 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -29,6 +29,7 @@ namespace Maya { public MainWindow window; public static GLib.Settings? saved_state = null; public static GLib.Settings? wingpanel_settings = null; + public static bool run_in_background = false; static construct { if (SettingsSchemaSource.get_default ().lookup ("io.elementary.calendar.savedstate", true) != null) { @@ -58,10 +59,18 @@ namespace Maya { public const OptionEntry[] APP_OPTIONS = { { "add-event", 'a', 0, OptionArg.NONE, out Option.add_event, N_("Create an event"), null }, { "show-day", 's', 0, OptionArg.STRING, out Option.show_day, N_("Focus the given day"), N_("date") }, + { "background", 'b', 0, OptionArg.NONE, out run_in_background, "Run the Application in background", null}, { null } }; protected override void activate () { + if (run_in_background) { + run_in_background = false; + new Calendar.TodayEventMonitor (); + hold (); + return; + } + if (get_windows () != null) { get_windows ().data.present (); // present window if app is already running return; diff --git a/src/TodayEventMonitor.vala b/src/TodayEventMonitor.vala new file mode 100644 index 000000000..8cf1da056 --- /dev/null +++ b/src/TodayEventMonitor.vala @@ -0,0 +1,180 @@ +/* + * Copyright 2021 elementary, Inc. (https://elementary.io) + * 2014 Corentin Noël + * + * 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 3 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, see . + */ + +public class Calendar.TodayEventMonitor : GLib.Object { + private Gee.HashMultiMap event_uids; + + construct { + load_today_events (); + Timeout.add_seconds (86400, () => { + event_uids.clear (); + load_today_events (); + return true; + }); + } + + private void load_today_events () { + event_uids = new Gee.HashMultiMap (); + var model = new Calendar.EventStore (); + model.events_added.connect (on_events_added); + model.events_updated.connect (on_events_updated); + model.events_removed.connect (on_events_removed); + model.month_start = Calendar.Util.datetime_get_start_of_month (new DateTime.now_local ()); + } + + private void on_events_added (E.Source source, Gee.Collection events) { + var extension = (E.SourceAlarms)source.get_extension (E.SOURCE_EXTENSION_ALARMS); + if (extension.get_include_me () == false) { + return; + } + + Idle.add ( () => { + foreach (var event in events) + add_event (source, event); + + return false; + }); + } + + private void on_events_updated (E.Source source, Gee.Collection events) { + Idle.add ( () => { + foreach (var event in events) + update_event (source, event); + + return false; + }); + } + + private void on_events_removed (E.Source source, Gee.Collection events) { + Idle.add ( () => { + foreach (var event in events) + remove_event (source, event); + + return false; + }); + } + + private void add_event (E.Source source, ECal.Component event) { + unowned ICal.Component comp = event.get_icalcomponent (); + debug ("Event [%s, %s, %s]".printf (comp.get_summary (), source.dup_display_name (), comp.get_uid ())); + foreach (var alarm_uid in event.get_alarm_uids ()) { + ECal.ComponentAlarm e_alarm = event.get_alarm (alarm_uid); + ECal.ComponentAlarmAction action; + +#if E_CAL_2_0 + action = e_alarm.get_action (); +#else + e_alarm.get_action (out action); +#endif + if (action == ECal.ComponentAlarmAction.DISPLAY) { + ECal.ComponentAlarmTrigger trigger; +#if E_CAL_2_0 + trigger = e_alarm.get_trigger (); +#else + e_alarm.get_trigger (out trigger); +#endif + if (trigger.get_kind () == ECal.ComponentAlarmTriggerKind.RELATIVE_START) { + ICal.Duration duration = trigger.get_duration (); + var start_time = Calendar.Util.icaltime_to_datetime (comp.get_dtstart ()); + var now = new DateTime.now_local (); + if (now.compare (start_time) > 0) { + continue; + } + start_time = start_time.add_weeks (-(int)duration.get_weeks ()); //vala-lint=space-before-paren + start_time = start_time.add_days (-(int)duration.get_days ()); //vala-lint=space-before-paren + start_time = start_time.add_hours (-(int)duration.get_hours ()); //vala-lint=space-before-paren + start_time = start_time.add_minutes (-(int)duration.get_minutes ()); //vala-lint=space-before-paren + start_time = start_time.add_seconds (-(int)duration.get_seconds ()); //vala-lint=space-before-paren + if (start_time.get_year () == now.get_year () && start_time.get_day_of_year () == now.get_day_of_year ()) { + var time = time_until_now (start_time); + if (time >= 0) { + add_timeout.begin (source, event, (uint)time); + } + } + } + + } else { + warning ("Event [%s, %s, %s]: Unhandled alarm action: %s", comp.get_summary (), source.dup_display_name (), comp.get_uid (), action.to_string ()); + } + } + } + + public async void add_timeout (E.Source source, ECal.Component event, uint interval) { + var uid = "%u-%u".printf (interval, GLib.Random.next_int ()); + event_uids.set (event, uid); + debug ("adding timeout uid:%s", uid); + Timeout.add_seconds (interval, () => { + var extension = (E.SourceAlarms)source.get_extension (E.SOURCE_EXTENSION_ALARMS); + if (extension != null) { + extension.set_last_notified (new DateTime.now_local ().to_string ()); + } + + send_event_notification (event, uid); + return false; + }); + } + + public void send_event_notification (ECal.Component event, string uid) { + if (!(uid in event_uids[event])) { + return; + } + + unowned ICal.Component comp = event.get_icalcomponent (); + var primary_text = "%s".printf (comp.get_summary ()); + var start_time = Calendar.Util.icaltime_to_datetime (comp.get_dtstart ()); + var now = new DateTime.now_local (); + string secondary_text = ""; + var h24_settings = new GLib.Settings ("org.gnome.desktop.interface"); + var format = h24_settings.get_string ("clock-format"); + var text = Granite.DateTime.get_default_time_format (format.contains ("12h")); + if (start_time.get_year () == now.get_year ()) { + if (start_time.get_day_of_year () == now.get_day_of_year ()) { + secondary_text = Granite.DateTime.get_relative_datetime (start_time); + } else { + secondary_text = start_time.format ("%s, %s".printf (Granite.DateTime.get_default_date_format (), text)); + } + } else { + secondary_text = start_time.format ("%s, %s".printf (Granite.DateTime.get_default_date_format (false, true, true), text)); + } + + var notification = new GLib.Notification (primary_text); + notification.set_body (secondary_text); + + GLib.Application.get_default ().send_notification (uid, notification); + } + + private void update_event (E.Source source, ECal.Component event) { + remove_event (source, event); +#if !E_CAL_2_0 + event.rescan (); +#endif + event.commit_sequence (); + add_event (source, event); + } + + private void remove_event (E.Source source, ECal.Component event) { + if (event in event_uids) { + event_uids.remove_all (event); + } + } + + private TimeSpan time_until_now (GLib.DateTime dt) { + var now = new DateTime.now_local (); + return dt.difference (now) / TimeSpan.SECOND; + } +} diff --git a/src/meson.build b/src/meson.build index 8d2624b4d..03b7757de 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,6 +5,7 @@ calendar_files = files( 'ImportDialog.vala', 'LanguagesFormat.vala', 'MainWindow.vala', + 'TodayEventMonitor.vala', 'EventEdition/EventDialog.vala', 'EventEdition/GuestGrid.vala', 'EventEdition/GuestsPanel.vala',