summaryrefslogtreecommitdiff
path: root/applets
diff options
context:
space:
mode:
Diffstat (limited to 'applets')
-rw-r--r--applets/clock/Makefile.am27
-rw-r--r--applets/clock/calendar-client.c2095
-rw-r--r--applets/clock/calendar-client.h151
-rw-r--r--applets/clock/calendar-debug.h50
-rw-r--r--applets/clock/calendar-sources.c503
-rw-r--r--applets/clock/calendar-sources.h64
-rw-r--r--applets/clock/calendar-window.c1148
-rw-r--r--applets/clock/calendar-window.h3
-rw-r--r--applets/clock/clock-face.c17
-rw-r--r--applets/clock/clock-face.h1
-rw-r--r--applets/clock/clock-location-tile.c34
-rw-r--r--applets/clock/clock-location.c16
-rw-r--r--applets/clock/clock-map.c27
-rw-r--r--applets/clock/clock-map.h1
-rw-r--r--applets/clock/clock-sunpos.c1
-rw-r--r--applets/clock/clock.c640
-rw-r--r--applets/clock/clock.ui736
-rw-r--r--applets/clock/org.mate.panel.applet.clock.gschema.xml.in10
-rw-r--r--applets/clock/system-timezone.c15
-rw-r--r--applets/clock/system-timezone.h1
-rw-r--r--applets/fish/Makefile.am2
-rw-r--r--applets/fish/fish.c46
-rw-r--r--applets/notification_area/Makefile.am2
-rw-r--r--applets/notification_area/main.c12
-rw-r--r--applets/notification_area/main.h1
-rw-r--r--applets/notification_area/na-grid.c24
-rw-r--r--applets/notification_area/org.mate.panel.NotificationAreaApplet.mate-panel-applet.desktop.in.in2
-rw-r--r--applets/notification_area/status-notifier/sn-dbus-menu-item.c25
-rw-r--r--applets/notification_area/status-notifier/sn-dbus-menu-item.h4
-rw-r--r--applets/notification_area/status-notifier/sn-dbus-menu.c6
-rw-r--r--applets/notification_area/status-notifier/sn-host-v0.c3
-rw-r--r--applets/notification_area/status-notifier/sn-item-v0.c61
-rw-r--r--applets/notification_area/status-notifier/sn-item.c1
-rw-r--r--applets/notification_area/system-tray/fixedtip.c2
-rw-r--r--applets/notification_area/system-tray/na-tray-child.c2
-rw-r--r--applets/notification_area/system-tray/na-tray-manager.c13
-rw-r--r--applets/notification_area/system-tray/na-tray.c14
-rw-r--r--applets/notification_area/testtray.c16
-rw-r--r--applets/wncklet/Makefile.am8
-rw-r--r--applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in5
-rw-r--r--applets/wncklet/showdesktop.c60
-rw-r--r--applets/wncklet/showdesktop.h1
-rw-r--r--applets/wncklet/wayland-backend.c395
-rw-r--r--applets/wncklet/wayland-backend.h2
-rw-r--r--applets/wncklet/window-list.c327
-rw-r--r--applets/wncklet/window-list.ui63
-rw-r--r--applets/wncklet/window-menu.c28
-rw-r--r--applets/wncklet/wncklet.c1
-rw-r--r--applets/wncklet/workspace-switcher.c232
49 files changed, 5952 insertions, 946 deletions
diff --git a/applets/clock/Makefile.am b/applets/clock/Makefile.am
index 55f94e93..5a932422 100644
--- a/applets/clock/Makefile.am
+++ b/applets/clock/Makefile.am
@@ -35,6 +35,15 @@ CLOCK_SOURCES = \
set-timezone.h \
$(BUILT_SOURCES)
+if HAVE_EDS
+CLOCK_SOURCES += \
+ calendar-client.c \
+ calendar-client.h \
+ calendar-sources.c \
+ calendar-sources.h \
+ calendar-debug.h
+endif
+
CLOCK_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(LIBMATE_PANEL_APPLET_CFLAGS) \
@@ -44,6 +53,10 @@ CLOCK_CPPFLAGS = \
-DMATELOCALEDIR=\""$(datadir)/locale"\" \
-DMATEWEATHER_I_KNOW_THIS_IS_UNSTABLE
+if HAVE_EDS
+CLOCK_CPPFLAGS += $(EDS_CFLAGS)
+endif
+
CLOCK_LDADD = \
../../libmate-panel-applet/libmate-panel-applet-4.la \
$(CLOCK_LIBS) \
@@ -51,6 +64,10 @@ CLOCK_LDADD = \
libsystem-timezone.la \
-lm
+if HAVE_EDS
+CLOCK_LDADD += $(EDS_LIBS)
+endif
+
test_system_timezone_SOURCES = \
test-system-timezone.c
test_system_timezone_LDADD = libsystem-timezone.la
@@ -67,6 +84,14 @@ libclock_applet_la_LIBADD = $(CLOCK_LDADD)
libclock_applet_la_LDFLAGS = -module -avoid-version
libclock_applet_la_CFLAGS = $(AM_CFLAGS)
$(libclock_applet_la_OBJECTS): $(BUILT_SOURCES)
+
+if ENABLE_WAYLAND
+libclock_applet_la_LIBADD += \
+ $(WAYLAND_LIBS)
+
+AM_CPPFLAGS += \
+ $(WAYLAND_CFLAGS)
+endif
else
APPLET_IN_PROCESS = false
APPLET_LOCATION = $(libexecdir)/clock-applet
@@ -138,9 +163,9 @@ $(applet_in_files): $(applet_in_files).in Makefile
$(applet_DATA): $(applet_in_files)
$(AM_V_GEN) $(MSGFMT) --desktop --keyword= --keyword=Name --keyword=Description --template $< -d $(top_srcdir)/po -o $@
+service_in_files = org.mate.panel.applet.ClockAppletFactory.service.in
if !CLOCK_INPROCESS
servicedir = $(datadir)/dbus-1/services
-service_in_files = org.mate.panel.applet.ClockAppletFactory.service.in
service_DATA = $(service_in_files:.service.in=.service)
org.mate.panel.applet.ClockAppletFactory.service: $(service_in_files)
diff --git a/applets/clock/calendar-client.c b/applets/clock/calendar-client.c
new file mode 100644
index 00000000..8bd9a2f3
--- /dev/null
+++ b/applets/clock/calendar-client.c
@@ -0,0 +1,2095 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Mark McLoughlin <[email protected]>
+ * William Jon McCann <[email protected]>
+ * Martin Grimme <[email protected]>
+ * Christian Kellner <[email protected]>
+ */
+
+#include <config.h>
+
+#include "calendar-client.h"
+
+#include <libintl.h>
+#include <string.h>
+#define HANDLE_LIBICAL_MEMORY
+
+#ifdef HAVE_EDS
+
+#include <libecal/libecal.h>
+#include "calendar-sources.h"
+#include "system-timezone.h"
+#endif
+
+#undef CALENDAR_ENABLE_DEBUG
+#include "calendar-debug.h"
+
+#ifndef _
+#define _(x) gettext(x)
+#endif
+
+#ifndef N_
+#define N_(x) x
+#endif
+
+#ifdef HAVE_EDS
+
+typedef struct _CalendarClientQuery CalendarClientQuery;
+typedef struct _CalendarClientSource CalendarClientSource;
+
+struct _CalendarClientQuery
+{
+ ECalClientView *view;
+ GHashTable *events;
+};
+
+struct _CalendarClientSource
+{
+ CalendarClient *client;
+ ECalClient *source;
+
+ CalendarClientQuery completed_query;
+ CalendarClientQuery in_progress_query;
+
+ guint changed_signal_id;
+
+ guint query_completed : 1;
+ guint query_in_progress : 1;
+};
+
+struct _CalendarClientPrivate
+{
+ CalendarSources *calendar_sources;
+
+ GSList *appointment_sources;
+ GSList *task_sources;
+
+ ICalTimezone *zone;
+
+ guint zone_listener;
+ GSettings *calendar_settings;
+
+ guint day;
+ guint month;
+ guint year;
+};
+
+static void calendar_client_finalize (GObject *object);
+static void calendar_client_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void calendar_client_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static GSList *calendar_client_update_sources_list (CalendarClient *client,
+ GSList *sources,
+ GList *esources,
+ guint changed_signal_id);
+static void calendar_client_appointment_sources_changed (CalendarClient *client);
+static void calendar_client_task_sources_changed (CalendarClient *client);
+
+static void calendar_client_stop_query (CalendarClient *client,
+ CalendarClientSource *source,
+ CalendarClientQuery *query);
+static void calendar_client_start_query (CalendarClient *client,
+ CalendarClientSource *source,
+ const char *query);
+
+static void calendar_client_source_finalize (CalendarClientSource *source);
+static void calendar_client_query_finalize (CalendarClientQuery *query);
+
+static void
+calendar_client_update_appointments (CalendarClient *client);
+static void
+calendar_client_update_tasks (CalendarClient *client);
+
+enum
+{
+ PROP_O,
+ PROP_DAY,
+ PROP_MONTH,
+ PROP_YEAR
+};
+
+enum
+{
+ APPOINTMENTS_CHANGED,
+ TASKS_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (CalendarClient, calendar_client, G_TYPE_OBJECT)
+
+static void
+calendar_client_class_init (CalendarClientClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->finalize = calendar_client_finalize;
+ gobject_class->set_property = calendar_client_set_property;
+ gobject_class->get_property = calendar_client_get_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_DAY,
+ g_param_spec_uint ("day",
+ "Day",
+ "The currently monitored day between 1 and 31 (0 denotes unset)",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_MONTH,
+ g_param_spec_uint ("month",
+ "Month",
+ "The currently monitored month between 0 and 11",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_YEAR,
+ g_param_spec_uint ("year",
+ "Year",
+ "The currently monitored year",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE));
+
+ signals [APPOINTMENTS_CHANGED] =
+ g_signal_new ("appointments-changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CalendarClientClass, tasks_changed),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [TASKS_CHANGED] =
+ g_signal_new ("tasks-changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CalendarClientClass, tasks_changed),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+}
+
+/* Timezone code adapted from evolution/calendar/gui/calendar-config.c */
+/* The current timezone, e.g. "Europe/London". It may be NULL, in which case
+ you should assume UTC. */
+static gchar *
+calendar_client_config_get_timezone (GSettings *calendar_settings)
+{
+ gchar *timezone = NULL;
+
+ if (calendar_settings == NULL)
+ return NULL;
+
+ /* Check if we can list the keys to see if timezone exists */
+ gchar **keys = g_settings_list_keys (calendar_settings);
+ gboolean has_timezone = FALSE;
+
+ for (gint i = 0; keys[i] != NULL; i++) {
+ if (g_strcmp0 (keys[i], "timezone") == 0) {
+ has_timezone = TRUE;
+ break;
+ }
+ }
+ g_strfreev (keys);
+
+ if (has_timezone) {
+ timezone = g_settings_get_string (calendar_settings, "timezone");
+ }
+
+ return timezone;
+}
+
+static ICalTimezone *
+calendar_client_config_get_icaltimezone (CalendarClient *client)
+{
+ gchar *location = NULL;
+ ICalTimezone *zone = NULL;
+
+ if (client->priv->calendar_settings != NULL)
+ location = calendar_client_config_get_timezone (client->priv->calendar_settings);
+
+ if (!location) {
+ /* MATE panel doesn't store timezone in GSettings
+ * Since libical timezone lookup often fails, just use UTC
+ * The display code will handle local time conversion */
+ return i_cal_timezone_get_utc_timezone ();
+ }
+
+ zone = i_cal_timezone_get_builtin_timezone (location);
+ g_free (location);
+
+ return zone;
+}
+
+static void
+calendar_client_set_timezone (CalendarClient *client)
+{
+ GList *list, *link;
+
+ client->priv->zone = calendar_client_config_get_icaltimezone (client);
+
+ list = calendar_sources_get_appointment_clients (client->priv->calendar_sources);
+ for (link = list; link != NULL; link = g_list_next (link))
+ {
+ ECalClient *cal = E_CAL_CLIENT (link->data);
+
+ e_cal_client_set_default_timezone (cal, client->priv->zone);
+ }
+ g_list_free (list);
+}
+
+static void
+calendar_client_timezone_changed_cb (GSettings *calendar_settings,
+ const gchar *key,
+ CalendarClient *client)
+{
+ calendar_client_set_timezone (client);
+}
+
+static void
+load_calendars (CalendarClient *client,
+ CalendarEventType type)
+{
+ GSList *l, *clients;
+
+ switch (type)
+ {
+ case CALENDAR_EVENT_APPOINTMENT:
+ clients = client->priv->appointment_sources;
+ break;
+ case CALENDAR_EVENT_TASK:
+ clients = client->priv->task_sources;
+ break;
+ case CALENDAR_EVENT_ALL:
+ default:
+ g_assert_not_reached ();
+ }
+
+ for (l = clients; l != NULL; l = l->next)
+ {
+ if (type == CALENDAR_EVENT_APPOINTMENT)
+ calendar_client_update_appointments (client);
+ else if (type == CALENDAR_EVENT_TASK)
+ calendar_client_update_tasks (client);
+ }
+}
+
+static void
+calendar_client_init (CalendarClient *client)
+{
+ GList *list;
+
+ client->priv = calendar_client_get_instance_private (client);
+
+ client->priv->calendar_sources = calendar_sources_get ();
+
+ list = calendar_sources_get_appointment_clients (client->priv->calendar_sources);
+ client->priv->appointment_sources =
+ calendar_client_update_sources_list (client, NULL, list, signals [APPOINTMENTS_CHANGED]);
+ g_list_free (list);
+
+ list = calendar_sources_get_task_clients (client->priv->calendar_sources);
+ client->priv->task_sources = calendar_client_update_sources_list (client, NULL, list, signals [TASKS_CHANGED]);
+ g_list_free (list);
+
+ /* set the timezone before loading the clients */
+ calendar_client_set_timezone (client);
+ load_calendars (client, CALENDAR_EVENT_APPOINTMENT);
+ load_calendars (client, CALENDAR_EVENT_TASK);
+
+ g_signal_connect_swapped (client->priv->calendar_sources,
+ "appointment-sources-changed",
+ G_CALLBACK (calendar_client_appointment_sources_changed),
+ client);
+ g_signal_connect_swapped (client->priv->calendar_sources,
+ "task-sources-changed",
+ G_CALLBACK (calendar_client_task_sources_changed),
+ client);
+
+ if (client->priv->calendar_settings != NULL)
+ client->priv->zone_listener = g_signal_connect (client->priv->calendar_settings,
+ "changed::timezone",
+ G_CALLBACK (calendar_client_timezone_changed_cb),
+ client);
+
+ client->priv->day = G_MAXUINT;
+ client->priv->month = G_MAXUINT;
+ client->priv->year = G_MAXUINT;
+}
+
+static void
+calendar_client_finalize (GObject *object)
+{
+ CalendarClient *client = CALENDAR_CLIENT (object);
+ GSList *l;
+
+ if (client->priv->zone_listener)
+ {
+ g_signal_handler_disconnect (client->priv->calendar_settings,
+ client->priv->zone_listener);
+ client->priv->zone_listener = 0;
+ }
+
+ if (client->priv->calendar_settings)
+ g_object_unref (client->priv->calendar_settings);
+ client->priv->calendar_settings = NULL;
+
+ for (l = client->priv->appointment_sources; l; l = l->next)
+ {
+ calendar_client_source_finalize (l->data);
+ g_free (l->data);
+ }
+ g_slist_free (client->priv->appointment_sources);
+ client->priv->appointment_sources = NULL;
+
+ for (l = client->priv->task_sources; l; l = l->next)
+ {
+ calendar_client_source_finalize (l->data);
+ g_free (l->data);
+ }
+ g_slist_free (client->priv->task_sources);
+ client->priv->task_sources = NULL;
+
+ if (client->priv->calendar_sources)
+ g_object_unref (client->priv->calendar_sources);
+ client->priv->calendar_sources = NULL;
+
+ G_OBJECT_CLASS (calendar_client_parent_class)->finalize (object);
+}
+
+static void
+calendar_client_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CalendarClient *client = CALENDAR_CLIENT (object);
+
+ switch (prop_id)
+ {
+ case PROP_DAY:
+ calendar_client_select_day (client, g_value_get_uint (value));
+ break;
+ case PROP_MONTH:
+ calendar_client_select_month (client,
+ g_value_get_uint (value),
+ client->priv->year);
+ break;
+ case PROP_YEAR:
+ calendar_client_select_month (client,
+ client->priv->month,
+ g_value_get_uint (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+calendar_client_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CalendarClient *client = CALENDAR_CLIENT (object);
+
+ switch (prop_id)
+ {
+ case PROP_DAY:
+ g_value_set_uint (value, client->priv->day);
+ break;
+ case PROP_MONTH:
+ g_value_set_uint (value, client->priv->month);
+ break;
+ case PROP_YEAR:
+ g_value_set_uint (value, client->priv->year);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+CalendarClient *
+calendar_client_new (GSettings *settings)
+{
+ CalendarClient *client = g_object_new (CALENDAR_TYPE_CLIENT, NULL);
+
+ /* Use the provided MATE panel settings instead of Evolution settings */
+ if (settings) {
+ client->priv->calendar_settings = g_object_ref (settings);
+ } else {
+ /* Fallback to Evolution calendar settings if available */
+ GSettingsSchemaSource *schema_source = g_settings_schema_source_get_default();
+ const gchar *evolution_calendar_schema = "org.gnome.evolution.calendar";
+ GSettingsSchema *schema = g_settings_schema_source_lookup (schema_source, evolution_calendar_schema, FALSE);
+ if (schema) {
+ client->priv->calendar_settings = g_settings_new (evolution_calendar_schema);
+ g_settings_schema_unref (schema);
+ } else {
+ /* No Evolution settings available, calendar_settings will remain NULL */
+ client->priv->calendar_settings = NULL;
+ }
+ }
+
+ return client;
+}
+
+/* @day and @month can happily be out of range as
+ * mktime() will normalize them correctly. From mktime(3):
+ *
+ * "If structure members are outside their legal interval,
+ * they will be normalized (so that, e.g., 40 October is
+ * changed into 9 November)."
+ *
+ * "What?", you say, "Something useful in libc?"
+ */
+static inline time_t
+make_time_for_day_begin (int day,
+ int month,
+ int year)
+{
+ struct tm localtime_tm = { 0, };
+
+ localtime_tm.tm_mday = day;
+ localtime_tm.tm_mon = month;
+ localtime_tm.tm_year = year - 1900;
+ localtime_tm.tm_isdst = -1;
+
+ return mktime (&localtime_tm);
+}
+
+static inline char *
+make_isodate_for_day_begin (int day,
+ int month,
+ int year)
+{
+ time_t utctime;
+
+ utctime = make_time_for_day_begin (day, month, year);
+
+ return utctime != -1 ? isodate_from_time_t (utctime) : NULL;
+}
+
+static time_t
+get_time_from_property (ICalComponent *icomp,
+ ICalPropertyKind prop_kind,
+ ICalTime * (* get_prop_func) (ICalProperty *prop),
+ ICalTimezone *default_zone)
+{
+ ICalProperty *prop;
+ ICalTime *ical_time;
+ ICalParameter *param;
+ ICalTimezone *timezone;
+ time_t retval;
+
+ prop = i_cal_component_get_first_property (icomp, prop_kind);
+ if (!prop)
+ return 0;
+
+ param = i_cal_property_get_first_parameter (prop, I_CAL_TZID_PARAMETER);
+ ical_time = get_prop_func (prop);
+ g_object_unref (prop);
+
+ if (param)
+ {
+ const char *tzid;
+
+ tzid = i_cal_parameter_get_tzid (param);
+ timezone = i_cal_timezone_get_builtin_timezone_from_tzid (tzid);
+ g_object_unref (param);
+
+ /* If timezone lookup failed, fall back to default zone */
+ if (!timezone) {
+ timezone = default_zone;
+ }
+ }
+ else if (i_cal_time_is_utc (ical_time))
+ {
+ timezone = i_cal_timezone_get_utc_timezone ();
+ }
+ else
+ {
+ timezone = default_zone;
+ }
+
+ retval = i_cal_time_as_timet_with_zone (ical_time, timezone);
+
+ g_object_unref (ical_time);
+
+ return retval;
+}
+
+static char *
+get_component_uid (ICalComponent *component)
+{
+ return g_strdup (i_cal_component_get_uid (component));
+}
+
+static char *
+get_component_rid (ICalComponent *component)
+{
+ ICalProperty *prop;
+ ICalTime *time;
+ char *rid;
+
+ prop = i_cal_component_get_first_property (component, I_CAL_RECURRENCEID_PROPERTY);
+ if (!prop)
+ return NULL;
+
+ time = i_cal_property_get_recurrenceid (prop);
+ g_object_unref (prop);
+
+ if (!i_cal_time_is_valid_time (time) || i_cal_time_is_null_time (time))
+ {
+ g_object_unref (time);
+ return NULL;
+ }
+
+ rid = g_strdup (i_cal_time_as_ical_string (time));
+ g_object_unref (time);
+
+ return rid;
+}
+
+static char *
+get_component_summary (ICalComponent *component)
+{
+ ICalProperty *prop;
+ char *summary;
+
+ prop = i_cal_component_get_first_property (component, I_CAL_SUMMARY_PROPERTY);
+ if (!prop)
+ return NULL;
+
+ summary = g_strdup (i_cal_property_get_summary (prop));
+ g_object_unref (prop);
+
+ return summary;
+}
+
+static char *
+get_component_description (ICalComponent *component)
+{
+ ICalProperty *prop;
+ char *description;
+
+ prop = i_cal_component_get_first_property (component, I_CAL_DESCRIPTION_PROPERTY);
+ if (!prop)
+ return NULL;
+
+ description = g_strdup (i_cal_property_get_description (prop));
+ g_object_unref (prop);
+
+ return description;
+}
+
+static inline time_t
+get_component_start_time (ICalComponent *component,
+ ICalTimezone *default_zone)
+{
+ return get_time_from_property (component,
+ I_CAL_DTSTART_PROPERTY,
+ i_cal_property_get_dtstart,
+ default_zone);
+}
+
+static inline time_t
+get_component_end_time (ICalComponent *component,
+ ICalTimezone *default_zone)
+{
+ return get_time_from_property (component,
+ I_CAL_DTEND_PROPERTY,
+ i_cal_property_get_dtend,
+ default_zone);
+}
+
+static gboolean
+get_component_is_all_day (ICalComponent *component,
+ time_t start_time,
+ ICalTimezone *default_zone)
+{
+ ICalTime *dtstart;
+ struct tm *start_tm;
+ time_t end_time;
+ ICalProperty *prop;
+ ICalDuration *duration;
+ gboolean is_all_day;
+
+ dtstart = i_cal_component_get_dtstart (component);
+
+ if (dtstart && i_cal_time_is_date (dtstart))
+ {
+ g_object_unref (dtstart);
+ return TRUE;
+ }
+
+ g_object_unref (dtstart);
+
+ start_tm = gmtime (&start_time);
+ if (start_tm->tm_sec != 0 ||
+ start_tm->tm_min != 0 ||
+ start_tm->tm_hour != 0)
+ return FALSE;
+
+ if ((end_time = get_component_end_time (component, default_zone)))
+ return (end_time - start_time) % 86400 == 0;
+
+ prop = i_cal_component_get_first_property (component, I_CAL_DURATION_PROPERTY);
+ if (!prop)
+ return FALSE;
+
+ duration = i_cal_property_get_duration (prop);
+ g_object_unref (prop);
+
+ is_all_day = i_cal_duration_as_int (duration) % 86400 == 0;
+ g_object_unref (duration);
+
+ return is_all_day;
+}
+
+static inline time_t
+get_component_due_time (ICalComponent *component,
+ ICalTimezone *default_zone)
+{
+ return get_time_from_property (component,
+ I_CAL_DUE_PROPERTY,
+ i_cal_property_get_due,
+ default_zone);
+}
+
+static guint
+get_component_percent_complete (ICalComponent *component)
+{
+ ICalPropertyStatus status;
+ ICalProperty *prop;
+ int percent_complete;
+
+ status = i_cal_component_get_status (component);
+ if (status == I_CAL_STATUS_COMPLETED)
+ return 100;
+
+ prop = i_cal_component_get_first_property (component, I_CAL_COMPLETED_PROPERTY);
+
+ if (prop)
+ {
+ g_object_unref (prop);
+ return 100;
+ }
+
+ prop = i_cal_component_get_first_property (component, I_CAL_PERCENTCOMPLETE_PROPERTY);
+ if (!prop)
+ return 0;
+
+ percent_complete = i_cal_property_get_percentcomplete (prop);
+ g_object_unref (prop);
+
+ return CLAMP (percent_complete, 0, 100);
+}
+
+static inline time_t
+get_component_completed_time (ICalComponent *component,
+ ICalTimezone *default_zone)
+{
+ return get_time_from_property (component,
+ I_CAL_COMPLETED_PROPERTY,
+ i_cal_property_get_completed,
+ default_zone);
+}
+
+static int
+get_component_priority (ICalComponent *component)
+{
+ ICalProperty *prop;
+ int priority;
+
+ prop = i_cal_component_get_first_property (component, I_CAL_PRIORITY_PROPERTY);
+ if (!prop)
+ return -1;
+
+ priority = i_cal_property_get_priority (prop);
+ g_object_unref (prop);
+
+ return priority;
+}
+
+static char *
+get_source_color (ECalClient *esource)
+{
+ ESource *source;
+ ECalClientSourceType source_type;
+ ESourceSelectable *extension;
+ const gchar *extension_name;
+
+ g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
+
+ source = e_client_get_source (E_CLIENT (esource));
+ source_type = e_cal_client_get_source_type (esource);
+
+ switch (source_type)
+ {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ extension_name = E_SOURCE_EXTENSION_CALENDAR;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ case E_CAL_CLIENT_SOURCE_TYPE_LAST:
+ default:
+ g_return_val_if_reached (NULL);
+ }
+
+ extension = e_source_get_extension (source, extension_name);
+
+ return e_source_selectable_dup_color (extension);
+}
+
+static gchar *
+get_source_backend_name (ECalClient *esource)
+{
+ ESource *source;
+ ECalClientSourceType source_type;
+ ESourceBackend *extension;
+ const gchar *extension_name;
+
+ g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
+
+ source = e_client_get_source (E_CLIENT (esource));
+ source_type = e_cal_client_get_source_type (esource);
+
+ switch (source_type)
+ {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ extension_name = E_SOURCE_EXTENSION_CALENDAR;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ case E_CAL_CLIENT_SOURCE_TYPE_LAST:
+ default:
+ g_return_val_if_reached (NULL);
+ }
+
+ extension = e_source_get_extension (source, extension_name);
+
+ return e_source_backend_dup_backend_name (extension);
+}
+
+static inline gboolean
+calendar_appointment_equal (CalendarAppointment *a,
+ CalendarAppointment *b)
+{
+ GSList *la, *lb;
+
+ if (g_slist_length (a->occurrences) != g_slist_length (b->occurrences))
+ return FALSE;
+
+ for (la = a->occurrences, lb = b->occurrences; la && lb; la = la->next, lb = lb->next)
+ {
+ CalendarOccurrence *oa = la->data;
+ CalendarOccurrence *ob = lb->data;
+
+ if (oa->start_time != ob->start_time ||
+ oa->end_time != ob->end_time)
+ return FALSE;
+ }
+
+ return
+ g_strcmp0 (a->uid, b->uid) == 0 &&
+ g_strcmp0 (a->backend_name, b->backend_name) == 0 &&
+ g_strcmp0 (a->summary, b->summary) == 0 &&
+ g_strcmp0 (a->description, b->description) == 0 &&
+ g_strcmp0 (a->color_string, b->color_string) == 0 &&
+ a->start_time == b->start_time &&
+ a->end_time == b->end_time &&
+ a->is_all_day == b->is_all_day;
+}
+
+static void
+calendar_appointment_copy (CalendarAppointment *appointment,
+ CalendarAppointment *appointment_copy)
+{
+ GSList *l;
+
+ g_assert (appointment != NULL);
+ g_assert (appointment_copy != NULL);
+
+ appointment_copy->occurrences = g_slist_copy (appointment->occurrences);
+ for (l = appointment_copy->occurrences; l; l = l->next)
+ {
+ CalendarOccurrence *occurrence = l->data;
+ CalendarOccurrence *occurrence_copy;
+
+ occurrence_copy = g_new0 (CalendarOccurrence, 1);
+ occurrence_copy->start_time = occurrence->start_time;
+ occurrence_copy->end_time = occurrence->end_time;
+
+ l->data = occurrence_copy;
+ }
+
+ appointment_copy->uid = g_strdup (appointment->uid);
+ appointment_copy->backend_name = g_strdup (appointment->backend_name);
+ appointment_copy->summary = g_strdup (appointment->summary);
+ appointment_copy->description = g_strdup (appointment->description);
+ appointment_copy->color_string = g_strdup (appointment->color_string);
+ appointment_copy->start_time = appointment->start_time;
+ appointment_copy->end_time = appointment->end_time;
+ appointment_copy->is_all_day = appointment->is_all_day;
+}
+
+static void
+calendar_appointment_finalize (CalendarAppointment *appointment)
+{
+ GSList *l;
+
+ for (l = appointment->occurrences; l; l = l->next)
+ g_free (l->data);
+ g_slist_free (appointment->occurrences);
+ appointment->occurrences = NULL;
+
+ g_free (appointment->uid);
+ appointment->uid = NULL;
+
+ g_free (appointment->rid);
+ appointment->rid = NULL;
+
+ g_free (appointment->backend_name);
+ appointment->backend_name = NULL;
+
+ g_free (appointment->summary);
+ appointment->summary = NULL;
+
+ g_free (appointment->description);
+ appointment->description = NULL;
+
+ g_free (appointment->color_string);
+ appointment->color_string = NULL;
+
+ appointment->start_time = 0;
+ appointment->is_all_day = FALSE;
+}
+
+static void
+calendar_appointment_init (CalendarAppointment *appointment,
+ ICalComponent *component,
+ CalendarClientSource *source,
+ ICalTimezone *default_zone)
+{
+ appointment->uid = get_component_uid (component);
+ appointment->rid = get_component_rid (component);
+ appointment->backend_name = get_source_backend_name (source->source);
+ appointment->summary = get_component_summary (component);
+ appointment->description = get_component_description (component);
+ appointment->color_string = get_source_color (source->source);
+ appointment->start_time = get_component_start_time (component, default_zone);
+ appointment->end_time = get_component_end_time (component, default_zone);
+ appointment->is_all_day = get_component_is_all_day (component,
+ appointment->start_time,
+ default_zone);
+}
+
+static inline gboolean
+calendar_task_equal (CalendarTask *a,
+ CalendarTask *b)
+{
+ return
+ g_strcmp0 (a->uid, b->uid) == 0 &&
+ g_strcmp0 (a->summary, b->summary) == 0 &&
+ g_strcmp0 (a->description, b->description) == 0 &&
+ g_strcmp0 (a->color_string, b->color_string) == 0 &&
+ a->start_time == b->start_time &&
+ a->due_time == b->due_time &&
+ a->percent_complete == b->percent_complete &&
+ a->completed_time == b->completed_time &&
+ a->priority == b->priority;
+}
+
+static void
+calendar_task_copy (CalendarTask *task,
+ CalendarTask *task_copy)
+{
+ g_assert (task != NULL);
+ g_assert (task_copy != NULL);
+
+ task_copy->uid = g_strdup (task->uid);
+ task_copy->summary = g_strdup (task->summary);
+ task_copy->description = g_strdup (task->description);
+ task_copy->color_string = g_strdup (task->color_string);
+ task_copy->start_time = task->start_time;
+ task_copy->due_time = task->due_time;
+ task_copy->percent_complete = task->percent_complete;
+ task_copy->completed_time = task->completed_time;
+ task_copy->priority = task->priority;
+}
+
+static void
+calendar_task_finalize (CalendarTask *task)
+{
+ g_free (task->uid);
+ task->uid = NULL;
+
+ g_free (task->summary);
+ task->summary = NULL;
+
+ g_free (task->description);
+ task->description = NULL;
+
+ g_free (task->color_string);
+ task->color_string = NULL;
+
+ task->percent_complete = 0;
+}
+
+static void
+calendar_task_init (CalendarTask *task,
+ ICalComponent *component,
+ CalendarClientSource *source,
+ ICalTimezone *default_zone)
+{
+ task->uid = get_component_uid (component);
+ task->summary = get_component_summary (component);
+ task->description = get_component_description (component);
+ task->color_string = get_source_color (source->source);
+ task->start_time = get_component_start_time (component, default_zone);
+ task->due_time = get_component_due_time (component, default_zone);
+ task->percent_complete = get_component_percent_complete (component);
+ task->completed_time = get_component_completed_time (component, default_zone);
+ task->priority = get_component_priority (component);
+}
+
+void
+calendar_event_free (CalendarEvent *event)
+{
+ switch (event->type)
+ {
+ case CALENDAR_EVENT_APPOINTMENT:
+ calendar_appointment_finalize (CALENDAR_APPOINTMENT (event));
+ break;
+ case CALENDAR_EVENT_TASK:
+ calendar_task_finalize (CALENDAR_TASK (event));
+ break;
+ case CALENDAR_EVENT_ALL:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_free (event);
+}
+
+static CalendarEvent *
+calendar_event_new (ICalComponent *component,
+ CalendarClientSource *source,
+ ICalTimezone *default_zone)
+{
+ CalendarEvent *event;
+ ICalComponentKind component_kind;
+
+ event = g_new0 (CalendarEvent, 1);
+ component_kind = i_cal_component_isa (component);
+
+ if (component_kind == I_CAL_VEVENT_COMPONENT)
+ {
+ event->type = CALENDAR_EVENT_APPOINTMENT;
+ calendar_appointment_init (CALENDAR_APPOINTMENT (event),
+ component, source, default_zone);
+ }
+ else if (component_kind == I_CAL_VTODO_COMPONENT)
+ {
+ event->type = CALENDAR_EVENT_TASK;
+ calendar_task_init (CALENDAR_TASK (event),
+ component, source, default_zone);
+ }
+ else
+ {
+ g_warning ("Unknown calendar component type: %d\n", component_kind);
+ g_free (event);
+
+ return NULL;
+ }
+
+ return event;
+}
+
+static CalendarEvent *
+calendar_event_copy (CalendarEvent *event)
+{
+ CalendarEvent *retval;
+
+ if (!event)
+ return NULL;
+
+ retval = g_new0 (CalendarEvent, 1);
+
+ retval->type = event->type;
+
+ switch (event->type)
+ {
+ case CALENDAR_EVENT_APPOINTMENT:
+ calendar_appointment_copy (CALENDAR_APPOINTMENT (event),
+ CALENDAR_APPOINTMENT (retval));
+ break;
+ case CALENDAR_EVENT_TASK:
+ calendar_task_copy (CALENDAR_TASK (event),
+ CALENDAR_TASK (retval));
+ break;
+ case CALENDAR_EVENT_ALL:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return retval;
+}
+
+static char *
+calendar_event_get_uid (CalendarEvent *event)
+{
+ switch (event->type)
+ {
+ case CALENDAR_EVENT_APPOINTMENT:
+ return g_strdup_printf ("%s%s", CALENDAR_APPOINTMENT (event)->uid, CALENDAR_APPOINTMENT (event)->rid ? CALENDAR_APPOINTMENT (event)->rid : "");
+ break;
+ case CALENDAR_EVENT_TASK:
+ return g_strdup (CALENDAR_TASK (event)->uid);
+ break;
+ case CALENDAR_EVENT_ALL:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return NULL;
+}
+
+static gboolean
+calendar_event_equal (CalendarEvent *a,
+ CalendarEvent *b)
+{
+ if (!a && !b)
+ return TRUE;
+
+ if ((a && !b) || (!a && b))
+ return FALSE;
+
+ if (a->type != b->type)
+ return FALSE;
+
+ switch (a->type)
+ {
+ case CALENDAR_EVENT_APPOINTMENT:
+ return calendar_appointment_equal (CALENDAR_APPOINTMENT (a),
+ CALENDAR_APPOINTMENT (b));
+ case CALENDAR_EVENT_TASK:
+ return calendar_task_equal (CALENDAR_TASK (a),
+ CALENDAR_TASK (b));
+ case CALENDAR_EVENT_ALL:
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+
+ return FALSE;
+}
+
+static inline void
+calendar_event_debug_dump (CalendarEvent *event)
+{
+#ifdef CALENDAR_ENABLE_DEBUG
+ switch (event->type)
+ {
+ case CALENDAR_EVENT_APPOINTMENT:
+ {
+ char *start_str;
+ char *end_str;
+ GSList *l;
+
+ start_str = CALENDAR_APPOINTMENT (event)->start_time ?
+ isodate_from_time_t (CALENDAR_APPOINTMENT (event)->start_time) :
+ g_strdup ("(undefined)");
+ end_str = CALENDAR_APPOINTMENT (event)->end_time ?
+ isodate_from_time_t (CALENDAR_APPOINTMENT (event)->end_time) :
+ g_strdup ("(undefined)");
+
+ g_free (start_str);
+ g_free (end_str);
+
+ for (l = CALENDAR_APPOINTMENT (event)->occurrences; l; l = l->next)
+ {
+ CalendarOccurrence *occurrence = l->data;
+
+ start_str = occurrence->start_time ?
+ isodate_from_time_t (occurrence->start_time) :
+ g_strdup ("(undefined)");
+
+ end_str = occurrence->end_time ?
+ isodate_from_time_t (occurrence->end_time) :
+ g_strdup ("(undefined)");
+
+ g_free (start_str);
+ g_free (end_str);
+ }
+ }
+ break;
+ case CALENDAR_EVENT_TASK:
+ {
+ char *start_str;
+ char *due_str;
+ char *completed_str;
+
+ start_str = CALENDAR_TASK (event)->start_time ?
+ isodate_from_time_t (CALENDAR_TASK (event)->start_time) :
+ g_strdup ("(undefined)");
+ due_str = CALENDAR_TASK (event)->due_time ?
+ isodate_from_time_t (CALENDAR_TASK (event)->due_time) :
+ g_strdup ("(undefined)");
+ completed_str = CALENDAR_TASK (event)->completed_time ?
+ isodate_from_time_t (CALENDAR_TASK (event)->completed_time) :
+ g_strdup ("(undefined)");
+
+ g_free (completed_str);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+#endif
+}
+
+static inline CalendarClientQuery *
+goddamn_this_is_crack (CalendarClientSource *source,
+ ECalClientView *view,
+ gboolean *emit_signal)
+{
+ g_assert (view != NULL);
+
+ if (source->completed_query.view == view)
+ {
+ if (emit_signal)
+ *emit_signal = TRUE;
+ return &source->completed_query;
+ }
+ else if (source->in_progress_query.view == view)
+ {
+ if (emit_signal)
+ *emit_signal = FALSE;
+ return &source->in_progress_query;
+ }
+
+ g_assert_not_reached ();
+
+ return NULL;
+}
+
+static void
+calendar_client_query_finalize (CalendarClientQuery *query)
+{
+ if (query->view)
+ g_object_unref (query->view);
+ query->view = NULL;
+
+ if (query->events)
+ g_hash_table_destroy (query->events);
+ query->events = NULL;
+}
+
+static void
+calendar_client_stop_query (CalendarClient *client,
+ CalendarClientSource *source,
+ CalendarClientQuery *query)
+{
+ if (query == &source->in_progress_query)
+ {
+ g_assert (source->query_in_progress != FALSE);
+
+ source->query_in_progress = FALSE;
+ }
+ else if (query == &source->completed_query)
+ {
+ g_assert (source->query_completed != FALSE);
+
+ source->query_completed = FALSE;
+ }
+ else
+ g_assert_not_reached ();
+
+ calendar_client_query_finalize (query);
+}
+
+typedef struct {
+ CalendarClient *client;
+ CalendarClientSource *source;
+ time_t start_time;
+ time_t end_time;
+ gboolean events_changed;
+ ICalTimezone *system_timezone;
+} InstanceGenerationData;
+
+static gboolean
+calendar_client_instance_cb (ICalComponent *icomp,
+ ICalTime *instance_start,
+ ICalTime *instance_end,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ InstanceGenerationData *data = user_data;
+ CalendarEvent *event;
+ CalendarEvent *old_event;
+ char *uid;
+ time_t start_time_t, end_time_t;
+
+ /* Convert instance times from their timezone to local time */
+ ICalTimezone *event_tz = i_cal_time_get_timezone(instance_start);
+
+ ICalTime *local_start = i_cal_time_clone(instance_start);
+ ICalTime *local_end = i_cal_time_clone(instance_end);
+
+ /* Convert to local timezone */
+ if (event_tz && data->system_timezone && event_tz != data->system_timezone) {
+ i_cal_time_convert_timezone(local_start, event_tz, data->system_timezone);
+ i_cal_time_convert_timezone(local_end, event_tz, data->system_timezone);
+ }
+
+ start_time_t = i_cal_time_as_timet (local_start);
+ end_time_t = i_cal_time_as_timet (local_end);
+
+ g_object_unref(local_start);
+ g_object_unref(local_end);
+
+ /* Create event from the component */
+ event = calendar_event_new (icomp, data->source, data->client->priv->zone);
+ if (!event)
+ return TRUE;
+
+ /* Override the times with the instance times (already converted to local timezone) */
+ if (event->type == CALENDAR_EVENT_APPOINTMENT) {
+ CALENDAR_APPOINTMENT (event)->start_time = start_time_t;
+ CALENDAR_APPOINTMENT (event)->end_time = end_time_t;
+
+ /* Create a single occurrence for this instance */
+ CalendarOccurrence *occurrence = g_new0 (CalendarOccurrence, 1);
+ occurrence->start_time = start_time_t;
+ occurrence->end_time = end_time_t;
+ CALENDAR_APPOINTMENT (event)->occurrences = g_slist_prepend (NULL, occurrence);
+ }
+
+ uid = calendar_event_get_uid (event);
+ old_event = g_hash_table_lookup (data->source->in_progress_query.events, uid);
+
+ if (!calendar_event_equal (event, old_event)) {
+ calendar_event_debug_dump (event);
+ g_hash_table_replace (data->source->in_progress_query.events, uid, event);
+ data->events_changed = TRUE;
+ } else {
+ calendar_event_free (event);
+ g_free (uid);
+ }
+
+ return TRUE;
+}
+
+static void
+calendar_client_start_query (CalendarClient *client,
+ CalendarClientSource *source,
+ const char *query)
+{
+ time_t month_begin, month_end;
+ GSList *objects = NULL;
+ GError *error = NULL;
+ InstanceGenerationData instance_data;
+
+ /* Validate that client is properly initialized */
+ if (client->priv->month == G_MAXUINT || client->priv->year == G_MAXUINT) {
+ return;
+ }
+
+ /* Calculate time range */
+ month_begin = make_time_for_day_begin (1, client->priv->month, client->priv->year);
+
+ /* Handle year rollover when month is December (11) */
+ if (client->priv->month == 11) { /* December */
+ month_end = make_time_for_day_begin (1, 0, client->priv->year + 1); /* January next year */
+ } else {
+ month_end = make_time_for_day_begin (1, client->priv->month + 1, client->priv->year);
+ }
+
+ /* Validate time range */
+ if (month_begin == -1 || month_end == -1) {
+ g_warning ("Invalid time range: month_begin=%ld, month_end=%ld", (long)month_begin, (long)month_end);
+ calendar_client_stop_query (client, source, &source->in_progress_query);
+ return;
+ }
+
+ if (source->query_in_progress)
+ calendar_client_stop_query (client, source, &source->in_progress_query);
+
+ source->query_in_progress = TRUE;
+ source->in_progress_query.view = NULL; /* No view needed for instance generation */
+ source->in_progress_query.events = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) calendar_event_free);
+
+ /* Get all objects for the month using query */
+ if (!e_cal_client_get_object_list_sync (source->source, query, &objects, NULL, &error)) {
+ g_warning ("Error getting calendar objects: %s", error->message);
+ g_error_free (error);
+ calendar_client_stop_query (client, source, &source->in_progress_query);
+ return;
+ }
+
+ /* Get system timezone once for all instances */
+ SystemTimezone *systz = system_timezone_new();
+ const char *system_tz_name = system_timezone_get(systz);
+ ICalTimezone *system_timezone = i_cal_timezone_get_builtin_timezone(system_tz_name);
+ g_object_unref(systz);
+
+ /* Set up instance generation data */
+ instance_data.client = client;
+ instance_data.source = source;
+ instance_data.start_time = month_begin;
+ instance_data.end_time = month_end;
+ instance_data.events_changed = FALSE;
+ instance_data.system_timezone = system_timezone;
+
+ /* Generate instances for each object with automatic timezone conversion */
+ for (GSList *l = objects; l; l = l->next) {
+ ICalComponent *component = l->data;
+
+ /* Some instances of recurring events may yield negative months - I think these are safe to skip */
+ if (month_begin < 0 || month_end < 0) {
+ continue;
+ }
+
+ e_cal_client_generate_instances_for_object_sync (source->source,
+ component,
+ month_begin,
+ month_end,
+ NULL, /* cancellable */
+ calendar_client_instance_cb,
+ &instance_data);
+ }
+
+ g_slist_free_full (objects, g_object_unref);
+
+ /* Query is now completed */
+ calendar_client_query_finalize (&source->completed_query);
+ source->completed_query = source->in_progress_query;
+ source->query_completed = TRUE;
+
+ source->query_in_progress = FALSE;
+ source->in_progress_query.view = NULL;
+ source->in_progress_query.events = NULL;
+
+ /* Emit signal to capture changed events */
+ if (instance_data.events_changed) {
+ g_signal_emit (source->client, source->changed_signal_id, 0);
+ }
+}
+
+static void
+calendar_client_update_appointments (CalendarClient *client)
+{
+ GSList *l;
+ char *query;
+ char *month_begin;
+ char *month_end;
+
+ if (client->priv->month == G_MAXUINT || client->priv->year == G_MAXUINT)
+ return;
+
+ month_begin = make_isodate_for_day_begin (1,
+ client->priv->month,
+ client->priv->year);
+
+ month_end = make_isodate_for_day_begin (1,
+ client->priv->month + 1,
+ client->priv->year);
+
+ query = g_strdup_printf ("occur-in-time-range? (make-time \"%s\") "
+ "(make-time \"%s\")",
+ month_begin, month_end);
+
+ for (l = client->priv->appointment_sources; l; l = l->next)
+ {
+ CalendarClientSource *cs = l->data;
+
+ calendar_client_start_query (client, cs, query);
+ }
+
+ g_free (month_begin);
+ g_free (month_end);
+ g_free (query);
+}
+
+/* FIXME:
+ * perhaps we should use evo's "hide_completed_tasks" pref?
+ */
+static void
+calendar_client_update_tasks (CalendarClient *client)
+{
+ GSList *l;
+ char *query;
+
+#ifdef FIX_BROKEN_TASKS_QUERY
+ /* FIXME: this doesn't work for tasks without a start or
+ * due date
+ * Look at filter_task() to see the behaviour we
+ * want.
+ */
+
+ char *day_begin;
+ char *day_end;
+
+ if (client->priv->day == G_MAXUINT ||
+ client->priv->month == G_MAXUINT ||
+ client->priv->year == G_MAXUINT)
+ return;
+
+ day_begin = make_isodate_for_day_begin (client->priv->day,
+ client->priv->month,
+ client->priv->year);
+
+ day_end = make_isodate_for_day_begin (client->priv->day + 1,
+ client->priv->month,
+ client->priv->year);
+ if (!day_begin || !day_end)
+ {
+ g_warning ("Cannot run query with invalid date: %dd %dy %dm\n",
+ client->priv->day,
+ client->priv->month,
+ client->priv->year);
+ g_free (day_begin);
+ g_free (day_end);
+ return;
+ }
+
+ query = g_strdup_printf ("(and (occur-in-time-range? (make-time \"%s\") "
+ "(make-time \"%s\")) "
+ "(or (not is-completed?) "
+ "(and (is-completed?) "
+ "(not (completed-before? (make-time \"%s\"))))))",
+ day_begin, day_end, day_begin);
+#else
+ query = g_strdup ("#t");
+#endif /* FIX_BROKEN_TASKS_QUERY */
+
+ for (l = client->priv->task_sources; l; l = l->next)
+ {
+ CalendarClientSource *cs = l->data;
+
+ calendar_client_start_query (client, cs, query);
+ }
+
+#ifdef FIX_BROKEN_TASKS_QUERY
+ g_free (day_begin);
+ g_free (day_end);
+#endif
+ g_free (query);
+}
+
+static void
+calendar_client_source_finalize (CalendarClientSource *source)
+{
+ source->client = NULL;
+
+ if (source->source) {
+ g_object_unref (source->source);
+ }
+ source->source = NULL;
+
+ calendar_client_query_finalize (&source->completed_query);
+ calendar_client_query_finalize (&source->in_progress_query);
+
+ source->query_completed = FALSE;
+ source->query_in_progress = FALSE;
+}
+
+static int
+compare_calendar_sources (CalendarClientSource *s1,
+ CalendarClientSource *s2)
+{
+ return (s1->source == s2->source) ? 0 : 1;
+}
+
+static GSList *
+calendar_client_update_sources_list (CalendarClient *client,
+ GSList *sources,
+ GList *esources,
+ guint changed_signal_id)
+{
+ GList *link;
+ GSList *retval, *l;
+
+ retval = NULL;
+
+ for (link = esources; link != NULL; link = g_list_next (link))
+ {
+ CalendarClientSource dummy_source;
+ CalendarClientSource *new_source;
+ GSList *s;
+ ECalClient *esource = link->data;
+
+ dummy_source.source = esource;
+
+ if ((s = g_slist_find_custom (sources,
+ &dummy_source,
+ (GCompareFunc) compare_calendar_sources)))
+ {
+ new_source = s->data;
+ sources = g_slist_delete_link (sources, s);
+ }
+ else
+ {
+ new_source = g_new0 (CalendarClientSource, 1);
+ new_source->client = client;
+ new_source->source = g_object_ref (esource);
+ new_source->changed_signal_id = changed_signal_id;
+ }
+
+ retval = g_slist_prepend (retval, new_source);
+ }
+
+ for (l = sources; l; l = l->next)
+ {
+ CalendarClientSource *source = l->data;
+
+ calendar_client_source_finalize (source);
+ g_free (source);
+ }
+ g_slist_free (sources);
+
+ return retval;
+}
+
+static void
+calendar_client_appointment_sources_changed (CalendarClient *client)
+{
+ GList *list;
+
+ list = calendar_sources_get_appointment_clients (client->priv->calendar_sources);
+
+ client->priv->appointment_sources = calendar_client_update_sources_list (client,
+ client->priv->appointment_sources,
+ list,
+ signals [APPOINTMENTS_CHANGED]);
+
+ load_calendars (client, CALENDAR_EVENT_APPOINTMENT);
+ calendar_client_update_appointments (client);
+
+ g_list_free (list);
+}
+
+static void
+calendar_client_task_sources_changed (CalendarClient *client)
+{
+ GList *list;
+
+ list = calendar_sources_get_task_clients (client->priv->calendar_sources);
+
+ client->priv->task_sources = calendar_client_update_sources_list (client,
+ client->priv->task_sources,
+ list,
+ signals [TASKS_CHANGED]);
+
+ load_calendars (client, CALENDAR_EVENT_TASK);
+ calendar_client_update_tasks (client);
+
+ g_list_free (list);
+}
+
+void
+calendar_client_get_date (CalendarClient *client,
+ guint *year,
+ guint *month,
+ guint *day)
+{
+ g_return_if_fail (CALENDAR_IS_CLIENT (client));
+
+ if (year)
+ *year = client->priv->year;
+
+ if (month)
+ *month = client->priv->month;
+
+ if (day)
+ *day = client->priv->day;
+}
+
+void
+calendar_client_select_month (CalendarClient *client,
+ guint month,
+ guint year)
+{
+ g_return_if_fail (CALENDAR_IS_CLIENT (client));
+ g_return_if_fail (month <= 11);
+
+ if (client->priv->year != year || client->priv->month != month)
+ {
+ client->priv->month = month;
+ client->priv->year = year;
+
+ calendar_client_update_appointments (client);
+ calendar_client_update_tasks (client);
+
+ g_object_freeze_notify (G_OBJECT (client));
+ g_object_notify (G_OBJECT (client), "month");
+ g_object_notify (G_OBJECT (client), "year");
+ g_object_thaw_notify (G_OBJECT (client));
+ }
+}
+
+void
+calendar_client_select_day (CalendarClient *client,
+ guint day)
+{
+ g_return_if_fail (CALENDAR_IS_CLIENT (client));
+ g_return_if_fail (day <= 31);
+
+ if (client->priv->day != day)
+ {
+ client->priv->day = day;
+
+ /* don't need to update appointments unless
+ * the selected month changes
+ */
+#ifdef FIX_BROKEN_TASKS_QUERY
+ calendar_client_update_tasks (client);
+#endif
+
+ g_object_notify (G_OBJECT (client), "day");
+ }
+}
+
+typedef struct
+{
+ CalendarClient *client;
+ GSList *events;
+ time_t start_time;
+ time_t end_time;
+} FilterData;
+
+typedef void (* CalendarEventFilterFunc) (const char *uid,
+ CalendarEvent *event,
+ FilterData *filter_data);
+
+static void
+filter_appointment (const char *uid,
+ CalendarEvent *event,
+ FilterData *filter_data)
+{
+ GSList *occurrences, *l;
+
+ if (event->type != CALENDAR_EVENT_APPOINTMENT)
+ return;
+
+ occurrences = CALENDAR_APPOINTMENT (event)->occurrences;
+ CALENDAR_APPOINTMENT (event)->occurrences = NULL;
+
+ for (l = occurrences; l; l = l->next)
+ {
+ CalendarOccurrence *occurrence = l->data;
+ time_t start_time = occurrence->start_time;
+ time_t end_time = occurrence->end_time;
+
+ if ((start_time >= filter_data->start_time &&
+ start_time < filter_data->end_time) ||
+ (start_time <= filter_data->start_time &&
+ (end_time - 1) > filter_data->start_time))
+ {
+ CalendarEvent *new_event;
+
+ new_event = calendar_event_copy (event);
+
+ CALENDAR_APPOINTMENT (new_event)->start_time = occurrence->start_time;
+ CALENDAR_APPOINTMENT (new_event)->end_time = occurrence->end_time;
+
+ filter_data->events = g_slist_prepend (filter_data->events, new_event);
+ }
+ }
+
+ CALENDAR_APPOINTMENT (event)->occurrences = occurrences;
+}
+
+static void
+filter_task (const char *uid,
+ CalendarEvent *event,
+ FilterData *filter_data)
+{
+#ifdef FIX_BROKEN_TASKS_QUERY
+ CalendarTask *task;
+#endif
+
+ if (event->type != CALENDAR_EVENT_TASK)
+ return;
+
+#ifdef FIX_BROKEN_TASKS_QUERY
+ task = CALENDAR_TASK (event);
+
+ if (task->start_time && task->start_time > filter_data->start_time)
+ return;
+
+ if (task->completed_time &&
+ (task->completed_time < filter_data->start_time ||
+ task->completed_time > filter_data->end_time))
+ return;
+#endif /* FIX_BROKEN_TASKS_QUERY */
+
+ filter_data->events = g_slist_prepend (filter_data->events,
+ calendar_event_copy (event));
+}
+
+static GSList *
+calendar_client_filter_events (CalendarClient *client,
+ GSList *sources,
+ CalendarEventFilterFunc filter_func,
+ time_t start_time,
+ time_t end_time)
+{
+ FilterData filter_data;
+ GSList *l;
+ GSList *retval;
+
+ if (!sources)
+ return NULL;
+
+ filter_data.client = client;
+ filter_data.events = NULL;
+ filter_data.start_time = start_time;
+ filter_data.end_time = end_time;
+
+ retval = NULL;
+ for (l = sources; l; l = l->next)
+ {
+ CalendarClientSource *source = l->data;
+
+ if (source->query_completed)
+ {
+ filter_data.events = NULL;
+ g_hash_table_foreach (source->completed_query.events,
+ (GHFunc) filter_func,
+ &filter_data);
+
+ filter_data.events = g_slist_reverse (filter_data.events);
+
+ retval = g_slist_concat (retval, filter_data.events);
+ }
+ }
+
+ return retval;
+}
+
+GSList *
+calendar_client_get_events (CalendarClient *client,
+ CalendarEventType event_mask)
+{
+ GSList *appointments;
+ GSList *tasks;
+ time_t day_begin;
+ time_t day_end;
+
+ g_return_val_if_fail (CALENDAR_IS_CLIENT (client), NULL);
+ g_return_val_if_fail (client->priv->day != G_MAXUINT, NULL);
+ g_return_val_if_fail (client->priv->month != G_MAXUINT, NULL);
+ g_return_val_if_fail (client->priv->year != G_MAXUINT, NULL);
+
+ day_begin = make_time_for_day_begin (client->priv->day,
+ client->priv->month,
+ client->priv->year);
+ day_end = make_time_for_day_begin (client->priv->day + 1,
+ client->priv->month,
+ client->priv->year);
+
+ appointments = NULL;
+ if (event_mask & CALENDAR_EVENT_APPOINTMENT)
+ {
+ appointments = calendar_client_filter_events (client,
+ client->priv->appointment_sources,
+ filter_appointment,
+ day_begin,
+ day_end);
+ }
+
+ tasks = NULL;
+ if (event_mask & CALENDAR_EVENT_TASK)
+ {
+ tasks = calendar_client_filter_events (client,
+ client->priv->task_sources,
+ filter_task,
+ day_begin,
+ day_end);
+ }
+
+ return g_slist_concat (appointments, tasks);
+}
+
+static inline int
+day_from_time_t (time_t t)
+{
+ struct tm *tm = localtime (&t);
+
+ g_assert (tm == NULL || (tm->tm_mday >=1 && tm->tm_mday <= 31));
+
+ return tm ? tm->tm_mday : 0;
+}
+
+void
+calendar_client_foreach_appointment_day (CalendarClient *client,
+ CalendarDayIter iter_func,
+ gpointer user_data)
+{
+ GSList *appointments, *l;
+ gboolean marked_days [32] = { FALSE, };
+ time_t month_begin;
+ time_t month_end;
+ int i;
+
+ g_return_if_fail (CALENDAR_IS_CLIENT (client));
+ g_return_if_fail (iter_func != NULL);
+ g_return_if_fail (client->priv->month != G_MAXUINT);
+ g_return_if_fail (client->priv->year != G_MAXUINT);
+
+ month_begin = make_time_for_day_begin (1,
+ client->priv->month,
+ client->priv->year);
+ month_end = make_time_for_day_begin (1,
+ client->priv->month + 1,
+ client->priv->year);
+
+ appointments = calendar_client_filter_events (client,
+ client->priv->appointment_sources,
+ filter_appointment,
+ month_begin,
+ month_end);
+ for (l = appointments; l; l = l->next)
+ {
+ CalendarAppointment *appointment = l->data;
+
+ if (appointment->start_time)
+ {
+ time_t day_time = appointment->start_time;
+
+ if (day_time >= month_begin)
+ marked_days [day_from_time_t (day_time)] = TRUE;
+
+ if (appointment->end_time)
+ {
+ int day_offset;
+ int duration = appointment->end_time - appointment->start_time;
+ /* mark the days for the appointment, no need to add an extra one when duration is a multiple of 86400 */
+ for (day_offset = 1; day_offset <= duration / 86400 && duration != day_offset * 86400; day_offset++)
+ {
+ time_t day_tm = appointment->start_time + day_offset * 86400;
+
+ if (day_tm > month_end)
+ break;
+ if (day_tm >= month_begin)
+ marked_days [day_from_time_t (day_tm)] = TRUE;
+ }
+ }
+ }
+ calendar_event_free (CALENDAR_EVENT (appointment));
+ }
+
+ g_slist_free (appointments);
+
+ for (i = 1; i < 32; i++)
+ {
+ if (marked_days [i])
+ iter_func (client, i, user_data);
+ }
+}
+
+void
+calendar_client_set_task_completed (CalendarClient *client,
+ char *task_uid,
+ gboolean task_completed,
+ guint percent_complete)
+{
+ GSList *l;
+ ECalClient *esource;
+ ICalComponent *component;
+ ICalProperty *prop;
+ ICalPropertyStatus status;
+
+ g_return_if_fail (CALENDAR_IS_CLIENT (client));
+ g_return_if_fail (task_uid != NULL);
+ g_return_if_fail (task_completed == FALSE || percent_complete == 100);
+
+ component = NULL;
+ esource = NULL;
+ for (l = client->priv->task_sources; l; l = l->next)
+ {
+ CalendarClientSource *source = l->data;
+
+ esource = source->source;
+ e_cal_client_get_object_sync (esource, task_uid, NULL, &component, NULL, NULL);
+ if (component)
+ break;
+ }
+
+ if (!component)
+ {
+ g_warning ("Cannot locate task with uid = '%s'\n", task_uid);
+ return;
+ }
+
+ g_assert (esource != NULL);
+
+ /* Completed time */
+ prop = i_cal_component_get_first_property (component, I_CAL_COMPLETED_PROPERTY);
+ if (task_completed)
+ {
+ ICalTime *completed_time;
+
+ completed_time = i_cal_time_new_current_with_zone (client->priv->zone);
+ if (!prop)
+ {
+ i_cal_component_take_property (component,
+ i_cal_property_new_completed (completed_time));
+ }
+ else
+ {
+ i_cal_property_set_completed (prop, completed_time);
+ }
+ }
+ else if (prop)
+ {
+ i_cal_component_remove_property (component, prop);
+ }
+ g_clear_object (&prop);
+
+ /* Percent complete */
+ prop = i_cal_component_get_first_property (component, I_CAL_PERCENTCOMPLETE_PROPERTY);
+ if (!prop)
+ {
+ i_cal_component_take_property (component,
+ i_cal_property_new_percentcomplete (percent_complete));
+ }
+ else
+ {
+ i_cal_property_set_percentcomplete (prop, percent_complete);
+ }
+ g_clear_object (&prop);
+
+ /* Status */
+ status = task_completed ? I_CAL_STATUS_COMPLETED : I_CAL_STATUS_NEEDSACTION;
+ prop = i_cal_component_get_first_property (component, I_CAL_STATUS_PROPERTY);
+ if (prop)
+ {
+ i_cal_property_set_status (prop, status);
+ }
+ else
+ {
+ i_cal_component_take_property (component, i_cal_property_new_status (status));
+ }
+ g_clear_object (&prop);
+
+ e_cal_client_modify_object_sync (esource,
+ component,
+ E_CAL_OBJ_MOD_ALL,
+ 0,
+ NULL,
+ NULL);
+}
+
+gboolean
+calendar_client_create_task (CalendarClient *client,
+ const char *summary)
+{
+ GSList *l;
+ ECalClient *task_client = NULL;
+ ICalComponent *vtodo_component;
+ gchar *uid;
+ GError *error = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (CALENDAR_IS_CLIENT (client), FALSE);
+ g_return_val_if_fail (summary != NULL && *summary != '\0', FALSE);
+
+ /* Use the first available task source (like the existing code does) */
+ for (l = client->priv->task_sources; l; l = l->next)
+ {
+ CalendarClientSource *source = l->data;
+ task_client = source->source;
+ if (task_client)
+ break;
+ }
+
+ if (!task_client)
+ {
+ g_warning ("No task client available for task creation");
+ return FALSE;
+ }
+
+ /* Create a simple VTODO component */
+ vtodo_component = i_cal_component_new (I_CAL_VTODO_COMPONENT);
+
+ /* Generate UID */
+ uid = e_util_generate_uid ();
+ i_cal_component_set_uid (vtodo_component, uid);
+ g_free (uid);
+
+ /* Set summary */
+ i_cal_component_set_summary (vtodo_component, summary);
+
+ /* Set created time */
+ ICalTime *now = i_cal_time_new_current_with_zone (client->priv->zone);
+ i_cal_component_set_dtstamp (vtodo_component, now);
+ g_object_unref (now);
+
+ /* Create the task */
+ success = e_cal_client_create_object_sync (task_client,
+ vtodo_component,
+ E_CAL_OPERATION_FLAG_NONE,
+ NULL, /* out uid */
+ NULL, /* cancellable */
+ &error);
+
+ if (error)
+ {
+ g_warning ("Failed to create task: %s", error->message);
+ g_error_free (error);
+ success = FALSE;
+ }
+
+ /* Cleanup */
+ g_object_unref (vtodo_component);
+
+ return success;
+}
+
+#endif /* HAVE_EDS */
diff --git a/applets/clock/calendar-client.h b/applets/clock/calendar-client.h
new file mode 100644
index 00000000..7747d23f
--- /dev/null
+++ b/applets/clock/calendar-client.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Mark McLoughlin <[email protected]>
+ * William Jon McCann <[email protected]>
+ * Martin Grimme <[email protected]>
+ * Christian Kellner <[email protected]>
+ */
+
+#ifndef __CALENDAR_CLIENT_H__
+#define __CALENDAR_CLIENT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ CALENDAR_EVENT_APPOINTMENT = 1 << 0,
+ CALENDAR_EVENT_TASK = 1 << 1,
+ CALENDAR_EVENT_ALL = (1 << 2) - 1
+} CalendarEventType;
+
+#define CALENDAR_TYPE_CLIENT (calendar_client_get_type ())
+#define CALENDAR_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CALENDAR_TYPE_CLIENT, CalendarClient))
+#define CALENDAR_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CALENDAR_TYPE_CLIENT, CalendarClientClass))
+#define CALENDAR_IS_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CALENDAR_TYPE_CLIENT))
+#define CALENDAR_IS_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CALENDAR_TYPE_CLIENT))
+#define CALENDAR_CLIENT_GET_CLASS(o)(G_TYPE_INSTANCE_GET_CLASS ((o), CALENDAR_TYPE_CLIENT, CalendarClientClass))
+
+typedef struct _CalendarClient CalendarClient;
+typedef struct _CalendarClientClass CalendarClientClass;
+typedef struct _CalendarClientPrivate CalendarClientPrivate;
+
+struct _CalendarClient
+{
+ GObject parent;
+ CalendarClientPrivate *priv;
+};
+
+struct _CalendarClientClass
+{
+ GObjectClass parent_class;
+
+ void (* appointments_changed) (CalendarClient *client);
+ void (* tasks_changed) (CalendarClient *client);
+};
+
+
+typedef struct
+{
+ time_t start_time;
+ time_t end_time;
+} CalendarOccurrence;
+
+typedef struct
+{
+ char *uid;
+ char *rid;
+ char *backend_name;
+ char *summary;
+ char *description;
+ char *color_string;
+ time_t start_time;
+ time_t end_time;
+ guint is_all_day : 1;
+
+ /* Only used internally */
+ GSList *occurrences;
+} CalendarAppointment;
+
+typedef struct
+{
+ char *uid;
+ char *summary;
+ char *description;
+ char *color_string;
+ char *url;
+ time_t start_time;
+ time_t due_time;
+ guint percent_complete;
+ time_t completed_time;
+ int priority;
+} CalendarTask;
+
+typedef struct
+{
+ union
+ {
+ CalendarAppointment appointment;
+ CalendarTask task;
+ } event;
+ CalendarEventType type;
+} CalendarEvent;
+
+#define CALENDAR_EVENT(e) ((CalendarEvent *)(e))
+#define CALENDAR_APPOINTMENT(e) ((CalendarAppointment *)(e))
+#define CALENDAR_TASK(e) ((CalendarTask *)(e))
+
+typedef void (* CalendarDayIter) (CalendarClient *client,
+ guint day,
+ gpointer user_data);
+
+
+GType calendar_client_get_type (void) G_GNUC_CONST;
+CalendarClient *calendar_client_new (GSettings *settings);
+
+void calendar_client_get_date (CalendarClient *client,
+ guint *year,
+ guint *month,
+ guint *day);
+void calendar_client_select_month (CalendarClient *client,
+ guint month,
+ guint year);
+void calendar_client_select_day (CalendarClient *client,
+ guint day);
+
+GSList *calendar_client_get_events (CalendarClient *client,
+ CalendarEventType event_mask);
+void calendar_client_foreach_appointment_day (CalendarClient *client,
+ CalendarDayIter iter_func,
+ gpointer user_data);
+
+void calendar_client_set_task_completed (CalendarClient *client,
+ char *task_uid,
+ gboolean task_completed,
+ guint percent_complete);
+
+gboolean calendar_client_create_task (CalendarClient *client,
+ const char *summary);
+
+void calendar_event_free (CalendarEvent *event);
+
+G_END_DECLS
+
+#endif /* __CALENDAR_CLIENT_H__ */
diff --git a/applets/clock/calendar-debug.h b/applets/clock/calendar-debug.h
new file mode 100644
index 00000000..39befd74
--- /dev/null
+++ b/applets/clock/calendar-debug.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Mark McLoughlin <[email protected]>
+ */
+
+#ifndef __CALENDAR_DEBUG_H__
+#define __CALENDAR_DEBUG_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#ifdef CALENDAR_ENABLE_DEBUG
+
+#include <stdio.h>
+
+#ifdef G_HAVE_ISO_VARARGS
+# define dprintf(...) fprintf (stderr, __VA_ARGS__);
+#elif defined(G_HAVE_GNUC_VARARGS)
+# define dprintf(args...) fprintf (stderr, args);
+#endif
+
+#else /* if !defined (CALENDAR_DEBUG) */
+
+#ifdef G_HAVE_ISO_VARARGS
+# define dprintf(...)
+#elif defined(G_HAVE_GNUC_VARARGS)
+# define dprintf(args...)
+#endif
+
+#endif /* CALENDAR_ENABLE_DEBUG */
+
+G_END_DECLS
+
+#endif /* __CALENDAR_DEBUG_H__ */
diff --git a/applets/clock/calendar-sources.c b/applets/clock/calendar-sources.c
new file mode 100644
index 00000000..54c6dc8c
--- /dev/null
+++ b/applets/clock/calendar-sources.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Mark McLoughlin <[email protected]>
+ * William Jon McCann <[email protected]>
+ * Martin Grimme <[email protected]>
+ * Christian Kellner <[email protected]>
+ */
+
+#include <config.h>
+
+#include "calendar-sources.h"
+
+#include <libintl.h>
+#include <string.h>
+#define HANDLE_LIBICAL_MEMORY
+
+
+#include <libecal/libecal.h>
+
+#undef CALENDAR_ENABLE_DEBUG
+#include "calendar-debug.h"
+
+#ifndef _
+#define _(x) gettext(x)
+#endif
+
+#ifndef N_
+#define N_(x) x
+#endif
+
+typedef struct _ClientData ClientData;
+typedef struct _CalendarSourceData CalendarSourceData;
+
+struct _ClientData
+{
+ ECalClient *client;
+ gulong backend_died_id;
+};
+
+struct _CalendarSourceData
+{
+ ECalClientSourceType source_type;
+ CalendarSources *sources;
+ guint changed_signal;
+
+ /* ESource -> EClient */
+ GHashTable *clients;
+
+ guint timeout_id;
+
+ guint loaded : 1;
+};
+
+struct _CalendarSourcesPrivate
+{
+ ESourceRegistry *registry;
+ gulong source_added_id;
+ gulong source_changed_id;
+ gulong source_removed_id;
+
+ CalendarSourceData appointment_sources;
+ CalendarSourceData task_sources;
+};
+
+static void calendar_sources_finalize (GObject *object);
+
+static void backend_died_cb (EClient *client, CalendarSourceData *source_data);
+static void calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
+ ESource *source,
+ CalendarSources *sources);
+static void calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
+ ESource *source,
+ CalendarSources *sources);
+
+enum
+{
+ APPOINTMENT_SOURCES_CHANGED,
+ TASK_SOURCES_CHANGED,
+ LAST_SIGNAL
+};
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static CalendarSources *calendar_sources_singleton = NULL;
+
+static void
+client_data_free (ClientData *data)
+{
+ g_signal_handler_disconnect (data->client, data->backend_died_id);
+ g_object_unref (data->client);
+ g_free (data);
+}
+
+G_DEFINE_TYPE_WITH_PRIVATE (CalendarSources, calendar_sources, G_TYPE_OBJECT)
+
+static void
+calendar_sources_class_init (CalendarSourcesClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->finalize = calendar_sources_finalize;
+
+ signals [APPOINTMENT_SOURCES_CHANGED] =
+ g_signal_new ("appointment-sources-changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CalendarSourcesClass,
+ appointment_sources_changed),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [TASK_SOURCES_CHANGED] =
+ g_signal_new ("task-sources-changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CalendarSourcesClass,
+ task_sources_changed),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+}
+
+static void
+calendar_sources_init (CalendarSources *sources)
+{
+ GError *error = NULL;
+
+ sources->priv = calendar_sources_get_instance_private (sources);
+
+ /* XXX Not sure what to do if this fails.
+ * Should this class implement GInitable or pass the
+ * registry in as a G_PARAM_CONSTRUCT_ONLY property? */
+ sources->priv->registry = e_source_registry_new_sync (NULL, &error);
+ if (error != NULL) {
+ g_critical ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+
+ sources->priv->source_added_id = g_signal_connect (sources->priv->registry,
+ "source-added",
+ G_CALLBACK (calendar_sources_registry_source_changed_cb),
+ sources);
+ sources->priv->source_changed_id = g_signal_connect (sources->priv->registry,
+ "source-changed",
+ G_CALLBACK (calendar_sources_registry_source_changed_cb),
+ sources);
+ sources->priv->source_removed_id = g_signal_connect (sources->priv->registry,
+ "source-removed",
+ G_CALLBACK (calendar_sources_registry_source_removed_cb),
+ sources);
+
+ sources->priv->appointment_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
+ sources->priv->appointment_sources.sources = sources;
+ sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED];
+ sources->priv->appointment_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
+ (GEqualFunc) e_source_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) client_data_free);
+ sources->priv->appointment_sources.timeout_id = 0;
+
+ sources->priv->task_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
+ sources->priv->task_sources.sources = sources;
+ sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED];
+ sources->priv->task_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
+ (GEqualFunc) e_source_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) client_data_free);
+ sources->priv->task_sources.timeout_id = 0;
+}
+
+static void
+calendar_sources_finalize_source_data (CalendarSources *sources,
+ CalendarSourceData *source_data)
+{
+ if (source_data->loaded)
+ {
+ g_hash_table_destroy (source_data->clients);
+ source_data->clients = NULL;
+
+ if (source_data->timeout_id != 0)
+ {
+ g_source_remove (source_data->timeout_id);
+ source_data->timeout_id = 0;
+ }
+
+ source_data->loaded = FALSE;
+ }
+}
+
+static void
+calendar_sources_finalize (GObject *object)
+{
+ CalendarSources *sources = CALENDAR_SOURCES (object);
+
+ if (sources->priv->registry)
+ {
+ g_signal_handler_disconnect (sources->priv->registry,
+ sources->priv->source_added_id);
+ g_signal_handler_disconnect (sources->priv->registry,
+ sources->priv->source_changed_id);
+ g_signal_handler_disconnect (sources->priv->registry,
+ sources->priv->source_removed_id);
+ g_object_unref (sources->priv->registry);
+ }
+ sources->priv->registry = NULL;
+
+ calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources);
+ calendar_sources_finalize_source_data (sources, &sources->priv->task_sources);
+
+ G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object);
+}
+
+CalendarSources *
+calendar_sources_get (void)
+{
+ gpointer singleton_location = &calendar_sources_singleton;
+
+ if (calendar_sources_singleton)
+ return g_object_ref (calendar_sources_singleton);
+
+ calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL);
+ g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton),
+ singleton_location);
+
+ return calendar_sources_singleton;
+}
+
+/* The clients are just created here but not loaded */
+static void
+create_client_for_source (ESource *source,
+ ECalClientSourceType source_type,
+ CalendarSourceData *source_data)
+{
+ ClientData *data;
+ GError *error;
+ EClient *client;
+
+ client = g_hash_table_lookup (source_data->clients, source);
+ g_return_if_fail (client == NULL);
+
+ error = NULL;
+ client = e_cal_client_connect_sync (source, source_type, -1, NULL, &error);
+
+ if (!client)
+ {
+ g_warning ("Could not load source '%s': %s",
+ e_source_get_uid (source),
+ error->message);
+
+ g_clear_error (&error);
+ return;
+ }
+
+ data = g_new0 (ClientData, 1);
+ data->client = E_CAL_CLIENT (client); /* takes ownership */
+ data->backend_died_id = g_signal_connect (client,
+ "backend-died",
+ G_CALLBACK (backend_died_cb),
+ source_data);
+
+ g_hash_table_insert (source_data->clients, g_object_ref (source), data);
+}
+
+static inline void
+debug_dump_ecal_list (GHashTable *clients)
+{
+#ifdef CALENDAR_ENABLE_DEBUG
+ GList *list, *link;
+
+ dprintf ("Loaded clients:\n");
+ list = g_hash_table_get_keys (clients);
+ for (link = list; link != NULL; link = g_list_next (link))
+ {
+ ESource *source = E_SOURCE (link->data);
+
+ dprintf (" %s %s\n",
+ e_source_get_uid (source),
+ e_source_get_display_name (source));
+ }
+#endif
+}
+
+static void
+calendar_sources_load_esource_list (ESourceRegistry *registry,
+ CalendarSourceData *source_data);
+
+static gboolean
+backend_restart (gpointer data)
+{
+ CalendarSourceData *source_data = data;
+ ESourceRegistry *registry;
+
+ registry = source_data->sources->priv->registry;
+ calendar_sources_load_esource_list (registry, source_data);
+ g_signal_emit (source_data->sources, source_data->changed_signal, 0);
+
+ source_data->timeout_id = 0;
+
+ return FALSE;
+}
+
+static void
+backend_died_cb (EClient *client, CalendarSourceData *source_data)
+{
+ ESource *source;
+ const char *display_name;
+
+ source = e_client_get_source (client);
+ display_name = e_source_get_display_name (source);
+ g_warning ("The calendar backend for '%s' has crashed.", display_name);
+ g_hash_table_remove (source_data->clients, source);
+
+ if (source_data->timeout_id != 0)
+ {
+ g_source_remove (source_data->timeout_id);
+ source_data->timeout_id = 0;
+ }
+
+ source_data->timeout_id = g_timeout_add_seconds (2, backend_restart,
+ source_data);
+}
+
+static void
+calendar_sources_load_esource_list (ESourceRegistry *registry,
+ CalendarSourceData *source_data)
+{
+ GList *list, *link;
+ const gchar *extension_name;
+
+ switch (source_data->source_type)
+ {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ extension_name = E_SOURCE_EXTENSION_CALENDAR;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ case E_CAL_CLIENT_SOURCE_TYPE_LAST:
+ default:
+ g_return_if_reached ();
+ }
+
+ list = e_source_registry_list_sources (registry, extension_name);
+
+ for (link = list; link != NULL; link = g_list_next (link))
+ {
+ ESource *source = E_SOURCE (link->data);
+ ESourceSelectable *extension;
+ gboolean show_source;
+
+ extension = e_source_get_extension (source, extension_name);
+ show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
+
+ if (show_source)
+ create_client_for_source (source, source_data->source_type, source_data);
+ }
+
+ debug_dump_ecal_list (source_data->clients);
+
+ g_list_free_full (list, g_object_unref);
+}
+
+static void
+calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
+ ESource *source,
+ CalendarSources *sources)
+{
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
+ {
+ CalendarSourceData *source_data;
+ ESourceSelectable *extension;
+ gboolean have_client;
+ gboolean show_source;
+
+ source_data = &sources->priv->appointment_sources;
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
+ have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
+ show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
+
+ if (!show_source && have_client)
+ {
+ g_hash_table_remove (source_data->clients, source);
+ g_signal_emit (sources, source_data->changed_signal, 0);
+ }
+ if (show_source && !have_client)
+ {
+ create_client_for_source (source, source_data->source_type, source_data);
+ g_signal_emit (sources, source_data->changed_signal, 0);
+ }
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+ {
+ CalendarSourceData *source_data;
+ ESourceSelectable *extension;
+ gboolean have_client;
+ gboolean show_source;
+
+ source_data = &sources->priv->task_sources;
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
+ have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
+ show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
+
+ if (!show_source && have_client)
+ {
+ g_hash_table_remove (source_data->clients, source);
+ g_signal_emit (sources, source_data->changed_signal, 0);
+ }
+ if (show_source && !have_client)
+ {
+ create_client_for_source (source, source_data->source_type, source_data);
+ g_signal_emit (sources, source_data->changed_signal, 0);
+ }
+ }
+}
+
+static void
+calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
+ ESource *source,
+ CalendarSources *sources)
+{
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
+ {
+ CalendarSourceData *source_data;
+
+ source_data = &sources->priv->appointment_sources;
+ g_hash_table_remove (source_data->clients, source);
+ g_signal_emit (sources, source_data->changed_signal, 0);
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+ {
+ CalendarSourceData *source_data;
+
+ source_data = &sources->priv->task_sources;
+ g_hash_table_remove (source_data->clients, source);
+ g_signal_emit (sources, source_data->changed_signal, 0);
+ }
+}
+
+GList *
+calendar_sources_get_appointment_clients (CalendarSources *sources)
+{
+ GList *list, *link;
+
+ g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
+
+ if (!sources->priv->appointment_sources.loaded)
+ {
+ calendar_sources_load_esource_list (sources->priv->registry,
+ &sources->priv->appointment_sources);
+ sources->priv->appointment_sources.loaded = TRUE;
+ }
+
+ list = g_hash_table_get_values (sources->priv->appointment_sources.clients);
+
+ for (link = list; link != NULL; link = g_list_next (link))
+ link->data = ((ClientData *) link->data)->client;
+
+ return list;
+}
+
+GList *
+calendar_sources_get_task_clients (CalendarSources *sources)
+{
+ GList *list, *link;
+
+ g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
+
+ if (!sources->priv->task_sources.loaded)
+ {
+ calendar_sources_load_esource_list (sources->priv->registry,
+ &sources->priv->task_sources);
+ sources->priv->task_sources.loaded = TRUE;
+ }
+
+ list = g_hash_table_get_values (sources->priv->task_sources.clients);
+
+ for (link = list; link != NULL; link = g_list_next (link))
+ link->data = ((ClientData *) link->data)->client;
+
+ return list;
+}
diff --git a/applets/clock/calendar-sources.h b/applets/clock/calendar-sources.h
new file mode 100644
index 00000000..1dfc7445
--- /dev/null
+++ b/applets/clock/calendar-sources.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Mark McLoughlin <[email protected]>
+ * William Jon McCann <[email protected]>
+ * Martin Grimme <[email protected]>
+ * Christian Kellner <[email protected]>
+ */
+
+#ifndef __CALENDAR_SOURCES_H__
+#define __CALENDAR_SOURCES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CALENDAR_TYPE_SOURCES (calendar_sources_get_type ())
+#define CALENDAR_SOURCES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CALENDAR_TYPE_SOURCES, CalendarSources))
+#define CALENDAR_SOURCES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CALENDAR_TYPE_SOURCES, CalendarSourcesClass))
+#define CALENDAR_IS_SOURCES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CALENDAR_TYPE_SOURCES))
+#define CALENDAR_IS_SOURCES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CALENDAR_TYPE_SOURCES))
+#define CALENDAR_SOURCES_GET_CLASS(o)(G_TYPE_INSTANCE_GET_CLASS ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesClass))
+
+typedef struct _CalendarSources CalendarSources;
+typedef struct _CalendarSourcesClass CalendarSourcesClass;
+typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate;
+
+struct _CalendarSources
+{
+ GObject parent;
+ CalendarSourcesPrivate *priv;
+};
+
+struct _CalendarSourcesClass
+{
+ GObjectClass parent_class;
+
+ void (* appointment_sources_changed) (CalendarSources *sources);
+ void (* task_sources_changed) (CalendarSources *sources);
+};
+
+
+GType calendar_sources_get_type (void) G_GNUC_CONST;
+CalendarSources *calendar_sources_get (void);
+GList *calendar_sources_get_appointment_clients (CalendarSources *sources);
+GList *calendar_sources_get_task_clients (CalendarSources *sources);
+
+G_END_DECLS
+
+#endif /* __CALENDAR_SOURCES_H__ */
diff --git a/applets/clock/calendar-window.c b/applets/clock/calendar-window.c
index 8fe2c77c..ae9f1d7c 100644
--- a/applets/clock/calendar-window.c
+++ b/applets/clock/calendar-window.c
@@ -38,11 +38,24 @@
#include "clock.h"
#include "clock-utils.h"
#include "clock-typebuiltins.h"
+#ifdef HAVE_EDS
+#include "calendar-client.h"
+#endif
#define KEY_LOCATIONS_EXPANDED "expand-locations"
+#ifdef HAVE_EDS
+#define KEY_SHOW_CALENDAR_EVENTS "show-calendar-events"
+#define KEY_SHOW_TASKS "show-tasks"
+#define SCHEMA_CALENDAR_APP "org.mate.desktop.default-applications.office.calendar"
+#define SCHEMA_TASKS_APP "org.mate.desktop.default-applications.office.tasks"
+#endif
+
enum {
EDIT_LOCATIONS,
+#ifdef HAVE_EDS
+ PERMISSION_READY,
+#endif
LAST_SIGNAL
};
@@ -60,6 +73,33 @@ struct _CalendarWindowPrivate {
GtkWidget *locations_list;
GSettings *settings;
+
+ /* Signal handler IDs for proper cleanup */
+ gulong calendar_month_changed_id;
+ gulong calendar_day_selected_id;
+
+#ifdef HAVE_EDS
+ ClockFormat time_format;
+
+ CalendarClient *client;
+
+ GtkWidget *appointment_list;
+
+ GtkListStore *appointments_model;
+ GtkListStore *tasks_model;
+
+ GtkTreeSelection *previous_selection;
+
+ GtkTreeModelFilter *appointments_filter;
+ GtkTreeModelFilter *tasks_filter;
+
+ GtkWidget *task_list;
+ GtkWidget *task_entry;
+
+ /* EDS-specific signal handler IDs */
+ gulong client_appointments_changed_id;
+ gulong client_tasks_changed_id;
+#endif /* HAVE_EDS */
};
G_DEFINE_TYPE_WITH_PRIVATE (CalendarWindow, calendar_window, GTK_TYPE_WINDOW)
@@ -84,6 +124,63 @@ static GtkWidget * create_hig_frame (CalendarWindow *calwin,
const char *key,
GCallback callback);
+#ifdef HAVE_EDS
+enum {
+ APPOINTMENT_COLUMN_UID,
+ APPOINTMENT_COLUMN_TYPE,
+ APPOINTMENT_COLUMN_SUMMARY,
+ APPOINTMENT_COLUMN_DESCRIPTION,
+ APPOINTMENT_COLUMN_START_TIME,
+ APPOINTMENT_COLUMN_START_TEXT,
+ APPOINTMENT_COLUMN_END_TIME,
+ APPOINTMENT_COLUMN_ALL_DAY,
+ APPOINTMENT_COLUMN_COLOR,
+ N_APPOINTMENT_COLUMNS
+};
+
+enum {
+ TASK_COLUMN_UID,
+ TASK_COLUMN_TYPE,
+ TASK_COLUMN_SUMMARY,
+ TASK_COLUMN_DESCRIPTION,
+ TASK_COLUMN_START_TIME,
+ TASK_COLUMN_START_TEXT,
+ TASK_COLUMN_DUE_TIME,
+ TASK_COLUMN_DUE_TEXT,
+ TASK_COLUMN_PERCENT_COMPLETE,
+ TASK_COLUMN_PERCENT_COMPLETE_TEXT,
+ TASK_COLUMN_COMPLETED,
+ TASK_COLUMN_COMPLETED_TIME,
+ TASK_COLUMN_PRIORITY,
+ TASK_COLUMN_COLOR,
+ N_TASK_COLUMNS
+};
+
+enum {
+ APPOINTMENT_TYPE_APPOINTMENT,
+ TASK_TYPE_TASK
+};
+
+static void calendar_window_pack_pim (CalendarWindow *calwin, GtkWidget *vbox);
+static char *format_time (ClockFormat format, time_t t, gint year, gint month, gint day);
+static void update_frame_visibility (GtkWidget *frame, GtkTreeModel *model);
+static GtkWidget *create_appointment_list (CalendarWindow *calwin, GtkWidget **tree_view, GtkWidget **scrolled_window);
+static GtkWidget *create_task_list (CalendarWindow *calwin, GtkWidget **tree_view, GtkWidget **scrolled_window);
+static void calendar_window_create_appointments_model (CalendarWindow *calwin);
+static void calendar_window_create_tasks_model (CalendarWindow *calwin);
+static void handle_appointments_changed (CalendarWindow *calwin);
+static void handle_tasks_changed (CalendarWindow *calwin);
+static void mark_day_on_calendar (CalendarClient *client, guint day, CalendarWindow *calwin);
+static gboolean is_for_filter (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
+static gboolean appointment_tooltip_query_cb (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data);
+static gboolean task_tooltip_query_cb (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data);
+static void appointment_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
+static void task_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
+static void task_completion_toggled_cb (GtkCellRendererToggle *cell, gchar *path_str, CalendarWindow *calwin);
+static gboolean task_entry_key_press_cb (GtkWidget *widget, GdkEventKey *event, CalendarWindow *calwin);
+static void task_entry_activate_cb (GtkEntry *entry, CalendarWindow *calwin);
+#endif /* HAVE_EDS */
+
static void calendar_mark_today(GtkCalendar *calendar)
{
time_t now;
@@ -93,10 +190,10 @@ static void calendar_mark_today(GtkCalendar *calendar)
gtk_calendar_get_date(calendar, &year, &month, &day);
time(&now);
localtime_r (&now, &tm1);
- if ((tm1.tm_mon == month) && (tm1.tm_year + 1900 == year)) {
- gtk_calendar_mark_day (GTK_CALENDAR (calendar), tm1.tm_mday);
+ if ((tm1.tm_mon == (int) month) && (tm1.tm_year + 1900 == (int) year)) {
+ gtk_calendar_mark_day (GTK_CALENDAR (calendar), (guint) tm1.tm_mday);
} else {
- gtk_calendar_unmark_day (GTK_CALENDAR (calendar), tm1.tm_mday);
+ gtk_calendar_unmark_day (GTK_CALENDAR (calendar), (guint) tm1.tm_mday);
}
}
@@ -110,7 +207,22 @@ static gboolean calendar_update(gpointer user_data)
static void calendar_month_changed_cb(GtkCalendar *calendar, gpointer user_data)
{
gtk_calendar_clear_marks(calendar);
- g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, calendar_update, user_data, NULL);
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, calendar_update, calendar, NULL);
+
+#ifdef HAVE_EDS
+ /* Update calendar client when date changes */
+ CalendarWindow *calwin = CALENDAR_WINDOW (user_data);
+ if (calwin->priv->client) {
+ guint year, month, day;
+ gtk_calendar_get_date (calendar, &year, &month, &day);
+ calendar_client_select_month (calwin->priv->client, month, year);
+ calendar_client_select_day (calwin->priv->client, day);
+
+ /* Refresh appointments and tasks for the new date */
+ handle_appointments_changed (calwin);
+ handle_tasks_changed (calwin);
+ }
+#endif
}
static GtkWidget *
@@ -131,12 +243,14 @@ calendar_window_create_calendar (CalendarWindow *calwin)
localtime_r (calwin->priv->current_time, &tm1);
gtk_calendar_select_month (GTK_CALENDAR (calendar),
- tm1.tm_mon, tm1.tm_year + 1900);
- gtk_calendar_select_day (GTK_CALENDAR (calendar), tm1.tm_mday);
+ (guint) tm1.tm_mon, (guint) (tm1.tm_year + 1900));
+ gtk_calendar_select_day (GTK_CALENDAR (calendar), (guint) tm1.tm_mday);
calendar_mark_today (GTK_CALENDAR(calendar));
- g_signal_connect(calendar, "month-changed",
- G_CALLBACK(calendar_month_changed_cb), calendar);
+ calwin->priv->calendar_month_changed_id = g_signal_connect(calendar, "month-changed",
+ G_CALLBACK(calendar_month_changed_cb), calwin);
+ calwin->priv->calendar_day_selected_id = g_signal_connect(calendar, "day-selected",
+ G_CALLBACK(calendar_month_changed_cb), calwin);
return calendar;
}
@@ -250,6 +364,52 @@ edit_locations (CalendarWindow *calwin)
g_signal_emit (calwin, signals[EDIT_LOCATIONS], 0);
}
+#ifdef HAVE_EDS
+static gboolean
+hide_task_entry_idle (gpointer user_data)
+{
+ CalendarWindow *calwin = CALENDAR_WINDOW (user_data);
+ if (calwin->priv->task_entry) {
+ gtk_widget_hide (calwin->priv->task_entry);
+ }
+ return FALSE; /* Remove the idle source */
+}
+
+static gboolean
+focus_task_entry_idle (gpointer user_data)
+{
+ CalendarWindow *calwin = CALENDAR_WINDOW (user_data);
+ if (calwin->priv->task_entry && gtk_widget_get_visible (calwin->priv->task_entry)) {
+ gtk_widget_grab_focus (calwin->priv->task_entry);
+ }
+ return FALSE; /* Remove the idle source */
+}
+
+static void
+add_task (CalendarWindow *calwin)
+{
+ if (calwin->priv->task_entry) {
+ gtk_widget_show (calwin->priv->task_entry);
+ gtk_widget_set_can_focus (calwin->priv->task_entry, TRUE);
+ gtk_widget_set_sensitive (calwin->priv->task_entry, TRUE);
+
+ /* Make sure parent window is active */
+ gtk_window_present (GTK_WINDOW (calwin));
+
+ /* Ensure widget is realized */
+ if (!gtk_widget_get_realized (calwin->priv->task_entry)) {
+ gtk_widget_realize (calwin->priv->task_entry);
+ }
+
+ /* Try to grab focus immediately */
+ gtk_widget_grab_focus (calwin->priv->task_entry);
+
+ /* Also try to grab focus in idle callback in case immediate focus fails */
+ g_idle_add (focus_task_entry_idle, calwin);
+ }
+}
+#endif
+
static void
calendar_window_pack_locations (CalendarWindow *calwin, GtkWidget *vbox)
{
@@ -287,12 +447,22 @@ calendar_window_fill (CalendarWindow *calwin)
calwin->priv->calendar = calendar_window_create_calendar (calwin);
gtk_widget_show (calwin->priv->calendar);
+#ifdef HAVE_EDS
+ /* Calendar client will be initialized later in calendar_window_pack_pim */
+#endif
+
if (!calwin->priv->invert_order) {
gtk_box_pack_start (GTK_BOX (vbox),
calwin->priv->calendar, TRUE, FALSE, 0);
+#ifdef HAVE_EDS
+ calendar_window_pack_pim (calwin, vbox);
+#endif
calendar_window_pack_locations (calwin, vbox);
} else {
calendar_window_pack_locations (calwin, vbox);
+#ifdef HAVE_EDS
+ calendar_window_pack_pim (calwin, vbox);
+#endif
gtk_box_pack_start (GTK_BOX (vbox),
calwin->priv->calendar, TRUE, FALSE, 0);
}
@@ -403,6 +573,35 @@ calendar_window_dispose (GObject *object)
calwin = CALENDAR_WINDOW (object);
+ g_clear_pointer (&calwin->priv->prefs_path, g_free);
+
+ /* Disconnect calendar signals */
+ if (calwin->priv->calendar && calwin->priv->calendar_month_changed_id > 0) {
+ g_signal_handler_disconnect (calwin->priv->calendar, calwin->priv->calendar_month_changed_id);
+ calwin->priv->calendar_month_changed_id = 0;
+ }
+ if (calwin->priv->calendar && calwin->priv->calendar_day_selected_id > 0) {
+ g_signal_handler_disconnect (calwin->priv->calendar, calwin->priv->calendar_day_selected_id);
+ calwin->priv->calendar_day_selected_id = 0;
+ }
+
+#ifdef HAVE_EDS
+ /* Disconnect client signals */
+ if (calwin->priv->client) {
+ if (calwin->priv->client_appointments_changed_id > 0) {
+ g_signal_handler_disconnect (calwin->priv->client, calwin->priv->client_appointments_changed_id);
+ calwin->priv->client_appointments_changed_id = 0;
+ }
+ if (calwin->priv->client_tasks_changed_id > 0) {
+ g_signal_handler_disconnect (calwin->priv->client, calwin->priv->client_tasks_changed_id);
+ calwin->priv->client_tasks_changed_id = 0;
+ }
+ g_signal_handlers_disconnect_by_data (calwin->priv->client, calwin);
+ g_object_unref (calwin->priv->client);
+ calwin->priv->client = NULL;
+ }
+#endif
+
if (calwin->priv->settings)
g_object_unref (calwin->priv->settings);
calwin->priv->settings = NULL;
@@ -473,6 +672,13 @@ calendar_window_init (CalendarWindow *calwin)
calwin->priv = calendar_window_get_instance_private (calwin);
+#ifdef HAVE_EDS
+ /* Initialize signal handler IDs */
+ calwin->priv->calendar_month_changed_id = 0;
+ calwin->priv->calendar_day_selected_id = 0;
+ calwin->priv->client_appointments_changed_id = 0;
+#endif
+
window = GTK_WINDOW (calwin);
gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_DOCK);
gtk_window_set_decorated (window, FALSE);
@@ -486,7 +692,8 @@ calendar_window_init (CalendarWindow *calwin)
GtkWidget *
calendar_window_new (time_t *static_current_time,
const char *prefs_path,
- gboolean invert_order)
+ gboolean invert_order,
+ GSettings *settings)
{
CalendarWindow *calwin;
@@ -497,6 +704,13 @@ calendar_window_new (time_t *static_current_time,
"prefs-path", prefs_path,
NULL);
+#ifdef HAVE_EDS
+ /* Store settings for calendar client initialization in init */
+ if (settings) {
+ calwin->priv->settings = g_object_ref (settings);
+ }
+#endif
+
return GTK_WIDGET (calwin);
}
@@ -504,6 +718,16 @@ void
calendar_window_refresh (CalendarWindow *calwin)
{
g_return_if_fail (CALENDAR_IS_WINDOW (calwin));
+
+#ifdef HAVE_EDS
+ if (calwin->priv->appointments_filter && calwin->priv->appointment_list)
+ gtk_tree_model_filter_refilter (calwin->priv->appointments_filter);
+
+ /* Update frame visibility based on model content */
+ if (calwin->priv->appointment_list && calwin->priv->appointments_filter)
+ update_frame_visibility (calwin->priv->appointment_list,
+ GTK_TREE_MODEL (calwin->priv->appointments_filter));
+#endif
}
gboolean
@@ -541,8 +765,6 @@ void
calendar_window_set_show_weeks (CalendarWindow *calwin,
gboolean show_weeks)
{
- GtkCalendarDisplayOptions options;
-
g_return_if_fail (CALENDAR_IS_WINDOW (calwin));
if (show_weeks == calwin->priv->show_weeks)
@@ -551,6 +773,8 @@ calendar_window_set_show_weeks (CalendarWindow *calwin,
calwin->priv->show_weeks = show_weeks;
if (calwin->priv->calendar) {
+ GtkCalendarDisplayOptions options;
+
options = gtk_calendar_get_display_options (GTK_CALENDAR (calwin->priv->calendar));
if (show_weeks)
@@ -571,7 +795,11 @@ calendar_window_get_time_format (CalendarWindow *calwin)
g_return_val_if_fail (CALENDAR_IS_WINDOW (calwin),
CLOCK_FORMAT_INVALID);
+#ifdef HAVE_EDS
+ return calwin->priv->time_format;
+#else
return CLOCK_FORMAT_INVALID;
+#endif
}
static time_t *
@@ -617,17 +845,901 @@ calendar_window_set_prefs_path (CalendarWindow *calwin,
!strcmp (calwin->priv->prefs_path, prefs_path))
return;
- if (calwin->priv->prefs_path)
- g_free (calwin->priv->prefs_path);
- calwin->priv->prefs_path = NULL;
-
+ g_free (calwin->priv->prefs_path);
if (prefs_path && prefs_path [0])
calwin->priv->prefs_path = g_strdup (prefs_path);
+ else
+ calwin->priv->prefs_path = NULL;
g_object_notify (G_OBJECT (calwin), "prefs-path");
- if (calwin->priv->settings)
- g_object_unref (calwin->priv->settings);
+ /* Only create new settings if we don't already have shared settings */
+ if (!calwin->priv->settings) {
+ calwin->priv->settings = g_settings_new_with_path (CLOCK_SCHEMA, calwin->priv->prefs_path);
+ }
+}
+
+#ifdef HAVE_EDS
+
+static char *
+format_time (ClockFormat format,
+ time_t t,
+ gint year,
+ gint month,
+ gint day)
+{
+ GDateTime *dt;
+ gchar *time;
+
+ if (!t)
+ return NULL;
+
+ /* Evolution timestamps are in UTC but represent local appointment times
+ * Since TZID lookup failed, treat UTC timestamp as local time directly */
+ dt = g_date_time_new_from_unix_utc (t);
+ time = NULL;
+
+ if (!dt)
+ return NULL;
+
+ /* Always show time since we're filtering by selected date */
+ if (format == CLOCK_FORMAT_12) {
+ /* Translators: This is a strftime format string.
+ * It is used to display the time in 12-hours format
+ * (eg, like in the US: 8:10 am). The %p expands to
+ * am/pm.
+ */
+ time = g_date_time_format (dt, _("%l:%M %p"));
+ } else {
+ /* Translators: This is a strftime format string.
+ * It is used to display the time in 24-hours format
+ * (eg, like in France: 20:10).
+ */
+ time = g_date_time_format (dt, _("%H:%M"));
+ }
+
+ g_date_time_unref (dt);
+ return time;
+}
+
+static void
+update_frame_visibility (GtkWidget *frame,
+ GtkTreeModel *model)
+{
+ GtkTreeIter iter;
+ gboolean model_empty;
+
+ if (!frame)
+ return;
+
+ model_empty = !gtk_tree_model_get_iter_first (model, &iter);
+
+ if (model_empty)
+ gtk_widget_hide (frame);
+ else
+ gtk_widget_show (frame);
+}
+
+
+static void
+calendar_window_create_appointments_model (CalendarWindow *calwin)
+{
+ calwin->priv->appointments_model = gtk_list_store_new (N_APPOINTMENT_COLUMNS,
+ G_TYPE_STRING, /* APPOINTMENT_COLUMN_UID */
+ G_TYPE_INT, /* APPOINTMENT_COLUMN_TYPE */
+ G_TYPE_STRING, /* APPOINTMENT_COLUMN_SUMMARY */
+ G_TYPE_STRING, /* APPOINTMENT_COLUMN_DESCRIPTION */
+ G_TYPE_ULONG, /* APPOINTMENT_COLUMN_START_TIME */
+ G_TYPE_STRING, /* APPOINTMENT_COLUMN_START_TEXT */
+ G_TYPE_ULONG, /* APPOINTMENT_COLUMN_END_TIME */
+ G_TYPE_BOOLEAN, /* APPOINTMENT_COLUMN_ALL_DAY */
+ G_TYPE_STRING); /* APPOINTMENT_COLUMN_COLOR */
+
+ calwin->priv->appointments_filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (calwin->priv->appointments_model), NULL));
+ gtk_tree_model_filter_set_visible_func (calwin->priv->appointments_filter,
+ (GtkTreeModelFilterVisibleFunc) is_for_filter,
+ GINT_TO_POINTER (APPOINTMENT_TYPE_APPOINTMENT),
+ NULL);
+}
+
+static void
+calendar_window_create_tasks_model (CalendarWindow *calwin)
+{
+ calwin->priv->tasks_model = gtk_list_store_new (N_TASK_COLUMNS,
+ G_TYPE_STRING, /* TASK_COLUMN_UID */
+ G_TYPE_INT, /* TASK_COLUMN_TYPE */
+ G_TYPE_STRING, /* TASK_COLUMN_SUMMARY */
+ G_TYPE_STRING, /* TASK_COLUMN_DESCRIPTION */
+ G_TYPE_ULONG, /* TASK_COLUMN_START_TIME */
+ G_TYPE_STRING, /* TASK_COLUMN_START_TEXT */
+ G_TYPE_ULONG, /* TASK_COLUMN_DUE_TIME */
+ G_TYPE_STRING, /* TASK_COLUMN_DUE_TEXT */
+ G_TYPE_INT, /* TASK_COLUMN_PERCENT_COMPLETE */
+ G_TYPE_STRING, /* TASK_COLUMN_PERCENT_COMPLETE_TEXT */
+ G_TYPE_BOOLEAN, /* TASK_COLUMN_COMPLETED */
+ G_TYPE_ULONG, /* TASK_COLUMN_COMPLETED_TIME */
+ G_TYPE_INT, /* TASK_COLUMN_PRIORITY */
+ G_TYPE_STRING); /* TASK_COLUMN_COLOR */
+
+ calwin->priv->tasks_filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (calwin->priv->tasks_model), NULL));
+ gtk_tree_model_filter_set_visible_func (calwin->priv->tasks_filter,
+ (GtkTreeModelFilterVisibleFunc) is_for_filter,
+ GINT_TO_POINTER (TASK_TYPE_TASK),
+ NULL);
+}
+
+static GtkWidget *
+create_hig_calendar_frame (CalendarWindow *calwin,
+ const char *title,
+ const char *button_label,
+ const char *key,
+ GCallback callback)
+{
+ return create_hig_frame (calwin, title, button_label, key, callback);
+}
+
+
+static GtkWidget *
+create_appointment_list (CalendarWindow *calwin,
+ GtkWidget **tree_view,
+ GtkWidget **scrolled_window)
+{
+ GtkWidget *frame;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell;
+
+ frame = create_hig_calendar_frame (calwin, _("Appointments"), NULL,
+ KEY_SHOW_CALENDAR_EVENTS, NULL);
+
+ list = gtk_tree_view_new ();
+ gtk_tree_view_set_model (GTK_TREE_VIEW (list),
+ GTK_TREE_MODEL (calwin->priv->appointments_filter));
+
+ column = gtk_tree_view_column_new ();
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "text", APPOINTMENT_COLUMN_START_TEXT,
+ NULL);
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell,
+ "wrap-mode", PANGO_WRAP_WORD_CHAR,
+ "wrap-width", 200,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "text", APPOINTMENT_COLUMN_SUMMARY,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
+
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
+ gtk_widget_set_has_tooltip (list, TRUE);
+ g_signal_connect (list, "query-tooltip", G_CALLBACK (appointment_tooltip_query_cb), calwin);
+ g_signal_connect (list, "row-activated", G_CALLBACK (appointment_row_activated_cb), calwin);
+
+ *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (*scrolled_window),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (*scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_widget_set_size_request (*scrolled_window, -1, 150);
+ gtk_container_add (GTK_CONTAINER (*scrolled_window), list);
+
+ gtk_container_add (GTK_CONTAINER (frame), *scrolled_window);
+
+ /* Ensure the scrolled window and tree view are visible */
+ gtk_widget_show (*scrolled_window);
+ gtk_widget_show (list);
+
+ /* Appointment list widgets created */
+
+ *tree_view = list;
+ return frame;
+}
+
+static GtkWidget *
+create_task_list (CalendarWindow *calwin,
+ GtkWidget **tree_view,
+ GtkWidget **scrolled_window)
+{
+ GtkWidget *frame;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell;
+
+ frame = create_hig_calendar_frame (calwin, _("Tasks"), _("Add"),
+ KEY_SHOW_TASKS, G_CALLBACK (add_task));
+
+ list = gtk_tree_view_new ();
+ gtk_tree_view_set_model (GTK_TREE_VIEW (list),
+ GTK_TREE_MODEL (calwin->priv->tasks_filter));
+
+ column = gtk_tree_view_column_new ();
+
+ /* Completion checkbox */
+ cell = gtk_cell_renderer_toggle_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "active", TASK_COLUMN_COMPLETED,
+ NULL);
+ g_signal_connect (cell, "toggled", G_CALLBACK (task_completion_toggled_cb), calwin);
+
+
+ /* Task summary */
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell,
+ "wrap-mode", PANGO_WRAP_WORD_CHAR,
+ "wrap-width", 200,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "text", TASK_COLUMN_SUMMARY,
+ "strikethrough", TASK_COLUMN_COMPLETED,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
+
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
+ gtk_widget_set_has_tooltip (list, TRUE);
+ g_signal_connect (list, "query-tooltip", G_CALLBACK (task_tooltip_query_cb), calwin);
+ g_signal_connect (list, "row-activated", G_CALLBACK (task_row_activated_cb), calwin);
+
+ *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (*scrolled_window),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (*scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_widget_set_size_request (*scrolled_window, -1, 150);
+ gtk_container_add (GTK_CONTAINER (*scrolled_window), list);
+
+ gtk_container_add (GTK_CONTAINER (frame), *scrolled_window);
+
+ /* Create task entry field */
+ calwin->priv->task_entry = gtk_entry_new ();
+ gtk_entry_set_placeholder_text (GTK_ENTRY (calwin->priv->task_entry), _("Enter task description..."));
+ gtk_widget_set_can_focus (calwin->priv->task_entry, TRUE);
+ gtk_widget_set_sensitive (calwin->priv->task_entry, TRUE);
+ g_signal_connect (calwin->priv->task_entry, "key-press-event", G_CALLBACK (task_entry_key_press_cb), calwin);
+ g_signal_connect (calwin->priv->task_entry, "activate", G_CALLBACK (task_entry_activate_cb), calwin);
+ gtk_container_add (GTK_CONTAINER (frame), calwin->priv->task_entry);
+
+ /* Ensure the scrolled window and tree view are visible */
+ gtk_widget_show (*scrolled_window);
+ gtk_widget_show (list);
+
+ /* Hide task entry after all show operations are complete */
+ g_idle_add (hide_task_entry_idle, calwin);
+
+ *tree_view = list;
+ return frame;
+}
+
+static void
+calendar_window_pack_pim (CalendarWindow *calwin,
+ GtkWidget *vbox)
+{
+ GtkWidget *list;
+ GtkWidget *tree_view;
+ GtkWidget *scrolled_window;
+ gboolean show_calendar_events;
+ gboolean show_tasks;
+
+ /* Check if calendar events should be shown */
+ show_calendar_events = g_settings_get_boolean (calwin->priv->settings, KEY_SHOW_CALENDAR_EVENTS);
+ show_tasks = g_settings_get_boolean (calwin->priv->settings, KEY_SHOW_TASKS);
+
+ if (!show_calendar_events && !show_tasks) {
+ return;
+ }
+
+ /* Initialize calendar client if not already done */
+ if (!calwin->priv->client && calwin->priv->settings) {
+ calwin->priv->client = calendar_client_new (calwin->priv->settings);
+
+ if (calwin->priv->client) {
+ if (show_calendar_events) {
+ calwin->priv->client_appointments_changed_id = g_signal_connect_swapped (calwin->priv->client,
+ "appointments-changed",
+ G_CALLBACK (handle_appointments_changed),
+ calwin);
+ }
+ if (show_tasks) {
+ calwin->priv->client_tasks_changed_id = g_signal_connect_swapped (calwin->priv->client,
+ "tasks-changed",
+ G_CALLBACK (handle_tasks_changed),
+ calwin);
+ }
+ }
+ }
+
+ if (!calwin->priv->client) {
+ g_warning ("Failed to create calendar client in calendar_window_pack_pim");
+ return;
+ }
+
+ /* Create and pack appointments list if enabled */
+ if (show_calendar_events) {
+ calendar_window_create_appointments_model (calwin);
+ list = create_appointment_list (calwin, &tree_view, &scrolled_window);
+ update_frame_visibility (list,
+ GTK_TREE_MODEL (calwin->priv->appointments_filter));
+ calwin->priv->appointment_list = list;
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ calwin->priv->appointment_list,
+ TRUE, TRUE, 0);
+ }
+
+ /* Create and pack tasks list if enabled */
+ if (show_tasks) {
+ calendar_window_create_tasks_model (calwin);
+ list = create_task_list (calwin, &tree_view, &scrolled_window);
+ update_frame_visibility (list,
+ GTK_TREE_MODEL (calwin->priv->tasks_filter));
+ calwin->priv->task_list = list;
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ calwin->priv->task_list,
+ TRUE, TRUE, 0);
+ }
+
+ /* Initialize calendar client with current date now that client is ready */
+ if (calwin->priv->client && calwin->priv->calendar) {
+ guint year, month, day;
+ gtk_calendar_get_date (GTK_CALENDAR (calwin->priv->calendar), &year, &month, &day);
+ /* Set a flag to indicate we're initializing to prevent redundant calls */
+ g_object_set_data (G_OBJECT (calwin), "initializing", GINT_TO_POINTER (1));
+
+ calendar_client_select_month (calwin->priv->client, month, year);
+ calendar_client_select_day (calwin->priv->client, day);
+
+ /* Clear the initialization flag and trigger initial load */
+ g_object_set_data (G_OBJECT (calwin), "initializing", GINT_TO_POINTER (0));
+
+ /* Now trigger the initial appointments and tasks load */
+ if (show_calendar_events) {
+ handle_appointments_changed (calwin);
+ }
+ if (show_tasks) {
+ handle_tasks_changed (calwin);
+ }
+ }
+}
+
+static gboolean
+is_for_filter (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gint type;
+ gint expected_type = GPOINTER_TO_INT (data);
+
+ /* Check if this is a task model or appointment model */
+ if (expected_type == TASK_TYPE_TASK) {
+ gtk_tree_model_get (model, iter, TASK_COLUMN_TYPE, &type, -1);
+ } else {
+ gtk_tree_model_get (model, iter, APPOINTMENT_COLUMN_TYPE, &type, -1);
+ }
+ return type == expected_type;
+}
+
+static void
+mark_day_on_calendar (CalendarClient *client,
+ guint day,
+ CalendarWindow *calwin)
+{
+ gtk_calendar_mark_day (GTK_CALENDAR (calwin->priv->calendar), day);
+}
+
+
+
+static gint
+compare_appointments_by_time (const CalendarAppointment *a, const CalendarAppointment *b)
+{
+ /* Sort by start time - earlier appointments first */
+ if (a->start_time < b->start_time)
+ return -1;
+ else if (a->start_time > b->start_time)
+ return 1;
+ else
+ return 0;
+}
+
+static gint
+compare_tasks_by_due_time (const CalendarTask *a, const CalendarTask *b)
+{
+ /* Sort by due time - earlier due dates first, then by priority */
+ if (a->due_time && b->due_time) {
+ if (a->due_time < b->due_time)
+ return -1;
+ else if (a->due_time > b->due_time)
+ return 1;
+ } else if (a->due_time && !b->due_time) {
+ return -1; /* Tasks with due dates come first */
+ } else if (!a->due_time && b->due_time) {
+ return 1;
+ }
+
+ /* If due times are equal or both missing, sort by priority (higher priority first) */
+ if (a->priority > b->priority)
+ return -1;
+ else if (a->priority < b->priority)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+handle_appointments_changed (CalendarWindow *calwin)
+{
+ GSList *events, *l;
+ guint year, month, day;
+
+ /* Skip redundant calls during initialization */
+ if (g_object_get_data (G_OBJECT (calwin), "initializing")) {
+ return;
+ }
+
+ if (calwin->priv->calendar) {
+ gtk_calendar_clear_marks (GTK_CALENDAR (calwin->priv->calendar));
+
+ calendar_client_foreach_appointment_day (calwin->priv->client,
+ (CalendarDayIter) mark_day_on_calendar,
+ calwin);
+ }
+
+ gtk_list_store_clear (calwin->priv->appointments_model);
+
+ calendar_client_get_date (calwin->priv->client, &year, &month, &day);
+
+ events = calendar_client_get_events (calwin->priv->client,
+ CALENDAR_EVENT_APPOINTMENT);
+
+ /* Sort appointments by start time for better display order */
+ events = g_slist_sort (events, (GCompareFunc) compare_appointments_by_time);
+
+ /* Found appointments for current date */
+ for (l = events; l; l = l->next) {
+ CalendarAppointment *appointment = l->data;
+ GtkTreeIter iter;
+ char *start_text;
+
+ g_assert (CALENDAR_EVENT (appointment)->type == CALENDAR_EVENT_APPOINTMENT);
+
+ if (appointment->is_all_day)
+ start_text = g_strdup (_("All Day"));
+ else
+ start_text = format_time (calendar_window_get_time_format (calwin),
+ appointment->start_time,
+ year, month, day);
+
+ gtk_list_store_append (calwin->priv->appointments_model, &iter);
+ /* Appointment added to model */
+ gtk_list_store_set (calwin->priv->appointments_model, &iter,
+ APPOINTMENT_COLUMN_UID, appointment->uid,
+ APPOINTMENT_COLUMN_TYPE, APPOINTMENT_TYPE_APPOINTMENT,
+ APPOINTMENT_COLUMN_SUMMARY, appointment->summary,
+ APPOINTMENT_COLUMN_DESCRIPTION, appointment->description,
+ APPOINTMENT_COLUMN_START_TIME, (gint64)appointment->start_time,
+ APPOINTMENT_COLUMN_START_TEXT, start_text,
+ APPOINTMENT_COLUMN_END_TIME, (gint64)appointment->end_time,
+ APPOINTMENT_COLUMN_ALL_DAY, appointment->is_all_day,
+ APPOINTMENT_COLUMN_COLOR, appointment->color_string,
+ -1);
+
+ g_free (start_text);
+ }
+
+ /* Refresh filter before checking visibility */
+ if (calwin->priv->appointments_filter)
+ gtk_tree_model_filter_refilter (calwin->priv->appointments_filter);
+
+ update_frame_visibility (calwin->priv->appointment_list,
+ GTK_TREE_MODEL (calwin->priv->appointments_filter));
+}
+
+static void
+handle_tasks_changed (CalendarWindow *calwin)
+{
+ GSList *events, *l;
+ guint year, month, day;
+
+ /* Skip redundant calls during initialization */
+ if (g_object_get_data (G_OBJECT (calwin), "initializing")) {
+ return;
+ }
+
+ if (!calwin->priv->tasks_model) {
+ return;
+ }
+
+ gtk_list_store_clear (calwin->priv->tasks_model);
+
+ calendar_client_get_date (calwin->priv->client, &year, &month, &day);
+
+ events = calendar_client_get_events (calwin->priv->client,
+ CALENDAR_EVENT_TASK);
+
+ /* Sort tasks by due time for better display order */
+ events = g_slist_sort (events, (GCompareFunc) compare_tasks_by_due_time);
+
+ /* Found tasks for current date */
+ for (l = events; l; l = l->next) {
+ CalendarTask *task = (CalendarTask *) l->data;
+ GtkTreeIter iter;
+ char *start_text = NULL;
+ char *due_text = NULL;
+ char *percent_complete_text = NULL;
+ gboolean completed;
+
+ g_assert (CALENDAR_EVENT (task)->type == CALENDAR_EVENT_TASK);
+
+ if (task->start_time) {
+ start_text = format_time (calendar_window_get_time_format (calwin),
+ task->start_time,
+ year, month, day);
+ } else {
+ start_text = g_strdup ("");
+ }
+
+ if (task->due_time) {
+ due_text = format_time (calendar_window_get_time_format (calwin),
+ task->due_time,
+ year, month, day);
+ } else {
+ due_text = g_strdup ("");
+ }
+
+ /* Format percent complete as text */
+ if (task->percent_complete > 0) {
+ percent_complete_text = g_strdup_printf ("%d%%", task->percent_complete);
+ } else {
+ percent_complete_text = g_strdup ("");
+ }
+
+ completed = (task->percent_complete == 100);
+
+ gtk_list_store_append (calwin->priv->tasks_model, &iter);
+ gtk_list_store_set (calwin->priv->tasks_model, &iter,
+ TASK_COLUMN_UID, task->uid,
+ TASK_COLUMN_TYPE, TASK_TYPE_TASK,
+ TASK_COLUMN_SUMMARY, task->summary,
+ TASK_COLUMN_DESCRIPTION, task->description,
+ TASK_COLUMN_START_TIME, (gint64)task->start_time,
+ TASK_COLUMN_START_TEXT, start_text,
+ TASK_COLUMN_DUE_TIME, (gint64)task->due_time,
+ TASK_COLUMN_DUE_TEXT, due_text,
+ TASK_COLUMN_PERCENT_COMPLETE, task->percent_complete,
+ TASK_COLUMN_PERCENT_COMPLETE_TEXT, percent_complete_text,
+ TASK_COLUMN_COMPLETED, completed,
+ TASK_COLUMN_COMPLETED_TIME, (gint64)task->completed_time,
+ TASK_COLUMN_PRIORITY, task->priority,
+ TASK_COLUMN_COLOR, task->color_string,
+ -1);
+
+ g_free (start_text);
+ g_free (due_text);
+ g_free (percent_complete_text);
+ }
+
+ /* Refresh filter before checking visibility */
+ if (calwin->priv->tasks_filter)
+ gtk_tree_model_filter_refilter (calwin->priv->tasks_filter);
+
+ update_frame_visibility (calwin->priv->task_list,
+ GTK_TREE_MODEL (calwin->priv->tasks_filter));
+}
+
+static void
+appointment_row_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data)
+{
+ GAppInfo *app_info;
+ GError *error = NULL;
+
+ /* Launch Evolution calendar */
+ app_info = g_app_info_get_default_for_type ("text/calendar", FALSE);
+ if (!app_info) {
+ /* Try launching evolution directly if no calendar app is set */
+ app_info = g_app_info_create_from_commandline ("evolution -c calendar",
+ "Evolution Calendar",
+ G_APP_INFO_CREATE_NONE,
+ &error);
+ }
+
+ if (app_info) {
+ if (!g_app_info_launch (app_info, NULL, NULL, &error)) {
+ g_warning ("Failed to launch calendar application: %s", error->message);
+ g_error_free (error);
+ }
+ g_object_unref (app_info);
+ } else {
+ g_warning ("No calendar application found");
+ if (error) {
+ g_warning ("Error: %s", error->message);
+ g_error_free (error);
+ }
+ }
+}
- calwin->priv->settings = g_settings_new_with_path (CLOCK_SCHEMA, calwin->priv->prefs_path);
+static gboolean
+appointment_tooltip_query_cb (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ gpointer user_data)
+{
+ GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gchar *summary, *description, *start_text;
+ gchar *tooltip_text, *end_text;
+ gboolean all_day;
+ gulong start_time, end_time;
+
+ if (!gtk_tree_view_get_tooltip_context (tree_view, &x, &y, keyboard_mode,
+ &model, &path, &iter)) {
+ return FALSE;
+ }
+
+ gtk_tree_model_get (model, &iter,
+ APPOINTMENT_COLUMN_SUMMARY, &summary,
+ APPOINTMENT_COLUMN_DESCRIPTION, &description,
+ APPOINTMENT_COLUMN_START_TEXT, &start_text,
+ APPOINTMENT_COLUMN_START_TIME, &start_time,
+ APPOINTMENT_COLUMN_END_TIME, &end_time,
+ APPOINTMENT_COLUMN_ALL_DAY, &all_day,
+ -1);
+
+ if (!summary) {
+ gtk_tree_path_free (path);
+ return FALSE;
+ }
+
+ /* Format end time */
+ if (!all_day && end_time > 0) {
+ GDateTime *end_dt = g_date_time_new_from_unix_utc (end_time);
+ if (end_dt) {
+ end_text = g_date_time_format (end_dt, "%H:%M");
+ g_date_time_unref (end_dt);
+ } else {
+ end_text = NULL;
+ }
+ } else {
+ end_text = NULL;
+ }
+
+ if (description && strlen (description) > 0) {
+ if (all_day) {
+ tooltip_text = g_markup_printf_escaped ("<b>%s</b>\n%s\nAll Day", summary, description);
+ } else if (end_text) {
+ tooltip_text = g_markup_printf_escaped ("<b>%s</b>\n%s\n%s - %s", summary, description, start_text ? start_text : "", end_text);
+ } else {
+ tooltip_text = g_markup_printf_escaped ("<b>%s</b>\n%s\n%s", summary, description, start_text ? start_text : "");
+ }
+ } else {
+ if (all_day) {
+ tooltip_text = g_markup_printf_escaped ("<b>%s</b>\nAll Day", summary);
+ } else if (end_text) {
+ tooltip_text = g_markup_printf_escaped ("<b>%s</b>\n%s - %s", summary, start_text ? start_text : "", end_text);
+ } else {
+ tooltip_text = g_markup_printf_escaped ("<b>%s</b>\n%s", summary, start_text ? start_text : "");
+ }
+ }
+
+ gtk_tooltip_set_markup (tooltip, tooltip_text);
+ gtk_tree_view_set_tooltip_row (tree_view, tooltip, path);
+
+ g_free (summary);
+ g_free (description);
+ g_free (start_text);
+ g_free (end_text);
+ g_free (tooltip_text);
+ gtk_tree_path_free (path);
+
+ return TRUE;
+}
+
+static gboolean
+task_tooltip_query_cb (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ gpointer user_data)
+{
+ GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gchar *summary, *description, *start_text, *due_text;
+ gchar *tooltip_text;
+ gint percent_complete, priority;
+
+ if (!gtk_tree_view_get_tooltip_context (tree_view, &x, &y, keyboard_mode,
+ &model, &path, &iter)) {
+ return FALSE;
+ }
+
+ gtk_tree_model_get (model, &iter,
+ TASK_COLUMN_SUMMARY, &summary,
+ TASK_COLUMN_DESCRIPTION, &description,
+ TASK_COLUMN_START_TEXT, &start_text,
+ TASK_COLUMN_DUE_TEXT, &due_text,
+ TASK_COLUMN_PERCENT_COMPLETE, &percent_complete,
+ TASK_COLUMN_PRIORITY, &priority,
+ -1);
+
+ if (!summary) {
+ gtk_tree_path_free (path);
+ return FALSE;
+ }
+
+ /* Build tooltip with task information */
+ if (description && strlen (description) > 0) {
+ tooltip_text = g_markup_printf_escaped ("<b>%s</b>\n%s\nProgress: %d%%",
+ summary, description, percent_complete);
+ } else {
+ tooltip_text = g_markup_printf_escaped ("<b>%s</b>\nProgress: %d%%",
+ summary, percent_complete);
+ }
+
+ /* Add due date if available */
+ if (due_text && strlen (due_text) > 0) {
+ gchar *temp = tooltip_text;
+ tooltip_text = g_markup_printf_escaped ("%s\nDue: %s", temp, due_text);
+ g_free (temp);
+ }
+
+ gtk_tooltip_set_markup (tooltip, tooltip_text);
+ gtk_tree_view_set_tooltip_row (tree_view, tooltip, path);
+
+ g_free (summary);
+ g_free (description);
+ g_free (start_text);
+ g_free (due_text);
+ g_free (tooltip_text);
+ gtk_tree_path_free (path);
+
+ return TRUE;
+}
+
+static void
+task_row_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data)
+{
+ GAppInfo *app_info;
+ GError *error = NULL;
+
+ /* Launch Evolution tasks */
+ app_info = g_app_info_get_default_for_uri_scheme ("task");
+ if (!app_info) {
+ /* Fallback to Evolution tasks directly */
+ app_info = g_app_info_create_from_commandline ("evolution --component=tasks",
+ "Evolution Tasks",
+ G_APP_INFO_CREATE_NONE,
+ &error);
+ }
+
+ if (app_info) {
+ g_app_info_launch (app_info, NULL, NULL, &error);
+ g_object_unref (app_info);
+ }
+
+ if (error) {
+ g_warning ("Failed to launch Evolution tasks: %s", error->message);
+ g_error_free (error);
+ }
}
+
+static void
+task_completion_toggled_cb (GtkCellRendererToggle *cell,
+ gchar *path_str,
+ CalendarWindow *calwin)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gchar *task_uid;
+ gboolean completed;
+ gint percent_complete;
+
+ path = gtk_tree_path_new_from_string (path_str);
+
+ if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (calwin->priv->tasks_filter), &iter, path)) {
+ gtk_tree_path_free (path);
+ return;
+ }
+
+ gtk_tree_model_get (GTK_TREE_MODEL (calwin->priv->tasks_filter), &iter,
+ TASK_COLUMN_UID, &task_uid,
+ TASK_COLUMN_COMPLETED, &completed,
+ TASK_COLUMN_PERCENT_COMPLETE, &percent_complete,
+ -1);
+
+ /* Toggle completion state */
+ completed = !completed;
+ percent_complete = completed ? 100 : 0;
+
+ /* Update the Evolution task */
+ if (calwin->priv->client && task_uid) {
+ calendar_client_set_task_completed (calwin->priv->client,
+ task_uid,
+ completed,
+ percent_complete);
+ }
+
+ g_free (task_uid);
+ gtk_tree_path_free (path);
+}
+
+static gboolean
+task_entry_key_press_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ CalendarWindow *calwin)
+{
+ const gchar *text;
+
+ if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
+ /* Get the text from the entry */
+ text = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ /* Create task if text is not empty */
+ if (text && *text != '\0') {
+ if (calwin->priv->client) {
+ gboolean success = calendar_client_create_task (calwin->priv->client, text);
+ if (success) {
+ /* Clear the entry and hide it */
+ gtk_entry_set_text (GTK_ENTRY (widget), "");
+ gtk_widget_hide (widget);
+ } else {
+ g_warning ("Failed to create task");
+ }
+ }
+ }
+ return TRUE; /* Event handled */
+ } else if (event->keyval == GDK_KEY_Escape) {
+ /* Clear the entry and hide it */
+ gtk_entry_set_text (GTK_ENTRY (widget), "");
+ gtk_widget_hide (widget);
+ return TRUE; /* Event handled */
+ }
+
+ return FALSE; /* Let other handlers process the event */
+}
+
+static void
+task_entry_activate_cb (GtkEntry *entry,
+ CalendarWindow *calwin)
+{
+ const gchar *text;
+
+ /* Get the text from the entry */
+ text = gtk_entry_get_text (entry);
+
+ /* Create task if text is not empty */
+ if (text && *text != '\0') {
+ if (calwin->priv->client) {
+ gboolean success = calendar_client_create_task (calwin->priv->client, text);
+ if (success) {
+ /* Clear the entry and hide it */
+ gtk_entry_set_text (entry, "");
+ gtk_widget_hide (GTK_WIDGET (entry));
+ } else {
+ g_warning ("Failed to create task");
+ }
+ }
+ }
+}
+
+#endif /* HAVE_EDS */
diff --git a/applets/clock/calendar-window.h b/applets/clock/calendar-window.h
index d4ef49dd..e24fdfc3 100644
--- a/applets/clock/calendar-window.h
+++ b/applets/clock/calendar-window.h
@@ -61,7 +61,8 @@ struct _CalendarWindowClass {
GType calendar_window_get_type (void) G_GNUC_CONST;
GtkWidget *calendar_window_new (time_t *static_current_time,
const char *prefs_dir,
- gboolean invert_order);
+ gboolean invert_order,
+ GSettings *settings);
void calendar_window_refresh (CalendarWindow *calwin);
diff --git a/applets/clock/clock-face.c b/applets/clock/clock-face.c
index a30afd13..7340c8a8 100644
--- a/applets/clock/clock-face.c
+++ b/applets/clock/clock-face.c
@@ -369,20 +369,9 @@ clock_face_finalize (GObject *obj)
{
ClockFacePrivate *priv = clock_face_get_instance_private (CLOCK_FACE(obj));
- if (priv->location) {
- g_object_unref (priv->location);
- priv->location = NULL;
- }
-
- if (priv->face_pixbuf) {
- g_object_unref (priv->face_pixbuf);
- priv->face_pixbuf = NULL;
- }
-
- if (priv->size_widget) {
- g_object_unref (priv->size_widget);
- priv->size_widget = NULL;
- }
+ g_clear_object (&priv->location);
+ g_clear_object (&priv->face_pixbuf);
+ g_clear_object (&priv->size_widget);
G_OBJECT_CLASS (clock_face_parent_class)->finalize (obj);
diff --git a/applets/clock/clock-face.h b/applets/clock/clock-face.h
index 3c665c63..00106ca4 100644
--- a/applets/clock/clock-face.h
+++ b/applets/clock/clock-face.h
@@ -56,7 +56,6 @@ GtkWidget *clock_face_new_with_location (ClockFaceSize size,
GtkWidget *size_widget);
gboolean clock_face_refresh (ClockFace *this);
-
#ifdef __cplusplus
}
#endif
diff --git a/applets/clock/clock-location-tile.c b/applets/clock/clock-location-tile.c
index 55bc749a..385d0da1 100644
--- a/applets/clock/clock-location-tile.c
+++ b/applets/clock/clock-location-tile.c
@@ -82,7 +82,7 @@ clock_location_tile_new (ClockLocation *loc,
g_signal_connect (priv->weather_icon, "query-tooltip",
G_CALLBACK (weather_tooltip), this);
- priv->location_weather_updated_id = g_signal_connect (G_OBJECT (loc), "weather-updated",
+ priv->location_weather_updated_id = g_signal_connect (loc, "weather-updated",
G_CALLBACK (update_weather_icon), this);
return this;
@@ -140,22 +140,22 @@ clock_location_tile_finalize (GObject *g_obj)
priv = clock_location_tile_get_instance_private (this);
if (priv->location) {
- g_signal_handler_disconnect (priv->location, priv->location_weather_updated_id);
- priv->location_weather_updated_id = 0;
-
- g_object_unref (priv->location);
- priv->location = NULL;
- }
+#if GLIB_CHECK_VERSION(2,62,0)
+ g_clear_signal_handler (&priv->location_weather_updated_id,
+ priv->location);
+#else
+ if (priv->location_weather_updated_id != 0) {
+ g_signal_handler_disconnect (priv->location,
+ priv->location_weather_updated_id);
+ priv->location_weather_updated_id = 0;
+ }
+#endif
- if (priv->button_group) {
- g_object_unref (priv->button_group);
- priv->button_group = NULL;
+ g_clear_object (&priv->location);
}
- if (priv->current_group) {
- g_object_unref (priv->current_group);
- priv->current_group = NULL;
- }
+ g_clear_object (&priv->button_group);
+ g_clear_object (&priv->current_group);
G_OBJECT_CLASS (clock_location_tile_parent_class)->finalize (g_obj);
}
@@ -434,7 +434,7 @@ format_time (struct tm *now,
* weekday differs from the weekday at the location
* (the %A expands to the weekday). The %p expands to
* am/pm. */
- format = _("%l:%M <small>%p (%A)</small>");
+ format = _("%_I:%M <small>%p (%A)</small>");
}
else {
/* Translators: This is a strftime format string.
@@ -451,7 +451,7 @@ format_time (struct tm *now,
* It is used to display the time in 12-hours format
* (eg, like in the US: 8:10 am). The %p expands to
* am/pm. */
- format = _("%l:%M <small>%p</small>");
+ format = _("%_I:%M <small>%p</small>");
}
else {
/* Translators: This is a strftime format string.
@@ -497,7 +497,7 @@ convert_time_to_str (time_t now, ClockFormat clock_format)
* It is used to display the time in 12-hours format (eg, like
* in the US: 8:10 am). The %p expands to am/pm.
*/
- format = _("%l:%M %p");
+ format = _("%_I:%M %p");
}
else {
/* Translators: This is a strftime format string.
diff --git a/applets/clock/clock-location.c b/applets/clock/clock-location.c
index e5b84bf1..637d834d 100644
--- a/applets/clock/clock-location.c
+++ b/applets/clock/clock-location.c
@@ -210,17 +210,14 @@ clock_location_finalize (GObject *g_obj)
G_CALLBACK (network_changed),
CLOCK_LOCATION (g_obj));
- g_free (priv->name);
- g_free (priv->city);
+ g_clear_pointer (&priv->name, g_free);
+ g_clear_pointer (&priv->city, g_free);
- if (priv->systz) {
- g_object_unref (priv->systz);
- priv->systz = NULL;
- }
+ g_clear_object (&priv->systz);
- g_free (priv->timezone);
- g_free (priv->tzname);
- g_free (priv->weather_code);
+ g_clear_pointer (&priv->timezone, g_free);
+ g_clear_pointer (&priv->tzname, g_free);
+ g_clear_pointer (&priv->weather_code, g_free);
if (priv->weather_info) {
weather_info_free (priv->weather_info);
@@ -437,7 +434,6 @@ clock_location_is_current (ClockLocation *loc)
return FALSE;
}
-
glong
clock_location_get_offset (ClockLocation *loc)
{
diff --git a/applets/clock/clock-map.c b/applets/clock/clock-map.c
index fc096c60..97441c62 100644
--- a/applets/clock/clock-map.c
+++ b/applets/clock/clock-map.c
@@ -150,32 +150,15 @@ clock_map_finalize (GObject *g_obj)
priv->highlight_timeout_id = 0;
}
- if (priv->stock_map_pixbuf) {
- g_object_unref (priv->stock_map_pixbuf);
- priv->stock_map_pixbuf = NULL;
- }
+ g_clear_object (&priv->stock_map_pixbuf);
for (i = 0; i < MARKER_NB; i++) {
- if (priv->location_marker_pixbuf[i]) {
- g_object_unref (priv->location_marker_pixbuf[i]);
- priv->location_marker_pixbuf[i] = NULL;
- }
+ g_clear_object (&priv->location_marker_pixbuf[i]);
}
- if (priv->location_map_pixbuf) {
- g_object_unref (priv->location_map_pixbuf);
- priv->location_map_pixbuf = NULL;
- }
-
- if (priv->shadow_pixbuf) {
- g_object_unref (priv->shadow_pixbuf);
- priv->shadow_pixbuf = NULL;
- }
-
- if (priv->shadow_map_pixbuf) {
- g_object_unref (priv->shadow_map_pixbuf);
- priv->shadow_map_pixbuf = NULL;
- }
+ g_clear_object (&priv->location_map_pixbuf);
+ g_clear_object (&priv->shadow_pixbuf);
+ g_clear_object (&priv->shadow_map_pixbuf);
G_OBJECT_CLASS (clock_map_parent_class)->finalize (g_obj);
}
diff --git a/applets/clock/clock-map.h b/applets/clock/clock-map.h
index 0df4a416..d400fa9b 100644
--- a/applets/clock/clock-map.h
+++ b/applets/clock/clock-map.h
@@ -36,7 +36,6 @@ void clock_map_refresh (ClockMap *this);
void clock_map_update_time (ClockMap *this);
void clock_map_blink_location (ClockMap *this, ClockLocation *loc);
-
#ifdef __cplusplus
}
#endif
diff --git a/applets/clock/clock-sunpos.c b/applets/clock/clock-sunpos.c
index 20a7b619..7b3ee86a 100644
--- a/applets/clock/clock-sunpos.c
+++ b/applets/clock/clock-sunpos.c
@@ -170,7 +170,6 @@ sun_position (time_t unix_time, gdouble *lat, gdouble *lon)
*lon = ra;
}
-
#if 0
int
main (int argc, char *argv[])
diff --git a/applets/clock/clock.c b/applets/clock/clock.c
index 1b3480fe..4b7920c1 100644
--- a/applets/clock/clock.c
+++ b/applets/clock/clock.c
@@ -51,6 +51,11 @@
#include <gdk/gdkkeysyms.h>
#include <gio/gio.h>
+#if defined (CLOCK_INPROCESS) && defined (HAVE_WAYLAND)
+#include <gdk/gdkwayland.h>
+#include <gtk-layer-shell/gtk-layer-shell.h>
+#endif
+
#ifdef HAVE_X11
#include <gdk/gdkx.h>
#endif
@@ -84,6 +89,20 @@
#define KEY_CITIES "cities"
#define KEY_TEMPERATURE_UNIT "temperature-unit"
#define KEY_SPEED_UNIT "speed-unit"
+#define KEY_SHOW_CALENDAR_EVENTS "show-calendar-events"
+#define KEY_SHOW_TASKS "show-tasks"
+
+/* For watching for when the system resumes from sleep mode (e.g. suspend)
+ * and updating the clock as soon as that happens. */
+#define LOGIND_RUNNING() (access("/run/systemd/seats/", F_OK) >= 0)
+#define SYSTEMD_LOGIND_SERVICE "org.freedesktop.login1"
+#define SYSTEMD_LOGIND_PATH "/org/freedesktop/login1"
+#define SYSTEMD_LOGIND_INTERFACE "org.freedesktop.login1.Manager"
+#define CK_SERVICE "org.freedesktop.ConsoleKit"
+#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager"
+#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
+
+#define _clock_get_widget(x,y) (GTK_WIDGET (gtk_builder_get_object ((x)->builder, (y))))
enum {
COL_CITY_NAME = 0,
@@ -110,6 +129,7 @@ struct _ClockData {
GtkWidget *props;
GtkWidget *calendar_popup;
+ gboolean calendar_popup_destroying;
GtkWidget *clock_vbox;
GtkSizeGroup *clock_group;
@@ -184,6 +204,8 @@ struct _ClockData {
GSettings *settings;
const gchar *weather_icon_name;
+
+ GDBusProxy *system_manager_proxy;
};
/* Used to count the number of clock instances. It's there to know when we
@@ -241,13 +263,6 @@ clock_box_class_init (ClockBoxClass *klass)
/* Clock */
-static inline GtkWidget *
-_clock_get_widget (ClockData *cd,
- const char *name)
-{
- return GTK_WIDGET (gtk_builder_get_object (cd->builder, name));
-}
-
static void
unfix_size (ClockData *cd)
{
@@ -373,7 +388,7 @@ calculate_minimum_height (GtkWidget *widget,
{
GtkStateFlags state;
GtkStyleContext *style_context;
- const PangoFontDescription *font_desc;
+ PangoFontDescription *font_desc;
GtkBorder padding;
PangoContext *pango_context;
PangoFontMetrics *metrics;
@@ -394,6 +409,7 @@ calculate_minimum_height (GtkWidget *widget,
descent = pango_font_metrics_get_descent (metrics);
pango_font_metrics_unref (metrics);
+ pango_font_description_free (font_desc);
gtk_style_context_get_padding (style_context, state, &padding);
@@ -445,7 +461,7 @@ get_updated_timeformat (ClockData *cd)
/* Translators: This is a strftime format string.
* It is used to display the time in 12-hours format (eg, like
* in the US: 8:10 am). The %p expands to am/pm. */
- time_format = cd->showseconds ? _("%l:%M:%S %p") : _("%l:%M %p");
+ time_format = cd->showseconds ? _("%_I:%M:%S %p") : _("%_I:%M %p");
else
/* Translators: This is a strftime format string.
* It is used to display the time in 24-hours format (eg, like
@@ -751,6 +767,12 @@ free_locations (ClockData *cd)
static void
destroy_clock (GtkWidget * widget, ClockData *cd)
{
+ if (cd->system_manager_proxy)
+ {
+ g_signal_handlers_disconnect_by_data (cd->system_manager_proxy, cd);
+ g_object_unref (cd->system_manager_proxy);
+ }
+
if (cd->settings)
g_signal_handlers_disconnect_by_data( cd->settings, cd);
@@ -769,9 +791,12 @@ destroy_clock (GtkWidget * widget, ClockData *cd)
gtk_widget_destroy (cd->props);
cd->props = NULL;
- if (cd->calendar_popup)
+ if (cd->calendar_popup && !cd->calendar_popup_destroying) {
+ cd->calendar_popup_destroying = TRUE;
gtk_widget_destroy (cd->calendar_popup);
+ }
cd->calendar_popup = NULL;
+ cd->calendar_popup_destroying = FALSE;
g_free (cd->timeformat);
@@ -838,11 +863,23 @@ create_calendar (ClockData *cd)
{
GtkWidget *window;
char *prefs_path;
+ char *fallback_path = NULL;
prefs_path = mate_panel_applet_get_preferences_path (MATE_PANEL_APPLET (cd->applet));
+
+ /* Provide a fallback preferences path in case the applet doesn't
+ * provide one. This happens when running outside the actual panel,
+ * like in mate-panel-test-applets */
+ if (!prefs_path || !prefs_path[0]) {
+ fallback_path = g_strdup ("/org/mate/panel/applets/clock/");
+ g_free (prefs_path);
+ prefs_path = fallback_path;
+ }
+
window = calendar_window_new (&cd->current_time,
prefs_path,
- cd->orient == MATE_PANEL_APPLET_ORIENT_UP);
+ cd->orient == MATE_PANEL_APPLET_ORIENT_UP,
+ cd->settings);
g_free (prefs_path);
calendar_window_set_show_weeks (CALENDAR_WINDOW (window),
@@ -854,9 +891,9 @@ create_calendar (ClockData *cd)
g_signal_connect (window, "edit-locations",
G_CALLBACK (edit_locations_cb), cd);
- g_signal_connect (window, "delete_event",
+ g_signal_connect (window, "delete-event",
G_CALLBACK (delete_event), cd->panel_button);
- g_signal_connect (window, "key_press_event",
+ g_signal_connect (window, "key-press-event",
G_CALLBACK (close_on_escape), cd->panel_button);
/*Name this window so the default theme can be overridden in panel theme,
@@ -874,105 +911,203 @@ create_calendar (ClockData *cd)
static void
position_calendar_popup (ClockData *cd)
{
-#ifdef HAVE_X11
- GtkRequisition req;
- GtkAllocation allocation;
- GdkDisplay *display;
- GdkScreen *screen;
- GdkRectangle monitor;
- GdkGravity gravity = GDK_GRAVITY_NORTH_WEST;
- int button_w, button_h;
- int x, y;
- int w, h;
- int i, n;
- gboolean found_monitor = FALSE;
-
- if (!GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
- return;
+#if defined(HAVE_X11) && defined(GDK_WINDOWING_X11)
+ if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
+ {
+ GtkRequisition req;
+ GtkAllocation allocation;
+ GdkDisplay *display;
+ GdkScreen *screen;
+ GdkRectangle monitor;
+ GdkGravity gravity = GDK_GRAVITY_NORTH_WEST;
+ int button_w, button_h;
+ int x, y;
+ int w, h;
+ int i, n;
+ gboolean found_monitor = FALSE;
+
+
+ /* Get root origin of the toggle button, and position above that. */
+ gdk_window_get_origin (gtk_widget_get_window (cd->panel_button),
+ &x, &y);
+
+ gtk_window_get_size (GTK_WINDOW (cd->calendar_popup), &w, &h);
+ gtk_widget_get_preferred_size (cd->calendar_popup, &req, NULL);
+ w = req.width;
+ h = req.height;
+
+ gtk_widget_get_allocation (cd->panel_button, &allocation);
+ button_w = allocation.width;
+ button_h = allocation.height;
+
+ display = gtk_widget_get_display (GTK_WIDGET (cd->calendar_popup));
+
+ n = gdk_display_get_n_monitors (display);
+ for (i = 0; i < n; i++) {
+ gdk_monitor_get_geometry (gdk_display_get_monitor (display, i), &monitor);
+ if (x >= monitor.x && x <= monitor.x + monitor.width &&
+ y >= monitor.y && y <= monitor.y + monitor.height) {
+ found_monitor = TRUE;
+ break;
+ }
+ }
- /* Get root origin of the toggle button, and position above that. */
- gdk_window_get_origin (gtk_widget_get_window (cd->panel_button),
- &x, &y);
+ if (!found_monitor) {
+ /* eek, we should be on one of those xinerama
+ monitors */
+ monitor.x = 0;
+ monitor.y = 0;
+ monitor.width = WidthOfScreen (gdk_x11_screen_get_xscreen (screen));
+ monitor.height = HeightOfScreen (gdk_x11_screen_get_xscreen (screen));
+ }
- gtk_window_get_size (GTK_WINDOW (cd->calendar_popup), &w, &h);
- gtk_widget_get_preferred_size (cd->calendar_popup, &req, NULL);
- w = req.width;
- h = req.height;
+ /* Based on panel orientation, position the popup.
+ * Ignore window gravity since the window is undecorated.
+ * The orientations are all named backward from what
+ * I expected.
+ */
+ switch (cd->orient) {
+ case MATE_PANEL_APPLET_ORIENT_RIGHT:
+ x += button_w;
+ if ((y + h) > monitor.y + monitor.height)
+ y -= (y + h) - (monitor.y + monitor.height);
+
+ if ((y + h) > (monitor.height / 2))
+ gravity = GDK_GRAVITY_SOUTH_WEST;
+ else
+ gravity = GDK_GRAVITY_NORTH_WEST;
- gtk_widget_get_allocation (cd->panel_button, &allocation);
- button_w = allocation.width;
- button_h = allocation.height;
-
- screen = gtk_window_get_screen (GTK_WINDOW (cd->calendar_popup));
- display = gdk_screen_get_display (screen);
-
- n = gdk_display_get_n_monitors (display);
- for (i = 0; i < n; i++) {
- gdk_monitor_get_geometry (gdk_display_get_monitor (display, i), &monitor);
- if (x >= monitor.x && x <= monitor.x + monitor.width &&
- y >= monitor.y && y <= monitor.y + monitor.height) {
- found_monitor = TRUE;
break;
- }
- }
+ case MATE_PANEL_APPLET_ORIENT_LEFT:
+ x -= w;
+ if ((y + h) > monitor.y + monitor.height)
+ y -= (y + h) - (monitor.y + monitor.height);
- if (!found_monitor) {
- /* eek, we should be on one of those xinerama
- monitors */
- monitor.x = 0;
- monitor.y = 0;
- monitor.width = WidthOfScreen (gdk_x11_screen_get_xscreen (screen));
- monitor.height = HeightOfScreen (gdk_x11_screen_get_xscreen (screen));
- }
+ if ((y + h) > (monitor.height / 2))
+ gravity = GDK_GRAVITY_SOUTH_EAST;
+ else
+ gravity = GDK_GRAVITY_NORTH_EAST;
- /* Based on panel orientation, position the popup.
- * Ignore window gravity since the window is undecorated.
- * The orientations are all named backward from what
- * I expected.
- */
- switch (cd->orient) {
- case MATE_PANEL_APPLET_ORIENT_RIGHT:
- x += button_w;
- if ((y + h) > monitor.y + monitor.height)
- y -= (y + h) - (monitor.y + monitor.height);
+ break;
+ case MATE_PANEL_APPLET_ORIENT_DOWN:
+ y += button_h;
+ if ((x + w) > monitor.x + monitor.width)
+ x -= (x + w) - (monitor.x + monitor.width);
- if ((y + h) > (monitor.height / 2))
- gravity = GDK_GRAVITY_SOUTH_WEST;
- else
gravity = GDK_GRAVITY_NORTH_WEST;
- break;
- case MATE_PANEL_APPLET_ORIENT_LEFT:
- x -= w;
- if ((y + h) > monitor.y + monitor.height)
- y -= (y + h) - (monitor.y + monitor.height);
+ break;
+ case MATE_PANEL_APPLET_ORIENT_UP:
+ y -= h;
+ if ((x + w) > monitor.x + monitor.width)
+ x -= (x + w) - (monitor.x + monitor.width);
- if ((y + h) > (monitor.height / 2))
- gravity = GDK_GRAVITY_SOUTH_EAST;
- else
- gravity = GDK_GRAVITY_NORTH_EAST;
+ gravity = GDK_GRAVITY_SOUTH_WEST;
- break;
- case MATE_PANEL_APPLET_ORIENT_DOWN:
- y += button_h;
- if ((x + w) > monitor.x + monitor.width)
- x -= (x + w) - (monitor.x + monitor.width);
+ break;
+ }
- gravity = GDK_GRAVITY_NORTH_WEST;
+ gtk_window_move (GTK_WINDOW (cd->calendar_popup), x, y);
+ gtk_window_set_gravity (GTK_WINDOW (cd->calendar_popup), gravity);
+ }
+#endif
- break;
- case MATE_PANEL_APPLET_ORIENT_UP:
- y -= h;
- if ((x + w) > monitor.x + monitor.width)
- x -= (x + w) - (monitor.x + monitor.width);
+ /*Only build wayland support when building in process*/
+#if defined(CLOCK_INPROCESS) && defined(HAVE_WAYLAND) && defined(GDK_WINDOWING_WAYLAND)
+ if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()))
+ {
+ GtkWindow *window;
+ GdkWindow *panelwin;
+ GtkWidget *toplevel;
+ int x, y, w, h, panel_w, panel_h;
+
+ /*Get the calendar window dimensions*/
+ window = (GTK_WINDOW (cd->calendar_popup));
+ gtk_window_get_size (window, &w, &h);
+ /*Find the position of the applet*/
+ gdk_window_get_origin (gtk_widget_get_window (cd->panel_button),
+ &x, &y);
+
+ /*Get the panel dimensions*/
+ toplevel = gtk_widget_get_toplevel (cd->applet);
+ panelwin = gtk_widget_get_window (toplevel);
+ gdk_window_get_geometry (panelwin, NULL, NULL, &panel_w, &panel_h);
+
+ /*Set up GTK Layer Shell*/
+ gtk_layer_init_for_window (window);
+ gtk_layer_set_layer (window, GTK_LAYER_SHELL_LAYER_TOP);
+
+ switch (cd->orient) {
+ case MATE_PANEL_APPLET_ORIENT_RIGHT:
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_LEFT, TRUE);
+ gtk_layer_set_margin (window, GTK_LAYER_SHELL_EDGE_LEFT, 0);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE);
+ if (y < (panel_h - h))
+ {
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_TOP, TRUE);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE);
+ gtk_layer_set_margin (window, GTK_LAYER_SHELL_EDGE_TOP, y);
+ }
+ else
+ {
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_TOP, FALSE);
+ }
+ break;
- gravity = GDK_GRAVITY_SOUTH_WEST;
+ case MATE_PANEL_APPLET_ORIENT_LEFT:
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE);
+ gtk_layer_set_margin (window, GTK_LAYER_SHELL_EDGE_RIGHT, 0);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_LEFT, FALSE);
+ if (y < (panel_h - h))
+ {
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_TOP, TRUE);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE);
+ gtk_layer_set_margin (window, GTK_LAYER_SHELL_EDGE_TOP, y);
+ }
+ else
+ {
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_TOP, FALSE);
+ }
+ break;
- break;
- }
+ case MATE_PANEL_APPLET_ORIENT_DOWN:
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_TOP, TRUE);
+ gtk_layer_set_margin (window, GTK_LAYER_SHELL_EDGE_TOP, 0);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE);
+ if (x < (panel_w - w))
+ {
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_LEFT, TRUE);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE);
+ gtk_layer_set_margin (window, GTK_LAYER_SHELL_EDGE_LEFT, x);
+ }
+ else
+ {
+ gtk_layer_set_anchor(window, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_LEFT, FALSE);
+ }
+ break;
- gtk_window_move (GTK_WINDOW (cd->calendar_popup), x, y);
- gtk_window_set_gravity (GTK_WINDOW (cd->calendar_popup), gravity);
+ case MATE_PANEL_APPLET_ORIENT_UP:
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE);
+ gtk_layer_set_margin (window, GTK_LAYER_SHELL_EDGE_BOTTOM, 0);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_TOP, FALSE);
+ if (x < (panel_w - w))
+ {
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_LEFT, TRUE);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE);
+ gtk_layer_set_margin (window, GTK_LAYER_SHELL_EDGE_LEFT, x);
+ }
+ else
+ {
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE);
+ gtk_layer_set_anchor (window, GTK_LAYER_SHELL_EDGE_LEFT, FALSE);
+ }
+ break;
+ }
+ return;
+ }
#endif
}
@@ -1049,7 +1184,6 @@ create_cities_store (ClockData *cd)
}
g_slist_free (list);
-
if (cd->prefs_window) {
GtkWidget *widget = _clock_get_widget (cd, "cities_list");
gtk_tree_view_set_model (GTK_TREE_VIEW (widget),
@@ -1070,35 +1204,17 @@ sort_locations_by_time (gconstpointer a, gconstpointer b)
clock_location_localtime (loc_a, &tm_a);
clock_location_localtime (loc_b, &tm_b);
- ret = (tm_a.tm_year == tm_b.tm_year) ? 0 : 1;
- if (ret) {
- return (tm_a.tm_year < tm_b.tm_year) ? -1 : 1;
- }
-
- ret = (tm_a.tm_mon == tm_b.tm_mon) ? 0 : 1;
- if (ret) {
- return (tm_a.tm_mon < tm_b.tm_mon) ? -1 : 1;
- }
-
- ret = (tm_a.tm_mday == tm_b.tm_mday) ? 0 : 1;
- if (ret) {
- return (tm_a.tm_mday < tm_b.tm_mday) ? -1 : 1;
- }
-
- ret = (tm_a.tm_hour == tm_b.tm_hour) ? 0 : 1;
- if (ret) {
- return (tm_a.tm_hour < tm_b.tm_hour) ? -1 : 1;
- }
-
- ret = (tm_a.tm_min == tm_b.tm_min) ? 0 : 1;
- if (ret) {
- return (tm_a.tm_min < tm_b.tm_min) ? -1 : 1;
- }
-
- ret = (tm_a.tm_sec == tm_b.tm_sec) ? 0 : 1;
- if (ret) {
- return (tm_a.tm_sec < tm_b.tm_sec) ? -1 : 1;
- }
+ ret = tm_a.tm_year - tm_b.tm_year;
+ if (! ret)
+ ret = tm_a.tm_mon - tm_b.tm_mon;
+ if (! ret)
+ ret = tm_a.tm_mday - tm_b.tm_mday;
+ if (! ret)
+ ret = tm_a.tm_hour - tm_b.tm_hour;
+ if (! ret)
+ ret = tm_a.tm_min - tm_b.tm_min;
+ if (! ret)
+ ret = tm_a.tm_sec - tm_b.tm_sec;
return ret;
}
@@ -1149,6 +1265,7 @@ location_tile_need_clock_format_cb(ClockLocationTile *tile, gpointer data)
static void
create_cities_section (ClockData *cd)
{
+ GtkWidget *cities_box;
GSList *node;
GSList *cities;
GSList *l;
@@ -1162,8 +1279,16 @@ create_cities_section (ClockData *cd)
g_slist_free (cd->location_tiles);
cd->location_tiles = NULL;
- cd->cities_section = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
- gtk_container_set_border_width (GTK_CONTAINER (cd->cities_section), 0);
+ cd->cities_section = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cd->cities_section),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (cd->cities_section),
+ GTK_SHADOW_NONE);
+ gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (cd->cities_section),
+ TRUE);
+
+ cities_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
cities = cd->locations;
if (g_slist_length (cities) == 0) {
@@ -1185,7 +1310,7 @@ create_cities_section (ClockData *cd)
g_signal_connect (city, "need-clock-format",
G_CALLBACK (location_tile_need_clock_format_cb), cd);
- gtk_box_pack_start (GTK_BOX (cd->cities_section),
+ gtk_box_pack_start (GTK_BOX (cities_box),
GTK_WIDGET (city),
FALSE, FALSE, 0);
@@ -1196,6 +1321,8 @@ create_cities_section (ClockData *cd)
g_slist_free (node);
+ gtk_container_add (GTK_CONTAINER (cd->cities_section), cities_box);
+
gtk_box_pack_end (GTK_BOX (cd->clock_vbox),
cd->cities_section, FALSE, FALSE, 0);
@@ -1239,9 +1366,11 @@ static void
update_calendar_popup (ClockData *cd)
{
if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (cd->panel_button))) {
- if (cd->calendar_popup) {
+ if (cd->calendar_popup && !cd->calendar_popup_destroying) {
+ cd->calendar_popup_destroying = TRUE;
gtk_widget_destroy (cd->calendar_popup);
cd->calendar_popup = NULL;
+ cd->calendar_popup_destroying = FALSE;
cd->cities_section = NULL;
cd->map_widget = NULL;
cd->clock_vbox = NULL;
@@ -1254,7 +1383,7 @@ update_calendar_popup (ClockData *cd)
return;
}
- if (!cd->calendar_popup) {
+ if (!cd->calendar_popup && !cd->calendar_popup_destroying) {
cd->calendar_popup = create_calendar (cd);
g_object_add_weak_pointer (G_OBJECT (cd->calendar_popup),
(gpointer *) &cd->calendar_popup);
@@ -1288,7 +1417,7 @@ do_not_eat_button_press (GtkWidget *widget,
GdkEventButton *event)
{
if (event->button != 1)
- g_signal_stop_emission_by_name (widget, "button_press_event");
+ g_signal_stop_emission_by_name (widget, "button-press-event");
return FALSE;
}
@@ -1342,7 +1471,6 @@ force_no_button_vertical_padding (GtkWidget *widget)
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider);
-
gtk_widget_set_name (widget, "clock-applet-button");
}
@@ -1367,11 +1495,11 @@ create_main_clock_label (ClockData *cd)
label = gtk_label_new (NULL);
/*Fixme-this is invalid for labels with any recent GTK3 version, maybe all of them*/
/*
- g_signal_connect (label, "size_request",
+ g_signal_connect (label, "size-request",
G_CALLBACK (clock_size_request),
cd);
*/
- g_signal_connect_swapped (label, "style_set",
+ g_signal_connect_swapped (label, "style-set",
G_CALLBACK (unfix_size),
cd);
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
@@ -1417,11 +1545,11 @@ create_clock_widget (ClockData *cd)
{
/* Main toggle button */
cd->panel_button = create_main_clock_button ();
- g_signal_connect (cd->panel_button, "button_press_event",
+ g_signal_connect (cd->panel_button, "button-press-event",
G_CALLBACK (do_not_eat_button_press), NULL);
g_signal_connect (cd->panel_button, "toggled",
G_CALLBACK (toggle_calendar), cd);
- g_signal_connect (G_OBJECT (cd->panel_button), "destroy",
+ g_signal_connect (cd->panel_button, "destroy",
G_CALLBACK (destroy_clock),
cd);
gtk_widget_show (cd->panel_button);
@@ -1826,11 +1954,11 @@ output_cb (GtkSpinButton *spin,
{
GtkAdjustment *adj;
gchar *text;
- int value;
+ gdouble value;
adj = gtk_spin_button_get_adjustment (spin);
- value = (int) gtk_adjustment_get_value (adj);
- text = g_strdup_printf ("%02d", value);
+ value = gtk_adjustment_get_value (adj);
+ text = g_strdup_printf ("%02.0f", value);
gtk_entry_set_text (GTK_ENTRY (spin), text);
g_free (text);
@@ -1846,7 +1974,7 @@ ensure_time_settings_window_is_created (ClockData *cd)
return;
cd->set_time_window = _clock_get_widget (cd, "set-time-window");
- g_signal_connect (cd->set_time_window, "delete_event",
+ g_signal_connect (cd->set_time_window, "delete-event",
G_CALLBACK (delete_time_settings), cd);
cd->calendar = _clock_get_widget (cd, "calendar");
@@ -1854,12 +1982,6 @@ ensure_time_settings_window_is_created (ClockData *cd)
cd->minutes_spin = _clock_get_widget (cd, "minutes_spin");
cd->seconds_spin = _clock_get_widget (cd, "seconds_spin");
- gtk_entry_set_width_chars (GTK_ENTRY (cd->hours_spin), 2);
- gtk_entry_set_width_chars (GTK_ENTRY (cd->minutes_spin), 2);
- gtk_entry_set_width_chars (GTK_ENTRY (cd->seconds_spin), 2);
- gtk_entry_set_alignment (GTK_ENTRY (cd->hours_spin), 1.0);
- gtk_entry_set_alignment (GTK_ENTRY (cd->minutes_spin), 1.0);
- gtk_entry_set_alignment (GTK_ENTRY (cd->seconds_spin), 1.0);
g_signal_connect (cd->seconds_spin, "wrapped", G_CALLBACK (wrap_cb), cd);
g_signal_connect (cd->minutes_spin, "wrapped", G_CALLBACK (wrap_cb), cd);
g_signal_connect (cd->hours_spin, "wrapped", G_CALLBACK (wrap_cb), cd);
@@ -1923,7 +2045,8 @@ format_changed (GSettings *settings,
gchar *key,
ClockData *clock)
{
- int new_format;
+ ClockFormat new_format;
+
new_format = g_settings_get_enum (settings, key);
if (!clock->can_handle_format_12 && new_format == CLOCK_FORMAT_12)
@@ -2126,9 +2249,6 @@ location_set_current_cb (ClockLocation *loc,
static void
locations_changed (ClockData *cd)
{
- GSList *l;
- glong id;
-
if (!cd->locations) {
if (cd->weather_obox)
gtk_widget_hide (cd->weather_obox);
@@ -2143,8 +2263,9 @@ locations_changed (ClockData *cd)
gtk_widget_show (cd->weather_obox);
}
- for (l = cd->locations; l; l = l->next) {
+ for (GSList *l = cd->locations; l; l = l->next) {
ClockLocation *loc = l->data;
+ glong id;
id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (loc), "weather-updated"));
if (id == 0) {
@@ -2163,7 +2284,6 @@ locations_changed (ClockData *cd)
create_cities_section (cd);
}
-
static void
set_locations (ClockData *cd, GSList *locations)
{
@@ -2324,21 +2444,25 @@ clock_timezone_changed (SystemTimezone *systz,
refresh_click_timeout_time_only (cd);
}
+#define unit_combo_box_set_active(x,y) (gtk_combo_box_set_active (GTK_COMBO_BOX (gtk_builder_get_object (cd->builder, (x))), (y)))
+
static void
temperature_unit_changed (GSettings *settings,
gchar *key,
ClockData *cd)
{
- cd->temperature_unit = g_settings_get_enum (settings, key);
- if (cd->temperature_unit > 0)
- {
- GtkWidget *widget;
- gint oldvalue;
- widget = _clock_get_widget (cd, "temperature_combo");
- oldvalue = gtk_combo_box_get_active (GTK_COMBO_BOX (widget)) + 2;
- if (oldvalue != cd->speed_unit)
- gtk_combo_box_set_active (GTK_COMBO_BOX (widget), cd->temperature_unit - 2);
- }
+ TempUnit temperature_unit = g_settings_get_enum (settings, key);
+
+ if (cd->temperature_unit == temperature_unit)
+ return;
+
+ /* TEMP_UNIT_INVALID = 0 */
+ if (temperature_unit == TEMP_UNIT_INVALID)
+ return;
+
+ unit_combo_box_set_active ("temperature_combo",
+ ((int) temperature_unit) - 2);
+ cd->temperature_unit = temperature_unit;
update_weather_locations (cd);
}
@@ -2347,16 +2471,18 @@ speed_unit_changed (GSettings *settings,
gchar *key,
ClockData *cd)
{
- cd->speed_unit = g_settings_get_enum (settings, key);
- if (cd->speed_unit > 0)
- {
- GtkWidget *widget;
- gint oldvalue;
- widget = _clock_get_widget (cd, "wind_speed_combo");
- oldvalue = gtk_combo_box_get_active (GTK_COMBO_BOX (widget)) + 2;
- if (oldvalue != cd->speed_unit)
- gtk_combo_box_set_active (GTK_COMBO_BOX (widget), cd->speed_unit - 2);
- }
+ SpeedUnit speed_unit = g_settings_get_enum (settings, key);
+
+ if (cd->speed_unit == speed_unit)
+ return;
+
+ /* SPEED_UNIT_INVALID = 0 */
+ if (speed_unit == SPEED_UNIT_INVALID)
+ return;
+
+ unit_combo_box_set_active ("wind_speed_combo",
+ ((int) speed_unit) - 2);
+ cd->speed_unit = speed_unit;
update_weather_locations (cd);
}
@@ -2399,18 +2525,8 @@ show_week_changed (GSettings *settings,
static void
setup_gsettings (ClockData *cd)
{
- gint format;
- gchar *custom_format;
-
cd->settings = mate_panel_applet_settings_new (MATE_PANEL_APPLET (cd->applet), CLOCK_SCHEMA);
- /* hack to allow users to set custom format in dconf-editor */
- format = g_settings_get_enum (cd->settings, KEY_FORMAT);
- custom_format = g_settings_get_string (cd->settings, KEY_CUSTOM_FORMAT);
- g_settings_set_enum (cd->settings, KEY_FORMAT, format);
- g_settings_set_string (cd->settings, KEY_CUSTOM_FORMAT, custom_format);
- g_free (custom_format);
-
g_signal_connect (cd->settings, "changed::" KEY_FORMAT, G_CALLBACK (format_changed), cd);
g_signal_connect (cd->settings, "changed::" KEY_SHOW_SECONDS, G_CALLBACK (show_seconds_changed), cd);
g_signal_connect (cd->settings, "changed::" KEY_SHOW_DATE, G_CALLBACK (show_date_changed), cd);
@@ -2426,7 +2542,6 @@ setup_gsettings (ClockData *cd)
static GSList *
parse_gsettings_cities (ClockData *cd, gchar **values)
{
- gint i;
LocationParserData data;
GMarkupParseContext *context;
@@ -2436,7 +2551,7 @@ parse_gsettings_cities (ClockData *cd, gchar **values)
context = g_markup_parse_context_new (&location_parser, 0, &data, NULL);
if (values) {
- for (i = 0; values[i]; i++) {
+ for (gint i = 0; values[i]; i++) {
g_markup_parse_context_parse (context, values[i], strlen(values[i]), NULL);
}
}
@@ -2484,6 +2599,86 @@ load_gsettings (ClockData *cd)
set_locations (cd, cities);
}
+/* When the system manager (ConsoleKit or systemd-logind) reports that
+ * the system has just resumed from sleep mode (e.g. suspend/hibernate),
+ * update the clock and the weather/temperature readings. That way, if
+ * the user suspended the system for an hour and then wakes the system up,
+ * the user will immediately see the current time and (if possible)
+ * updated weather data. Without this extra code, the user would most
+ * likely wake the system up and see the weather from an hour ago, and if
+ * the clock is set to not display seconds (only minutes), the clock may
+ * show an inaccurate time for up to a minute after resume.
+ */
+static void
+system_manager_signal_cb (GDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ GVariant *parameters,
+ ClockData *cd)
+{
+ if (g_strcmp0 (signal_name, "PrepareForSleep") == 0)
+ {
+ GVariant *variant;
+ gboolean active;
+
+ variant = g_variant_get_child_value (parameters, 0);
+ active = g_variant_get_boolean (variant);
+ g_variant_unref (variant);
+
+ /* The PrepareForSleep signal is emitted by ConsoleKit2 and
+ * logind, both before sleep mode is entered, and
+ * immediately after the system has resumed from sleep mode.
+ * Listeners of this signal distinguish between the two
+ * states by checking the parameter to this signal. If the
+ * parameter is TRUE, the system is about to enter sleep
+ * mode; if FALSE, the system has resumed from sleep mode.
+ * We only care about updating the clock after resumption,
+ * so test if the parameter is FALSE.
+ */
+ if (active == FALSE)
+ {
+ update_clock (cd);
+ update_weather_locations (cd);
+ }
+ }
+}
+
+static void
+setup_monitor_for_resume (ClockData *cd)
+{
+ gboolean logind_running;
+ const char * service;
+ const char * path;
+ const char * interface;
+
+ /* If logind is running, connect to logind; otherwise use ConsoleKit.
+ */
+ logind_running = LOGIND_RUNNING ();
+ if (logind_running) {
+ service = SYSTEMD_LOGIND_SERVICE;
+ path = SYSTEMD_LOGIND_PATH;
+ interface = SYSTEMD_LOGIND_INTERFACE;
+ } else {
+ service = CK_SERVICE;
+ path = CK_MANAGER_PATH;
+ interface = CK_MANAGER_INTERFACE;
+ }
+
+ cd->system_manager_proxy = g_dbus_proxy_new_for_bus_sync
+ (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ service, path, interface,
+ NULL, NULL);
+
+ if (cd->system_manager_proxy != NULL) {
+ g_signal_connect (cd->system_manager_proxy,
+ "g-signal",
+ G_CALLBACK (system_manager_signal_cb),
+ cd);
+ }
+}
+
static gboolean
fill_clock_applet (MatePanelApplet *applet)
{
@@ -2517,13 +2712,11 @@ fill_clock_applet (MatePanelApplet *applet)
/* we have to bind change_orient before we do applet_widget_add
since we need to get an initial change_orient signal to set our
initial oriantation, and we get that during the _add call */
- g_signal_connect (G_OBJECT (cd->applet),
- "change_orient",
+ g_signal_connect (cd->applet, "change-orient",
G_CALLBACK (applet_change_orient),
cd);
- g_signal_connect (G_OBJECT (cd->panel_button),
- "size_allocate",
+ g_signal_connect (cd->panel_button, "size-allocate",
G_CALLBACK (panel_button_change_pixel_size),
cd);
@@ -2555,10 +2748,15 @@ fill_clock_applet (MatePanelApplet *applet)
/* Make sure the weather icon gets updated when the panel size changes*/
g_signal_connect (cd->applet,
- "change_size",
+ "change-size",
G_CALLBACK (weather_icon_updated_cb),
cd);
+ /* If ConsoleKit or systemd-logind is available, set up to update
+ * the clock if/when the system resumes from sleep (e.g. suspend/
+ * hibernate). */
+ setup_monitor_for_resume (cd);
+
return TRUE;
}
@@ -2912,7 +3110,7 @@ run_prefs_locations_add (GtkButton *button, ClockData *cd)
if (g_object_get_data (G_OBJECT (edit_window), "delete-handler") == NULL) {
g_object_set_data (G_OBJECT (edit_window), "delete-handler",
- GINT_TO_POINTER (g_signal_connect (edit_window, "delete_event", G_CALLBACK (edit_delete), cd)));
+ GINT_TO_POINTER (g_signal_connect (edit_window, "delete-event", G_CALLBACK (edit_delete), cd)));
}
location_update_ok_sensitivity (cd);
@@ -3041,7 +3239,6 @@ speed_combo_changed (GtkComboBox *combo, ClockData *cd)
g_settings_set_enum (cd->settings, KEY_SPEED_UNIT, value);
}
-
static void
fill_prefs_window (ClockData *cd)
{
@@ -3109,6 +3306,35 @@ fill_prefs_window (ClockData *cd)
g_settings_bind (cd->settings, KEY_SHOW_TEMPERATURE, widget, "active",
G_SETTINGS_BIND_DEFAULT);
+#ifdef HAVE_EDS
+ /* Set the EDS calendar event checkboxes */
+ widget = _clock_get_widget (cd, "show_calendar_events_check");
+ if (widget) {
+ /* Bind to gsettings - this should sync with calendar window automatically */
+ g_settings_bind (cd->settings, KEY_SHOW_CALENDAR_EVENTS, widget, "active",
+ G_SETTINGS_BIND_DEFAULT);
+ } else {
+ g_warning ("Could not find show_calendar_events_check widget in preferences");
+ }
+
+ /* Set the EDS tasks checkboxes */
+ widget = _clock_get_widget (cd, "show_tasks_check");
+ if (widget) {
+ /* Bind to gsettings - this should sync with calendar window automatically */
+ g_settings_bind (cd->settings, KEY_SHOW_TASKS, widget, "active",
+ G_SETTINGS_BIND_DEFAULT);
+ } else {
+ g_warning ("Could not find show_tasks_check widget in preferences");
+ }
+
+#else
+ /* Hide the Calendar tab if EDS is not available */
+ widget = _clock_get_widget (cd, "vbox-calendar");
+ gtk_widget_hide (widget);
+ widget = _clock_get_widget (cd, "label-calendar-tab");
+ gtk_widget_hide (widget);
+#endif
+
/* Fill the Cities list */
widget = _clock_get_widget (cd, "cities_list");
@@ -3195,36 +3421,35 @@ ensure_prefs_window_is_created (ClockData *cd)
location_name_label = _clock_get_widget (cd, "location-name-label");
timezone_label = _clock_get_widget (cd, "timezone-label");
-
if (!clock_locale_supports_am_pm ())
gtk_widget_hide (clock_options);
selection = gtk_tree_view_get_selection (cd->prefs_locations);
- g_signal_connect (G_OBJECT (selection), "changed",
+ g_signal_connect (selection, "changed",
G_CALLBACK (prefs_locations_changed), cd);
- g_signal_connect (G_OBJECT (cd->prefs_window), "delete_event",
+ g_signal_connect (cd->prefs_window, "delete-event",
G_CALLBACK (prefs_hide_event), cd);
- g_signal_connect (G_OBJECT (prefs_close_button), "clicked",
+ g_signal_connect (prefs_close_button, "clicked",
G_CALLBACK (prefs_hide), cd);
- g_signal_connect (G_OBJECT (prefs_help_button), "clicked",
+ g_signal_connect (prefs_help_button, "clicked",
G_CALLBACK (prefs_help), cd);
cd->prefs_location_remove_button = _clock_get_widget (cd, "prefs-locations-remove-button");
- g_signal_connect (G_OBJECT (cd->prefs_location_remove_button), "clicked",
+ g_signal_connect (cd->prefs_location_remove_button, "clicked",
G_CALLBACK (run_prefs_locations_remove), cd);
cd->prefs_location_add_button = _clock_get_widget (cd, "prefs-locations-add-button");
- g_signal_connect (G_OBJECT (cd->prefs_location_add_button), "clicked",
+ g_signal_connect (cd->prefs_location_add_button, "clicked",
G_CALLBACK (run_prefs_locations_add), cd);
cd->prefs_location_edit_button = _clock_get_widget (cd, "prefs-locations-edit-button");
- g_signal_connect (G_OBJECT (cd->prefs_location_edit_button), "clicked",
+ g_signal_connect (cd->prefs_location_edit_button, "clicked",
G_CALLBACK (run_prefs_locations_edit), cd);
edit_window = _clock_get_widget (cd, "edit-location-window");
@@ -3232,7 +3457,7 @@ ensure_prefs_window_is_created (ClockData *cd)
gtk_window_set_transient_for (GTK_WINDOW (edit_window),
GTK_WINDOW (cd->prefs_window));
- g_signal_connect (G_OBJECT (edit_window), "delete_event",
+ g_signal_connect (edit_window, "delete-event",
G_CALLBACK (edit_hide_event), cd);
edit_cancel_button = _clock_get_widget (cd, "edit-location-cancel-button");
@@ -3248,9 +3473,9 @@ ensure_prefs_window_is_created (ClockData *cd)
gtk_label_set_mnemonic_widget (GTK_LABEL (location_name_label),
GTK_WIDGET (cd->location_entry));
- g_signal_connect (G_OBJECT (cd->location_entry), "notify::location",
+ g_signal_connect (cd->location_entry, "notify::location",
G_CALLBACK (location_changed), cd);
- g_signal_connect (G_OBJECT (cd->location_entry), "changed",
+ g_signal_connect (cd->location_entry, "changed",
G_CALLBACK (location_name_changed), cd);
zone_box = _clock_get_widget (cd, "edit-location-timezone-box");
@@ -3260,15 +3485,15 @@ ensure_prefs_window_is_created (ClockData *cd)
gtk_label_set_mnemonic_widget (GTK_LABEL (timezone_label),
GTK_WIDGET (cd->zone_combo));
- g_signal_connect (G_OBJECT (cd->zone_combo), "notify::tzid",
+ g_signal_connect (cd->zone_combo, "notify::tzid",
G_CALLBACK (location_timezone_changed), cd);
mateweather_location_unref (world);
- g_signal_connect (G_OBJECT (edit_cancel_button), "clicked",
+ g_signal_connect (edit_cancel_button, "clicked",
G_CALLBACK (edit_hide), cd);
- g_signal_connect (G_OBJECT (edit_ok_button), "clicked",
+ g_signal_connect (edit_ok_button, "clicked",
G_CALLBACK (run_prefs_edit_save), cd);
/* Set up the time setting section */
@@ -3346,8 +3571,7 @@ display_properties_dialog (ClockData *cd, gboolean start_in_locations_page)
GtkWidget *notebook = _clock_get_widget (cd, "notebook");
gtk_widget_add_events (notebook, GDK_SCROLL_MASK);
- g_signal_connect (GTK_NOTEBOOK (notebook),
- "scroll-event",
+ g_signal_connect (notebook, "scroll-event",
G_CALLBACK (on_notebook_scroll_event),
NULL);
diff --git a/applets/clock/clock.ui b/applets/clock/clock.ui
index 822aca0b..2f9cfa78 100644
--- a/applets/clock/clock.ui
+++ b/applets/clock/clock.ui
@@ -1,55 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.0 -->
+<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkAdjustment" id="adjustment1">
<property name="upper">59</property>
<property name="value">59</property>
- <property name="step_increment">1</property>
- <property name="page_increment">30</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">30</property>
</object>
<object class="GtkAdjustment" id="adjustment2">
<property name="upper">59</property>
<property name="value">59</property>
- <property name="step_increment">1</property>
- <property name="page_increment">30</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">30</property>
</object>
<object class="GtkAdjustment" id="adjustment3">
<property name="upper">23</property>
<property name="value">23</property>
- <property name="step_increment">1</property>
- <property name="page_increment">12</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">12</property>
</object>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">process-stop</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">process-stop</property>
</object>
<object class="GtkDialog" id="set-time-window">
- <property name="can_focus">False</property>
- <property name="border_width">5</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">5</property>
<property name="title" translatable="yes">Time &amp; Date</property>
<property name="resizable">False</property>
- <property name="type_hint">dialog</property>
+ <property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkButton" id="cancel-set-time-button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="receives-default">False</property>
<property name="image">image1</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -61,9 +61,9 @@
<object class="GtkButton" id="set-time-button">
<property name="label" translatable="yes">_Set System Time</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -75,28 +75,28 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="pack_type">end</property>
+ <property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="yscale">0</property>
- <property name="top_padding">7</property>
- <property name="bottom_padding">6</property>
- <property name="left_padding">7</property>
- <property name="right_padding">7</property>
+ <property name="top-padding">7</property>
+ <property name="bottom-padding">6</property>
+ <property name="left-padding">7</property>
+ <property name="right-padding">7</property>
<child>
<object class="GtkBox" id="time_settings_box">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkCalendar" id="calendar">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="can-focus">True</property>
<property name="year">2009</property>
<property name="month">5</property>
<property name="day">3</property>
@@ -108,23 +108,26 @@
</packing>
</child>
<child>
+ <!-- n-columns=3 n-rows=3 -->
<object class="GtkGrid" id="table1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="valign">start</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">12</property>
+ <property name="row-spacing">6</property>
+ <property name="column-spacing">12</property>
<child>
<object class="GtkBox" id="hbox61">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<child>
<object class="GtkSpinButton" id="hours_spin">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">●</property>
+ <property name="can-focus">True</property>
+ <property name="invisible-char">●</property>
+ <property name="width-chars">2</property>
+ <property name="xalign">1</property>
<property name="adjustment">adjustment3</property>
- <property name="climb_rate">1</property>
+ <property name="climb-rate">1</property>
<property name="numeric">True</property>
<property name="wrap">True</property>
</object>
@@ -137,10 +140,12 @@
<child>
<object class="GtkSpinButton" id="minutes_spin">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">●</property>
+ <property name="can-focus">True</property>
+ <property name="invisible-char">●</property>
+ <property name="width-chars">2</property>
+ <property name="xalign">1</property>
<property name="adjustment">adjustment2</property>
- <property name="climb_rate">1</property>
+ <property name="climb-rate">1</property>
<property name="numeric">True</property>
<property name="wrap">True</property>
</object>
@@ -153,10 +158,12 @@
<child>
<object class="GtkSpinButton" id="seconds_spin">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">●</property>
+ <property name="can-focus">True</property>
+ <property name="invisible-char">●</property>
+ <property name="width-chars">2</property>
+ <property name="xalign">1</property>
<property name="adjustment">adjustment1</property>
- <property name="climb_rate">1</property>
+ <property name="climb-rate">1</property>
<property name="numeric">True</property>
<property name="wrap">True</property>
</object>
@@ -168,48 +175,63 @@
</child>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="current_time_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label">23:59:59</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label236">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">_Time:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">hours_spin</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">hours_spin</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label235">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">Current Time:</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
</packing>
</child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
</object>
<packing>
<property name="expand">False</property>
@@ -232,71 +254,68 @@
<action-widget response="0">cancel-set-time-button</action-widget>
<action-widget response="0">set-time-button</action-widget>
</action-widgets>
- <child>
- <placeholder/>
- </child>
</object>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">gtk-ok</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">gtk-ok</property>
</object>
<object class="GtkImage" id="image3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">process-stop</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">process-stop</property>
</object>
<object class="GtkImage" id="image4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">help-browser</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">help-browser</property>
</object>
<object class="GtkImage" id="image5">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">window-close</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">window-close</property>
</object>
<object class="GtkImage" id="image6">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">list-add</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">list-add</property>
</object>
<object class="GtkImage" id="image7">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">gtk-edit</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">gtk-edit</property>
</object>
<object class="GtkImage" id="image8">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">list-remove</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">list-remove</property>
</object>
<object class="GtkDialog" id="prefs-window">
- <property name="can_focus">False</property>
- <property name="border_width">5</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">5</property>
<property name="title" translatable="yes">Clock Preferences</property>
<property name="resizable">False</property>
- <property name="window_position">center</property>
- <property name="type_hint">dialog</property>
+ <property name="window-position">center</property>
+ <property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<child>
<object class="GtkButton" id="prefs-help-button">
<property name="label" translatable="yes">_Help</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="receives-default">False</property>
<property name="image">image4</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -308,10 +327,10 @@
<object class="GtkButton" id="time-settings-button">
<property name="label" translatable="yes">Time _Settings</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -323,12 +342,12 @@
<object class="GtkButton" id="prefs-close-button">
<property name="label" translatable="yes">_Close</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="has_default">True</property>
- <property name="receives_default">False</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="has-default">True</property>
+ <property name="receives-default">False</property>
<property name="image">image5</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -340,32 +359,32 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="pack_type">end</property>
+ <property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="border_width">5</property>
+ <property name="can-focus">True</property>
+ <property name="border-width">5</property>
<child>
<object class="GtkBox" id="vbox17">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">12</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">12</property>
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<child>
<object class="GtkBox" id="clock-options">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label210">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">Clock Format</property>
<property name="xalign">0</property>
<attributes>
@@ -381,21 +400,21 @@
<child>
<object class="GtkAlignment" id="alignment32">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="left_padding">12</property>
+ <property name="can-focus">False</property>
+ <property name="left-padding">12</property>
<child>
<object class="GtkBox" id="hbox49">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="spacing">13</property>
<child>
<object class="GtkRadioButton" id="12hr_radio">
<property name="label" translatable="yes">_12 hour format</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="draw_indicator">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -407,10 +426,10 @@
<object class="GtkRadioButton" id="24hr_radio">
<property name="label" translatable="yes">_24 hour format</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="draw_indicator">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
<property name="group">12hr_radio</property>
</object>
<packing>
@@ -438,13 +457,13 @@
<child>
<object class="GtkBox" id="vbox29">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label229">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">Panel Display</property>
<property name="xalign">0</property>
<attributes>
@@ -460,22 +479,22 @@
<child>
<object class="GtkAlignment" id="alignment33">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="left_padding">12</property>
+ <property name="can-focus">False</property>
+ <property name="left-padding">12</property>
<child>
<object class="GtkBox" id="vbox30">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="date_check">
<property name="label" translatable="yes">Show the _date</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="draw_indicator">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -487,10 +506,10 @@
<object class="GtkCheckButton" id="seconds_check">
<property name="label" translatable="yes">Show seco_nds</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="draw_indicator">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -502,10 +521,10 @@
<object class="GtkCheckButton" id="weeks_check">
<property name="label" translatable="yes">Show wee_k numbers in calendar</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="draw_indicator">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -517,10 +536,10 @@
<object class="GtkCheckButton" id="weather_check">
<property name="label" translatable="yes">Show _weather</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="draw_indicator">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -532,10 +551,10 @@
<object class="GtkCheckButton" id="temperature_check">
<property name="label" translatable="yes">Show _temperature</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="draw_indicator">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -564,38 +583,39 @@
<child type="tab">
<object class="GtkLabel" id="label209">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">General</property>
</object>
<packing>
- <property name="tab_fill">False</property>
+ <property name="tab-fill">False</property>
</packing>
</child>
<child>
<object class="GtkBox" id="vbox24">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">12</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">12</property>
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<child>
<object class="GtkBox" id="hbox54">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow10">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="vscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
+ <property name="can-focus">True</property>
+ <property name="expand">True</property>
+ <property name="hscrollbar-policy">never</property>
+ <property name="vscrollbar-policy">automatic</property>
+ <property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="cities_list">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="headers_visible">False</property>
- <property name="hover_expand">True</property>
+ <property name="can-focus">True</property>
+ <property name="headers-visible">False</property>
+ <property name="hover-expand">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
@@ -611,19 +631,19 @@
<child>
<object class="GtkButtonBox" id="vbuttonbox2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
- <property name="layout_style">start</property>
+ <property name="layout-style">start</property>
<child>
<object class="GtkButton" id="prefs-locations-add-button">
<property name="label" translatable="yes">_Add</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="receives-default">False</property>
<property name="image">image6</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -635,11 +655,11 @@
<object class="GtkButton" id="prefs-locations-edit-button">
<property name="label" translatable="yes">_Edit</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="receives-default">False</property>
<property name="image">image7</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -651,11 +671,11 @@
<object class="GtkButton" id="prefs-locations-remove-button">
<property name="label" translatable="yes">_Remove</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="receives-default">False</property>
<property name="image">image8</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -685,31 +705,31 @@
<child type="tab">
<object class="GtkLabel" id="label220">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">Locations</property>
</object>
<packing>
<property name="position">1</property>
- <property name="tab_fill">False</property>
+ <property name="tab-fill">False</property>
</packing>
</child>
<child>
<object class="GtkBox" id="vbox27">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">12</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">12</property>
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<child>
<object class="GtkBox" id="vbox28">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label224">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">Display</property>
<property name="xalign">0</property>
<attributes>
@@ -725,107 +745,120 @@
<child>
<object class="GtkAlignment" id="alignment34">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="left_padding">12</property>
+ <property name="can-focus">False</property>
+ <property name="left-padding">12</property>
<child>
+ <!-- n-columns=3 n-rows=4 -->
<object class="GtkGrid" id="table25">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">12</property>
- <property name="column_homogeneous">True</property>
+ <property name="can-focus">False</property>
+ <property name="row-spacing">6</property>
+ <property name="column-spacing">12</property>
+ <property name="column-homogeneous">True</property>
<child>
<object class="GtkComboBox" id="visibility_combo">
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">3</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label232">
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">_Visibility unit:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">visibility_combo</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">visibility_combo</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">3</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="pressure_combo">
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">2</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label231">
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">_Pressure unit:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">pressure_combo</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">pressure_combo</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">2</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="wind_speed_combo">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="temperature_combo">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label228">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">_Wind speed unit:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">wind_speed_combo</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">wind_speed_combo</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label227">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">_Temperature unit:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">temperature_combo</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">temperature_combo</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
</packing>
</child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
</object>
</child>
</object>
@@ -850,12 +883,107 @@
<child type="tab">
<object class="GtkLabel" id="label223">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">Weather</property>
</object>
<packing>
<property name="position">2</property>
- <property name="tab_fill">False</property>
+ <property name="tab-fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="vbox-calendar">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkBox" id="calendar-options">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label-calendar">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Evolution Calendar Integration</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="vbox-calendar-options">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="show_calendar_events_check">
+ <property name="label" translatable="yes">Show calendar _events</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_tasks_check">
+ <property name="label" translatable="yes">Show _tasks</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label-calendar-tab">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Calendar</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab-fill">False</property>
</packing>
</child>
</object>
@@ -872,9 +1000,6 @@
<action-widget response="0">time-settings-button</action-widget>
<action-widget response="-7">prefs-close-button</action-widget>
</action-widgets>
- <child>
- <placeholder/>
- </child>
</object>
<object class="GtkListStore" id="liststore1">
<columns>
@@ -905,28 +1030,28 @@
</data>
</object>
<object class="GtkDialog" id="edit-location-window">
- <property name="can_focus">False</property>
- <property name="border_width">5</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">5</property>
<property name="resizable">False</property>
- <property name="type_hint">dialog</property>
+ <property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<child>
<object class="GtkButton" id="edit-location-cancel-button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="receives-default">False</property>
<property name="image">image3</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -938,11 +1063,11 @@
<object class="GtkButton" id="edit-location-ok-button">
<property name="label" translatable="yes">_OK</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="receives-default">False</property>
<property name="image">image2</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -954,37 +1079,38 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="pack_type">end</property>
+ <property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
+ <!-- n-columns=3 n-rows=3 -->
<object class="GtkGrid" id="table26">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">6</property>
+ <property name="can-focus">False</property>
+ <property name="row-spacing">6</property>
+ <property name="column-spacing">6</property>
<child>
<object class="GtkBox" id="vbox34">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow12">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="vscrollbar_policy">never</property>
+ <property name="can-focus">True</property>
+ <property name="hscrollbar-policy">never</property>
+ <property name="vscrollbar-policy">never</property>
<child>
<object class="GtkViewport" id="viewport3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="shadow_type">none</property>
+ <property name="can-focus">False</property>
+ <property name="shadow-type">none</property>
<child>
<object class="GtkImage" id="image30">
- <property name="can_focus">False</property>
- <property name="icon_name">image-missing</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">image-missing</property>
</object>
</child>
</object>
@@ -997,102 +1123,103 @@
</packing>
</child>
<child>
+ <!-- n-columns=4 n-rows=5 -->
<object class="GtkGrid" id="table27">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">5</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">5</property>
<property name="orientation">vertical</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">6</property>
+ <property name="row-spacing">6</property>
+ <property name="column-spacing">6</property>
<child>
<object class="GtkLabel" id="label243">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="vexpand">True</property>
<property name="label" translatable="yes" comments="Languages that have a single word that translates as either &quot;state&quot; or &quot;province&quot; should use that instead of &quot;region&quot;.">&lt;small&gt;&lt;i&gt;Type a city, region, or country name and then select a match from the pop-up.&lt;/i&gt;&lt;/small&gt;</property>
- <property name="use_markup">True</property>
+ <property name="use-markup">True</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
<property name="width">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="edit-location-name-box">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
<property name="width">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="edit-location-timezone-box">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">2</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">2</property>
<property name="width">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="timezone-label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">_Timezone:</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">2</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="location-name-label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">_Location Name:</property>
- <property name="use_underline">True</property>
+ <property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="edit-location-latitude-entry">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="can-focus">True</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
- <property name="invisible_char">•</property>
+ <property name="invisible-char">•</property>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">3</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="edit-location-latitude-combo">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="model">liststore2</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext2"/>
@@ -1102,42 +1229,42 @@
</child>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">3</property>
+ <property name="left-attach">2</property>
+ <property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label240">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="valign">center</property>
<property name="label" translatable="yes">&lt;i&gt;(optional)&lt;/i&gt;</property>
- <property name="use_markup">True</property>
+ <property name="use-markup">True</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">3</property>
- <property name="top_attach">3</property>
+ <property name="left-attach">3</property>
+ <property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label239">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="valign">center</property>
<property name="label" translatable="yes">&lt;i&gt;(optional)&lt;/i&gt;</property>
- <property name="use_markup">True</property>
+ <property name="use-markup">True</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">3</property>
- <property name="top_attach">4</property>
+ <property name="left-attach">3</property>
+ <property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="edit-location-longitude-combo">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="model">liststore1</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
@@ -1147,51 +1274,51 @@
</child>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">4</property>
+ <property name="left-attach">2</property>
+ <property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="edit-location-longitude-entry">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="can-focus">True</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
- <property name="invisible_char">•</property>
+ <property name="invisible-char">•</property>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="top_attach">4</property>
+ <property name="left-attach">1</property>
+ <property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label238">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">L_ongitude:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">edit-location-longitude-entry</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">edit-location-longitude-entry</property>
<property name="xalign">0</property>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">4</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkBox" id="vbox35">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkLabel" id="label237">
<property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="can-focus">False</property>
<property name="label" translatable="yes">L_atitude:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">edit-location-latitude-entry</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">edit-location-latitude-entry</property>
<property name="xalign">0</property>
</object>
<packing>
@@ -1202,8 +1329,8 @@
</child>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">3</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">3</property>
</packing>
</child>
<child>
@@ -1218,10 +1345,34 @@
</child>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
</packing>
</child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
</object>
<packing>
<property name="expand">False</property>
@@ -1235,8 +1386,5 @@
<action-widget response="-6">edit-location-cancel-button</action-widget>
<action-widget response="-5">edit-location-ok-button</action-widget>
</action-widgets>
- <child>
- <placeholder/>
- </child>
</object>
</interface>
diff --git a/applets/clock/org.mate.panel.applet.clock.gschema.xml.in b/applets/clock/org.mate.panel.applet.clock.gschema.xml.in
index ee1b6902..ad822e46 100644
--- a/applets/clock/org.mate.panel.applet.clock.gschema.xml.in
+++ b/applets/clock/org.mate.panel.applet.clock.gschema.xml.in
@@ -84,5 +84,15 @@
<summary>Speed unit</summary>
<description>The unit to use when showing wind speed.</description>
</key>
+ <key name="show-calendar-events" type="b">
+ <default>true</default>
+ <summary>Show calendar events</summary>
+ <description>If true, display calendar events from Evolution in the calendar window.</description>
+ </key>
+ <key name="show-tasks" type="b">
+ <default>false</default>
+ <summary>Show tasks</summary>
+ <description>If true, display tasks from Evolution in the calendar window.</description>
+ </key>
</schema>
</schemalist>
diff --git a/applets/clock/system-timezone.c b/applets/clock/system-timezone.c
index 199756e0..a6f9c75d 100644
--- a/applets/clock/system-timezone.c
+++ b/applets/clock/system-timezone.c
@@ -213,8 +213,7 @@ system_timezone_constructor (GType type,
g_object_unref (file);
if (priv->monitors[i])
- g_signal_connect (G_OBJECT (priv->monitors[i]),
- "changed",
+ g_signal_connect (priv->monitors [i], "changed",
G_CALLBACK (system_timezone_monitor_changed),
obj);
}
@@ -234,13 +233,11 @@ system_timezone_finalize (GObject *obj)
systz = SYSTEM_TIMEZONE (obj);
priv = system_timezone_get_instance_private (systz);
- g_free (priv->tz);
- g_free (priv->env_tz);
+ g_clear_pointer (&priv->tz, g_free);
+ g_clear_pointer (&priv->env_tz, g_free);
for (i = 0; i < CHECK_NB; i++) {
- if (priv->monitors[i])
- g_object_unref (priv->monitors[i]);
- priv->monitors[i] = NULL;
+ g_clear_object (&priv->monitors[i]);
}
G_OBJECT_CLASS (system_timezone_parent_class)->finalize (obj);
@@ -283,7 +280,6 @@ system_timezone_monitor_changed (GFileMonitor *handle,
g_free (new_tz);
}
-
/*
* Code to deal with the system timezone on all distros.
* There's no dependency on the SystemTimezone GObject here.
@@ -372,7 +368,6 @@ system_timezone_write_etc_timezone (const char *tz,
return retval;
}
-
/* Read a file that looks like a key-file (but there's no need for groups)
* and get the last value for a specific key */
static char *
@@ -710,7 +705,6 @@ recursive_compare (struct stat *localtime_stat,
return NULL;
}
-
static gboolean
files_are_identical_inode (struct stat *a_stat,
struct stat *b_stat,
@@ -721,7 +715,6 @@ files_are_identical_inode (struct stat *a_stat,
return (a_stat->st_ino == b_stat->st_ino);
}
-
/* Determine if /etc/localtime is a hard link to some file, by looking at
* the inodes */
static char *
diff --git a/applets/clock/system-timezone.h b/applets/clock/system-timezone.h
index 0ede8976..8a2c22ef 100644
--- a/applets/clock/system-timezone.h
+++ b/applets/clock/system-timezone.h
@@ -35,7 +35,6 @@ extern "C" {
#define SYSTEM_ZONEINFODIR "/usr/share/zoneinfo"
#endif
-
#define SYSTEM_TIMEZONE_TYPE (system_timezone_get_type ())
#define SYSTEM_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SYSTEM_TIMEZONE_TYPE, SystemTimezone))
#define SYSTEM_TIMEZONE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), SYSTEM_TIMEZONE_TYPE, SystemTimezoneClass))
diff --git a/applets/fish/Makefile.am b/applets/fish/Makefile.am
index 814d5d8c..604983a3 100644
--- a/applets/fish/Makefile.am
+++ b/applets/fish/Makefile.am
@@ -53,9 +53,9 @@ $(applet_in_files): $(applet_in_files).in Makefile
$(applet_DATA): $(applet_in_files)
$(AM_V_GEN) $(MSGFMT) --desktop --keyword= --keyword=Name --keyword=Description --template $< -d $(top_srcdir)/po -o $@
+service_in_files = org.mate.panel.applet.FishAppletFactory.service.in
if !FISH_INPROCESS
servicedir = $(datadir)/dbus-1/services
-service_in_files = org.mate.panel.applet.FishAppletFactory.service.in
service_DATA = $(service_in_files:.service.in=.service)
org.mate.panel.applet.FishAppletFactory.service: $(service_in_files)
diff --git a/applets/fish/fish.c b/applets/fish/fish.c
index ac6cdcb8..e0898334 100644
--- a/applets/fish/fish.c
+++ b/applets/fish/fish.c
@@ -115,7 +115,6 @@ typedef struct {
MatePanelAppletClass klass;
} FishAppletClass;
-
static gboolean load_fish_image (FishApplet *fish);
static void update_pixmap (FishApplet *fish);
static void something_fishy_going_on (FishApplet *fish, const char *message);
@@ -502,7 +501,7 @@ static void display_preferences_dialog(GtkAction* action, FishApplet* fish)
NULL /* label_post */,
FISH_ROTATE_KEY /* key */);
- g_signal_connect (fish->preferences_dialog, "delete_event",
+ g_signal_connect (fish->preferences_dialog, "delete-event",
G_CALLBACK (delete_event), fish);
g_signal_connect (fish->preferences_dialog, "response",
G_CALLBACK (handle_response), fish);
@@ -856,7 +855,7 @@ static void display_fortune_dialog(FishApplet* fish)
gtk_dialog_set_default_response (
GTK_DIALOG (fish->fortune_dialog), GTK_RESPONSE_CLOSE);
- g_signal_connect (fish->fortune_dialog, "delete_event",
+ g_signal_connect (fish->fortune_dialog, "delete-event",
G_CALLBACK (delete_event), fish);
g_signal_connect (fish->fortune_dialog, "response",
G_CALLBACK (handle_fortune_response), fish);
@@ -938,8 +937,7 @@ static void display_fortune_dialog(FishApplet* fish)
clear_fortune_text (fish);
- screen = gtk_widget_get_screen (GTK_WIDGET (fish));
- display = gdk_screen_get_display (screen);
+ display = gtk_widget_get_display (GTK_WIDGET (fish));
display_name = g_strdup (gdk_display_get_name (display));
g_spawn_async_with_pipes (NULL, /* working directory */
argv,
@@ -1661,15 +1659,15 @@ static void setup_fish_widget(FishApplet* fish)
GDK_LEAVE_NOTIFY_MASK |
GDK_BUTTON_RELEASE_MASK);
- g_signal_connect_swapped (widget, "enter_notify_event",
+ g_signal_connect_swapped (widget, "enter-notify-event",
G_CALLBACK (fish_enter_notify), fish);
- g_signal_connect_swapped (widget, "leave_notify_event",
+ g_signal_connect_swapped (widget, "leave-notify-event",
G_CALLBACK (fish_leave_notify), fish);
- g_signal_connect_swapped (widget, "button_release_event",
+ g_signal_connect_swapped (widget, "button-release-event",
G_CALLBACK (handle_button_release), fish);
gtk_widget_add_events (fish->drawing_area, GDK_BUTTON_RELEASE_MASK);
- g_signal_connect_swapped (fish->drawing_area, "button_release_event",
+ g_signal_connect_swapped (fish->drawing_area, "button-release-event",
G_CALLBACK (handle_button_release), fish);
load_fish_image (fish);
@@ -1681,7 +1679,7 @@ static void setup_fish_widget(FishApplet* fish)
set_tooltip (fish);
set_ally_name_desc (GTK_WIDGET (fish), fish);
- g_signal_connect (fish, "key_press_event",
+ g_signal_connect (fish, "key-press-event",
G_CALLBACK (handle_keypress), fish);
gtk_widget_show_all (widget);
@@ -1779,28 +1777,14 @@ static void fish_applet_dispose (GObject *object)
fish);
if (fish->timeout)
- {
g_source_remove (fish->timeout);
- }
-
fish->timeout = 0;
- if (fish->settings)
- g_object_unref (fish->settings);
- fish->settings = NULL;
-
- if (fish->lockdown_settings)
- g_object_unref (fish->lockdown_settings);
- fish->lockdown_settings = NULL;
-
- g_free (fish->name);
- fish->name = NULL;
-
- g_free (fish->image);
- fish->image = NULL;
-
- g_free (fish->command);
- fish->command = NULL;
+ g_clear_object (&fish->settings);
+ g_clear_object (&fish->lockdown_settings);
+ g_clear_pointer (&fish->name, g_free);
+ g_clear_pointer (&fish->image, g_free);
+ g_clear_pointer (&fish->command, g_free);
if (fish->surface)
cairo_surface_destroy (fish->surface);
@@ -1808,9 +1792,7 @@ static void fish_applet_dispose (GObject *object)
fish->surface_width = 0;
fish->surface_height = 0;
- if (fish->pixbuf)
- g_object_unref (fish->pixbuf);
- fish->pixbuf = NULL;
+ g_clear_object (&fish->pixbuf);
if (fish->preferences_dialog)
gtk_widget_destroy (fish->preferences_dialog);
diff --git a/applets/notification_area/Makefile.am b/applets/notification_area/Makefile.am
index 365cdf5d..8f6ec8d3 100644
--- a/applets/notification_area/Makefile.am
+++ b/applets/notification_area/Makefile.am
@@ -83,9 +83,9 @@ $(applet_in_files): $(applet_in_files).in Makefile
-e "s|\@VERSION\@|$(PACKAGE_VERSION)|" \
$< > $@
+service_in_files = org.mate.panel.applet.NotificationAreaAppletFactory.service.in
if !NOTIFICATION_AREA_INPROCESS
servicedir = $(datadir)/dbus-1/services
-service_in_files = org.mate.panel.applet.NotificationAreaAppletFactory.service.in
service_DATA = $(service_in_files:.service.in=.service)
org.mate.panel.applet.NotificationAreaAppletFactory.service: $(service_in_files)
diff --git a/applets/notification_area/main.c b/applets/notification_area/main.c
index 2f0dafdb..71221b65 100644
--- a/applets/notification_area/main.c
+++ b/applets/notification_area/main.c
@@ -73,7 +73,6 @@ static void (*parent_class_style_updated) (GtkWidget *widget);
static void (*parent_class_change_background)(MatePanelApplet* panel_applet, MatePanelAppletBackgroundType type, GdkRGBA* color, cairo_pattern_t* pattern);
static void (*parent_class_change_orient)(MatePanelApplet *panel_applet, MatePanelAppletOrient orient);
-
#ifdef PROVIDE_WATCHER_SERVICE
/* Quite dirty way of providing the org.kde.StatusNotifierWatcher service
* ourselves, in case the session doesn't already */
@@ -103,7 +102,6 @@ sn_watcher_service_ref (void)
}
#endif
-
static GtkOrientation
get_gtk_orientation_from_applet_orient (MatePanelAppletOrient orient)
{
@@ -141,7 +139,7 @@ static void
setup_gsettings (NaTrayApplet *applet)
{
applet->priv->settings = mate_panel_applet_settings_new (MATE_PANEL_APPLET (applet), NA_TRAY_SCHEMA);
- g_signal_connect (applet->priv->settings, "changed::" KEY_MIN_ICON_SIZE, G_CALLBACK (gsettings_changed_min_icon_size), applet);
+ g_signal_connect_object (G_OBJECT(applet->priv->settings), "changed::" KEY_MIN_ICON_SIZE, G_CALLBACK (gsettings_changed_min_icon_size), applet, 0);
}
static void
@@ -201,7 +199,7 @@ ensure_prefs_window_is_created (NaTrayApplet *applet)
g_signal_connect_swapped (applet->priv->dialog->preferences_dialog, "response",
G_CALLBACK (na_preferences_dialog_response), applet);
- g_signal_connect (G_OBJECT (applet->priv->dialog->preferences_dialog), "delete_event",
+ g_signal_connect (applet->priv->dialog->preferences_dialog, "delete-event",
G_CALLBACK (na_preferences_dialog_hide_event), applet);
}
@@ -300,7 +298,6 @@ static const GtkActionEntry menu_actions [] = {
G_CALLBACK (about_cb) }
};
-
static void
na_tray_applet_realize (GtkWidget *widget)
{
@@ -497,11 +494,6 @@ applet_factory (MatePanelApplet *applet,
strcmp (iid, "SystemTrayApplet") == 0))
return FALSE;
- if (!GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (applet)))) {
- g_warning ("Notification area only works on X");
- return FALSE;
- }
-
#ifndef NOTIFICATION_AREA_INPROCESS
gtk_window_set_default_icon_name (NOTIFICATION_AREA_ICON);
#endif
diff --git a/applets/notification_area/main.h b/applets/notification_area/main.h
index 32041cdf..a4325ba5 100644
--- a/applets/notification_area/main.h
+++ b/applets/notification_area/main.h
@@ -17,7 +17,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-
#ifndef __NA_TRAY_APPLET_H__
#define __NA_TRAY_APPLET_H__
diff --git a/applets/notification_area/na-grid.c b/applets/notification_area/na-grid.c
index dbd95d50..9a4c45d9 100644
--- a/applets/notification_area/na-grid.c
+++ b/applets/notification_area/na-grid.c
@@ -316,21 +316,31 @@ na_grid_realize (GtkWidget *widget)
{
NaGrid *self = NA_GRID (widget);
GdkScreen *screen;
- GtkOrientation orientation;
- NaHost *tray_host;
+ GdkDisplay *display;
+ GSettings *settings;
GTK_WIDGET_CLASS (na_grid_parent_class)->realize (widget);
+ display = gdk_display_get_default ();
/* Instantiate the hosts now we have a screen */
screen = gtk_widget_get_screen (GTK_WIDGET (self));
- orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (self));
- tray_host = na_tray_new_for_screen (screen, orientation);
- g_object_bind_property (self, "orientation",
+ if (GDK_IS_X11_DISPLAY (display))
+ {
+ GtkOrientation orientation;
+ NaHost *tray_host;
+
+ orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (self));
+ tray_host = na_tray_new_for_screen (screen, orientation);
+ g_object_bind_property (self, "orientation",
tray_host, "orientation",
G_BINDING_DEFAULT);
- add_host (self, tray_host);
- add_host (self, sn_host_v0_new ());
+ add_host (self, tray_host);
+ }
+ settings = g_settings_new ("org.mate.panel");
+ if (g_settings_get_boolean (settings, "enable-sni-support"))
+ add_host (self, sn_host_v0_new ());
+ g_object_unref (settings);
}
static void
diff --git a/applets/notification_area/org.mate.panel.NotificationAreaApplet.mate-panel-applet.desktop.in.in b/applets/notification_area/org.mate.panel.NotificationAreaApplet.mate-panel-applet.desktop.in.in
index f7652db6..811348bc 100644
--- a/applets/notification_area/org.mate.panel.NotificationAreaApplet.mate-panel-applet.desktop.in.in
+++ b/applets/notification_area/org.mate.panel.NotificationAreaApplet.mate-panel-applet.desktop.in.in
@@ -11,7 +11,7 @@ Description=Area where notification icons appear
# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
Icon=mate-panel-notification-area
MateComponentId=OAFIID:MATE_NotificationAreaApplet;OAFIID:MATE_SystemTrayApplet;
-Platforms=X11;
+Platforms=X11;Wayland;
X-MATE-Bugzilla-Bugzilla=MATE
X-MATE-Bugzilla-Product=mate-panel
X-MATE-Bugzilla-Component=notification area
diff --git a/applets/notification_area/status-notifier/sn-dbus-menu-item.c b/applets/notification_area/status-notifier/sn-dbus-menu-item.c
index 62f21ca7..07d1957e 100644
--- a/applets/notification_area/status-notifier/sn-dbus-menu-item.c
+++ b/applets/notification_area/status-notifier/sn-dbus-menu-item.c
@@ -16,11 +16,12 @@
*/
#include <config.h>
+#include <libmate-desktop/mate-image-menu-item.h>
#include "sn-dbus-menu-item.h"
static GdkPixbuf *
-pxibuf_new (GVariant *variant)
+pixbuf_new (GVariant *variant)
{
gsize length;
const guchar *data;
@@ -149,7 +150,7 @@ sn_dbus_menu_item_new (GVariant *props)
else if (g_strcmp0 (prop, "icon-name") == 0)
item->icon_name = g_variant_dup_string (value, NULL);
else if (g_strcmp0 (prop, "icon-data") == 0)
- item->icon_data = pxibuf_new (value);
+ item->icon_data = pixbuf_new (value);
else if (g_strcmp0 (prop, "label") == 0)
item->label = g_variant_dup_string (value, NULL);
else if (g_strcmp0 (prop, "shortcut") == 0)
@@ -203,8 +204,8 @@ sn_dbus_menu_item_new (GVariant *props)
cairo_surface_destroy (surface);
}
- item->item = gtk_image_menu_item_new ();
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item->item),
+ item->item = mate_image_menu_item_new ();
+ mate_image_menu_item_set_image (MATE_IMAGE_MENU_ITEM (item->item),
image);
}
@@ -252,7 +253,7 @@ sn_dbus_menu_item_new (GVariant *props)
}
void
-sn_dubs_menu_item_free (gpointer data)
+sn_dbus_menu_item_free (gpointer data)
{
SnDBusMenuItem *item;
@@ -325,7 +326,7 @@ sn_dbus_menu_item_update_props (SnDBusMenuItem *item,
image = NULL;
}
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item->item),
+ mate_image_menu_item_set_image (MATE_IMAGE_MENU_ITEM (item->item),
image);
}
else if (g_strcmp0 (prop, "icon-data") == 0)
@@ -333,7 +334,7 @@ sn_dbus_menu_item_update_props (SnDBusMenuItem *item,
GtkWidget *image;
g_clear_object (&item->icon_data);
- item->icon_data = pxibuf_new (value);
+ item->icon_data = pixbuf_new (value);
if (item->icon_data)
{
@@ -347,7 +348,7 @@ sn_dbus_menu_item_update_props (SnDBusMenuItem *item,
image = NULL;
}
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item->item),
+ mate_image_menu_item_set_image (MATE_IMAGE_MENU_ITEM (item->item),
image);
}
else if (g_strcmp0 (prop, "label") == 0)
@@ -437,18 +438,18 @@ sn_dbus_menu_item_remove_props (SnDBusMenuItem *item,
else if (g_strcmp0 (prop, "icon-name") == 0)
{
g_clear_pointer (&item->icon_name, g_free);
- if (GTK_IS_IMAGE_MENU_ITEM (item->item))
+ if (MATE_IS_IMAGE_MENU_ITEM (item->item))
{
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item->item),
+ mate_image_menu_item_set_image (MATE_IMAGE_MENU_ITEM (item->item),
NULL);
}
}
else if (g_strcmp0 (prop, "icon-data") == 0)
{
g_clear_object (&item->icon_data);
- if (GTK_IS_IMAGE_MENU_ITEM (item->item))
+ if (MATE_IS_IMAGE_MENU_ITEM (item->item))
{
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item->item),
+ mate_image_menu_item_set_image (MATE_IMAGE_MENU_ITEM (item->item),
NULL);
}
}
diff --git a/applets/notification_area/status-notifier/sn-dbus-menu-item.h b/applets/notification_area/status-notifier/sn-dbus-menu-item.h
index c89de5c3..458417f2 100644
--- a/applets/notification_area/status-notifier/sn-dbus-menu-item.h
+++ b/applets/notification_area/status-notifier/sn-dbus-menu-item.h
@@ -16,7 +16,7 @@
*/
#ifndef SN_DBUS_MENU_ITEM_H
-#define SN_DUBS_MENU_ITEM_H
+#define SN_DBUS_MENU_ITEM_H
#include <gtk/gtk.h>
@@ -51,7 +51,7 @@ typedef struct
SnDBusMenuItem *sn_dbus_menu_item_new (GVariant *props);
-void sn_dubs_menu_item_free (gpointer data);
+void sn_dbus_menu_item_free (gpointer data);
void sn_dbus_menu_item_update_props (SnDBusMenuItem *item,
GVariant *props);
diff --git a/applets/notification_area/status-notifier/sn-dbus-menu.c b/applets/notification_area/status-notifier/sn-dbus-menu.c
index 03a95a4b..7b25c63f 100644
--- a/applets/notification_area/status-notifier/sn-dbus-menu.c
+++ b/applets/notification_area/status-notifier/sn-dbus-menu.c
@@ -419,8 +419,8 @@ sn_dbus_menu_finalize (GObject *object)
menu = SN_DBUS_MENU (object);
- g_free (menu->bus_name);
- g_free (menu->object_path);
+ g_clear_pointer (&menu->bus_name, g_free);
+ g_clear_pointer (&menu->object_path, g_free);
G_OBJECT_CLASS (sn_dbus_menu_parent_class)->finalize (object);
}
@@ -485,7 +485,7 @@ sn_dbus_menu_class_init (SnDBusMenuClass *menu_class)
static void
sn_dbus_menu_init (SnDBusMenu *menu)
{
- menu->items = g_hash_table_new_full (NULL, NULL, NULL, sn_dubs_menu_item_free);
+ menu->items = g_hash_table_new_full (NULL, NULL, NULL, sn_dbus_menu_item_free);
menu->cancellable = g_cancellable_new ();
}
diff --git a/applets/notification_area/status-notifier/sn-host-v0.c b/applets/notification_area/status-notifier/sn-host-v0.c
index 64da5da9..4ad6b359 100644
--- a/applets/notification_area/status-notifier/sn-host-v0.c
+++ b/applets/notification_area/status-notifier/sn-host-v0.c
@@ -193,7 +193,6 @@ register_host_cb (GObject *source_object,
GError *error;
SnHostV0 *v0;
gchar **items;
- gint i;
error = NULL;
sn_watcher_v0_gen_call_register_host_finish (SN_WATCHER_V0_GEN (source_object),
@@ -224,7 +223,7 @@ register_host_cb (GObject *source_object,
items = sn_watcher_v0_gen_dup_registered_items (v0->watcher);
if (items) {
- for (i = 0; items[i] != NULL; i++)
+ for (gint i = 0; items[i] != NULL; i++)
add_registered_item (v0, items[i]);
}
diff --git a/applets/notification_area/status-notifier/sn-item-v0.c b/applets/notification_area/status-notifier/sn-item-v0.c
index 3ec4d037..9e66f361 100644
--- a/applets/notification_area/status-notifier/sn-item-v0.c
+++ b/applets/notification_area/status-notifier/sn-item-v0.c
@@ -156,7 +156,7 @@ compare_size (gconstpointer a,
}
static cairo_surface_t *
-get_surface (SnItemV0 *v0,
+get_surface (SnIconPixmap **icon_pixmap,
GtkOrientation orientation,
gint size)
{
@@ -165,10 +165,10 @@ get_surface (SnItemV0 *v0,
SnIconPixmap *pixmap = NULL;
GList *l;
- g_assert (v0->icon_pixmap != NULL && v0->icon_pixmap[0] != NULL);
+ g_assert (icon_pixmap != NULL && icon_pixmap[0] != NULL);
- for (i = 0; v0->icon_pixmap[i] != NULL; i++)
- pixmaps = g_list_prepend (pixmaps, v0->icon_pixmap[i]);
+ for (i = 0; icon_pixmap[i] != NULL; i++)
+ pixmaps = g_list_prepend (pixmaps, icon_pixmap[i]);
pixmaps = g_list_sort_with_data (pixmaps, compare_size,
GUINT_TO_POINTER (orientation));
@@ -234,6 +234,9 @@ get_icon_by_name (const gchar *icon_name,
NULL, GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
}
+#define ICON_NAME_VALID(icon_name) (icon_name && icon_name[0] != '\0')
+#define ICON_PIXMAP_VALID(icon_pixmap) (icon_pixmap && icon_pixmap[0] != NULL)
+
static void
update (SnItemV0 *v0)
{
@@ -241,7 +244,9 @@ update (SnItemV0 *v0)
GtkImage *image;
SnTooltip *tip;
gint icon_size;
- gboolean visible;
+ const gchar *icon_name;
+ SnIconPixmap **icon_pixmap;
+
g_return_if_fail (SN_IS_ITEM_V0 (v0));
image = GTK_IMAGE (v0->image);
@@ -251,23 +256,43 @@ update (SnItemV0 *v0)
else
icon_size = MAX (1, v0->effective_icon_size);
- if (v0->icon_name != NULL && v0->icon_name[0] != '\0')
+ /* If the item requests attention and has a specific icon for it, use that
+ * instead of the default icon. */
+ if (g_strcmp0 (v0->status, "NeedsAttention") == 0 &&
+ (ICON_NAME_VALID (v0->attention_icon_name) ||
+ ICON_PIXMAP_VALID (v0->attention_icon_pixmap)))
+ {
+ icon_name = v0->attention_icon_name;
+ icon_pixmap = v0->attention_icon_pixmap;
+ }
+ else
+ {
+ icon_name = v0->icon_name;
+ icon_pixmap = v0->icon_pixmap;
+ }
+
+ if (ICON_NAME_VALID (icon_name))
{
cairo_surface_t *surface;
gint scale;
scale = gtk_widget_get_scale_factor (GTK_WIDGET (image));
- surface = get_icon_by_name (v0->icon_name, icon_size, scale);
+ surface = get_icon_by_name (icon_name, icon_size, scale);
if (!surface)
{
GdkPixbuf *pixbuf;
/*try to find icons specified by path and filename*/
- pixbuf = gdk_pixbuf_new_from_file (v0->icon_name, NULL);
+ pixbuf = gdk_pixbuf_new_from_file (icon_name, NULL);
if (pixbuf && icon_size > 1)
{
/*An icon specified by path and filename may be the wrong size for the tray */
- pixbuf = gdk_pixbuf_scale_simple (pixbuf, icon_size-2, icon_size-2, GDK_INTERP_BILINEAR);
+ GdkPixbuf *scaled_pixbuf;
+
+ scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf, icon_size-2, icon_size-2, GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+ pixbuf = scaled_pixbuf;
+
surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
}
if (pixbuf)
@@ -281,11 +306,11 @@ update (SnItemV0 *v0)
gtk_image_set_from_surface (image, surface);
cairo_surface_destroy (surface);
}
- else if (v0->icon_pixmap != NULL && v0->icon_pixmap[0] != NULL)
+ else if (ICON_PIXMAP_VALID (icon_pixmap))
{
cairo_surface_t *surface;
- surface = get_surface (v0,
+ surface = get_surface (icon_pixmap,
gtk_orientable_get_orientation (GTK_ORIENTABLE (v0)),
icon_size);
if (surface != NULL)
@@ -341,7 +366,7 @@ update (SnItemV0 *v0)
/* TODO: hide "Passive" items with a setting? */
/*Special case mate-polkit*/
if (g_strcmp0 (v0->status, "password-dialog") != 0){
- visible = g_strcmp0 (v0->status, "Passive") != 0;
+ gboolean visible = g_strcmp0 (v0->status, "Passive") != 0;
gtk_widget_set_visible (GTK_WIDGET (v0), visible);
}
else
@@ -640,7 +665,7 @@ update_title (GObject *source_object,
v0 = SN_ITEM_V0 (user_data);
- g_clear_pointer (&v0->title, g_free);
+ g_free (v0->title);
v0->title = g_variant_dup_string (variant, NULL);
g_clear_pointer (&variant, g_variant_unref);
@@ -668,7 +693,7 @@ update_icon_name (GObject *source_object,
v0 = SN_ITEM_V0 (user_data);
- g_clear_pointer (&v0->icon_name, g_free);
+ g_free (v0->icon_name);
v0->icon_name = g_variant_dup_string (variant, NULL);
g_clear_pointer (&variant, g_variant_unref);
@@ -719,7 +744,7 @@ update_overlay_icon_name (GObject *source_object,
v0 = SN_ITEM_V0 (user_data);
- g_clear_pointer (&v0->overlay_icon_name, g_free);
+ g_free (v0->overlay_icon_name);
v0->overlay_icon_name = g_variant_dup_string (variant, NULL);
g_clear_pointer (&variant, g_variant_unref);
@@ -770,7 +795,7 @@ update_attention_icon_name (GObject *source_object,
v0 = SN_ITEM_V0 (user_data);
- g_clear_pointer (&v0->attention_icon_name, g_free);
+ g_free (v0->attention_icon_name);
v0->attention_icon_name = g_variant_dup_string (variant, NULL);
g_clear_pointer (&variant, g_variant_unref);
@@ -792,7 +817,7 @@ update_attention_icon_pixmap (GObject *source_object,
v0 = SN_ITEM_V0 (user_data);
- g_clear_pointer (&v0->attention_icon_pixmap, icon_pixmap_free);
+ icon_pixmap_free (v0->attention_icon_pixmap);
v0->attention_icon_pixmap = icon_pixmap_new (variant);
g_clear_pointer (&variant, g_variant_unref);
@@ -821,7 +846,7 @@ update_tooltip (GObject *source_object,
v0 = SN_ITEM_V0 (user_data);
- g_clear_pointer (&v0->tooltip, sn_tooltip_free);
+ sn_tooltip_free (v0->tooltip);
v0->tooltip = sn_tooltip_new (variant);
g_clear_pointer (&variant, g_variant_unref);
diff --git a/applets/notification_area/status-notifier/sn-item.c b/applets/notification_area/status-notifier/sn-item.c
index 3dd0d605..9677e67e 100644
--- a/applets/notification_area/status-notifier/sn-item.c
+++ b/applets/notification_area/status-notifier/sn-item.c
@@ -358,6 +358,7 @@ sn_item_ready (SnItem *item)
priv = SN_ITEM (item)->priv;
priv->menu = sn_dbus_menu_new (priv->bus_name, menu);
g_object_ref_sink (priv->menu);
+ gtk_button_set_always_show_image (GTK_BUTTON (item),TRUE);
}
static const gchar *
diff --git a/applets/notification_area/system-tray/fixedtip.c b/applets/notification_area/system-tray/fixedtip.c
index 180cbdd7..528e1767 100644
--- a/applets/notification_area/system-tray/fixedtip.c
+++ b/applets/notification_area/system-tray/fixedtip.c
@@ -128,7 +128,7 @@ na_fixed_tip_init (NaFixedTip *fixedtip)
gtk_widget_add_events (GTK_WIDGET (fixedtip), GDK_BUTTON_PRESS_MASK);
- g_signal_connect (fixedtip, "button_press_event",
+ g_signal_connect (fixedtip, "button-press-event",
G_CALLBACK (button_press_handler), NULL);
fixedtip->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
diff --git a/applets/notification_area/system-tray/na-tray-child.c b/applets/notification_area/system-tray/na-tray-child.c
index cd7c305a..9eb03e22 100644
--- a/applets/notification_area/system-tray/na-tray-child.c
+++ b/applets/notification_area/system-tray/na-tray-child.c
@@ -546,7 +546,7 @@ na_tray_child_set_composited (NaTrayChild *child,
if (child->composited == composited)
return;
- child->composited = composited;
+ child->composited = (composited != FALSE);
if (gtk_widget_get_realized (GTK_WIDGET (child)))
gdk_window_set_composited (gtk_widget_get_window (GTK_WIDGET (child)),
composited);
diff --git a/applets/notification_area/system-tray/na-tray-manager.c b/applets/notification_area/system-tray/na-tray-manager.c
index f001c653..2bb679d8 100644
--- a/applets/notification_area/system-tray/na-tray-manager.c
+++ b/applets/notification_area/system-tray/na-tray-manager.c
@@ -148,7 +148,7 @@ na_tray_manager_class_init (NaTrayManagerClass *klass)
G_PARAM_STATIC_BLURB));
manager_signals[TRAY_ICON_ADDED] =
- g_signal_new ("tray_icon_added",
+ g_signal_new ("tray-icon-added",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added),
@@ -158,7 +158,7 @@ na_tray_manager_class_init (NaTrayManagerClass *klass)
GTK_TYPE_SOCKET);
manager_signals[TRAY_ICON_REMOVED] =
- g_signal_new ("tray_icon_removed",
+ g_signal_new ("tray-icon-removed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed),
@@ -167,7 +167,7 @@ na_tray_manager_class_init (NaTrayManagerClass *klass)
G_TYPE_NONE, 1,
GTK_TYPE_SOCKET);
manager_signals[MESSAGE_SENT] =
- g_signal_new ("message_sent",
+ g_signal_new ("message-sent",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NaTrayManagerClass, message_sent),
@@ -179,7 +179,7 @@ na_tray_manager_class_init (NaTrayManagerClass *klass)
G_TYPE_LONG,
G_TYPE_LONG);
manager_signals[MESSAGE_CANCELLED] =
- g_signal_new ("message_cancelled",
+ g_signal_new ("message-cancelled",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled),
@@ -306,7 +306,7 @@ na_tray_manager_handle_dock_request (NaTrayManager *manager,
return;
}
- g_signal_connect (child, "plug_removed",
+ g_signal_connect (child, "plug-removed",
G_CALLBACK (na_tray_manager_plug_removed), manager);
gtk_socket_add_id (GTK_SOCKET (child), icon_window);
@@ -537,7 +537,6 @@ na_tray_manager_unmanage (NaTrayManager *manager)
{
#ifdef GDK_WINDOWING_X11
GdkDisplay *display;
- guint32 timestamp;
GtkWidget *invisible;
GdkWindow *window;
@@ -556,7 +555,7 @@ na_tray_manager_unmanage (NaTrayManager *manager)
if (gdk_selection_owner_get_for_display (display, manager->selection_atom) ==
window)
{
- timestamp = gdk_x11_get_server_time (window);
+ guint32 timestamp = gdk_x11_get_server_time (window);
gdk_selection_owner_set_for_display (display,
NULL,
manager->selection_atom,
diff --git a/applets/notification_area/system-tray/na-tray.c b/applets/notification_area/system-tray/na-tray.c
index fee75dc8..8e4c3068 100644
--- a/applets/notification_area/system-tray/na-tray.c
+++ b/applets/notification_area/system-tray/na-tray.c
@@ -161,13 +161,9 @@ static void
icon_tip_buffer_free (gpointer data,
gpointer userdata)
{
- IconTipBuffer *buffer;
-
- buffer = data;
+ IconTipBuffer *buffer = data;
g_free (buffer->text);
- buffer->text = NULL;
-
g_free (buffer);
}
@@ -456,16 +452,16 @@ na_tray_constructor (GType type,
{
trays_screens [screen_number].tray_manager = tray_manager;
- g_signal_connect (tray_manager, "tray_icon_added",
+ g_signal_connect (tray_manager, "tray-icon-added",
G_CALLBACK (tray_added),
&trays_screens [screen_number]);
- g_signal_connect (tray_manager, "tray_icon_removed",
+ g_signal_connect (tray_manager, "tray-icon-removed",
G_CALLBACK (tray_removed),
&trays_screens [screen_number]);
- g_signal_connect (tray_manager, "message_sent",
+ g_signal_connect (tray_manager, "message-sent",
G_CALLBACK (message_sent),
&trays_screens [screen_number]);
- g_signal_connect (tray_manager, "message_cancelled",
+ g_signal_connect (tray_manager, "message-cancelled",
G_CALLBACK (message_cancelled),
&trays_screens [screen_number]);
diff --git a/applets/notification_area/testtray.c b/applets/notification_area/testtray.c
index b7d884f4..15252b71 100644
--- a/applets/notification_area/testtray.c
+++ b/applets/notification_area/testtray.c
@@ -86,12 +86,16 @@ tray_removed_cb (GtkContainer *box, GtkWidget *icon, TrayData *data)
static void orientation_changed_cb (GtkComboBox *combo, TrayData *data)
{
- GtkOrientation orientation = (GtkOrientation) gtk_combo_box_get_active (combo);
-
- g_print ("[Screen %u tray %p] Setting orientation to \"%s\"\n",
- data->screen_num, data->traybox, orientation == 0 ? "horizontal" : "vertical");
-
- gtk_orientable_set_orientation (GTK_ORIENTABLE (data->traybox), orientation);
+ gint active;
+
+ if ((active = gtk_combo_box_get_active (combo)) != -1) {
+ GtkOrientation orientation = (GtkOrientation) active;
+ g_print ("[Screen %u tray %p] Setting orientation to \"%s\"\n",
+ data->screen_num,
+ data->traybox,
+ orientation == GTK_ORIENTATION_HORIZONTAL ? "horizontal" : "vertical");
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (data->traybox), orientation);
+ }
}
static void
diff --git a/applets/wncklet/Makefile.am b/applets/wncklet/Makefile.am
index ed442671..137e471b 100644
--- a/applets/wncklet/Makefile.am
+++ b/applets/wncklet/Makefile.am
@@ -78,9 +78,9 @@ $(applet_in_files): $(applet_in_files).in Makefile
$(applet_DATA): $(applet_in_files)
$(AM_V_GEN) $(MSGFMT) --desktop --keyword= --keyword=Name --keyword=Description --template $< -d $(top_srcdir)/po -o $@
+service_in_files = org.mate.panel.applet.WnckletFactory.service.in
if !WNCKLET_INPROCESS
servicedir = $(datadir)/dbus-1/services
-service_in_files = org.mate.panel.applet.WnckletFactory.service.in
service_DATA = $(service_in_files:.service.in=.service)
org.mate.panel.applet.WnckletFactory.service: $(service_in_files)
@@ -107,16 +107,10 @@ BUILT_SOURCES = \
wncklet-resources.c \
wncklet-resources.h
-if HAVE_WINDOW_PREVIEWS
wncklet_gschemas_in = \
org.mate.panel.applet.window-list.gschema.xml.in \
org.mate.panel.applet.window-list-previews.gschema.xml.in \
org.mate.panel.applet.workspace-switcher.gschema.xml.in
-else
-wncklet_gschemas_in = \
- org.mate.panel.applet.window-list.gschema.xml.in \
- org.mate.panel.applet.workspace-switcher.gschema.xml.in
-endif
gsettings_SCHEMAS = $(wncklet_gschemas_in:.xml.in=.xml)
@GSETTINGS_RULES@
diff --git a/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in b/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in
index a02301f5..93f32b46 100644
--- a/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in
+++ b/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in
@@ -25,5 +25,10 @@
<summary>Whether to enable mouse scrolling in the switch window list</summary>
<description>If true, enable mouse scrolling in window list, otherwise disable mouse scrolling in window list.</description>
</key>
+ <key name="middle-click-close" type="b">
+ <default>true</default>
+ <summary>Close window on middle mouse click</summary>
+ <description>If true, then clicking the middle mouse button over a taskbar item will close the window.</description>
+ </key>
</schema>
</schemalist>
diff --git a/applets/wncklet/showdesktop.c b/applets/wncklet/showdesktop.c
index aabc1a63..190077f1 100644
--- a/applets/wncklet/showdesktop.c
+++ b/applets/wncklet/showdesktop.c
@@ -43,7 +43,6 @@
#define TIMEOUT_ACTIVATE_SECONDS 1
#define SHOW_DESKTOP_ICON "user-desktop"
-
typedef struct {
/* widgets */
GtkWidget* applet;
@@ -316,7 +315,7 @@ static gboolean do_not_eat_button_press(GtkWidget* widget, GdkEventButton* event
{
if (event->button != 1)
{
- g_signal_stop_emission_by_name(widget, "button_press_event");
+ g_signal_stop_emission_by_name(widget, "button-press-event");
}
return FALSE;
@@ -371,15 +370,20 @@ static void show_desktop_applet_realized(MatePanelApplet* applet, gpointer data)
#ifdef HAVE_X11
if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
- sdd->wnck_screen = wnck_screen_get(gdk_x11_screen_get_screen_number (screen));
+ {
+ sdd->wnck_screen = wnck_screen_get (gdk_x11_screen_get_screen_number (screen));
+ if (sdd->wnck_screen != NULL)
+ wncklet_connect_while_alive (sdd->wnck_screen,
+ "showing_desktop_changed",
+ G_CALLBACK (show_desktop_changed_callback),
+ sdd,
+ sdd->applet);
+ else
+ g_warning ("Could not get WnckScreen!");
+ }
#endif /* HAVE_X11 */
- if (sdd->wnck_screen != NULL)
- wncklet_connect_while_alive(sdd->wnck_screen, "showing_desktop_changed", G_CALLBACK(show_desktop_changed_callback), sdd, sdd->applet);
- else
- g_warning("Could not get WnckScreen!");
-
- show_desktop_changed_callback(sdd->wnck_screen, sdd);
+ show_desktop_changed_callback (sdd->wnck_screen, sdd);
sdd->icon_theme = gtk_icon_theme_get_for_screen (screen);
wncklet_connect_while_alive(sdd->icon_theme, "changed", G_CALLBACK(theme_changed_callback), sdd, sdd->applet);
@@ -422,7 +426,9 @@ gboolean show_desktop_applet_fill(MatePanelApplet* applet)
sdd->size = mate_panel_applet_get_size(MATE_PANEL_APPLET(sdd->applet));
- g_signal_connect(G_OBJECT(sdd->applet), "realize", G_CALLBACK(show_desktop_applet_realized), sdd);
+ g_signal_connect (sdd->applet, "realize",
+ G_CALLBACK (show_desktop_applet_realized),
+ sdd);
sdd->button = gtk_toggle_button_new ();
@@ -442,21 +448,29 @@ gboolean show_desktop_applet_fill(MatePanelApplet* applet)
atk_obj = gtk_widget_get_accessible(sdd->button);
atk_object_set_name (atk_obj, _("Show Desktop Button"));
- g_signal_connect(G_OBJECT(sdd->button), "button_press_event", G_CALLBACK(do_not_eat_button_press), NULL);
+ g_signal_connect (sdd->button, "button-press-event",
+ G_CALLBACK(do_not_eat_button_press),
+ NULL);
- g_signal_connect(G_OBJECT(sdd->button), "toggled", G_CALLBACK(button_toggled_callback), sdd);
+ g_signal_connect (sdd->button, "toggled",
+ G_CALLBACK (button_toggled_callback),
+ sdd);
gtk_container_set_border_width(GTK_CONTAINER(sdd->button), 0);
gtk_container_add(GTK_CONTAINER(sdd->button), sdd->image);
gtk_container_add(GTK_CONTAINER(sdd->applet), sdd->button);
- g_signal_connect (G_OBJECT(sdd->button), "size_allocate", G_CALLBACK(button_size_allocated), sdd);
+ g_signal_connect (sdd->button, "size-allocate",
+ G_CALLBACK (button_size_allocated),
+ sdd);
/* FIXME: Update this comment. */
/* we have to bind change_orient before we do applet_widget_add
since we need to get an initial change_orient signal to set our
initial oriantation, and we get that during the _add call */
- g_signal_connect(G_OBJECT (sdd->applet), "change_orient", G_CALLBACK (applet_change_orient), sdd);
+ g_signal_connect (sdd->applet, "change-orient",
+ G_CALLBACK (applet_change_orient),
+ sdd);
action_group = gtk_action_group_new("ShowDesktop Applet Actions");
gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE);
@@ -466,12 +480,18 @@ gboolean show_desktop_applet_fill(MatePanelApplet* applet)
action_group);
g_object_unref(action_group);
- g_signal_connect(G_OBJECT(sdd->applet), "destroy", G_CALLBACK(applet_destroyed), sdd);
+ g_signal_connect (sdd->applet, "destroy",
+ G_CALLBACK (applet_destroyed),
+ sdd);
gtk_drag_dest_set(GTK_WIDGET(sdd->button), 0, NULL, 0, 0);
- g_signal_connect(G_OBJECT(sdd->button), "drag_motion", G_CALLBACK (button_drag_motion), sdd);
- g_signal_connect(G_OBJECT(sdd->button), "drag_leave", G_CALLBACK (button_drag_leave), sdd);
+ g_signal_connect (sdd->button, "drag-motion",
+ G_CALLBACK (button_drag_motion),
+ sdd);
+ g_signal_connect (sdd->button, "drag-leave",
+ G_CALLBACK (button_drag_leave),
+ sdd);
gtk_widget_show_all(sdd->applet);
@@ -552,7 +572,9 @@ static void button_toggled_callback(GtkWidget* button, ShowDesktopData* sdd)
g_object_add_weak_pointer(G_OBJECT(dialog), (gpointer) &dialog);
- g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK(gtk_widget_destroy),
+ NULL);
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
gtk_window_set_screen(GTK_WINDOW(dialog), gtk_widget_get_screen(button));
@@ -573,7 +595,7 @@ static void show_desktop_changed_callback(WnckScreen* screen, ShowDesktopData* s
{
#ifdef HAVE_X11
if (sdd->wnck_screen != NULL)
- sdd->showing_desktop = wnck_screen_get_showing_desktop(sdd->wnck_screen);
+ sdd->showing_desktop = (wnck_screen_get_showing_desktop(sdd->wnck_screen) != FALSE);
#endif /* HAVE_X11 */
update_button_state (sdd);
diff --git a/applets/wncklet/showdesktop.h b/applets/wncklet/showdesktop.h
index e82ed713..7aa7e60d 100644
--- a/applets/wncklet/showdesktop.h
+++ b/applets/wncklet/showdesktop.h
@@ -39,4 +39,3 @@ gboolean show_desktop_applet_fill(MatePanelApplet* applet);
#endif
-
diff --git a/applets/wncklet/wayland-backend.c b/applets/wncklet/wayland-backend.c
index a6f50a77..f10b2d65 100644
--- a/applets/wncklet/wayland-backend.c
+++ b/applets/wncklet/wayland-backend.c
@@ -26,25 +26,45 @@
#endif
#include <gdk/gdkwayland.h>
+#include <gio/gdesktopappinfo.h>
#include "wayland-backend.h"
#include "wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-client.h"
-static const int window_button_width = 140;
+/*shorter than wnck-tasklist due to common use of larger fonts*/
+#define TASKLIST_TEXT_MAX_WIDTH 16
+
+/*In the future this could be changable from the panel-prefs dialog*/
+static const int max_button_width = 180;
+static const int icon_size = 16;
+
+typedef struct
+{
+ GtkWidget *menu;
+ GtkWidget *maximize;
+ GtkWidget *minimize;
+ GtkWidget *on_top;
+ GtkWidget *close;
+} ContextMenu;
typedef struct
{
GtkWidget *list;
GtkWidget *outer_box;
+ ContextMenu *context_menu;
struct zwlr_foreign_toplevel_manager_v1 *manager;
} TasklistManager;
typedef struct
{
GtkWidget *button;
+ GtkWidget *icon;
GtkWidget *label;
struct zwlr_foreign_toplevel_handle_v1 *toplevel;
gboolean active;
+ gboolean maximized;
+ gboolean minimized;
+ gboolean fullscreen;
} ToplevelTask;
static const char *tasklist_manager_key = "tasklist_manager";
@@ -57,6 +77,8 @@ static uint32_t foreign_toplevel_manager_global_version = 0;
static ToplevelTask *toplevel_task_new (TasklistManager *tasklist, struct zwlr_foreign_toplevel_handle_v1 *handle);
+guint buttons, tasklist_width;
+
static void
wl_registry_handle_global (void *_data,
struct wl_registry *registry,
@@ -119,7 +141,7 @@ foreign_toplevel_manager_handle_toplevel (void *data,
{
TasklistManager *tasklist = data;
ToplevelTask *task = toplevel_task_new (tasklist, toplevel);
- gtk_box_pack_start (GTK_BOX (tasklist->list), task->button, TRUE, TRUE, 2);
+ gtk_box_pack_start (GTK_BOX (tasklist->list), task->button, TRUE, TRUE, 0);
}
static void
@@ -161,6 +183,74 @@ tasklist_manager_disconnected_from_widget (TasklistManager *tasklist)
if (tasklist->manager)
zwlr_foreign_toplevel_manager_v1_stop (tasklist->manager);
+
+ if (tasklist->context_menu)
+ {
+ gtk_widget_destroy (tasklist->context_menu->menu);
+ g_free (tasklist->context_menu);
+ tasklist->context_menu = NULL;
+ }
+}
+
+static void
+menu_on_maximize (GtkMenuItem *item, gpointer user_data)
+{
+ ToplevelTask *task = g_object_get_data (G_OBJECT (item), toplevel_task_key);
+ if (task->toplevel) {
+ if (task->maximized) {
+ zwlr_foreign_toplevel_handle_v1_unset_maximized (task->toplevel);
+ } else {
+ zwlr_foreign_toplevel_handle_v1_set_maximized (task->toplevel);
+ }
+ }
+}
+
+static void
+menu_on_minimize (GtkMenuItem *item, gpointer user_data)
+{
+ ToplevelTask *task = g_object_get_data (G_OBJECT (item), toplevel_task_key);
+ if (task->toplevel) {
+ if (task->minimized) {
+ zwlr_foreign_toplevel_handle_v1_unset_minimized (task->toplevel);
+ } else {
+ zwlr_foreign_toplevel_handle_v1_set_minimized (task->toplevel);
+ }
+ }
+}
+
+static void
+menu_on_close (GtkMenuItem *item, gpointer user_data)
+{
+ ToplevelTask *task = g_object_get_data (G_OBJECT (item), toplevel_task_key);
+ if (task->toplevel) {
+ zwlr_foreign_toplevel_handle_v1_close (task->toplevel);
+ }
+}
+
+static ContextMenu *
+context_menu_new ()
+{
+ ContextMenu *menu = g_new0 (ContextMenu, 1);
+ menu->menu = gtk_menu_new ();
+ menu->maximize = gtk_menu_item_new ();
+ menu->minimize = gtk_menu_item_new ();
+ menu->on_top = gtk_check_menu_item_new_with_label ("Always On Top");
+ menu->close = gtk_menu_item_new_with_label ("Close");
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), menu->maximize);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), menu->minimize);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), gtk_separator_menu_item_new ());
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), menu->on_top);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), gtk_separator_menu_item_new ());
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), menu->close);
+
+ gtk_widget_show_all (menu->menu);
+
+ g_signal_connect (menu->maximize, "activate", G_CALLBACK (menu_on_maximize), NULL);
+ g_signal_connect (menu->minimize, "activate", G_CALLBACK (menu_on_minimize), NULL);
+ g_signal_connect (menu->close, "activate", G_CALLBACK (menu_on_close), NULL);
+ gtk_widget_set_sensitive (menu->on_top, FALSE);
+ return menu;
}
static TasklistManager *
@@ -170,7 +260,7 @@ tasklist_manager_new (void)
return NULL;
TasklistManager *tasklist = g_new0 (TasklistManager, 1);
- tasklist->list = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ tasklist->list = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_set_homogeneous (GTK_BOX (tasklist->list), TRUE);
tasklist->outer_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_start (GTK_BOX (tasklist->outer_box), tasklist->list, FALSE, FALSE, 0);
@@ -186,6 +276,7 @@ tasklist_manager_new (void)
tasklist_manager_key,
tasklist,
(GDestroyNotify)tasklist_manager_disconnected_from_widget);
+ tasklist->context_menu = context_menu_new ();
return tasklist;
}
@@ -207,7 +298,27 @@ foreign_toplevel_handle_app_id (void *data,
struct zwlr_foreign_toplevel_handle_v1 *toplevel,
const char *app_id)
{
- /* ignore */
+ ToplevelTask *task = data;
+
+ gchar *app_id_lower = g_utf8_strdown (app_id, -1);
+ gchar *desktop_app_id = g_strdup_printf ("%s.desktop", app_id_lower);
+ GDesktopAppInfo *app_info = g_desktop_app_info_new (desktop_app_id);
+
+ if (app_info) {
+ GIcon *icon = g_app_info_get_icon (G_APP_INFO (app_info));
+ if (icon) {
+ gtk_image_set_from_gicon (GTK_IMAGE (task->icon), icon, GTK_ICON_SIZE_MENU);
+ goto cleanup;
+ }
+ }
+ gtk_image_set_from_icon_name (GTK_IMAGE (task->icon), app_id_lower, GTK_ICON_SIZE_MENU);
+
+cleanup:
+ if (app_info) {
+ g_object_unref (G_OBJECT (app_info));
+ }
+ g_free (app_id_lower);
+ g_free (desktop_app_id);
}
static void
@@ -234,6 +345,9 @@ foreign_toplevel_handle_state (void *data,
ToplevelTask *task = data;
task->active = FALSE;
+ task->maximized = FALSE;
+ task->minimized = FALSE;
+ task->fullscreen = FALSE;
enum zwlr_foreign_toplevel_handle_v1_state *i;
wl_array_for_each (i, state)
@@ -243,7 +357,15 @@ foreign_toplevel_handle_state (void *data,
case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED:
task->active = TRUE;
break;
-
+ case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED:
+ task->maximized = TRUE;
+ break;
+ case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED:
+ task->minimized = TRUE;
+ break;
+ case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN:
+ task->fullscreen = TRUE;
+ break;
default:
break;
}
@@ -264,10 +386,87 @@ foreign_toplevel_handle_closed (void *data,
struct zwlr_foreign_toplevel_handle_v1 *toplevel)
{
ToplevelTask *task = data;
+
if (task->button)
+ {
+ GtkOrientation orient;
+ GtkWidget *button;
+ GtkWidget *box;
+ GtkWidget *outer_box = gtk_widget_get_parent (GTK_WIDGET (task->button));
gtk_widget_destroy (task->button);
-}
+ buttons = buttons -1;
+
+ if (buttons == 0)
+ return;
+ /* We don't need to modify button size on a vertical panel*/
+ orient = gtk_orientable_get_orientation (GTK_ORIENTABLE (outer_box));
+ if (orient == GTK_ORIENTATION_VERTICAL)
+ return;
+
+ /* Horizontal panel: if buttons can now fit
+ * with both labels and icons show them
+ */
+ if (tasklist_width / buttons > icon_size * 3)
+ {
+ GList* children = gtk_container_get_children (GTK_CONTAINER (outer_box));
+ while (children != NULL)
+ {
+ button = GTK_WIDGET (children->data);
+
+ /* If maximum width buttons fix, expand to that dimension*/
+ if (buttons * max_button_width < tasklist_width)
+ gtk_widget_set_size_request (button, max_button_width, -1);
+
+ /* Otherwise expand remaining buttons to fill the tasklist*/
+ else
+ gtk_widget_set_size_request (button, tasklist_width / buttons, -1);
+
+ gtk_widget_show_all (button);
+ children = children->next;
+ }
+ }
+ /* If buttons with icons will fit, bring them back*/
+ else if (tasklist_width / buttons > icon_size * 2)
+ {
+ GtkWidget *widget;
+ GList* children = gtk_container_get_children (GTK_CONTAINER (outer_box));
+ while (children != NULL)
+ {
+ button = GTK_WIDGET (children->data);
+ box = gtk_bin_get_child (GTK_BIN (button));
+ GList* contents = gtk_container_get_children (GTK_CONTAINER (box));
+ while (contents != NULL)
+ {
+ widget = GTK_WIDGET (contents->data);
+ if (GTK_IS_LABEL (widget))
+ gtk_widget_hide (widget);
+
+ if (GTK_IS_IMAGE (widget))
+ gtk_widget_show (widget);
+
+ contents = contents->next;
+ gtk_widget_show (box);
+ gtk_widget_show (button);
+ }
+
+ children = children->next;
+ gtk_widget_set_size_request (button, tasklist_width / buttons, -1);
+ }
+ }
+ /* If we still cannot fit labels or icons, just fill the available space*/
+ else
+ {
+ GList* children = gtk_container_get_children (GTK_CONTAINER (outer_box));
+ while (children != NULL)
+ {
+ button = GTK_WIDGET (children->data);
+ gtk_widget_set_size_request (button, tasklist_width / buttons, -1);
+ children = children->next;
+ }
+ }
+ }
+}
static const struct zwlr_foreign_toplevel_handle_v1_listener foreign_toplevel_handle_listener = {
.title = foreign_toplevel_handle_title,
@@ -285,6 +484,7 @@ toplevel_task_disconnected_from_widget (ToplevelTask *task)
struct zwlr_foreign_toplevel_handle_v1 *toplevel = task->toplevel;
task->button = NULL;
+ task->icon = NULL;
task->label = NULL;
task->toplevel = NULL;
@@ -313,13 +513,25 @@ toplevel_task_handle_clicked (GtkButton *button, ToplevelTask *task)
}
}
-static gboolean on_toplevel_button_press (GtkWidget *widget, GdkEvent *event, gpointer user_data)
+static gboolean on_toplevel_button_press (GtkWidget *button, GdkEvent *event, TasklistManager *tasklist)
{
/* Assume event is a button press */
if (((GdkEventButton*)event)->button == GDK_BUTTON_SECONDARY)
{
- /* Returning true for secondary clicks suppresses the applet's default context menu,
- * which we do not want to show up for task buttons */
+ ContextMenu *menu = tasklist->context_menu;
+ ToplevelTask *task = g_object_get_data (G_OBJECT (button), toplevel_task_key);
+
+ g_object_set_data (G_OBJECT (menu->maximize), toplevel_task_key, task);
+ g_object_set_data (G_OBJECT (menu->minimize), toplevel_task_key, task);
+ g_object_set_data (G_OBJECT (menu->close), toplevel_task_key, task);
+
+ gtk_menu_item_set_label (GTK_MENU_ITEM (menu->minimize),
+ task->minimized ? "Unminimize" : "Minimize");
+ gtk_menu_item_set_label (GTK_MENU_ITEM (menu->maximize),
+ task->maximized ? "Unmaximize" : "Maximize");
+
+ gtk_menu_popup_at_widget (GTK_MENU (menu->menu), button,
+ GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_SOUTH_WEST, event);
return TRUE;
}
else
@@ -332,18 +544,172 @@ static ToplevelTask *
toplevel_task_new (TasklistManager *tasklist, struct zwlr_foreign_toplevel_handle_v1 *toplevel)
{
ToplevelTask *task = g_new0 (ToplevelTask, 1);
+ GtkWidget *button;
+ GtkOrientation orient;
+ buttons = buttons + 1;
+ orient = gtk_orientable_get_orientation (GTK_ORIENTABLE (tasklist->outer_box));
task->button = gtk_button_new ();
g_signal_connect (task->button, "clicked", G_CALLBACK (toplevel_task_handle_clicked), task);
+ task->icon = gtk_image_new_from_icon_name ("unknown", icon_size);
+
task->label = gtk_label_new ("");
- gtk_label_set_max_width_chars (GTK_LABEL (task->label), 1);
- gtk_widget_set_size_request (task->label, window_button_width, -1);
+ gtk_label_set_max_width_chars (GTK_LABEL (task->label), TASKLIST_TEXT_MAX_WIDTH);
gtk_label_set_ellipsize (GTK_LABEL (task->label), PANGO_ELLIPSIZE_END);
- gtk_container_add (GTK_CONTAINER(task->button), task->label);
+ gtk_label_set_xalign (GTK_LABEL (task->label), 0.0);
+
+ GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (box), task->icon, FALSE, FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (box), task->label, TRUE, TRUE, 2);
+ gtk_container_add (GTK_CONTAINER (task->button), box);
+ gtk_widget_set_name (task->button , "tasklist-button");
gtk_widget_show_all (task->button);
+ /* Buttons on a vertical panel are not affected by how many are needed
+ * GTK handles compressing contents as needed as the window width tells
+ * GTK how much space to allocate the label and icon. Buttons will use
+ * the full width of a vertical panel without any special attention
+ * so break out here instead of breaking the vertical panel case
+ */
+
+ if (orient == GTK_ORIENTATION_VERTICAL)
+ {
+ gtk_widget_show_all (task->button);
+ task->toplevel = toplevel;
+ zwlr_foreign_toplevel_handle_v1_add_listener (toplevel,
+ &foreign_toplevel_handle_listener,
+ task);
+ g_object_set_data_full (G_OBJECT (task->button),
+ toplevel_task_key,
+ task,
+ (GDestroyNotify)toplevel_task_disconnected_from_widget);
+
+ g_signal_connect (task->button, "button-press-event",
+ G_CALLBACK (on_toplevel_button_press),
+ tasklist);
+
+ return task;
+ }
+
+ /* On horizontal panels, GTK does not by default limit the width of the tasklist
+ * as it does not run out of space in the window until the entire panel is used,
+ * leaving buttons at full width until then and overflowing all other applets
+ *
+ * Thus we must get the tasklist's allocated width when extra space remains,
+ * which will be most of the distance between the handle and the next applet
+ * From there, we can expand buttons and/or hide elements as needed
+ */
+
+
+ tasklist_width = gtk_widget_get_allocated_width (GTK_WIDGET (tasklist->outer_box));
+
+ /* First button can be buggy with this so hardcode it to expand to the limit */
+ if (buttons == 1)
+ gtk_widget_set_size_request (task->button, max_button_width, -1);
+
+ /* if the number of buttons forces width to less than 3x the icon size, shrink them */
+ if ((buttons != 0) && (tasklist_width > 1 )&& (tasklist_width / buttons < (icon_size * 3)))
+ {
+ /* adjust the current button first or it can be missed */
+ if (tasklist_width / buttons > icon_size * 2)
+ {
+ gtk_widget_hide (task->label);
+ gtk_widget_show (task->icon);
+ }
+ else
+ {
+ gtk_widget_show (task->label);
+ gtk_widget_hide (task->icon);
+ }
+ gtk_widget_show (box);
+ gtk_widget_show (task->button);
+
+ /* iterate over all the buttons, first hide labels
+ * then hide icons and bring back labels
+ */
+ GtkWidget *widget;
+
+ GList* children = gtk_container_get_children (GTK_CONTAINER (tasklist->list));
+ while (children != NULL)
+ {
+ button = GTK_WIDGET (children->data);
+ box = gtk_bin_get_child (GTK_BIN (button));
+
+ /* hide labels of all buttons but show icons if only icons will fit */
+ if (tasklist_width / buttons > icon_size * 2)
+ {
+ /* find the icon and the label, show just the icon */
+ GList* contents = gtk_container_get_children (GTK_CONTAINER (box));
+
+ while (contents != NULL)
+ {
+ widget = GTK_WIDGET (contents->data);
+ if (GTK_IS_LABEL (widget))
+ gtk_widget_hide (widget);
+
+ if (GTK_IS_IMAGE (widget))
+ gtk_widget_show (widget);
+
+ contents = contents->next;
+ }
+ }
+ else
+ {
+ /* find the icon and the label, show just the label as it is more
+ * compressable than the icon. Though less meaningful at this size,
+ * it is enough to keep the tasklist from disappearing on themes
+ * that do not set borders around tasklist buttons.
+ * This is same behavior as on x11 save that an extreme number of
+ * buttons (50+ on 700px of space) can still overflow
+ */
+
+ GList* contents = gtk_container_get_children (GTK_CONTAINER (box));
+ while (contents != NULL)
+ {
+ widget = GTK_WIDGET (contents->data);
+ if (GTK_IS_LABEL (widget))
+ gtk_widget_show (widget);
+
+ if (GTK_IS_IMAGE (widget))
+ gtk_widget_hide (widget);
+
+ contents = contents->next;
+ }
+ }
+ /*expand buttons with labels or everything hidden to fit remaining space*/
+ gtk_widget_set_size_request (button, tasklist_width / buttons, -1);
+ /*show the button and any contents that fit, then get the next button*/
+ gtk_widget_show (box);
+ gtk_widget_show (button);
+
+ children = children->next;
+ }
+ }
+ else
+ {
+ GList* children = gtk_container_get_children (GTK_CONTAINER(tasklist->list));
+ while (children != NULL)
+ {
+ button = GTK_WIDGET (children->data);
+ if (((buttons ) * max_button_width < tasklist_width) || (buttons == 1))
+
+ /*Don't let buttons expand over the maximum button size*/
+ gtk_widget_set_size_request (button, max_button_width, -1);
+
+ else
+ /*if full width buttons won't fit, size them to just fill the tasklist*/
+ gtk_widget_set_size_request (button, tasklist_width / buttons, -1);
+
+ children = children->next;
+ }
+ gtk_widget_show_all (task->button);
+ }
+
+ /*Reset the tasklist width after button adjustments*/
+ tasklist_width = gtk_widget_get_allocated_width (GTK_WIDGET (tasklist->outer_box));
+
task->toplevel = toplevel;
zwlr_foreign_toplevel_handle_v1_add_listener (toplevel,
&foreign_toplevel_handle_listener,
@@ -353,10 +719,9 @@ toplevel_task_new (TasklistManager *tasklist, struct zwlr_foreign_toplevel_handl
task,
(GDestroyNotify)toplevel_task_disconnected_from_widget);
- g_signal_connect (G_OBJECT (task->button),
- "button-press-event",
+ g_signal_connect (task->button, "button-press-event",
G_CALLBACK (on_toplevel_button_press),
- task);
+ tasklist);
return task;
}
diff --git a/applets/wncklet/wayland-backend.h b/applets/wncklet/wayland-backend.h
index 0ea4634a..e2402236 100644
--- a/applets/wncklet/wayland-backend.h
+++ b/applets/wncklet/wayland-backend.h
@@ -38,11 +38,9 @@ extern "C" {
GtkWidget* wayland_tasklist_new (void);
void wayland_tasklist_set_orientation (GtkWidget* tasklist_widget, GtkOrientation orient);
-
#ifdef __cplusplus
}
#endif
#endif /* _WNCKLET_APPLET_WAYLAND_BACKEND_H_ */
-
diff --git a/applets/wncklet/window-list.c b/applets/wncklet/window-list.c
index dc36b89b..744d0ef7 100644
--- a/applets/wncklet/window-list.c
+++ b/applets/wncklet/window-list.c
@@ -39,9 +39,7 @@
#define WINDOW_LIST_ICON "mate-panel-window-list"
#define WINDOW_LIST_SCHEMA "org.mate.panel.applet.window-list"
-#ifdef HAVE_WINDOW_PREVIEWS
#define WINDOW_LIST_PREVIEW_SCHEMA "org.mate.panel.applet.window-list-previews"
-#endif /* HAVE_WINDOW_PREVIEWS */
typedef enum {
TASKLIST_NEVER_GROUP,
@@ -52,17 +50,16 @@ typedef enum {
typedef struct {
GtkWidget* applet;
GtkWidget* tasklist;
-#ifdef HAVE_WINDOW_PREVIEWS
GtkWidget* preview;
gboolean show_window_thumbnails;
gint thumbnail_size;
-#endif
gboolean include_all_workspaces;
TasklistGroupingType grouping;
gboolean move_unminimized_windows;
gboolean scroll_enable;
+ gboolean middle_click_close;
GtkOrientation orientation;
int size;
@@ -70,33 +67,28 @@ typedef struct {
gboolean needs_hints;
#endif
- GtkIconTheme* icon_theme;
-
/* Properties: */
GtkWidget* properties_dialog;
GtkWidget* wayland_info_label;
GtkWidget* show_current_radio;
GtkWidget* show_all_radio;
-#ifdef HAVE_WINDOW_PREVIEWS
GtkWidget* window_thumbnail_box;
GtkWidget* show_thumbnails_check;
GtkWidget* thumbnail_size_label;
GtkWidget* thumbnail_size_spin;
-#endif
GtkWidget* never_group_radio;
GtkWidget* auto_group_radio;
GtkWidget* always_group_radio;
GtkWidget* move_minimized_radio;
GtkWidget* mouse_scroll_check;
+ GtkWidget* middle_click_close_check;
GtkWidget* change_workspace_radio;
GtkWidget* minimized_windows_box;
GtkWidget* window_grouping_box;
GtkWidget* window_list_content_box;
GSettings* settings;
-#ifdef HAVE_WINDOW_PREVIEWS
GSettings* preview_settings;
-#endif
} TasklistData;
static void call_system_monitor(GtkAction* action, TasklistData* tasklist);
@@ -138,6 +130,7 @@ static void tasklist_update(TasklistData* tasklist)
wnck_tasklist_set_include_all_workspaces(WNCK_TASKLIST(tasklist->tasklist), tasklist->include_all_workspaces);
wnck_tasklist_set_switch_workspace_on_unminimize(WNCK_TASKLIST(tasklist->tasklist), tasklist->move_unminimized_windows);
wnck_tasklist_set_scroll_enabled (WNCK_TASKLIST(tasklist->tasklist), tasklist->scroll_enable);
+ wnck_tasklist_set_middle_click_close (WNCK_TASKLIST (tasklist->tasklist), tasklist->middle_click_close);
}
#endif /* HAVE_X11 */
@@ -202,11 +195,6 @@ static void response_cb(GtkWidget* widget, int id, TasklistData* tasklist)
}
}
-static void applet_realized(MatePanelApplet* applet, TasklistData* tasklist)
-{
- tasklist->icon_theme = gtk_icon_theme_get_for_screen(gtk_widget_get_screen(tasklist->applet));
-}
-
static void applet_change_orient(MatePanelApplet* applet, MatePanelAppletOrient orient, TasklistData* tasklist)
{
GtkOrientation new_orient;
@@ -246,7 +234,6 @@ static void applet_change_background(MatePanelApplet* applet, MatePanelAppletBac
}
#ifdef HAVE_X11
-#ifdef HAVE_WINDOW_PREVIEWS
static cairo_surface_t*
preview_window_thumbnail (WnckWindow *wnck_window,
TasklistData *tasklist,
@@ -255,7 +242,6 @@ preview_window_thumbnail (WnckWindow *wnck_window,
int *thumbnail_scale)
{
GdkWindow *window;
- GdkWindow *window_wrapper = NULL;
Window win;
cairo_surface_t *thumbnail;
cairo_t *cr;
@@ -264,20 +250,9 @@ preview_window_thumbnail (WnckWindow *wnck_window,
win = wnck_window_get_xid (wnck_window);
- if ((window = gdk_x11_window_lookup_for_display (gdk_display_get_default (), win)) == NULL)
- {
- if ((window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), win)) == NULL)
- {
- return NULL;
- }
- else
- {
- window_wrapper = window;
- }
- }
- else
+ if ((window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), win)) == NULL)
{
- g_object_ref (window);
+ return NULL;
}
*thumbnail_scale = scale = gdk_window_get_scale_factor (window);
@@ -300,6 +275,8 @@ preview_window_thumbnail (WnckWindow *wnck_window,
*thumbnail_width = (int) ((double) width * ratio);
}
+ gdk_x11_display_error_trap_push (gdk_window_get_display (window));
+
thumbnail = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
*thumbnail_width,
*thumbnail_height);
@@ -310,44 +287,81 @@ preview_window_thumbnail (WnckWindow *wnck_window,
cairo_paint (cr);
cairo_destroy (cr);
- if (window_wrapper)
- g_object_unref (window_wrapper);
+ if (gdk_x11_display_error_trap_pop (gdk_window_get_display (window)))
+ {
+ cairo_surface_destroy (thumbnail);
+ thumbnail = NULL;
+ }
+
g_object_unref (window);
return thumbnail;
}
+static int g_int_compare(gconstpointer a, gconstpointer b)
+{
+ gint a_val = GPOINTER_TO_INT(a);
+ gint b_val = GPOINTER_TO_INT(b);
+ if (a_val > b_val) return -1;
+ if (a_val == b_val) return 0;
+ return 1;
+}
+
+static int find_offset(GList *list, gdouble target)
+{
+ GList *node = list;
+ while (node != NULL) {
+ int value = GPOINTER_TO_INT(node->data);
+ if (value <= target)
+ return value;
+ node = node->next;
+ }
+ return -1;
+}
+
#define PREVIEW_PADDING 5
static void
-preview_window_reposition (TasklistData *tasklist,
- cairo_surface_t *thumbnail,
+preview_window_reposition (WnckTasklist *tl,
+ TasklistData *tasklist,
int width,
int height,
int scale)
{
+ /* Known issues:
+ * - When grouping is toggled the previews won't be centered correctly until a new window is opened or one is closed.
+ * - Previews are not shown at all for grouped windows, this function is not called when hovering over those.
+ */
+
GdkMonitor *monitor;
GdkRectangle monitor_geom;
- int x_pos, y_pos;
+ MatePanelAppletOrient orient;
+ gdouble x_pos, y_pos;
+ int x_offset, y_offset;
- /* Set position at pointer, then re-adjust from there to just outside of the pointer */
- gtk_window_set_position (GTK_WINDOW (tasklist->preview), GTK_WIN_POS_MOUSE);
- gtk_window_get_position (GTK_WINDOW (tasklist->preview), &x_pos, &y_pos);
+ /* Get mouse position */
+ gdk_device_get_position_double (gdk_seat_get_pointer (gdk_display_get_default_seat (gdk_display_get_default ())), NULL, &x_pos, &y_pos);
/* Get geometry of monitor where tasklist is located to calculate correct position of preview */
monitor = gdk_display_get_monitor_at_point (gdk_display_get_default (), x_pos, y_pos);
gdk_monitor_get_geometry (monitor, &monitor_geom);
+ /* Get the position where the window list applet starts */
+ gdk_window_get_origin (gtk_widget_get_window (gtk_widget_get_parent (GTK_WIDGET(tl))), &x_offset, &y_offset);
+
+ /* Get panel orientation */
+ orient = mate_panel_applet_get_orient (MATE_PANEL_APPLET (tasklist->applet));
+
/* Add padding to clear the panel */
- switch (mate_panel_applet_get_orient (MATE_PANEL_APPLET (tasklist->applet)))
+ switch (orient)
{
case MATE_PANEL_APPLET_ORIENT_LEFT:
- x_pos = monitor_geom.width + monitor_geom.x - (width/scale + tasklist->size) - PREVIEW_PADDING;
+ x_pos = monitor_geom.width + monitor_geom.x - width - tasklist->size - PREVIEW_PADDING;
break;
case MATE_PANEL_APPLET_ORIENT_RIGHT:
x_pos = tasklist->size + PREVIEW_PADDING;
break;
case MATE_PANEL_APPLET_ORIENT_UP:
- y_pos = monitor_geom.height + monitor_geom.y - (height/scale + tasklist->size) - PREVIEW_PADDING;
+ y_pos = monitor_geom.height + monitor_geom.y - height - tasklist->size - PREVIEW_PADDING;
break;
case MATE_PANEL_APPLET_ORIENT_DOWN:
default:
@@ -355,6 +369,56 @@ preview_window_reposition (TasklistData *tasklist,
break;
}
+ /* Collect the allocation.x/y values of each button into lists.
+ * We need to iterate over all of them because grouped buttons will be the last children,
+ * even though they are positioned at the beginning. And not all buttons will have the exact same width.
+ * This allows us to avoid off-by-one errors that would cause the preview to be positioned over the adjacent button. */
+ GList *alloc_x_list = NULL;
+ GList *alloc_y_list = NULL;
+ GtkAllocation last_alloc;
+ GList* children = gtk_container_get_children (GTK_CONTAINER(tl));
+ while (children != NULL)
+ {
+ if (g_strcmp0 (gtk_widget_get_name (children->data), "tasklist-button") == 0) {
+ GtkAllocation alloc;
+ gtk_widget_get_allocation (children->data, &alloc);
+
+ /* Skip grouped buttons: these usually have alloc width/heigh=1, except right after grouping is toggled.
+ * Then simply open or close a new window to get the correct offset. */
+ if (alloc.width < 2 || alloc.height < 2)
+ {
+ children = children->next;
+ continue;
+ }
+
+ /* Keep x and y offsets in sorted lists */
+ alloc_x_list = g_list_insert_sorted (alloc_x_list, GINT_TO_POINTER(alloc.x), g_int_compare);
+ alloc_y_list = g_list_insert_sorted (alloc_y_list, GINT_TO_POINTER(alloc.y), g_int_compare);
+
+ /* The width/height from the last allocation will be used for centering the preview.
+ * It might be off by a pixel because not all buttons have the exact same width/height but this isn't critical. */
+ last_alloc = alloc;
+ }
+ children = children->next;
+ }
+
+ /* Center preview at the midpoint of the tasklist button */
+ if (orient == MATE_PANEL_APPLET_ORIENT_LEFT || orient == MATE_PANEL_APPLET_ORIENT_RIGHT)
+ {
+ /* Vertical panel */
+ y_pos = y_offset + find_offset (alloc_y_list, y_pos - y_offset) + (last_alloc.height - height) / 2;
+ y_pos = y_pos < PREVIEW_PADDING ? PREVIEW_PADDING : y_pos;
+ }
+ else if (orient == MATE_PANEL_APPLET_ORIENT_UP || orient == MATE_PANEL_APPLET_ORIENT_DOWN)
+ {
+ /* Horizontal panel */
+ x_pos = x_offset + find_offset (alloc_x_list, x_pos - x_offset) + (last_alloc.width - width) / 2;
+ x_pos = x_pos < PREVIEW_PADDING ? PREVIEW_PADDING : x_pos;
+ }
+
+ g_list_free (alloc_x_list);
+ g_list_free (alloc_y_list);
+
gtk_window_move (GTK_WINDOW (tasklist->preview), x_pos, y_pos);
}
@@ -414,12 +478,14 @@ static gboolean applet_enter_notify_event (WnckTasklist *tl, GList *wnck_windows
gtk_widget_set_app_paintable (tasklist->preview, TRUE);
gtk_window_set_default_size (GTK_WINDOW (tasklist->preview), thumbnail_width/thumbnail_scale, thumbnail_height/thumbnail_scale);
gtk_window_set_resizable (GTK_WINDOW (tasklist->preview), TRUE);
-
- preview_window_reposition (tasklist, thumbnail, thumbnail_width, thumbnail_height, thumbnail_scale);
+ preview_window_reposition (tl, tasklist, thumbnail_width/thumbnail_scale, thumbnail_height/thumbnail_scale, thumbnail_scale);
gtk_widget_show (tasklist->preview);
- g_signal_connect_data (G_OBJECT (tasklist->preview), "draw", G_CALLBACK (preview_window_draw), thumbnail, (GClosureNotify) G_CALLBACK (cairo_surface_destroy), 0);
+ g_signal_connect_data (tasklist->preview, "draw",
+ G_CALLBACK (preview_window_draw), thumbnail,
+ (GClosureNotify) G_CALLBACK (cairo_surface_destroy),
+ 0);
return FALSE;
}
@@ -434,7 +500,6 @@ static gboolean applet_leave_notify_event (WnckTasklist *tl, GList *wnck_windows
return FALSE;
}
-#endif /* HAVE_WINDOW_PREVIEWS */
#endif /* HAVE_X11 */
static void applet_change_pixel_size(MatePanelApplet* applet, gint size, TasklistData* tasklist)
@@ -523,7 +588,6 @@ static void display_all_workspaces_changed(GSettings* settings, gchar* key, Task
tasklist_properties_update_content_radio(tasklist);
}
-#ifdef HAVE_WINDOW_PREVIEWS
static void tasklist_update_thumbnail_size_spin(TasklistData* tasklist)
{
GtkWidget* button;
@@ -546,7 +610,6 @@ static void thumbnail_size_changed(GSettings *settings, gchar* key, TasklistData
tasklist->thumbnail_size = g_settings_get_int(settings, key);
tasklist_update_thumbnail_size_spin(tasklist);
}
-#endif
static GtkWidget* get_grouping_button(TasklistData* tasklist, TasklistGroupingType type)
{
@@ -603,7 +666,6 @@ static void tasklist_update_unminimization_radio(TasklistData* tasklist)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
}
-
static void move_unminimized_windows_changed(GSettings* settings, gchar* key, TasklistData* tasklist)
{
gboolean value;
@@ -622,6 +684,12 @@ static void scroll_enabled_changed (GSettings* settings, gchar* key, TasklistDat
tasklist_update(tasklist);
}
+static void middle_click_close_changed (GSettings* settings, gchar* key, TasklistData* tasklist)
+{
+ tasklist->middle_click_close = g_settings_get_boolean (settings, key);
+ tasklist_update(tasklist);
+}
+
static void setup_gsettings(TasklistData* tasklist)
{
tasklist->settings = mate_panel_applet_settings_new (MATE_PANEL_APPLET (tasklist->applet), WINDOW_LIST_SCHEMA);
@@ -631,7 +699,6 @@ static void setup_gsettings(TasklistData* tasklist)
G_CALLBACK (display_all_workspaces_changed),
tasklist);
-#ifdef HAVE_WINDOW_PREVIEWS
tasklist->preview_settings = mate_panel_applet_settings_new (MATE_PANEL_APPLET (tasklist->applet), WINDOW_LIST_PREVIEW_SCHEMA);
g_signal_connect (tasklist->preview_settings,
@@ -643,7 +710,6 @@ static void setup_gsettings(TasklistData* tasklist)
"changed::thumbnail-window-size",
G_CALLBACK (thumbnail_size_changed),
tasklist);
-#endif
g_signal_connect (tasklist->settings,
"changed::group-windows",
G_CALLBACK (group_windows_changed),
@@ -656,6 +722,10 @@ static void setup_gsettings(TasklistData* tasklist)
"changed::scroll-enabled",
G_CALLBACK (scroll_enabled_changed),
tasklist);
+ g_signal_connect (tasklist->settings,
+ "changed::middle-click-close",
+ G_CALLBACK (middle_click_close_changed),
+ tasklist);
}
static void applet_size_allocate(GtkWidget *widget, GtkAllocation *allocation, TasklistData *tasklist)
@@ -690,55 +760,6 @@ static void applet_size_allocate(GtkWidget *widget, GtkAllocation *allocation, T
mate_panel_applet_set_size_hints(MATE_PANEL_APPLET(tasklist->applet), size_hints, len, 0);
}
-#ifdef HAVE_X11
-/* Currently only used on X11, but should work on Wayland as well when needed */
-static GdkPixbuf* icon_loader_func(const char* icon, int size, unsigned int flags, void* data)
-{
- TasklistData* tasklist;
- GdkPixbuf* retval;
- char* icon_no_extension;
- char* p;
-
- tasklist = data;
-
- if (icon == NULL || strcmp(icon, "") == 0)
- return NULL;
-
- if (g_path_is_absolute(icon))
- {
- if (g_file_test(icon, G_FILE_TEST_EXISTS))
- {
- return gdk_pixbuf_new_from_file_at_size(icon, size, size, NULL);
- }
- else
- {
- char* basename;
-
- basename = g_path_get_basename(icon);
- retval = icon_loader_func(basename, size, flags, data);
- g_free(basename);
-
- return retval;
- }
- }
-
- /* This is needed because some .desktop files have an icon name *and*
- * an extension as icon */
- icon_no_extension = g_strdup(icon);
- p = strrchr(icon_no_extension, '.');
-
- if (p && (strcmp(p, ".png") == 0 || strcmp(p, ".xpm") == 0 || strcmp(p, ".svg") == 0))
- {
- *p = 0;
- }
-
- retval = gtk_icon_theme_load_icon(tasklist->icon_theme, icon_no_extension, size, 0, NULL);
- g_free(icon_no_extension);
-
- return retval;
-}
-#endif /* HAVE_X11 */
-
gboolean window_list_applet_fill(MatePanelApplet* applet)
{
TasklistData* tasklist;
@@ -751,16 +772,15 @@ gboolean window_list_applet_fill(MatePanelApplet* applet)
tasklist->applet = GTK_WIDGET(applet);
provider = gtk_css_provider_new ();
- screen = gdk_screen_get_default ();
gtk_css_provider_load_from_data (provider,
".mate-panel-menu-bar button,\n"
" #tasklist-button {\n"
" padding: 0px;\n"
" margin: 0px;\n }",
-1, NULL);
- gtk_style_context_add_provider_for_screen (screen,
- GTK_STYLE_PROVIDER (provider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ gtk_style_context_add_provider_for_screen (gtk_widget_get_screen (tasklist->applet),
+ GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider);
mate_panel_applet_set_flags(MATE_PANEL_APPLET(tasklist->applet), MATE_PANEL_APPLET_EXPAND_MAJOR | MATE_PANEL_APPLET_EXPAND_MINOR | MATE_PANEL_APPLET_HAS_HANDLE);
@@ -769,11 +789,9 @@ gboolean window_list_applet_fill(MatePanelApplet* applet)
tasklist->include_all_workspaces = g_settings_get_boolean (tasklist->settings, "display-all-workspaces");
-#ifdef HAVE_WINDOW_PREVIEWS
tasklist->show_window_thumbnails = g_settings_get_boolean (tasklist->preview_settings, "show-window-thumbnails");
tasklist->thumbnail_size = g_settings_get_int (tasklist->preview_settings, "thumbnail-window-size");
-#endif
tasklist->grouping = g_settings_get_enum (tasklist->settings, "group-windows");
@@ -781,6 +799,8 @@ gboolean window_list_applet_fill(MatePanelApplet* applet)
tasklist->scroll_enable = g_settings_get_boolean (tasklist->settings, "scroll-enabled");
+ tasklist->middle_click_close = g_settings_get_boolean (tasklist->settings, "middle-click-close");
+
tasklist->size = mate_panel_applet_get_size(applet);
#if !defined(WNCKLET_INPROCESS) && !GTK_CHECK_VERSION (3, 23, 0)
@@ -805,13 +825,12 @@ gboolean window_list_applet_fill(MatePanelApplet* applet)
{
tasklist->tasklist = wnck_tasklist_new();
- wnck_tasklist_set_middle_click_close (WNCK_TASKLIST (tasklist->tasklist), TRUE);
- wnck_tasklist_set_icon_loader(WNCK_TASKLIST(tasklist->tasklist), icon_loader_func, tasklist, NULL);
-
-#ifdef HAVE_WINDOW_PREVIEWS
- g_signal_connect(G_OBJECT(tasklist->tasklist), "task_enter_notify", G_CALLBACK(applet_enter_notify_event), tasklist);
- g_signal_connect(G_OBJECT(tasklist->tasklist), "task_leave_notify", G_CALLBACK(applet_leave_notify_event), tasklist);
-#endif /* HAVE_WINDOW_PREVIEWS */
+ g_signal_connect (tasklist->tasklist, "task-enter-notify",
+ G_CALLBACK (applet_enter_notify_event),
+ tasklist);
+ g_signal_connect (tasklist->tasklist, "task-leave-notify",
+ G_CALLBACK (applet_leave_notify_event),
+ tasklist);
}
else
#endif /* HAVE_X11 */
@@ -830,25 +849,33 @@ gboolean window_list_applet_fill(MatePanelApplet* applet)
tasklist_apply_orientation(tasklist);
- g_signal_connect(G_OBJECT(tasklist->tasklist), "destroy", G_CALLBACK(destroy_tasklist), tasklist);
- g_signal_connect(G_OBJECT(tasklist->applet), "size_allocate", G_CALLBACK(applet_size_allocate), tasklist);
+ g_signal_connect (tasklist->tasklist, "destroy",
+ G_CALLBACK (destroy_tasklist),
+ tasklist);
+ g_signal_connect (tasklist->applet, "size-allocate",
+ G_CALLBACK (applet_size_allocate),
+ tasklist);
gtk_container_add(GTK_CONTAINER(tasklist->applet), tasklist->tasklist);
- g_signal_connect(G_OBJECT(tasklist->applet), "realize", G_CALLBACK(applet_realized), tasklist);
- g_signal_connect(G_OBJECT(tasklist->applet), "change_orient", G_CALLBACK(applet_change_orient), tasklist);
- g_signal_connect(G_OBJECT(tasklist->applet), "change_size", G_CALLBACK(applet_change_pixel_size), tasklist);
- g_signal_connect(G_OBJECT(tasklist->applet), "change_background", G_CALLBACK(applet_change_background), tasklist);
+ g_signal_connect (tasklist->applet, "change-orient",
+ G_CALLBACK (applet_change_orient),
+ tasklist);
+ g_signal_connect (tasklist->applet, "change-size",
+ G_CALLBACK (applet_change_pixel_size),
+ tasklist);
+ g_signal_connect (tasklist->applet, "change-background",
+ G_CALLBACK(applet_change_background),
+ tasklist);
action_group = gtk_action_group_new("Tasklist Applet Actions");
gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE);
gtk_action_group_add_actions(action_group, tasklist_menu_actions, G_N_ELEMENTS(tasklist_menu_actions), tasklist);
-
/* disable the item of system monitor, if not exists.
* example, mate-system-monitor, o gnome-system-monitor */
char* programpath;
- int i;
+ gsize i;
for (i = 0; i < G_N_ELEMENTS(system_monitors); i += 1)
{
@@ -870,7 +897,6 @@ gboolean window_list_applet_fill(MatePanelApplet* applet)
_system_monitor_found:;
/* end of system monitor item */
-
mate_panel_applet_setup_menu_from_resource (MATE_PANEL_APPLET (tasklist->applet),
WNCKLET_RESOURCE_PATH "window-list-menu.xml",
action_group);
@@ -894,7 +920,7 @@ gboolean window_list_applet_fill(MatePanelApplet* applet)
static void call_system_monitor(GtkAction* action, TasklistData* tasklist)
{
- int i;
+ gsize i;
for (i = 0; i < G_N_ELEMENTS(system_monitors); i += 1)
{
@@ -912,7 +938,6 @@ static void call_system_monitor(GtkAction* action, TasklistData* tasklist)
}
}
-
static void display_help_dialog(GtkAction* action, TasklistData* tasklist)
{
wncklet_display_help(tasklist->applet, "mate-user-guide", "windowlist", WINDOW_LIST_ICON);
@@ -960,12 +985,10 @@ static void group_windows_toggled(GtkToggleButton* button, TasklistData* tasklis
}
}
-#ifdef HAVE_WINDOW_PREVIEWS
static void thumbnail_size_spin_changed(GtkSpinButton* button, TasklistData* tasklist)
{
g_settings_set_int(tasklist->preview_settings, "thumbnail-window-size", gtk_spin_button_get_value_as_int(button));
}
-#endif
static void move_minimized_toggled(GtkToggleButton* button, TasklistData* tasklist)
{
@@ -1016,9 +1039,7 @@ static void setup_dialog_wayland(TasklistData* tasklist)
gtk_widget_set_sensitive(tasklist->window_grouping_box, FALSE);
gtk_widget_set_sensitive(tasklist->minimized_windows_box, FALSE);
-#ifdef HAVE_WINDOW_PREVIEWS
gtk_widget_set_sensitive(tasklist->window_thumbnail_box, FALSE);
-#endif /* HAVE_WINDOW_PREVIEWS */
}
#endif /* HAVE_WAYLAND */
@@ -1026,7 +1047,6 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist)
{
GtkWidget* button;
-
tasklist->wayland_info_label = WID("wayland_info_label");
tasklist->show_current_radio = WID("show_current_radio");
tasklist->show_all_radio = WID("show_all_radio");
@@ -1039,7 +1059,6 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist)
setup_sensitivity(tasklist, builder, "never_group_radio", "auto_group_radio", "always_group_radio", "group-windows" /* key */);
-#ifdef HAVE_WINDOW_PREVIEWS
tasklist->window_thumbnail_box = WID("window_thumbnail_box");
tasklist->show_thumbnails_check = WID("show_thumbnails_check");
tasklist->thumbnail_size_label = WID("thumbnail_size_label");
@@ -1056,13 +1075,10 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist)
g_object_bind_property(tasklist->show_thumbnails_check, "active", tasklist->thumbnail_size_label, "sensitive", G_BINDING_DEFAULT);
g_object_bind_property(tasklist->show_thumbnails_check, "active", tasklist->thumbnail_size_spin, "sensitive", G_BINDING_DEFAULT);
-#else
- gtk_widget_hide(WID("window_thumbnail_box"));
-#endif
-
tasklist->move_minimized_radio = WID("move_minimized_radio");
tasklist->change_workspace_radio = WID("change_workspace_radio");
tasklist->mouse_scroll_check = WID("mouse_scroll_check");
+ tasklist->middle_click_close_check = WID("middle_click_close_check");
tasklist->minimized_windows_box = WID("minimized_windows_box");
tasklist->window_grouping_box = WID("window_grouping_box");
tasklist->window_list_content_box = WID("window_list_content_box");
@@ -1076,9 +1092,15 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist)
g_object_set_data(G_OBJECT(tasklist->auto_group_radio), "group_value", "auto");
g_object_set_data(G_OBJECT(tasklist->always_group_radio), "group_value", "always");
- g_signal_connect(G_OBJECT(tasklist->never_group_radio), "toggled", (GCallback) group_windows_toggled, tasklist);
- g_signal_connect(G_OBJECT(tasklist->auto_group_radio), "toggled", (GCallback) group_windows_toggled, tasklist);
- g_signal_connect(G_OBJECT(tasklist->always_group_radio), "toggled", (GCallback) group_windows_toggled, tasklist);
+ g_signal_connect (tasklist->never_group_radio, "toggled",
+ (GCallback) group_windows_toggled,
+ tasklist);
+ g_signal_connect (tasklist->auto_group_radio, "toggled",
+ (GCallback) group_windows_toggled,
+ tasklist);
+ g_signal_connect (tasklist->always_group_radio, "toggled",
+ (GCallback) group_windows_toggled,
+ tasklist);
/* Mouse Scroll: */
g_settings_bind (tasklist->settings,
@@ -1087,22 +1109,37 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist)
"active",
G_SETTINGS_BIND_DEFAULT);
-#ifdef HAVE_WINDOW_PREVIEWS
+ /* Middle mouse click to close window: */
+ g_settings_bind (tasklist->settings,
+ "middle-click-close",
+ tasklist->middle_click_close_check,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
/* change thumbnail size: */
tasklist_update_thumbnail_size_spin(tasklist);
- g_signal_connect(G_OBJECT(tasklist->thumbnail_size_spin), "value-changed", (GCallback) thumbnail_size_spin_changed, tasklist);
-#endif
+ g_signal_connect (tasklist->thumbnail_size_spin, "value-changed",
+ (GCallback) thumbnail_size_spin_changed,
+ tasklist);
/* move window when unminimizing: */
tasklist_update_unminimization_radio(tasklist);
- g_signal_connect(G_OBJECT(tasklist->move_minimized_radio), "toggled", (GCallback) move_minimized_toggled, tasklist);
+ g_signal_connect (tasklist->move_minimized_radio, "toggled",
+ (GCallback) move_minimized_toggled,
+ tasklist);
/* Tasklist content: */
tasklist_properties_update_content_radio (tasklist);
- g_signal_connect(G_OBJECT(tasklist->show_all_radio), "toggled", (GCallback) display_all_workspaces_toggled, tasklist);
+ g_signal_connect (tasklist->show_all_radio, "toggled",
+ (GCallback) display_all_workspaces_toggled,
+ tasklist);
- g_signal_connect_swapped(WID("done_button"), "clicked", (GCallback) gtk_widget_hide, tasklist->properties_dialog);
- g_signal_connect(tasklist->properties_dialog, "response", G_CALLBACK(response_cb), tasklist);
+ g_signal_connect_swapped (WID ("done_button"), "clicked",
+ (GCallback) gtk_widget_hide,
+ tasklist->properties_dialog);
+ g_signal_connect (tasklist->properties_dialog, "response",
+ G_CALLBACK (response_cb),
+ tasklist);
#ifdef HAVE_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
@@ -1141,11 +1178,9 @@ static void destroy_tasklist(GtkWidget* widget, TasklistData* tasklist)
{
g_signal_handlers_disconnect_by_data (G_OBJECT (tasklist->applet), tasklist);
-#ifdef HAVE_WINDOW_PREVIEWS
g_signal_handlers_disconnect_by_data (G_OBJECT (tasklist->tasklist), tasklist);
g_signal_handlers_disconnect_by_data (tasklist->preview_settings, tasklist);
g_object_unref(tasklist->preview_settings);
-#endif
g_signal_handlers_disconnect_by_data (tasklist->settings, tasklist);
@@ -1154,10 +1189,8 @@ static void destroy_tasklist(GtkWidget* widget, TasklistData* tasklist)
if (tasklist->properties_dialog)
gtk_widget_destroy(tasklist->properties_dialog);
-#ifdef HAVE_WINDOW_PREVIEWS
if (tasklist->preview)
gtk_widget_destroy(tasklist->preview);
-#endif
g_free(tasklist);
}
diff --git a/applets/wncklet/window-list.ui b/applets/wncklet/window-list.ui
index 50c9d851..2ed06c0e 100644
--- a/applets/wncklet/window-list.ui
+++ b/applets/wncklet/window-list.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
- <requires lib="gtk+" version="3.24"/>
+ <requires lib="gtk+" version="3.22"/>
<object class="GtkAdjustment" id="adjustment1">
<property name="upper">999</property>
<property name="step-increment">1</property>
@@ -76,6 +76,7 @@
<object class="GtkNotebook">
<property name="visible">True</property>
<property name="can-focus">True</property>
+ <property name="border_width">5</property>
<child>
<object class="GtkBox" id="behaviour_vbox">
<property name="visible">True</property>
@@ -278,6 +279,64 @@
</packing>
</child>
<child>
+ <object class="GtkBox" id="middle_click_close_box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="middle_click_close_label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Middle mouse button</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="middle_click_close_vbox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">6</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="middle_click_close_check">
+ <property name="label" translatable="yes">_Click to close window</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkBox" id="mouse_scroll_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
@@ -332,7 +391,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">3</property>
</packing>
</child>
</object>
diff --git a/applets/wncklet/window-menu.c b/applets/wncklet/window-menu.c
index 804898d8..4b4e48dc 100644
--- a/applets/wncklet/window-menu.c
+++ b/applets/wncklet/window-menu.c
@@ -218,7 +218,7 @@ static gboolean window_menu_key_press_event(GtkWidget* widget, GdkEventKey* even
static gboolean filter_button_press(GtkWidget* widget, GdkEventButton* event, gpointer data)
{
if (event->button != 1)
- g_signal_stop_emission_by_name(widget, "button_press_event");
+ g_signal_stop_emission_by_name(widget, "button-press-event");
return FALSE;
}
@@ -239,7 +239,7 @@ gboolean window_menu_applet_fill(MatePanelApplet* applet)
window_menu->orient = mate_panel_applet_get_orient(applet);
g_signal_connect(window_menu->applet, "destroy", G_CALLBACK(window_menu_destroy), window_menu);
- g_signal_connect(window_menu->applet, "key_press_event", G_CALLBACK(window_menu_key_press_event), window_menu);
+ g_signal_connect(window_menu->applet, "key-press-event", G_CALLBACK(window_menu_key_press_event), window_menu);
action_group = gtk_action_group_new("WindowMenu Applet Actions");
gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE);
@@ -271,13 +271,23 @@ gboolean window_menu_applet_fill(MatePanelApplet* applet)
gtk_container_add(GTK_CONTAINER(window_menu->applet), window_menu->selector);
- g_signal_connect(window_menu->applet, "size-allocate", G_CALLBACK(window_menu_size_allocate), window_menu);
-
- g_signal_connect_after(G_OBJECT(window_menu->applet), "focus-in-event", G_CALLBACK(gtk_widget_queue_draw), window_menu);
- g_signal_connect_after(G_OBJECT(window_menu->applet), "focus-out-event", G_CALLBACK(gtk_widget_queue_draw), window_menu);
- g_signal_connect_after(G_OBJECT(window_menu->selector), "draw", G_CALLBACK(window_menu_on_draw), window_menu);
-
- g_signal_connect(G_OBJECT(window_menu->selector), "button_press_event", G_CALLBACK(filter_button_press), window_menu);
+ g_signal_connect (window_menu->applet, "size-allocate",
+ G_CALLBACK(window_menu_size_allocate),
+ window_menu);
+
+ g_signal_connect_after (window_menu->applet, "focus-in-event",
+ G_CALLBACK (gtk_widget_queue_draw),
+ window_menu);
+ g_signal_connect_after (window_menu->applet, "focus-out-event",
+ G_CALLBACK (gtk_widget_queue_draw),
+ window_menu);
+ g_signal_connect_after (window_menu->selector, "draw",
+ G_CALLBACK (window_menu_on_draw),
+ window_menu);
+
+ g_signal_connect (window_menu->selector, "button_press_event",
+ G_CALLBACK (filter_button_press),
+ window_menu);
gtk_widget_show_all(GTK_WIDGET(window_menu->applet));
diff --git a/applets/wncklet/wncklet.c b/applets/wncklet/wncklet.c
index 67b2ae78..3b185910 100644
--- a/applets/wncklet/wncklet.c
+++ b/applets/wncklet/wncklet.c
@@ -148,7 +148,6 @@ static gboolean wncklet_factory(MatePanelApplet* applet, const char* iid, gpoint
return retval;
}
-
#ifdef WNCKLET_INPROCESS
MATE_PANEL_APPLET_IN_PROCESS_FACTORY("WnckletFactory", PANEL_TYPE_APPLET, "WindowNavigationApplets", wncklet_factory, NULL)
#else
diff --git a/applets/wncklet/workspace-switcher.c b/applets/wncklet/workspace-switcher.c
index a58abce2..e3fda355 100644
--- a/applets/wncklet/workspace-switcher.c
+++ b/applets/wncklet/workspace-switcher.c
@@ -53,6 +53,153 @@
#define WORKSPACE_SWITCHER_ICON "mate-panel-workspace-switcher"
+/* Container for the WnckPager to work around the sizing issues we have in the
+ * panel. See
+ * https://github.com/mate-desktop/mate-panel/issues/1230#issuecomment-1046235088 */
+
+typedef struct _PagerContainer PagerContainer;
+typedef GtkBinClass PagerContainerClass;
+
+static GType pager_container_get_type (void);
+
+#define PAGER_CONTAINER_TYPE (pager_container_get_type ())
+#define PAGER_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PAGER_CONTAINER_TYPE, PagerContainer))
+
+struct _PagerContainer
+{
+ GtkBin parent;
+ GtkOrientation orientation;
+ int size;
+};
+
+G_DEFINE_TYPE (PagerContainer, pager_container, GTK_TYPE_BIN)
+
+static gboolean
+queue_resize_idle_cb (gpointer user_data)
+{
+ gtk_widget_queue_resize (GTK_WIDGET (user_data));
+ return G_SOURCE_REMOVE;
+}
+
+static void
+pager_container_get_preferred_width (GtkWidget *widget,
+ int *minimum_width,
+ int *natural_width)
+{
+ PagerContainer *self;
+
+ self = PAGER_CONTAINER (widget);
+
+ if (self->orientation == GTK_ORIENTATION_VERTICAL)
+ {
+ /* self->size is panel width */
+ *minimum_width = *natural_width = self->size;
+ }
+ else
+ {
+ /* self->size is panel size/height, that will get allocated to pager, request width for this size */
+ gtk_widget_get_preferred_width_for_height (gtk_bin_get_child (GTK_BIN (self)),
+ self->size,
+ minimum_width,
+ natural_width);
+ }
+}
+
+static void
+pager_container_get_preferred_height (GtkWidget *widget,
+ int *minimum_height,
+ int *natural_height)
+{
+ PagerContainer *self;
+
+ self = PAGER_CONTAINER (widget);
+
+ if (self->orientation == GTK_ORIENTATION_VERTICAL)
+ {
+ /* self->size is panel size/width that will get allocated to pager, request height for this size */
+ gtk_widget_get_preferred_height_for_width (gtk_bin_get_child (GTK_BIN (self)),
+ self->size,
+ minimum_height,
+ natural_height);
+ }
+ else
+ {
+ /* self->size is panel height */
+ *minimum_height = *natural_height = self->size;
+ }
+}
+
+static void
+pager_container_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ PagerContainer *self;
+ int size;
+
+ self = PAGER_CONTAINER (widget);
+
+ if (self->orientation == GTK_ORIENTATION_VERTICAL)
+ size = allocation->width;
+ else
+ size = allocation->height;
+
+ size = MAX (size, 1);
+
+ if (self->size != size)
+ {
+ self->size = size;
+ g_idle_add (queue_resize_idle_cb, self);
+ return;
+ }
+
+ GTK_WIDGET_CLASS (pager_container_parent_class)->size_allocate (widget,
+ allocation);
+}
+
+static void
+pager_container_class_init (PagerContainerClass *self_class)
+{
+ GtkWidgetClass *widget_class;
+
+ widget_class = GTK_WIDGET_CLASS (self_class);
+
+ widget_class->get_preferred_width = pager_container_get_preferred_width;
+ widget_class->get_preferred_height = pager_container_get_preferred_height;
+ widget_class->size_allocate = pager_container_size_allocate;
+}
+
+static void
+pager_container_init (PagerContainer *self)
+{
+}
+
+static GtkWidget *
+pager_container_new (GtkWidget *child,
+ GtkOrientation orientation)
+{
+ PagerContainer *self;
+
+ self = g_object_new (PAGER_CONTAINER_TYPE, "child", child, NULL);
+
+ self->orientation = orientation;
+
+ return GTK_WIDGET (self);
+}
+
+static void
+pager_container_set_orientation (PagerContainer *self,
+ GtkOrientation orientation)
+{
+ if (self->orientation == orientation)
+ return;
+
+ self->orientation = orientation;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+/* Pager applet itself */
+
typedef enum {
PAGER_WM_MARCO,
PAGER_WM_METACITY,
@@ -65,6 +212,7 @@ typedef enum {
typedef struct {
GtkWidget* applet;
+ GtkWidget* pager_container;
GtkWidget* pager;
WnckScreen* screen;
@@ -290,6 +438,8 @@ static void applet_change_orient(MatePanelApplet* applet, MatePanelAppletOrient
pager->orientation = new_orient;
pager_update(pager);
+ pager_container_set_orientation(PAGER_CONTAINER(pager->pager_container), pager->orientation);
+
if (pager->label_row_col)
gtk_label_set_text(GTK_LABEL(pager->label_row_col), pager->orientation == GTK_ORIENTATION_HORIZONTAL ? _("rows") : _("columns"));
}
@@ -490,11 +640,12 @@ static const GtkActionEntry pager_menu_actions[] = {
static void num_rows_changed(GSettings* settings, gchar* key, PagerData* pager)
{
- int n_rows = DEFAULT_ROWS;
-
- n_rows = g_settings_get_int (settings, key);
+ int n_rows;
- n_rows = CLAMP(n_rows, 1, MAX_REASONABLE_ROWS);
+ n_rows = CLAMP (g_settings_get_int (settings, key),
+ 1,
+ MIN (wnck_screen_get_workspace_count (pager->screen),
+ MAX_REASONABLE_ROWS));
pager->n_rows = n_rows;
pager_update(pager);
@@ -519,12 +670,9 @@ static void display_workspace_names_changed(GSettings* settings, gchar* key, Pag
}
}
-
static void all_workspaces_changed(GSettings* settings, gchar* key, PagerData* pager)
{
- gboolean value = TRUE; /* Default value */
-
- value = g_settings_get_boolean (settings, key);
+ gboolean value = g_settings_get_boolean (settings, key);
pager->display_all = value;
pager_update(pager);
@@ -650,21 +798,37 @@ gboolean workspace_switcher_applet_fill(MatePanelApplet* applet)
context = gtk_widget_get_style_context (pager->pager);
gtk_style_context_add_class (context, "wnck-pager");
- g_signal_connect(G_OBJECT(pager->pager), "destroy", G_CALLBACK(destroy_pager), pager);
+ g_signal_connect (pager->pager, "destroy",
+ G_CALLBACK (destroy_pager),
+ pager);
/* overwrite default WnckPager widget scroll-event */
- g_signal_connect(G_OBJECT(pager->pager), "scroll-event", G_CALLBACK(applet_scroll), pager);
-
- gtk_container_add(GTK_CONTAINER(pager->applet), pager->pager);
-
- g_signal_connect(G_OBJECT(pager->applet), "realize", G_CALLBACK(applet_realized), pager);
- g_signal_connect(G_OBJECT(pager->applet), "unrealize", G_CALLBACK(applet_unrealized), pager);
- g_signal_connect(G_OBJECT(pager->applet), "change_orient", G_CALLBACK(applet_change_orient), pager);
- g_signal_connect(G_OBJECT(pager->applet), "change_background", G_CALLBACK(applet_change_background), pager);
- g_signal_connect(G_OBJECT(pager->applet), "style-updated", G_CALLBACK(applet_style_updated), context);
-
- gtk_widget_show(pager->pager);
- gtk_widget_show(pager->applet);
+ g_signal_connect (pager->pager, "scroll-event",
+ G_CALLBACK (applet_scroll),
+ pager);
+
+ pager->pager_container = pager_container_new(pager->pager, pager->orientation);
+ gtk_container_add(GTK_CONTAINER(pager->applet), pager->pager_container);
+
+ g_signal_connect (pager->applet, "realize",
+ G_CALLBACK (applet_realized),
+ pager);
+ g_signal_connect (pager->applet, "unrealize",
+ G_CALLBACK (applet_unrealized),
+ pager);
+ g_signal_connect (pager->applet, "change-orient",
+ G_CALLBACK (applet_change_orient),
+ pager);
+ g_signal_connect (pager->applet, "change-background",
+ G_CALLBACK (applet_change_background),
+ pager);
+ g_signal_connect (pager->applet, "style-updated",
+ G_CALLBACK (applet_style_updated),
+ context);
+
+ gtk_widget_show (pager->pager);
+ gtk_widget_show (pager->pager_container);
+ gtk_widget_show (pager->applet);
action_group = gtk_action_group_new("WorkspaceSwitcher Applet Actions");
gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE);
@@ -686,7 +850,6 @@ gboolean workspace_switcher_applet_fill(MatePanelApplet* applet)
return TRUE;
}
-
static void display_help_dialog(GtkAction* action, PagerData* pager)
{
wncklet_display_help(pager->applet, "mate-user-guide", "overview-workspaces", WORKSPACE_SWITCHER_ICON);
@@ -823,6 +986,8 @@ on_num_workspaces_value_changed (GtkSpinButton *button,
{
int workspace_count = gtk_spin_button_get_value_as_int (button);
wnck_screen_change_workspace_count(pager->screen, workspace_count);
+ if (workspace_count < pager->n_rows)
+ g_settings_set_int (pager->settings, "num-rows", workspace_count);
}
#endif /* HAVE_X11 */
}
@@ -998,7 +1163,6 @@ static void setup_dialog(GtkBuilder* builder, PagerData* pager)
if (marco_workspaces_settings != NULL)
g_object_unref (marco_workspaces_settings);
-
/* Wrap workspaces: */
if (pager->wrap_workspaces_toggle)
{
@@ -1006,18 +1170,24 @@ static void setup_dialog(GtkBuilder* builder, PagerData* pager)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pager->wrap_workspaces_toggle), pager->wrap_workspaces);
}
- g_signal_connect(G_OBJECT(pager->wrap_workspaces_toggle), "toggled", (GCallback) wrap_workspaces_toggled, pager);
+ g_signal_connect (pager->wrap_workspaces_toggle, "toggled",
+ (GCallback) wrap_workspaces_toggled,
+ pager);
/* Display workspace names: */
- g_signal_connect(G_OBJECT(pager->display_workspaces_toggle), "toggled", (GCallback) display_workspace_names_toggled, pager);
+ g_signal_connect (pager->display_workspaces_toggle, "toggled",
+ (GCallback) display_workspace_names_toggled,
+ pager);
value = pager->display_names;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pager->display_workspaces_toggle), value);
/* Display all workspaces: */
- g_signal_connect(G_OBJECT(pager->all_workspaces_radio), "toggled", (GCallback) all_workspaces_toggled, pager);
+ g_signal_connect (pager->all_workspaces_radio, "toggled",
+ (GCallback) all_workspaces_toggled,
+ pager);
if (pager->display_all)
{
@@ -1041,7 +1211,7 @@ static void setup_dialog(GtkBuilder* builder, PagerData* pager)
gtk_label_set_text(GTK_LABEL(pager->label_row_col), pager->orientation == GTK_ORIENTATION_HORIZONTAL ? _("rows") : _("columns"));
g_signal_connect(pager->properties_dialog, "destroy", G_CALLBACK(properties_dialog_destroyed), pager);
- g_signal_connect(pager->properties_dialog, "delete_event", G_CALLBACK(delete_event), pager);
+ g_signal_connect(pager->properties_dialog, "delete-event", G_CALLBACK(delete_event), pager);
g_signal_connect(pager->properties_dialog, "response", G_CALLBACK(response_cb), pager);
g_signal_connect(WID("done_button"), "clicked", (GCallback) close_dialog, pager);
@@ -1063,9 +1233,13 @@ static void setup_dialog(GtkBuilder* builder, PagerData* pager)
}
#endif /* HAVE_X11 */
- g_signal_connect (pager->num_workspaces_spin, "value-changed", G_CALLBACK (on_num_workspaces_value_changed), pager);
+ g_signal_connect (pager->num_workspaces_spin, "value-changed",
+ G_CALLBACK (on_num_workspaces_value_changed),
+ pager);
- g_signal_connect(G_OBJECT(pager->workspaces_tree), "focus_out_event", (GCallback) workspaces_tree_focused_out, pager);
+ g_signal_connect (pager->workspaces_tree, "focus-out-event",
+ (GCallback) workspaces_tree_focused_out,
+ pager);
pager->workspaces_store = gtk_list_store_new(1, G_TYPE_STRING, NULL);
update_workspaces_model(pager);