/* * openmoko-dates -- OpenMoko Calendar Application * * Authored by Chris Lord * * Copyright (C) 2006-2007 OpenMoko Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser Public License as published by * the Free Software Foundation; version 2 of the license. * * 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 Lesser Public License for more details. * * Current Version: $Rev$ ($Date$) [$Author$] */ #include #include #include #include #include #include #include #include #include "dates-timezone-tile.h" #include "dates.h" #include "dates-list.h" #include "dates-utils.h" static void edit_toggled_cb (GtkToggleToolButton *button, DatesData *data); static const gchar *summary_field = "Summary"; static const gchar *location_field = "Location"; static const gchar *category_field = "Category"; static const gchar *description_field = "Description"; static const gchar *locations[] = { "No timezone set (UTC)","UTC", "Amsterdam", "Europe/Amsterdam", "Auckland", "Pacific/Auckland", "Berlin", "Europe/Berlin", "Buenos Aires", "America/Argentina/Buenos_Aires", "Cairo", "Africa/Cairo", "Calcutta", "Asia/Calcutta", "Chicago", "America/Chicago", "Denver", "America/Denver", "Dublin", "Europe/Dublin", "Hong Kong", "Asia/Hong_Kong", "Honolulu", "Pacific/Honolulu", "Istanbul", "Europe/Istanbul", "Jakarta", "Asia/Jakarta", "Karachi", "Asia/Karachi", "Kinshasa", "Africa/Kinshasa", "Lagos", "Africa/Lagos", "Lima", "America/Lima", "London", "Europe/London", "Los Angeles", "America/Los_Angeles", "Madrid", "Europe/Madrid", "Manila", "Asia/Manila", "Mexico City", "America/Mexico_City", "Montreal", "America/Montreal", "Moscow", "Europe/Moscow", "New York", "America/New_York", "Paris", "Europe/Paris", "Rome", "Europe/Rome", "Sao Paulo", "America/Sao_Paulo", "Seoul", "Asia/Seoul", "Shanghai", "Asia/Shanghai", "Singapore", "Asia/Singapore", "Stockholm", "Europe/Stockholm", "Sydney", "Australia/Sydney", "Taipei", "Asia/Taipei", "Tehran", "Asia/Tehran", "Tijuana", "America/Tijuana", "Tokyo", "Asia/Tokyo", "Toronto", "America/Toronto", "Vancouver", "America/Vancouver", NULL, NULL, }; /* * Some timezones I considered adding, but were missing from libical: * Beijing * Dallas * Delhi * Geneva * Osaka-Kobe-Kyoto * Mexico * Mumbai * Peking * Rhein-Ruhr * Rio de Janeiro * San Francisco * Seattle * Washington DC */ static gboolean hidden = TRUE; static void timezone_combo_changed_cb (GtkComboBox *combo, DatesData *data); static void clear_details (DatesData *data) { GtkTextBuffer *buffer; GtkTreeIter iter; GtkTreeModel *model = gtk_combo_box_get_model ( GTK_COMBO_BOX (data->category_combo)); while (gtk_tree_model_get_iter_first (model, &iter)) gtk_combo_box_remove_text ( GTK_COMBO_BOX (data->category_combo), 0); g_signal_handlers_disconnect_by_func (data->timezone_combo, timezone_combo_changed_cb, data); model = gtk_combo_box_get_model (GTK_COMBO_BOX (data->timezone_combo)); while (gtk_tree_model_iter_n_children (model, NULL) >= G_N_ELEMENTS(locations)/2) gtk_combo_box_remove_text ( GTK_COMBO_BOX (data->timezone_combo), (G_N_ELEMENTS(locations)/2) - 1); gtk_entry_set_text (GTK_ENTRY (GTK_BIN ( data->category_combo)->child), ""); gtk_entry_set_text (GTK_ENTRY (data->summary_entry), ""); gtk_entry_set_text (GTK_ENTRY (data->location_entry), ""); buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW (data->description_text_view)); gtk_text_buffer_set_text (buffer, "", -1); jana_gtk_date_time_set_time (JANA_GTK_DATE_TIME (data->start_datetime), NULL); jana_gtk_date_time_set_time (JANA_GTK_DATE_TIME (data->end_datetime), NULL); jana_gtk_recurrence_set_recur (JANA_GTK_RECURRENCE (data->recur), NULL); jana_gtk_recurrence_set_time (JANA_GTK_RECURRENCE (data->recur), NULL); gtk_widget_hide (data->category_combo); gtk_widget_hide (data->summary_entry); gtk_widget_hide (data->location_entry); gtk_widget_hide (data->description_frame); gtk_widget_hide (data->timezone_combo); gtk_frame_set_shadow_type (GTK_FRAME (data->start_frame), GTK_SHADOW_NONE); gtk_frame_set_shadow_type (GTK_FRAME (data->end_frame), GTK_SHADOW_NONE); gtk_widget_hide (data->start_label); gtk_widget_hide (data->end_label); gtk_widget_hide (data->allday_check_button); gtk_widget_hide (data->recur_frame); } static void fill_details (DatesData *data) { gchar *summary, *location, *description, **categories; gboolean location_set = FALSE; GList *cat, *category_list; const gchar *tz_location; JanaTime *start, *end; JanaRecurrence *recur; GtkTextBuffer *buffer; gint i; summary = jana_event_get_summary (data->selected_event); location = jana_event_get_location (data->selected_event); description = jana_event_get_description (data->selected_event); categories = jana_event_get_categories (data->selected_event); start = jana_event_get_start (data->selected_event); end = jana_event_get_end (data->selected_event); recur = jana_event_get_recurrence (data->selected_event); if (summary) { gtk_entry_set_text (GTK_ENTRY (data->summary_entry),summary); gtk_widget_show (data->summary_entry); } if (location) { gtk_entry_set_text (GTK_ENTRY (data->location_entry), location); gtk_widget_show (data->location_entry); } if (description) { buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW (data->description_text_view)); gtk_text_buffer_set_text (buffer, description, -1); gtk_widget_show (data->description_frame); } category_list = g_list_copy (data->category_list); category_list = g_list_sort (category_list, (GCompareFunc) g_utf8_collate); category_list = dates_utils_insert_preset_categories (category_list); for (cat = category_list; cat; cat = cat->next) { const gchar *category = (const gchar *)cat->data; gtk_combo_box_append_text ( GTK_COMBO_BOX (data->category_combo), category); } gtk_combo_box_prepend_text (GTK_COMBO_BOX (data->category_combo), "No category"); if (categories && categories[0]) { /* TODO: Support multiple categories? */ gtk_entry_set_text (GTK_ENTRY (GTK_BIN ( data->category_combo)->child), categories[0]); gtk_widget_show (data->category_combo); } g_list_free (category_list); gtk_widget_show (data->timezone_combo); /* Find the pretty time-zone name */ tz_location = jana_ecal_time_get_location (JANA_ECAL_TIME (start)); for (i = 1; locations[i]; i += 2) { if (strcmp (locations[i], tz_location) == 0) { gtk_combo_box_set_active (GTK_COMBO_BOX ( data->timezone_combo), (i/2)); location_set = TRUE; break; } } /* Pretty location name not found, use ugly one */ if (!location_set) { g_free (location); location = g_strdup (tz_location); gtk_combo_box_append_text (GTK_COMBO_BOX (data->timezone_combo), g_strdelimit (location, "_", ' ')); gtk_combo_box_set_active (GTK_COMBO_BOX ( data->timezone_combo), (G_N_ELEMENTS(locations)/2)-1); } /* Add 'Other...' timezone option */ gtk_combo_box_append_text (GTK_COMBO_BOX (data->timezone_combo), "Other..."); jana_gtk_date_time_set_time (JANA_GTK_DATE_TIME (data->start_datetime), start); jana_gtk_date_time_set_time (JANA_GTK_DATE_TIME (data->end_datetime), end); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( data->allday_check_button), jana_time_get_isdate (start)); g_signal_connect (data->timezone_combo, "changed", G_CALLBACK (timezone_combo_changed_cb), data); jana_gtk_recurrence_set_recur (JANA_GTK_RECURRENCE (data->recur),recur); jana_gtk_recurrence_set_time (JANA_GTK_RECURRENCE (data->recur), start); gtk_widget_show (data->recur_frame); g_free (summary); g_free (location); g_free (description); g_strfreev (categories); g_object_unref (start); g_object_unref (end); jana_recurrence_free (recur); } static void edit_event (DatesData *data) { GtkTextBuffer *buffer; GtkTextIter start_iter, end_iter; const gchar *summary, *location, *category; gchar *old_summary, *old_location, *old_description, *description, **old_categories, *old_category; JanaTime *old_start, *old_end, *start, *end; JanaRecurrence *old_recur, *recur; gboolean changed = FALSE; old_summary = jana_event_get_summary (data->selected_event); if (!old_summary) old_summary = g_strdup (""); summary = gtk_entry_get_text (GTK_ENTRY (data->summary_entry)); if (!summary) summary = ""; old_location = jana_event_get_location (data->selected_event); if (!old_location) old_location = g_strdup (""); location = gtk_entry_get_text (GTK_ENTRY (data->location_entry)); if (!location) location = ""; old_description = jana_event_get_description (data->selected_event); if (!old_description) old_description = g_strdup (""); buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW ( data->description_text_view)); gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter); description = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE); if (!description) description = g_strdup (""); old_start = jana_event_get_start (data->selected_event); start = jana_gtk_date_time_get_time (JANA_GTK_DATE_TIME ( data->start_datetime)); old_end = jana_event_get_end (data->selected_event); end = jana_gtk_date_time_get_time (JANA_GTK_DATE_TIME ( data->end_datetime)); old_categories = jana_event_get_categories (data->selected_event); old_category = (old_categories && old_categories[0]) ? g_strdup (old_categories[0]) : g_strdup (""); g_strfreev (old_categories); category = gtk_entry_get_text (GTK_ENTRY (GTK_BIN ( data->category_combo)->child)); old_recur = jana_event_get_recurrence (data->selected_event); recur = jana_gtk_recurrence_get_recur ( JANA_GTK_RECURRENCE (data->recur)); if ((strcmp (summary, old_summary) != 0) || (strcmp (location, old_location) != 0) || (strcmp (description, old_description) != 0) || (jana_utils_time_compare (start, old_start, FALSE) != 0) || (jana_utils_time_compare (end, old_end, FALSE) != 0) || (strcmp (category, old_category) != 0) || (jana_utils_recurrence_diff (old_recur, recur))) changed = TRUE; if (changed) { const gchar *categories[2]; categories[0] = category; categories[1] = NULL; jana_event_set_summary (data->selected_event, summary); jana_event_set_location (data->selected_event, location); jana_event_set_description (data->selected_event, description); jana_event_set_start (data->selected_event, start); jana_event_set_end (data->selected_event, end); jana_event_set_categories (data->selected_event, categories); jana_event_set_recurrence (data->selected_event, recur); if (data->selected_store) { jana_store_modify_component (data->selected_store, JANA_COMPONENT (data->selected_event)); } else if (data->stores) { /* Create a new event */ if ((!summary) || (summary[0] == '\0')) { /* TODO: Dialog warning about empty summary */ } data->selected_store = g_object_ref ( data->stores->data); jana_store_add_component (data->selected_store, JANA_COMPONENT (data->selected_event)); } } g_object_unref (end); g_object_unref (old_end); g_object_unref (start); g_object_unref (old_start); g_free (description); g_free (old_description); g_free (old_location); g_free (old_summary); g_free (old_category); jana_recurrence_free (old_recur); jana_recurrence_free (recur); } static void page_shown (DatesData *data) { if (data->title_idle) { g_source_remove (data->title_idle); data->title_idle = 0; } gtk_window_set_title (GTK_WINDOW (data->window), "Details"); /* Reset to view mode */ gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON ( data->edit_button), FALSE); /* Set event details, if we have one selected */ clear_details (data); if (data->selected_event) { fill_details (data); gtk_toggle_tool_button_set_active ( GTK_TOGGLE_TOOL_BUTTON (data->edit_button), data->editing); } edit_toggled_cb (GTK_TOGGLE_TOOL_BUTTON (data->edit_button), data); } static void page_hidden (DatesData *data) { gtk_toggle_tool_button_set_active ( GTK_TOGGLE_TOOL_BUTTON (data->edit_button), FALSE); edit_toggled_cb (GTK_TOGGLE_TOOL_BUTTON (data->edit_button), data); clear_details (data); if (!data->title_idle) data->title_idle = g_idle_add ((GSourceFunc) dates_utils_title_idle_cb, data); } static void notify_visible_cb (GObject *gobject, GParamSpec *arg1, DatesData *data) { if ((!hidden) && (!GTK_WIDGET_VISIBLE (gobject))) { hidden = TRUE; page_hidden (data); } } static gboolean visibility_notify_event_cb (GtkWidget *widget, GdkEventVisibility *event, DatesData *data) { if (((event->state == GDK_VISIBILITY_PARTIAL) || (event->state == GDK_VISIBILITY_UNOBSCURED)) && (hidden)) { hidden = FALSE; page_shown (data); }/* else if ((event->state == GDK_VISIBILITY_FULLY_OBSCURED) && (!hidden)) { hidden = TRUE; }*/ return FALSE; } static void unmap_cb (GtkWidget *widget, DatesData *data) { if (!hidden) { hidden = TRUE; page_hidden (data); } } static void delete_clicked_cb (GtkToolButton *button, DatesData *data) { if (!data->selected_event) return; if (!data->selected_store) { /* TODO: Should we prompt when cancelling a new event? */ g_object_unref (data->selected_event); data->selected_event = NULL; clear_details (data); gtk_toggle_tool_button_set_active ( GTK_TOGGLE_TOOL_BUTTON (data->edit_button), FALSE); edit_toggled_cb (GTK_TOGGLE_TOOL_BUTTON (data->edit_button), data); /* Switch to the last page */ gtk_notebook_set_current_page (GTK_NOTEBOOK ( data->notebook), data->last_page); } else { gchar *summary; GtkDialog *dialog; summary = jana_event_get_summary (data->selected_event); dialog = GTK_DIALOG (gtk_message_dialog_new_with_markup ( GTK_WINDOW (data->window), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "Would you like to delete\n" "\"%s\"?", summary)); gtk_dialog_add_buttons (dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_DELETE, GTK_RESPONSE_ACCEPT, NULL); if (gtk_dialog_run (dialog) == GTK_RESPONSE_ACCEPT) { jana_store_remove_component (data->selected_store, JANA_COMPONENT (data->selected_event)); g_object_unref (data->selected_event); data->selected_event = NULL; g_object_unref (data->selected_store); data->selected_store = NULL; clear_details (data); gtk_toggle_tool_button_set_active ( GTK_TOGGLE_TOOL_BUTTON (data->edit_button), FALSE); edit_toggled_cb (GTK_TOGGLE_TOOL_BUTTON ( data->edit_button), data); /* Switch to the last page */ gtk_notebook_set_current_page (GTK_NOTEBOOK ( data->notebook), data->last_page); } gtk_widget_destroy (GTK_WIDGET (dialog)); g_free (summary); } } static void set_entry_editable (GtkWidget *entry, gboolean editable) { gtk_editable_set_editable (GTK_EDITABLE (entry), editable); gtk_entry_set_has_frame (GTK_ENTRY (entry), editable); /* FIXME: No way to hide cursor on GtkEntry :( */ if (editable) { gtk_widget_modify_base (entry, GTK_STATE_NORMAL, NULL); gtk_widget_modify_text (entry, GTK_STATE_NORMAL, NULL); gtk_widget_show (entry); } else { if (g_object_get_data (G_OBJECT (entry), "empty")) { gtk_entry_set_text (GTK_ENTRY (entry), ""); gtk_widget_hide (entry); } gtk_widget_modify_base (entry, GTK_STATE_NORMAL, &entry->style->bg[GTK_STATE_NORMAL]); gtk_widget_modify_text (entry, GTK_STATE_NORMAL, &entry->style->fg[GTK_STATE_NORMAL]); gtk_editable_select_region (GTK_EDITABLE (entry), 0, 0); } } static void set_text_view_editable (GtkWidget *text_view, GtkWidget *frame, gboolean editable) { gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), editable); g_object_set (G_OBJECT (text_view), "cursor-visible", editable, NULL); if (editable) { gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_widget_modify_base (text_view, GTK_STATE_NORMAL, NULL); gtk_widget_modify_text (text_view, GTK_STATE_NORMAL, NULL); gtk_widget_show (frame); } else { GtkTextBuffer *buffer; gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); gtk_widget_modify_base (text_view, GTK_STATE_NORMAL, &text_view->style->bg[GTK_STATE_NORMAL]); gtk_widget_modify_text (text_view, GTK_STATE_NORMAL, &text_view->style->fg[GTK_STATE_NORMAL]); if (g_object_get_data (G_OBJECT (text_view), "empty")) { gtk_widget_hide (frame); } else { buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW (text_view)); if (gtk_text_buffer_get_char_count (buffer) == 0) gtk_widget_hide (frame); } } } /* These 2 callbacks stolen from openmoko-contacts and modified, * originally by Thomas Wood. */ static gboolean entry_focus_in_cb (GtkWidget *entry, GdkEventFocus *event, const gchar *fieldname) { if (g_object_get_data (G_OBJECT (entry), "empty")) { gtk_entry_set_text (GTK_ENTRY (entry), ""); gtk_widget_modify_text (entry, GTK_STATE_NORMAL, NULL); g_object_set_data (G_OBJECT (entry), "empty", NULL); } return FALSE; } static gboolean entry_focus_out_cb (GtkWidget *entry, GdkEventFocus *event, const gchar *fieldname) { const gchar *contents = gtk_entry_get_text (GTK_ENTRY (entry)); /* simple way to check if we are editing (has frame == editing) */ if (!gtk_entry_get_has_frame (GTK_ENTRY (entry))) return FALSE; if (contents && (contents[0] == '\0')) { gtk_entry_set_text (GTK_ENTRY (entry), fieldname); gtk_widget_modify_text (entry, GTK_STATE_NORMAL, &entry->style->text[GTK_STATE_INSENSITIVE]); g_object_set_data (G_OBJECT (entry), "empty", GUINT_TO_POINTER (1)); } return FALSE; } static gboolean text_view_focus_in_cb (GtkWidget *text_view, GdkEventFocus *event, const gchar *fieldname) { if (g_object_get_data (G_OBJECT (text_view), "empty")) { GtkTextBuffer *buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW (text_view)); gtk_text_buffer_set_text (buffer, "", -1); gtk_widget_modify_text (text_view, GTK_STATE_NORMAL, NULL); g_object_set_data (G_OBJECT (text_view), "empty", NULL); } return FALSE; } static gboolean text_view_focus_out_cb (GtkWidget *text_view, GdkEventFocus *event, const gchar *fieldname) { GtkTextBuffer *buffer; GtkTextIter start, end; gchar *contents; gboolean editing; /* Check if we're editing by checking cursor visibility */ g_object_get (G_OBJECT (text_view), "cursor-visible", &editing, NULL); if (!editing) return FALSE; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); gtk_text_buffer_get_bounds (buffer, &start, &end); contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); if (contents && (contents[0] == '\0')) { gtk_text_buffer_set_text (buffer, fieldname, -1); gtk_widget_modify_text (text_view, GTK_STATE_NORMAL, &text_view->style->text[GTK_STATE_INSENSITIVE]); g_object_set_data (G_OBJECT (text_view), "empty", GUINT_TO_POINTER (1)); } g_free (contents); return FALSE; } static void allday_toggled_cb (GtkToggleButton *toggle, DatesData *data) { JanaTime *start, *end; start = jana_gtk_date_time_get_time (JANA_GTK_DATE_TIME ( data->start_datetime)); end = jana_gtk_date_time_get_time (JANA_GTK_DATE_TIME ( data->end_datetime)); jana_time_set_isdate (start, gtk_toggle_button_get_active (toggle)); jana_time_set_isdate (end, gtk_toggle_button_get_active (toggle)); if (jana_utils_time_compare (start, end, TRUE) == 0) jana_time_set_day (end, jana_time_get_day (end) + 1); jana_event_set_start (data->selected_event, start); jana_event_set_start (data->selected_event, end); jana_gtk_date_time_set_time (JANA_GTK_DATE_TIME ( data->start_datetime), start); jana_gtk_date_time_set_time (JANA_GTK_DATE_TIME ( data->end_datetime), end); g_object_unref (start); g_object_unref (end); } static void edit_toggled_cb (GtkToggleToolButton *button, DatesData *data) { gboolean editable = gtk_toggle_tool_button_get_active (button); data->editing = editable; if (editable && (!data->selected_event)) { GtkDialog *dialog; gint result; dialog = GTK_DIALOG (gtk_message_dialog_new ( GTK_WINDOW (data->window), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "Create a new event?")); gtk_dialog_add_buttons (dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT, NULL); result = gtk_dialog_run (dialog); if (result == GTK_RESPONSE_ACCEPT) { JanaTime *time = jana_time_duplicate (data->start); jana_time_set_isdate (time, FALSE); jana_time_set_hours (time, 9); dates_utils_create_new_event (data, time); fill_details (data); /* Set the last selected tab to this one */ data->last_page = -1; } gtk_widget_destroy (GTK_WIDGET (dialog)); if (result != GTK_RESPONSE_ACCEPT) { gtk_toggle_tool_button_set_active ( GTK_TOGGLE_TOOL_BUTTON (data->edit_button), FALSE); return; } } gtk_widget_set_sensitive (data->category_combo, editable); set_entry_editable (data->summary_entry, editable); set_entry_editable (data->location_entry, editable); gtk_widget_set_sensitive (data->timezone_combo, editable); set_text_view_editable (data->description_text_view, data->description_frame, editable); g_object_set (G_OBJECT (data->allday_check_button), "visible", editable, NULL); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->start_toggle), FALSE); g_object_set (G_OBJECT (data->start_toggle), "visible", editable, NULL); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->end_toggle), FALSE); g_object_set (G_OBJECT (data->end_toggle), "visible", editable, NULL); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->recur_toggle), FALSE); g_object_set (G_OBJECT (data->recur_toggle), "visible", editable, NULL); gtk_frame_set_shadow_type (GTK_FRAME (data->recur_frame), editable ? GTK_SHADOW_IN : GTK_SHADOW_NONE); jana_gtk_date_time_set_editable (JANA_GTK_DATE_TIME ( data->start_datetime), FALSE); jana_gtk_date_time_set_editable (JANA_GTK_DATE_TIME ( data->end_datetime), FALSE); jana_gtk_recurrence_set_editable (JANA_GTK_RECURRENCE ( data->recur), FALSE); if (editable) { g_signal_connect (data->allday_check_button, "toggled", G_CALLBACK (allday_toggled_cb), data); gtk_widget_hide (data->start_label); gtk_widget_hide (data->end_label); gtk_widget_show (data->category_combo); gtk_widget_modify_text (GTK_BIN (data->category_combo)->child, GTK_STATE_INSENSITIVE, NULL); gtk_widget_modify_bg (data->start_ebox, GTK_STATE_NORMAL, &data->summary_entry->style->base[GTK_STATE_NORMAL]); gtk_widget_modify_bg (data->end_ebox, GTK_STATE_NORMAL, &data->summary_entry->style->base[GTK_STATE_NORMAL]); gtk_frame_set_shadow_type (GTK_FRAME (data->start_frame), GTK_SHADOW_IN); gtk_frame_set_shadow_type (GTK_FRAME (data->end_frame), GTK_SHADOW_IN); /* Fill widgets with hint data if necessary */ entry_focus_out_cb (GTK_BIN (data->category_combo)->child, NULL, category_field); entry_focus_out_cb (data->location_entry, NULL, location_field); text_view_focus_out_cb (data->description_text_view, NULL, description_field); /* Focus summary entry widget */ gtk_widget_grab_focus (data->summary_entry); } else { g_signal_handlers_disconnect_by_func (data->allday_check_button, allday_toggled_cb, data); if (data->selected_event) { gtk_widget_show (data->start_label); gtk_widget_show (data->end_label); gtk_frame_set_shadow_type ( GTK_FRAME (data->start_frame), GTK_SHADOW_NONE); gtk_frame_set_shadow_type ( GTK_FRAME (data->end_frame), GTK_SHADOW_NONE); } if (g_object_get_data (G_OBJECT (GTK_BIN ( data->category_combo)->child), "empty") || (gtk_combo_box_get_active (GTK_COMBO_BOX ( data->category_combo)) == 0)) { gtk_widget_hide (data->category_combo); gtk_entry_set_text (GTK_ENTRY (GTK_BIN ( data->category_combo)->child), ""); } entry_focus_in_cb (data->summary_entry, NULL, summary_field); entry_focus_in_cb (data->location_entry, NULL, location_field); text_view_focus_in_cb (data->description_text_view, NULL, description_field); if (data->selected_event) edit_event (data); gtk_widget_modify_text (GTK_BIN (data->category_combo)->child, GTK_STATE_INSENSITIVE, >K_BIN (data->category_combo)-> child->style->text[GTK_STATE_NORMAL]); gtk_widget_modify_text (GTK_BIN (data->timezone_combo)->child, GTK_STATE_INSENSITIVE, >K_BIN (data->timezone_combo)-> child->style->text[GTK_STATE_NORMAL]); gtk_widget_modify_bg (data->start_ebox, GTK_STATE_NORMAL, NULL); gtk_widget_modify_bg (data->end_ebox, GTK_STATE_NORMAL, NULL); } } static void time_toggled_cb (GtkToggleButton *toggle, GtkAlignment *align) { JanaGtkDateTime *dt; gboolean active = gtk_toggle_button_get_active (toggle); if (active) gtk_alignment_set (align, 0.5, 0.5, 1, 1); else gtk_alignment_set (align, 0, 0.5, 0, 1); dt = JANA_GTK_DATE_TIME (gtk_bin_get_child (GTK_BIN (align))); jana_gtk_date_time_set_editable (dt, active); } static void combo_changed_cb (GtkComboBox *combo, GtkWidget *entry) { gtk_widget_modify_text (entry, GTK_STATE_NORMAL, NULL); g_object_set_data (G_OBJECT (entry), "empty", NULL); gtk_widget_grab_focus (entry); } static gboolean ignore_next = FALSE; static void start_changed_cb (JanaGtkDateTime *start, DatesData *data) { gint year, month, day, hours, minutes; glong seconds; JanaTime *initial, *current, *end; if (!data->selected_event) return; current = jana_gtk_date_time_get_time (start); if (!current) return; if (!ignore_next) { /* Move the end time with the start time */ end = jana_event_get_end (data->selected_event); initial = jana_event_get_start (data->selected_event); jana_utils_time_diff (initial, current, &year, &month, &day, &hours, &minutes, &seconds); jana_utils_time_adjust (end, year, month, day, hours, minutes, seconds); ignore_next = TRUE; jana_gtk_date_time_set_time ( JANA_GTK_DATE_TIME (data->end_datetime), end); g_object_unref (initial); g_object_unref (end); } else { ignore_next = FALSE; } jana_event_set_start (data->selected_event, current); jana_gtk_recurrence_set_time ( JANA_GTK_RECURRENCE (data->recur), current); g_object_unref (current); } static void end_changed_cb (JanaGtkDateTime *end, DatesData *data) { gint year, month, day, hours, minutes; glong seconds; JanaTime *initial, *current, *start; if (!data->selected_event) return; current = jana_gtk_date_time_get_time (end); if (!current) return; if (!ignore_next) { /* Shift the start back if we move before it */ start = jana_gtk_date_time_get_time ( JANA_GTK_DATE_TIME (data->start_datetime)); if (jana_utils_time_compare (current, start, FALSE) <= 0) { initial = jana_event_get_end (data->selected_event); jana_utils_time_diff (initial, current, &year, &month, &day, &hours, &minutes, &seconds); jana_utils_time_adjust (start, year, month, day, hours, minutes, seconds); ignore_next = TRUE; jana_gtk_date_time_set_time (JANA_GTK_DATE_TIME ( data->start_datetime), start); g_object_unref (initial); } g_object_unref (start); } else { ignore_next = FALSE; } jana_event_set_end (data->selected_event, current); g_object_unref (current); } static void set_location (DatesData *data, gchar *new_location) { JanaTime *start, *end; const gchar *old_location; start = jana_event_get_start (data->selected_event); old_location = jana_ecal_time_get_location (JANA_ECAL_TIME (start)); if (strcmp (old_location, new_location) != 0) { end = jana_event_get_end (data->selected_event); jana_ecal_time_set_location ( JANA_ECAL_TIME (start), new_location); jana_ecal_time_set_location ( JANA_ECAL_TIME (end), new_location); jana_event_set_start (data->selected_event, start); jana_event_set_end (data->selected_event, end); jana_gtk_date_time_set_time (JANA_GTK_DATE_TIME ( data->start_datetime), start); jana_gtk_date_time_set_time (JANA_GTK_DATE_TIME ( data->end_datetime), end); g_object_unref (end); } g_object_unref (start); } static void timezone_tile_clicked_cb (TakuTile *tile, DatesData *data) { GtkWidget *window; gchar *location; gint i; gboolean location_set = FALSE; /* Get timezone from tile */ location = g_strdup (taku_icon_tile_get_primary ( TAKU_ICON_TILE (tile))); /* Set timezone */ g_strdelimit (location, " ", '_'); set_location (data, location); /* Select timezone */ for (i = 1; locations[i]; i+= 2) { if (strcmp (location, locations[i]) == 0) { gtk_combo_box_set_active (GTK_COMBO_BOX ( data->timezone_combo), i/2); location_set = TRUE; break; } } if (!location_set) { /* Check the extra locations that may have been added */ GtkTreeModel *model = gtk_combo_box_get_model ( GTK_COMBO_BOX (data->timezone_combo)); gint n_children = gtk_tree_model_iter_n_children ( model, NULL); g_strdelimit (location, "_", ' '); for (i = (G_N_ELEMENTS(locations)/2)-1; i < n_children-1; i++) { GtkTreeIter iter; gchar *text; gint cmp; gtk_tree_model_iter_nth_child (model, &iter, NULL, i); gtk_tree_model_get (model, &iter, 0, &text, -1); cmp = strcmp (text, location); g_free (text); if (cmp == 0) { gtk_combo_box_set_active (GTK_COMBO_BOX ( data->timezone_combo), i); location_set = TRUE; break; } } /* Add the location */ if (!location_set) { gtk_combo_box_insert_text (GTK_COMBO_BOX ( data->timezone_combo), n_children - 1, location); gtk_combo_box_set_active (GTK_COMBO_BOX ( data->timezone_combo), n_children - 1); } } g_free (location); /* Close window */ window = gtk_widget_get_toplevel (GTK_WIDGET (tile)); gtk_widget_destroy (window); } static void timezone_search_changed_cb (GtkEntry *entry, DatesData *data) { TakuTable *table = TAKU_TABLE (g_object_get_data ( G_OBJECT (entry), "table")); taku_table_set_filter (table, (gpointer)gtk_entry_get_text (entry)); } static void timezone_combo_changed_cb (GtkComboBox *combo, DatesData *data) { GtkTreeModel *model; gchar *new_location; gint n_children, active; model = gtk_combo_box_get_model (combo); n_children = gtk_tree_model_iter_n_children (model, NULL); active = gtk_combo_box_get_active (combo); if (active < 0) return; if (active < (G_N_ELEMENTS(locations)/2) - 1) { new_location = g_strdup (locations[(active*2)+1]); } else if (active < (n_children - 1)) { new_location = gtk_combo_box_get_active_text (combo); g_strdelimit (new_location, " ", '_'); } else { /* Create timezone dialog */ JanaTime *now; GtkWidget *window, *box, *scroll, *table, *tile, *entry; gchar **full_locations; gint i; /* Create window, set parent and set undecorated */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (data->window)); gtk_window_set_modal (GTK_WINDOW (window), TRUE); gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG); /* Create table and add timezone tiles */ table = taku_table_new (); full_locations = jana_ecal_utils_get_locations (); now = jana_ecal_utils_time_now (NULL); for (i = 0; full_locations[i]; i++) { jana_ecal_time_set_location (JANA_ECAL_TIME (now), full_locations[i]); tile = dates_timezone_tile_new (); dates_timezone_tile_set_timezone (DATES_TIMEZONE_TILE ( tile), JANA_ECAL_TIME (now)); gtk_container_add (GTK_CONTAINER (table), tile); g_signal_connect (tile, "clicked", G_CALLBACK (timezone_tile_clicked_cb), data); } g_object_unref (now); /* Create search entry */ entry = gtk_entry_new (); g_object_set_data (G_OBJECT (entry), "table", (gpointer)table); g_signal_connect (entry, "changed", G_CALLBACK (timezone_search_changed_cb), data); /* Pack and show */ scroll = moko_finger_scroll_new (); moko_finger_scroll_add_with_viewport ( MOKO_FINGER_SCROLL (scroll), table); box = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0); gtk_box_pack_end (GTK_BOX (box), entry, FALSE, TRUE, 0); gtk_container_add (GTK_CONTAINER (window), box); gtk_window_resize (GTK_WINDOW (window), (data->window->allocation.width * 2)/3, (data->window->allocation.height * 2)/3); gtk_widget_show_all (window); gtk_widget_grab_focus (entry); return; } /* Set the location */ set_location (data, new_location); g_free (new_location); } static void recur_toggled_cb (GtkToggleButton *toggle, DatesData *data) { jana_gtk_recurrence_set_editable (JANA_GTK_RECURRENCE ( data->recur), gtk_toggle_button_get_active (toggle)); } GtkWidget * dates_create_view_page (DatesData *data) { GtkWidget *vbox, *toolbar, *scroll, *viewport, *edit_vbox, *start_vbox, *end_vbox, *start_hbox, *end_hbox, *recur_vbox; GtkToolItem *button; gint i; vbox = gtk_vbox_new (FALSE, 0); /* Create toolbar */ toolbar = gtk_toolbar_new (); gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0); /* Delete button */ button = dates_utils_toolbutton_new (GTK_STOCK_DELETE); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), button, 0); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), gtk_separator_tool_item_new (), 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (delete_clicked_cb), data); /* Edit toggle */ data->edit_button = dates_utils_toggle_toolbutton_new (GTK_STOCK_EDIT); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), data->edit_button, 0); g_signal_connect (G_OBJECT (data->edit_button), "toggled", G_CALLBACK (edit_toggled_cb), data); gtk_widget_show_all (toolbar); /* Create editing widgets */ edit_vbox = gtk_vbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (edit_vbox), 12); gtk_widget_show (edit_vbox); data->summary_entry = gtk_entry_new (); data->location_entry = gtk_entry_new (); data->description_text_view = gtk_text_view_new (); gtk_text_view_set_accepts_tab ( GTK_TEXT_VIEW (data->description_text_view), FALSE); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW ( data->description_text_view), GTK_WRAP_WORD_CHAR); data->description_frame = gtk_frame_new (NULL); gtk_container_add (GTK_CONTAINER (data->description_frame), data->description_text_view); gtk_widget_show (data->description_text_view); data->category_combo = gtk_combo_box_entry_new_text (); /* All-day event check-box */ data->allday_check_button = gtk_check_button_new_with_label ("All-day event"); /* Time-zone combo box */ data->timezone_combo = gtk_combo_box_new_text (); for (i = 0; locations[i]; i+=2) { gtk_combo_box_append_text (GTK_COMBO_BOX ( data->timezone_combo), locations[i]); } /* Start time editor widgets */ data->start_frame = gtk_frame_new (NULL); data->start_ebox = gtk_event_box_new (); data->start_datetime = jana_gtk_date_time_new (NULL); data->start_align = gtk_alignment_new (0, 0.5, 0, 1); gtk_container_set_border_width (GTK_CONTAINER (data->start_align), 6); data->start_toggle = gtk_toggle_button_new_with_label ("Change start"); gtk_widget_set_name (data->start_toggle, "moko-dates-vborder-button"); gtk_container_add (GTK_CONTAINER (data->start_align), data->start_datetime); gtk_container_add (GTK_CONTAINER (data->start_ebox), data->start_align); gtk_container_add (GTK_CONTAINER (data->start_frame), data->start_ebox); start_vbox = gtk_vbox_new (FALSE, 0); start_hbox = gtk_hbox_new (FALSE, 0); data->start_label = gtk_label_new ("Starts: "); gtk_box_pack_start (GTK_BOX (start_hbox), data->start_label, FALSE, TRUE, 0); gtk_box_pack_end (GTK_BOX (start_hbox), data->start_frame, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (start_vbox), start_hbox, FALSE, TRUE, 0); gtk_box_pack_end (GTK_BOX (start_vbox), data->start_toggle, FALSE, TRUE, 0); gtk_frame_set_shadow_type (GTK_FRAME (data->start_frame), GTK_SHADOW_NONE); gtk_widget_show_all (data->start_frame); gtk_widget_show (start_hbox); gtk_widget_show (start_vbox); g_signal_connect (data->start_datetime, "changed", G_CALLBACK (start_changed_cb), data); /* End time editor widgets */ data->end_frame = gtk_frame_new (NULL); data->end_ebox = gtk_event_box_new (); data->end_datetime = jana_gtk_date_time_new (NULL); data->end_align = gtk_alignment_new (0, 0.5, 0, 1); gtk_container_set_border_width (GTK_CONTAINER (data->end_align), 6); data->end_toggle = gtk_toggle_button_new_with_label ("Change end"); gtk_widget_set_name (data->end_toggle, "moko-dates-vborder-button"); gtk_container_add (GTK_CONTAINER (data->end_align), data->end_datetime); gtk_container_add (GTK_CONTAINER (data->end_ebox), data->end_align); gtk_container_add (GTK_CONTAINER (data->end_frame), data->end_ebox); end_vbox = gtk_vbox_new (FALSE, 0); end_hbox = gtk_hbox_new (FALSE, 0); data->end_label = gtk_label_new ("Ends: "); gtk_box_pack_start (GTK_BOX (end_hbox), data->end_label, FALSE, TRUE, 0); gtk_box_pack_end (GTK_BOX (end_hbox), data->end_frame, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (end_vbox), end_hbox, FALSE, TRUE, 0); gtk_box_pack_end (GTK_BOX (end_vbox), data->end_toggle, FALSE, TRUE, 0); gtk_frame_set_shadow_type (GTK_FRAME (data->end_frame), GTK_SHADOW_NONE); gtk_widget_show_all (data->end_frame); gtk_widget_show (end_hbox); gtk_widget_show (end_vbox); g_signal_connect (data->end_datetime, "changed", G_CALLBACK (end_changed_cb), data); /* Recurrence editing widgets */ data->recur_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (data->recur_frame), GTK_SHADOW_NONE); data->recur_ebox = gtk_event_box_new (); data->recur = jana_gtk_recurrence_new (); gtk_container_set_border_width (GTK_CONTAINER (data->recur_ebox), 6); gtk_container_add (GTK_CONTAINER (data->recur_frame), data->recur_ebox); gtk_container_add (GTK_CONTAINER (data->recur_ebox), data->recur); gtk_widget_show_all (data->recur_ebox); data->recur_toggle = gtk_toggle_button_new_with_label ( "Change repeats"); gtk_widget_set_name (data->recur_toggle, "moko-dates-vborder-button"); g_signal_connect (data->recur_toggle, "toggled", G_CALLBACK (recur_toggled_cb), data); recur_vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (recur_vbox), data->recur_frame, FALSE, TRUE, 0); gtk_box_pack_end (GTK_BOX (recur_vbox), data->recur_toggle, FALSE, TRUE, 0); gtk_widget_show (recur_vbox); /* Pack editing widgets */ gtk_box_pack_start (GTK_BOX (edit_vbox), data->category_combo, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (edit_vbox), data->summary_entry, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (edit_vbox), data->location_entry, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (edit_vbox), data->description_frame, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (edit_vbox), data->allday_check_button, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (edit_vbox), data->timezone_combo, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (edit_vbox), start_vbox, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (edit_vbox), end_vbox, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (edit_vbox), recur_vbox, FALSE, TRUE, 0); g_signal_connect (data->start_toggle, "toggled", G_CALLBACK (time_toggled_cb), data->start_align); g_signal_connect (data->end_toggle, "toggled", G_CALLBACK (time_toggled_cb), data->end_align); viewport = gtk_viewport_new (NULL, NULL); gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE); gtk_container_add (GTK_CONTAINER (viewport), edit_vbox); gtk_widget_show (viewport); scroll = moko_finger_scroll_new (); gtk_container_add (GTK_CONTAINER (scroll), viewport); gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0); gtk_widget_show (scroll); gtk_widget_add_events (viewport, GDK_VISIBILITY_NOTIFY_MASK); g_signal_connect (viewport, "visibility-notify-event", G_CALLBACK (visibility_notify_event_cb), data); g_signal_connect (viewport, "notify::visible", G_CALLBACK (notify_visible_cb), data); g_signal_connect (vbox, "unmap", G_CALLBACK (unmap_cb), data); g_signal_connect (data->category_combo, "changed", G_CALLBACK (combo_changed_cb), GTK_BIN (data->category_combo)->child); g_signal_connect (data->summary_entry, "focus-in-event", G_CALLBACK (entry_focus_in_cb), (gpointer)summary_field); g_signal_connect (data->summary_entry, "focus-out-event", G_CALLBACK (entry_focus_out_cb), (gpointer)summary_field); g_signal_connect (data->location_entry, "focus-in-event", G_CALLBACK (entry_focus_in_cb), (gpointer)location_field); g_signal_connect (data->location_entry, "focus-out-event", G_CALLBACK (entry_focus_out_cb), (gpointer)location_field); g_signal_connect (GTK_BIN (data->category_combo)->child, "focus-in-event", G_CALLBACK (entry_focus_in_cb), (gpointer)category_field); g_signal_connect (GTK_BIN (data->category_combo)->child, "focus-out-event", G_CALLBACK (entry_focus_out_cb), (gpointer)category_field); g_signal_connect (G_OBJECT (data->description_text_view), "focus-in-event", G_CALLBACK (text_view_focus_in_cb), (gpointer)description_field); g_signal_connect (G_OBJECT (data->description_text_view), "focus-out-event", G_CALLBACK (text_view_focus_out_cb), (gpointer)description_field); /* Set entries uneditable */ jana_gtk_date_time_set_editable (JANA_GTK_DATE_TIME ( data->start_datetime), FALSE); jana_gtk_date_time_set_editable (JANA_GTK_DATE_TIME ( data->end_datetime), FALSE); return vbox; }