/* Orage - Calendar and alarm handler * * Copyright (c) 2005-2008 Juha Kautto (juha at xfce.org) * Copyright (c) 2004-2005 Mickael Graf (korbinus at xfce.org) * * 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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation 51 Franklin Street, 5th Floor Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #include #include #include #include #include #include #include "functions.h" #include "mainbox.h" #include "reminder.h" #include "about-xfcalendar.h" #include "ical-code.h" #include "event-list.h" #include "appointment.h" #include "parameters.h" #include "tray_icon.h" #include "day-view.h" #define BORDER_SIZE 10 enum { COL_TIME = 0 ,COL_FLAGS ,COL_HEAD ,COL_UID ,COL_SORT ,CAL_CATEGORIES ,NUM_COLS }; enum { DRAG_TARGET_STRING = 0 ,DRAG_TARGET_COUNT }; static const GtkTargetEntry drag_targets[] = { { "STRING", 0, DRAG_TARGET_STRING } }; static void do_appt_win(char *mode, char *uid, el_win *el) { appt_win *apptw; apptw = create_appt_win(mode, uid); if (apptw) { /* we started this, so keep track of it */ el->apptw_list = g_list_prepend(el->apptw_list, apptw); /* inform the appointment that we are interested in it */ apptw->el = el; } }; static void start_appt_win(char *mode, el_win *el , GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path) { gchar *uid = NULL, *flags = NULL; if (gtk_tree_model_get_iter(model, iter, path)) { gtk_tree_model_get(model, iter, COL_UID, &uid, -1); #ifdef HAVE_ARCHIVE gtk_tree_model_get(model, iter, COL_FLAGS, &flags, -1); if (flags && flags[3] == 'A') { xfical_unarchive_uid(uid); /* note that file id changes after archive */ uid[0]='O'; refresh_el_win(el); } g_free(flags); #endif do_appt_win(mode, uid, el); g_free(uid); } } static void editEvent(GtkTreeView *view, GtkTreePath *path , GtkTreeViewColumn *col, gpointer user_data) { el_win *el = (el_win *)user_data; GtkTreeModel *model; GtkTreeIter iter; model = gtk_tree_view_get_model(view); start_appt_win("UPDATE", el, model, &iter, path); } static gint sortEvent_comp(GtkTreeModel *model , GtkTreeIter *i1, GtkTreeIter *i2, gpointer data) { gint col = GPOINTER_TO_INT(data); gint ret; gchar *text1, *text2; gtk_tree_model_get(model, i1, col, &text1, -1); gtk_tree_model_get(model, i2, col, &text2, -1); ret = strcmp(text1, text2); g_free(text1); g_free(text2); return(ret); } static int append_time(char *result, char *ical_time, int i) { result[i++] = ical_time[9]; result[i++] = ical_time[10]; result[i++] = ':'; result[i++] = ical_time[11]; result[i++] = ical_time[12]; result[i++] = ' '; result[i] = '\0'; return(6); } static char *format_time(el_win *el, xfical_appt *appt, char *par) { char *result; char *tmp; int i = 0; char *start_ical_time; char *end_ical_time; gboolean same_date; struct tm t = {0,0,0,0,0,0,0,0,0}; start_ical_time = appt->starttimecur; end_ical_time = appt->endtimecur; same_date = !strncmp(start_ical_time, end_ical_time, 8); result = g_new0(char, 51); if (el->page == EVENT_PAGE && el->days == 0) { /* special formatting for 1 day VEVENTS */ if (start_ical_time[8] == 'T') { /* time part available */ if (strncmp(start_ical_time, par, 8) < 0) i = g_strlcpy(result, "+00:00 ", 50); else i += append_time(result, start_ical_time, i); i = g_strlcat(result, "- ", 50); if (strncmp(par, end_ical_time , 8) < 0) i = g_strlcat(result, "24:00+", 50); else i += append_time(result, end_ical_time, i); } else {/* date only appointment */ i = g_strlcpy(result, _("All day"), 50); } } else { /* normally show date and time */ t = orage_icaltime_to_tm_time(appt->starttimecur, TRUE); tmp = orage_tm_date_to_i18_date(&t); i = g_strlcpy(result, tmp, 50); if (start_ical_time[8] == 'T') { /* time part available */ result[i++] = ' '; i += append_time(result, start_ical_time, i); i = g_strlcat(result, "- ", 50); if (el->page == TODO_PAGE && !appt->use_due_time) { i = g_strlcat(result, "...", 50); } else { if (!same_date) { t = orage_icaltime_to_tm_time(appt->endtimecur, TRUE); tmp = orage_tm_date_to_i18_date(&t); i = g_strlcat(result, tmp, 50); result[i++] = ' '; } i += append_time(result, end_ical_time, i); } } else {/* date only */ i = g_strlcat(result, " - ", 50); if (el->page == TODO_PAGE && !appt->use_due_time) { i = g_strlcat(result, "...", 50); } else { t = orage_icaltime_to_tm_time(appt->endtimecur, TRUE); tmp = orage_tm_date_to_i18_date(&t); i = g_strlcat(result, tmp, 50); } } } return(result); } static void flags_data_func(GtkTreeViewColumn *col, GtkCellRenderer *rend , GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) { gchar *categories; GdkColor *color; gtk_tree_model_get(model, iter, CAL_CATEGORIES, &categories, -1); if ((color = orage_category_list_contains(categories)) == NULL) g_object_set(rend , "background-set", FALSE , NULL); else g_object_set(rend , "background-gdk", color , "background-set", TRUE , NULL); g_free(categories); } static void start_time_data_func(GtkTreeViewColumn *col, GtkCellRenderer *rend , GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) { el_win *el = (el_win *)user_data; gchar *stime, *etime, *stime2; gchar start_time[17], end_time[17]; gint len; if (el->page == EVENT_PAGE) { if (!el->today || el->days != 0) { /* reset */ g_object_set(rend , "foreground-set", FALSE , "strikethrough-set", FALSE , "weight-set", FALSE , NULL); return; } gtk_tree_model_get(model, iter, COL_TIME, &stime, -1); if (stime[0] == '+') stime++; etime = stime + 8; /* hh:mm - hh:mm */ /* only add special highlight if we are on today (=start with time) */ if (stime[2] != ':') { g_object_set(rend , "foreground-set", FALSE , "strikethrough-set", FALSE , "weight-set", FALSE , NULL); } else if (strncmp(etime, el->time_now, 5) < 0) { /* gone */ g_object_set(rend , "foreground-set", FALSE , "strikethrough", TRUE , "strikethrough-set", TRUE , "weight", PANGO_WEIGHT_LIGHT , "weight-set", TRUE , NULL); } else if (strncmp(stime, el->time_now, 5) <= 0 && strncmp(etime, el->time_now, 5) >= 0) { /* current */ g_object_set(rend , "foreground", "Blue" , "foreground-set", TRUE , "strikethrough-set", FALSE , "weight", PANGO_WEIGHT_BOLD , "weight-set", TRUE , NULL); } else { /* future */ g_object_set(rend , "foreground-set", FALSE , "strikethrough-set", FALSE , "weight", PANGO_WEIGHT_BOLD , "weight-set", TRUE , NULL); } g_free(stime); } else if (el->page == TODO_PAGE) { gtk_tree_model_get(model, iter, COL_SORT, &stime, -1); if (stime[8] == 'T') { /* date+time */ len = 15; } else { /* date only */ len = 8; } strncpy(start_time, stime, len); gtk_tree_model_get(model, iter, COL_TIME, &stime2, -1); if (g_str_has_suffix(stime2, "- ...")) /* no due time */ strncpy(end_time, "99999", len); /* long in the future*/ else /* normal due time*/ strncpy(end_time, stime+len, len); if (strncmp(end_time, el->date_now, len) < 0) { /* gone */ g_object_set(rend , "foreground", "Red" , "foreground-set", TRUE , "strikethrough-set", FALSE , "weight", PANGO_WEIGHT_BOLD , "weight-set", TRUE , NULL); } else if (strncmp(start_time, el->date_now, len) <= 0 && strncmp(end_time, el->date_now, len) >= 0) { /* current */ g_object_set(rend , "foreground", "Blue" , "foreground-set", TRUE , "strikethrough-set", FALSE , "weight", PANGO_WEIGHT_BOLD , "weight-set", TRUE , NULL); } else { /* future */ g_object_set(rend , "foreground-set", FALSE , "strikethrough-set", FALSE , "weight", PANGO_WEIGHT_LIGHT , "weight-set", TRUE , NULL); } g_free(stime); g_free(stime2); } else { g_object_set(rend , "foreground-set", FALSE , "strikethrough-set", FALSE , "weight-set", FALSE , NULL); } } static void add_el_row(el_win *el, xfical_appt *appt, char *par) { GtkTreeIter iter1; GtkListStore *list1; gchar *title = NULL; gchar flags[6]; gchar *stime; gchar /* *s_sort,*/ *s_sort1; gint len = 50; stime = format_time(el, appt, par); if (appt->alarmtime != 0) if (appt->sound != NULL) flags[0] = 'S'; else flags[0] = 'A'; else flags[0] = 'n'; if (appt->freq == XFICAL_FREQ_NONE) flags[1] = 'n'; else if (appt->freq == XFICAL_FREQ_DAILY) flags[1] = 'D'; else if (appt->freq == XFICAL_FREQ_WEEKLY) flags[1] = 'W'; else if (appt->freq == XFICAL_FREQ_MONTHLY) flags[1] = 'M'; else if (appt->freq == XFICAL_FREQ_YEARLY) flags[1] = 'Y'; else flags[1] = 'n'; if (appt->availability != 0) flags[2] = 'B'; else flags[2] = 'f'; flags[3] = appt->uid[0]; /* file type */ if (appt->type == XFICAL_TYPE_EVENT) flags[4] = 'E'; else if (appt->type == XFICAL_TYPE_TODO) flags[4] = 'T'; else /* Journal */ flags[4] = 'J'; flags[5] = '\0'; if (appt->title != NULL) title = g_strdup(appt->title); else if (appt->note != NULL) { /* let's take len chars of the first line from the text */ if ((title = g_strstr_len(appt->note, strlen(appt->note), "\n")) != NULL) { if ((strlen(appt->note)-strlen(title)) < len) len = strlen(appt->note)-strlen(title); } title = g_strndup(appt->note, len); } s_sort1 = g_strconcat(appt->starttimecur, appt->endtimecur, NULL); /* s_sort = g_utf8_collate_key(s_sort1, -1); */ list1 = el->ListStore; gtk_list_store_append(list1, &iter1); gtk_list_store_set(list1, &iter1 , COL_TIME, stime , COL_FLAGS, flags , COL_HEAD, title , COL_UID, appt->uid , COL_SORT, s_sort1 , CAL_CATEGORIES, appt->categories , -1); g_free(title); g_free(s_sort1); g_free(stime); /* g_free(s_sort); */ } static void searh_rows(el_win *el, gchar *search_string, gchar *file_type) { xfical_appt *appt; for (appt = xfical_appt_get_next_with_string(search_string, TRUE , file_type); appt; appt = xfical_appt_get_next_with_string(search_string, FALSE , file_type)) { add_el_row(el, appt, NULL); xfical_appt_free(appt); } } static void search_data(el_win *el) { gchar *search_string = NULL, file_type[8]; gint i; search_string = g_utf8_strup(gtk_entry_get_text( (GtkEntry *)el->search_entry),-1); /* first search base orage file */ if (!xfical_file_open(TRUE)) return; strcpy(file_type, "O00."); searh_rows(el, search_string, file_type); /* then process all foreign files */ for (i = 0; i < g_par.foreign_count; i++) { g_sprintf(file_type, "F%02d.", i); searh_rows(el, search_string, file_type); } #ifdef HAVE_ARCHIVE /* finally process always archive file also */ if (xfical_archive_open()) { strcpy(file_type, "A00."); searh_rows(el, search_string, file_type); xfical_archive_close(); } #endif xfical_file_close(TRUE); g_free(search_string); } static void app_rows(el_win *el, char *a_day, char *par, xfical_type ical_type , gchar *file_type) { xfical_appt *appt; for (appt = xfical_appt_get_next_on_day(a_day, TRUE, el->days , ical_type , file_type); appt; appt = xfical_appt_get_next_on_day(a_day, FALSE, el->days , ical_type , file_type)) { add_el_row(el, appt, par); xfical_appt_free(appt); } } static void app_data(el_win *el, char *a_day, char *par) { xfical_type ical_type; gchar file_type[8]; gint i; switch (el->page) { case EVENT_PAGE: ical_type = XFICAL_TYPE_EVENT; break; case TODO_PAGE: ical_type = XFICAL_TYPE_TODO; break; case JOURNAL_PAGE: ical_type = XFICAL_TYPE_JOURNAL; break; default: ical_type = XFICAL_TYPE_EVENT; /* to satisfy c-compiler checks */ g_error("wrong page in app_data (%d)\n", el->page); } /* first search base orage file */ if (!xfical_file_open(TRUE)) return; strcpy(file_type, "O00."); app_rows(el, a_day, par, ical_type, file_type); /* then process all foreign files */ for (i = 0; i < g_par.foreign_count; i++) { g_sprintf(file_type, "F%02d.", i); app_rows(el, a_day, par, ical_type, file_type); } #ifdef HAVE_ARCHIVE /* finally process archive file for JOURNAL only */ if (ical_type == XFICAL_TYPE_JOURNAL) { if (xfical_archive_open()) { strcpy(file_type, "A00."); app_rows(el, a_day, par, ical_type, file_type); xfical_archive_close(); } } #endif xfical_file_close(TRUE); } static void refresh_time_field(el_win *el) { GtkCellRenderer *rend; GtkTreeViewColumn *col; /* this is needed if we want to make time field smaller again */ col = gtk_tree_view_get_column(GTK_TREE_VIEW(el->TreeView), 0); gtk_tree_view_remove_column(GTK_TREE_VIEW(el->TreeView), col); rend = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes( _("Time"), rend , "text", COL_TIME, NULL); gtk_tree_view_column_set_cell_data_func(col, rend , start_time_data_func, el, NULL); gtk_tree_view_insert_column(GTK_TREE_VIEW(el->TreeView), col, 0); } static void event_data(el_win *el) { char *title; /* in %x strftime format */ char *s_time; /* in icaltime format */ char a_day[9]; /* yyyymmdd */ struct tm *t, t_title; if (el->days == 0) refresh_time_field(el); el->days = gtk_spin_button_get_value(GTK_SPIN_BUTTON(el->event_spin)); title = (char *)gtk_window_get_title(GTK_WINDOW(el->Window)); t_title = orage_i18_date_to_tm_date(title); s_time = orage_tm_time_to_icaltime(&t_title); strncpy(a_day, s_time, 8); a_day[8] = '\0'; t = orage_localtime(); g_sprintf(el->time_now, "%02d:%02d", t->tm_hour, t->tm_min); if ( t_title.tm_year == t->tm_year && t_title.tm_mon == t->tm_mon && t_title.tm_mday == t->tm_mday) el->today = TRUE; else el->today = FALSE; app_data(el, a_day, a_day); } static void todo_data(el_win *el) { char *s_time; char a_day[9]; /* yyyymmdd */ struct tm *t; el->days = 0; /* not used */ t = orage_localtime(); s_time = orage_tm_time_to_icaltime(t); strncpy(a_day, s_time, 8); a_day[8] = '\0'; strncpy(el->date_now, s_time, XFICAL_APPT_TIME_FORMAT_LEN); app_data(el, a_day, NULL); } static void journal_data(el_win *el) { char a_day[9]; /* yyyymmdd */ el->days = 10*365; /* long enough time to get everything from future */ strcpy(a_day, orage_i18_date_to_icaltime(gtk_button_get_label( GTK_BUTTON(el->journal_start_button)))); app_data(el, a_day, NULL); } void refresh_el_win(el_win *el) { orage_category_get_list(); if (el->Window && el->ListStore && el->TreeView) { gtk_list_store_clear(el->ListStore); el->page = gtk_notebook_get_current_page(GTK_NOTEBOOK(el->Notebook)); switch (el->page) { case EVENT_PAGE: event_data(el); break; case TODO_PAGE: todo_data(el); break; case JOURNAL_PAGE: journal_data(el); break; case SEARCH_PAGE: search_data(el); break; default: g_warning("refresh_el_win: unknown tab"); break; } } } static gboolean upd_notebook(el_win *el) { static gint prev_page = -1; gint cur_page; /* we only show the page IF it is different than the last * page changed with this method */ cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(el->Notebook)); if (cur_page != prev_page) { prev_page = cur_page; refresh_el_win(el); } return(FALSE); /* we do this only once */ } static void on_notebook_page_switch(GtkNotebook *notebook , GtkNotebookPage *page, guint page_num, gpointer user_data) { el_win *el = (el_win *)user_data; /* we can't do refresh_el_win here directly since it is slow and * gtk_notebook_get_current_page points to old page at this time, * so we end up showing wrong page. * This timeout also makes it possible to avoid unnecessary * refreshes when tab is change quickly; like with mouse roll */ g_timeout_add(300, (GtkFunction)upd_notebook, el); } static void on_Search_clicked(GtkButton *b, gpointer user_data) { el_win *el = (el_win *)user_data; gtk_notebook_set_current_page(GTK_NOTEBOOK(el->Notebook), SEARCH_PAGE); refresh_el_win((el_win *)user_data); } static void on_View_search_activate_cb(GtkMenuItem *mi, gpointer user_data) { el_win *el = (el_win *)user_data; gtk_notebook_set_current_page(GTK_NOTEBOOK(el->Notebook), SEARCH_PAGE); refresh_el_win((el_win *)user_data); } static void set_el_data(el_win *el, char *title) { gtk_window_set_title(GTK_WINDOW(el->Window), (const gchar*)title); gtk_notebook_set_current_page(GTK_NOTEBOOK(el->Notebook), EVENT_PAGE); refresh_el_win(el); } static void set_el_data_from_cal(el_win *el) { char *title; title = orage_cal_to_i18_date( GTK_CALENDAR(((CalWin *)g_par.xfcal)->mCalendar)); set_el_data(el, title); } static void duplicate_appointment(el_win *el) { GtkTreeSelection *sel; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; GList *list; gint list_len; sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(el->TreeView)); list = gtk_tree_selection_get_selected_rows(sel, &model); list_len = g_list_length(list); if (list_len > 0) { if (list_len > 1) g_warning("Copy: too many rows selected\n"); path = (GtkTreePath *)g_list_nth_data(list, 0); start_appt_win("COPY", el, model, &iter, path); } else { g_warning("Copy: No row selected\n"); xfce_message_dialog(GTK_WINDOW(el->Window) , _("Info") , GTK_STOCK_DIALOG_INFO , _("No rows have been selected.") , _("Click a row to select it and after that you can copy it.") , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT , NULL); } g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); g_list_free(list); } static void on_Copy_clicked(GtkButton *b, gpointer user_data) { duplicate_appointment((el_win *)user_data); } static void on_File_duplicate_activate_cb(GtkMenuItem *mi, gpointer user_data) { duplicate_appointment((el_win *)user_data); } static void close_window(el_win *el) { appt_win *apptw; GList *apptw_list; gtk_window_get_size(GTK_WINDOW(el->Window) , &g_par.el_size_x, &g_par.el_size_y); write_parameters(); /* need to clean the appointment list and inform all appointments that * we are not interested anymore (= should not get updated) */ apptw_list = el->apptw_list; for (apptw_list = g_list_first(apptw_list); apptw_list != NULL; apptw_list = g_list_next(apptw_list)) { apptw = (appt_win *)apptw_list->data; if (apptw) /* appointment window is still alive */ apptw->el = NULL; /* not interested anymore */ else orage_message(110, "close_window: not null appt window"); } g_list_free(el->apptw_list); gtk_widget_destroy(el->Window); /* destroy the eventlist window */ gtk_object_destroy(GTK_OBJECT(el->Tooltips)); g_free(el); el = NULL; } static void on_Close_clicked(GtkButton *b, gpointer user_data) { close_window((el_win *)user_data); } static void on_Dayview_clicked(GtkButton *b, gpointer user_data) { el_win *el = (el_win *)user_data; char *title; title = (char *)gtk_window_get_title(GTK_WINDOW(el->Window)); create_day_win(title); } static void on_File_close_activate_cb(GtkMenuItem *mi, gpointer user_data) { close_window((el_win *)user_data); } static gboolean on_Window_delete_event(GtkWidget *w, GdkEvent *e , gpointer user_data) { close_window((el_win *)user_data); return(FALSE); } static void on_Refresh_clicked(GtkButton *b, gpointer user_data) { refresh_el_win((el_win*)user_data); } static void on_View_refresh_activate_cb(GtkMenuItem *mi, gpointer user_data) { refresh_el_win((el_win*)user_data); } static void changeSelectedDate(el_win *el, gint day) { struct tm tm_date; tm_date = orage_i18_date_to_tm_date( gtk_window_get_title(GTK_WINDOW(el->Window))); orage_move_day(&tm_date, day); orage_select_date(GTK_CALENDAR(((CalWin *)g_par.xfcal)->mCalendar) , tm_date.tm_year + 1900, tm_date.tm_mon, tm_date.tm_mday); set_el_data_from_cal(el); } static void on_Previous_clicked(GtkButton *b, gpointer user_data) { changeSelectedDate((el_win *)user_data, -1); } static void on_Go_previous_activate_cb(GtkMenuItem *mi, gpointer user_data) { changeSelectedDate((el_win *)user_data, -1); } static void go_to_today(el_win *el) { orage_select_today(GTK_CALENDAR(((CalWin *)g_par.xfcal)->mCalendar)); set_el_data_from_cal(el); } static void on_Today_clicked(GtkButton *b, gpointer user_data) { go_to_today((el_win *)user_data); } static void on_Go_today_activate_cb(GtkMenuItem *mi, gpointer user_data) { go_to_today((el_win *)user_data); } static void on_Next_clicked(GtkButton *b, gpointer user_data) { changeSelectedDate((el_win *)user_data, 1); } static void on_Go_next_activate_cb(GtkMenuItem *mi, gpointer user_data) { changeSelectedDate((el_win *)user_data, 1); } static void create_new_appointment(el_win *el) { char *title, a_day[10]; title = (char *)gtk_window_get_title(GTK_WINDOW(el->Window)); strcpy(a_day, orage_i18_date_to_icaltime(title)); do_appt_win("NEW", a_day, el); } static void on_File_newApp_activate_cb(GtkMenuItem *mi, gpointer user_data) { create_new_appointment((el_win *)user_data); } static void on_Create_toolbutton_clicked_cb(GtkButton *b, gpointer user_data) { create_new_appointment((el_win *)user_data); } static void on_spin_changed(GtkSpinButton *b, gpointer user_data) { refresh_el_win((el_win *)user_data); } static void delete_appointment(el_win *el) { gint result; GtkTreeSelection *sel; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; GList *list; gint list_len, i; gchar *uid = NULL; result = xfce_message_dialog(GTK_WINDOW(el->Window) , _("Warning") , GTK_STOCK_DIALOG_WARNING , _("You will permanently remove all\nselected appointments.") , _("Do you want to continue?") , GTK_STOCK_NO, GTK_RESPONSE_CANCEL , GTK_STOCK_YES, GTK_RESPONSE_ACCEPT , NULL); if (result == GTK_RESPONSE_ACCEPT) { if (!xfical_file_open(TRUE)) return; sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(el->TreeView)); list = gtk_tree_selection_get_selected_rows(sel, &model); list_len = g_list_length(list); for (i = 0; i < list_len; i++) { path = (GtkTreePath *)g_list_nth_data(list, i); if (gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get(model, &iter, COL_UID, &uid, -1); result = xfical_appt_del(uid); if (result) orage_message(30, "Removed: %s", uid); else g_warning("Removal failed: %s", uid); g_free(uid); } } xfical_file_close(TRUE); refresh_el_win(el); orage_mark_appointments(); g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); g_list_free(list); } } static void on_Delete_clicked(GtkButton *b, gpointer user_data) { delete_appointment((el_win *)user_data); } static void on_File_delete_activate_cb(GtkMenuItem *mi, gpointer user_data) { delete_appointment((el_win *)user_data); } static void on_journal_start_button_clicked(GtkWidget *button , gpointer *user_data) { el_win *el = (el_win *)user_data; if (orage_date_button_clicked(button, el->Window)) refresh_el_win(el); } static void drag_data_get(GtkWidget *widget, GdkDragContext *context , GtkSelectionData *selection_data, guint info, guint time , gpointer user_data) { GtkTreeSelection *sel; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; GList *list; gint list_len, i; gchar *uid = NULL; GString *result = NULL; sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); list = gtk_tree_selection_get_selected_rows(sel, &model); list_len = g_list_length(list); for (i = 0; i < list_len; i++) { path = (GtkTreePath *)g_list_nth_data(list, i); if (gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get(model, &iter, COL_UID, &uid, -1); if (i == 0) { /* first */ result = g_string_new(uid); } else { g_string_append(result, ","); g_string_append(result, uid); } g_free(uid); } } if (!gtk_selection_data_set_text(selection_data, result->str, -1)) g_warning("drag_data_get failed\n"); g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); g_list_free(list); g_string_free(result, TRUE); } static void build_menu(el_win *el) { GtkWidget *menu_separator; el->Menubar = gtk_menu_bar_new(); gtk_box_pack_start(GTK_BOX(el->Vbox), el->Menubar, FALSE, FALSE, 0); /********** File menu **********/ el->File_menu = orage_menu_new(_("_File"), el->Menubar); el->File_menu_new = orage_image_menu_item_new_from_stock("gtk-new" , el->File_menu, el->accel_group); menu_separator = orage_separator_menu_item_new(el->File_menu); el->File_menu_duplicate = orage_menu_item_new_with_mnemonic(_("D_uplicate") , el->File_menu); gtk_widget_add_accelerator(el->File_menu_duplicate , "activate", el->accel_group , GDK_d, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); menu_separator = orage_separator_menu_item_new(el->File_menu); el->File_menu_delete = orage_image_menu_item_new_from_stock("gtk-delete" , el->File_menu, el->accel_group); menu_separator = orage_separator_menu_item_new(el->File_menu); el->File_menu_close = orage_image_menu_item_new_from_stock("gtk-close" , el->File_menu, el->accel_group); /********** View menu **********/ el->View_menu = orage_menu_new(_("_View"), el->Menubar); el->View_menu_refresh = orage_image_menu_item_new_from_stock ("gtk-refresh" , el->View_menu, el->accel_group); gtk_widget_add_accelerator(el->View_menu_refresh , "activate", el->accel_group , GDK_r, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); gtk_widget_add_accelerator(el->View_menu_refresh , "activate", el->accel_group , GDK_Return, 0, 0); gtk_widget_add_accelerator(el->View_menu_refresh , "activate", el->accel_group , GDK_KP_Enter, 0, 0); menu_separator = orage_separator_menu_item_new(el->View_menu); el->View_menu_search = orage_image_menu_item_new_from_stock("gtk-find" , el->View_menu, el->accel_group); /********** Go menu **********/ el->Go_menu = orage_menu_new(_("_Go"), el->Menubar); el->Go_menu_today = orage_image_menu_item_new_from_stock("gtk-home" , el->Go_menu, el->accel_group); gtk_widget_add_accelerator(el->Go_menu_today , "activate", el->accel_group , GDK_Home, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE); el->Go_menu_prev = orage_image_menu_item_new_from_stock("gtk-go-back" , el->Go_menu, el->accel_group); gtk_widget_add_accelerator(el->Go_menu_prev , "activate", el->accel_group , GDK_Left, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE); el->Go_menu_next = orage_image_menu_item_new_from_stock("gtk-go-forward" , el->Go_menu, el->accel_group); gtk_widget_add_accelerator(el->Go_menu_next , "activate", el->accel_group , GDK_Right, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE); g_signal_connect((gpointer)el->File_menu_new, "activate" , G_CALLBACK(on_File_newApp_activate_cb), el); g_signal_connect((gpointer)el->File_menu_duplicate, "activate" , G_CALLBACK(on_File_duplicate_activate_cb), el); g_signal_connect((gpointer)el->File_menu_delete, "activate" , G_CALLBACK(on_File_delete_activate_cb), el); g_signal_connect((gpointer)el->View_menu_refresh, "activate" , G_CALLBACK(on_View_refresh_activate_cb), el); g_signal_connect((gpointer)el->View_menu_search, "activate" , G_CALLBACK(on_View_search_activate_cb), el); g_signal_connect((gpointer)el->Go_menu_today, "activate" , G_CALLBACK(on_Go_today_activate_cb), el); g_signal_connect((gpointer)el->Go_menu_prev, "activate" , G_CALLBACK(on_Go_previous_activate_cb), el); g_signal_connect((gpointer)el->Go_menu_next, "activate" , G_CALLBACK(on_Go_next_activate_cb), el); g_signal_connect((gpointer)el->File_menu_close, "activate" , G_CALLBACK(on_File_close_activate_cb), el); } static void build_toolbar(el_win *el) { GtkWidget *toolbar_separator; gint i = 0; el->Toolbar = gtk_toolbar_new(); gtk_box_pack_start(GTK_BOX(el->Vbox), el->Toolbar, FALSE, FALSE, 0); gtk_toolbar_set_tooltips(GTK_TOOLBAR(el->Toolbar), TRUE); el->Create_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-new", el->Tooltips, _("New"), i++); el->Copy_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-copy", el->Tooltips, _("Duplicate"), i++); el->Delete_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-delete", el->Tooltips, _("Delete"), i++); toolbar_separator = orage_toolbar_append_separator(el->Toolbar, i++); el->Previous_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-go-back", el->Tooltips, _("Back"), i++); el->Today_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-home", el->Tooltips, _("Today"), i++); el->Next_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-go-forward", el->Tooltips, _("Forward"), i++); toolbar_separator = orage_toolbar_append_separator(el->Toolbar, i++); el->Refresh_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-refresh", el->Tooltips, _("Refresh"), i++); el->Search_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-find", el->Tooltips, _("Find"), i++); toolbar_separator = orage_toolbar_append_separator(el->Toolbar, i++); el->Close_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-close", el->Tooltips, _("Close"), i++); el->Dayview_toolbutton = orage_toolbar_append_button(el->Toolbar , "gtk-zoom-in", el->Tooltips, _("Dayview"), i++); g_signal_connect((gpointer)el->Create_toolbutton, "clicked" , G_CALLBACK(on_Create_toolbutton_clicked_cb), el); g_signal_connect((gpointer)el->Copy_toolbutton, "clicked" , G_CALLBACK(on_Copy_clicked), el); g_signal_connect((gpointer)el->Delete_toolbutton, "clicked" , G_CALLBACK(on_Delete_clicked), el); g_signal_connect((gpointer)el->Previous_toolbutton, "clicked" , G_CALLBACK(on_Previous_clicked), el); g_signal_connect((gpointer)el->Today_toolbutton, "clicked" , G_CALLBACK(on_Today_clicked), el); g_signal_connect((gpointer)el->Next_toolbutton, "clicked" , G_CALLBACK(on_Next_clicked), el); g_signal_connect((gpointer)el->Refresh_toolbutton, "clicked" , G_CALLBACK(on_Refresh_clicked), el); g_signal_connect((gpointer)el->Search_toolbutton, "clicked" , G_CALLBACK(on_Search_clicked), el); g_signal_connect((gpointer)el->Close_toolbutton, "clicked" , G_CALLBACK(on_Close_clicked), el); g_signal_connect((gpointer)el->Dayview_toolbutton, "clicked" , G_CALLBACK(on_Dayview_clicked), el); } static void build_event_tab(el_win *el) { gint row; GtkWidget *label, *hbox; el->event_tab_label = gtk_label_new(_("Event")); /* FIXME: remove these tables, which are not needed anymore */ el->event_notebook_page = orage_table_new(1, BORDER_SIZE); label = gtk_label_new(_("Extra days to show ")); hbox = gtk_hbox_new(FALSE, 0); el->event_spin = gtk_spin_button_new_with_range(0, 999, 1); gtk_box_pack_start(GTK_BOX(hbox), el->event_spin, FALSE, FALSE, 15); orage_table_add_row(el->event_notebook_page , label, hbox , row = 0, (GTK_FILL), (0)); gtk_notebook_append_page(GTK_NOTEBOOK(el->Notebook) , el->event_notebook_page, el->event_tab_label); g_signal_connect((gpointer)el->event_spin, "value-changed" , G_CALLBACK(on_spin_changed), el); } static void build_todo_tab(el_win *el) { el->todo_tab_label = gtk_label_new(_("Todo")); /* FIXME: remove these tables, which are not needed anymore */ el->todo_notebook_page = orage_table_new(1, BORDER_SIZE); gtk_notebook_append_page(GTK_NOTEBOOK(el->Notebook) , el->todo_notebook_page, el->todo_tab_label); } static void build_journal_tab(el_win *el) { gint row; GtkWidget *label, *hbox; struct tm *tm; gchar *sdate; el->journal_tab_label = gtk_label_new(_("Journal")); /* FIXME: remove these tables, which are not needed anymore */ el->journal_notebook_page = orage_table_new(1, BORDER_SIZE); label = gtk_label_new(_("Journal entries starting from:")); hbox = gtk_hbox_new(FALSE, 0); el->journal_start_button = gtk_button_new(); tm = orage_localtime(); tm->tm_year -= 1; sdate = orage_tm_date_to_i18_date(tm); gtk_button_set_label(GTK_BUTTON(el->journal_start_button) , (const gchar *)sdate); gtk_box_pack_start(GTK_BOX(hbox), el->journal_start_button , FALSE, FALSE, 15); orage_table_add_row(el->journal_notebook_page , label, hbox , row = 0, (GTK_FILL), (0)); gtk_notebook_append_page(GTK_NOTEBOOK(el->Notebook) , el->journal_notebook_page, el->journal_tab_label); g_signal_connect((gpointer)el->journal_start_button, "clicked" , G_CALLBACK(on_journal_start_button_clicked), el); } static void build_search_tab(el_win *el) { gint row; GtkWidget *label; el->search_tab_label = gtk_label_new(_("Search")); /* FIXME: remove these tables, which are not needed anymore */ el->search_notebook_page = orage_table_new(1, BORDER_SIZE); label = gtk_label_new(_("Search text ")); el->search_entry = gtk_entry_new(); orage_table_add_row(el->search_notebook_page , label, el->search_entry , row = 0, (GTK_EXPAND | GTK_FILL), (0)); gtk_notebook_append_page(GTK_NOTEBOOK(el->Notebook) , el->search_notebook_page, el->search_tab_label); } static void build_notebook(el_win *el) { el->Notebook = gtk_notebook_new(); gtk_box_pack_start(GTK_BOX(el->Vbox), el->Notebook, FALSE, FALSE, 0); build_event_tab(el); build_todo_tab(el); build_journal_tab(el); build_search_tab(el); g_signal_connect((gpointer)el->Notebook, "switch-page" , G_CALLBACK(on_notebook_page_switch), el); } static void build_event_list(el_win *el) { GtkCellRenderer *rend; GtkTreeViewColumn *col; /* Scrolled window */ el->ScrolledWindow = gtk_scrolled_window_new(NULL, NULL); gtk_box_pack_start(GTK_BOX(el->Vbox), el->ScrolledWindow , TRUE, TRUE, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(el->ScrolledWindow) , GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); /* Tree view */ el->ListStore = gtk_list_store_new(NUM_COLS , G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING , G_TYPE_STRING, G_TYPE_STRING); el->TreeView = gtk_tree_view_new(); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(el->TreeView), TRUE); el->TreeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(el->TreeView)); gtk_tree_selection_set_mode(el->TreeSelection, GTK_SELECTION_MULTIPLE); el->TreeSortable = GTK_TREE_SORTABLE(el->ListStore); gtk_tree_sortable_set_sort_func(el->TreeSortable, COL_SORT , sortEvent_comp, GINT_TO_POINTER(COL_SORT), NULL); gtk_tree_sortable_set_sort_column_id(el->TreeSortable , COL_SORT, GTK_SORT_ASCENDING); gtk_tree_view_set_model(GTK_TREE_VIEW(el->TreeView) , GTK_TREE_MODEL(el->ListStore)); gtk_container_add(GTK_CONTAINER(el->ScrolledWindow), el->TreeView); rend = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes( _("Time"), rend , "text", COL_TIME , NULL); gtk_tree_view_column_set_cell_data_func(col, rend, start_time_data_func , el, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(el->TreeView), col); rend = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes( _("Flags"), rend , "text", COL_FLAGS , NULL); gtk_tree_view_column_set_cell_data_func(col, rend, flags_data_func , el, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(el->TreeView), col); rend = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes( _("Title"), rend , "text", COL_HEAD , NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(el->TreeView), col); rend = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes("uid", rend , "text", COL_UID , NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(el->TreeView), col); gtk_tree_view_column_set_visible(col, FALSE); rend = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes("sort", rend , "text", COL_SORT , NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(el->TreeView), col); gtk_tree_view_column_set_visible(col, FALSE); rend = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes("cat", rend , "text", CAL_CATEGORIES , NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(el->TreeView), col); gtk_tree_view_column_set_visible(col, FALSE); gtk_tooltips_set_tip(el->Tooltips, el->TreeView, _("Double click line to edit it.\n\nFlags in order:\n\t 1. Alarm: n=no alarm\n\t\t A=visual Alarm S=also Sound alarm\n\t 2. Recurrence: n=no recurrence\n\t\t D=Daily W=Weekly M=Monthly Y=Yearly\n\t 3. Type: f=free B=Busy\n\t 4. Located in file:\n\t\tO=Orage A=Archive F=Foreign\n\t 5. Appointment type:\n\t\tE=Event T=Todo J=Journal"), NULL); g_signal_connect(el->TreeView, "row-activated", G_CALLBACK(editEvent), el); } el_win *create_el_win(char *start_date) { GdkPixbuf *pixbuf; el_win *el; /* initialisation + main window + base vbox */ el = g_new(el_win, 1); el->today = FALSE; el->days = 0; el->time_now[0] = 0; el->apptw_list = NULL; el->Tooltips = gtk_tooltips_new(); el->accel_group = gtk_accel_group_new(); el->Window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(el->Window) , g_par.el_size_x, g_par.el_size_y); gtk_window_add_accel_group(GTK_WINDOW(el->Window), el->accel_group); el->Vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(el->Window), el->Vbox); build_menu(el); build_toolbar(el); build_notebook(el); build_event_list(el); g_signal_connect((gpointer)el->Window, "delete_event" , G_CALLBACK(on_Window_delete_event), el); gtk_widget_show_all(el->Window); if (start_date == NULL) set_el_data_from_cal(el); else set_el_data(el, start_date); gtk_drag_source_set(el->TreeView, GDK_BUTTON1_MASK , drag_targets, DRAG_TARGET_COUNT, GDK_ACTION_COPY); pixbuf = orage_create_icon(TRUE, 16, 16); gtk_drag_source_set_icon_pixbuf(el->TreeView, pixbuf); g_object_unref(pixbuf); g_signal_connect(el->TreeView, "drag_data_get" , G_CALLBACK(drag_data_get), NULL); return(el); }