aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.in5
-rw-r--r--src/datetime-prefs.c176
-rw-r--r--src/datetime-service.c446
-rw-r--r--src/indicator-datetime.c5
4 files changed, 444 insertions, 188 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
index cec15a1..e152d86 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -40,10 +40,7 @@ libexec_PROGRAMS = indicator-datetime-service$(EXEEXT)
subdir = src
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/intltool.m4 \
- $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
- $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
- $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
diff --git a/src/datetime-prefs.c b/src/datetime-prefs.c
index e69d58b..5248911 100644
--- a/src/datetime-prefs.c
+++ b/src/datetime-prefs.c
@@ -30,6 +30,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <locale.h>
#include <langinfo.h>
#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <unique/unique.h>
#include <polkitgtk/polkitgtk.h>
@@ -46,6 +47,11 @@ GDBusProxy * proxy = NULL;
GtkWidget * auto_radio = NULL;
GtkWidget * tz_entry = NULL;
CcTimezoneMap * tzmap = NULL;
+GtkWidget * time_spin = NULL;
+GtkWidget * date_spin = NULL;
+guint save_time_id = 0;
+gboolean user_edited_time = FALSE;
+gboolean changing_time = FALSE;
/* Turns the boolean property into a string gsettings */
static GVariant *
@@ -238,13 +244,60 @@ void proxy_ready (GObject *object, GAsyncResult *res, gpointer user_data)
NULL, tz_query_answered, NULL);
}
+static gboolean
+are_spinners_focused (void)
+{
+ // save_time_id means that we were in focus and haven't finished our save
+ // yet, so act like we are still focused.
+ return save_time_id || gtk_widget_has_focus (time_spin) || gtk_widget_has_focus (date_spin);
+}
+
+static gboolean
+save_time (gpointer user_data)
+{
+ if (user_edited_time) {
+ gdouble current_value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (date_spin));
+ g_dbus_proxy_call (proxy, "SetTime", g_variant_new ("(x)", (guint64)current_value),
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, dbus_set_answered, "time");
+ }
+ user_edited_time = FALSE;
+ save_time_id = 0;
+ return FALSE;
+}
+
+static gboolean
+spin_focus_in (void)
+{
+ if (save_time_id > 0) {
+ g_source_remove (save_time_id);
+ save_time_id = 0;
+ }
+ return FALSE;
+}
+
+static gboolean
+spin_focus_out (void)
+{
+ /* We want to only save when both spinners are unfocused. But it's difficult
+ to tell who is about to get focus during a focus-out. So we set an idle
+ callback to save the time if we don't focus in to another spinner by that
+ time. */
+ if (save_time_id == 0) {
+ save_time_id = g_idle_add ((GSourceFunc)save_time, NULL);
+ }
+ return FALSE;
+}
+
static int
-input_time_text (GtkWidget * spinner, gdouble *value, gpointer user_data)
+input_time_text (GtkWidget * spinner, gdouble * value, gpointer user_data)
{
gboolean is_time = (gboolean)GPOINTER_TO_INT (g_object_get_data (G_OBJECT (spinner), "is-time"));
const gchar * text = gtk_entry_get_text (GTK_ENTRY (spinner));
- GDateTime * now = g_date_time_new_now_local ();
+ gdouble current_value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spinner));
+ *value = current_value;
+
+ GDateTime * now = g_date_time_new_from_unix_local (current_value);
gint year, month, day, hour, minute, second;
year = g_date_time_get_year (now);
month = g_date_time_get_month (now);
@@ -315,10 +368,13 @@ input_time_text (GtkWidget * spinner, gdouble *value, gpointer user_data)
return TRUE;
}
- GDateTime * datetime = g_date_time_new_local (year, month, day, hour, minute, second);
-
- g_dbus_proxy_call (proxy, "SetTime", g_variant_new ("(x)", g_date_time_to_unix (datetime)),
- G_DBUS_CALL_FLAGS_NONE, -1, NULL, dbus_set_answered, "time");
+ gboolean prev_changing = changing_time;
+ changing_time = TRUE;
+ GDateTime * new_time = g_date_time_new_local (year, month, day, hour, minute, second);
+ *value = g_date_time_to_unix (new_time);
+ user_edited_time = TRUE;
+ g_date_time_unref (new_time);
+ changing_time = prev_changing;
return TRUE;
}
@@ -326,12 +382,6 @@ input_time_text (GtkWidget * spinner, gdouble *value, gpointer user_data)
static gboolean
format_time_text (GtkWidget * spinner, gpointer user_data)
{
- if (gtk_widget_has_focus (spinner)) {
- /* Don't do anything if we have focus, user is likely editing us */
- return TRUE;
- }
-
- GDateTime * datetime = (GDateTime *)g_object_get_data (G_OBJECT (spinner), "datetime");
gboolean is_time = (gboolean)GPOINTER_TO_INT (g_object_get_data (G_OBJECT (spinner), "is-time"));
const gchar * format;
@@ -346,39 +396,71 @@ format_time_text (GtkWidget * spinner, gpointer user_data)
format = "%Y-%m-%d";
}
+ GDateTime * datetime = g_date_time_new_from_unix_local (gtk_spin_button_get_value (GTK_SPIN_BUTTON (spinner)));
gchar * formatted = g_date_time_format (datetime, format);
gtk_entry_set_text (GTK_ENTRY (spinner), formatted);
+ g_date_time_unref (datetime);
return TRUE;
}
+static void
+spin_copy_value (GtkSpinButton * spinner, GtkSpinButton * other)
+{
+ if (gtk_spin_button_get_value (spinner) != gtk_spin_button_get_value (other)) {
+ gtk_spin_button_set_value (other, gtk_spin_button_get_value (spinner));
+ }
+ if (!changing_time) { /* Means user pressed spin buttons */
+ user_edited_time = TRUE;
+ }
+}
+
static gboolean
-update_spinner (GtkWidget * spinner)
+update_spinners (void)
{
/* Add datetime object to spinner, which will hold the real time value, rather
- then using the value of the spinner itself. */
- GDateTime * datetime = g_date_time_new_now_local ();
- g_object_set_data_full (G_OBJECT (spinner), "datetime", datetime, (GDestroyNotify)g_date_time_unref);
-
- format_time_text (spinner, NULL);
-
+ then using the value of the spinner itself. And don't update while user is
+ editing. */
+ if (!are_spinners_focused ()) {
+ gboolean prev_changing = changing_time;
+ changing_time = TRUE;
+ GDateTime * now = g_date_time_new_now_local ();
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (time_spin), (gdouble)g_date_time_to_unix (now));
+ /* will be copied to other spin button */
+ g_date_time_unref (now);
+ changing_time = prev_changing;
+ }
return TRUE;
}
static void
-setup_time_spinner (GtkWidget * spinner, GtkWidget * other, gboolean is_time)
+setup_time_spinners (GtkWidget * time, GtkWidget * date)
{
- /* Set up spinner to have reasonable behavior */
- gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), FALSE);
- g_signal_connect (spinner, "input", G_CALLBACK (input_time_text), other);
- g_signal_connect (spinner, "output", G_CALLBACK (format_time_text), other);
- g_object_set_data (G_OBJECT (spinner), "is-time", GINT_TO_POINTER (is_time));
+ g_signal_connect (time, "input", G_CALLBACK (input_time_text), date);
+ g_signal_connect (date, "input", G_CALLBACK (input_time_text), time);
- /* 2 seconds is what the indicator itself uses */
- guint time_id = g_timeout_add_seconds (2, (GSourceFunc)update_spinner, spinner);
- g_signal_connect_swapped (spinner, "destroy", G_CALLBACK (g_source_remove), GINT_TO_POINTER (time_id));
+ g_signal_connect (time, "output", G_CALLBACK (format_time_text), date);
+ g_signal_connect (date, "output", G_CALLBACK (format_time_text), time);
+
+ g_signal_connect (time, "focus-in-event", G_CALLBACK (spin_focus_in), date);
+ g_signal_connect (date, "focus-in-event", G_CALLBACK (spin_focus_in), time);
- update_spinner (spinner);
+ g_signal_connect (time, "focus-out-event", G_CALLBACK (spin_focus_out), date);
+ g_signal_connect (date, "focus-out-event", G_CALLBACK (spin_focus_out), time);
+
+ g_signal_connect (time, "value-changed", G_CALLBACK (spin_copy_value), date);
+ g_signal_connect (date, "value-changed", G_CALLBACK (spin_copy_value), time);
+
+ g_object_set_data (G_OBJECT (time), "is-time", GINT_TO_POINTER (TRUE));
+ g_object_set_data (G_OBJECT (date), "is-time", GINT_TO_POINTER (FALSE));
+
+ time_spin = time;
+ date_spin = date;
+
+ /* 2 seconds is what the indicator itself uses */
+ guint time_id = g_timeout_add_seconds (2, (GSourceFunc)update_spinners, NULL);
+ g_signal_connect_swapped (time_spin, "destroy", G_CALLBACK (g_source_remove), GINT_TO_POINTER (time_id));
+ update_spinners ();
}
static void
@@ -426,6 +508,32 @@ timezone_selected (GtkEntryCompletion * widget, GtkTreeModel * model,
return FALSE; // Do normal action too
}
+static gboolean
+key_pressed (GtkWidget * widget, GdkEventKey * event, gpointer user_data)
+{
+ switch (event->keyval) {
+ case GDK_KEY_Escape:
+ gtk_widget_destroy (widget);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static GtkWidget *
+get_child_of_type (GtkContainer * parent, GType type)
+{
+ GList * children, * iter;
+
+ children = gtk_container_get_children (parent);
+ for (iter = children; iter; iter = iter->next) {
+ if (G_TYPE_CHECK_INSTANCE_TYPE (iter->data, type)) {
+ return GTK_WIDGET (iter->data);
+ }
+ }
+
+ return NULL;
+}
+
static GtkWidget *
create_dialog (void)
{
@@ -451,6 +559,11 @@ create_dialog (void)
polkit_lock_button_set_unlock_text (POLKIT_LOCK_BUTTON (polkit_button), _("Unlock to change these settings"));
polkit_lock_button_set_lock_text (POLKIT_LOCK_BUTTON (polkit_button), _("Lock to prevent further changes"));
gtk_box_pack_start (GTK_BOX (WIG ("timeDateBox")), polkit_button, FALSE, TRUE, 0);
+ /* Make sure border around button is visible */
+ GtkWidget * polkit_button_button = get_child_of_type (GTK_CONTAINER (polkit_button), GTK_TYPE_BUTTON);
+ if (polkit_button_button != NULL) {
+ gtk_button_set_relief (GTK_BUTTON (polkit_button_button), GTK_RELIEF_NORMAL);
+ }
/* Add map */
tzmap = cc_timezone_map_new ();
@@ -505,14 +618,14 @@ create_dialog (void)
gtk_widget_set_sensitive (WIG ("showEventsCheck"), (evo_path != NULL));
g_free (evo_path);
- setup_time_spinner (WIG ("timeSpinner"), WIG ("dateSpinner"), TRUE);
- setup_time_spinner (WIG ("dateSpinner"), WIG ("timeSpinner"), FALSE);
+ setup_time_spinners (WIG ("timeSpinner"), WIG ("dateSpinner"));
GtkWidget * dlg = WIG ("timeDateDialog");
auto_radio = WIG ("automaticTimeRadio");
tz_entry = WIG ("timezoneEntry");
g_signal_connect (WIG ("locationsButton"), "clicked", G_CALLBACK (show_locations), dlg);
+ g_signal_connect (dlg, "key-press-event", G_CALLBACK (key_pressed), NULL);
/* Grab proxy for settings daemon */
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL,
@@ -566,6 +679,7 @@ main (int argc, char ** argv)
gtk_widget_show_all (dlg);
g_signal_connect (dlg, "response", G_CALLBACK(gtk_main_quit), NULL);
+ g_signal_connect (dlg, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_main ();
}
diff --git a/src/datetime-service.c b/src/datetime-service.c
index 6303a98..3685c96 100644
--- a/src/datetime-service.c
+++ b/src/datetime-service.c
@@ -47,7 +47,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <libical/icaltime.h>
#include <cairo/cairo.h>
-
#include <oobs/oobs-timeconfig.h>
#include "datetime-interface.h"
@@ -73,6 +72,7 @@ static DatetimeInterface * dbus = NULL;
static DbusmenuMenuitem * date = NULL;
static DbusmenuMenuitem * calendar = NULL;
static DbusmenuMenuitem * settings = NULL;
+static DbusmenuMenuitem * events_separator = NULL;
static DbusmenuMenuitem * locations_separator = NULL;
static DbusmenuMenuitem * geo_location = NULL;
static DbusmenuMenuitem * current_location = NULL;
@@ -80,6 +80,8 @@ static DbusmenuMenuitem * current_location = NULL;
static DbusmenuMenuitem * add_appointment = NULL;
static GList * appointments = NULL;
static GList * dconflocations = NULL;
+static GList * comp_instances = NULL;
+static gboolean updating_appointments = FALSE;
GSettings *conf;
@@ -91,6 +93,13 @@ static GeoclueAddress * geo_address = NULL;
static gchar * current_timezone = NULL;
static gchar * geo_timezone = NULL;
+struct comp_instance {
+ ECalComponent *comp;
+ time_t start;
+ time_t end;
+ ESource *source;
+};
+
static void
set_timezone_label (DbusmenuMenuitem * mi, const gchar * location)
{
@@ -127,7 +136,7 @@ check_timezone_sync (void) {
} else {
g_debug("Timezones are different");
}
-
+
gboolean show = g_settings_get_boolean (conf, SETTINGS_SHOW_LOCATIONS_S);
if (geo_location != NULL && current_location != NULL) {
@@ -278,8 +287,50 @@ month_changed_cb (DbusmenuMenuitem * menuitem, GVariant *variant, guint timestam
return TRUE;
}
+static guint ecaltimer = 0;
+
+static void
+start_ecal_timer(void)
+{
+ if (ecaltimer != 0) g_source_remove(ecaltimer);
+ if (update_appointment_menu_items(NULL))
+ ecaltimer = g_timeout_add_seconds(60*5, update_appointment_menu_items, NULL);
+}
+
+static void
+stop_ecal_timer(void)
+{
+ if (ecaltimer != 0) g_source_remove(ecaltimer);
+}
+
+static void
+show_events_changed (void)
+{
+ if (g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) {
+ dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
+ dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
+ start_ecal_timer();
+ } else {
+ dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
+ dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
+ /* Remove all of the previous appointments */
+ if (appointments != NULL) {
+ g_debug("Freeing old appointments");
+ while (appointments != NULL) {
+ DbusmenuMenuitem * litem = DBUSMENU_MENUITEM(appointments->data);
+ g_debug("Freeing old appointment: %p", litem);
+ // Remove all the existing menu items which are in appointments.
+ appointments = g_list_remove(appointments, litem);
+ dbusmenu_menuitem_child_delete(root, DBUSMENU_MENUITEM(litem));
+ g_object_unref(G_OBJECT(litem));
+ }
+ }
+ stop_ecal_timer();
+ }
+}
+
/* Looks for the calendar application and enables the item if
- we have one */
+ we have one, starts ecal timer if events are turned on */
static gboolean
check_for_calendar (gpointer user_data)
{
@@ -293,21 +344,26 @@ check_for_calendar (gpointer user_data)
dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- DbusmenuMenuitem * separator = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
- dbusmenu_menuitem_child_add_position(root, separator, 2);
-
+ events_separator = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set(events_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
+ dbusmenu_menuitem_child_add_position(root, events_separator, 2);
add_appointment = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set (add_appointment, DBUSMENU_MENUITEM_PROP_LABEL, _("Add Appointment"));
+ dbusmenu_menuitem_property_set (add_appointment, DBUSMENU_MENUITEM_PROP_LABEL, _("Add Appointment"));
dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
g_signal_connect(G_OBJECT(add_appointment), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), "evolution -c calendar");
dbusmenu_menuitem_child_add_position (root, add_appointment, 3);
- // Update the calendar items every 5 minutes if it updates the first time
- if (update_appointment_menu_items(NULL))
- g_timeout_add_seconds(60*5, update_appointment_menu_items, NULL);
-
+
+ if (g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) {
+ dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
+ dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
+ start_ecal_timer();
+ } else {
+ dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
+ dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
+ stop_ecal_timer();
+ }
+
// Connect to event::month-changed
g_signal_connect(calendar, "event::month-changed", G_CALLBACK(month_changed_cb), NULL);
g_free(evo);
@@ -326,6 +382,7 @@ update_timezone_menu_items(gpointer user_data) {
g_debug("Updating timezone menu items");
gchar ** locations = g_settings_get_strv(conf, SETTINGS_LOCATIONS_S);
+
if (locations == NULL) {
g_debug("No locations configured (NULL)");
return FALSE;
@@ -381,7 +438,11 @@ update_timezone_menu_items(gpointer user_data) {
// Authentication function
static gchar *
-auth_func (ECal *ecal, const gchar *prompt, const gchar *key, gpointer user_data) {
+auth_func (ECal *ecal,
+ const gchar *prompt,
+ const gchar *key,
+ gpointer user_data)
+{
gboolean remember; // TODO: Is this useful? Should we be storing it somewhere?
ESource *source = e_cal_get_source (ecal);
gchar *auth_domain = e_source_get_duped_property (source, "auth-domain");
@@ -407,47 +468,92 @@ auth_func (ECal *ecal, const gchar *prompt, const gchar *key, gpointer user_data
return password;
}
+static gint
+compare_comp_instances (gconstpointer a,
+ gconstpointer b)
+{
+ const struct comp_instance *ci_a = a;
+ const struct comp_instance *ci_b = b;
+ time_t d = ci_a->start - ci_b->start;
+ if (d < 0) return -1;
+ else if (d > 0) return 1;
+ return 0;
+}
-// Compare function for g_list_sort of ECalComponent objects
-static gint
-compare_appointment_items (ECalComponent *a,
- ECalComponent *b) {
-
- ECalComponentDateTime datetime_a, datetime_b;
- struct tm tm_a, tm_b;
- time_t t_a, t_b;
- gint retval = 0;
-
- if (a == NULL || b == NULL) return retval;
-
- ECalComponentVType vtype = e_cal_component_get_vtype (a);
+static gboolean
+populate_appointment_instances (ECalComponent *comp,
+ time_t instance_start,
+ time_t instance_end,
+ gpointer data)
+{
+ g_debug("Appending item %p", comp);
- if (vtype != E_CAL_COMPONENT_EVENT && vtype != E_CAL_COMPONENT_TODO) return -1;
+ ECalComponentVType vtype = e_cal_component_get_vtype (comp);
+ if (vtype != E_CAL_COMPONENT_EVENT && vtype != E_CAL_COMPONENT_TODO) return FALSE;
- if (vtype == E_CAL_COMPONENT_EVENT)
- e_cal_component_get_dtstart (a, &datetime_a);
- else
- e_cal_component_get_due (a, &datetime_a);
- tm_a = icaltimetype_to_tm(datetime_a.value);
- t_a = mktime(&tm_a);
+ icalproperty_status status;
+ e_cal_component_get_status (comp, &status);
+ if (status == ICAL_STATUS_COMPLETED || status == ICAL_STATUS_CANCELLED) return FALSE;
- vtype = e_cal_component_get_vtype (b);
- if (vtype != E_CAL_COMPONENT_EVENT && vtype != E_CAL_COMPONENT_TODO) return 1;
+ g_object_ref(comp);
+
+ ECalComponentDateTime datetime;
+ icaltimezone *appointment_zone = NULL;
+ icaltimezone *current_zone = NULL;
if (vtype == E_CAL_COMPONENT_EVENT)
- e_cal_component_get_dtstart (b, &datetime_b);
+ e_cal_component_get_dtstart (comp, &datetime);
else
- e_cal_component_get_due (b, &datetime_b);
- tm_b = icaltimetype_to_tm(datetime_b.value);
- t_b = mktime(&tm_b);
+ e_cal_component_get_due (comp, &datetime);
- // Compare datetime_a and datetime_b, newest first in this sort.
- if (t_a > t_b) retval = 1;
- else if (t_a < t_b) retval = -1;
+ appointment_zone = icaltimezone_get_builtin_timezone_from_tzid(datetime.tzid);
+ current_zone = icaltimezone_get_builtin_timezone_from_tzid(current_timezone);
+ if (!appointment_zone || datetime.value->is_date) { // If it's today put in the current timezone?
+ appointment_zone = current_zone;
+ }
+
+ // TODO: Convert the timezone into a 3 letter abbreviation if it's different to current_timezone
+ // TODO: Add the appointment timezone to the list if it's not already there.
+
+ GSList *period_list = NULL, *l;
+ if (e_cal_component_has_recurrences (comp)) {
+ e_cal_component_get_rdate_list (comp, &period_list);
+ g_debug("ECalComponent has recurrences");
+ } else {
+ g_debug("ECalComponent doesn't have recurrences");
+ }
+
+ struct comp_instance *ci;
+ ci = g_new (struct comp_instance, 1);
+
+ // Do we get rdate_list?
+ if (period_list != NULL) {
+ g_debug("Got recurring periods");
+ for (l = period_list; l; l = l->next) {
+ ECalComponentPeriod *period = l->data;
+ struct tm tmp_tm = icaltimetype_to_tm_with_zone (&period->start, appointment_zone, current_zone);
+ time_t start = mktime(&tmp_tm);
+ g_debug("period time: %d", (int)start);
+
+ tmp_tm = icaltimetype_to_tm_with_zone (&period->u.end, appointment_zone, current_zone);
+ time_t end = mktime(&tmp_tm);
+
+ if (start >= instance_start && end < instance_end) {
+ ci->start = start;
+ ci->end = end;
+ }
+ }
+ } else {
+ ci->start = instance_start;
+ ci->end = instance_end;
+ g_debug("Got no recurring periods set time to start %s, end %s", ctime(&instance_start), ctime(&instance_end));
+ }
+
+ ci->comp = comp;
+ ci->source = E_SOURCE(data);
- e_cal_component_free_datetime (&datetime_a);
- e_cal_component_free_datetime (&datetime_b);
- return retval;
+ comp_instances = g_list_append(comp_instances, ci);
+ return TRUE;
}
/* Populate the menu with todays, next 5 appointments.
@@ -456,16 +562,19 @@ compare_appointment_items (ECalComponent *a,
* this is a problem mainly on the EDS side of things, not ours.
*/
static gboolean
-update_appointment_menu_items (gpointer user_data) {
+update_appointment_menu_items (gpointer user_data)
+{
// FFR: we should take into account short term timers, for instance
// tea timers, pomodoro timers etc... that people may add, this is hinted to in the spec.
if (calendar == NULL) return FALSE;
if (!g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) return FALSE;
+ if (updating_appointments) return TRUE;
+ updating_appointments = TRUE;
time_t t1, t2;
- gchar *query, *is, *ie, *ad;
- GList *objects = NULL, *l;
- GList *allobjects = NULL;
+ gchar *ad;
+ GList *l;
+ //GList *allobjects = NULL;
GSList *g;
GError *gerror = NULL;
gint i;
@@ -474,12 +583,7 @@ update_appointment_menu_items (gpointer user_data) {
time(&t1);
time(&t2);
- t2 += (time_t) (7 * 24 * 60 * 60); /* 7 days ahead of now */
-
- is = isodate_from_time_t(t1);
- ie = isodate_from_time_t(t2);
-
- gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
+ t2 += (time_t) (7 * 24 * 60 * 60); /* 7 days ahead of now, we actually need number_of_days_in_this_month */
/* Remove all of the previous appointments */
if (appointments != NULL) {
@@ -495,15 +599,23 @@ update_appointment_menu_items (gpointer user_data) {
}
// TODO Remove all highlights from the calendar widget
-
- // FIXME can we put a limit on the number of results? Or if not complete, or is event/todo? Or sort it by date?
- query = g_strdup_printf("(occur-in-time-range? (make-time\"%s\") (make-time\"%s\"))", is, ie);
if (!e_cal_get_sources(&sources, E_CAL_SOURCE_TYPE_EVENT, &gerror)) {
g_debug("Failed to get ecal sources\n");
return FALSE;
}
-
+
+ // Free comp_instances if not NULL
+ if (comp_instances != NULL) {
+ g_debug("Freeing comp_instances: may be an overlap\n");
+ for (l = comp_instances; l; l = l->next) {
+ const struct comp_instance *ci = l->data;
+ g_object_unref(ci->comp);
+ g_list_free(comp_instances);
+ comp_instances = NULL;
+ }
+ }
+
// iterate the query for all sources
for (g = e_source_list_peek_groups (sources); g; g = g->next) {
ESourceGroup *group = E_SOURCE_GROUP (g->data);
@@ -511,7 +623,7 @@ update_appointment_menu_items (gpointer user_data) {
for (s = e_source_group_peek_sources (group); s; s = s->next) {
ESource *source = E_SOURCE (s->data);
- g_signal_connect (G_OBJECT(source), "changed", G_CALLBACK (update_appointment_menu_items), NULL);
+ //g_signal_connect (G_OBJECT(source), "changed", G_CALLBACK (update_appointment_menu_items), NULL);
ECal *ecal = e_cal_new(source, E_CAL_SOURCE_TYPE_EVENT);
e_cal_set_auth_func (ecal, (ECalAuthFunc) auth_func, NULL);
@@ -522,55 +634,35 @@ update_appointment_menu_items (gpointer user_data) {
continue;
}
- g_debug("Getting objects with query: %s", query);
- if (!e_cal_get_object_list_as_comp(ecal, query, &objects, &gerror)) {
- g_debug("Failed to get objects\n");
- g_free(ecal);
- gerror = NULL;
- continue;
- }
- g_debug("Number of objects returned: %d", g_list_length(objects));
-
- if (allobjects == NULL) {
- allobjects = objects;
- } else if (objects != NULL) {
- allobjects = g_list_concat(allobjects, objects);
- g_object_unref(objects);
- }
+ g_debug("Generating instances");
+ e_cal_generate_instances (ecal, t1, t2, (ECalRecurInstanceFn) populate_appointment_instances, (gpointer) source);
+ g_debug("Number of objects returned: %d", g_list_length(comp_instances));
}
}
-
- // Sort the list see above FIXME regarding queries
- g_debug("Sorting objects list");
- allobjects = g_list_sort(allobjects, (GCompareFunc) compare_appointment_items);
+ GList *sorted_comp_instances = g_list_sort(comp_instances, compare_comp_instances);
+ comp_instances = NULL;
i = 0;
- for (l = allobjects; l; l = l->next) {
- ECalComponent *ecalcomp = l->data;
+
+ gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
+ if (width == 0) width = 12;
+ if (height == 0) height = 12;
+ for (l = sorted_comp_instances; l; l = l->next) {
+ struct comp_instance *ci = l->data;
+ ECalComponent *ecalcomp = ci->comp;
ECalComponentText valuetext;
- ECalComponentDateTime datetime;
- icaltimezone *appointment_zone = NULL;
- icaltimezone *current_zone = NULL;
- icalproperty_status status;
+ //ECalComponentDateTime datetime;
gchar *summary, *cmd;
char right[20];
//const gchar *uri;
- struct tm tmp_tm;
DbusmenuMenuitem * item;
- g_debug("Start Object");
- ECalComponentVType vtype = e_cal_component_get_vtype (ecalcomp);
-
- // See above FIXME regarding query result
- // If it's not an event or todo, continue no-increment
- if (vtype != E_CAL_COMPONENT_EVENT && vtype != E_CAL_COMPONENT_TODO)
- continue;
-
- // See above FIXME regarding query result
- // if the status is completed, continue no-increment
- e_cal_component_get_status (ecalcomp, &status);
- if (status == ICAL_STATUS_COMPLETED || status == ICAL_STATUS_CANCELLED)
- continue;
-
+ g_debug("Start Object %p", ecalcomp);
+
+ // TODO Mark days
+
+ if (i >= 5) continue;
+ i++;
+
item = dbusmenu_menuitem_new();
dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TYPE, APPOINTMENT_MENUITEM_TYPE);
dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
@@ -584,97 +676,143 @@ update_appointment_menu_items (gpointer user_data) {
g_debug("Summary: %s", summary);
g_free (summary);
- // Due text
- if (vtype == E_CAL_COMPONENT_EVENT)
- e_cal_component_get_dtstart (ecalcomp, &datetime);
- else
- e_cal_component_get_due (ecalcomp, &datetime);
-
+ //appointment_zone = icaltimezone_get_builtin_timezone_from_tzid(datetime.tzid);
+ //current_zone = icaltimezone_get_builtin_timezone_from_tzid(current_timezone);
+ //if (!appointment_zone || datetime.value->is_date) { // If it's today put in the current timezone?
+ // appointment_zone = current_zone;
+ //}
// FIXME need to get the timezone of the above datetime,
// and get the icaltimezone of the geoclue timezone/selected timezone (whichever is preferred)
- if (!datetime.value) {
- g_free(item);
- continue;
- }
-
- appointment_zone = icaltimezone_get_builtin_timezone_from_tzid(datetime.tzid);
- current_zone = icaltimezone_get_builtin_timezone_from_tzid(current_timezone);
- if (!appointment_zone || datetime.value->is_date) { // If it's today put in the current timezone?
- appointment_zone = current_zone;
- }
// TODO: Convert the timezone into a 3 letter abbreviation if it's different to current_timezone
// TODO: Add the appointment timezone to the list if it's not already there.
- tmp_tm = icaltimetype_to_tm_with_zone (datetime.value, appointment_zone, current_zone);
+ //tmp_tm = icaltimetype_to_tm_with_zone (datetime.value, appointment_zone, current_zone);
+
+ // Due text
+ ECalComponentVType vtype = e_cal_component_get_vtype (ecalcomp);
- g_debug("Generate time string");
// Get today
time_t curtime = time(NULL);
- struct tm* today = localtime(&curtime);
- if (today->tm_mday == tmp_tm.tm_mday &&
- today->tm_mon == tmp_tm.tm_mon &&
- today->tm_year == tmp_tm.tm_year)
- strftime(right, 20, "%l:%M %P", &tmp_tm);
+ struct tm *today = localtime(&curtime);
+
+ int mday = today->tm_mday;
+ int mon = today->tm_mon;
+ int year = today->tm_year;
+
+ struct tm *due;
+ g_debug("Start time %s", ctime(&ci->start));
+ if (vtype == E_CAL_COMPONENT_EVENT) due = localtime(&ci->start);
+ else if (vtype == E_CAL_COMPONENT_TODO) due = localtime(&ci->end);
+ else continue;
+
+ strftime(right, 20, "%a %l:%M %p", due);
+ g_debug("Start time %s -> %s", asctime(due), right);
+
+ int dmday = due->tm_mday;
+ int dmon = due->tm_mon;
+ int dyear = due->tm_year;
+
+ if ((mday == dmday) && (mon == dmon) && (year == dyear))
+ strftime(right, 20, "%l:%M %p", due);
else
- strftime(right, 20, "%a %l:%M %P", &tmp_tm);
+ strftime(right, 20, "%a %l:%M %p", due);
g_debug("Appointment time: %s", right);
- g_debug("Appointment timezone: %s", datetime.tzid);
- g_debug("Appointment timezone: %s", icaltimezone_get_tzid(appointment_zone)); // These two should be the same
+ //g_debug("Appointment timezone: %s", datetime.tzid);
+ //g_debug("Appointment timezone: %s", icaltimezone_get_tzid(appointment_zone)); // These two should be the same
//g_debug("Calendar timezone: %s", ecal_timezone);
dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_RIGHT, right);
- e_cal_component_free_datetime (&datetime);
+ //e_cal_component_free_datetime (&datetime);
- ad = isodate_from_time_t(mktime(&tmp_tm));
// Now we pull out the URI for the calendar event and try to create a URI that'll work when we execute evolution
// FIXME Because the URI stuff is really broken, we're going to open the calendar at todays date instead
-
//e_cal_component_get_uid(ecalcomp, &uri);
+ ad = isodate_from_time_t(mktime(due));
cmd = g_strconcat("evolution calendar:///?startdate=", ad, NULL);
g_signal_connect (G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK (activate_cb), cmd);
g_debug("Command to Execute: %s", cmd);
-
- // FIXME This is now more difficult to get right with more sources, as we need to keep track
- // of which ecal or source goes with each ECalComponent :/
-
- //ESource *source = e_cal_get_source (ecal);
- //e_source_get_color (source, &source_color); api has been changed
- const gchar *color_spec = NULL; //e_source_peek_color_spec(source);
+
+ const gchar *color_spec = e_source_peek_color_spec(ci->source);
g_debug("Colour to use: %s", color_spec);
// Draw the correct icon for the appointment type and then tint it using mask fill.
// For now we'll create a circle
if (color_spec != NULL) {
+ // Fixme causes segfault, but we have colours now yay!
GdkColor color;
- gdk_color_parse (color_spec, &color);
-
- cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
- cairo_t *cr = cairo_create(cs);
- cairo_arc (cr, width/2, height/2, width/2, 0, 2 * M_PI);
+ gdk_color_parse (color_spec, &color);
+
+ cairo_surface_t *surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width, height );
+ // Width keeps becoming zero!!
+ if (width == 0) width = 12;
+ if (height == 0) height = 12;
+ cairo_t *cr = cairo_create(surface);
gdk_cairo_set_source_color(cr, &color);
- cairo_fill(cr);
-
- GdkPixbuf * pixbuf = gdk_pixbuf_get_from_drawable(NULL, (GdkDrawable*)cs,
- gdk_colormap_new(gdk_drawable_get_visual((GdkDrawable*)cs), TRUE), 0,0,0,0, width, height);
- cairo_destroy(cr);
+ cairo_paint(cr);
+ cairo_set_source_rgba(cr, 0,0,0,0.5);
+ cairo_set_line_width(cr, 1);
+ cairo_rectangle (cr, 0.5, 0.5, width-1, height-1);
+ cairo_stroke(cr);
+ // Convert to pixbuf, in gtk3 this is done with gdk_pixbuf_get_from_surface
+ cairo_content_t content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
+ // Width keeps becoming zero!!
+ if (width == 0) width = 12;
+ if (height == 0) height = 12;
+ GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ !!(content & CAIRO_CONTENT_ALPHA),
+ 8, width, height);
+ if (pixbuf != NULL) {
+ gint sstride = cairo_image_surface_get_stride( surface );
+ gint dstride = gdk_pixbuf_get_rowstride (pixbuf);
+ guchar *spixels = cairo_image_surface_get_data( surface );
+ guchar *dpixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ int x, y;
+ for (y = 0; y < height; y++) {
+ guint32 *src = (guint32 *) spixels;
+
+ for (x = 0; x < width; x++) {
+ guint alpha = src[x] >> 24;
+
+ if (alpha == 0) {
+ dpixels[x * 4 + 0] = 0;
+ dpixels[x * 4 + 1] = 0;
+ dpixels[x * 4 + 2] = 0;
+ } else {
+ dpixels[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+ dpixels[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
+ dpixels[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
+ }
+ dpixels[x * 4 + 3] = alpha;
+ }
+ spixels += sstride;
+ dpixels += dstride;
+ }
+
+ cairo_surface_destroy (surface);
+ cairo_destroy(cr);
- dbusmenu_menuitem_property_set_image (item, APPOINTMENT_MENUITEM_PROP_ICON, pixbuf);
+ dbusmenu_menuitem_property_set_image (item, APPOINTMENT_MENUITEM_PROP_ICON, pixbuf);
+ }
}
- dbusmenu_menuitem_child_add_position (root, item, 3+i);
+ dbusmenu_menuitem_child_add_position (root, item, 2+i);
appointments = g_list_append (appointments, item); // Keep track of the items here to make them east to remove
g_debug("Adding appointment: %p", item);
-
- if (i == 4) break; // See above FIXME regarding query result limit
- i++;
}
if (gerror != NULL) g_error_free(gerror);
- g_object_unref(allobjects);
+ for (l = sorted_comp_instances; l; l = l->next) {
+ const struct comp_instance *ci = l->data;
+ g_object_unref(ci->comp);
+ g_list_free(sorted_comp_instances);
+ }
+
+ updating_appointments = FALSE;
g_debug("End of objects");
return TRUE;
}
@@ -756,6 +894,8 @@ build_menus (DbusmenuMenuitem * root)
check_timezone_sync();
g_signal_connect (conf, "changed::" SETTINGS_SHOW_LOCATIONS_S, G_CALLBACK (show_locations_changed), NULL);
+ g_signal_connect (conf, "changed::" SETTINGS_LOCATIONS_S, G_CALLBACK (show_locations_changed), NULL);
+ g_signal_connect (conf, "changed::" SETTINGS_SHOW_EVENTS_S, G_CALLBACK (show_events_changed), NULL);
DbusmenuMenuitem * separator = dbusmenu_menuitem_new();
dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
diff --git a/src/indicator-datetime.c b/src/indicator-datetime.c
index 2099929..7659812 100644
--- a/src/indicator-datetime.c
+++ b/src/indicator-datetime.c
@@ -23,6 +23,11 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "config.h"
#endif
+#include <locale.h>
+#include <langinfo.h>
+#include <string.h>
+#include <time.h>
+
/* GStuff */
#include <glib.h>
#include <glib/gprintf.h>