Logo Search packages:      
Sourcecode: dates version File versions  Download package


/* -*- mode:C; c-file-style:"linux"; tab-width:8; -*- */
 *  Dates - An electronic calendar optimised for embedded devices.
 *  Principal author    : Chris Lord <chris@o-hand.com>
 *  Maemo port          : Tomas Frydrych <tf@o-hand.com>
 *  Copyright (c) 2005 - 2006 OpenedHand Ltd - http://o-hand.com
 *  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, 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
 *  GNU General Public License for more details.

#include <string.h>
#include <math.h>
#include <libecal/e-cal-time-util.h>
#include <libical/icaltime.h>
#include <gconf/gconf-client.h>

#include "dates_types.h"
#include "dates_platform.h"
#include "dates_callbacks.h"

#include "gconf-bridge.h"

static GtkWidget *
create_dates_view ()
      GtkWidget *widget;
      widget = dates_view_new ();
      /* TODO: This is temporary until the widget sizes itself properly */
      gtk_widget_set_size_request (widget, 240, 240);
      gtk_widget_show (widget);
      return widget;

static void
dates_load_calendars (DatesData *d)
      ESourceList *source_list;
      GSList *group_list;
      gboolean remote_present = FALSE;
      GError *error = NULL;

      /* Load calendars */
      source_list = e_source_list_new_for_gconf_default (CALENDAR_GCONF_SOURCES);

      if (!source_list) {
            g_error ("Error loading ESource list");

      group_list = e_source_list_peek_groups (source_list);

       * If the group list is completely empty then we are in a totally
       * fresh state. Create the local group and a source for it.
      if (group_list == NULL)
            ESourceGroup *local_group;
            ESource *system_source;
            ECal *system_ecal;
            const gchar *uri = NULL;
            gchar *base_uri = NULL;
            gchar *path = NULL;
            gchar *dirname = NULL;

            /* If no groups then we must be on a first run */

            /* First we must create a calendar so that we can get it's
             * uri so we can know the base uri for group.
            system_ecal = e_cal_new_system_calendar ();

            if (!e_cal_open (system_ecal, FALSE, &error))
                  g_error ("Error creating system calendar: %s", error->message);

            /* Extract the URI from the newly created calendar */
            uri = e_cal_get_uri (system_ecal);

            if (uri == NULL)
                  g_error ("Error getting the uri for the new system calendar");

            /* Create a default "On This Computer" Group. */
            path = g_filename_from_uri (uri, NULL, NULL);
            dirname = g_path_get_dirname (path);

            base_uri = g_strdup_printf ("file://%s", dirname);

            local_group = e_source_group_new (_("On This Computer"), base_uri);

            g_free (path);
            g_free (dirname);
            g_free (base_uri);

            /* Now create a default source for it */
            system_source = e_source_new (_("Personal"), "system");

            /* Default Evolution colour */
            e_source_set_color (system_source, 0xBECEDD);

            /* Set the group for the source and put it in the group */
            e_source_set_group (system_source, local_group);
            e_source_group_add_source (local_group, system_source, 0);

            /* Put the group for local things into the list */
            if (!e_source_list_add_group (source_list, local_group, 0))
                  g_warning ("Error adding group");

            /* The system calendar client is no longer needed */
            g_object_unref (system_ecal);
            d->first_load = TRUE;

      /* Check the available groups for a "On The Web" one */
      for (GSList *group_list_it = group_list; group_list_it != NULL;
                  group_list_it = g_slist_next (group_list_it))
            ESourceGroup *group;

            group = (ESourceGroup *)group_list_it->data;

            if (strncmp(e_source_group_peek_base_uri (group), "webcal://", 9) == 0)
                  remote_present = TRUE;

      /* If not present then create it */
      if (!remote_present)
            ESourceGroup *remote_group;
            remote_group = e_source_group_new (_("On The Web"), "webcal://");

            if (!e_source_list_add_group (source_list, remote_group, 0))
                  g_warning ("Error adding group");

      /* Make sure that the sources point back to their group */
      for (GSList *group_list_it = group_list; group_list_it != NULL;
                  group_list_it = g_slist_next (group_list_it))
            ESourceGroup *group;
            GSList *source_list;

            group = (ESourceGroup *)group_list_it->data;

            source_list = e_source_group_peek_sources (group);

            for (GSList *source_list_it = source_list; source_list_it != NULL;
                        source_list_it = g_slist_next (source_list_it))
                  ESource *source;
                  source = (ESource *)source_list_it->data;

                  /* Set the source's group */
                  e_source_set_group (source, group);

       * Ensure that the local group has a base_uri starting with file://
       * (previous versions had it starting with file:/ which is incorrect)
      for (GSList *group_list_it = group_list; group_list_it != NULL;
                  group_list_it = g_slist_next (group_list_it))
            ESourceGroup *group;
            const gchar *base_uri;;

            group = (ESourceGroup *)group_list_it->data;

            base_uri = e_source_group_peek_base_uri (group);

            if (strncmp(base_uri, "file:/", 6) == 0 && 
                        strncmp (base_uri, "file://", 7) != 0)
                  gchar *new_uri = NULL;
                  gchar *path = g_filename_from_uri (base_uri, NULL, NULL);
                  GSList *source_list = NULL;

                  new_uri = g_strdup_printf ("file://%s", path);
                  e_source_group_set_base_uri (group, new_uri);

                   * Also ensure that the sources contained within this
                   * group have an appropriate uri setup. Removing the
                   * absolute uri in favour of a relative one.
                  source_list = e_source_group_peek_sources (group);

                  for (GSList *source_list_it = source_list; source_list_it != NULL;
                              source_list_it = g_slist_next (source_list_it))
                        ESource *source = (ESource *)source_list_it->data;

                        if (g_str_equal (e_source_peek_relative_uri (source), ""))
                              const gchar *uri = e_source_peek_absolute_uri (source);
                              gchar *path = g_filename_from_uri (uri, NULL, NULL);
                              gchar *base_name = g_path_get_basename (path);

                              e_source_set_absolute_uri (source, NULL);
                              e_source_set_relative_uri (source, base_name);

                              g_free (base_name);
                              g_free (path);

                  g_free (path);
                  g_free (new_uri);

      /* Sync the list with version in gconf */
      if (!e_source_list_sync (source_list, &error))
            g_warning ("Error syncing ESourceList: %s",
            g_error_free (error);

      dates_autoselect_calendars (d, source_list);

      g_signal_connect (G_OBJECT (source_list), "changed",
            G_CALLBACK (dates_sources_changed_cb), d);
      dates_update_calendars (source_list, d);

      d->source_list = source_list;

/* Thanks to GnuCash to find out how to do this */
static void
dates_autoconnect (const gchar *handler_name, GObject *object,
                  const gchar *signal_name, const gchar *signal_data,
                  GObject *connect_object, gboolean after,
                  gpointer user_data)
      static GModule *symbols = NULL;
      GCallback func;
      GCallback *pfunc = &func;
      if (!symbols) {
            symbols = g_module_open(NULL, 0);
      if (!g_module_symbol (symbols, handler_name, (gpointer *)pfunc)) {
            g_warning ("Handler '%s' not found.", handler_name);

      if (connect_object) {
            if (after)
                  g_signal_connect_object (object, signal_name,
                        func, connect_object, G_CONNECT_AFTER);
                  g_signal_connect_object (object, signal_name,
                        func, connect_object, 0);
      } else {
            if (after)
                  g_signal_connect_after(object, signal_name,
                        func, user_data);
                  g_signal_connect(object, signal_name, func, user_data);

static gboolean
dates_selected_calendars_filter_func (GtkTreeModel *model,
                              GtkTreePath *path,
                              GtkTreeIter *iter,
                              gpointer data)
      GSList **list;
      gboolean selected;
      ECal *ecal;
      gchar *uid;

      gtk_tree_model_get (model, iter,
                  COL_SELECTED, &selected,
                  COL_CALPTR, &ecal, -1);
      uid = (gchar *)e_source_peek_uid (e_cal_get_source (ecal));
      list = (GSList **)data;
      if (selected) *list = g_slist_prepend (*list, uid);

      return FALSE; /* foreach goes on */

static gboolean
dates_sel_or_read_only_filter_func (GtkTreeModel *model,
                            GtkTreeIter *iter,
                            gpointer user_data)
      gboolean not_read_only, selected;

      gtk_tree_model_get (model, iter,
                  COL_SELECTED, &selected,
                  COL_NOTREADONLY, &not_read_only, -1);
      if ((!not_read_only) || (!selected))
            return FALSE;
            return TRUE;

static void
dates_toggle_renderer_toggled_cb (GtkCellRendererToggle *renderer,
                          gchar *path_str,
                          gpointer userdata)
      GtkTreeModel *model;
      GtkTreeIter iter;
      gboolean toggle;
      GSList *selected;

      model = GTK_TREE_MODEL (userdata);
      gtk_tree_model_get_iter_from_string (model, &iter, path_str);
      gtk_tree_model_get (model, &iter,
                      COL_SELECTED, &toggle, -1);
      gtk_list_store_set (GTK_LIST_STORE (model), &iter,
                      COL_SELECTED, !toggle, -1);

      selected = NULL;
      gtk_tree_model_foreach (model, dates_selected_calendars_filter_func,

      /* Changing the GConf selected calendars string will cause the
       * calendars to be updated (we're listening to this GConf value).
      gconf_client_set_list (gconf_client_get_default (),
            selected, NULL);

static void
connect_callbacks_view (DatesData * d)
      g_signal_connect (G_OBJECT (d->view), "date_changed",
                    G_CALLBACK (dates_date_changed_cb), d);
      g_signal_connect (G_OBJECT (d->view), "event_selected",
                    G_CALLBACK (dates_event_selected_cb), d);
      g_signal_connect (G_OBJECT (d->view), "event_moved",
                    G_CALLBACK (dates_event_moved_cb), d);
      g_signal_connect (G_OBJECT (d->view), "event_sized",
                    G_CALLBACK (dates_event_sized_cb), d);
      g_signal_connect (G_OBJECT (d->view), "event_activated",
                    G_CALLBACK (dates_edit_cb), d);
      g_signal_connect (G_OBJECT (d->view), "button_press_event",
                    G_CALLBACK (dates_button_press_cb), d);
      g_signal_connect (G_OBJECT (d->view), "key_press_event",
                    G_CALLBACK (dates_cal_key_press_cb), d);
      g_signal_connect (G_OBJECT (d->view), "ical_drop",
                    G_CALLBACK (dates_ical_drop_cb), d);

main (int argc, char **argv)
      GtkWidget *widget;
      DatesData data;
      GtkTreeModel *filter;
      GConfClient *client;
      GConfBridge *bridge;
      GOptionContext *context;
      static gint plug = 0;
#ifdef DEBUG
      const gchar *debug;
      static GOptionEntry entries[] = {
            { "plug", 'p', 0, G_OPTION_ARG_INT, &plug,
                  "Socket ID of an XEmbed socket to plug into", NULL },
            { NULL }

      memset (&data, 0, sizeof (DatesData));
      /* Initialise the i18n support code */
      bindtextdomain (GETTEXT_PACKAGE, DATES_LOCALE_DIR);;
      bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
      textdomain (GETTEXT_PACKAGE);

      g_set_application_name (_("Dates"));

      context = g_option_context_new (_(" - A light-weight, zooming calendar"));
      g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
      g_option_context_add_group (context, gtk_get_option_group (TRUE));
      g_option_context_parse (context, &argc, &argv, NULL);

      /* NOTE: No need to call gtk_init after the g_option_context_parse */
      /* gtk_init (&argc, &argv); */

#ifdef DEBUG
      debug = g_getenv ("DATES_DEBUG");
      if (debug)
            data.debug = g_parse_debug_string (debug,
                  dates_debug_keys, G_N_ELEMENTS (dates_debug_keys));

      /* Set critical errors to close application */
      /*g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL);*/

      data.view = DATES_VIEW (create_dates_view ());

      connect_callbacks_view (&data);

      gtk_window_set_default_icon_name ("dates");
      dates_platform_init (&data);
      dates_platform_create_ui (&data);
      client = gconf_client_get_default ();
      if (!client)
            g_error ("Failed to retrieve default gconf client");
      data.zoom    = gconf_client_get_int (client, DATES_GCONF_ZOOM, NULL);
      data.comp    = NULL;
      data.widgets = NULL;
      data.waiting = NONE;

      if (data.zoom == 0) {
            data.zoom = 3;
            gtk_widget_set_sensitive (data.TBZoomIn, FALSE);
      } else if (data.zoom == 3) {
            gtk_widget_set_sensitive (data.TBZoomIn, FALSE);
      } else if (data.zoom == 16) {
            gtk_widget_set_sensitive (data.TBZoomOut, FALSE);

      gtk_widget_set_sensitive (data.TBDelete, FALSE);
        /* Perhaps in a future version? */
        /* dates_view_set_use_list (data.view, TRUE);*/

      dates_zoom_change (data.zoom, data.view);

      /* Load calendars */
      data.cal_list_store = gtk_list_store_new (COL_LAST, G_TYPE_STRING,

      /* TODO: UI for multiple calendars */
            GtkTreeView *treeview;
            GtkCellRenderer *toggle_renderer, *label_renderer;
            GtkTreeViewColumn *toggle_column, *label_column;

            treeview = GTK_TREE_VIEW (data.cal_tree_view);
            gtk_tree_view_set_model (treeview,
                  GTK_TREE_MODEL (data.cal_list_store));

            toggle_renderer = gtk_cell_renderer_toggle_new ();
            g_signal_connect (G_OBJECT (toggle_renderer), "toggled",
                  G_CALLBACK (dates_toggle_renderer_toggled_cb),
            toggle_column = gtk_tree_view_column_new_with_attributes (
                  _("Selected"), toggle_renderer,
                  "active", COL_SELECTED, NULL);
            gtk_tree_view_append_column (treeview, toggle_column);

            label_renderer = gtk_cell_renderer_text_new ();
            label_column = gtk_tree_view_column_new_with_attributes (
                  _("Calendar"), label_renderer,
                  "text", COL_CALNAME, NULL);
            gtk_tree_view_append_column (treeview, label_column);

      data.first_load = FALSE;
      dates_load_calendars (&data);

            GtkTreeSelection *selection;
            GtkTreeIter iter;

            /* Select the first item in the list */
            selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data.cal_tree_view));
            gtk_tree_model_get_iter_first (GTK_TREE_MODEL (data.cal_list_store), &iter);
            gtk_tree_selection_select_iter (selection, &iter);

      /* Set transient parent for dialogs */
      if (GTK_IS_WINDOW (data.calendars_dialog))
            gtk_window_set_transient_for (
                  GTK_WINDOW (data.calendars_dialog),
                  GTK_WINDOW (data.main_window));
      if (GTK_IS_WINDOW (data.details_dialog))
            gtk_window_set_transient_for (
                  GTK_WINDOW (data.details_dialog),
                  GTK_WINDOW (data.main_window));
      if (GTK_IS_WINDOW (data.time_dialog) &&
          GTK_IS_WINDOW (data.details_dialog))
            gtk_window_set_transient_for (
                  GTK_WINDOW (data.time_dialog),
                  GTK_WINDOW (data.details_dialog));
      if (GTK_IS_WINDOW (data.exceptions_dialog) &&
          GTK_IS_WINDOW (data.details_dialog))
            gtk_window_set_transient_for (
                  GTK_WINDOW (data.exceptions_dialog),
                  GTK_WINDOW (data.details_dialog));

      /* Create tree model filter to filter out read-only calendars */
      filter = gtk_tree_model_filter_new (
            GTK_TREE_MODEL (data.cal_list_store), NULL);
      gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
            dates_sel_or_read_only_filter_func, NULL, NULL);
      /* Set tree model for details dialog calendar selector */
      widget = data.details_calendar_combobox;
      gtk_combo_box_set_model (GTK_COMBO_BOX (widget), filter);
      dates_date_changed_cb (data.view, &data);

      /* Bind widgets to gconf properties */
      bridge = gconf_bridge_get ();
      gconf_bridge_bind_window (bridge, DATES_GCONF_WINDOW, GTK_WINDOW (
            data.main_window), TRUE, TRUE);
/*    gconf_bridge_bind_property (bridge, DATES_GCONF_PREFIX,
            G_OBJECT (data.view), "week_start");
      gconf_bridge_bind_property (bridge, DATES_GCONF_PREFIX,
            G_OBJECT (data.view), "use_24h");*/

      /* Calendar selection signals */
      gconf_client_add_dir (client, CALENDAR_GCONF_PREFIX,
      gconf_client_notify_add (client, CALENDAR_GCONF_SELECTED,
            dates_gconf_selected_cb, &data, NULL, NULL);
      widget = data.main_window;

      dates_platform_pre_show (&data);
      if (plug > 0) {
            GtkWidget *plug_widget;
            GtkWidget *contents;
#ifdef DEBUG
            if (data.debug & DATES_DEBUG_XEMBED)
                  g_debug ("Plugging into socket %d", plug);
            plug_widget = gtk_plug_new (plug);
            contents = g_object_ref (gtk_bin_get_child (GTK_BIN (widget)));
            gtk_container_remove (GTK_CONTAINER (widget), contents);
            gtk_container_add (GTK_CONTAINER (plug_widget), contents);
            g_object_unref (contents);
            g_signal_connect (G_OBJECT (plug_widget), "destroy",
                          G_CALLBACK (gtk_main_quit), NULL);
            gtk_widget_hide (data.main_menu);
            gtk_widget_show (data.header_eventbox);
            gtk_widget_show (plug_widget);
      } else {
            gtk_widget_show (data.main_window);

      gtk_main ();

      /* clean up */
      g_option_context_free (context);
      return 0;

Generated by  Doxygen 1.6.0   Back to index