From 7a164bf678490da821f38f7329d4f0776645411c Mon Sep 17 00:00:00 2001 From: worldofpeace Date: Wed, 22 Apr 2020 13:04:18 -0400 Subject: [PATCH] pantheon.wingpanel-indicator-datetime: patch in a lot of fixes These were rejected upstream, sadly https://github.com/elementary/wingpanel-indicator-datetime/pull/207. --- .../wingpanel-indicators/datetime/207.patch | 4726 +++++++++++++++++ .../wingpanel-indicators/datetime/default.nix | 11 +- 2 files changed, 4736 insertions(+), 1 deletion(-) create mode 100644 pkgs/desktops/pantheon/desktop/wingpanel-indicators/datetime/207.patch diff --git a/pkgs/desktops/pantheon/desktop/wingpanel-indicators/datetime/207.patch b/pkgs/desktops/pantheon/desktop/wingpanel-indicators/datetime/207.patch new file mode 100644 index 00000000000..c4d6d8574a5 --- /dev/null +++ b/pkgs/desktops/pantheon/desktop/wingpanel-indicators/datetime/207.patch @@ -0,0 +1,4726 @@ +From 20228e34bf97f67b1dd542a22e92cd90f0db5c72 Mon Sep 17 00:00:00 2001 +From: Dirli +Date: Thu, 9 Apr 2020 16:30:16 +0300 +Subject: [PATCH 1/8] added a single namespace + +--- + meson.build | 14 +- + src/DateIterator.vala | 82 ----- + src/DateRange.vala | 66 ---- + src/Indicator.vala | 341 +++++++++--------- + .../calendar => Models}/CalendarModel.vala | 4 +- + src/Services/TimeManager.vala | 230 ++++++------ + src/Util/DateIterator.vala | 84 +++++ + src/Util/DateRange.vala | 68 ++++ + src/{Widgets/calendar => Util}/Util.vala | 8 +- + src/Widgets/CalendarView.vala | 185 ++++++++++ + src/Widgets/EventRow.vala | 184 +++++----- + src/Widgets/{calendar => }/Grid.vala | 12 +- + src/Widgets/GridDay.vala | 180 +++++++++ + src/Widgets/PanelLabel.vala | 80 ++-- + src/Widgets/calendar/CalendarView.vala | 183 ---------- + src/Widgets/calendar/GridDay.vala | 178 --------- + 16 files changed, 957 insertions(+), 942 deletions(-) + delete mode 100644 src/DateIterator.vala + delete mode 100644 src/DateRange.vala + rename src/{Widgets/calendar => Models}/CalendarModel.vala (99%) + create mode 100644 src/Util/DateIterator.vala + create mode 100644 src/Util/DateRange.vala + rename src/{Widgets/calendar => Util}/Util.vala (96%) + create mode 100644 src/Widgets/CalendarView.vala + rename src/Widgets/{calendar => }/Grid.vala (95%) + create mode 100644 src/Widgets/GridDay.vala + delete mode 100644 src/Widgets/calendar/CalendarView.vala + delete mode 100644 src/Widgets/calendar/GridDay.vala + +diff --git a/meson.build b/meson.build +index 2555723..b44c5bd 100644 +--- a/meson.build ++++ b/meson.build +@@ -39,16 +39,16 @@ endif + shared_module( + meson.project_name(), + gresource, +- 'src/DateIterator.vala', +- 'src/DateRange.vala', + 'src/Indicator.vala', ++ 'src/Util/DateIterator.vala', ++ 'src/Util/DateRange.vala', ++ 'src/Util/Util.vala', ++ 'src/Models/CalendarModel.vala', ++ 'src/Widgets/CalendarView.vala', + 'src/Widgets/EventRow.vala', ++ 'src/Widgets/Grid.vala', ++ 'src/Widgets/GridDay.vala', + 'src/Widgets/PanelLabel.vala', +- 'src/Widgets/calendar/CalendarModel.vala', +- 'src/Widgets/calendar/CalendarView.vala', +- 'src/Widgets/calendar/Grid.vala', +- 'src/Widgets/calendar/GridDay.vala', +- 'src/Widgets/calendar/Util.vala', + 'src/Services/TimeManager.vala', + dependencies: [ + dependency('glib-2.0'), +diff --git a/src/DateIterator.vala b/src/DateIterator.vala +deleted file mode 100644 +index 961895b..0000000 +--- a/src/DateIterator.vala ++++ /dev/null +@@ -1,82 +0,0 @@ +-/* +- * Copyright 2011-2018 elementary, Inc. (https://elementary.io) +- * +- * 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. +- * +- * Authored by: Corentin Noël +- */ +- +-public class Util.DateIterator : Object, Gee.Traversable, Gee.Iterator { +- public GLib.DateTime current { get; construct set; } +- public Util.DateRange range { get; construct; } +- +- // Required by Gee.Iterator +- public bool valid { +- get { +- return true; +- } +- } +- +- // Required by Gee.Iterator +- public bool read_only { +- get { +- return false; +- } +- } +- +- public DateIterator (Util.DateRange range) { +- Object ( +- range: range, +- current: range.first_dt.add_days (-1) +- ); +- } +- +- public bool @foreach (Gee.ForallFunc f) { +- var element = range.first_dt; +- +- while (element.compare (range.last_dt) < 0) { +- if (f (element) == false) { +- return false; +- } +- +- element = element.add_days (1); +- } +- +- return true; +- } +- +- public bool next () { +- if (!has_next ()) { +- return false; +- } +- +- current = this.current.add_days (1); +- +- return true; +- } +- +- public bool has_next () { +- return current.compare (range.last_dt) < 0; +- } +- +- public new GLib.DateTime get () { +- return current; +- } +- +- public void remove () { +- assert_not_reached (); +- } +-} +diff --git a/src/DateRange.vala b/src/DateRange.vala +deleted file mode 100644 +index 08e4c00..0000000 +--- a/src/DateRange.vala ++++ /dev/null +@@ -1,66 +0,0 @@ +-/* +- * Copyright 2011-2019 elementary, Inc. (https://elementary.io) +- * +- * 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. +- * +- * Authored by: Corentin Noël +- */ +- +-/* Represents date range from 'first' to 'last' inclusive */ +-public class Util.DateRange : Object, Gee.Traversable, Gee.Iterable { +- public GLib.DateTime first_dt { get; construct; } +- public GLib.DateTime last_dt { get; construct; } +- +- public bool @foreach (Gee.ForallFunc f) { +- foreach (var date in this) { +- if (f (date) == false) { +- return false; +- } +- } +- +- return true; +- } +- +- public DateRange (GLib.DateTime first, GLib.DateTime last) { +- Object ( +- first_dt: first, +- last_dt: last +- ); +- } +- +- public bool equals (DateRange other) { +- return (first_dt == other.first_dt && last_dt == other.last_dt); +- } +- +- public Gee.Iterator iterator () { +- return new DateIterator (this); +- } +- +- public Gee.List to_list () { +- var list = new Gee.ArrayList ((Gee.EqualDataFunc? )datetime_equal_func); +- +- foreach (var date in this) { +- list.add (date); +- } +- +- return list; +- } +- +- /* Returns true if 'a' and 'b' are the same GLib.DateTime */ +- private bool datetime_equal_func (GLib.DateTime a, GLib.DateTime b) { +- return a.equal (b); +- } +-} +diff --git a/src/Indicator.vala b/src/Indicator.vala +index 77aa35c..b712c12 100644 +--- a/src/Indicator.vala ++++ b/src/Indicator.vala +@@ -17,218 +17,219 @@ + * Boston, MA 02110-1301 USA. + */ + +-public class DateTime.Indicator : Wingpanel.Indicator { +- public static GLib.Settings settings; +- +- private Widgets.PanelLabel panel_label; +- private Gtk.Grid main_grid; +- private Widgets.CalendarView calendar; +- private Gtk.ListBox event_listbox; +- private uint update_events_idle_source = 0; +- +- public Indicator () { +- Object ( +- code_name: Wingpanel.Indicator.DATETIME, +- display_name: _("Date & Time"), +- description: _("The date and time indicator") +- ); +- } +- +- static construct { +- settings = new GLib.Settings ("io.elementary.desktop.wingpanel.datetime"); +- } ++namespace DateTimeIndicator { ++ public class Indicator : Wingpanel.Indicator { ++ public static GLib.Settings settings; ++ ++ private Widgets.PanelLabel panel_label; ++ private Gtk.Grid main_grid; ++ private Widgets.CalendarView calendar; ++ private Gtk.ListBox event_listbox; ++ private uint update_events_idle_source = 0; ++ ++ public Indicator () { ++ Object ( ++ code_name: Wingpanel.Indicator.DATETIME, ++ display_name: _("Date & Time"), ++ description: _("The date and time indicator") ++ ); ++ } + +- construct { +- visible = true; +- } ++ static construct { ++ settings = new GLib.Settings ("io.elementary.desktop.wingpanel.datetime"); ++ } + +- public override Gtk.Widget get_display_widget () { +- if (panel_label == null) { +- panel_label = new Widgets.PanelLabel (); ++ construct { ++ visible = true; + } + +- return panel_label; +- } ++ public override Gtk.Widget get_display_widget () { ++ if (panel_label == null) { ++ panel_label = new Widgets.PanelLabel (); ++ } + +- public override Gtk.Widget? get_widget () { +- if (main_grid == null) { +- calendar = new Widgets.CalendarView (); +- calendar.margin_bottom = 6; +- +- var placeholder_label = new Gtk.Label (_("No Events on This Day")); +- placeholder_label.wrap = true; +- placeholder_label.wrap_mode = Pango.WrapMode.WORD; +- placeholder_label.margin_start = 12; +- placeholder_label.margin_end = 12; +- placeholder_label.max_width_chars = 20; +- placeholder_label.justify = Gtk.Justification.CENTER; +- placeholder_label.show_all (); +- +- var placeholder_style_context = placeholder_label.get_style_context (); +- placeholder_style_context.add_class (Gtk.STYLE_CLASS_DIM_LABEL); +- placeholder_style_context.add_class (Granite.STYLE_CLASS_H3_LABEL); +- +- event_listbox = new Gtk.ListBox (); +- event_listbox.selection_mode = Gtk.SelectionMode.NONE; +- event_listbox.set_header_func (header_update_func); +- event_listbox.set_placeholder (placeholder_label); +- event_listbox.set_sort_func (sort_function); +- +- var scrolled_window = new Gtk.ScrolledWindow (null, null); +- scrolled_window.hscrollbar_policy = Gtk.PolicyType.NEVER; +- scrolled_window.add (event_listbox); +- +- var settings_button = new Gtk.ModelButton (); +- settings_button.text = _("Date & Time Settings…"); +- +- main_grid = new Gtk.Grid (); +- main_grid.margin_top = 12; +- main_grid.attach (calendar, 0, 0); +- main_grid.attach (new Gtk.Separator (Gtk.Orientation.VERTICAL), 1, 0); +- main_grid.attach (scrolled_window, 2, 0); +- main_grid.attach (new Wingpanel.Widgets.Separator (), 0, 2, 3); +- main_grid.attach (settings_button, 0, 3, 3); +- +- var size_group = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL); +- size_group.add_widget (calendar); +- size_group.add_widget (event_listbox); +- +- calendar.day_double_click.connect (() => { +- close (); +- }); ++ return panel_label; ++ } + +- calendar.selection_changed.connect ((date) => { +- idle_update_events (); +- }); ++ public override Gtk.Widget? get_widget () { ++ if (main_grid == null) { ++ calendar = new Widgets.CalendarView (); ++ calendar.margin_bottom = 6; ++ ++ var placeholder_label = new Gtk.Label (_("No Events on This Day")); ++ placeholder_label.wrap = true; ++ placeholder_label.wrap_mode = Pango.WrapMode.WORD; ++ placeholder_label.margin_start = 12; ++ placeholder_label.margin_end = 12; ++ placeholder_label.max_width_chars = 20; ++ placeholder_label.justify = Gtk.Justification.CENTER; ++ placeholder_label.show_all (); ++ ++ var placeholder_style_context = placeholder_label.get_style_context (); ++ placeholder_style_context.add_class (Gtk.STYLE_CLASS_DIM_LABEL); ++ placeholder_style_context.add_class (Granite.STYLE_CLASS_H3_LABEL); ++ ++ event_listbox = new Gtk.ListBox (); ++ event_listbox.selection_mode = Gtk.SelectionMode.NONE; ++ event_listbox.set_header_func (header_update_func); ++ event_listbox.set_placeholder (placeholder_label); ++ event_listbox.set_sort_func (sort_function); ++ ++ var scrolled_window = new Gtk.ScrolledWindow (null, null); ++ scrolled_window.hscrollbar_policy = Gtk.PolicyType.NEVER; ++ scrolled_window.add (event_listbox); ++ ++ var settings_button = new Gtk.ModelButton (); ++ settings_button.text = _("Date & Time Settings…"); ++ ++ main_grid = new Gtk.Grid (); ++ main_grid.margin_top = 12; ++ main_grid.attach (calendar, 0, 0); ++ main_grid.attach (new Gtk.Separator (Gtk.Orientation.VERTICAL), 1, 0); ++ main_grid.attach (scrolled_window, 2, 0); ++ main_grid.attach (new Wingpanel.Widgets.Separator (), 0, 2, 3); ++ main_grid.attach (settings_button, 0, 3, 3); ++ ++ var size_group = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL); ++ size_group.add_widget (calendar); ++ size_group.add_widget (event_listbox); ++ ++ calendar.day_double_click.connect (() => { ++ close (); ++ }); ++ ++ calendar.selection_changed.connect ((date) => { ++ idle_update_events (); ++ }); ++ ++ event_listbox.row_activated.connect ((row) => { ++ calendar.show_date_in_maya (((EventRow) row).date); ++ close (); ++ }); ++ ++ settings_button.clicked.connect (() => { ++ try { ++ AppInfo.launch_default_for_uri ("settings://time", null); ++ } catch (Error e) { ++ warning ("Failed to open time and date settings: %s", e.message); ++ } ++ }); ++ } + +- event_listbox.row_activated.connect ((row) => { +- calendar.show_date_in_maya (((DateTime.EventRow) row).date); +- close (); +- }); ++ return main_grid; ++ } + +- settings_button.clicked.connect (() => { +- try { +- AppInfo.launch_default_for_uri ("settings://time", null); +- } catch (Error e) { +- warning ("Failed to open time and date settings: %s", e.message); ++ private void header_update_func (Gtk.ListBoxRow lbrow, Gtk.ListBoxRow? lbbefore) { ++ var row = (EventRow) lbrow; ++ if (lbbefore != null) { ++ var before = (EventRow) lbbefore; ++ if (row.is_allday == before.is_allday) { ++ row.set_header (null); ++ return; + } +- }); +- } + +- return main_grid; +- } ++ if (row.is_allday != before.is_allday) { ++ var header_label = new Granite.HeaderLabel (_("During the Day")); ++ header_label.margin_start = header_label.margin_end = 6; + +- private void header_update_func (Gtk.ListBoxRow lbrow, Gtk.ListBoxRow? lbbefore) { +- var row = (DateTime.EventRow) lbrow; +- if (lbbefore != null) { +- var before = (DateTime.EventRow) lbbefore; +- if (row.is_allday == before.is_allday) { +- row.set_header (null); ++ row.set_header (header_label); ++ return; ++ } ++ } else { ++ if (row.is_allday) { ++ var allday_header = new Granite.HeaderLabel (_("All Day")); ++ allday_header.margin_start = allday_header.margin_end = 6; ++ ++ row.set_header (allday_header); ++ } + return; + } ++ } + +- if (row.is_allday != before.is_allday) { +- var header_label = new Granite.HeaderLabel (_("During the Day")); +- header_label.margin_start = header_label.margin_end = 6; ++ [CCode (instance_pos = -1)] ++ private int sort_function (Gtk.ListBoxRow child1, Gtk.ListBoxRow child2) { ++ var e1 = (EventRow) child1; ++ var e2 = (EventRow) child2; + +- row.set_header (header_label); +- return; ++ if (e1.start_time.compare (e2.start_time) != 0) { ++ return e1.start_time.compare (e2.start_time); + } +- } else { +- if (row.is_allday) { +- var allday_header = new Granite.HeaderLabel (_("All Day")); +- allday_header.margin_start = allday_header.margin_end = 6; + +- row.set_header (allday_header); ++ // If they have the same date, sort them wholeday first ++ if (e1.is_allday) { ++ return -1; ++ } else if (e2.is_allday) { ++ return 1; + } +- return; +- } +- } +- +- [CCode (instance_pos = -1)] +- private int sort_function (Gtk.ListBoxRow child1, Gtk.ListBoxRow child2) { +- var e1 = (EventRow) child1; +- var e2 = (EventRow) child2; + +- if (e1.start_time.compare (e2.start_time) != 0) { +- return e1.start_time.compare (e2.start_time); ++ return 0; + } + +- // If they have the same date, sort them wholeday first +- if (e1.is_allday) { +- return -1; +- } else if (e2.is_allday) { +- return 1; ++ private void update_events_model (E.Source source, Gee.Collection events) { ++ idle_update_events (); + } + +- return 0; +- } +- +- private void update_events_model (E.Source source, Gee.Collection events) { +- idle_update_events (); +- } ++ private void idle_update_events () { ++ if (update_events_idle_source > 0) { ++ GLib.Source.remove (update_events_idle_source); ++ } + +- private void idle_update_events () { +- if (update_events_idle_source > 0) { +- GLib.Source.remove (update_events_idle_source); ++ update_events_idle_source = GLib.Idle.add (update_events); + } + +- update_events_idle_source = GLib.Idle.add (update_events); +- } +- +- private bool update_events () { +- foreach (unowned Gtk.Widget widget in event_listbox.get_children ()) { +- widget.destroy (); +- } ++ private bool update_events () { ++ foreach (unowned Gtk.Widget widget in event_listbox.get_children ()) { ++ widget.destroy (); ++ } + +- if (calendar.selected_date == null) { +- update_events_idle_source = 0; +- return GLib.Source.REMOVE; +- } ++ if (calendar.selected_date == null) { ++ update_events_idle_source = 0; ++ return GLib.Source.REMOVE; ++ } + +- var date = calendar.selected_date; ++ var date = calendar.selected_date; + +- var model = Widgets.CalendarModel.get_default (); ++ var model = Models.CalendarModel.get_default (); + +- var events_on_day = new Gee.TreeMap (); ++ var events_on_day = new Gee.TreeMap (); + +- model.source_events.@foreach ((source, component_map) => { +- foreach (var comp in component_map.get_values ()) { +- if (Util.calcomp_is_on_day (comp, date)) { +- unowned ICal.Component ical = comp.get_icalcomponent (); +- var event_uid = ical.get_uid (); +- if (!events_on_day.has_key (event_uid)) { +- events_on_day[event_uid] = new DateTime.EventRow (date, ical, source); ++ model.source_events.@foreach ((source, component_map) => { ++ foreach (var comp in component_map.get_values ()) { ++ if (Util.calcomp_is_on_day (comp, date)) { ++ unowned ICal.Component ical = comp.get_icalcomponent (); ++ var event_uid = ical.get_uid (); ++ if (!events_on_day.has_key (event_uid)) { ++ events_on_day[event_uid] = new EventRow (date, ical, source); + +- event_listbox.add (events_on_day[event_uid]); ++ event_listbox.add (events_on_day[event_uid]); ++ } + } + } +- } +- }); ++ }); + +- event_listbox.show_all (); +- update_events_idle_source = 0; +- return GLib.Source.REMOVE; +- } ++ event_listbox.show_all (); ++ update_events_idle_source = 0; ++ return GLib.Source.REMOVE; ++ } + +- public override void opened () { +- calendar.show_today (); ++ public override void opened () { ++ calendar.show_today (); + +- Widgets.CalendarModel.get_default ().events_added.connect (update_events_model); +- Widgets.CalendarModel.get_default ().events_updated.connect (update_events_model); +- Widgets.CalendarModel.get_default ().events_removed.connect (update_events_model); +- } ++ Models.CalendarModel.get_default ().events_added.connect (update_events_model); ++ Models.CalendarModel.get_default ().events_updated.connect (update_events_model); ++ Models.CalendarModel.get_default ().events_removed.connect (update_events_model); ++ } + +- public override void closed () { +- Widgets.CalendarModel.get_default ().events_added.disconnect (update_events_model); +- Widgets.CalendarModel.get_default ().events_updated.disconnect (update_events_model); +- Widgets.CalendarModel.get_default ().events_removed.disconnect (update_events_model); ++ public override void closed () { ++ Models.CalendarModel.get_default ().events_added.disconnect (update_events_model); ++ Models.CalendarModel.get_default ().events_updated.disconnect (update_events_model); ++ Models.CalendarModel.get_default ().events_removed.disconnect (update_events_model); ++ } + } + } +- + public Wingpanel.Indicator get_indicator (Module module) { + debug ("Activating DateTime Indicator"); +- var indicator = new DateTime.Indicator (); ++ var indicator = new DateTimeIndicator.Indicator (); + + return indicator; + } +diff --git a/src/Widgets/calendar/CalendarModel.vala b/src/Models/CalendarModel.vala +similarity index 99% +rename from src/Widgets/calendar/CalendarModel.vala +rename to src/Models/CalendarModel.vala +index 7602303..965b93e 100644 +--- a/src/Widgets/calendar/CalendarModel.vala ++++ b/src/Models/CalendarModel.vala +@@ -15,8 +15,8 @@ + * along with this program. If not, see . + */ + +-namespace DateTime.Widgets { +- public class CalendarModel : Object { ++namespace DateTimeIndicator { ++ public class Models.CalendarModel : Object { + /* The data_range is the range of dates for which this model is storing + * data. + * +diff --git a/src/Services/TimeManager.vala b/src/Services/TimeManager.vala +index b68f158..5baa136 100644 +--- a/src/Services/TimeManager.vala ++++ b/src/Services/TimeManager.vala +@@ -32,153 +32,155 @@ interface FDO.Accounts : Object { + public abstract string find_user_by_name (string username) throws GLib.Error; + } + +-public class DateTime.Services.TimeManager : Gtk.Calendar { +- private static TimeManager? instance = null; ++namespace DateTimeIndicator { ++ public class Services.TimeManager : Gtk.Calendar { ++ private static TimeManager? instance = null; + +- public signal void minute_changed (); ++ public signal void minute_changed (); + +- private GLib.DateTime? current_time = null; +- private uint timeout_id = 0; +- private Manager? manager = null; ++ private GLib.DateTime? current_time = null; ++ private uint timeout_id = 0; ++ private Manager? manager = null; + +- public bool clock_show_seconds { get; set; } +- public bool is_12h { get; set; } ++ public bool clock_show_seconds { get; set; } ++ public bool is_12h { get; set; } + +- public TimeManager () { +- update_current_time (); +- +- if (current_time == null) { +- return; +- } ++ public TimeManager () { ++ update_current_time (); + +- add_timeout (); +- try { +- var clock_settings = new GLib.Settings ("io.elementary.desktop.wingpanel.datetime"); +- clock_settings.bind ("clock-show-seconds", this, "clock-show-seconds", SettingsBindFlags.DEFAULT); ++ if (current_time == null) { ++ return; ++ } + +- notify["clock-show-seconds"].connect (() => { +- add_timeout (); +- }); ++ add_timeout (); ++ try { ++ var clock_settings = new GLib.Settings ("io.elementary.desktop.wingpanel.datetime"); ++ clock_settings.bind ("clock-show-seconds", this, "clock-show-seconds", SettingsBindFlags.DEFAULT); + +- // Listen for the D-BUS server that controls time settings +- Bus.watch_name (BusType.SYSTEM, "org.freedesktop.timedate1", BusNameWatcherFlags.NONE, on_watch, on_unwatch); +- // Listen for the signal that is fired when waking up from sleep, then update time +- manager = Bus.get_proxy_sync (BusType.SYSTEM, "org.freedesktop.login1", "/org/freedesktop/login1"); +- manager.prepare_for_sleep.connect ((sleeping) => { +- if (!sleeping) { +- update_current_time (); +- minute_changed (); ++ notify["clock-show-seconds"].connect (() => { + add_timeout (); +- } +- }); +- } catch (Error e) { +- warning (e.message); ++ }); ++ ++ // Listen for the D-BUS server that controls time settings ++ Bus.watch_name (BusType.SYSTEM, "org.freedesktop.timedate1", BusNameWatcherFlags.NONE, on_watch, on_unwatch); ++ // Listen for the signal that is fired when waking up from sleep, then update time ++ manager = Bus.get_proxy_sync (BusType.SYSTEM, "org.freedesktop.login1", "/org/freedesktop/login1"); ++ manager.prepare_for_sleep.connect ((sleeping) => { ++ if (!sleeping) { ++ update_current_time (); ++ minute_changed (); ++ add_timeout (); ++ } ++ }); ++ } catch (Error e) { ++ warning (e.message); ++ } + } +- } +- +- construct { +- setup_time_format.begin (); +- } + +- private async void setup_time_format () { +- try { +- var accounts_service = yield GLib.Bus.get_proxy (GLib.BusType.SYSTEM, +- "org.freedesktop.Accounts", +- "/org/freedesktop/Accounts"); +- var user_path = accounts_service.find_user_by_name (GLib.Environment.get_user_name ()); +- +- var greeter_act = yield GLib.Bus.get_proxy (GLib.BusType.SYSTEM, +- "org.freedesktop.Accounts", +- user_path, +- GLib.DBusProxyFlags.GET_INVALIDATED_PROPERTIES); +- is_12h = ("12h" in greeter_act.time_format); +- ((GLib.DBusProxy) greeter_act).g_properties_changed.connect ((changed_properties, invalidated_properties) => { +- if (changed_properties.lookup_value ("TimeFormat", GLib.VariantType.STRING) != null) { +- is_12h = ("12h" in greeter_act.time_format); +- } +- }); +- } catch (Error e) { +- critical (e.message); +- // Connect to the GSettings instead +- var clock_settings = new GLib.Settings ("org.gnome.desktop.interface"); +- clock_settings.changed["clock-format"].connect (() => { +- is_12h = ("12h" in clock_settings.get_string ("clock-format")); +- }); +- +- is_12h = ("12h" in clock_settings.get_string ("clock-format")); ++ construct { ++ setup_time_format.begin (); + } +- } + +- private void on_watch (DBusConnection conn) { +- // Start updating the time display quicker because someone is changing settings +- add_timeout (true); +- } ++ private async void setup_time_format () { ++ try { ++ var accounts_service = yield GLib.Bus.get_proxy (GLib.BusType.SYSTEM, ++ "org.freedesktop.Accounts", ++ "/org/freedesktop/Accounts"); ++ var user_path = accounts_service.find_user_by_name (GLib.Environment.get_user_name ()); ++ ++ var greeter_act = yield GLib.Bus.get_proxy (GLib.BusType.SYSTEM, ++ "org.freedesktop.Accounts", ++ user_path, ++ GLib.DBusProxyFlags.GET_INVALIDATED_PROPERTIES); ++ is_12h = ("12h" in greeter_act.time_format); ++ ((GLib.DBusProxy) greeter_act).g_properties_changed.connect ((changed_properties, invalidated_properties) => { ++ if (changed_properties.lookup_value ("TimeFormat", GLib.VariantType.STRING) != null) { ++ is_12h = ("12h" in greeter_act.time_format); ++ } ++ }); ++ } catch (Error e) { ++ critical (e.message); ++ // Connect to the GSettings instead ++ var clock_settings = new GLib.Settings ("org.gnome.desktop.interface"); ++ clock_settings.changed["clock-format"].connect (() => { ++ is_12h = ("12h" in clock_settings.get_string ("clock-format")); ++ }); + +- private void on_unwatch (DBusConnection conn) { +- // Stop updating the time display quicker +- add_timeout (false); +- } ++ is_12h = ("12h" in clock_settings.get_string ("clock-format")); ++ } ++ } + +- private void add_timeout (bool update_fast = false) { +- uint interval; +- if (update_fast || clock_show_seconds) { +- interval = 500; +- } else { +- interval = calculate_time_until_next_minute (); ++ private void on_watch (DBusConnection conn) { ++ // Start updating the time display quicker because someone is changing settings ++ add_timeout (true); + } + +- if (timeout_id > 0) { +- Source.remove (timeout_id); ++ private void on_unwatch (DBusConnection conn) { ++ // Stop updating the time display quicker ++ add_timeout (false); + } + +- timeout_id = Timeout.add (interval, () => { +- update_current_time (); +- minute_changed (); +- add_timeout (update_fast); ++ private void add_timeout (bool update_fast = false) { ++ uint interval; ++ if (update_fast || clock_show_seconds) { ++ interval = 500; ++ } else { ++ interval = calculate_time_until_next_minute (); ++ } + +- return false; +- }); +- } ++ if (timeout_id > 0) { ++ Source.remove (timeout_id); ++ } + +- public string format (string format) { +- if (current_time == null) { +- return "undef"; ++ timeout_id = Timeout.add (interval, () => { ++ update_current_time (); ++ minute_changed (); ++ add_timeout (update_fast); ++ ++ return false; ++ }); + } + +- return current_time.format (format); +- } ++ public string format (string format) { ++ if (current_time == null) { ++ return "undef"; ++ } + +- public GLib.DateTime get_current_time () { +- return current_time; +- } ++ return current_time.format (format); ++ } + +- private void update_current_time () { +- var local_time = new GLib.DateTime.now_local (); ++ public GLib.DateTime get_current_time () { ++ return current_time; ++ } + +- if (local_time == null) { +- critical ("Can't get the local time."); ++ private void update_current_time () { ++ var local_time = new GLib.DateTime.now_local (); + +- return; +- } ++ if (local_time == null) { ++ critical ("Can't get the local time."); + +- current_time = local_time; +- } ++ return; ++ } + +- private uint calculate_time_until_next_minute () { +- if (current_time == null) { +- return 60 * 1000; ++ current_time = local_time; + } + +- var seconds_until_next_minute = 60 - (current_time.to_unix () % 60); ++ private uint calculate_time_until_next_minute () { ++ if (current_time == null) { ++ return 60 * 1000; ++ } + +- return (uint)seconds_until_next_minute * 1000; +- } ++ var seconds_until_next_minute = 60 - (current_time.to_unix () % 60); + +- public static TimeManager get_default () { +- if (instance == null) { +- instance = new TimeManager (); ++ return (uint)seconds_until_next_minute * 1000; + } + +- return instance; ++ public static TimeManager get_default () { ++ if (instance == null) { ++ instance = new TimeManager (); ++ } ++ ++ return instance; ++ } + } + } +diff --git a/src/Util/DateIterator.vala b/src/Util/DateIterator.vala +new file mode 100644 +index 0000000..c2c771e +--- /dev/null ++++ b/src/Util/DateIterator.vala +@@ -0,0 +1,84 @@ ++/* ++ * Copyright 2011-2018 elementary, Inc. (https://elementary.io) ++ * ++ * 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. ++ * ++ * Authored by: Corentin Noël ++ */ ++ ++namespace DateTimeIndicator { ++ public class Util.DateIterator : Object, Gee.Traversable, Gee.Iterator { ++ public GLib.DateTime current { get; construct set; } ++ public Util.DateRange range { get; construct; } ++ ++ // Required by Gee.Iterator ++ public bool valid { ++ get { ++ return true; ++ } ++ } ++ ++ // Required by Gee.Iterator ++ public bool read_only { ++ get { ++ return false; ++ } ++ } ++ ++ public DateIterator (Util.DateRange range) { ++ Object ( ++ range: range, ++ current: range.first_dt.add_days (-1) ++ ); ++ } ++ ++ public bool @foreach (Gee.ForallFunc f) { ++ var element = range.first_dt; ++ ++ while (element.compare (range.last_dt) < 0) { ++ if (f (element) == false) { ++ return false; ++ } ++ ++ element = element.add_days (1); ++ } ++ ++ return true; ++ } ++ ++ public bool next () { ++ if (!has_next ()) { ++ return false; ++ } ++ ++ current = this.current.add_days (1); ++ ++ return true; ++ } ++ ++ public bool has_next () { ++ return current.compare (range.last_dt) < 0; ++ } ++ ++ public new GLib.DateTime get () { ++ return current; ++ } ++ ++ public void remove () { ++ assert_not_reached (); ++ } ++ } ++} +diff --git a/src/Util/DateRange.vala b/src/Util/DateRange.vala +new file mode 100644 +index 0000000..82da7c8 +--- /dev/null ++++ b/src/Util/DateRange.vala +@@ -0,0 +1,68 @@ ++/* ++ * Copyright 2011-2019 elementary, Inc. (https://elementary.io) ++ * ++ * 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. ++ * ++ * Authored by: Corentin Noël ++ */ ++ ++namespace DateTimeIndicator { ++/* Represents date range from 'first' to 'last' inclusive */ ++ public class Util.DateRange : Object, Gee.Traversable, Gee.Iterable { ++ public GLib.DateTime first_dt { get; construct; } ++ public GLib.DateTime last_dt { get; construct; } ++ ++ public bool @foreach (Gee.ForallFunc f) { ++ foreach (var date in this) { ++ if (f (date) == false) { ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ ++ public DateRange (GLib.DateTime first, GLib.DateTime last) { ++ Object ( ++ first_dt: first, ++ last_dt: last ++ ); ++ } ++ ++ public bool equals (DateRange other) { ++ return (first_dt == other.first_dt && last_dt == other.last_dt); ++ } ++ ++ public Gee.Iterator iterator () { ++ return new DateIterator (this); ++ } ++ ++ public Gee.List to_list () { ++ var list = new Gee.ArrayList ((Gee.EqualDataFunc? )datetime_equal_func); ++ ++ foreach (var date in this) { ++ list.add (date); ++ } ++ ++ return list; ++ } ++ ++ /* Returns true if 'a' and 'b' are the same GLib.DateTime */ ++ private bool datetime_equal_func (GLib.DateTime a, GLib.DateTime b) { ++ return a.equal (b); ++ } ++ } ++} +diff --git a/src/Widgets/calendar/Util.vala b/src/Util/Util.vala +similarity index 96% +rename from src/Widgets/calendar/Util.vala +rename to src/Util/Util.vala +index e51f784..c261f4b 100644 +--- a/src/Widgets/calendar/Util.vala ++++ b/src/Util/Util.vala +@@ -19,7 +19,7 @@ + * Authored by: Corentin Noël + */ + +-namespace Util { ++namespace DateTimeIndicator.Util { + static bool has_scrolled = false; + + public bool on_scroll_event (Gdk.EventScroll event) { +@@ -35,7 +35,7 @@ namespace Util { + + /* It's mouse scroll ! */ + if (choice == 1 || choice == -1) { +- DateTime.Widgets.CalendarModel.get_default ().change_month ((int)choice); ++ Models.CalendarModel.get_default ().change_month ((int)choice); + + return true; + } +@@ -46,14 +46,14 @@ namespace Util { + + if (choice > 0.3) { + reset_timer.begin (); +- DateTime.Widgets.CalendarModel.get_default ().change_month (1); ++ Models.CalendarModel.get_default ().change_month (1); + + return true; + } + + if (choice < -0.3) { + reset_timer.begin (); +- DateTime.Widgets.CalendarModel.get_default ().change_month (-1); ++ Models.CalendarModel.get_default ().change_month (-1); + + return true; + } +diff --git a/src/Widgets/CalendarView.vala b/src/Widgets/CalendarView.vala +new file mode 100644 +index 0000000..65cee28 +--- /dev/null ++++ b/src/Widgets/CalendarView.vala +@@ -0,0 +1,185 @@ ++/*- ++ * Copyright (c) 2011–2018 elementary, Inc. (https://elementary.io) ++ * ++ * 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 . ++ * ++ * Authored by: Maxwell Barvian ++ * Corentin Noël ++ */ ++ ++namespace DateTimeIndicator { ++ public class Widgets.CalendarView : Gtk.Grid { ++ public signal void day_double_click (); ++ public signal void event_updates (); ++ public signal void selection_changed (GLib.DateTime? new_date); ++ ++ public GLib.DateTime? selected_date { get; private set; } ++ ++ private Widgets.Grid grid; ++ private Gtk.Stack stack; ++ private Gtk.Grid big_grid; ++ ++ construct { ++ var label = new Gtk.Label (new GLib.DateTime.now_local ().format (_("%OB, %Y"))); ++ label.hexpand = true; ++ label.margin_start = 6; ++ label.xalign = 0; ++ label.width_chars = 13; ++ ++ var provider = new Gtk.CssProvider (); ++ provider.load_from_resource ("/io/elementary/desktop/wingpanel/datetime/ControlHeader.css"); ++ ++ var label_style_context = label.get_style_context (); ++ label_style_context.add_class ("header-label"); ++ label_style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ ++ var left_button = new Gtk.Button.from_icon_name ("pan-start-symbolic"); ++ var center_button = new Gtk.Button.from_icon_name ("office-calendar-symbolic"); ++ center_button.tooltip_text = _("Go to today's date"); ++ var right_button = new Gtk.Button.from_icon_name ("pan-end-symbolic"); ++ ++ var box_buttons = new Gtk.Grid (); ++ box_buttons.margin_end = 6; ++ box_buttons.valign = Gtk.Align.CENTER; ++ box_buttons.get_style_context ().add_class (Gtk.STYLE_CLASS_LINKED); ++ box_buttons.add (left_button); ++ box_buttons.add (center_button); ++ box_buttons.add (right_button); ++ ++ big_grid = create_big_grid (); ++ ++ stack = new Gtk.Stack (); ++ stack.add (big_grid); ++ stack.show_all (); ++ stack.expand = true; ++ ++ stack.notify["transition-running"].connect (() => { ++ if (stack.transition_running == false) { ++ stack.get_children ().foreach ((child) => { ++ if (child != stack.visible_child) { ++ child.destroy (); ++ } ++ }); ++ } ++ }); ++ ++ column_spacing = 6; ++ row_spacing = 6; ++ margin_start = margin_end = 10; ++ attach (label, 0, 0); ++ attach (box_buttons, 1, 0); ++ attach (stack, 0, 1, 2); ++ ++ var model = Models.CalendarModel.get_default (); ++ model.notify["data-range"].connect (() => { ++ label.label = model.month_start.format (_("%OB, %Y")); ++ ++ sync_with_model (); ++ ++ selected_date = null; ++ selection_changed (selected_date); ++ }); ++ ++ left_button.clicked.connect (() => { ++ model.change_month (-1); ++ }); ++ ++ right_button.clicked.connect (() => { ++ model.change_month (1); ++ }); ++ ++ center_button.clicked.connect (() => { ++ show_today (); ++ }); ++ } ++ ++ private Gtk.Grid create_big_grid () { ++ grid = new Widgets.Grid (); ++ grid.show_all (); ++ ++ grid.on_event_add.connect ((date) => { ++ show_date_in_maya (date); ++ day_double_click (); ++ }); ++ ++ grid.selection_changed.connect ((date) => { ++ selected_date = date; ++ selection_changed (date); ++ }); ++ ++ return grid; ++ } ++ ++ public void show_today () { ++ var calmodel = Models.CalendarModel.get_default (); ++ var today = Util.strip_time (new GLib.DateTime.now_local ()); ++ var start = Util.get_start_of_month (today); ++ selected_date = today; ++ if (!start.equal (calmodel.month_start)) { ++ calmodel.month_start = start; ++ } ++ sync_with_model (); ++ ++ grid.set_focus_to_today (); ++ } ++ ++ // TODO: As far as maya supports it use the Dbus Activation feature to run the calendar-app. ++ public void show_date_in_maya (GLib.DateTime date) { ++ var command = "io.elementary.calendar --show-day %s".printf (date.format ("%F")); ++ ++ try { ++ var appinfo = AppInfo.create_from_commandline (command, null, AppInfoCreateFlags.NONE); ++ appinfo.launch_uris (null, null); ++ } catch (GLib.Error e) { ++ var dialog = new Granite.MessageDialog.with_image_from_icon_name ( ++ _("Unable To Launch Calendar"), ++ _("The program \"io.elementary.calendar\" may not be installed"), ++ "dialog-error" ++ ); ++ dialog.show_error_details (e.message); ++ dialog.run (); ++ dialog.destroy (); ++ } ++ } ++ ++ /* Sets the calendar widgets to the date range of the model */ ++ private void sync_with_model () { ++ var model = Models.CalendarModel.get_default (); ++ if (grid.grid_range != null && (model.data_range.equals (grid.grid_range) || grid.grid_range.first_dt.compare (model.data_range.first_dt) == 0)) { ++ grid.update_today (); ++ return; // nothing else to do ++ } ++ ++ GLib.DateTime previous_first = null; ++ if (grid.grid_range != null) ++ previous_first = grid.grid_range.first_dt; ++ ++ big_grid = create_big_grid (); ++ stack.add (big_grid); ++ ++ grid.set_range (model.data_range, model.month_start); ++ grid.update_weeks (model.data_range.first_dt, model.num_weeks); ++ ++ if (previous_first != null) { ++ if (previous_first.compare (grid.grid_range.first_dt) == -1) { ++ stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT; ++ } else { ++ stack.transition_type = Gtk.StackTransitionType.SLIDE_RIGHT; ++ } ++ } ++ ++ stack.set_visible_child (big_grid); ++ } ++ } ++} +diff --git a/src/Widgets/EventRow.vala b/src/Widgets/EventRow.vala +index 8e0513e..1268311 100644 +--- a/src/Widgets/EventRow.vala ++++ b/src/Widgets/EventRow.vala +@@ -17,104 +17,106 @@ + * Boston, MA 02110-1301 USA. + */ + +-public class DateTime.EventRow : Gtk.ListBoxRow { +- public GLib.DateTime date { get; construct; } +- public unowned ICal.Component component { get; construct; } +- public unowned E.SourceCalendar cal { get; construct; } +- +- public GLib.DateTime start_time { get; private set; } +- public GLib.DateTime? end_time { get; private set; } +- public bool is_allday { get; private set; default = false; } +- +- private static Services.TimeManager time_manager; +- private static Gtk.CssProvider css_provider; +- +- private Gtk.Grid grid; +- private Gtk.Image event_image; +- private Gtk.Label time_label; +- +- public EventRow (GLib.DateTime date, ICal.Component component, E.Source source) { +- Object ( +- component: component, +- date: date, +- cal: (E.SourceCalendar?) source.get_extension (E.SOURCE_EXTENSION_CALENDAR) +- ); +- } +- +- static construct { +- css_provider = new Gtk.CssProvider (); +- css_provider.load_from_resource ("/io/elementary/desktop/wingpanel/datetime/EventRow.css"); +- +- time_manager = Services.TimeManager.get_default (); +- } ++namespace DateTimeIndicator { ++ public class EventRow : Gtk.ListBoxRow { ++ public GLib.DateTime date { get; construct; } ++ public unowned ICal.Component component { get; construct; } ++ public unowned E.SourceCalendar cal { get; construct; } ++ ++ public GLib.DateTime start_time { get; private set; } ++ public GLib.DateTime? end_time { get; private set; } ++ public bool is_allday { get; private set; default = false; } ++ ++ private static Services.TimeManager time_manager; ++ private static Gtk.CssProvider css_provider; ++ ++ private Gtk.Grid grid; ++ private Gtk.Image event_image; ++ private Gtk.Label time_label; ++ ++ public EventRow (GLib.DateTime date, ICal.Component component, E.Source source) { ++ Object ( ++ component: component, ++ date: date, ++ cal: (E.SourceCalendar?) source.get_extension (E.SOURCE_EXTENSION_CALENDAR) ++ ); ++ } + +- construct { +- start_time = Util.ical_to_date_time (component.get_dtstart ()); +- end_time = Util.ical_to_date_time (component.get_dtend ()); ++ static construct { ++ css_provider = new Gtk.CssProvider (); ++ css_provider.load_from_resource ("/io/elementary/desktop/wingpanel/datetime/EventRow.css"); + +- if (end_time != null && Util.is_the_all_day (start_time, end_time)) { +- is_allday = true; ++ time_manager = Services.TimeManager.get_default (); + } + +- unowned string icon_name = "office-calendar-symbolic"; +- if (end_time == null) { +- icon_name = "alarm-symbolic"; ++ construct { ++ start_time = Util.ical_to_date_time (component.get_dtstart ()); ++ end_time = Util.ical_to_date_time (component.get_dtend ()); ++ ++ if (end_time != null && Util.is_the_all_day (start_time, end_time)) { ++ is_allday = true; ++ } ++ ++ unowned string icon_name = "office-calendar-symbolic"; ++ if (end_time == null) { ++ icon_name = "alarm-symbolic"; ++ } ++ ++ event_image = new Gtk.Image.from_icon_name (icon_name, Gtk.IconSize.MENU); ++ event_image.valign = Gtk.Align.START; ++ ++ unowned Gtk.StyleContext event_image_context = event_image.get_style_context (); ++ event_image_context.add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ ++ var name_label = new Gtk.Label (component.get_summary ()); ++ name_label.hexpand = true; ++ name_label.ellipsize = Pango.EllipsizeMode.END; ++ name_label.lines = 3; ++ name_label.max_width_chars = 30; ++ name_label.wrap = true; ++ name_label.wrap_mode = Pango.WrapMode.WORD_CHAR; ++ name_label.xalign = 0; ++ ++ unowned Gtk.StyleContext name_label_context = name_label.get_style_context (); ++ name_label_context.add_class ("title"); ++ name_label_context.add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ ++ time_label = new Gtk.Label (null); ++ time_label.use_markup = true; ++ time_label.xalign = 0; ++ time_label.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); ++ ++ grid = new Gtk.Grid (); ++ grid.column_spacing = 6; ++ grid.margin = 3; ++ grid.margin_start = grid.margin_end = 6; ++ grid.attach (event_image, 0, 0); ++ grid.attach (name_label, 1, 0); ++ if (!is_allday) { ++ grid.attach (time_label, 1, 1); ++ } ++ ++ unowned Gtk.StyleContext grid_context = grid.get_style_context (); ++ grid_context.add_class ("event"); ++ grid_context.add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ ++ add (grid); ++ ++ set_color (); ++ cal.notify["color"].connect (set_color); ++ ++ update_timelabel (); ++ time_manager.notify["is-12h"].connect (update_timelabel); + } + +- event_image = new Gtk.Image.from_icon_name (icon_name, Gtk.IconSize.MENU); +- event_image.valign = Gtk.Align.START; +- +- unowned Gtk.StyleContext event_image_context = event_image.get_style_context (); +- event_image_context.add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); +- +- var name_label = new Gtk.Label (component.get_summary ()); +- name_label.hexpand = true; +- name_label.ellipsize = Pango.EllipsizeMode.END; +- name_label.lines = 3; +- name_label.max_width_chars = 30; +- name_label.wrap = true; +- name_label.wrap_mode = Pango.WrapMode.WORD_CHAR; +- name_label.xalign = 0; +- +- unowned Gtk.StyleContext name_label_context = name_label.get_style_context (); +- name_label_context.add_class ("title"); +- name_label_context.add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); +- +- time_label = new Gtk.Label (null); +- time_label.use_markup = true; +- time_label.xalign = 0; +- time_label.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); +- +- grid = new Gtk.Grid (); +- grid.column_spacing = 6; +- grid.margin = 3; +- grid.margin_start = grid.margin_end = 6; +- grid.attach (event_image, 0, 0); +- grid.attach (name_label, 1, 0); +- if (!is_allday) { +- grid.attach (time_label, 1, 1); ++ private void update_timelabel () { ++ var time_format = Granite.DateTime.get_default_time_format (time_manager.is_12h); ++ time_label.label = "%s – %s".printf (start_time.format (time_format), end_time.format (time_format)); + } + +- unowned Gtk.StyleContext grid_context = grid.get_style_context (); +- grid_context.add_class ("event"); +- grid_context.add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); +- +- add (grid); +- +- set_color (); +- cal.notify["color"].connect (set_color); +- +- update_timelabel (); +- time_manager.notify["is-12h"].connect (update_timelabel); +- } +- +- private void update_timelabel () { +- var time_format = Granite.DateTime.get_default_time_format (time_manager.is_12h); +- time_label.label = "%s – %s".printf (start_time.format (time_format), end_time.format (time_format)); +- } +- +- private void set_color () { +- Util.set_event_calendar_color (cal, grid); +- Util.set_event_calendar_color (cal, event_image); ++ private void set_color () { ++ Util.set_event_calendar_color (cal, grid); ++ Util.set_event_calendar_color (cal, event_image); ++ } + } + } +diff --git a/src/Widgets/calendar/Grid.vala b/src/Widgets/Grid.vala +similarity index 95% +rename from src/Widgets/calendar/Grid.vala +rename to src/Widgets/Grid.vala +index 2b48636..165d11d 100644 +--- a/src/Widgets/calendar/Grid.vala ++++ b/src/Widgets/Grid.vala +@@ -20,11 +20,11 @@ + * Corentin Noël + */ + +-namespace DateTime.Widgets { ++namespace DateTimeIndicator { + /** + * Represents the entire date grid as a table. + */ +- public class Grid : Gtk.Grid { ++ public class Widgets.Grid : Gtk.Grid { + public Util.DateRange grid_range { get; private set; } + + /* +@@ -59,7 +59,7 @@ namespace DateTime.Widgets { + hexpand = true; + attach (week_sep_revealer, 1, 1, 1, 6); + +- DateTime.Indicator.settings.bind ("show-weeks", week_sep_revealer, "reveal-child", GLib.SettingsBindFlags.DEFAULT); ++ Indicator.settings.bind ("show-weeks", week_sep_revealer, "reveal-child", GLib.SettingsBindFlags.DEFAULT); + + data = new Gee.HashMap (); + events |= Gdk.EventMask.SCROLL_MASK; +@@ -77,7 +77,7 @@ namespace DateTime.Widgets { + day.set_selected (true); + day.set_state_flags (Gtk.StateFlags.FOCUSED, false); + selection_changed (selected_date); +- var calmodel = CalendarModel.get_default (); ++ var calmodel = Models.CalendarModel.get_default (); + var date_month = selected_date.get_month () - calmodel.month_start.get_month (); + var date_year = selected_date.get_year () - calmodel.month_start.get_year (); + +@@ -127,7 +127,7 @@ namespace DateTime.Widgets { + /* Create new widgets for the new range */ + + var date = Util.strip_time (today); +- date = date.add_days (CalendarModel.get_default ().week_starts_on - date.get_day_of_week ()); ++ date = date.add_days (Models.CalendarModel.get_default ().week_starts_on - date.get_day_of_week ()); + foreach (var label in header_labels) { + label.label = date.format ("%a"); + date = date.add_days (1); +@@ -221,7 +221,7 @@ namespace DateTime.Widgets { + week_labels[c].add (week_label); + week_labels[c].show_all (); + +- DateTime.Indicator.settings.bind ("show-weeks", week_labels[c], "reveal-child", GLib.SettingsBindFlags.DEFAULT); ++ Indicator.settings.bind ("show-weeks", week_labels[c], "reveal-child", GLib.SettingsBindFlags.DEFAULT); + + attach (week_labels[c], 0, c + 1); + +diff --git a/src/Widgets/GridDay.vala b/src/Widgets/GridDay.vala +new file mode 100644 +index 0000000..8c44443 +--- /dev/null ++++ b/src/Widgets/GridDay.vala +@@ -0,0 +1,180 @@ ++// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- ++/*- ++ * Copyright (c) 2011–2018 elementary, Inc. (https://elementary.io) ++ * ++ * 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 . ++ * ++ * Authored by: Maxwell Barvian ++ * Corentin Noël ++ */ ++ ++namespace DateTimeIndicator { ++/** ++ * Represents a single day on the grid. ++ */ ++ public class Widgets.GridDay : Gtk.EventBox { ++ /* ++ * Event emitted when the day is double clicked or the ENTER key is pressed. ++ */ ++ public signal void on_event_add (GLib.DateTime date); ++ ++ public GLib.DateTime date { get; construct set; } ++ ++ private static Gtk.CssProvider provider; ++ private static Models.CalendarModel model; ++ ++ private Gee.HashMap event_dots; ++ private Gtk.Grid event_grid; ++ private Gtk.Label label; ++ private bool valid_grab = false; ++ ++ public GridDay (GLib.DateTime date) { ++ Object (date: date); ++ } ++ ++ static construct { ++ model = Models.CalendarModel.get_default (); ++ ++ provider = new Gtk.CssProvider (); ++ provider.load_from_resource ("/io/elementary/desktop/wingpanel/datetime/GridDay.css"); ++ } ++ ++ construct { ++ unowned Gtk.StyleContext style_context = get_style_context (); ++ style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ style_context.add_class ("circular"); ++ ++ label = new Gtk.Label (null); ++ ++ event_grid = new Gtk.Grid (); ++ event_grid.halign = Gtk.Align.CENTER; ++ event_grid.height_request = 6; ++ ++ var grid = new Gtk.Grid (); ++ grid.halign = grid.valign = Gtk.Align.CENTER; ++ grid.attach (label, 0, 0); ++ grid.attach (event_grid, 0, 1); ++ ++ can_focus = true; ++ events |= Gdk.EventMask.BUTTON_PRESS_MASK; ++ events |= Gdk.EventMask.KEY_PRESS_MASK; ++ events |= Gdk.EventMask.SMOOTH_SCROLL_MASK; ++ set_size_request (35, 35); ++ halign = Gtk.Align.CENTER; ++ hexpand = true; ++ add (grid); ++ show_all (); ++ ++ // Signals and handlers ++ button_press_event.connect (on_button_press); ++ key_press_event.connect (on_key_press); ++ scroll_event.connect ((event) => {return Util.on_scroll_event (event);}); ++ ++ notify["date"].connect (() => { ++ label.label = date.get_day_of_month ().to_string (); ++ }); ++ ++ event_dots = new Gee.HashMap (); ++ ++ model.events_added.connect (add_event_dots); ++ model.events_removed.connect (remove_event_dots); ++ } ++ ++ private void add_event_dots (E.Source source, Gee.Collection events) { ++ foreach (var component in events) { ++ if (event_dots.size >= 3) { ++ return; ++ } ++ ++ if (Util.calcomp_is_on_day (component, date)) { ++ unowned ICal.Component ical = component.get_icalcomponent (); ++ ++ var event_uid = ical.get_uid (); ++ if (!event_dots.has_key (event_uid)) { ++ var event_dot = new Gtk.Image (); ++ event_dot.gicon = new ThemedIcon ("pager-checked-symbolic"); ++ event_dot.pixel_size = 6; ++ ++ unowned Gtk.StyleContext style_context = event_dot.get_style_context (); ++ style_context.add_class (Granite.STYLE_CLASS_ACCENT); ++ style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ ++ var source_calendar = (E.SourceCalendar?) source.get_extension (E.SOURCE_EXTENSION_CALENDAR); ++ Util.set_event_calendar_color (source_calendar, event_dot); ++ ++ event_dots[event_uid] = event_dot; ++ ++ event_grid.add (event_dot); ++ } ++ } ++ } ++ ++ event_grid.show_all (); ++ } ++ ++ private void remove_event_dots (E.Source source, Gee.Collection events) { ++ foreach (var component in events) { ++ unowned ICal.Component ical = component.get_icalcomponent (); ++ var event_uid = ical.get_uid (); ++ var dot = event_dots[event_uid]; ++ if (dot != null) { ++ dot.destroy (); ++ event_dots.remove (event_uid); ++ } ++ } ++ } ++ ++ public void set_selected (bool selected) { ++ if (selected) { ++ set_state_flags (Gtk.StateFlags.SELECTED, true); ++ } else { ++ set_state_flags (Gtk.StateFlags.NORMAL, true); ++ } ++ } ++ ++ public void grab_focus_force () { ++ valid_grab = true; ++ grab_focus (); ++ } ++ ++ public override void grab_focus () { ++ if (valid_grab) { ++ base.grab_focus (); ++ valid_grab = false; ++ } ++ } ++ ++ public void sensitive_container (bool sens) { ++ label.sensitive = sens; ++ event_grid.sensitive = sens; ++ } ++ ++ private bool on_button_press (Gdk.EventButton event) { ++ if (event.type == Gdk.EventType.2BUTTON_PRESS && event.button == Gdk.BUTTON_PRIMARY) ++ on_event_add (date); ++ valid_grab = true; ++ grab_focus (); ++ return false; ++ } ++ ++ private bool on_key_press (Gdk.EventKey event) { ++ if (event.keyval == Gdk.keyval_from_name ("Return") ) { ++ on_event_add (date); ++ return true; ++ } ++ ++ return false; ++ } ++ } ++} +diff --git a/src/Widgets/PanelLabel.vala b/src/Widgets/PanelLabel.vala +index f253f9b..dff9a21 100644 +--- a/src/Widgets/PanelLabel.vala ++++ b/src/Widgets/PanelLabel.vala +@@ -17,55 +17,57 @@ + * Boston, MA 02110-1301 USA. + */ + +-public class DateTime.Widgets.PanelLabel : Gtk.Grid { +- private Gtk.Label date_label; +- private Gtk.Label time_label; +- private Services.TimeManager time_manager; ++namespace DateTimeIndicator { ++ public class Widgets.PanelLabel : Gtk.Grid { ++ private Gtk.Label date_label; ++ private Gtk.Label time_label; ++ private Services.TimeManager time_manager; + +- public string clock_format { get; set; } +- public bool clock_show_seconds { get; set; } +- public bool clock_show_weekday { get; set; } ++ public string clock_format { get; set; } ++ public bool clock_show_seconds { get; set; } ++ public bool clock_show_weekday { get; set; } + +- construct { +- date_label = new Gtk.Label (null); +- date_label.margin_end = 12; ++ construct { ++ date_label = new Gtk.Label (null); ++ date_label.margin_end = 12; + +- var date_revealer = new Gtk.Revealer (); +- date_revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_LEFT; +- date_revealer.add (date_label); ++ var date_revealer = new Gtk.Revealer (); ++ date_revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_LEFT; ++ date_revealer.add (date_label); + +- time_label = new Gtk.Label (null); ++ time_label = new Gtk.Label (null); + +- valign = Gtk.Align.CENTER; +- add (date_revealer); +- add (time_label); ++ valign = Gtk.Align.CENTER; ++ add (date_revealer); ++ add (time_label); + +- var clock_settings = new GLib.Settings ("io.elementary.desktop.wingpanel.datetime"); +- clock_settings.bind ("clock-format", this, "clock-format", SettingsBindFlags.DEFAULT); +- clock_settings.bind ("clock-show-seconds", this, "clock-show-seconds", SettingsBindFlags.DEFAULT); +- clock_settings.bind ("clock-show-date", date_revealer, "reveal_child", SettingsBindFlags.DEFAULT); +- clock_settings.bind ("clock-show-weekday", this, "clock-show-weekday", SettingsBindFlags.DEFAULT); ++ var clock_settings = new GLib.Settings ("io.elementary.desktop.wingpanel.datetime"); ++ clock_settings.bind ("clock-format", this, "clock-format", SettingsBindFlags.DEFAULT); ++ clock_settings.bind ("clock-show-seconds", this, "clock-show-seconds", SettingsBindFlags.DEFAULT); ++ clock_settings.bind ("clock-show-date", date_revealer, "reveal_child", SettingsBindFlags.DEFAULT); ++ clock_settings.bind ("clock-show-weekday", this, "clock-show-weekday", SettingsBindFlags.DEFAULT); + +- notify.connect (() => { +- update_labels (); +- }); ++ notify.connect (() => { ++ update_labels (); ++ }); + +- time_manager = Services.TimeManager.get_default (); +- time_manager.minute_changed.connect (update_labels); +- time_manager.notify["is-12h"].connect (update_labels); +- } +- +- private void update_labels () { +- string date_format; +- if (clock_format == "ISO8601") { +- date_format = "%F"; +- } else { +- date_format = Granite.DateTime.get_default_date_format (clock_show_weekday, true, false); ++ time_manager = Services.TimeManager.get_default (); ++ time_manager.minute_changed.connect (update_labels); ++ time_manager.notify["is-12h"].connect (update_labels); + } + +- date_label.label = time_manager.format (date_format); ++ private void update_labels () { ++ string date_format; ++ if (clock_format == "ISO8601") { ++ date_format = "%F"; ++ } else { ++ date_format = Granite.DateTime.get_default_date_format (clock_show_weekday, true, false); ++ } + +- string time_format = Granite.DateTime.get_default_time_format (time_manager.is_12h, clock_show_seconds); +- time_label.label = time_manager.format (time_format); ++ date_label.label = time_manager.format (date_format); ++ ++ string time_format = Granite.DateTime.get_default_time_format (time_manager.is_12h, clock_show_seconds); ++ time_label.label = time_manager.format (time_format); ++ } + } + } +diff --git a/src/Widgets/calendar/CalendarView.vala b/src/Widgets/calendar/CalendarView.vala +deleted file mode 100644 +index ef3f8f9..0000000 +--- a/src/Widgets/calendar/CalendarView.vala ++++ /dev/null +@@ -1,183 +0,0 @@ +-/*- +- * Copyright (c) 2011–2018 elementary, Inc. (https://elementary.io) +- * +- * 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 . +- * +- * Authored by: Maxwell Barvian +- * Corentin Noël +- */ +- +-public class DateTime.Widgets.CalendarView : Gtk.Grid { +- public signal void day_double_click (); +- public signal void event_updates (); +- public signal void selection_changed (GLib.DateTime? new_date); +- +- public GLib.DateTime? selected_date { get; private set; } +- +- private Grid grid; +- private Gtk.Stack stack; +- private Gtk.Grid big_grid; +- +- construct { +- var label = new Gtk.Label (new GLib.DateTime.now_local ().format (_("%OB, %Y"))); +- label.hexpand = true; +- label.margin_start = 6; +- label.xalign = 0; +- label.width_chars = 13; +- +- var provider = new Gtk.CssProvider (); +- provider.load_from_resource ("/io/elementary/desktop/wingpanel/datetime/ControlHeader.css"); +- +- var label_style_context = label.get_style_context (); +- label_style_context.add_class ("header-label"); +- label_style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); +- +- var left_button = new Gtk.Button.from_icon_name ("pan-start-symbolic"); +- var center_button = new Gtk.Button.from_icon_name ("office-calendar-symbolic"); +- center_button.tooltip_text = _("Go to today's date"); +- var right_button = new Gtk.Button.from_icon_name ("pan-end-symbolic"); +- +- var box_buttons = new Gtk.Grid (); +- box_buttons.margin_end = 6; +- box_buttons.valign = Gtk.Align.CENTER; +- box_buttons.get_style_context ().add_class (Gtk.STYLE_CLASS_LINKED); +- box_buttons.add (left_button); +- box_buttons.add (center_button); +- box_buttons.add (right_button); +- +- big_grid = create_big_grid (); +- +- stack = new Gtk.Stack (); +- stack.add (big_grid); +- stack.show_all (); +- stack.expand = true; +- +- stack.notify["transition-running"].connect (() => { +- if (stack.transition_running == false) { +- stack.get_children ().foreach ((child) => { +- if (child != stack.visible_child) { +- child.destroy (); +- } +- }); +- } +- }); +- +- column_spacing = 6; +- row_spacing = 6; +- margin_start = margin_end = 10; +- attach (label, 0, 0); +- attach (box_buttons, 1, 0); +- attach (stack, 0, 1, 2); +- +- var model = CalendarModel.get_default (); +- model.notify["data-range"].connect (() => { +- label.label = model.month_start.format (_("%OB, %Y")); +- +- sync_with_model (); +- +- selected_date = null; +- selection_changed (selected_date); +- }); +- +- left_button.clicked.connect (() => { +- model.change_month (-1); +- }); +- +- right_button.clicked.connect (() => { +- model.change_month (1); +- }); +- +- center_button.clicked.connect (() => { +- show_today (); +- }); +- } +- +- private Gtk.Grid create_big_grid () { +- grid = new DateTime.Widgets.Grid (); +- grid.show_all (); +- +- grid.on_event_add.connect ((date) => { +- show_date_in_maya (date); +- day_double_click (); +- }); +- +- grid.selection_changed.connect ((date) => { +- selected_date = date; +- selection_changed (date); +- }); +- +- return grid; +- } +- +- public void show_today () { +- var calmodel = CalendarModel.get_default (); +- var today = Util.strip_time (new GLib.DateTime.now_local ()); +- var start = Util.get_start_of_month (today); +- selected_date = today; +- if (!start.equal (calmodel.month_start)) { +- calmodel.month_start = start; +- } +- sync_with_model (); +- +- grid.set_focus_to_today (); +- } +- +- // TODO: As far as maya supports it use the Dbus Activation feature to run the calendar-app. +- public void show_date_in_maya (GLib.DateTime date) { +- var command = "io.elementary.calendar --show-day %s".printf (date.format ("%F")); +- +- try { +- var appinfo = AppInfo.create_from_commandline (command, null, AppInfoCreateFlags.NONE); +- appinfo.launch_uris (null, null); +- } catch (GLib.Error e) { +- var dialog = new Granite.MessageDialog.with_image_from_icon_name ( +- _("Unable To Launch Calendar"), +- _("The program \"io.elementary.calendar\" may not be installed"), +- "dialog-error" +- ); +- dialog.show_error_details (e.message); +- dialog.run (); +- dialog.destroy (); +- } +- } +- +- /* Sets the calendar widgets to the date range of the model */ +- private void sync_with_model () { +- var model = CalendarModel.get_default (); +- if (grid.grid_range != null && (model.data_range.equals (grid.grid_range) || grid.grid_range.first_dt.compare (model.data_range.first_dt) == 0)) { +- grid.update_today (); +- return; // nothing else to do +- } +- +- GLib.DateTime previous_first = null; +- if (grid.grid_range != null) +- previous_first = grid.grid_range.first_dt; +- +- big_grid = create_big_grid (); +- stack.add (big_grid); +- +- grid.set_range (model.data_range, model.month_start); +- grid.update_weeks (model.data_range.first_dt, model.num_weeks); +- +- if (previous_first != null) { +- if (previous_first.compare (grid.grid_range.first_dt) == -1) { +- stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT; +- } else { +- stack.transition_type = Gtk.StackTransitionType.SLIDE_RIGHT; +- } +- } +- +- stack.set_visible_child (big_grid); +- } +-} +diff --git a/src/Widgets/calendar/GridDay.vala b/src/Widgets/calendar/GridDay.vala +deleted file mode 100644 +index a9b5a28..0000000 +--- a/src/Widgets/calendar/GridDay.vala ++++ /dev/null +@@ -1,178 +0,0 @@ +-// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- +-/*- +- * Copyright (c) 2011–2018 elementary, Inc. (https://elementary.io) +- * +- * 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 . +- * +- * Authored by: Maxwell Barvian +- * Corentin Noël +- */ +- +-/** +- * Represents a single day on the grid. +- */ +-public class DateTime.Widgets.GridDay : Gtk.EventBox { +- /* +- * Event emitted when the day is double clicked or the ENTER key is pressed. +- */ +- public signal void on_event_add (GLib.DateTime date); +- +- public GLib.DateTime date { get; construct set; } +- +- private static Gtk.CssProvider provider; +- private static Widgets.CalendarModel model; +- +- private Gee.HashMap event_dots; +- private Gtk.Grid event_grid; +- private Gtk.Label label; +- private bool valid_grab = false; +- +- public GridDay (GLib.DateTime date) { +- Object (date: date); +- } +- +- static construct { +- model = Widgets.CalendarModel.get_default (); +- +- provider = new Gtk.CssProvider (); +- provider.load_from_resource ("/io/elementary/desktop/wingpanel/datetime/GridDay.css"); +- } +- +- construct { +- unowned Gtk.StyleContext style_context = get_style_context (); +- style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); +- style_context.add_class ("circular"); +- +- label = new Gtk.Label (null); +- +- event_grid = new Gtk.Grid (); +- event_grid.halign = Gtk.Align.CENTER; +- event_grid.height_request = 6; +- +- var grid = new Gtk.Grid (); +- grid.halign = grid.valign = Gtk.Align.CENTER; +- grid.attach (label, 0, 0); +- grid.attach (event_grid, 0, 1); +- +- can_focus = true; +- events |= Gdk.EventMask.BUTTON_PRESS_MASK; +- events |= Gdk.EventMask.KEY_PRESS_MASK; +- events |= Gdk.EventMask.SMOOTH_SCROLL_MASK; +- set_size_request (35, 35); +- halign = Gtk.Align.CENTER; +- hexpand = true; +- add (grid); +- show_all (); +- +- // Signals and handlers +- button_press_event.connect (on_button_press); +- key_press_event.connect (on_key_press); +- scroll_event.connect ((event) => {return Util.on_scroll_event (event);}); +- +- notify["date"].connect (() => { +- label.label = date.get_day_of_month ().to_string (); +- }); +- +- event_dots = new Gee.HashMap (); +- +- model.events_added.connect (add_event_dots); +- model.events_removed.connect (remove_event_dots); +- } +- +- private void add_event_dots (E.Source source, Gee.Collection events) { +- foreach (var component in events) { +- if (event_dots.size >= 3) { +- return; +- } +- +- if (Util.calcomp_is_on_day (component, date)) { +- unowned ICal.Component ical = component.get_icalcomponent (); +- +- var event_uid = ical.get_uid (); +- if (!event_dots.has_key (event_uid)) { +- var event_dot = new Gtk.Image (); +- event_dot.gicon = new ThemedIcon ("pager-checked-symbolic"); +- event_dot.pixel_size = 6; +- +- unowned Gtk.StyleContext style_context = event_dot.get_style_context (); +- style_context.add_class (Granite.STYLE_CLASS_ACCENT); +- style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); +- +- var source_calendar = (E.SourceCalendar?) source.get_extension (E.SOURCE_EXTENSION_CALENDAR); +- Util.set_event_calendar_color (source_calendar, event_dot); +- +- event_dots[event_uid] = event_dot; +- +- event_grid.add (event_dot); +- } +- } +- } +- +- event_grid.show_all (); +- } +- +- private void remove_event_dots (E.Source source, Gee.Collection events) { +- foreach (var component in events) { +- unowned ICal.Component ical = component.get_icalcomponent (); +- var event_uid = ical.get_uid (); +- var dot = event_dots[event_uid]; +- if (dot != null) { +- dot.destroy (); +- event_dots.remove (event_uid); +- } +- } +- } +- +- public void set_selected (bool selected) { +- if (selected) { +- set_state_flags (Gtk.StateFlags.SELECTED, true); +- } else { +- set_state_flags (Gtk.StateFlags.NORMAL, true); +- } +- } +- +- public void grab_focus_force () { +- valid_grab = true; +- grab_focus (); +- } +- +- public override void grab_focus () { +- if (valid_grab) { +- base.grab_focus (); +- valid_grab = false; +- } +- } +- +- public void sensitive_container (bool sens) { +- label.sensitive = sens; +- event_grid.sensitive = sens; +- } +- +- private bool on_button_press (Gdk.EventButton event) { +- if (event.type == Gdk.EventType.2BUTTON_PRESS && event.button == Gdk.BUTTON_PRIMARY) +- on_event_add (date); +- valid_grab = true; +- grab_focus (); +- return false; +- } +- +- private bool on_key_press (Gdk.EventKey event) { +- if (event.keyval == Gdk.keyval_from_name ("Return") ) { +- on_event_add (date); +- return true; +- } +- +- return false; +- } +-} + +From 11f11073d74dac1d831560c3a92687531aaf846e Mon Sep 17 00:00:00 2001 +From: Dirli +Date: Thu, 9 Apr 2020 20:46:31 +0300 +Subject: [PATCH 2/8] Events list moved to a separate file + +--- + meson.build | 1 + + src/Indicator.vala | 113 +++------------------------------ + src/Widgets/EventsListBox.vala | 102 +++++++++++++++++++++++++++++ + 3 files changed, 113 insertions(+), 103 deletions(-) + create mode 100644 src/Widgets/EventsListBox.vala + +diff --git a/meson.build b/meson.build +index b44c5bd..1b52276 100644 +--- a/meson.build ++++ b/meson.build +@@ -46,6 +46,7 @@ shared_module( + 'src/Models/CalendarModel.vala', + 'src/Widgets/CalendarView.vala', + 'src/Widgets/EventRow.vala', ++ 'src/Widgets/EventsListBox.vala', + 'src/Widgets/Grid.vala', + 'src/Widgets/GridDay.vala', + 'src/Widgets/PanelLabel.vala', +diff --git a/src/Indicator.vala b/src/Indicator.vala +index b712c12..6a8d001 100644 +--- a/src/Indicator.vala ++++ b/src/Indicator.vala +@@ -22,9 +22,9 @@ namespace DateTimeIndicator { + public static GLib.Settings settings; + + private Widgets.PanelLabel panel_label; +- private Gtk.Grid main_grid; + private Widgets.CalendarView calendar; +- private Gtk.ListBox event_listbox; ++ private Widgets.EventsListBox event_listbox; ++ private Gtk.Grid main_grid; + private uint update_events_idle_source = 0; + + public Indicator () { +@@ -56,24 +56,7 @@ namespace DateTimeIndicator { + calendar = new Widgets.CalendarView (); + calendar.margin_bottom = 6; + +- var placeholder_label = new Gtk.Label (_("No Events on This Day")); +- placeholder_label.wrap = true; +- placeholder_label.wrap_mode = Pango.WrapMode.WORD; +- placeholder_label.margin_start = 12; +- placeholder_label.margin_end = 12; +- placeholder_label.max_width_chars = 20; +- placeholder_label.justify = Gtk.Justification.CENTER; +- placeholder_label.show_all (); +- +- var placeholder_style_context = placeholder_label.get_style_context (); +- placeholder_style_context.add_class (Gtk.STYLE_CLASS_DIM_LABEL); +- placeholder_style_context.add_class (Granite.STYLE_CLASS_H3_LABEL); +- +- event_listbox = new Gtk.ListBox (); +- event_listbox.selection_mode = Gtk.SelectionMode.NONE; +- event_listbox.set_header_func (header_update_func); +- event_listbox.set_placeholder (placeholder_label); +- event_listbox.set_sort_func (sort_function); ++ event_listbox = new Widgets.EventsListBox (); + + var scrolled_window = new Gtk.ScrolledWindow (null, null); + scrolled_window.hscrollbar_policy = Gtk.PolicyType.NEVER; +@@ -84,11 +67,11 @@ namespace DateTimeIndicator { + + main_grid = new Gtk.Grid (); + main_grid.margin_top = 12; +- main_grid.attach (calendar, 0, 0); +- main_grid.attach (new Gtk.Separator (Gtk.Orientation.VERTICAL), 1, 0); +- main_grid.attach (scrolled_window, 2, 0); +- main_grid.attach (new Wingpanel.Widgets.Separator (), 0, 2, 3); +- main_grid.attach (settings_button, 0, 3, 3); ++ main_grid.attach (calendar, 0, 0); ++ main_grid.attach (new Wingpanel.Widgets.Separator (), 0, 1); ++ main_grid.attach (settings_button, 0, 2); ++ main_grid.attach (new Gtk.Separator (Gtk.Orientation.VERTICAL), 1, 0, 1, 3); ++ main_grid.attach (scrolled_window, 2, 0, 1, 3); + + var size_group = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL); + size_group.add_widget (calendar); +@@ -119,52 +102,6 @@ namespace DateTimeIndicator { + return main_grid; + } + +- private void header_update_func (Gtk.ListBoxRow lbrow, Gtk.ListBoxRow? lbbefore) { +- var row = (EventRow) lbrow; +- if (lbbefore != null) { +- var before = (EventRow) lbbefore; +- if (row.is_allday == before.is_allday) { +- row.set_header (null); +- return; +- } +- +- if (row.is_allday != before.is_allday) { +- var header_label = new Granite.HeaderLabel (_("During the Day")); +- header_label.margin_start = header_label.margin_end = 6; +- +- row.set_header (header_label); +- return; +- } +- } else { +- if (row.is_allday) { +- var allday_header = new Granite.HeaderLabel (_("All Day")); +- allday_header.margin_start = allday_header.margin_end = 6; +- +- row.set_header (allday_header); +- } +- return; +- } +- } +- +- [CCode (instance_pos = -1)] +- private int sort_function (Gtk.ListBoxRow child1, Gtk.ListBoxRow child2) { +- var e1 = (EventRow) child1; +- var e2 = (EventRow) child2; +- +- if (e1.start_time.compare (e2.start_time) != 0) { +- return e1.start_time.compare (e2.start_time); +- } +- +- // If they have the same date, sort them wholeday first +- if (e1.is_allday) { +- return -1; +- } else if (e2.is_allday) { +- return 1; +- } +- +- return 0; +- } +- + private void update_events_model (E.Source source, Gee.Collection events) { + idle_update_events (); + } +@@ -174,42 +111,12 @@ namespace DateTimeIndicator { + GLib.Source.remove (update_events_idle_source); + } + +- update_events_idle_source = GLib.Idle.add (update_events); +- } +- +- private bool update_events () { +- foreach (unowned Gtk.Widget widget in event_listbox.get_children ()) { +- widget.destroy (); +- } ++ update_events_idle_source = GLib.Idle.add (() => { ++ event_listbox.update_events (calendar.selected_date); + +- if (calendar.selected_date == null) { + update_events_idle_source = 0; + return GLib.Source.REMOVE; +- } +- +- var date = calendar.selected_date; +- +- var model = Models.CalendarModel.get_default (); +- +- var events_on_day = new Gee.TreeMap (); +- +- model.source_events.@foreach ((source, component_map) => { +- foreach (var comp in component_map.get_values ()) { +- if (Util.calcomp_is_on_day (comp, date)) { +- unowned ICal.Component ical = comp.get_icalcomponent (); +- var event_uid = ical.get_uid (); +- if (!events_on_day.has_key (event_uid)) { +- events_on_day[event_uid] = new EventRow (date, ical, source); +- +- event_listbox.add (events_on_day[event_uid]); +- } +- } +- } + }); +- +- event_listbox.show_all (); +- update_events_idle_source = 0; +- return GLib.Source.REMOVE; + } + + public override void opened () { +diff --git a/src/Widgets/EventsListBox.vala b/src/Widgets/EventsListBox.vala +new file mode 100644 +index 0000000..547e4c5 +--- /dev/null ++++ b/src/Widgets/EventsListBox.vala +@@ -0,0 +1,102 @@ ++namespace DateTimeIndicator { ++ public class Widgets.EventsListBox : Gtk.ListBox { ++ ++ public EventsListBox () { ++ selection_mode = Gtk.SelectionMode.NONE; ++ ++ var placeholder_label = new Gtk.Label (_("No Events on This Day")); ++ placeholder_label.wrap = true; ++ placeholder_label.wrap_mode = Pango.WrapMode.WORD; ++ placeholder_label.margin_start = 12; ++ placeholder_label.margin_end = 12; ++ placeholder_label.max_width_chars = 20; ++ placeholder_label.justify = Gtk.Justification.CENTER; ++ placeholder_label.show_all (); ++ ++ var placeholder_style_context = placeholder_label.get_style_context (); ++ placeholder_style_context.add_class (Gtk.STYLE_CLASS_DIM_LABEL); ++ placeholder_style_context.add_class (Granite.STYLE_CLASS_H3_LABEL); ++ ++ set_header_func (header_update_func); ++ set_placeholder (placeholder_label); ++ set_sort_func (sort_function); ++ } ++ ++ public void update_events (GLib.DateTime? selected_date) { ++ foreach (unowned Gtk.Widget widget in get_children ()) { ++ widget.destroy (); ++ } ++ ++ if (selected_date == null) { ++ return; ++ } ++ ++ var model = Models.CalendarModel.get_default (); ++ ++ var events_on_day = new Gee.TreeMap (); ++ ++ model.source_events.@foreach ((source, component_map) => { ++ foreach (var comp in component_map.get_values ()) { ++ if (Util.calcomp_is_on_day (comp, selected_date)) { ++ unowned ICal.Component ical = comp.get_icalcomponent (); ++ var event_uid = ical.get_uid (); ++ if (!events_on_day.has_key (event_uid)) { ++ events_on_day[event_uid] = new EventRow (selected_date, ical, source); ++ ++ add (events_on_day[event_uid]); ++ } ++ } ++ } ++ }); ++ ++ show_all (); ++ return; ++ } ++ ++ private void header_update_func (Gtk.ListBoxRow lbrow, Gtk.ListBoxRow? lbbefore) { ++ var row = (EventRow) lbrow; ++ if (lbbefore != null) { ++ var before = (EventRow) lbbefore; ++ if (row.is_allday == before.is_allday) { ++ row.set_header (null); ++ return; ++ } ++ ++ if (row.is_allday != before.is_allday) { ++ var header_label = new Granite.HeaderLabel (_("During the Day")); ++ header_label.margin_start = header_label.margin_end = 6; ++ ++ row.set_header (header_label); ++ return; ++ } ++ } else { ++ if (row.is_allday) { ++ var allday_header = new Granite.HeaderLabel (_("All Day")); ++ allday_header.margin_start = allday_header.margin_end = 6; ++ ++ row.set_header (allday_header); ++ } ++ return; ++ } ++ } ++ ++ [CCode (instance_pos = -1)] ++ private int sort_function (Gtk.ListBoxRow child1, Gtk.ListBoxRow child2) { ++ var e1 = (EventRow) child1; ++ var e2 = (EventRow) child2; ++ ++ if (e1.start_time.compare (e2.start_time) != 0) { ++ return e1.start_time.compare (e2.start_time); ++ } ++ ++ // If they have the same date, sort them wholeday first ++ if (e1.is_allday) { ++ return -1; ++ } else if (e2.is_allday) { ++ return 1; ++ } ++ ++ return 0; ++ } ++ } ++} + +From 262d91676f78f031bcfbff637344e52c63ab89bd Mon Sep 17 00:00:00 2001 +From: Dirli +Date: Sat, 11 Apr 2020 21:56:33 +0300 +Subject: [PATCH 3/8] added event manager + +--- + meson.build | 1 + + src/Indicator.vala | 38 ++++-- + src/Models/CalendarModel.vala | 224 +------------------------------ + src/Services/EventsManager.vala | 226 ++++++++++++++++++++++++++++++++ + src/Util/Util.vala | 57 ++------ + src/Widgets/CalendarView.vala | 9 ++ + src/Widgets/EventsListBox.vala | 7 +- + src/Widgets/Grid.vala | 31 +++++ + src/Widgets/GridDay.vala | 111 +++++++++++----- + 9 files changed, 385 insertions(+), 319 deletions(-) + create mode 100644 src/Services/EventsManager.vala + +diff --git a/meson.build b/meson.build +index 1b52276..e348e3d 100644 +--- a/meson.build ++++ b/meson.build +@@ -50,6 +50,7 @@ shared_module( + 'src/Widgets/Grid.vala', + 'src/Widgets/GridDay.vala', + 'src/Widgets/PanelLabel.vala', ++ 'src/Services/EventsManager.vala', + 'src/Services/TimeManager.vala', + dependencies: [ + dependency('glib-2.0'), +diff --git a/src/Indicator.vala b/src/Indicator.vala +index 6a8d001..bf4358f 100644 +--- a/src/Indicator.vala ++++ b/src/Indicator.vala +@@ -24,9 +24,14 @@ namespace DateTimeIndicator { + private Widgets.PanelLabel panel_label; + private Widgets.CalendarView calendar; + private Widgets.EventsListBox event_listbox; ++ ++ private Services.EventsManager event_manager; ++ + private Gtk.Grid main_grid; + private uint update_events_idle_source = 0; + ++ private bool opened_widget = false; ++ + public Indicator () { + Object ( + code_name: Wingpanel.Indicator.DATETIME, +@@ -56,10 +61,22 @@ namespace DateTimeIndicator { + calendar = new Widgets.CalendarView (); + calendar.margin_bottom = 6; + ++ event_manager = new Services.EventsManager (); ++ event_manager.events_updated.connect (update_events_model); ++ event_manager.events_added.connect ((source, events) => { ++ calendar.add_event_dots (source, events); ++ update_events_model (source, events); ++ }); ++ event_manager.events_removed.connect ((source, events) => { ++ calendar.remove_event_dots (source, events); ++ update_events_model (source, events); ++ }); ++ + event_listbox = new Widgets.EventsListBox (); + + var scrolled_window = new Gtk.ScrolledWindow (null, null); + scrolled_window.hscrollbar_policy = Gtk.PolicyType.NEVER; ++ scrolled_window.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC; + scrolled_window.add (event_listbox); + + var settings_button = new Gtk.ModelButton (); +@@ -90,6 +107,12 @@ namespace DateTimeIndicator { + close (); + }); + ++ var model = Models.CalendarModel.get_default (); ++ model.notify["month-start"].connect (() => { ++ model.compute_ranges (); ++ event_manager.load_all_sources (); ++ }); ++ + settings_button.clicked.connect (() => { + try { + AppInfo.launch_default_for_uri ("settings://time", null); +@@ -103,7 +126,9 @@ namespace DateTimeIndicator { + } + + private void update_events_model (E.Source source, Gee.Collection events) { +- idle_update_events (); ++ if (opened_widget) { ++ idle_update_events (); ++ } + } + + private void idle_update_events () { +@@ -112,7 +137,7 @@ namespace DateTimeIndicator { + } + + update_events_idle_source = GLib.Idle.add (() => { +- event_listbox.update_events (calendar.selected_date); ++ event_listbox.update_events (calendar.selected_date, event_manager.source_events); + + update_events_idle_source = 0; + return GLib.Source.REMOVE; +@@ -122,18 +147,15 @@ namespace DateTimeIndicator { + public override void opened () { + calendar.show_today (); + +- Models.CalendarModel.get_default ().events_added.connect (update_events_model); +- Models.CalendarModel.get_default ().events_updated.connect (update_events_model); +- Models.CalendarModel.get_default ().events_removed.connect (update_events_model); ++ opened_widget = true; + } + + public override void closed () { +- Models.CalendarModel.get_default ().events_added.disconnect (update_events_model); +- Models.CalendarModel.get_default ().events_updated.disconnect (update_events_model); +- Models.CalendarModel.get_default ().events_removed.disconnect (update_events_model); ++ opened_widget = false; + } + } + } ++ + public Wingpanel.Indicator get_indicator (Module module) { + debug ("Activating DateTime Indicator"); + var indicator = new DateTimeIndicator.Indicator (); +diff --git a/src/Models/CalendarModel.vala b/src/Models/CalendarModel.vala +index 965b93e..d60a9ac 100644 +--- a/src/Models/CalendarModel.vala ++++ b/src/Models/CalendarModel.vala +@@ -35,17 +35,6 @@ namespace DateTimeIndicator { + /* The start of week, ie. Monday=1 or Sunday=7 */ + public GLib.DateWeekday week_starts_on { get; set; } + +- public HashTable> source_events { get; private set; } +- +- /* Notifies when events are added, updated, or removed */ +- public signal void events_added (E.Source source, Gee.Collection events); +- public signal void events_updated (E.Source source, Gee.Collection events); +- public signal void events_removed (E.Source source, Gee.Collection events); +- +- private E.SourceRegistry registry { get; private set; } +- private HashTable source_client; +- private HashTable source_view; +- + private static CalendarModel? calendar_model = null; + + public static CalendarModel get_default () { +@@ -59,12 +48,6 @@ namespace DateTimeIndicator { + } + + construct { +- open.begin (); +- +- source_client = new HashTable (str_hash, str_equal); +- source_events = new HashTable > (Util.source_hash_func, Util.source_equal_func); +- source_view = new HashTable (str_hash, str_equal); +- + int week_start = Posix.NLTime.FIRST_WEEKDAY.to_string ().data[0]; + if (week_start >= 1 && week_start <= 7) { + week_starts_on = (GLib.DateWeekday) (week_start - 1); +@@ -72,66 +55,6 @@ namespace DateTimeIndicator { + + month_start = Util.get_start_of_month (); + compute_ranges (); +- notify["month-start"].connect (on_parameter_changed); +- } +- +- private async void open () { +- try { +- registry = yield new E.SourceRegistry (null); +- registry.source_removed.connect (remove_source); +- registry.source_added.connect ((source) => add_source_async.begin (source)); +- +- // Add sources +- registry.list_sources (E.SOURCE_EXTENSION_CALENDAR).foreach ((source) => { +- E.SourceCalendar cal = (E.SourceCalendar)source.get_extension (E.SOURCE_EXTENSION_CALENDAR); +- if (cal.selected == true && source.enabled == true) { +- add_source_async.begin (source); +- } +- }); +- +- load_all_sources (); +- } catch (GLib.Error error) { +- critical (error.message); +- } +- } +- +- private void load_all_sources () { +- lock (source_client) { +- foreach (var id in source_client.get_keys ()) { +- var source = registry.ref_source (id); +- E.SourceCalendar cal = (E.SourceCalendar)source.get_extension (E.SOURCE_EXTENSION_CALENDAR); +- +- if (cal.selected == true && source.enabled == true) { +- load_source (source); +- } +- } +- } +- } +- +- private void remove_source (E.Source source) { +- debug ("Removing source '%s'", source.dup_display_name ()); +- /* Already out of the model, so do nothing */ +- unowned string uid = source.get_uid (); +- +- if (!source_view.contains (uid)) { +- return; +- } +- +- var current_view = source_view.get (uid); +- try { +- current_view.stop (); +- } catch (Error e) { +- warning (e.message); +- } +- +- source_view.remove (uid); +- lock (source_client) { +- source_client.remove (uid); +- } +- +- var events = source_events.get (source).get_values ().read_only_view; +- events_removed (source, events); +- source_events.remove (source); + } + + public void change_month (int relative) { +@@ -144,7 +67,7 @@ namespace DateTimeIndicator { + + /* --- Helper Methods ---// */ + +- private void compute_ranges () { ++ public void compute_ranges () { + var month_end = month_start.add_full (0, 1, -1); + + int dow = month_start.get_day_of_week (); +@@ -182,150 +105,5 @@ namespace DateTimeIndicator { + + debug (@"Date ranges: ($data_range_first <= $month_start < $month_end <= $data_range_last)"); + } +- +- private void load_source (E.Source source) { +- /* create empty source-event map */ +- var events = new Gee.TreeMultiMap ( +- (GLib.CompareDataFunc?) GLib.strcmp, +- (GLib.CompareDataFunc?) Util.calcomponent_compare_func +- ); +- source_events.set (source, events); +- /* query client view */ +- var iso_first = ECal.isodate_from_time_t ((time_t)data_range.first_dt.to_unix ()); +- var iso_last = ECal.isodate_from_time_t ((time_t)data_range.last_dt.add_days (1).to_unix ()); +- var query = @"(occur-in-time-range? (make-time \"$iso_first\") (make-time \"$iso_last\"))"; +- +- ECal.Client client; +- lock (source_client) { +- client = source_client.get (source.dup_uid ()); +- } +- +- if (client == null) { +- return; +- } +- +- debug ("Getting client-view for source '%s'", source.dup_display_name ()); +- client.get_view.begin (query, null, (obj, results) => { +- var view = on_client_view_received (results, source, client); +- view.objects_added.connect ((objects) => on_objects_added (source, client, objects)); +- view.objects_removed.connect ((objects) => on_objects_removed (source, client, objects)); +- view.objects_modified.connect ((objects) => on_objects_modified (source, client, objects)); +- try { +- view.start (); +- } catch (Error e) { +- critical (e.message); +- } +- +- source_view.set (source.dup_uid (), view); +- }); +- } +- +- private async void add_source_async (E.Source source) { +- debug ("Adding source '%s'", source.dup_display_name ()); +- try { +- var client = (ECal.Client) ECal.Client.connect_sync (source, ECal.ClientSourceType.EVENTS, -1, null); +- source_client.insert (source.dup_uid (), client); +- } catch (Error e) { +- critical (e.message); +- } +- +- Idle.add (() => { +- load_source (source); +- +- return false; +- }); +- } +- +- private void debug_event (E.Source source, ECal.Component event) { +- unowned ICal.Component comp = event.get_icalcomponent (); +- debug (@"Event ['$(comp.get_summary())', $(source.dup_display_name()), $(comp.get_uid()))]"); +- } +- +- /* --- Signal Handlers ---// */ +- private void on_parameter_changed () { +- compute_ranges (); +- load_all_sources (); +- } +- +- private ECal.ClientView on_client_view_received (AsyncResult results, E.Source source, ECal.Client client) { +- ECal.ClientView view; +- try { +- debug ("Received client-view for source '%s'", source.dup_display_name ()); +- bool status = client.get_view.end (results, out view); +- assert (status == true); +- } catch (Error e) { +- critical ("Error loading client-view from source '%s': %s", source.dup_display_name (), e.message); +- } +- +- return view; +- } +- +-#if E_CAL_2_0 +- private void on_objects_added (E.Source source, ECal.Client client, SList objects) { +-#else +- private void on_objects_added (E.Source source, ECal.Client client, SList objects) { +-#endif +- debug (@"Received $(objects.length()) added event(s) for source '%s'", source.dup_display_name ()); +- var events = source_events.get (source); +- var added_events = new Gee.ArrayList ((Gee.EqualDataFunc?) Util.calcomponent_equal_func); +- objects.foreach ((comp) => { +- unowned string uid = comp.get_uid (); +-#if E_CAL_2_0 +- client.generate_instances_for_object_sync (comp, (time_t) data_range.first_dt.to_unix (), (time_t) data_range.last_dt.to_unix (), null, (comp, start, end) => { +- var event = new ECal.Component.from_icalcomponent (comp); +-#else +- client.generate_instances_for_object_sync (comp, (time_t) data_range.first_dt.to_unix (), (time_t) data_range.last_dt.to_unix (), (event, start, end) => { +-#endif +- debug_event (source, event); +- events.set (uid, event); +- added_events.add (event); +- return true; +- }); +- }); +- } +- +-#if E_CAL_2_0 +- private void on_objects_modified (E.Source source, ECal.Client client, SList objects) { +-#else +- private void on_objects_modified (E.Source source, ECal.Client client, SList objects) { +-#endif +- debug (@"Received $(objects.length()) modified event(s) for source '%s'", source.dup_display_name ()); +- var updated_events = new Gee.ArrayList ((Gee.EqualDataFunc?) Util.calcomponent_equal_func); +- +- objects.foreach ((comp) => { +- unowned string uid = comp.get_uid (); +- var events = source_events.get (source).get (uid); +- updated_events.add_all (events); +- foreach (var event in events) { +- debug_event (source, event); +- } +- }); +- +- events_updated (source, updated_events.read_only_view); +- } +- +-#if E_CAL_2_0 +- private void on_objects_removed (E.Source source, ECal.Client client, SList cids) { +-#else +- private void on_objects_removed (E.Source source, ECal.Client client, SList cids) { +-#endif +- debug (@"Received $(cids.length()) removed event(s) for source '%s'", source.dup_display_name ()); +- var events = source_events.get (source); +- var removed_events = new Gee.ArrayList ((Gee.EqualDataFunc?) Util.calcomponent_equal_func); +- +- cids.foreach ((cid) => { +- if (cid == null) { +- return; +- } +- +- var comps = events.get (cid.get_uid ()); +- foreach (ECal.Component event in comps) { +- removed_events.add (event); +- debug_event (source, event); +- } +- }); +- +- events_removed (source, removed_events.read_only_view); +- } + } + } +diff --git a/src/Services/EventsManager.vala b/src/Services/EventsManager.vala +new file mode 100644 +index 0000000..d939777 +--- /dev/null ++++ b/src/Services/EventsManager.vala +@@ -0,0 +1,226 @@ ++namespace DateTimeIndicator { ++ public class Services.EventsManager : GLib.Object { ++ public signal void events_added (E.Source source, Gee.Collection events); ++ public signal void events_updated (E.Source source, Gee.Collection events); ++ public signal void events_removed (E.Source source, Gee.Collection events); ++ ++ public HashTable> source_events { get; private set; } ++ ++ private E.SourceRegistry registry { get; private set; } ++ private HashTable source_client; ++ private HashTable source_view; ++ ++ public EventsManager () { ++ ++ } ++ ++ construct { ++ source_client = new HashTable (str_hash, str_equal); ++ source_events = new HashTable > (Util.source_hash_func, Util.source_equal_func); ++ source_view = new HashTable (str_hash, str_equal); ++ ++ open.begin (); ++ } ++ ++ private async void open () { ++ try { ++ registry = yield new E.SourceRegistry (null); ++ registry.source_removed.connect (remove_source); ++ registry.source_added.connect ((source) => add_source_async.begin (source)); ++ ++ // Add sources ++ registry.list_sources (E.SOURCE_EXTENSION_CALENDAR).foreach ((source) => { ++ E.SourceCalendar cal = (E.SourceCalendar)source.get_extension (E.SOURCE_EXTENSION_CALENDAR); ++ if (cal.selected == true && source.enabled == true) { ++ add_source_async.begin (source); ++ } ++ }); ++ ++ load_all_sources (); ++ } catch (GLib.Error error) { ++ critical (error.message); ++ } ++ } ++ ++ public void load_all_sources () { ++ lock (source_client) { ++ foreach (var id in source_client.get_keys ()) { ++ var source = registry.ref_source (id); ++ E.SourceCalendar cal = (E.SourceCalendar)source.get_extension (E.SOURCE_EXTENSION_CALENDAR); ++ ++ if (cal.selected == true && source.enabled == true) { ++ load_source (source); ++ } ++ } ++ } ++ } ++ ++ private void remove_source (E.Source source) { ++ debug ("Removing source '%s'", source.dup_display_name ()); ++ /* Already out of the model, so do nothing */ ++ unowned string uid = source.get_uid (); ++ ++ if (!source_view.contains (uid)) { ++ return; ++ } ++ ++ var current_view = source_view.get (uid); ++ try { ++ current_view.stop (); ++ } catch (Error e) { ++ warning (e.message); ++ } ++ ++ source_view.remove (uid); ++ lock (source_client) { ++ source_client.remove (uid); ++ } ++ ++ var events = source_events.get (source).get_values ().read_only_view; ++ events_removed (source, events); ++ source_events.remove (source); ++ } ++ ++ private void load_source (E.Source source) { ++ var model = Models.CalendarModel.get_default (); ++ ++ /* create empty source-event map */ ++ var events = new Gee.TreeMultiMap ( ++ (GLib.CompareDataFunc?) GLib.strcmp, ++ (GLib.CompareDataFunc?) Util.calcomponent_compare_func ++ ); ++ source_events.set (source, events); ++ /* query client view */ ++ var iso_first = ECal.isodate_from_time_t ((time_t) model.data_range.first_dt.to_unix ()); ++ var iso_last = ECal.isodate_from_time_t ((time_t) model.data_range.last_dt.add_days (1).to_unix ()); ++ var query = @"(occur-in-time-range? (make-time \"$iso_first\") (make-time \"$iso_last\"))"; ++ ++ ECal.Client client; ++ lock (source_client) { ++ client = source_client.get (source.dup_uid ()); ++ } ++ ++ if (client == null) { ++ return; ++ } ++ ++ debug ("Getting client-view for source '%s'", source.dup_display_name ()); ++ client.get_view.begin (query, null, (obj, results) => { ++ var view = on_client_view_received (results, source, client); ++ view.objects_added.connect ((objects) => on_objects_added (source, client, objects)); ++ view.objects_removed.connect ((objects) => on_objects_removed (source, client, objects)); ++ view.objects_modified.connect ((objects) => on_objects_modified (source, client, objects)); ++ try { ++ view.start (); ++ } catch (Error e) { ++ critical (e.message); ++ } ++ ++ source_view.set (source.dup_uid (), view); ++ }); ++ } ++ ++ private async void add_source_async (E.Source source) { ++ debug ("Adding source '%s'", source.dup_display_name ()); ++ try { ++ var client = (ECal.Client) ECal.Client.connect_sync (source, ECal.ClientSourceType.EVENTS, -1, null); ++ source_client.insert (source.dup_uid (), client); ++ } catch (Error e) { ++ critical (e.message); ++ } ++ ++ Idle.add (() => { ++ load_source (source); ++ ++ return false; ++ }); ++ } ++ ++ private void debug_event (E.Source source, ECal.Component event) { ++ unowned ICal.Component comp = event.get_icalcomponent (); ++ debug (@"Event ['$(comp.get_summary())', $(source.dup_display_name()), $(comp.get_uid()))]"); ++ } ++ ++ private ECal.ClientView on_client_view_received (AsyncResult results, E.Source source, ECal.Client client) { ++ ECal.ClientView view; ++ try { ++ debug ("Received client-view for source '%s'", source.dup_display_name ()); ++ bool status = client.get_view.end (results, out view); ++ assert (status == true); ++ } catch (Error e) { ++ critical ("Error loading client-view from source '%s': %s", source.dup_display_name (), e.message); ++ } ++ ++ return view; ++ } ++ ++#if E_CAL_2_0 ++ private void on_objects_added (E.Source source, ECal.Client client, SList objects) { ++#else ++ private void on_objects_added (E.Source source, ECal.Client client, SList objects) { ++#endif ++ debug (@"Received $(objects.length()) added event(s) for source '%s'", source.dup_display_name ()); ++ var events = source_events.get (source); ++ var added_events = new Gee.ArrayList ((Gee.EqualDataFunc?) Util.calcomponent_equal_func); ++ var model = Models.CalendarModel.get_default (); ++ objects.foreach ((comp) => { ++ unowned string uid = comp.get_uid (); ++#if E_CAL_2_0 ++ client.generate_instances_for_object_sync (comp, (time_t) model.data_range.first_dt.to_unix (), (time_t) model.data_range.last_dt.to_unix (), null, (comp, start, end) => { ++ var event = new ECal.Component.from_icalcomponent (comp); ++#else ++ client.generate_instances_for_object_sync (comp, (time_t) model.data_range.first_dt.to_unix (), (time_t) model.data_range.last_dt.to_unix (), (event, start, end) => { ++#endif ++ debug_event (source, event); ++ events.set (uid, event); ++ added_events.add (event); ++ return true; ++ }); ++ }); ++ } ++ ++#if E_CAL_2_0 ++ private void on_objects_modified (E.Source source, ECal.Client client, SList objects) { ++#else ++ private void on_objects_modified (E.Source source, ECal.Client client, SList objects) { ++#endif ++ debug (@"Received $(objects.length()) modified event(s) for source '%s'", source.dup_display_name ()); ++ var updated_events = new Gee.ArrayList ((Gee.EqualDataFunc?) Util.calcomponent_equal_func); ++ ++ objects.foreach ((comp) => { ++ unowned string uid = comp.get_uid (); ++ var events = source_events.get (source).get (uid); ++ updated_events.add_all (events); ++ foreach (var event in events) { ++ debug_event (source, event); ++ } ++ }); ++ ++ events_updated (source, updated_events.read_only_view); ++ } ++ ++#if E_CAL_2_0 ++ private void on_objects_removed (E.Source source, ECal.Client client, SList cids) { ++#else ++ private void on_objects_removed (E.Source source, ECal.Client client, SList cids) { ++#endif ++ debug (@"Received $(cids.length()) removed event(s) for source '%s'", source.dup_display_name ()); ++ var events = source_events.get (source); ++ var removed_events = new Gee.ArrayList ((Gee.EqualDataFunc?) Util.calcomponent_equal_func); ++ ++ cids.foreach ((cid) => { ++ if (cid == null) { ++ return; ++ } ++ ++ var comps = events.get (cid.get_uid ()); ++ foreach (ECal.Component event in comps) { ++ removed_events.add (event); ++ debug_event (source, event); ++ } ++ }); ++ ++ events_removed (source, removed_events.read_only_view); ++ } ++ } ++} +diff --git a/src/Util/Util.vala b/src/Util/Util.vala +index c261f4b..b0bdf98 100644 +--- a/src/Util/Util.vala ++++ b/src/Util/Util.vala +@@ -20,47 +20,6 @@ + */ + + namespace DateTimeIndicator.Util { +- static bool has_scrolled = false; +- +- public bool on_scroll_event (Gdk.EventScroll event) { +- double delta_x; +- double delta_y; +- event.get_scroll_deltas (out delta_x, out delta_y); +- +- double choice = delta_x; +- +- if (((int)delta_x).abs () < ((int)delta_y).abs ()) { +- choice = delta_y; +- } +- +- /* It's mouse scroll ! */ +- if (choice == 1 || choice == -1) { +- Models.CalendarModel.get_default ().change_month ((int)choice); +- +- return true; +- } +- +- if (has_scrolled == true) { +- return true; +- } +- +- if (choice > 0.3) { +- reset_timer.begin (); +- Models.CalendarModel.get_default ().change_month (1); +- +- return true; +- } +- +- if (choice < -0.3) { +- reset_timer.begin (); +- Models.CalendarModel.get_default ().change_month (-1); +- +- return true; +- } +- +- return false; +- } +- + public GLib.DateTime get_start_of_month (owned GLib.DateTime? date = null) { + if (date == null) { + date = new GLib.DateTime.now_local (); +@@ -225,12 +184,12 @@ namespace DateTimeIndicator.Util { + return a.dup_uid () == b.dup_uid (); + } + +- public async void reset_timer () { +- has_scrolled = true; +- Timeout.add (500, () => { +- has_scrolled = false; +- +- return false; +- }); +- } ++ // public async void reset_timer () { ++ // has_scrolled = true; ++ // Timeout.add (500, () => { ++ // has_scrolled = false; ++ // ++ // return false; ++ // }); ++ // } + } +diff --git a/src/Widgets/CalendarView.vala b/src/Widgets/CalendarView.vala +index 65cee28..070580f 100644 +--- a/src/Widgets/CalendarView.vala ++++ b/src/Widgets/CalendarView.vala +@@ -181,5 +181,14 @@ namespace DateTimeIndicator { + + stack.set_visible_child (big_grid); + } ++ ++ public void add_event_dots (E.Source source, Gee.Collection events) { ++ grid.add_event_dots (source, events); ++ } ++ ++ ++ public void remove_event_dots (E.Source source, Gee.Collection events) { ++ grid.remove_event_dots (source, events); ++ } + } + } +diff --git a/src/Widgets/EventsListBox.vala b/src/Widgets/EventsListBox.vala +index 547e4c5..c25af2e 100644 +--- a/src/Widgets/EventsListBox.vala ++++ b/src/Widgets/EventsListBox.vala +@@ -1,6 +1,5 @@ + namespace DateTimeIndicator { + public class Widgets.EventsListBox : Gtk.ListBox { +- + public EventsListBox () { + selection_mode = Gtk.SelectionMode.NONE; + +@@ -22,7 +21,7 @@ namespace DateTimeIndicator { + set_sort_func (sort_function); + } + +- public void update_events (GLib.DateTime? selected_date) { ++ public void update_events (GLib.DateTime? selected_date, HashTable> source_events) { + foreach (unowned Gtk.Widget widget in get_children ()) { + widget.destroy (); + } +@@ -31,11 +30,9 @@ namespace DateTimeIndicator { + return; + } + +- var model = Models.CalendarModel.get_default (); +- + var events_on_day = new Gee.TreeMap (); + +- model.source_events.@foreach ((source, component_map) => { ++ source_events.@foreach ((source, component_map) => { + foreach (var comp in component_map.get_values ()) { + if (Util.calcomp_is_on_day (comp, selected_date)) { + unowned ICal.Component ical = comp.get_icalcomponent (); +diff --git a/src/Widgets/Grid.vala b/src/Widgets/Grid.vala +index 165d11d..660f212 100644 +--- a/src/Widgets/Grid.vala ++++ b/src/Widgets/Grid.vala +@@ -261,5 +261,36 @@ namespace DateTimeIndicator { + return date.get_year () * 10000 + date.get_month () * 100 + date.get_day_of_month (); + } + ++ public void add_event_dots (E.Source source, Gee.Collection events) { ++ data.foreach ((entry) => { ++ ++ foreach (var component in events) { ++ if (entry.value.skip_day ()) { ++ return true; ++ } ++ ++ if (Util.calcomp_is_on_day (component, entry.value.date)) { ++ entry.value.add_dots (source, component.get_icalcomponent ()); ++ } ++ } ++ ++ entry.value.show_event_grid (); ++ ++ return true; ++ }); ++ } ++ ++ public void remove_event_dots (E.Source source, Gee.Collection events) { ++ foreach (var component in events) { ++ unowned ICal.Component ical = component.get_icalcomponent (); ++ var event_uid = ical.get_uid (); ++ data.foreach ((entry) => { ++ if (entry.value.exist_event (event_uid)) { ++ entry.value.remove_dots (event_uid); ++ } ++ return true; ++ }); ++ } ++ } + } + } +diff --git a/src/Widgets/GridDay.vala b/src/Widgets/GridDay.vala +index 8c44443..00f82ea 100644 +--- a/src/Widgets/GridDay.vala ++++ b/src/Widgets/GridDay.vala +@@ -31,6 +31,8 @@ namespace DateTimeIndicator { + + public GLib.DateTime date { get; construct set; } + ++ private bool has_scrolled = false; ++ + private static Gtk.CssProvider provider; + private static Models.CalendarModel model; + +@@ -79,59 +81,100 @@ namespace DateTimeIndicator { + // Signals and handlers + button_press_event.connect (on_button_press); + key_press_event.connect (on_key_press); +- scroll_event.connect ((event) => {return Util.on_scroll_event (event);}); ++ scroll_event.connect (on_scroll_event); + + notify["date"].connect (() => { + label.label = date.get_day_of_month ().to_string (); + }); + + event_dots = new Gee.HashMap (); +- +- model.events_added.connect (add_event_dots); +- model.events_removed.connect (remove_event_dots); + } + +- private void add_event_dots (E.Source source, Gee.Collection events) { +- foreach (var component in events) { +- if (event_dots.size >= 3) { +- return; +- } ++ public bool on_scroll_event (Gdk.EventScroll event) { ++ double delta_x; ++ double delta_y; ++ event.get_scroll_deltas (out delta_x, out delta_y); ++ ++ double choice = delta_x; ++ ++ if (((int)delta_x).abs () < ((int)delta_y).abs ()) { ++ choice = delta_y; ++ } ++ ++ /* It's mouse scroll ! */ ++ if (choice == 1 || choice == -1) { ++ Models.CalendarModel.get_default ().change_month ((int)choice); + +- if (Util.calcomp_is_on_day (component, date)) { +- unowned ICal.Component ical = component.get_icalcomponent (); ++ return true; ++ } + +- var event_uid = ical.get_uid (); +- if (!event_dots.has_key (event_uid)) { +- var event_dot = new Gtk.Image (); +- event_dot.gicon = new ThemedIcon ("pager-checked-symbolic"); +- event_dot.pixel_size = 6; ++ if (has_scrolled == true) { ++ return true; ++ } + +- unowned Gtk.StyleContext style_context = event_dot.get_style_context (); +- style_context.add_class (Granite.STYLE_CLASS_ACCENT); +- style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ if (choice > 0.3) { ++ reset_timer.begin (); ++ Models.CalendarModel.get_default ().change_month (1); + +- var source_calendar = (E.SourceCalendar?) source.get_extension (E.SOURCE_EXTENSION_CALENDAR); +- Util.set_event_calendar_color (source_calendar, event_dot); ++ return true; ++ } + +- event_dots[event_uid] = event_dot; ++ if (choice < -0.3) { ++ reset_timer.begin (); ++ Models.CalendarModel.get_default ().change_month (-1); + +- event_grid.add (event_dot); +- } +- } ++ return true; + } + ++ return false; ++ } ++ ++ public async void reset_timer () { ++ has_scrolled = true; ++ Timeout.add (500, () => { ++ has_scrolled = false; ++ ++ return false; ++ }); ++ } ++ ++ public bool skip_day () { ++ return event_dots.size >= 3 ? true : false; ++ } ++ ++ public void show_event_grid () { + event_grid.show_all (); + } + +- private void remove_event_dots (E.Source source, Gee.Collection events) { +- foreach (var component in events) { +- unowned ICal.Component ical = component.get_icalcomponent (); +- var event_uid = ical.get_uid (); +- var dot = event_dots[event_uid]; +- if (dot != null) { +- dot.destroy (); +- event_dots.remove (event_uid); +- } ++ public void add_dots (E.Source source, ICal.Component ical) { ++ var event_uid = ical.get_uid (); ++ if (!event_dots.has_key (event_uid)) { ++ var event_dot = new Gtk. Image (); ++ event_dot.gicon = new ThemedIcon ("pager-checked-symbolic"); ++ event_dot.pixel_size = 6; ++ ++ unowned Gtk.StyleContext style_context = event_dot.get_style_context (); ++ style_context.add_class (Granite.STYLE_CLASS_ACCENT); ++ style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ ++ var source_calendar = (E.SourceCalendar?) source.get_extension (E.SOURCE_EXTENSION_CALENDAR); ++ Util.set_event_calendar_color (source_calendar, event_dot); ++ ++ event_dots[event_uid] = event_dot; ++ ++ event_grid.add (event_dot); ++ } ++ } ++ ++ public bool exist_event (string ical_uid) { ++ return event_dots.has_key (ical_uid); ++ } ++ ++ public void remove_dots (string event_uid) { ++ var dot = event_dots[event_uid]; ++ if (dot != null) { ++ dot.destroy (); ++ event_dots.unset (event_uid); + } + } + + +From c99db5e63b9c48aa1d069b97b98b0c07481de27d Mon Sep 17 00:00:00 2001 +From: Dirli +Date: Sat, 11 Apr 2020 23:44:14 +0300 +Subject: [PATCH 4/8] rename calendar elements + +--- + meson.build | 4 +-- + .../{GridDay.vala => CalendarDay.vala} | 4 +-- + src/Widgets/{Grid.vala => CalendarGrid.vala} | 26 +++++++-------- + src/Widgets/CalendarView.vala | 33 +++++++++---------- + 4 files changed, 33 insertions(+), 34 deletions(-) + rename src/Widgets/{GridDay.vala => CalendarDay.vala} (98%) + rename src/Widgets/{Grid.vala => CalendarGrid.vala} (91%) + +diff --git a/meson.build b/meson.build +index e348e3d..dcdd9b2 100644 +--- a/meson.build ++++ b/meson.build +@@ -44,11 +44,11 @@ shared_module( + 'src/Util/DateRange.vala', + 'src/Util/Util.vala', + 'src/Models/CalendarModel.vala', ++ 'src/Widgets/CalendarDay.vala', ++ 'src/Widgets/CalendarGrid.vala', + 'src/Widgets/CalendarView.vala', + 'src/Widgets/EventRow.vala', + 'src/Widgets/EventsListBox.vala', +- 'src/Widgets/Grid.vala', +- 'src/Widgets/GridDay.vala', + 'src/Widgets/PanelLabel.vala', + 'src/Services/EventsManager.vala', + 'src/Services/TimeManager.vala', +diff --git a/src/Widgets/GridDay.vala b/src/Widgets/CalendarDay.vala +similarity index 98% +rename from src/Widgets/GridDay.vala +rename to src/Widgets/CalendarDay.vala +index 00f82ea..a5ca920 100644 +--- a/src/Widgets/GridDay.vala ++++ b/src/Widgets/CalendarDay.vala +@@ -23,7 +23,7 @@ namespace DateTimeIndicator { + /** + * Represents a single day on the grid. + */ +- public class Widgets.GridDay : Gtk.EventBox { ++ public class Widgets.CalendarDay : Gtk.EventBox { + /* + * Event emitted when the day is double clicked or the ENTER key is pressed. + */ +@@ -41,7 +41,7 @@ namespace DateTimeIndicator { + private Gtk.Label label; + private bool valid_grab = false; + +- public GridDay (GLib.DateTime date) { ++ public CalendarDay (GLib.DateTime date) { + Object (date: date); + } + +diff --git a/src/Widgets/Grid.vala b/src/Widgets/CalendarGrid.vala +similarity index 91% +rename from src/Widgets/Grid.vala +rename to src/Widgets/CalendarGrid.vala +index 660f212..66e2757 100644 +--- a/src/Widgets/Grid.vala ++++ b/src/Widgets/CalendarGrid.vala +@@ -24,7 +24,7 @@ namespace DateTimeIndicator { + /** + * Represents the entire date grid as a table. + */ +- public class Widgets.Grid : Gtk.Grid { ++ public class Widgets.CalendarGrid : Gtk.Grid { + public Util.DateRange grid_range { get; private set; } + + /* +@@ -34,8 +34,8 @@ namespace DateTimeIndicator { + + public signal void selection_changed (GLib.DateTime new_date); + +- private Gee.HashMap data; +- private GridDay selected_gridday; ++ private Gee.HashMap data; ++ private Widgets.CalendarDay selected_gridday; + private Gtk.Label[] header_labels; + private Gtk.Revealer[] week_labels; + +@@ -61,12 +61,12 @@ namespace DateTimeIndicator { + + Indicator.settings.bind ("show-weeks", week_sep_revealer, "reveal-child", GLib.SettingsBindFlags.DEFAULT); + +- data = new Gee.HashMap (); ++ data = new Gee.HashMap (); + events |= Gdk.EventMask.SCROLL_MASK; + events |= Gdk.EventMask.SMOOTH_SCROLL_MASK; + } + +- private void on_day_focus_in (GridDay day) { ++ private void on_day_focus_in (Widgets.CalendarDay day) { + debug ("on_day_focus_in %s", day.date.to_string ()); + if (selected_gridday != null) { + selected_gridday.set_selected (false); +@@ -94,7 +94,7 @@ namespace DateTimeIndicator { + Gee.List dates = grid_range.to_list (); + for (int i = 0; i < dates.size; i++) { + var date = dates[i]; +- GridDay? day = data[day_hash (date)]; ++ Widgets.CalendarDay? day = data[day_hash (date)]; + if (day != null && day.name == "today") { + day.grab_focus_force (); + return; +@@ -119,7 +119,7 @@ namespace DateTimeIndicator { + + var new_dates = new_range.to_list (); + +- var data_new = new Gee.HashMap (); ++ var data_new = new Gee.HashMap (); + + /* Assert that a valid number of weeks should be displayed */ + assert (new_dates.size % 7 == 0); +@@ -138,7 +138,7 @@ namespace DateTimeIndicator { + + for (i = 0; i < new_dates.size; i++) { + var new_date = new_dates[i]; +- GridDay day; ++ Widgets.CalendarDay day; + + if (i < old_dates.size) { + /* A widget already exists for this date, just change it */ +@@ -147,7 +147,7 @@ namespace DateTimeIndicator { + day = update_day (data[day_hash (old_date)], new_date, today, month_start); + } else { + /* Still update_day to get the color of etc. right */ +- day = update_day (new GridDay (new_date), new_date, today, month_start); ++ day = update_day (new Widgets.CalendarDay (new_date), new_date, today, month_start); + day.on_event_add.connect ((date) => on_event_add (date)); + day.scroll_event.connect ((event) => { scroll_event (event); return false; }); + day.focus_in_event.connect ((event) => { +@@ -182,9 +182,9 @@ namespace DateTimeIndicator { + } + + /** +- * Updates the given GridDay so that it shows the given date. Changes to its style etc. ++ * Updates the given CalendarDay so that it shows the given date. Changes to its style etc. + */ +- private GridDay update_day (GridDay day, GLib.DateTime new_date, GLib.DateTime today, GLib.DateTime month_start) { ++ private Widgets.CalendarDay update_day (Widgets.CalendarDay day, GLib.DateTime new_date, GLib.DateTime today, GLib.DateTime month_start) { + update_today_style (day, new_date, today); + if (new_date.get_month () == month_start.get_month ()) { + day.sensitive_container (true); +@@ -237,13 +237,13 @@ namespace DateTimeIndicator { + int i = 0; + for (i = 0; i < dates.size; i++) { + var date = dates[i]; +- GridDay? day = data[day_hash (date)]; ++ Widgets.CalendarDay? day = data[day_hash (date)]; + if (day == null) return; + update_today_style (day, date, today); + } + } + +- private void update_today_style (GridDay day, GLib.DateTime date, GLib.DateTime today) { ++ private void update_today_style (Widgets.CalendarDay day, GLib.DateTime date, GLib.DateTime today) { + if (date.get_day_of_year () == today.get_day_of_year () && date.get_year () == today.get_year ()) { + day.name = "today"; + day.get_style_context ().add_class (Granite.STYLE_CLASS_ACCENT); +diff --git a/src/Widgets/CalendarView.vala b/src/Widgets/CalendarView.vala +index 070580f..db2139c 100644 +--- a/src/Widgets/CalendarView.vala ++++ b/src/Widgets/CalendarView.vala +@@ -26,7 +26,7 @@ namespace DateTimeIndicator { + + public GLib.DateTime? selected_date { get; private set; } + +- private Widgets.Grid grid; ++ private Widgets.CalendarGrid calendar_grid; + private Gtk.Stack stack; + private Gtk.Grid big_grid; + +@@ -105,20 +105,20 @@ namespace DateTimeIndicator { + } + + private Gtk.Grid create_big_grid () { +- grid = new Widgets.Grid (); +- grid.show_all (); ++ calendar_grid = new Widgets.CalendarGrid (); ++ calendar_grid.show_all (); + +- grid.on_event_add.connect ((date) => { ++ calendar_grid.on_event_add.connect ((date) => { + show_date_in_maya (date); + day_double_click (); + }); + +- grid.selection_changed.connect ((date) => { ++ calendar_grid.selection_changed.connect ((date) => { + selected_date = date; + selection_changed (date); + }); + +- return grid; ++ return calendar_grid; + } + + public void show_today () { +@@ -131,7 +131,7 @@ namespace DateTimeIndicator { + } + sync_with_model (); + +- grid.set_focus_to_today (); ++ calendar_grid.set_focus_to_today (); + } + + // TODO: As far as maya supports it use the Dbus Activation feature to run the calendar-app. +@@ -156,23 +156,23 @@ namespace DateTimeIndicator { + /* Sets the calendar widgets to the date range of the model */ + private void sync_with_model () { + var model = Models.CalendarModel.get_default (); +- if (grid.grid_range != null && (model.data_range.equals (grid.grid_range) || grid.grid_range.first_dt.compare (model.data_range.first_dt) == 0)) { +- grid.update_today (); ++ if (calendar_grid.grid_range != null && (model.data_range.equals (calendar_grid.grid_range) || calendar_grid.grid_range.first_dt.compare (model.data_range.first_dt) == 0)) { ++ calendar_grid.update_today (); + return; // nothing else to do + } + + GLib.DateTime previous_first = null; +- if (grid.grid_range != null) +- previous_first = grid.grid_range.first_dt; ++ if (calendar_grid.grid_range != null) ++ previous_first = calendar_grid.grid_range.first_dt; + + big_grid = create_big_grid (); + stack.add (big_grid); + +- grid.set_range (model.data_range, model.month_start); +- grid.update_weeks (model.data_range.first_dt, model.num_weeks); ++ calendar_grid.set_range (model.data_range, model.month_start); ++ calendar_grid.update_weeks (model.data_range.first_dt, model.num_weeks); + + if (previous_first != null) { +- if (previous_first.compare (grid.grid_range.first_dt) == -1) { ++ if (previous_first.compare (calendar_grid.grid_range.first_dt) == -1) { + stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT; + } else { + stack.transition_type = Gtk.StackTransitionType.SLIDE_RIGHT; +@@ -183,12 +183,11 @@ namespace DateTimeIndicator { + } + + public void add_event_dots (E.Source source, Gee.Collection events) { +- grid.add_event_dots (source, events); ++ calendar_grid.add_event_dots (source, events); + } + +- + public void remove_event_dots (E.Source source, Gee.Collection events) { +- grid.remove_event_dots (source, events); ++ calendar_grid.remove_event_dots (source, events); + } + } + } + +From 6b1b15305a9c90ff8b7e7244727a225984d34a14 Mon Sep 17 00:00:00 2001 +From: Dirli +Date: Sun, 12 Apr 2020 03:10:24 +0300 +Subject: [PATCH 5/8] evolution data server now optional + +--- + meson.build | 55 ++++++++++++++--------- + meson_options.txt | 1 + + src/Indicator.vala | 43 ++++++++++-------- + src/Util/Util.vala | 85 ++++++++++++++++------------------- + src/Widgets/CalendarDay.vala | 2 + + src/Widgets/CalendarGrid.vala | 2 + + src/Widgets/CalendarView.vala | 2 + + 7 files changed, 104 insertions(+), 86 deletions(-) + create mode 100644 meson_options.txt + +diff --git a/meson.build b/meson.build +index dcdd9b2..5fee9ab 100644 +--- a/meson.build ++++ b/meson.build +@@ -24,18 +24,40 @@ gresource = gnome.compile_resources( + wingpanel_dep = dependency('wingpanel-2.0') + wingpanel_indicatorsdir = wingpanel_dep.get_pkgconfig_variable('indicatorsdir', define_variable: ['libdir', libdir]) + ++deps = [ ++ dependency('glib-2.0'), ++ dependency('gobject-2.0'), ++ dependency('granite'), ++ dependency('gtk+-3.0'), ++ wingpanel_dep, ++ meson.get_compiler('vala').find_library('posix') ++] + +-libecal_dep = dependency('libecal-2.0', required: false) +-if libecal_dep.found() +- libical_dep = dependency('libical-glib') +- add_project_arguments('--define=E_CAL_2_0', language: 'vala') +- add_project_arguments('-DLIBICAL_GLIB_UNSTABLE_API=1', language: 'c') +-else +- libecal_dep = dependency('libecal-1.2', version: '>=3.8.0') +- libical_dep = dependency('libical') +- add_project_arguments('--vapidir', join_paths(meson.current_source_dir(), 'vapi'), language: 'vala') ++opt_files = [] ++ ++if get_option('evo') ++ libecal_dep = dependency ('libecal-2.0', required: false) ++ if libecal_dep.found() ++ deps += dependency('libical-glib') ++ deps += libecal_dep ++ add_project_arguments('--define=E_CAL_2_0', language: 'vala') ++ add_project_arguments('-DLIBICAL_GLIB_UNSTABLE_API=1', language: 'c') ++ else ++ deps += dependency('libecal-1.2', version: '>=3.8.0') ++ deps += dependency('libical') ++ add_project_arguments('--vapidir', join_paths(meson.current_source_dir(), 'vapi'), language: 'vala') ++ endif ++ ++ deps += dependency ('libedataserver-1.2') ++ opt_files += files ( ++ 'src/Widgets/EventRow.vala', ++ 'src/Widgets/EventsListBox.vala', ++ 'src/Services/EventsManager.vala', ++ ) ++ add_project_arguments('--define=USE_EVO', language: 'vala') + endif + ++ + shared_module( + meson.project_name(), + gresource, +@@ -47,21 +69,10 @@ shared_module( + 'src/Widgets/CalendarDay.vala', + 'src/Widgets/CalendarGrid.vala', + 'src/Widgets/CalendarView.vala', +- 'src/Widgets/EventRow.vala', +- 'src/Widgets/EventsListBox.vala', + 'src/Widgets/PanelLabel.vala', +- 'src/Services/EventsManager.vala', + 'src/Services/TimeManager.vala', +- dependencies: [ +- dependency('glib-2.0'), +- dependency('gobject-2.0'), +- dependency('granite'), +- dependency('gtk+-3.0'), +- libecal_dep, +- dependency('libedataserver-1.2'), +- libical_dep, +- wingpanel_dep, +- ], ++ opt_files, ++ dependencies: deps, + install: true, + install_dir : wingpanel_indicatorsdir, + ) +diff --git a/meson_options.txt b/meson_options.txt +new file mode 100644 +index 0000000..a1c9f0e +--- /dev/null ++++ b/meson_options.txt +@@ -0,0 +1 @@ ++option ('evo', type : 'boolean', value : true) +diff --git a/src/Indicator.vala b/src/Indicator.vala +index bf4358f..31c7af5 100644 +--- a/src/Indicator.vala ++++ b/src/Indicator.vala +@@ -23,9 +23,11 @@ namespace DateTimeIndicator { + + private Widgets.PanelLabel panel_label; + private Widgets.CalendarView calendar; +- private Widgets.EventsListBox event_listbox; + ++#if USE_EVO ++ private Widgets.EventsListBox event_listbox; + private Services.EventsManager event_manager; ++#endif + + private Gtk.Grid main_grid; + private uint update_events_idle_source = 0; +@@ -60,7 +62,20 @@ namespace DateTimeIndicator { + if (main_grid == null) { + calendar = new Widgets.CalendarView (); + calendar.margin_bottom = 6; ++ calendar.day_double_click.connect (() => { ++ close (); ++ }); ++ ++ var settings_button = new Gtk.ModelButton (); ++ settings_button.text = _("Date & Time Settings…"); + ++ main_grid = new Gtk.Grid (); ++ main_grid.margin_top = 12; ++ main_grid.attach (calendar, 0, 0); ++ main_grid.attach (new Wingpanel.Widgets.Separator (), 0, 1); ++ main_grid.attach (settings_button, 0, 2); ++ ++#if USE_EVO + event_manager = new Services.EventsManager (); + event_manager.events_updated.connect (update_events_model); + event_manager.events_added.connect ((source, events) => { +@@ -73,20 +88,16 @@ namespace DateTimeIndicator { + }); + + event_listbox = new Widgets.EventsListBox (); ++ event_listbox.row_activated.connect ((row) => { ++ calendar.show_date_in_maya (((EventRow) row).date); ++ close (); ++ }); + + var scrolled_window = new Gtk.ScrolledWindow (null, null); + scrolled_window.hscrollbar_policy = Gtk.PolicyType.NEVER; + scrolled_window.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC; + scrolled_window.add (event_listbox); + +- var settings_button = new Gtk.ModelButton (); +- settings_button.text = _("Date & Time Settings…"); +- +- main_grid = new Gtk.Grid (); +- main_grid.margin_top = 12; +- main_grid.attach (calendar, 0, 0); +- main_grid.attach (new Wingpanel.Widgets.Separator (), 0, 1); +- main_grid.attach (settings_button, 0, 2); + main_grid.attach (new Gtk.Separator (Gtk.Orientation.VERTICAL), 1, 0, 1, 3); + main_grid.attach (scrolled_window, 2, 0, 1, 3); + +@@ -94,23 +105,17 @@ namespace DateTimeIndicator { + size_group.add_widget (calendar); + size_group.add_widget (event_listbox); + +- calendar.day_double_click.connect (() => { +- close (); +- }); +- + calendar.selection_changed.connect ((date) => { + idle_update_events (); + }); +- +- event_listbox.row_activated.connect ((row) => { +- calendar.show_date_in_maya (((EventRow) row).date); +- close (); +- }); ++#endif + + var model = Models.CalendarModel.get_default (); + model.notify["month-start"].connect (() => { + model.compute_ranges (); ++#if USE_EVO + event_manager.load_all_sources (); ++#endif + }); + + settings_button.clicked.connect (() => { +@@ -125,6 +130,7 @@ namespace DateTimeIndicator { + return main_grid; + } + ++#if USE_EVO + private void update_events_model (E.Source source, Gee.Collection events) { + if (opened_widget) { + idle_update_events (); +@@ -143,6 +149,7 @@ namespace DateTimeIndicator { + return GLib.Source.REMOVE; + }); + } ++#endif + + public override void opened () { + calendar.show_today (); +diff --git a/src/Util/Util.vala b/src/Util/Util.vala +index b0bdf98..26b343a 100644 +--- a/src/Util/Util.vala ++++ b/src/Util/Util.vala +@@ -32,38 +32,6 @@ namespace DateTimeIndicator.Util { + return datetime.add_full (0, 0, 0, -datetime.get_hour (), -datetime.get_minute (), -datetime.get_second ()); + } + +- /** +- * Converts the given ICal.Time to a DateTime. +- */ +- public TimeZone timezone_from_ical (ICal.Time date) { +- int is_daylight; +- var interval = date.get_timezone ().get_utc_offset (null, out is_daylight); +- bool is_positive = interval >= 0; +- interval = interval.abs (); +- var hours = (interval / 3600); +- var minutes = (interval % 3600) / 60; +- var hour_string = "%s%02d:%02d".printf (is_positive ? "+" : "-", hours, minutes); +- +- return new TimeZone (hour_string); +- } +- +- /** +- * Converts the given ICal.Time to a DateTime. +- * XXX : Track next versions of evolution in order to convert ICal.Timezone to GLib.TimeZone with a dedicated function… +- */ +- public GLib.DateTime ical_to_date_time (ICal.Time date) { +-#if E_CAL_2_0 +- int year, month, day, hour, minute, second; +- date.get_date (out year, out month, out day); +- date.get_time (out hour, out minute, out second); +- return new GLib.DateTime (timezone_from_ical (date), year, month, +- day, hour, minute, second); +-#else +- return new GLib.DateTime (timezone_from_ical (date), date.year, date.month, +- date.day, date.hour, date.minute, date.second); +-#endif +- } +- + /** + * Say if an event lasts all day. + */ +@@ -78,6 +46,7 @@ namespace DateTimeIndicator.Util { + } + } + ++#if USE_EVO + private Gee.HashMap? providers; + public void set_event_calendar_color (E.SourceCalendar cal, Gtk.Widget widget) { + if (providers == null) { +@@ -104,6 +73,38 @@ namespace DateTimeIndicator.Util { + style_context.add_provider (providers[color], Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + } + ++ /** ++ * Converts the given ICal.Time to a DateTime. ++ */ ++ public TimeZone timezone_from_ical (ICal.Time date) { ++ int is_daylight; ++ var interval = date.get_timezone ().get_utc_offset (null, out is_daylight); ++ bool is_positive = interval >= 0; ++ interval = interval.abs (); ++ var hours = (interval / 3600); ++ var minutes = (interval % 3600) / 60; ++ var hour_string = "%s%02d:%02d".printf (is_positive ? "+" : "-", hours, minutes); ++ ++ return new TimeZone (hour_string); ++ } ++ ++ /** ++ * Converts the given ICal.Time to a DateTime. ++ * XXX : Track next versions of evolution in order to convert ICal.Timezone to GLib.TimeZone with a dedicated function… ++ */ ++ public GLib.DateTime ical_to_date_time (ICal.Time date) { ++#if E_CAL_2_0 ++ int year, month, day, hour, minute, second; ++ date.get_date (out year, out month, out day); ++ date.get_time (out hour, out minute, out second); ++ return new GLib.DateTime (timezone_from_ical (date), year, month, ++ day, hour, minute, second); ++#else ++ return new GLib.DateTime (timezone_from_ical (date), date.year, date.month, ++ date.day, date.hour, date.minute, date.second); ++#endif ++ } ++ + /* + * Gee Utility Functions + */ +@@ -113,6 +114,11 @@ namespace DateTimeIndicator.Util { + return key.dup_uid (). hash (); + } + ++ /* Returns true if 'a' and 'b' are the same E.Source */ ++ public bool source_equal_func (E.Source a, E.Source b) { ++ return a.dup_uid () == b.dup_uid (); ++ } ++ + /* Returns true if 'a' and 'b' are the same ECal.Component */ + public bool calcomponent_equal_func (ECal.Component a, ECal.Component b) { + return a.get_id ().equal (b.get_id ()); +@@ -178,18 +184,5 @@ namespace DateTimeIndicator.Util { + + return false; + } +- +- /* Returns true if 'a' and 'b' are the same E.Source */ +- public bool source_equal_func (E.Source a, E.Source b) { +- return a.dup_uid () == b.dup_uid (); +- } +- +- // public async void reset_timer () { +- // has_scrolled = true; +- // Timeout.add (500, () => { +- // has_scrolled = false; +- // +- // return false; +- // }); +- // } ++#endif + } +diff --git a/src/Widgets/CalendarDay.vala b/src/Widgets/CalendarDay.vala +index a5ca920..f9c742a 100644 +--- a/src/Widgets/CalendarDay.vala ++++ b/src/Widgets/CalendarDay.vala +@@ -146,6 +146,7 @@ namespace DateTimeIndicator { + event_grid.show_all (); + } + ++#if USE_EVO + public void add_dots (E.Source source, ICal.Component ical) { + var event_uid = ical.get_uid (); + if (!event_dots.has_key (event_uid)) { +@@ -177,6 +178,7 @@ namespace DateTimeIndicator { + event_dots.unset (event_uid); + } + } ++#endif + + public void set_selected (bool selected) { + if (selected) { +diff --git a/src/Widgets/CalendarGrid.vala b/src/Widgets/CalendarGrid.vala +index 66e2757..1d1e06c 100644 +--- a/src/Widgets/CalendarGrid.vala ++++ b/src/Widgets/CalendarGrid.vala +@@ -261,6 +261,7 @@ namespace DateTimeIndicator { + return date.get_year () * 10000 + date.get_month () * 100 + date.get_day_of_month (); + } + ++#if USE_EVO + public void add_event_dots (E.Source source, Gee.Collection events) { + data.foreach ((entry) => { + +@@ -292,5 +293,6 @@ namespace DateTimeIndicator { + }); + } + } ++#endif + } + } +diff --git a/src/Widgets/CalendarView.vala b/src/Widgets/CalendarView.vala +index db2139c..77c233c 100644 +--- a/src/Widgets/CalendarView.vala ++++ b/src/Widgets/CalendarView.vala +@@ -182,6 +182,7 @@ namespace DateTimeIndicator { + stack.set_visible_child (big_grid); + } + ++#if USE_EVO + public void add_event_dots (E.Source source, Gee.Collection events) { + calendar_grid.add_event_dots (source, events); + } +@@ -189,5 +190,6 @@ namespace DateTimeIndicator { + public void remove_event_dots (E.Source source, Gee.Collection events) { + calendar_grid.remove_event_dots (source, events); + } ++#endif + } + } + +From 16715f5114c0597d6961880bf877f04414400334 Mon Sep 17 00:00:00 2001 +From: Dirli +Date: Mon, 13 Apr 2020 22:21:07 +0300 +Subject: [PATCH 6/8] returned dots in the calendar + +--- + src/Indicator.vala | 6 ++-- + src/Services/EventsManager.vala | 28 +++++---------- + src/Widgets/CalendarDay.vala | 62 ++++++++++++++++++--------------- + src/Widgets/CalendarGrid.vala | 29 ++++++--------- + 4 files changed, 57 insertions(+), 68 deletions(-) + +diff --git a/src/Indicator.vala b/src/Indicator.vala +index 31c7af5..c7550aa 100644 +--- a/src/Indicator.vala ++++ b/src/Indicator.vala +@@ -105,8 +105,10 @@ namespace DateTimeIndicator { + size_group.add_widget (calendar); + size_group.add_widget (event_listbox); + +- calendar.selection_changed.connect ((date) => { +- idle_update_events (); ++ event_manager.open.begin ((obj, res) => { ++ calendar.selection_changed.connect ((date) => { ++ idle_update_events (); ++ }); + }); + #endif + +diff --git a/src/Services/EventsManager.vala b/src/Services/EventsManager.vala +index d939777..ad0397d 100644 +--- a/src/Services/EventsManager.vala ++++ b/src/Services/EventsManager.vala +@@ -10,33 +10,25 @@ namespace DateTimeIndicator { + private HashTable source_client; + private HashTable source_view; + +- public EventsManager () { +- +- } +- + construct { + source_client = new HashTable (str_hash, str_equal); + source_events = new HashTable > (Util.source_hash_func, Util.source_equal_func); + source_view = new HashTable (str_hash, str_equal); +- +- open.begin (); + } + +- private async void open () { ++ public async void open () { + try { + registry = yield new E.SourceRegistry (null); + registry.source_removed.connect (remove_source); +- registry.source_added.connect ((source) => add_source_async.begin (source)); ++ registry.source_added.connect (add_source); + + // Add sources + registry.list_sources (E.SOURCE_EXTENSION_CALENDAR).foreach ((source) => { +- E.SourceCalendar cal = (E.SourceCalendar)source.get_extension (E.SOURCE_EXTENSION_CALENDAR); ++ E.SourceCalendar cal = (E.SourceCalendar) source.get_extension (E.SOURCE_EXTENSION_CALENDAR); + if (cal.selected == true && source.enabled == true) { +- add_source_async.begin (source); ++ add_source (source); + } + }); +- +- load_all_sources (); + } catch (GLib.Error error) { + critical (error.message); + } +@@ -46,7 +38,7 @@ namespace DateTimeIndicator { + lock (source_client) { + foreach (var id in source_client.get_keys ()) { + var source = registry.ref_source (id); +- E.SourceCalendar cal = (E.SourceCalendar)source.get_extension (E.SOURCE_EXTENSION_CALENDAR); ++ E.SourceCalendar cal = (E.SourceCalendar) source.get_extension (E.SOURCE_EXTENSION_CALENDAR); + + if (cal.selected == true && source.enabled == true) { + load_source (source); +@@ -120,7 +112,7 @@ namespace DateTimeIndicator { + }); + } + +- private async void add_source_async (E.Source source) { ++ private void add_source (E.Source source) { + debug ("Adding source '%s'", source.dup_display_name ()); + try { + var client = (ECal.Client) ECal.Client.connect_sync (source, ECal.ClientSourceType.EVENTS, -1, null); +@@ -129,11 +121,7 @@ namespace DateTimeIndicator { + critical (e.message); + } + +- Idle.add (() => { +- load_source (source); +- +- return false; +- }); ++ load_source (source); + } + + private void debug_event (E.Source source, ECal.Component event) { +@@ -177,6 +165,8 @@ namespace DateTimeIndicator { + return true; + }); + }); ++ ++ events_added (source, added_events.read_only_view); + } + + #if E_CAL_2_0 +diff --git a/src/Widgets/CalendarDay.vala b/src/Widgets/CalendarDay.vala +index f9c742a..a2c4922 100644 +--- a/src/Widgets/CalendarDay.vala ++++ b/src/Widgets/CalendarDay.vala +@@ -36,7 +36,8 @@ namespace DateTimeIndicator { + private static Gtk.CssProvider provider; + private static Models.CalendarModel model; + +- private Gee.HashMap event_dots; ++ // private Gee.HashMap event_dots; ++ private Gee.ArrayList event_dots; + private Gtk.Grid event_grid; + private Gtk.Label label; + private bool valid_grab = false; +@@ -87,7 +88,8 @@ namespace DateTimeIndicator { + label.label = date.get_day_of_month ().to_string (); + }); + +- event_dots = new Gee.HashMap (); ++ // event_dots = new Gee.HashMap (); ++ event_dots = new Gee.ArrayList (); + } + + public bool on_scroll_event (Gdk.EventScroll event) { +@@ -138,44 +140,46 @@ namespace DateTimeIndicator { + }); + } + +- public bool skip_day () { +- return event_dots.size >= 3 ? true : false; +- } +- +- public void show_event_grid () { +- event_grid.show_all (); +- } +- + #if USE_EVO + public void add_dots (E.Source source, ICal.Component ical) { + var event_uid = ical.get_uid (); +- if (!event_dots.has_key (event_uid)) { +- var event_dot = new Gtk. Image (); +- event_dot.gicon = new ThemedIcon ("pager-checked-symbolic"); +- event_dot.pixel_size = 6; ++ if (event_dots.contains (event_uid)) { ++ return; ++ } + +- unowned Gtk.StyleContext style_context = event_dot.get_style_context (); +- style_context.add_class (Granite.STYLE_CLASS_ACCENT); +- style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ event_dots.add (event_uid); ++ if (event_dots.size > 3) { ++ return; ++ } + +- var source_calendar = (E.SourceCalendar?) source.get_extension (E.SOURCE_EXTENSION_CALENDAR); +- Util.set_event_calendar_color (source_calendar, event_dot); ++ var event_dot = new Gtk.Image (); ++ event_dot.gicon = new ThemedIcon ("pager-checked-symbolic"); ++ event_dot.pixel_size = 6; + +- event_dots[event_uid] = event_dot; ++ unowned Gtk.StyleContext style_context = event_dot.get_style_context (); ++ style_context.add_class (Granite.STYLE_CLASS_ACCENT); ++ style_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + +- event_grid.add (event_dot); +- } +- } ++ var source_calendar = (E.SourceCalendar?) source.get_extension (E.SOURCE_EXTENSION_CALENDAR); ++ Util.set_event_calendar_color (source_calendar, event_dot); + +- public bool exist_event (string ical_uid) { +- return event_dots.has_key (ical_uid); ++ event_grid.add (event_dot); ++ event_dot.show (); + } + + public void remove_dots (string event_uid) { +- var dot = event_dots[event_uid]; +- if (dot != null) { +- dot.destroy (); +- event_dots.unset (event_uid); ++ if (event_dots.contains (event_uid)) { ++ return; ++ } ++ ++ event_dots.remove (event_uid); ++ if (event_dots.size >= 3) { ++ return; ++ } ++ ++ var w = event_grid.get_children (); ++ if (w.length () > 0) { ++ w.nth_data (0).destroy (); + } + } + #endif +diff --git a/src/Widgets/CalendarGrid.vala b/src/Widgets/CalendarGrid.vala +index 1d1e06c..c544404 100644 +--- a/src/Widgets/CalendarGrid.vala ++++ b/src/Widgets/CalendarGrid.vala +@@ -263,22 +263,16 @@ namespace DateTimeIndicator { + + #if USE_EVO + public void add_event_dots (E.Source source, Gee.Collection events) { +- data.foreach ((entry) => { +- +- foreach (var component in events) { +- if (entry.value.skip_day ()) { +- return true; +- } +- +- if (Util.calcomp_is_on_day (component, entry.value.date)) { +- entry.value.add_dots (source, component.get_icalcomponent ()); +- } ++ foreach (var component in events) { ++ unowned ICal.Component? icomp = component.get_icalcomponent (); ++ ICal.Time? start_time = icomp.get_dtstart (); ++ time_t start_unix = start_time.as_timet (); ++ var t = new DateTime.from_unix_utc (start_unix); ++ var d_hash = day_hash (t); ++ if (data.has_key (d_hash)) { ++ data[d_hash].add_dots (source, component.get_icalcomponent ()); + } +- +- entry.value.show_event_grid (); +- +- return true; +- }); ++ } + } + + public void remove_event_dots (E.Source source, Gee.Collection events) { +@@ -286,9 +280,8 @@ namespace DateTimeIndicator { + unowned ICal.Component ical = component.get_icalcomponent (); + var event_uid = ical.get_uid (); + data.foreach ((entry) => { +- if (entry.value.exist_event (event_uid)) { +- entry.value.remove_dots (event_uid); +- } ++ entry.value.remove_dots (event_uid); ++ + return true; + }); + } + +From 149da28659883b01ceb0773b35c018d31aa1d912 Mon Sep 17 00:00:00 2001 +From: Dirli +Date: Tue, 14 Apr 2020 00:16:01 +0300 +Subject: [PATCH 7/8] issue #55 + +--- + src/Services/EventsManager.vala | 2 ++ + src/Widgets/CalendarDay.vala | 2 +- + src/Widgets/CalendarGrid.vala | 11 +++++++---- + 3 files changed, 10 insertions(+), 5 deletions(-) + +diff --git a/src/Services/EventsManager.vala b/src/Services/EventsManager.vala +index ad0397d..959762f 100644 +--- a/src/Services/EventsManager.vala ++++ b/src/Services/EventsManager.vala +@@ -208,6 +208,8 @@ namespace DateTimeIndicator { + removed_events.add (event); + debug_event (source, event); + } ++ ++ events.remove_all (cid.get_uid ()); + }); + + events_removed (source, removed_events.read_only_view); +diff --git a/src/Widgets/CalendarDay.vala b/src/Widgets/CalendarDay.vala +index a2c4922..10d088c 100644 +--- a/src/Widgets/CalendarDay.vala ++++ b/src/Widgets/CalendarDay.vala +@@ -168,7 +168,7 @@ namespace DateTimeIndicator { + } + + public void remove_dots (string event_uid) { +- if (event_dots.contains (event_uid)) { ++ if (!event_dots.contains (event_uid)) { + return; + } + +diff --git a/src/Widgets/CalendarGrid.vala b/src/Widgets/CalendarGrid.vala +index c544404..6e6cbdb 100644 +--- a/src/Widgets/CalendarGrid.vala ++++ b/src/Widgets/CalendarGrid.vala +@@ -279,11 +279,14 @@ namespace DateTimeIndicator { + foreach (var component in events) { + unowned ICal.Component ical = component.get_icalcomponent (); + var event_uid = ical.get_uid (); +- data.foreach ((entry) => { +- entry.value.remove_dots (event_uid); + +- return true; +- }); ++ ICal.Time? start_time = ical.get_dtstart (); ++ time_t start_unix = start_time.as_timet (); ++ var t = new DateTime.from_unix_utc (start_unix); ++ var d_hash = day_hash (t); ++ if (data.has_key (d_hash)) { ++ data[d_hash].remove_dots (event_uid); ++ } + } + } + #endif + +From a3910e2b8242b8c4837cc764da7f268a02d05d6e Mon Sep 17 00:00:00 2001 +From: Dirli +Date: Fri, 17 Apr 2020 00:52:53 +0300 +Subject: [PATCH 8/8] issue #127 + +--- + src/Indicator.vala | 1 + + src/Models/CalendarModel.vala | 17 ++++++--- + src/Services/EventsManager.vala | 17 +++++++++ + src/Widgets/CalendarDay.vala | 3 +- + src/Widgets/CalendarGrid.vala | 66 ++++++++++++++++++++++----------- + src/Widgets/CalendarView.vala | 17 ++++----- + src/Widgets/EventsListBox.vala | 6 ++- + 7 files changed, 87 insertions(+), 40 deletions(-) + +diff --git a/src/Indicator.vala b/src/Indicator.vala +index c7550aa..63614e0 100644 +--- a/src/Indicator.vala ++++ b/src/Indicator.vala +@@ -116,6 +116,7 @@ namespace DateTimeIndicator { + model.notify["month-start"].connect (() => { + model.compute_ranges (); + #if USE_EVO ++ event_listbox.clear_list (); + event_manager.load_all_sources (); + #endif + }); +diff --git a/src/Models/CalendarModel.vala b/src/Models/CalendarModel.vala +index d60a9ac..48b6e78 100644 +--- a/src/Models/CalendarModel.vala ++++ b/src/Models/CalendarModel.vala +@@ -57,12 +57,17 @@ namespace DateTimeIndicator { + compute_ranges (); + } + +- public void change_month (int relative) { +- month_start = month_start.add_months (relative); +- } +- +- public void change_year (int relative) { +- month_start = month_start.add_years (relative); ++ public void change_month (int m_relative, int y_relative = 0) { ++ if (y_relative == 0) { ++ month_start = month_start.add_months (m_relative); ++ } else { ++ if (m_relative == 0) { ++ month_start = month_start.add_years (y_relative); ++ } else { ++ GLib.DateTime tmp_date = month_start.add_months (m_relative); ++ month_start = tmp_date.add_years (y_relative); ++ } ++ } + } + + /* --- Helper Methods ---// */ +diff --git a/src/Services/EventsManager.vala b/src/Services/EventsManager.vala +index 959762f..6ece557 100644 +--- a/src/Services/EventsManager.vala ++++ b/src/Services/EventsManager.vala +@@ -1,3 +1,20 @@ ++/* ++ * Copyright (c) 2011-2020 elementary, Inc. (https://elementary.io) ++ * ++ * 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 DateTimeIndicator { + public class Services.EventsManager : GLib.Object { + public signal void events_added (E.Source source, Gee.Collection events); +diff --git a/src/Widgets/CalendarDay.vala b/src/Widgets/CalendarDay.vala +index 10d088c..735fdc1 100644 +--- a/src/Widgets/CalendarDay.vala ++++ b/src/Widgets/CalendarDay.vala +@@ -36,7 +36,6 @@ namespace DateTimeIndicator { + private static Gtk.CssProvider provider; + private static Models.CalendarModel model; + +- // private Gee.HashMap event_dots; + private Gee.ArrayList event_dots; + private Gtk.Grid event_grid; + private Gtk.Label label; +@@ -105,7 +104,7 @@ namespace DateTimeIndicator { + + /* It's mouse scroll ! */ + if (choice == 1 || choice == -1) { +- Models.CalendarModel.get_default ().change_month ((int)choice); ++ Models.CalendarModel.get_default ().change_month ((int) choice); + + return true; + } +diff --git a/src/Widgets/CalendarGrid.vala b/src/Widgets/CalendarGrid.vala +index 6e6cbdb..ef8edb6 100644 +--- a/src/Widgets/CalendarGrid.vala ++++ b/src/Widgets/CalendarGrid.vala +@@ -32,7 +32,7 @@ namespace DateTimeIndicator { + */ + public signal void on_event_add (GLib.DateTime date); + +- public signal void selection_changed (GLib.DateTime new_date); ++ public signal void selection_changed (GLib.DateTime new_date, bool up); + + private Gee.HashMap data; + private Widgets.CalendarDay selected_gridday; +@@ -66,25 +66,32 @@ namespace DateTimeIndicator { + events |= Gdk.EventMask.SMOOTH_SCROLL_MASK; + } + +- private void on_day_focus_in (Widgets.CalendarDay day) { +- debug ("on_day_focus_in %s", day.date.to_string ()); ++ private bool on_day_focus_in (Gdk.EventFocus event) { ++ var day = get_focus_child (); ++ if (day == null) { ++ return false; ++ } ++ + if (selected_gridday != null) { + selected_gridday.set_selected (false); + } + +- var selected_date = day.date; +- selected_gridday = day; +- day.set_selected (true); ++ var selected_date = (day as Widgets.CalendarDay).date; ++ selected_gridday = day as Widgets.CalendarDay; ++ (day as Widgets.CalendarDay).set_selected (true); + day.set_state_flags (Gtk.StateFlags.FOCUSED, false); +- selection_changed (selected_date); + var calmodel = Models.CalendarModel.get_default (); + var date_month = selected_date.get_month () - calmodel.month_start.get_month (); + var date_year = selected_date.get_year () - calmodel.month_start.get_year (); + + if (date_month != 0 || date_year != 0) { +- calmodel.change_month (date_month); +- calmodel.change_year (date_year); ++ selection_changed (selected_date, false); ++ calmodel.change_month (date_month, date_year); ++ } else { ++ selection_changed (selected_date, true); + } ++ ++ return false; + } + + public void set_focus_to_today () { +@@ -106,7 +113,7 @@ namespace DateTimeIndicator { + * Sets the given range to be displayed in the grid. Note that the number of days + * must remain the same. + */ +- public void set_range (Util.DateRange new_range, GLib.DateTime month_start) { ++ public void set_range (Util.DateRange new_range, GLib.DateTime month_start, GLib.DateTime? selected_date) { + var today = new GLib.DateTime.now_local (); + + Gee.List old_dates; +@@ -138,28 +145,46 @@ namespace DateTimeIndicator { + + for (i = 0; i < new_dates.size; i++) { + var new_date = new_dates[i]; +- Widgets.CalendarDay day; ++ Widgets.CalendarDay? day = null; + + if (i < old_dates.size) { + /* A widget already exists for this date, just change it */ + + var old_date = old_dates[i]; +- day = update_day (data[day_hash (old_date)], new_date, today, month_start); +- } else { ++ var d_hash = day_hash (old_date); ++ if (data.has_key (d_hash)) { ++ day = data[d_hash]; ++ } ++ } ++ ++ if (day == null) { + /* Still update_day to get the color of etc. right */ +- day = update_day (new Widgets.CalendarDay (new_date), new_date, today, month_start); ++ day = new Widgets.CalendarDay (new_date); + day.on_event_add.connect ((date) => on_event_add (date)); +- day.scroll_event.connect ((event) => { scroll_event (event); return false; }); +- day.focus_in_event.connect ((event) => { +- on_day_focus_in (day); +- ++ day.scroll_event.connect ((event) => { ++ scroll_event (event); + return false; + }); ++ day.focus_in_event.connect (on_day_focus_in); + + attach (day, col + 2, row); + day.show_all (); + } + ++ update_day (day, new_date, month_start); ++ update_today_style (day, new_date, today); ++ if (selected_date != null && day.date.equal (selected_date)) { ++ /* disabled the signal to avoid unnecessary signals and selected ++ * the specified day from the new period */ ++ debug (@"focus selected day $selected_date"); ++ day.focus_in_event.disconnect (on_day_focus_in); ++ day.grab_focus_force (); ++ day.set_selected (true); ++ day.set_state_flags (Gtk.StateFlags.FOCUSED, false); ++ selected_gridday = day; ++ day.focus_in_event.connect (on_day_focus_in); ++ } ++ + col = (col + 1) % 7; + row = (col == 0) ? row + 1 : row; + data_new.set (day_hash (new_date), day); +@@ -184,8 +209,7 @@ namespace DateTimeIndicator { + /** + * Updates the given CalendarDay so that it shows the given date. Changes to its style etc. + */ +- private Widgets.CalendarDay update_day (Widgets.CalendarDay day, GLib.DateTime new_date, GLib.DateTime today, GLib.DateTime month_start) { +- update_today_style (day, new_date, today); ++ private void update_day (Widgets.CalendarDay day, GLib.DateTime new_date, GLib.DateTime month_start) { + if (new_date.get_month () == month_start.get_month ()) { + day.sensitive_container (true); + } else { +@@ -193,8 +217,6 @@ namespace DateTimeIndicator { + } + + day.date = new_date; +- +- return day; + } + + public void update_weeks (GLib.DateTime date, int nr_of_weeks) { +diff --git a/src/Widgets/CalendarView.vala b/src/Widgets/CalendarView.vala +index 77c233c..fe957ab 100644 +--- a/src/Widgets/CalendarView.vala ++++ b/src/Widgets/CalendarView.vala +@@ -85,10 +85,7 @@ namespace DateTimeIndicator { + model.notify["data-range"].connect (() => { + label.label = model.month_start.format (_("%OB, %Y")); + +- sync_with_model (); +- +- selected_date = null; +- selection_changed (selected_date); ++ sync_with_model (selected_date != null); + }); + + left_button.clicked.connect (() => { +@@ -113,9 +110,11 @@ namespace DateTimeIndicator { + day_double_click (); + }); + +- calendar_grid.selection_changed.connect ((date) => { ++ calendar_grid.selection_changed.connect ((date, up) => { + selected_date = date; +- selection_changed (date); ++ if (up) { ++ selection_changed (date); ++ } + }); + + return calendar_grid; +@@ -154,9 +153,9 @@ namespace DateTimeIndicator { + } + + /* Sets the calendar widgets to the date range of the model */ +- private void sync_with_model () { ++ private void sync_with_model (bool show_selected = false) { + var model = Models.CalendarModel.get_default (); +- if (calendar_grid.grid_range != null && (model.data_range.equals (calendar_grid.grid_range) || calendar_grid.grid_range.first_dt.compare (model.data_range.first_dt) == 0)) { ++ if (!show_selected && calendar_grid.grid_range != null && (model.data_range.equals (calendar_grid.grid_range) || calendar_grid.grid_range.first_dt.compare (model.data_range.first_dt) == 0)) { + calendar_grid.update_today (); + return; // nothing else to do + } +@@ -168,7 +167,7 @@ namespace DateTimeIndicator { + big_grid = create_big_grid (); + stack.add (big_grid); + +- calendar_grid.set_range (model.data_range, model.month_start); ++ calendar_grid.set_range (model.data_range, model.month_start, show_selected ? selected_date : null); + calendar_grid.update_weeks (model.data_range.first_dt, model.num_weeks); + + if (previous_first != null) { +diff --git a/src/Widgets/EventsListBox.vala b/src/Widgets/EventsListBox.vala +index c25af2e..9022072 100644 +--- a/src/Widgets/EventsListBox.vala ++++ b/src/Widgets/EventsListBox.vala +@@ -21,10 +21,14 @@ namespace DateTimeIndicator { + set_sort_func (sort_function); + } + +- public void update_events (GLib.DateTime? selected_date, HashTable> source_events) { ++ public void clear_list () { + foreach (unowned Gtk.Widget widget in get_children ()) { + widget.destroy (); + } ++ } ++ ++ public void update_events (GLib.DateTime? selected_date, HashTable> source_events) { ++ clear_list (); + + if (selected_date == null) { + return; diff --git a/pkgs/desktops/pantheon/desktop/wingpanel-indicators/datetime/default.nix b/pkgs/desktops/pantheon/desktop/wingpanel-indicators/datetime/default.nix index ed70687e050..c6a0e2f7779 100644 --- a/pkgs/desktops/pantheon/desktop/wingpanel-indicators/datetime/default.nix +++ b/pkgs/desktops/pantheon/desktop/wingpanel-indicators/datetime/default.nix @@ -1,5 +1,5 @@ { stdenv -, fetchFromGitHub +, fetchFromGitHub , pantheon , pkgconfig , meson @@ -28,6 +28,15 @@ stdenv.mkDerivation rec { sha256 = "0a0pqrpmrdd5pch30lizr9righlc7165z7krmnaxrzd0fvfkbr2h"; }; + patches = [ + # https://github.com/elementary/wingpanel-indicator-datetime/pull/207 + # Fixes lots of issues despite being rejected upstream + # https://github.com/elementary/wingpanel-indicator-datetime/issues/206 + # https://github.com/elementary/wingpanel-indicator-datetime/issues/55 + # https://github.com/elementary/wingpanel-indicator-datetime/issues/127 + ./207.patch + ]; + passthru = { updateScript = pantheon.updateScript { attrPath = "pantheon.${pname}";