4797 lines
144 KiB
C
4797 lines
144 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) version 3.
|
|
*
|
|
* 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with the program; if not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
*
|
|
* Authors:
|
|
* Damon Chaplin <damon@ximian.com>
|
|
* Rodrigo Moya <rodrigo@ximian.com>
|
|
*
|
|
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* EWeekView - displays the Week & Month views of the calendar.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "e-week-view.h"
|
|
#include "ea-calendar.h"
|
|
|
|
#include <math.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <glib/gi18n.h>
|
|
#include <libgnomecanvas/libgnomecanvas.h>
|
|
#include <text/e-text.h>
|
|
#include <misc/e-canvas-utils.h>
|
|
#include <e-util/e-unicode.h>
|
|
#include <e-util/e-categories-config.h>
|
|
#include <e-util/e-dialog-utils.h>
|
|
#include <e-util/e-util.h>
|
|
#include "dialogs/delete-comp.h"
|
|
#include "dialogs/delete-error.h"
|
|
#include "dialogs/send-comp.h"
|
|
#include "dialogs/cancel-comp.h"
|
|
#include "dialogs/recur-comp.h"
|
|
#include "dialogs/goto-dialog.h"
|
|
#include "calendar-config.h"
|
|
#include "comp-util.h"
|
|
#include "itip-utils.h"
|
|
#include "calendar-config.h"
|
|
#include "print.h"
|
|
#include "e-cal-model-calendar.h"
|
|
#include "e-week-view-event-item.h"
|
|
#include "e-week-view-layout.h"
|
|
#include "e-week-view-main-item.h"
|
|
#include "e-week-view-titles-item.h"
|
|
#include "misc.h"
|
|
#include <e-util/e-icon-factory.h>
|
|
|
|
/* Images */
|
|
#include "art/jump.xpm"
|
|
|
|
#define E_WEEK_VIEW_SMALL_FONT_PTSIZE 7
|
|
|
|
#define E_WEEK_VIEW_JUMP_BUTTON_WIDTH 16
|
|
#define E_WEEK_VIEW_JUMP_BUTTON_HEIGHT 8
|
|
|
|
#define E_WEEK_VIEW_JUMP_BUTTON_X_PAD 3
|
|
#define E_WEEK_VIEW_JUMP_BUTTON_Y_PAD 3
|
|
|
|
#define E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS -1
|
|
|
|
/* The timeout before we do a layout, so we don't do a layout for each event
|
|
* we get from the server. */
|
|
#define E_WEEK_VIEW_LAYOUT_TIMEOUT 100
|
|
|
|
typedef struct {
|
|
EWeekView *week_view;
|
|
ECalModelComponent *comp_data;
|
|
} AddEventData;
|
|
|
|
static void e_week_view_dispose (GObject *object);
|
|
static void e_week_view_realize (GtkWidget *widget);
|
|
static void e_week_view_set_colors (EWeekView *week_view, GtkWidget *widget);
|
|
static void e_week_view_unrealize (GtkWidget *widget);
|
|
static void e_week_view_style_set (GtkWidget *widget,
|
|
GtkStyle *previous_style);
|
|
static void e_week_view_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation);
|
|
static void e_week_view_recalc_cell_sizes (EWeekView *week_view);
|
|
static gint e_week_view_focus_in (GtkWidget *widget,
|
|
GdkEventFocus *event);
|
|
static gint e_week_view_focus_out (GtkWidget *widget,
|
|
GdkEventFocus *event);
|
|
static gboolean e_week_view_get_next_tab_event (EWeekView *week_view,
|
|
GtkDirectionType direction,
|
|
gint current_event_num,
|
|
gint current_span_num,
|
|
gint *next_event_num,
|
|
gint *next_span_num);
|
|
static gboolean e_week_view_focus (GtkWidget *widget,
|
|
GtkDirectionType direction);
|
|
static GList *e_week_view_get_selected_events (ECalendarView *cal_view);
|
|
static gboolean e_week_view_get_selected_time_range (ECalendarView *cal_view, time_t *start_time, time_t *end_time);
|
|
static void e_week_view_set_selected_time_range (ECalendarView *cal_view, time_t start_time, time_t end_time);
|
|
static gboolean e_week_view_get_visible_time_range (ECalendarView *cal_view, time_t *start_time, time_t *end_time);
|
|
static void e_week_view_paste_text (ECalendarView *week_view);
|
|
static void e_week_view_update_query (EWeekView *week_view);
|
|
|
|
static gboolean e_week_view_on_button_press (GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
EWeekView *week_view);
|
|
static gboolean e_week_view_on_button_release (GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
EWeekView *week_view);
|
|
static gboolean e_week_view_on_scroll (GtkWidget *widget,
|
|
GdkEventScroll *scroll,
|
|
EWeekView *week_view);
|
|
static gboolean e_week_view_on_motion (GtkWidget *widget,
|
|
GdkEventMotion *event,
|
|
EWeekView *week_view);
|
|
static gint e_week_view_convert_position_to_day (EWeekView *week_view,
|
|
gint x,
|
|
gint y);
|
|
static void e_week_view_update_selection (EWeekView *week_view,
|
|
gint day);
|
|
|
|
static void e_week_view_free_events (EWeekView *week_view);
|
|
static gboolean e_week_view_add_event (ECalComponent *comp,
|
|
time_t start,
|
|
time_t end,
|
|
gboolean prepend,
|
|
gpointer data);
|
|
static void e_week_view_check_layout (EWeekView *week_view);
|
|
static void e_week_view_ensure_events_sorted (EWeekView *week_view);
|
|
static void e_week_view_reshape_events (EWeekView *week_view);
|
|
static void e_week_view_reshape_event_span (EWeekView *week_view,
|
|
gint event_num,
|
|
gint span_num);
|
|
static void e_week_view_recalc_day_starts (EWeekView *week_view,
|
|
time_t lower);
|
|
static void e_week_view_on_editing_started (EWeekView *week_view,
|
|
GnomeCanvasItem *item);
|
|
static void e_week_view_on_editing_stopped (EWeekView *week_view,
|
|
GnomeCanvasItem *item);
|
|
static gboolean e_week_view_find_event_from_uid (EWeekView *week_view,
|
|
ECalClient *client,
|
|
const gchar *uid,
|
|
const gchar *rid,
|
|
gint *event_num_return);
|
|
typedef gboolean (* EWeekViewForeachEventCallback) (EWeekView *week_view,
|
|
gint event_num,
|
|
gpointer data);
|
|
static void e_week_view_foreach_event_with_uid (EWeekView *week_view,
|
|
const gchar *uid,
|
|
EWeekViewForeachEventCallback callback,
|
|
gpointer data);
|
|
static gboolean e_week_view_on_text_item_event (GnomeCanvasItem *item,
|
|
GdkEvent *event,
|
|
EWeekView *week_view);
|
|
static gboolean e_week_view_event_move (ECalendarView *cal_view, ECalViewMoveDirection direction);
|
|
static gint e_week_view_get_day_offset_of_event (EWeekView *week_view, time_t event_time);
|
|
static void e_week_view_change_event_time (EWeekView *week_view, time_t start_dt, time_t end_dt, gboolean is_all_day);
|
|
static gboolean e_week_view_on_jump_button_event (GnomeCanvasItem *item,
|
|
GdkEvent *event,
|
|
EWeekView *week_view);
|
|
static gboolean e_week_view_key_press (GtkWidget *widget, GdkEventKey *event);
|
|
static gboolean e_week_view_do_key_press (GtkWidget *widget,
|
|
GdkEventKey *event);
|
|
static gint e_week_view_get_adjust_days_for_move_up (EWeekView *week_view, gint
|
|
current_day);
|
|
static gint e_week_view_get_adjust_days_for_move_down (EWeekView *week_view,gint current_day);
|
|
static gint e_week_view_get_adjust_days_for_move_left (EWeekView *week_view,gint current_day);
|
|
static gint e_week_view_get_adjust_days_for_move_right (EWeekView *week_view,gint current_day);
|
|
static gboolean e_week_view_popup_menu (GtkWidget *widget);
|
|
|
|
static gboolean e_week_view_remove_event_cb (EWeekView *week_view,
|
|
gint event_num,
|
|
gpointer data);
|
|
static gboolean e_week_view_recalc_display_start_day (EWeekView *week_view);
|
|
|
|
static void e_week_view_queue_layout (EWeekView *week_view);
|
|
static void e_week_view_cancel_layout (EWeekView *week_view);
|
|
static gboolean e_week_view_layout_timeout_cb (gpointer data);
|
|
|
|
G_DEFINE_TYPE (EWeekView, e_week_view, E_TYPE_CALENDAR_VIEW)
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_COMPRESS_WEEKEND,
|
|
PROP_SHOW_EVENT_END_TIMES
|
|
};
|
|
|
|
static gint map_left[] = {0, 1, 2, 0, 1, 2, 2};
|
|
static gint map_right[] = {3, 4, 5, 3, 4, 5, 6};
|
|
|
|
static void
|
|
week_view_process_component (EWeekView *week_view,
|
|
ECalModelComponent *comp_data)
|
|
{
|
|
ECalComponent *comp = NULL;
|
|
AddEventData add_event_data;
|
|
/* rid is never used in this function? */
|
|
const gchar *uid;
|
|
gchar *rid = NULL;
|
|
|
|
/* If we don't have a valid date set yet, just return. */
|
|
if (!g_date_valid (&week_view->first_day_shown))
|
|
return;
|
|
|
|
comp = e_cal_component_new ();
|
|
if (!e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp))) {
|
|
g_object_unref (comp);
|
|
|
|
g_message (G_STRLOC ": Could not set icalcomponent on ECalComponent");
|
|
return;
|
|
}
|
|
|
|
e_cal_component_get_uid (comp, &uid);
|
|
if (e_cal_component_is_instance (comp))
|
|
rid = e_cal_component_get_recurid_as_string (comp);
|
|
else
|
|
rid = NULL;
|
|
|
|
/* Add the object */
|
|
add_event_data.week_view = week_view;
|
|
add_event_data.comp_data = comp_data;
|
|
e_week_view_add_event (comp, comp_data->instance_start, comp_data->instance_end, FALSE, &add_event_data);
|
|
|
|
g_object_unref (comp);
|
|
g_free (rid);
|
|
}
|
|
|
|
static void
|
|
week_view_update_row (EWeekView *week_view,
|
|
gint row)
|
|
{
|
|
ECalModelComponent *comp_data;
|
|
ECalModel *model;
|
|
gint event_num;
|
|
const gchar *uid;
|
|
gchar *rid = NULL;
|
|
|
|
model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
|
|
comp_data = e_cal_model_get_component_at (model, row);
|
|
g_return_if_fail (comp_data != NULL);
|
|
|
|
uid = icalcomponent_get_uid (comp_data->icalcomp);
|
|
if (e_cal_util_component_is_instance (comp_data->icalcomp)) {
|
|
icalproperty *prop;
|
|
|
|
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_RECURRENCEID_PROPERTY);
|
|
if (prop)
|
|
rid = icaltime_as_ical_string_r (icalcomponent_get_recurrenceid (comp_data->icalcomp));
|
|
}
|
|
|
|
if (e_week_view_find_event_from_uid (week_view, comp_data->client, uid, rid, &event_num))
|
|
e_week_view_remove_event_cb (week_view, event_num, NULL);
|
|
|
|
g_free (rid);
|
|
|
|
week_view_process_component (week_view, comp_data);
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
e_week_view_queue_layout (week_view);
|
|
}
|
|
|
|
static void
|
|
week_view_model_cell_changed_cb (EWeekView *week_view,
|
|
gint col,
|
|
gint row)
|
|
{
|
|
if (!E_CALENDAR_VIEW (week_view)->in_focus) {
|
|
e_week_view_free_events (week_view);
|
|
week_view->requires_update = TRUE;
|
|
return;
|
|
}
|
|
|
|
week_view_update_row (week_view, row);
|
|
}
|
|
|
|
static void
|
|
week_view_model_comps_deleted_cb (EWeekView *week_view,
|
|
gpointer data)
|
|
{
|
|
GSList *l, *list = data;
|
|
|
|
/* FIXME Stop editing? */
|
|
if (!E_CALENDAR_VIEW (week_view)->in_focus) {
|
|
e_week_view_free_events (week_view);
|
|
week_view->requires_update = TRUE;
|
|
return;
|
|
}
|
|
|
|
for (l = list; l != NULL; l = g_slist_next (l)) {
|
|
gint event_num;
|
|
const gchar *uid;
|
|
gchar *rid = NULL;
|
|
ECalModelComponent *comp_data = l->data;
|
|
|
|
uid = icalcomponent_get_uid (comp_data->icalcomp);
|
|
if (e_cal_util_component_is_instance (comp_data->icalcomp)) {
|
|
icalproperty *prop;
|
|
|
|
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_RECURRENCEID_PROPERTY);
|
|
if (prop)
|
|
rid = icaltime_as_ical_string_r (icalcomponent_get_recurrenceid (comp_data->icalcomp));
|
|
}
|
|
|
|
if (e_week_view_find_event_from_uid (week_view, comp_data->client, uid, rid, &event_num))
|
|
e_week_view_remove_event_cb (week_view, event_num, NULL);
|
|
g_free (rid);
|
|
}
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
e_week_view_queue_layout (week_view);
|
|
}
|
|
|
|
static void
|
|
week_view_model_row_changed_cb (EWeekView *week_view,
|
|
gint row)
|
|
{
|
|
if (!E_CALENDAR_VIEW (week_view)->in_focus) {
|
|
e_week_view_free_events (week_view);
|
|
week_view->requires_update = TRUE;
|
|
return;
|
|
}
|
|
|
|
week_view_update_row (week_view, row);
|
|
}
|
|
|
|
static void
|
|
week_view_model_rows_inserted_cb (EWeekView *week_view,
|
|
gint row,
|
|
gint count)
|
|
{
|
|
ECalModel *model;
|
|
gint i;
|
|
|
|
if (!E_CALENDAR_VIEW (week_view)->in_focus) {
|
|
e_week_view_free_events (week_view);
|
|
week_view->requires_update = TRUE;
|
|
return;
|
|
}
|
|
|
|
model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
|
|
|
|
for (i = 0; i < count; i++) {
|
|
ECalModelComponent *comp_data;
|
|
|
|
comp_data = e_cal_model_get_component_at (model, row + i);
|
|
if (comp_data == NULL) {
|
|
g_warning ("comp_data is NULL\n");
|
|
continue;
|
|
}
|
|
week_view_process_component (week_view, comp_data);
|
|
}
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
e_week_view_queue_layout (week_view);
|
|
}
|
|
|
|
static void
|
|
week_view_time_range_changed_cb (EWeekView *week_view,
|
|
time_t start_time,
|
|
time_t end_time,
|
|
ECalModel *model)
|
|
{
|
|
GDate date, base_date;
|
|
gint day_offset, weekday, week_start_offset;
|
|
gboolean update_adjustment_value = FALSE;
|
|
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
|
|
/* Calculate the weekday of the given date, 0 = Mon. */
|
|
weekday = g_date_get_weekday (&date) - 1;
|
|
|
|
/* Convert it to an offset from the start of the display. */
|
|
week_start_offset = (weekday + 7 - week_view->display_start_day) % 7;
|
|
|
|
/* Set the day_offset to the result, so we move back to the
|
|
* start of the week. */
|
|
day_offset = week_start_offset;
|
|
|
|
/* Calculate the base date, i.e. the first day shown when the
|
|
* scrollbar adjustment value is 0. */
|
|
base_date = date;
|
|
g_date_subtract_days (&base_date, day_offset);
|
|
|
|
/* See if we need to update the base date. */
|
|
if (!g_date_valid (&week_view->base_date)
|
|
|| week_view->update_base_date) {
|
|
week_view->base_date = base_date;
|
|
update_adjustment_value = TRUE;
|
|
}
|
|
|
|
/* See if we need to update the first day shown. */
|
|
if (!g_date_valid (&week_view->first_day_shown)
|
|
|| g_date_compare (&week_view->first_day_shown, &base_date)) {
|
|
week_view->first_day_shown = base_date;
|
|
start_time = time_add_day_with_zone (start_time, -day_offset,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
start_time = time_day_begin_with_zone (start_time,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
e_week_view_recalc_day_starts (week_view, start_time);
|
|
}
|
|
|
|
/* Reset the adjustment value to 0 if the base address has changed.
|
|
* Note that we do this after updating first_day_shown so that our
|
|
* signal handler will not try to reload the events. */
|
|
if (update_adjustment_value) {
|
|
GtkRange *range;
|
|
GtkAdjustment *adjustment;
|
|
|
|
range = GTK_RANGE (week_view->vscrollbar);
|
|
adjustment = gtk_range_get_adjustment (range);
|
|
gtk_adjustment_set_value (adjustment, 0);
|
|
}
|
|
|
|
if (!E_CALENDAR_VIEW (week_view)->in_focus) {
|
|
e_week_view_free_events (week_view);
|
|
week_view->requires_update = TRUE;
|
|
return;
|
|
}
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
|
|
/* FIXME Preserve selection if possible */
|
|
if (week_view->selection_start_day == -1 ||
|
|
(week_view->multi_week_view ? week_view->weeks_shown * 7 : 7) <= week_view->selection_start_day)
|
|
e_week_view_set_selected_time_range (E_CALENDAR_VIEW (week_view), start_time, start_time);
|
|
}
|
|
|
|
static void
|
|
timezone_changed_cb (ECalModel *cal_model,
|
|
icaltimezone *old_zone,
|
|
icaltimezone *new_zone,
|
|
gpointer user_data)
|
|
{
|
|
ECalendarView *cal_view = (ECalendarView *) user_data;
|
|
struct icaltimetype tt = icaltime_null_time ();
|
|
time_t lower;
|
|
EWeekView *week_view = (EWeekView *) cal_view;
|
|
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
if (!cal_view->in_focus) {
|
|
e_week_view_free_events (week_view);
|
|
week_view->requires_update = TRUE;
|
|
return;
|
|
}
|
|
|
|
/* If we don't have a valid date set yet, just return. */
|
|
if (!g_date_valid (&week_view->first_day_shown))
|
|
return;
|
|
|
|
/* Recalculate the new start of the first week. We just use exactly
|
|
* the same time, but with the new timezone. */
|
|
tt.year = g_date_get_year (&week_view->first_day_shown);
|
|
tt.month = g_date_get_month (&week_view->first_day_shown);
|
|
tt.day = g_date_get_day (&week_view->first_day_shown);
|
|
|
|
lower = icaltime_as_timet_with_zone (tt, new_zone);
|
|
|
|
e_week_view_recalc_day_starts (week_view, lower);
|
|
e_week_view_update_query (week_view);
|
|
}
|
|
|
|
static void
|
|
week_view_notify_week_start_day_cb (EWeekView *week_view)
|
|
{
|
|
GDate *first_day_shown;
|
|
|
|
first_day_shown = &week_view->first_day_shown;
|
|
|
|
e_week_view_recalc_display_start_day (week_view);
|
|
|
|
/* Recalculate the days shown and reload if necessary. */
|
|
if (g_date_valid (first_day_shown))
|
|
e_week_view_set_first_day_shown (week_view, first_day_shown);
|
|
|
|
gtk_widget_queue_draw (week_view->titles_canvas);
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
|
|
static void
|
|
week_view_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id) {
|
|
case PROP_COMPRESS_WEEKEND:
|
|
e_week_view_set_compress_weekend (
|
|
E_WEEK_VIEW (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
|
|
case PROP_SHOW_EVENT_END_TIMES:
|
|
e_week_view_set_show_event_end_times (
|
|
E_WEEK_VIEW (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
}
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
|
|
static void
|
|
week_view_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id) {
|
|
case PROP_COMPRESS_WEEKEND:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_week_view_get_compress_weekend (
|
|
E_WEEK_VIEW (object)));
|
|
return;
|
|
|
|
case PROP_SHOW_EVENT_END_TIMES:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_week_view_get_show_event_end_times (
|
|
E_WEEK_VIEW (object)));
|
|
return;
|
|
}
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
|
|
static void
|
|
week_view_constructed (GObject *object)
|
|
{
|
|
ECalModel *model;
|
|
ECalendarView *calendar_view;
|
|
|
|
/* Chain up to parent's constructed() method. */
|
|
G_OBJECT_CLASS (e_week_view_parent_class)->constructed (object);
|
|
|
|
calendar_view = E_CALENDAR_VIEW (object);
|
|
model = e_calendar_view_get_model (calendar_view);
|
|
|
|
e_week_view_recalc_display_start_day (E_WEEK_VIEW (object));
|
|
|
|
g_signal_connect_swapped (
|
|
model, "notify::week-start-day",
|
|
G_CALLBACK (week_view_notify_week_start_day_cb), object);
|
|
|
|
g_signal_connect_swapped (
|
|
model, "comps-deleted",
|
|
G_CALLBACK (week_view_model_comps_deleted_cb), object);
|
|
|
|
g_signal_connect_swapped (
|
|
model, "model-cell-changed",
|
|
G_CALLBACK (week_view_model_cell_changed_cb), object);
|
|
|
|
g_signal_connect_swapped (
|
|
model, "model-row-changed",
|
|
G_CALLBACK (week_view_model_row_changed_cb), object);
|
|
|
|
g_signal_connect_swapped (
|
|
model, "model-rows-inserted",
|
|
G_CALLBACK (week_view_model_rows_inserted_cb), object);
|
|
|
|
g_signal_connect_swapped (
|
|
model, "time-range-changed",
|
|
G_CALLBACK (week_view_time_range_changed_cb), object);
|
|
|
|
}
|
|
|
|
static void
|
|
week_view_cursor_key_up (EWeekView *week_view)
|
|
{
|
|
if (week_view->selection_start_day == -1)
|
|
return;
|
|
|
|
week_view->selection_start_day--;
|
|
|
|
if (week_view->selection_start_day < 0) {
|
|
e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_UP);
|
|
week_view->selection_start_day = 6;
|
|
}
|
|
|
|
week_view->selection_end_day = week_view->selection_start_day;
|
|
g_signal_emit_by_name (week_view, "selected_time_changed");
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
|
|
static void
|
|
week_view_cursor_key_down (EWeekView *week_view)
|
|
{
|
|
if (week_view->selection_start_day == -1)
|
|
return;
|
|
|
|
week_view->selection_start_day++;
|
|
|
|
if (week_view->selection_start_day > 6) {
|
|
e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_DOWN);
|
|
week_view->selection_start_day = 0;
|
|
}
|
|
|
|
week_view->selection_end_day = week_view->selection_start_day;
|
|
g_signal_emit_by_name (week_view, "selected_time_changed");
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
|
|
static void
|
|
week_view_cursor_key_left (EWeekView *week_view)
|
|
{
|
|
if (week_view->selection_start_day == -1)
|
|
return;
|
|
|
|
week_view->selection_start_day = map_left[week_view->selection_start_day];
|
|
week_view->selection_end_day = week_view->selection_start_day;
|
|
g_signal_emit_by_name (week_view, "selected_time_changed");
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
|
|
static void
|
|
week_view_cursor_key_right (EWeekView *week_view)
|
|
{
|
|
if (week_view->selection_start_day == -1)
|
|
return;
|
|
|
|
week_view->selection_start_day = map_right[week_view->selection_start_day];
|
|
week_view->selection_end_day = week_view->selection_start_day;
|
|
g_signal_emit_by_name (week_view, "selected_time_changed");
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
|
|
static void
|
|
month_scrol_by_week_changed_cb (GSettings *settings,
|
|
const gchar *key,
|
|
gpointer user_data)
|
|
{
|
|
EWeekView *week_view = user_data;
|
|
|
|
g_return_if_fail (week_view != NULL);
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
if (week_view->multi_week_view && week_view->month_scroll_by_week != calendar_config_get_month_scroll_by_week ()) {
|
|
week_view->multi_week_view = FALSE;
|
|
e_week_view_set_multi_week_view (week_view, TRUE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
e_week_view_class_init (EWeekViewClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
GtkWidgetClass *widget_class;
|
|
ECalendarViewClass *view_class;
|
|
|
|
object_class = G_OBJECT_CLASS (class);
|
|
object_class->set_property = week_view_set_property;
|
|
object_class->get_property = week_view_get_property;
|
|
object_class->constructed = week_view_constructed;
|
|
object_class->dispose = e_week_view_dispose;
|
|
|
|
widget_class = GTK_WIDGET_CLASS (class);
|
|
widget_class->realize = e_week_view_realize;
|
|
widget_class->unrealize = e_week_view_unrealize;
|
|
widget_class->style_set = e_week_view_style_set;
|
|
widget_class->size_allocate = e_week_view_size_allocate;
|
|
widget_class->focus_in_event = e_week_view_focus_in;
|
|
widget_class->focus_out_event = e_week_view_focus_out;
|
|
widget_class->key_press_event = e_week_view_key_press;
|
|
widget_class->popup_menu = e_week_view_popup_menu;
|
|
widget_class->focus = e_week_view_focus;
|
|
|
|
view_class = E_CALENDAR_VIEW_CLASS (class);
|
|
view_class->get_selected_events = e_week_view_get_selected_events;
|
|
view_class->get_selected_time_range = e_week_view_get_selected_time_range;
|
|
view_class->set_selected_time_range = e_week_view_set_selected_time_range;
|
|
view_class->get_visible_time_range = e_week_view_get_visible_time_range;
|
|
view_class->paste_text = e_week_view_paste_text;
|
|
|
|
class->cursor_key_up = week_view_cursor_key_up;
|
|
class->cursor_key_down = week_view_cursor_key_down;
|
|
class->cursor_key_left = week_view_cursor_key_left;
|
|
class->cursor_key_right = week_view_cursor_key_right;
|
|
|
|
/* XXX This property really belongs in EMonthView,
|
|
* but too much drawing code is tied to it. */
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_COMPRESS_WEEKEND,
|
|
g_param_spec_boolean (
|
|
"compress-weekend",
|
|
"Compress Weekend",
|
|
NULL,
|
|
TRUE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_SHOW_EVENT_END_TIMES,
|
|
g_param_spec_boolean (
|
|
"show-event-end-times",
|
|
"Show Event End Times",
|
|
NULL,
|
|
TRUE,
|
|
G_PARAM_READWRITE));
|
|
|
|
/* init the accessibility support for e_week_view */
|
|
e_week_view_a11y_init ();
|
|
}
|
|
|
|
static void
|
|
e_week_view_init (EWeekView *week_view)
|
|
{
|
|
GnomeCanvasGroup *canvas_group;
|
|
GtkAdjustment *adjustment;
|
|
GdkPixbuf *pixbuf;
|
|
gint i;
|
|
|
|
gtk_widget_set_can_focus (GTK_WIDGET (week_view), TRUE);
|
|
|
|
week_view->event_destroyed = FALSE;
|
|
week_view->events = g_array_new (FALSE, FALSE,
|
|
sizeof (EWeekViewEvent));
|
|
week_view->events_sorted = TRUE;
|
|
week_view->events_need_layout = FALSE;
|
|
week_view->events_need_reshape = FALSE;
|
|
|
|
week_view->layout_timeout_id = 0;
|
|
|
|
week_view->spans = NULL;
|
|
|
|
week_view->multi_week_view = FALSE;
|
|
week_view->month_scroll_by_week = FALSE;
|
|
week_view->scroll_by_week_notif_id = 0;
|
|
week_view->update_base_date = TRUE;
|
|
week_view->weeks_shown = 6;
|
|
week_view->rows = 6;
|
|
week_view->columns = 2;
|
|
week_view->compress_weekend = TRUE;
|
|
week_view->show_event_end_times = TRUE;
|
|
week_view->display_start_day = 0; /* Monday. */
|
|
|
|
g_date_clear (&week_view->base_date, 1);
|
|
g_date_clear (&week_view->first_day_shown, 1);
|
|
|
|
week_view->row_height = 10;
|
|
week_view->rows_per_cell = 1;
|
|
|
|
week_view->selection_start_day = -1;
|
|
week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
|
|
|
|
week_view->pressed_event_num = -1;
|
|
week_view->editing_event_num = -1;
|
|
|
|
week_view->last_edited_comp_string = NULL;
|
|
|
|
/* Create the small font. */
|
|
week_view->use_small_font = TRUE;
|
|
|
|
week_view->small_font_desc =
|
|
pango_font_description_copy (gtk_widget_get_style (GTK_WIDGET (week_view))->font_desc);
|
|
pango_font_description_set_size (week_view->small_font_desc,
|
|
E_WEEK_VIEW_SMALL_FONT_PTSIZE * PANGO_SCALE);
|
|
|
|
/* String to use in 12-hour time format for times in the morning. */
|
|
week_view->am_string = _("am");
|
|
|
|
/* String to use in 12-hour time format for times in the afternoon. */
|
|
week_view->pm_string = _("pm");
|
|
|
|
week_view->bc_event_time = 0;
|
|
week_view->before_click_dtstart = 0;
|
|
week_view->before_click_dtend = 0;
|
|
|
|
/*
|
|
* Titles Canvas. Note that we don't show it is only shown in the
|
|
* Month view.
|
|
*/
|
|
week_view->titles_canvas = e_canvas_new ();
|
|
gtk_table_attach (GTK_TABLE (week_view), week_view->titles_canvas,
|
|
1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
|
|
|
|
canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->titles_canvas)->root);
|
|
|
|
week_view->titles_canvas_item =
|
|
gnome_canvas_item_new (canvas_group,
|
|
e_week_view_titles_item_get_type (),
|
|
"EWeekViewTitlesItem::week_view", week_view,
|
|
NULL);
|
|
|
|
/*
|
|
* Main Canvas
|
|
*/
|
|
week_view->main_canvas = e_canvas_new ();
|
|
gtk_table_attach (GTK_TABLE (week_view), week_view->main_canvas,
|
|
1, 2, 1, 2,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
|
|
gtk_widget_show (week_view->main_canvas);
|
|
|
|
canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root);
|
|
|
|
week_view->main_canvas_item =
|
|
gnome_canvas_item_new (canvas_group,
|
|
e_week_view_main_item_get_type (),
|
|
"EWeekViewMainItem::week_view", week_view,
|
|
NULL);
|
|
|
|
g_signal_connect_after (
|
|
week_view->main_canvas, "button_press_event",
|
|
G_CALLBACK (e_week_view_on_button_press), week_view);
|
|
g_signal_connect (
|
|
week_view->main_canvas, "button_release_event",
|
|
G_CALLBACK (e_week_view_on_button_release), week_view);
|
|
g_signal_connect (
|
|
week_view->main_canvas, "scroll_event",
|
|
G_CALLBACK (e_week_view_on_scroll), week_view);
|
|
g_signal_connect (
|
|
week_view->main_canvas, "motion_notify_event",
|
|
G_CALLBACK (e_week_view_on_motion), week_view);
|
|
|
|
/* Create the buttons to jump to each days. */
|
|
pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) jump_xpm);
|
|
|
|
for (i = 0; i < E_WEEK_VIEW_MAX_WEEKS * 7; i++) {
|
|
week_view->jump_buttons[i] = gnome_canvas_item_new
|
|
(canvas_group,
|
|
gnome_canvas_pixbuf_get_type (),
|
|
"GnomeCanvasPixbuf::pixbuf", pixbuf,
|
|
NULL);
|
|
|
|
g_signal_connect (
|
|
week_view->jump_buttons[i], "event",
|
|
G_CALLBACK (e_week_view_on_jump_button_event), week_view);
|
|
}
|
|
week_view->focused_jump_button = E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS;
|
|
|
|
g_object_unref (pixbuf);
|
|
|
|
/*
|
|
* Scrollbar.
|
|
*/
|
|
adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, -52, 52, 1, 1, 1));
|
|
|
|
week_view->vscrollbar = gtk_vscrollbar_new (adjustment);
|
|
gtk_table_attach (GTK_TABLE (week_view), week_view->vscrollbar,
|
|
2, 3, 1, 2, 0, GTK_EXPAND | GTK_FILL, 0, 0);
|
|
gtk_widget_show (week_view->vscrollbar);
|
|
|
|
/* Create the cursors. */
|
|
week_view->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR);
|
|
week_view->move_cursor = gdk_cursor_new (GDK_FLEUR);
|
|
week_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
|
|
week_view->last_cursor_set = NULL;
|
|
|
|
week_view->requires_update = FALSE;
|
|
}
|
|
|
|
/**
|
|
* e_week_view_new:
|
|
* @Returns: a new #EWeekView.
|
|
*
|
|
* Creates a new #EWeekView.
|
|
**/
|
|
ECalendarView *
|
|
e_week_view_new (ECalModel *model)
|
|
{
|
|
ECalendarView *view;
|
|
g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
|
|
|
|
view = g_object_new (E_TYPE_WEEK_VIEW, "model", model, NULL);
|
|
|
|
g_signal_connect (
|
|
model, "timezone_changed",
|
|
G_CALLBACK (timezone_changed_cb), view);
|
|
|
|
return view;
|
|
}
|
|
|
|
static void
|
|
e_week_view_dispose (GObject *object)
|
|
{
|
|
EWeekView *week_view;
|
|
|
|
week_view = E_WEEK_VIEW (object);
|
|
|
|
e_week_view_cancel_layout (week_view);
|
|
|
|
if (week_view->events) {
|
|
e_week_view_free_events (week_view);
|
|
g_array_free (week_view->events, TRUE);
|
|
week_view->events = NULL;
|
|
}
|
|
|
|
if (week_view->small_font_desc) {
|
|
pango_font_description_free (week_view->small_font_desc);
|
|
week_view->small_font_desc = NULL;
|
|
}
|
|
|
|
if (week_view->normal_cursor) {
|
|
g_object_unref (week_view->normal_cursor);
|
|
week_view->normal_cursor = NULL;
|
|
}
|
|
if (week_view->move_cursor) {
|
|
g_object_unref (week_view->move_cursor);
|
|
week_view->move_cursor = NULL;
|
|
}
|
|
if (week_view->resize_width_cursor) {
|
|
g_object_unref (week_view->resize_width_cursor);
|
|
week_view->resize_width_cursor = NULL;
|
|
}
|
|
|
|
calendar_config_remove_notification (month_scrol_by_week_changed_cb, week_view);
|
|
|
|
/* Chain up to parent's dispose() method. */
|
|
G_OBJECT_CLASS (e_week_view_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
e_week_view_realize (GtkWidget *widget)
|
|
{
|
|
EWeekView *week_view;
|
|
|
|
if (GTK_WIDGET_CLASS (e_week_view_parent_class)->realize)
|
|
(*GTK_WIDGET_CLASS (e_week_view_parent_class)->realize)(widget);
|
|
|
|
week_view = E_WEEK_VIEW (widget);
|
|
|
|
/* Allocate the colors. */
|
|
e_week_view_set_colors (week_view, widget);
|
|
|
|
/* Create the pixmaps. */
|
|
week_view->reminder_icon = e_icon_factory_get_icon ("stock_bell", GTK_ICON_SIZE_MENU);
|
|
week_view->recurrence_icon = e_icon_factory_get_icon ("view-refresh", GTK_ICON_SIZE_MENU);
|
|
week_view->timezone_icon = e_icon_factory_get_icon ("stock_timezone", GTK_ICON_SIZE_MENU);
|
|
week_view->attach_icon = e_icon_factory_get_icon ("mail-attachment", GTK_ICON_SIZE_MENU);
|
|
week_view->meeting_icon = e_icon_factory_get_icon ("stock_people", GTK_ICON_SIZE_MENU);
|
|
}
|
|
|
|
static GdkColor
|
|
color_inc (GdkColor c,
|
|
gint amount)
|
|
{
|
|
#define dec(x) \
|
|
if (x + amount >= 0 \
|
|
&& x + amount <= 0xFFFF) \
|
|
x += amount; \
|
|
else if (amount <= 0) \
|
|
x = 0; \
|
|
else \
|
|
x = 0xFFFF;
|
|
|
|
dec (c.red);
|
|
dec (c.green);
|
|
dec (c.blue);
|
|
|
|
#undef dec
|
|
|
|
return c;
|
|
}
|
|
|
|
static void
|
|
e_week_view_set_colors (EWeekView *week_view,
|
|
GtkWidget *widget)
|
|
{
|
|
GtkStyle *style;
|
|
|
|
style = gtk_widget_get_style (widget);
|
|
|
|
week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS] = style->base[GTK_STATE_INSENSITIVE];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_ODD_MONTHS] = style->base[GTK_STATE_NORMAL];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND] = style->base[GTK_STATE_NORMAL];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER] = style->dark[GTK_STATE_NORMAL];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_EVENT_TEXT] = style->text[GTK_STATE_NORMAL];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_GRID] = style->dark[GTK_STATE_NORMAL];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_SELECTED] = style->base[GTK_STATE_SELECTED];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_SELECTED_UNFOCUSSED] = style->bg[GTK_STATE_SELECTED];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_DATES] = style->text[GTK_STATE_NORMAL];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_DATES_SELECTED] = style->text[GTK_STATE_SELECTED];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_TODAY] = style->base[GTK_STATE_SELECTED];
|
|
week_view->colors[E_WEEK_VIEW_COLOR_TODAY_BACKGROUND] = get_today_background (week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND]);
|
|
week_view->colors[E_WEEK_VIEW_COLOR_MONTH_NONWORKING_DAY] = color_inc (week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS], -0x0A0A);
|
|
}
|
|
|
|
static void
|
|
e_week_view_unrealize (GtkWidget *widget)
|
|
{
|
|
EWeekView *week_view;
|
|
|
|
week_view = E_WEEK_VIEW (widget);
|
|
|
|
g_object_unref (week_view->reminder_icon);
|
|
week_view->reminder_icon = NULL;
|
|
g_object_unref (week_view->recurrence_icon);
|
|
week_view->recurrence_icon = NULL;
|
|
g_object_unref (week_view->timezone_icon);
|
|
week_view->timezone_icon = NULL;
|
|
g_object_unref (week_view->attach_icon);
|
|
week_view->attach_icon = NULL;
|
|
g_object_unref (week_view->meeting_icon);
|
|
week_view->meeting_icon = NULL;
|
|
|
|
if (GTK_WIDGET_CLASS (e_week_view_parent_class)->unrealize)
|
|
(*GTK_WIDGET_CLASS (e_week_view_parent_class)->unrealize)(widget);
|
|
}
|
|
|
|
static gint
|
|
get_string_width (PangoLayout *layout,
|
|
const gchar *string)
|
|
{
|
|
gint width;
|
|
|
|
pango_layout_set_text (layout, string, -1);
|
|
pango_layout_get_pixel_size (layout, &width, NULL);
|
|
return width;
|
|
}
|
|
|
|
/* FIXME: This is also needed in e-day-view-time-item.c. We should probably use
|
|
* pango's approximation function, but it needs a language tag. Find out how to
|
|
* get one of those properly. */
|
|
static gint
|
|
get_digit_width (PangoLayout *layout)
|
|
{
|
|
gint digit;
|
|
gint max_digit_width = 1;
|
|
|
|
for (digit = '0'; digit <= '9'; digit++) {
|
|
gchar digit_char;
|
|
gint digit_width;
|
|
|
|
digit_char = digit;
|
|
|
|
pango_layout_set_text (layout, &digit_char, 1);
|
|
pango_layout_get_pixel_size (layout, &digit_width, NULL);
|
|
|
|
max_digit_width = MAX (max_digit_width, digit_width);
|
|
}
|
|
|
|
return max_digit_width;
|
|
}
|
|
|
|
static GdkColor
|
|
e_week_view_get_text_color (EWeekView *week_view,
|
|
EWeekViewEvent *event,
|
|
GtkWidget *widget)
|
|
{
|
|
GtkStyle *style;
|
|
GdkColor bg_color;
|
|
guint16 red, green, blue;
|
|
gdouble cc = 65535.0;
|
|
|
|
red = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].red;
|
|
green = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].green;
|
|
blue = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].blue;
|
|
|
|
if (is_comp_data_valid (event) && gdk_color_parse (e_cal_model_get_color_for_component (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), event->comp_data),
|
|
&bg_color)) {
|
|
red = bg_color.red;
|
|
green = bg_color.green;
|
|
blue = bg_color.blue;
|
|
}
|
|
|
|
style = gtk_widget_get_style (widget);
|
|
|
|
if ((red / cc > 0.7) || (green / cc > 0.7) || (blue / cc > 0.7 ))
|
|
return style->black;
|
|
else
|
|
return style->white;
|
|
}
|
|
|
|
static void
|
|
e_week_view_style_set (GtkWidget *widget,
|
|
GtkStyle *previous_style)
|
|
{
|
|
EWeekView *week_view;
|
|
GtkStyle *style;
|
|
gint day, day_width, max_day_width, max_abbr_day_width;
|
|
gint month, month_width, max_month_width, max_abbr_month_width;
|
|
gint span_num;
|
|
const gchar *name;
|
|
PangoFontDescription *font_desc;
|
|
PangoContext *pango_context;
|
|
PangoFontMetrics *font_metrics;
|
|
PangoLayout *layout;
|
|
EWeekViewEventSpan *span;
|
|
|
|
if (GTK_WIDGET_CLASS (e_week_view_parent_class)->style_set)
|
|
(*GTK_WIDGET_CLASS (e_week_view_parent_class)->style_set)(widget, previous_style);
|
|
|
|
week_view = E_WEEK_VIEW (widget);
|
|
style = gtk_widget_get_style (widget);
|
|
|
|
e_week_view_set_colors (week_view, widget);
|
|
if (week_view->spans) {
|
|
for (span_num = 0; span_num < week_view->spans->len;
|
|
span_num++) {
|
|
span = &g_array_index (week_view->spans,
|
|
EWeekViewEventSpan, span_num);
|
|
if (span->text_item) {
|
|
gnome_canvas_item_set (
|
|
span->text_item,
|
|
"fill_color_gdk", &style->text[GTK_STATE_NORMAL],
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set up Pango prerequisites */
|
|
font_desc = style->font_desc;
|
|
pango_context = gtk_widget_get_pango_context (widget);
|
|
font_metrics = pango_context_get_metrics (pango_context, font_desc,
|
|
pango_context_get_language (pango_context));
|
|
layout = pango_layout_new (pango_context);
|
|
|
|
/* Recalculate the height of each row based on the font size. */
|
|
week_view->row_height = PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
|
|
PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) +
|
|
E_WEEK_VIEW_EVENT_BORDER_HEIGHT * 2 + E_WEEK_VIEW_EVENT_TEXT_Y_PAD * 2;
|
|
week_view->row_height = MAX (week_view->row_height, E_WEEK_VIEW_ICON_HEIGHT + E_WEEK_VIEW_ICON_Y_PAD + E_WEEK_VIEW_EVENT_BORDER_HEIGHT * 2);
|
|
|
|
/* Check that the small font is smaller than the default font.
|
|
* If it isn't, we won't use it. */
|
|
if (week_view->small_font_desc) {
|
|
if (PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
|
|
PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
|
|
<= E_WEEK_VIEW_SMALL_FONT_PTSIZE)
|
|
week_view->use_small_font = FALSE;
|
|
}
|
|
|
|
/* Set the height of the top canvas. */
|
|
gtk_widget_set_size_request (
|
|
week_view->titles_canvas, -1,
|
|
PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
|
|
PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) + 5);
|
|
|
|
/* Save the sizes of various strings in the font, so we can quickly
|
|
* decide which date formats to use. */
|
|
|
|
max_day_width = 0;
|
|
max_abbr_day_width = 0;
|
|
for (day = 0; day < 7; day++) {
|
|
name = e_get_weekday_name (day + 1, FALSE);
|
|
day_width = get_string_width (layout, name);
|
|
week_view->day_widths[day] = day_width;
|
|
max_day_width = MAX (max_day_width, day_width);
|
|
|
|
name = e_get_weekday_name (day + 1, TRUE);
|
|
day_width = get_string_width (layout, name);
|
|
week_view->abbr_day_widths[day] = day_width;
|
|
max_abbr_day_width = MAX (max_abbr_day_width, day_width);
|
|
}
|
|
|
|
max_month_width = 0;
|
|
max_abbr_month_width = 0;
|
|
for (month = 0; month < 12; month++) {
|
|
name = e_get_month_name (month + 1, FALSE);
|
|
month_width = get_string_width (layout, name);
|
|
week_view->month_widths[month] = month_width;
|
|
max_month_width = MAX (max_month_width, month_width);
|
|
|
|
name = e_get_month_name (month + 1, TRUE);
|
|
month_width = get_string_width (layout, name);
|
|
week_view->abbr_month_widths[month] = month_width;
|
|
max_abbr_month_width = MAX (max_abbr_month_width, month_width);
|
|
}
|
|
|
|
week_view->space_width = get_string_width (layout, " ");
|
|
week_view->colon_width = get_string_width (layout, ":");
|
|
week_view->slash_width = get_string_width (layout, "/");
|
|
week_view->digit_width = get_digit_width (layout);
|
|
if (week_view->small_font_desc) {
|
|
pango_layout_set_font_description (layout, week_view->small_font_desc);
|
|
week_view->small_digit_width = get_digit_width (layout);
|
|
pango_layout_set_font_description (layout, style->font_desc);
|
|
}
|
|
week_view->max_day_width = max_day_width;
|
|
week_view->max_abbr_day_width = max_abbr_day_width;
|
|
week_view->max_month_width = max_month_width;
|
|
week_view->max_abbr_month_width = max_abbr_month_width;
|
|
|
|
week_view->am_string_width = get_string_width (layout,
|
|
week_view->am_string);
|
|
week_view->pm_string_width = get_string_width (layout,
|
|
week_view->pm_string);
|
|
|
|
g_object_unref (layout);
|
|
pango_font_metrics_unref (font_metrics);
|
|
}
|
|
|
|
/* This recalculates the sizes of each column. */
|
|
static void
|
|
e_week_view_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
EWeekView *week_view;
|
|
GtkAllocation canvas_allocation;
|
|
gdouble old_x2, old_y2, new_x2, new_y2;
|
|
|
|
week_view = E_WEEK_VIEW (widget);
|
|
|
|
(*GTK_WIDGET_CLASS (e_week_view_parent_class)->size_allocate) (widget, allocation);
|
|
|
|
e_week_view_recalc_cell_sizes (week_view);
|
|
|
|
/* Set the scroll region of the top canvas to its allocated size. */
|
|
gnome_canvas_get_scroll_region (
|
|
GNOME_CANVAS (week_view->titles_canvas),
|
|
NULL, NULL, &old_x2, &old_y2);
|
|
gtk_widget_get_allocation (
|
|
week_view->titles_canvas, &canvas_allocation);
|
|
new_x2 = canvas_allocation.width - 1;
|
|
new_y2 = canvas_allocation.height - 1;
|
|
if (old_x2 != new_x2 || old_y2 != new_y2)
|
|
gnome_canvas_set_scroll_region (
|
|
GNOME_CANVAS (week_view->titles_canvas),
|
|
0, 0, new_x2, new_y2);
|
|
|
|
/* Set the scroll region of the main canvas to its allocated width,
|
|
* but with the height depending on the number of rows needed. */
|
|
gnome_canvas_get_scroll_region (
|
|
GNOME_CANVAS (week_view->main_canvas),
|
|
NULL, NULL, &old_x2, &old_y2);
|
|
gtk_widget_get_allocation (
|
|
week_view->main_canvas, &canvas_allocation);
|
|
new_x2 = canvas_allocation.width - 1;
|
|
new_y2 = canvas_allocation.height - 1;
|
|
if (old_x2 != new_x2 || old_y2 != new_y2)
|
|
gnome_canvas_set_scroll_region (
|
|
GNOME_CANVAS (week_view->main_canvas),
|
|
0, 0, new_x2, new_y2);
|
|
|
|
/* Flag that we need to reshape the events. */
|
|
if (old_x2 != new_x2 || old_y2 != new_y2) {
|
|
week_view->events_need_reshape = TRUE;
|
|
e_week_view_check_layout (week_view);
|
|
}
|
|
}
|
|
|
|
static void
|
|
e_week_view_recalc_cell_sizes (EWeekView *week_view)
|
|
{
|
|
gfloat canvas_width, canvas_height, offset;
|
|
gint row, col;
|
|
GtkAllocation allocation;
|
|
GtkWidget *widget;
|
|
GtkStyle *style;
|
|
gint width, height, time_width;
|
|
PangoFontDescription *font_desc;
|
|
PangoContext *pango_context;
|
|
PangoFontMetrics *font_metrics;
|
|
|
|
if (week_view->multi_week_view) {
|
|
week_view->rows = week_view->weeks_shown * 2;
|
|
week_view->columns = week_view->compress_weekend ? 6 : 7;
|
|
} else {
|
|
week_view->rows = 6;
|
|
week_view->columns = 2;
|
|
}
|
|
|
|
gtk_widget_get_allocation (week_view->main_canvas, &allocation);
|
|
|
|
/* Calculate the column sizes, using floating point so that pixels
|
|
* get divided evenly. Note that we use one more element than the
|
|
* number of columns, to make it easy to get the column widths.
|
|
* We also add one to the width so that the right border of the last
|
|
* column is off the edge of the displayed area. */
|
|
canvas_width = allocation.width + 1;
|
|
canvas_width /= week_view->columns;
|
|
offset = 0;
|
|
for (col = 0; col <= week_view->columns; col++) {
|
|
week_view->col_offsets[col] = floor (offset + 0.5);
|
|
offset += canvas_width;
|
|
}
|
|
|
|
/* Calculate the cell widths based on the offsets. */
|
|
for (col = 0; col < week_view->columns; col++) {
|
|
week_view->col_widths[col] = week_view->col_offsets[col + 1]
|
|
- week_view->col_offsets[col];
|
|
}
|
|
|
|
/* Now do the same for the row heights. */
|
|
canvas_height = allocation.height + 1;
|
|
canvas_height /= week_view->rows;
|
|
offset = 0;
|
|
for (row = 0; row <= week_view->rows; row++) {
|
|
week_view->row_offsets[row] = floor (offset + 0.5);
|
|
offset += canvas_height;
|
|
}
|
|
|
|
/* Calculate the cell heights based on the offsets. */
|
|
for (row = 0; row < week_view->rows; row++) {
|
|
week_view->row_heights[row] = week_view->row_offsets[row + 1]
|
|
- week_view->row_offsets[row];
|
|
}
|
|
|
|
/* If the font hasn't been set yet just return. */
|
|
widget = GTK_WIDGET (week_view);
|
|
style = gtk_widget_get_style (widget);
|
|
if (!style)
|
|
return;
|
|
font_desc = style->font_desc;
|
|
if (!font_desc)
|
|
return;
|
|
|
|
pango_context = gtk_widget_get_pango_context (widget);
|
|
font_metrics = pango_context_get_metrics (pango_context, font_desc,
|
|
pango_context_get_language (pango_context));
|
|
|
|
/* Calculate the number of rows of events in each cell, for the large
|
|
* cells and the compressed weekend cells. */
|
|
if (week_view->multi_week_view) {
|
|
week_view->events_y_offset = E_WEEK_VIEW_DATE_T_PAD +
|
|
+ PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
|
|
+ PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
|
|
+ E_WEEK_VIEW_DATE_B_PAD;
|
|
} else {
|
|
week_view->events_y_offset = E_WEEK_VIEW_DATE_T_PAD
|
|
+ PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
|
|
+ PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
|
|
+ E_WEEK_VIEW_DATE_LINE_T_PAD + 1
|
|
+ E_WEEK_VIEW_DATE_LINE_B_PAD;
|
|
}
|
|
|
|
height = week_view->row_heights[0];
|
|
week_view->rows_per_cell = (height * 2 - week_view->events_y_offset)
|
|
/ (week_view->row_height + E_WEEK_VIEW_EVENT_Y_SPACING);
|
|
week_view->rows_per_cell = MIN (week_view->rows_per_cell,
|
|
E_WEEK_VIEW_MAX_ROWS_PER_CELL);
|
|
|
|
week_view->rows_per_compressed_cell =
|
|
(height - week_view->events_y_offset)
|
|
/ (week_view->row_height + E_WEEK_VIEW_EVENT_Y_SPACING);
|
|
week_view->rows_per_compressed_cell = MIN (week_view->rows_per_compressed_cell,
|
|
E_WEEK_VIEW_MAX_ROWS_PER_CELL);
|
|
|
|
/* Determine which time format to use, based on the width of the cells.
|
|
* We only allow the time to take up about half of the width. */
|
|
width = week_view->col_widths[0];
|
|
|
|
time_width = e_week_view_get_time_string_width (week_view);
|
|
|
|
week_view->time_format = E_WEEK_VIEW_TIME_NONE;
|
|
if (week_view->use_small_font && week_view->small_font_desc) {
|
|
if (week_view->show_event_end_times
|
|
&& width / 2 > time_width * 2 + E_WEEK_VIEW_EVENT_TIME_SPACING)
|
|
week_view->time_format = E_WEEK_VIEW_TIME_BOTH_SMALL_MIN;
|
|
else if (width / 2 > time_width)
|
|
week_view->time_format = E_WEEK_VIEW_TIME_START_SMALL_MIN;
|
|
} else {
|
|
if (week_view->show_event_end_times
|
|
&& width / 2 > time_width * 2 + E_WEEK_VIEW_EVENT_TIME_SPACING)
|
|
week_view->time_format = E_WEEK_VIEW_TIME_BOTH;
|
|
else if (width / 2 > time_width)
|
|
week_view->time_format = E_WEEK_VIEW_TIME_START;
|
|
}
|
|
|
|
pango_font_metrics_unref (font_metrics);
|
|
}
|
|
|
|
static gint
|
|
e_week_view_focus_in (GtkWidget *widget,
|
|
GdkEventFocus *event)
|
|
{
|
|
EWeekView *week_view;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
week_view = E_WEEK_VIEW (widget);
|
|
|
|
/* XXX Can't access flags directly anymore, but is it really needed?
|
|
* If so, could we call gtk_widget_send_focus_change() instead? */
|
|
#if 0
|
|
GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
|
|
#endif
|
|
|
|
if (E_CALENDAR_VIEW (week_view)->in_focus && week_view->requires_update) {
|
|
time_t my_start = 0, my_end = 0, model_start = 0, model_end = 0;
|
|
|
|
week_view->requires_update = FALSE;
|
|
|
|
e_cal_model_get_time_range (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), &model_start, &model_end);
|
|
|
|
if (e_calendar_view_get_visible_time_range (E_CALENDAR_VIEW (week_view), &my_start, &my_end) &&
|
|
model_start == my_start && model_end == my_end) {
|
|
/* update only when the same time range is set in a view and in a model;
|
|
* otherwise time range change invokes also query update */
|
|
e_week_view_update_query (week_view);
|
|
}
|
|
}
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
e_week_view_focus_out (GtkWidget *widget,
|
|
GdkEventFocus *event)
|
|
{
|
|
EWeekView *week_view;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
week_view = E_WEEK_VIEW (widget);
|
|
|
|
/* XXX Can't access flags directly anymore, but is it really needed?
|
|
* If so, could we call gtk_widget_send_focus_change() instead? */
|
|
#if 0
|
|
GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
|
|
#endif
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* e_week_view_get_next_tab_event
|
|
* @week_view: the week_view widget operate on
|
|
* @direction: GTK_DIR_TAB_BACKWARD or GTK_DIR_TAB_FORWARD.
|
|
* @current_event_num and @current_span_num: current status.
|
|
* @next_event_num: the event number focus should go next.
|
|
* -1 indicates focus should go to week_view widget.
|
|
* @next_span_num: always return 0.
|
|
**/
|
|
static gboolean
|
|
e_week_view_get_next_tab_event (EWeekView *week_view,
|
|
GtkDirectionType direction,
|
|
gint current_event_num,
|
|
gint current_span_num,
|
|
gint *next_event_num,
|
|
gint *next_span_num)
|
|
{
|
|
gint event_num;
|
|
|
|
g_return_val_if_fail (week_view != NULL, FALSE);
|
|
g_return_val_if_fail (next_event_num != NULL, FALSE);
|
|
g_return_val_if_fail (next_span_num != NULL, FALSE);
|
|
|
|
if (week_view->events->len <= 0)
|
|
return FALSE;
|
|
|
|
/* we only tab through events not spans */
|
|
*next_span_num = 0;
|
|
|
|
switch (direction) {
|
|
case GTK_DIR_TAB_BACKWARD:
|
|
event_num = current_event_num - 1;
|
|
break;
|
|
case GTK_DIR_TAB_FORWARD:
|
|
event_num = current_event_num + 1;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
if (event_num == -1)
|
|
/* backward, out of event range, go to week view widget
|
|
*/
|
|
*next_event_num = -1;
|
|
else if (event_num < -1)
|
|
/* backward from week_view, go to the last event
|
|
*/
|
|
*next_event_num = week_view->events->len - 1;
|
|
else if (event_num >= week_view->events->len)
|
|
/* forward, out of event range, go to week view widget
|
|
*/
|
|
*next_event_num = -1;
|
|
else
|
|
*next_event_num = event_num;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_focus (GtkWidget *widget,
|
|
GtkDirectionType direction)
|
|
{
|
|
EWeekView *week_view;
|
|
gint new_event_num;
|
|
gint new_span_num;
|
|
gint event_loop;
|
|
gboolean editable = FALSE;
|
|
static gint last_focus_event_num = -1, last_focus_span_num = -1;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
|
|
|
|
week_view = E_WEEK_VIEW (widget);
|
|
|
|
e_week_view_check_layout (week_view);
|
|
|
|
if (week_view->focused_jump_button == E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS) {
|
|
last_focus_event_num = week_view->editing_event_num;
|
|
last_focus_span_num = week_view->editing_span_num;
|
|
}
|
|
|
|
/* if there is not event, just grab week_view */
|
|
if (week_view->events->len == 0) {
|
|
gtk_widget_grab_focus (widget);
|
|
return TRUE;
|
|
}
|
|
|
|
for (event_loop = 0; event_loop < week_view->events->len;
|
|
++event_loop) {
|
|
if (!e_week_view_get_next_tab_event (week_view, direction,
|
|
last_focus_event_num,
|
|
last_focus_span_num,
|
|
&new_event_num,
|
|
&new_span_num))
|
|
return FALSE;
|
|
|
|
if (new_event_num == -1) {
|
|
/* focus should go to week_view widget
|
|
*/
|
|
gtk_widget_grab_focus (widget);
|
|
return TRUE;
|
|
}
|
|
|
|
editable = e_week_view_start_editing_event (week_view,
|
|
new_event_num,
|
|
new_span_num,
|
|
NULL);
|
|
last_focus_event_num = new_event_num;
|
|
last_focus_span_num = new_span_num;
|
|
|
|
if (editable)
|
|
break;
|
|
else {
|
|
/* check if we should go to the jump button */
|
|
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
gint current_day;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, new_event_num))
|
|
break;
|
|
|
|
event = &g_array_index (week_view->events,
|
|
EWeekViewEvent,
|
|
new_event_num);
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + new_span_num))
|
|
break;
|
|
|
|
span = &g_array_index (week_view->spans,
|
|
EWeekViewEventSpan,
|
|
event->spans_index + new_span_num);
|
|
current_day = span->start_day;
|
|
|
|
if ((week_view->focused_jump_button != current_day) &&
|
|
e_week_view_is_jump_button_visible (week_view, current_day)) {
|
|
|
|
/* focus go to the jump button */
|
|
e_week_view_stop_editing_event (week_view);
|
|
gnome_canvas_item_grab_focus (week_view->jump_buttons[current_day]);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return editable;
|
|
}
|
|
|
|
/* Returns the currently-selected event, or NULL if none */
|
|
static GList *
|
|
e_week_view_get_selected_events (ECalendarView *cal_view)
|
|
{
|
|
EWeekViewEvent *event = NULL;
|
|
GList *list = NULL;
|
|
EWeekView *week_view = (EWeekView *) cal_view;
|
|
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), NULL);
|
|
|
|
if (week_view->editing_event_num != -1) {
|
|
if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
|
|
return NULL;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent,
|
|
week_view->editing_event_num);
|
|
} else if (week_view->popup_event_num != -1) {
|
|
if (!is_array_index_in_bounds (week_view->events, week_view->popup_event_num))
|
|
return NULL;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent,
|
|
week_view->popup_event_num);
|
|
}
|
|
|
|
if (event)
|
|
list = g_list_prepend (list, event);
|
|
|
|
return list;
|
|
}
|
|
|
|
/* Restarts a query for the week view */
|
|
static void
|
|
e_week_view_update_query (EWeekView *week_view)
|
|
{
|
|
gint rows, r;
|
|
|
|
if (!E_CALENDAR_VIEW (week_view)->in_focus) {
|
|
e_week_view_free_events (week_view);
|
|
week_view->requires_update = TRUE;
|
|
return;
|
|
}
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
e_week_view_free_events (week_view);
|
|
e_week_view_queue_layout (week_view);
|
|
|
|
rows = e_table_model_row_count (E_TABLE_MODEL (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view))));
|
|
for (r = 0; r < rows; r++) {
|
|
ECalModelComponent *comp_data;
|
|
|
|
comp_data = e_cal_model_get_component_at (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), r);
|
|
if (comp_data == NULL) {
|
|
g_warning ("comp_data is NULL\n");
|
|
continue;
|
|
}
|
|
week_view_process_component (week_view, comp_data);
|
|
}
|
|
}
|
|
|
|
/* This sets the selected time range. The EWeekView will show the corresponding
|
|
* month and the days between start_time and end_time will be selected.
|
|
* To select a single day, use the same value for start_time & end_time. */
|
|
static void
|
|
e_week_view_set_selected_time_range (ECalendarView *cal_view,
|
|
time_t start_time,
|
|
time_t end_time)
|
|
{
|
|
GDate date, end_date;
|
|
gint num_days;
|
|
gboolean update_adjustment_value = FALSE;
|
|
EWeekView *week_view = E_WEEK_VIEW (cal_view);
|
|
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
if (!g_date_valid (&week_view->base_date)) {
|
|
/* This view has not been initialized/shown yet, thus skip this. */
|
|
return;
|
|
}
|
|
|
|
time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
|
|
/* Set the selection to the given days. */
|
|
week_view->selection_start_day = g_date_get_julian (&date)
|
|
- g_date_get_julian (&week_view->base_date);
|
|
if (end_time == start_time
|
|
|| end_time <= time_add_day_with_zone (start_time, 1,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view))))
|
|
week_view->selection_end_day = week_view->selection_start_day;
|
|
else {
|
|
time_to_gdate_with_zone (&end_date, end_time - 60, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
week_view->selection_end_day = g_date_get_julian (&end_date)
|
|
- g_date_get_julian (&week_view->base_date);
|
|
}
|
|
|
|
/* Make sure the selection is valid. */
|
|
num_days = week_view->multi_week_view ? week_view->weeks_shown * 7 : 7;
|
|
num_days--;
|
|
week_view->selection_start_day = CLAMP (week_view->selection_start_day,
|
|
0, num_days);
|
|
week_view->selection_end_day = CLAMP (week_view->selection_end_day,
|
|
week_view->selection_start_day,
|
|
num_days);
|
|
|
|
/* Reset the adjustment value to 0 if the base address has changed.
|
|
* Note that we do this after updating first_day_shown so that our
|
|
* signal handler will not try to reload the events. */
|
|
if (update_adjustment_value) {
|
|
GtkRange *range;
|
|
GtkAdjustment *adjustment;
|
|
|
|
range = GTK_RANGE (week_view->vscrollbar);
|
|
adjustment = gtk_range_get_adjustment (range);
|
|
gtk_adjustment_set_value (adjustment, 0);
|
|
}
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
|
|
void
|
|
e_week_view_set_selected_time_range_visible (EWeekView *week_view,
|
|
time_t start_time,
|
|
time_t end_time)
|
|
{
|
|
GDate date, end_date;
|
|
gint num_days;
|
|
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
|
|
/* Set the selection to the given days. */
|
|
week_view->selection_start_day = g_date_get_julian (&date)
|
|
- g_date_get_julian (&week_view->first_day_shown);
|
|
if (end_time == start_time
|
|
|| end_time <= time_add_day_with_zone (start_time, 1,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view))))
|
|
week_view->selection_end_day = week_view->selection_start_day;
|
|
else {
|
|
time_to_gdate_with_zone (&end_date, end_time - 60, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
week_view->selection_end_day = g_date_get_julian (&end_date)
|
|
- g_date_get_julian (&week_view->first_day_shown);
|
|
}
|
|
|
|
/* Make sure the selection is valid. */
|
|
num_days = week_view->multi_week_view ? week_view->weeks_shown * 7 : 7;
|
|
num_days--;
|
|
week_view->selection_start_day = CLAMP (week_view->selection_start_day,
|
|
0, num_days);
|
|
week_view->selection_end_day = CLAMP (week_view->selection_end_day,
|
|
week_view->selection_start_day,
|
|
num_days);
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
|
|
/* Returns the selected time range. */
|
|
static gboolean
|
|
e_week_view_get_selected_time_range (ECalendarView *cal_view,
|
|
time_t *start_time,
|
|
time_t *end_time)
|
|
{
|
|
gint start_day, end_day;
|
|
EWeekView *week_view = E_WEEK_VIEW (cal_view);
|
|
|
|
start_day = week_view->selection_start_day;
|
|
end_day = week_view->selection_end_day;
|
|
|
|
if (start_day == -1) {
|
|
start_day = 0;
|
|
end_day = 0;
|
|
}
|
|
|
|
if (start_time)
|
|
*start_time = week_view->day_starts[start_day];
|
|
|
|
if (end_time)
|
|
*end_time = week_view->day_starts[end_day + 1];
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Gets the visible time range. Returns FALSE if no time range has been set. */
|
|
static gboolean
|
|
e_week_view_get_visible_time_range (ECalendarView *cal_view,
|
|
time_t *start_time,
|
|
time_t *end_time)
|
|
{
|
|
gint num_days;
|
|
EWeekView *week_view = E_WEEK_VIEW (cal_view);
|
|
|
|
/* If we don't have a valid date set yet, return FALSE. */
|
|
if (!g_date_valid (&week_view->first_day_shown))
|
|
return FALSE;
|
|
|
|
num_days = week_view->multi_week_view ? week_view->weeks_shown * 7 : 7;
|
|
*start_time = week_view->day_starts[0];
|
|
*end_time = week_view->day_starts[num_days];
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Note that the returned date may be invalid if no date has been set yet. */
|
|
void
|
|
e_week_view_get_first_day_shown (EWeekView *week_view,
|
|
GDate *date)
|
|
{
|
|
*date = week_view->first_day_shown;
|
|
}
|
|
|
|
/* This sets the first day shown in the view. It will be rounded down to the
|
|
* nearest week. */
|
|
void
|
|
e_week_view_set_first_day_shown (EWeekView *week_view,
|
|
GDate *date)
|
|
{
|
|
GDate base_date;
|
|
gint weekday, day_offset, num_days;
|
|
gboolean update_adjustment_value = FALSE;
|
|
guint32 old_selection_start_julian = 0, old_selection_end_julian = 0;
|
|
struct icaltimetype start_tt = icaltime_null_time ();
|
|
time_t start_time;
|
|
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
/* Calculate the old selection range. */
|
|
if (week_view->selection_start_day != -1) {
|
|
old_selection_start_julian =
|
|
g_date_get_julian (&week_view->base_date)
|
|
+ week_view->selection_start_day;
|
|
old_selection_end_julian =
|
|
g_date_get_julian (&week_view->base_date)
|
|
+ week_view->selection_end_day;
|
|
}
|
|
|
|
/* Calculate the weekday of the given date, 0 = Mon. */
|
|
weekday = g_date_get_weekday (date) - 1;
|
|
|
|
/* Convert it to an offset from the start of the display. */
|
|
day_offset = (weekday + 7 - week_view->display_start_day) % 7;
|
|
|
|
/* Calculate the base date, i.e. the first day shown when the
|
|
* scrollbar adjustment value is 0. */
|
|
base_date = *date;
|
|
g_date_subtract_days (&base_date, day_offset);
|
|
|
|
/* See if we need to update the base date. */
|
|
if (!g_date_valid (&week_view->base_date)
|
|
|| g_date_compare (&week_view->base_date, &base_date)) {
|
|
week_view->base_date = base_date;
|
|
update_adjustment_value = TRUE;
|
|
}
|
|
|
|
/* See if we need to update the first day shown. */
|
|
if (!g_date_valid (&week_view->first_day_shown)
|
|
|| g_date_compare (&week_view->first_day_shown, &base_date)) {
|
|
week_view->first_day_shown = base_date;
|
|
|
|
start_tt.year = g_date_get_year (&base_date);
|
|
start_tt.month = g_date_get_month (&base_date);
|
|
start_tt.day = g_date_get_day (&base_date);
|
|
|
|
start_time = icaltime_as_timet_with_zone (start_tt,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
|
|
e_week_view_recalc_day_starts (week_view, start_time);
|
|
e_week_view_update_query (week_view);
|
|
}
|
|
|
|
/* Try to keep the previous selection, but if it is no longer shown
|
|
* just select the first day. */
|
|
if (week_view->selection_start_day != -1) {
|
|
week_view->selection_start_day = old_selection_start_julian
|
|
- g_date_get_julian (&base_date);
|
|
week_view->selection_end_day = old_selection_end_julian
|
|
- g_date_get_julian (&base_date);
|
|
|
|
/* Make sure the selection is valid. */
|
|
num_days = week_view->multi_week_view
|
|
? week_view->weeks_shown * 7 : 7;
|
|
num_days--;
|
|
week_view->selection_start_day =
|
|
CLAMP (week_view->selection_start_day, 0, num_days);
|
|
week_view->selection_end_day =
|
|
CLAMP (week_view->selection_end_day,
|
|
week_view->selection_start_day,
|
|
num_days);
|
|
}
|
|
|
|
/* Reset the adjustment value to 0 if the base address has changed.
|
|
* Note that we do this after updating first_day_shown so that our
|
|
* signal handler will not try to reload the events. */
|
|
if (update_adjustment_value) {
|
|
GtkRange *range;
|
|
GtkAdjustment *adjustment;
|
|
|
|
range = GTK_RANGE (week_view->vscrollbar);
|
|
adjustment = gtk_range_get_adjustment (range);
|
|
gtk_adjustment_set_value (adjustment, 0);
|
|
}
|
|
|
|
e_week_view_update_query (week_view);
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
|
|
/* Recalculates the time_t corresponding to the start of each day. */
|
|
static void
|
|
e_week_view_recalc_day_starts (EWeekView *week_view,
|
|
time_t lower)
|
|
{
|
|
gint num_days, day;
|
|
time_t tmp_time;
|
|
|
|
num_days = week_view->multi_week_view ? week_view->weeks_shown * 7 : 7;
|
|
|
|
tmp_time = lower;
|
|
week_view->day_starts[0] = tmp_time;
|
|
for (day = 1; day <= num_days; day++) {
|
|
tmp_time = time_add_day_with_zone (tmp_time, 1,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
week_view->day_starts[day] = tmp_time;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
e_week_view_get_multi_week_view (EWeekView *week_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
|
|
|
|
return week_view->multi_week_view;
|
|
}
|
|
|
|
void
|
|
e_week_view_set_multi_week_view (EWeekView *week_view,
|
|
gboolean multi_week_view)
|
|
{
|
|
GtkRange *range;
|
|
GtkAdjustment *adjustment;
|
|
gint page_increment, page_size;
|
|
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
if (week_view->multi_week_view == multi_week_view)
|
|
return;
|
|
|
|
week_view->multi_week_view = multi_week_view;
|
|
|
|
if (multi_week_view) {
|
|
gtk_widget_show (week_view->titles_canvas);
|
|
week_view->month_scroll_by_week = calendar_config_get_month_scroll_by_week ();
|
|
|
|
calendar_config_add_notification_month_scroll_by_week (month_scrol_by_week_changed_cb, week_view);
|
|
|
|
if (week_view->month_scroll_by_week) {
|
|
page_increment = 1;
|
|
page_size = 1;
|
|
} else {
|
|
page_increment = 4;
|
|
page_size = 5;
|
|
}
|
|
} else {
|
|
gtk_widget_hide (week_view->titles_canvas);
|
|
page_increment = page_size = 1;
|
|
|
|
if (week_view->scroll_by_week_notif_id) {
|
|
calendar_config_remove_notification (month_scrol_by_week_changed_cb, week_view);
|
|
week_view->scroll_by_week_notif_id = 0;
|
|
}
|
|
}
|
|
|
|
range = GTK_RANGE (week_view->vscrollbar);
|
|
adjustment = gtk_range_get_adjustment (range);
|
|
gtk_adjustment_set_page_increment (adjustment, page_increment);
|
|
gtk_adjustment_set_page_size (adjustment, page_size);
|
|
|
|
e_week_view_recalc_display_start_day (week_view);
|
|
e_week_view_recalc_cell_sizes (week_view);
|
|
|
|
if (g_date_valid (&week_view->first_day_shown))
|
|
e_week_view_set_first_day_shown (week_view,
|
|
&week_view->first_day_shown);
|
|
}
|
|
|
|
gboolean
|
|
e_week_view_get_update_base_date (EWeekView *week_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
|
|
|
|
return week_view->update_base_date;
|
|
}
|
|
|
|
void
|
|
e_week_view_set_update_base_date (EWeekView *week_view,
|
|
gboolean update_base_date)
|
|
{
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
week_view->update_base_date = update_base_date;
|
|
}
|
|
|
|
gint
|
|
e_week_view_get_weeks_shown (EWeekView *week_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), 1);
|
|
|
|
return week_view->weeks_shown;
|
|
}
|
|
|
|
void
|
|
e_week_view_set_weeks_shown (EWeekView *week_view,
|
|
gint weeks_shown)
|
|
{
|
|
GtkRange *range;
|
|
GtkAdjustment *adjustment;
|
|
gint page_increment, page_size;
|
|
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
weeks_shown = MIN (weeks_shown, E_WEEK_VIEW_MAX_WEEKS);
|
|
|
|
if (week_view->weeks_shown == weeks_shown)
|
|
return;
|
|
|
|
week_view->weeks_shown = weeks_shown;
|
|
|
|
if (week_view->multi_week_view) {
|
|
if (week_view->month_scroll_by_week) {
|
|
page_increment = 1;
|
|
page_size = 1;
|
|
} else {
|
|
page_increment = 4;
|
|
page_size = 5;
|
|
}
|
|
|
|
range = GTK_RANGE (week_view->vscrollbar);
|
|
adjustment = gtk_range_get_adjustment (range);
|
|
gtk_adjustment_set_page_increment (adjustment, page_increment);
|
|
gtk_adjustment_set_page_size (adjustment, page_size);
|
|
|
|
e_week_view_recalc_cell_sizes (week_view);
|
|
|
|
if (g_date_valid (&week_view->first_day_shown))
|
|
e_week_view_set_first_day_shown (week_view, &week_view->first_day_shown);
|
|
|
|
e_week_view_update_query (week_view);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
e_week_view_get_compress_weekend (EWeekView *week_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
|
|
|
|
return week_view->compress_weekend;
|
|
}
|
|
|
|
void
|
|
e_week_view_set_compress_weekend (EWeekView *week_view,
|
|
gboolean compress_weekend)
|
|
{
|
|
gboolean need_reload = FALSE;
|
|
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
if ((week_view->compress_weekend ? 1 : 0) == (compress_weekend ? 1 : 0))
|
|
return;
|
|
|
|
week_view->compress_weekend = compress_weekend;
|
|
|
|
/* The option only affects the month view. */
|
|
if (!week_view->multi_week_view)
|
|
return;
|
|
|
|
e_week_view_recalc_cell_sizes (week_view);
|
|
|
|
need_reload = e_week_view_recalc_display_start_day (week_view);
|
|
|
|
/* If the display_start_day has changed we need to recalculate the
|
|
* date range shown and reload all events, otherwise we only need to
|
|
* do a reshape. */
|
|
if (need_reload) {
|
|
/* Recalculate the days shown and reload if necessary. */
|
|
if (g_date_valid (&week_view->first_day_shown))
|
|
e_week_view_set_first_day_shown (week_view, &week_view->first_day_shown);
|
|
} else {
|
|
week_view->events_need_reshape = TRUE;
|
|
e_week_view_check_layout (week_view);
|
|
}
|
|
|
|
gtk_widget_queue_draw (week_view->titles_canvas);
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
|
|
g_object_notify (G_OBJECT (week_view), "compress-weekend");
|
|
}
|
|
|
|
/* Whether we display event end times. */
|
|
gboolean
|
|
e_week_view_get_show_event_end_times (EWeekView *week_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), TRUE);
|
|
|
|
return week_view->show_event_end_times;
|
|
}
|
|
|
|
void
|
|
e_week_view_set_show_event_end_times (EWeekView *week_view,
|
|
gboolean show_event_end_times)
|
|
{
|
|
g_return_if_fail (E_IS_WEEK_VIEW (week_view));
|
|
|
|
if ((week_view->show_event_end_times ? 1 : 0) == (show_event_end_times ? 1 : 0))
|
|
return;
|
|
|
|
week_view->show_event_end_times = show_event_end_times;
|
|
e_week_view_recalc_cell_sizes (week_view);
|
|
week_view->events_need_reshape = TRUE;
|
|
e_week_view_check_layout (week_view);
|
|
|
|
gtk_widget_queue_draw (week_view->titles_canvas);
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
|
|
g_object_notify (G_OBJECT (week_view), "show-event-end-times");
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_recalc_display_start_day (EWeekView *week_view)
|
|
{
|
|
ECalModel *model;
|
|
gint week_start_day;
|
|
gint display_start_day;
|
|
|
|
model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
|
|
week_start_day = e_cal_model_get_week_start_day (model);
|
|
|
|
/* The display start day defaults to week_start_day, but we have
|
|
* to use Saturday if the weekend is compressed and week_start_day
|
|
* is Sunday. */
|
|
display_start_day = week_start_day;
|
|
|
|
if (display_start_day == 6
|
|
&& (!week_view->multi_week_view || week_view->compress_weekend))
|
|
display_start_day = 5;
|
|
|
|
if (week_view->display_start_day != display_start_day) {
|
|
week_view->display_start_day = display_start_day;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Checks if the users participation status is NEEDS-ACTION and shows the summary as bold text */
|
|
static void
|
|
set_text_as_bold (EWeekViewEvent *event,
|
|
EWeekViewEventSpan *span,
|
|
ESourceRegistry *registry)
|
|
{
|
|
ECalComponent *comp;
|
|
GSList *attendees = NULL, *l;
|
|
gchar *address;
|
|
ECalComponentAttendee *at = NULL;
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return;
|
|
|
|
comp = e_cal_component_new ();
|
|
e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
|
|
address = itip_get_comp_attendee (
|
|
registry, comp, event->comp_data->client);
|
|
e_cal_component_get_attendee_list (comp, &attendees);
|
|
for (l = attendees; l; l = l->next) {
|
|
ECalComponentAttendee *attendee = l->data;
|
|
|
|
if ((g_str_equal (itip_strip_mailto (attendee->value), address))
|
|
|| (attendee->sentby && g_str_equal (itip_strip_mailto (attendee->sentby), address))) {
|
|
at = attendee;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* The attendee has not yet accepted the meeting, display the summary as bolded.
|
|
* If the attendee is not present, it might have come through a mailing list.
|
|
* In that case, we never show the meeting as bold even if it is unaccepted. */
|
|
if (at && (at->status == ICAL_PARTSTAT_NEEDSACTION))
|
|
gnome_canvas_item_set (span->text_item, "bold", TRUE, NULL);
|
|
|
|
e_cal_component_free_attendee_list (attendees);
|
|
g_free (address);
|
|
g_object_unref (comp);
|
|
}
|
|
|
|
/* This calls a given function for each event instance that matches the given
|
|
* uid. Note that it is safe for the callback to remove the event (since we
|
|
* step backwards through the arrays). */
|
|
static void
|
|
e_week_view_foreach_event_with_uid (EWeekView *week_view,
|
|
const gchar *uid,
|
|
EWeekViewForeachEventCallback callback,
|
|
gpointer data)
|
|
{
|
|
EWeekViewEvent *event;
|
|
gint event_num;
|
|
|
|
for (event_num = week_view->events->len - 1;
|
|
event_num >= 0;
|
|
event_num--) {
|
|
const gchar *u;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent,
|
|
event_num);
|
|
|
|
if (!is_comp_data_valid (event))
|
|
continue;
|
|
|
|
u = icalcomponent_get_uid (event->comp_data->icalcomp);
|
|
if (u && !strcmp (uid, u)) {
|
|
if (!(*callback) (week_view, event_num, data))
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_remove_event_cb (EWeekView *week_view,
|
|
gint event_num,
|
|
gpointer data)
|
|
{
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
gint span_num;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return TRUE;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
if (!event)
|
|
return TRUE;
|
|
|
|
/* If we were editing this event, set editing_event_num to -1 so
|
|
* on_editing_stopped doesn't try to update the event. */
|
|
if (week_view->editing_event_num == event_num)
|
|
week_view->editing_event_num = -1;
|
|
|
|
if (week_view->popup_event_num == event_num)
|
|
week_view->popup_event_num = -1;
|
|
|
|
if (is_comp_data_valid (event))
|
|
g_object_unref (event->comp_data);
|
|
event->comp_data = NULL;
|
|
|
|
if (week_view->spans) {
|
|
/* We leave the span elements in the array, but set the canvas item
|
|
* pointers to NULL. */
|
|
for (span_num = 0; span_num < event->num_spans; span_num++) {
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
|
|
break;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan,
|
|
event->spans_index + span_num);
|
|
|
|
if (span->text_item) {
|
|
g_object_run_dispose (G_OBJECT (span->text_item));
|
|
span->text_item = NULL;
|
|
}
|
|
if (span->background_item) {
|
|
g_object_run_dispose (G_OBJECT (span->background_item));
|
|
span->background_item = NULL;
|
|
}
|
|
}
|
|
|
|
/* Update event_num numbers for already created spans with event_num higher than our event_num */
|
|
for (span_num = 0; span_num < week_view->spans->len; span_num++) {
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan, span_num);
|
|
|
|
if (span && span->background_item && E_IS_WEEK_VIEW_EVENT_ITEM (span->background_item)) {
|
|
EWeekViewEventItem *wveitem = E_WEEK_VIEW_EVENT_ITEM (span->background_item);
|
|
gint wveitem_event_num;
|
|
|
|
wveitem_event_num =
|
|
e_week_view_event_item_get_event_num (wveitem);
|
|
|
|
if (wveitem_event_num > event_num)
|
|
e_week_view_event_item_set_event_num (
|
|
wveitem, wveitem_event_num - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
g_array_remove_index (week_view->events, event_num);
|
|
|
|
week_view->events_need_layout = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
e_week_view_get_day_position (EWeekView *week_view,
|
|
gint day,
|
|
gint *day_x,
|
|
gint *day_y,
|
|
gint *day_w,
|
|
gint *day_h)
|
|
{
|
|
gint cell_x, cell_y, cell_h;
|
|
|
|
e_week_view_layout_get_day_position (day,
|
|
week_view->multi_week_view,
|
|
week_view->weeks_shown,
|
|
week_view->display_start_day,
|
|
week_view->compress_weekend,
|
|
&cell_x, &cell_y, &cell_h);
|
|
|
|
*day_x = week_view->col_offsets[cell_x];
|
|
*day_y = week_view->row_offsets[cell_y];
|
|
|
|
*day_w = week_view->col_widths[cell_x];
|
|
*day_h = week_view->row_heights[cell_y];
|
|
|
|
while (cell_h > 1) {
|
|
*day_h += week_view->row_heights[cell_y + 1];
|
|
cell_h--;
|
|
cell_y++;
|
|
}
|
|
}
|
|
|
|
/* Returns the bounding box for a span of an event. Usually this can easily
|
|
* be determined by the start & end days and row of the span, which are set in
|
|
* e_week_view_layout_event (). Though we need a special case for the weekends
|
|
* when they are compressed, since the span may not fit.
|
|
* The bounding box includes the entire width of the days in the view (but
|
|
* not the vertical line down the right of the last day), though the displayed
|
|
* event doesn't normally extend to the edges of the day.
|
|
* It returns FALSE if the span isn't visible. */
|
|
gboolean
|
|
e_week_view_get_span_position (EWeekView *week_view,
|
|
gint event_num,
|
|
gint span_num,
|
|
gint *span_x,
|
|
gint *span_y,
|
|
gint *span_w)
|
|
{
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
gint num_days;
|
|
gint start_x, start_y, start_w, start_h;
|
|
gint end_x, end_y, end_w, end_h;
|
|
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
|
|
g_return_val_if_fail (event_num < week_view->events->len, FALSE);
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
g_return_val_if_fail (span_num < event->num_spans, FALSE);
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
|
|
return FALSE;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan,
|
|
event->spans_index + span_num);
|
|
|
|
if (!e_week_view_layout_get_span_position (event, span,
|
|
week_view->rows_per_cell,
|
|
week_view->rows_per_compressed_cell,
|
|
week_view->display_start_day,
|
|
week_view->multi_week_view,
|
|
week_view->compress_weekend,
|
|
&num_days)) {
|
|
return FALSE;
|
|
}
|
|
|
|
e_week_view_get_day_position (week_view, span->start_day,
|
|
&start_x, &start_y, &start_w, &start_h);
|
|
*span_y = start_y + week_view->events_y_offset
|
|
+ span->row * (week_view->row_height
|
|
+ E_WEEK_VIEW_EVENT_Y_SPACING);
|
|
if (num_days == 1) {
|
|
*span_x = start_x;
|
|
*span_w = start_w - 1;
|
|
} else {
|
|
e_week_view_get_day_position (week_view,
|
|
span->start_day + num_days - 1,
|
|
&end_x, &end_y, &end_w, &end_h);
|
|
*span_x = start_x;
|
|
*span_w = end_x + end_w - start_x - 1;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
ewv_pass_gdkevent_to_etext (EWeekView *week_view,
|
|
GdkEvent *gevent)
|
|
{
|
|
g_return_val_if_fail (week_view != NULL, FALSE);
|
|
g_return_val_if_fail (gevent != NULL, FALSE);
|
|
|
|
if (week_view->editing_event_num != -1 && week_view->editing_span_num != -1) {
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
|
|
return FALSE;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, week_view->editing_event_num);
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + week_view->editing_span_num))
|
|
return FALSE;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + week_view->editing_span_num);
|
|
|
|
if (span->text_item && E_IS_TEXT (span->text_item)) {
|
|
GNOME_CANVAS_ITEM_GET_CLASS (span->text_item)->event (span->text_item, gevent);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_on_button_press (GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
EWeekView *week_view)
|
|
{
|
|
gint x, y, day;
|
|
|
|
#if 0
|
|
g_print ("In e_week_view_on_button_press\n");
|
|
if (event->type == GDK_2BUTTON_PRESS)
|
|
g_print (" is a double-click\n");
|
|
if (week_view->pressed_event_num != -1)
|
|
g_print (" item is pressed\n");
|
|
#endif
|
|
|
|
/* Convert the mouse position to a week & day. */
|
|
x = event->x;
|
|
y = event->y;
|
|
day = e_week_view_convert_position_to_day (week_view, x, y);
|
|
if (day == -1)
|
|
return FALSE;
|
|
|
|
if (ewv_pass_gdkevent_to_etext (week_view, (GdkEvent *) event))
|
|
return TRUE;
|
|
|
|
/* If an event is pressed just return. */
|
|
if (week_view->pressed_event_num != -1)
|
|
return FALSE;
|
|
|
|
if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
|
|
time_t dtstart, dtend;
|
|
|
|
e_calendar_view_get_selected_time_range ((ECalendarView *) week_view, &dtstart, &dtend);
|
|
if (dtstart < week_view->before_click_dtend && dtend > week_view->before_click_dtstart) {
|
|
e_week_view_set_selected_time_range ((ECalendarView *) week_view, week_view->before_click_dtstart, week_view->before_click_dtend);
|
|
}
|
|
e_calendar_view_new_appointment_full (E_CALENDAR_VIEW (week_view), FALSE, FALSE, calendar_config_get_prefer_meeting ());
|
|
return TRUE;
|
|
}
|
|
|
|
if (event->button == 1) {
|
|
GdkWindow *window;
|
|
|
|
/* Start the selection drag. */
|
|
if (!gtk_widget_has_focus (GTK_WIDGET (week_view)) && !gtk_widget_has_focus (GTK_WIDGET (week_view->main_canvas)))
|
|
gtk_widget_grab_focus (GTK_WIDGET (week_view));
|
|
|
|
window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
|
|
|
|
if (gdk_pointer_grab (window, FALSE,
|
|
GDK_POINTER_MOTION_MASK
|
|
| GDK_BUTTON_RELEASE_MASK,
|
|
NULL, NULL, event->time) == 0) {
|
|
if (event->time - week_view->bc_event_time > 250)
|
|
e_calendar_view_get_selected_time_range ((ECalendarView *) week_view, &week_view->before_click_dtstart, &week_view->before_click_dtend);
|
|
week_view->bc_event_time = event->time;
|
|
week_view->selection_start_day = day;
|
|
week_view->selection_end_day = day;
|
|
week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_END;
|
|
g_signal_emit_by_name (week_view, "selected_time_changed");
|
|
|
|
/* FIXME: Optimise? */
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
} else if (event->button == 3) {
|
|
if (!gtk_widget_has_focus (GTK_WIDGET (week_view)))
|
|
gtk_widget_grab_focus (GTK_WIDGET (week_view));
|
|
|
|
if (day < week_view->selection_start_day || day > week_view->selection_end_day) {
|
|
week_view->selection_start_day = day;
|
|
week_view->selection_end_day = day;
|
|
week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
|
|
|
|
/* FIXME: Optimise? */
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
|
|
e_week_view_show_popup_menu (week_view, event, -1);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_on_button_release (GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
EWeekView *week_view)
|
|
{
|
|
#if 0
|
|
g_print ("In e_week_view_on_button_release\n");
|
|
#endif
|
|
|
|
if (week_view->selection_drag_pos != E_WEEK_VIEW_DRAG_NONE) {
|
|
week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
|
|
gdk_pointer_ungrab (event->time);
|
|
} else {
|
|
ewv_pass_gdkevent_to_etext (week_view, (GdkEvent *) event);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_on_scroll (GtkWidget *widget,
|
|
GdkEventScroll *scroll,
|
|
EWeekView *week_view)
|
|
{
|
|
GtkRange *range;
|
|
GtkAdjustment *adjustment;
|
|
gdouble page_increment;
|
|
gdouble new_value;
|
|
gdouble page_size;
|
|
gdouble lower;
|
|
gdouble upper;
|
|
gdouble value;
|
|
GtkWidget *tool_window = g_object_get_data (G_OBJECT (week_view), "tooltip-window");
|
|
guint timeout;
|
|
|
|
timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
|
|
if (timeout) {
|
|
g_source_remove (timeout);
|
|
g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
|
|
}
|
|
|
|
if (tool_window) {
|
|
gtk_widget_destroy (tool_window);
|
|
g_object_set_data (G_OBJECT (week_view), "tooltip-window", NULL);
|
|
}
|
|
|
|
range = GTK_RANGE (week_view->vscrollbar);
|
|
adjustment = gtk_range_get_adjustment (range);
|
|
|
|
page_increment = gtk_adjustment_get_page_increment (adjustment);
|
|
page_size = gtk_adjustment_get_page_size (adjustment);
|
|
lower = gtk_adjustment_get_lower (adjustment);
|
|
upper = gtk_adjustment_get_upper (adjustment);
|
|
value = gtk_adjustment_get_value (adjustment);
|
|
|
|
switch (scroll->direction) {
|
|
case GDK_SCROLL_UP:
|
|
new_value = value - page_increment;
|
|
break;
|
|
case GDK_SCROLL_DOWN:
|
|
new_value = value + page_increment;
|
|
break;
|
|
#if GTK_CHECK_VERSION(3,3,18)
|
|
case GDK_SCROLL_SMOOTH:
|
|
if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) {
|
|
new_value = value + scroll->delta_y * page_increment;
|
|
break;
|
|
}
|
|
return FALSE;
|
|
#endif
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
new_value = CLAMP (new_value, lower, upper - page_size);
|
|
gtk_adjustment_set_value (adjustment, new_value);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_on_motion (GtkWidget *widget,
|
|
GdkEventMotion *mevent,
|
|
EWeekView *week_view)
|
|
{
|
|
gint x, y, day;
|
|
|
|
#if 0
|
|
g_print ("In e_week_view_on_motion\n");
|
|
#endif
|
|
|
|
/* Convert the mouse position to a week & day. */
|
|
x = mevent->x;
|
|
y = mevent->y;
|
|
day = e_week_view_convert_position_to_day (week_view, x, y);
|
|
if (day == -1)
|
|
return FALSE;
|
|
|
|
if (week_view->selection_drag_pos != E_WEEK_VIEW_DRAG_NONE) {
|
|
e_week_view_update_selection (week_view, day);
|
|
return TRUE;
|
|
}
|
|
|
|
ewv_pass_gdkevent_to_etext (week_view, (GdkEvent *) mevent);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Converts a position in the canvas window to a day offset from the first
|
|
* day displayed. Returns -1 if the position is outside the grid. */
|
|
static gint
|
|
e_week_view_convert_position_to_day (EWeekView *week_view,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
gint col, row, grid_x = -1, grid_y = -1, week, day;
|
|
gint weekend_col;
|
|
|
|
/* First we convert it to a grid position. */
|
|
for (col = 0; col <= week_view->columns; col++) {
|
|
if (x < week_view->col_offsets[col]) {
|
|
grid_x = col - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (row = 0; row <= week_view->rows; row++) {
|
|
if (y < week_view->row_offsets[row]) {
|
|
grid_y = row - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If the mouse is outside the grid return FALSE. */
|
|
if (grid_x == -1 || grid_y == -1)
|
|
return -1;
|
|
|
|
/* Now convert the grid position to a week and day. */
|
|
if (week_view->multi_week_view) {
|
|
week = grid_y / 2;
|
|
day = grid_x;
|
|
|
|
if (week_view->compress_weekend) {
|
|
weekend_col = (5 + 7 - week_view->display_start_day) % 7;
|
|
if (grid_x > weekend_col
|
|
|| (grid_x == weekend_col && grid_y % 2 == 1))
|
|
day++;
|
|
}
|
|
} else {
|
|
week = 0;
|
|
|
|
for (day = 0; day < 7; day++) {
|
|
gint day_x = 0, day_y = 0, rows = 0;
|
|
e_week_view_layout_get_day_position (day, FALSE, 1, week_view->display_start_day, week_view->compress_weekend,
|
|
&day_x, &day_y, &rows);
|
|
|
|
if (grid_x == day_x && grid_y >= day_y && grid_y < day_y + rows)
|
|
break;
|
|
}
|
|
|
|
if (day == 7)
|
|
return -1;
|
|
}
|
|
|
|
return week * 7 + day;
|
|
}
|
|
|
|
static void
|
|
e_week_view_update_selection (EWeekView *week_view,
|
|
gint day)
|
|
{
|
|
gint tmp_day;
|
|
gboolean need_redraw = FALSE;
|
|
|
|
#if 0
|
|
g_print ("Updating selection %i,%i\n", week, day);
|
|
#endif
|
|
|
|
if (week_view->selection_drag_pos == E_WEEK_VIEW_DRAG_START) {
|
|
if (day != week_view->selection_start_day) {
|
|
need_redraw = TRUE;
|
|
week_view->selection_start_day = day;
|
|
}
|
|
} else {
|
|
if (day != week_view->selection_end_day) {
|
|
need_redraw = TRUE;
|
|
week_view->selection_end_day = day;
|
|
}
|
|
}
|
|
|
|
/* Switch the drag position if necessary. */
|
|
if (week_view->selection_start_day > week_view->selection_end_day) {
|
|
tmp_day = week_view->selection_start_day;
|
|
week_view->selection_start_day = week_view->selection_end_day;
|
|
week_view->selection_end_day = tmp_day;
|
|
if (week_view->selection_drag_pos == E_WEEK_VIEW_DRAG_START)
|
|
week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_END;
|
|
else
|
|
week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_START;
|
|
}
|
|
|
|
/* FIXME: Optimise? */
|
|
if (need_redraw) {
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
}
|
|
}
|
|
|
|
static void
|
|
e_week_view_free_events (EWeekView *week_view)
|
|
{
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
gint event_num, span_num, num_days, day;
|
|
|
|
/* Reset all our indices. */
|
|
week_view->pressed_event_num = -1;
|
|
week_view->pressed_span_num = -1;
|
|
week_view->editing_event_num = -1;
|
|
week_view->editing_span_num = -1;
|
|
week_view->popup_event_num = -1;
|
|
|
|
for (event_num = 0; event_num < week_view->events->len; event_num++) {
|
|
event = &g_array_index (week_view->events, EWeekViewEvent,
|
|
event_num);
|
|
|
|
if (is_comp_data_valid (event))
|
|
g_object_unref (event->comp_data);
|
|
}
|
|
|
|
g_array_set_size (week_view->events, 0);
|
|
|
|
/* Destroy all the old canvas items. */
|
|
if (week_view->spans) {
|
|
for (span_num = 0; span_num < week_view->spans->len;
|
|
span_num++) {
|
|
span = &g_array_index (week_view->spans,
|
|
EWeekViewEventSpan, span_num);
|
|
if (span->background_item)
|
|
g_object_run_dispose (G_OBJECT (span->background_item));
|
|
if (span->text_item)
|
|
g_object_run_dispose (G_OBJECT (span->text_item));
|
|
}
|
|
g_array_free (week_view->spans, TRUE);
|
|
week_view->spans = NULL;
|
|
}
|
|
|
|
/* Clear the number of rows used per day. */
|
|
num_days = week_view->multi_week_view ? week_view->weeks_shown * 7 : 7;
|
|
for (day = 0; day <= num_days; day++) {
|
|
week_view->rows_per_day[day] = 0;
|
|
}
|
|
|
|
/* Hide all the jump buttons. */
|
|
for (day = 0; day < E_WEEK_VIEW_MAX_WEEKS * 7; day++) {
|
|
gnome_canvas_item_hide (week_view->jump_buttons[day]);
|
|
}
|
|
}
|
|
|
|
/* This adds one event to the view, adding it to the appropriate array. */
|
|
static gboolean
|
|
e_week_view_add_event (ECalComponent *comp,
|
|
time_t start,
|
|
time_t end,
|
|
gboolean prepend,
|
|
gpointer data)
|
|
|
|
{
|
|
AddEventData *add_event_data;
|
|
EWeekViewEvent event;
|
|
gint num_days;
|
|
struct icaltimetype start_tt, end_tt;
|
|
|
|
add_event_data = data;
|
|
|
|
/* Check that the event times are valid. */
|
|
num_days = add_event_data->week_view->multi_week_view ? add_event_data->week_view->weeks_shown * 7 : 7;
|
|
|
|
/*if (start > end || start >= add_event_data->week_view->day_starts[num_days] || end <= add_event_data->week_view->day_starts[0]) {
|
|
g_print ("%s: week_view:%p\n", G_STRFUNC, add_event_data->week_view);
|
|
g_print ("\tstart: %s", ctime (&start));
|
|
g_print ("\tend: %s", ctime (&end));
|
|
g_print ("\tday_starts[0]: %s", ctime (&add_event_data->week_view->day_starts[0]));
|
|
g_print ("\tday_starts[%d]: %s\n", num_days, ctime (&add_event_data->week_view->day_starts[num_days]));
|
|
}*/
|
|
|
|
g_return_val_if_fail (start <= end, TRUE);
|
|
g_return_val_if_fail (start < add_event_data->week_view->day_starts[num_days], TRUE);
|
|
g_return_val_if_fail (end > add_event_data->week_view->day_starts[0], TRUE);
|
|
|
|
start_tt = icaltime_from_timet_with_zone (start, FALSE,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->week_view)));
|
|
end_tt = icaltime_from_timet_with_zone (end, FALSE,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->week_view)));
|
|
|
|
if (add_event_data->comp_data) {
|
|
event.comp_data = g_object_ref (add_event_data->comp_data);
|
|
} else {
|
|
event.comp_data = g_object_new (E_TYPE_CAL_MODEL_COMPONENT, NULL);
|
|
|
|
event.comp_data->client = g_object_ref (e_cal_model_get_default_client (e_calendar_view_get_model (E_CALENDAR_VIEW (add_event_data->week_view))));
|
|
e_cal_component_abort_sequence (comp);
|
|
event.comp_data->icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
|
|
}
|
|
event.start = start;
|
|
event.end = end;
|
|
event.tooltip = NULL;
|
|
event.timeout = -1;
|
|
event.color = NULL;
|
|
event.spans_index = 0;
|
|
event.num_spans = 0;
|
|
event.comp_data->instance_start = start;
|
|
event.comp_data->instance_end = end;
|
|
|
|
event.start_minute = start_tt.hour * 60 + start_tt.minute;
|
|
event.end_minute = end_tt.hour * 60 + end_tt.minute;
|
|
if (event.end_minute == 0 && start != end)
|
|
event.end_minute = 24 * 60;
|
|
|
|
event.different_timezone = FALSE;
|
|
if (!cal_comp_util_compare_event_timezones (
|
|
comp,
|
|
event.comp_data->client,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->week_view))))
|
|
event.different_timezone = TRUE;
|
|
|
|
if (prepend)
|
|
g_array_prepend_val (add_event_data->week_view->events, event);
|
|
else
|
|
g_array_append_val (add_event_data->week_view->events, event);
|
|
add_event_data->week_view->events_sorted = FALSE;
|
|
add_event_data->week_view->events_need_layout = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* This lays out the events, or reshapes them, as necessary. */
|
|
static void
|
|
e_week_view_check_layout (EWeekView *week_view)
|
|
{
|
|
/* Don't bother if we aren't visible. */
|
|
if (!E_CALENDAR_VIEW (week_view)->in_focus) {
|
|
e_week_view_free_events (week_view);
|
|
week_view->requires_update = TRUE;
|
|
return;
|
|
}
|
|
|
|
/* Make sure the events are sorted (by start and size). */
|
|
e_week_view_ensure_events_sorted (week_view);
|
|
|
|
if (week_view->events_need_layout)
|
|
week_view->spans = e_week_view_layout_events
|
|
(week_view->events, week_view->spans,
|
|
week_view->multi_week_view,
|
|
week_view->weeks_shown,
|
|
week_view->compress_weekend,
|
|
week_view->display_start_day,
|
|
week_view->day_starts,
|
|
week_view->rows_per_day);
|
|
|
|
if (week_view->events_need_layout || week_view->events_need_reshape)
|
|
e_week_view_reshape_events (week_view);
|
|
|
|
week_view->events_need_layout = FALSE;
|
|
week_view->events_need_reshape = FALSE;
|
|
}
|
|
|
|
static void
|
|
e_week_view_ensure_events_sorted (EWeekView *week_view)
|
|
{
|
|
if (!week_view->events_sorted) {
|
|
qsort (week_view->events->data,
|
|
week_view->events->len,
|
|
sizeof (EWeekViewEvent),
|
|
e_week_view_event_sort_func);
|
|
week_view->events_sorted = TRUE;
|
|
}
|
|
}
|
|
|
|
gint
|
|
e_week_view_event_sort_func (gconstpointer arg1,
|
|
gconstpointer arg2)
|
|
{
|
|
EWeekViewEvent *event1, *event2;
|
|
|
|
event1 = (EWeekViewEvent *) arg1;
|
|
event2 = (EWeekViewEvent *) arg2;
|
|
|
|
if (event1->start < event2->start)
|
|
return -1;
|
|
if (event1->start > event2->start)
|
|
return 1;
|
|
|
|
if (event1->end > event2->end)
|
|
return -1;
|
|
if (event1->end < event2->end)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
e_week_view_reshape_events (EWeekView *week_view)
|
|
{
|
|
EWeekViewEvent *event;
|
|
gint event_num, span_num;
|
|
gint num_days, day, day_x, day_y, day_w, day_h, max_rows;
|
|
gboolean is_weekend;
|
|
|
|
for (event_num = 0; event_num < week_view->events->len; event_num++) {
|
|
event = &g_array_index (week_view->events, EWeekViewEvent,
|
|
event_num);
|
|
if (!is_comp_data_valid (event))
|
|
continue;
|
|
|
|
for (span_num = 0; span_num < event->num_spans; span_num++) {
|
|
gchar *current_comp_string;
|
|
|
|
e_week_view_reshape_event_span (week_view, event_num,
|
|
span_num);
|
|
if (week_view->last_edited_comp_string == NULL)
|
|
continue;
|
|
current_comp_string = icalcomponent_as_ical_string_r (event->comp_data->icalcomp);
|
|
if (strncmp (current_comp_string, week_view->last_edited_comp_string,50) == 0) {
|
|
EWeekViewEventSpan *span;
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num)) {
|
|
g_free (current_comp_string);
|
|
continue;
|
|
}
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num);
|
|
e_canvas_item_grab_focus (span->text_item, TRUE);
|
|
g_free (week_view->last_edited_comp_string);
|
|
week_view->last_edited_comp_string = NULL;
|
|
}
|
|
g_free (current_comp_string);
|
|
}
|
|
}
|
|
|
|
/* Reshape the jump buttons and show/hide them as appropriate. */
|
|
num_days = week_view->multi_week_view ? week_view->weeks_shown * 7 : 7;
|
|
for (day = 0; day < num_days; day++) {
|
|
|
|
is_weekend = ((week_view->display_start_day + day) % 7 >= 5) ? TRUE : FALSE;
|
|
if (!is_weekend || (week_view->multi_week_view
|
|
&& !week_view->compress_weekend))
|
|
max_rows = week_view->rows_per_cell;
|
|
else
|
|
max_rows = week_view->rows_per_compressed_cell;
|
|
|
|
/* Determine whether the jump button should be shown. */
|
|
if (week_view->rows_per_day[day] <= max_rows) {
|
|
gnome_canvas_item_hide (week_view->jump_buttons[day]);
|
|
} else {
|
|
cairo_matrix_t matrix;
|
|
|
|
e_week_view_get_day_position (week_view, day,
|
|
&day_x, &day_y,
|
|
&day_w, &day_h);
|
|
|
|
cairo_matrix_init_translate (&matrix,
|
|
day_x + day_w - E_WEEK_VIEW_JUMP_BUTTON_X_PAD - E_WEEK_VIEW_JUMP_BUTTON_WIDTH,
|
|
day_y + day_h - E_WEEK_VIEW_JUMP_BUTTON_Y_PAD - E_WEEK_VIEW_JUMP_BUTTON_HEIGHT);
|
|
gnome_canvas_item_set_matrix (week_view->jump_buttons[day], &matrix);
|
|
|
|
gnome_canvas_item_show (week_view->jump_buttons[day]);
|
|
gnome_canvas_item_raise_to_top (week_view->jump_buttons[day]);
|
|
}
|
|
}
|
|
|
|
for (day = num_days; day < E_WEEK_VIEW_MAX_WEEKS * 7; day++) {
|
|
gnome_canvas_item_hide (week_view->jump_buttons[day]);
|
|
}
|
|
}
|
|
|
|
static EWeekViewEvent *
|
|
tooltip_get_view_event (EWeekView *week_view,
|
|
gint day,
|
|
gint event_num)
|
|
{
|
|
EWeekViewEvent *pevent;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return NULL;
|
|
|
|
pevent = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
return pevent;
|
|
}
|
|
|
|
static void
|
|
tooltip_destroy (EWeekView *week_view,
|
|
GnomeCanvasItem *item)
|
|
{
|
|
gint event_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "event-num"));
|
|
EWeekViewEvent *pevent;
|
|
guint timeout;
|
|
|
|
timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
|
|
if (timeout) {
|
|
g_source_remove (timeout);
|
|
g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
|
|
}
|
|
|
|
pevent = tooltip_get_view_event (week_view, -1, event_num);
|
|
if (pevent) {
|
|
if (pevent->tooltip && g_object_get_data (G_OBJECT (week_view), "tooltip-window")) {
|
|
gtk_widget_destroy (pevent->tooltip);
|
|
pevent->tooltip = NULL;
|
|
}
|
|
|
|
g_object_set_data (G_OBJECT (week_view), "tooltip-window", NULL);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
tooltip_event_cb (GnomeCanvasItem *item,
|
|
GdkEvent *event,
|
|
EWeekView *view)
|
|
{
|
|
gint event_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "event-num"));
|
|
EWeekViewEvent *pevent;
|
|
|
|
pevent = tooltip_get_view_event (view, -1, event_num);
|
|
|
|
switch (event->type) {
|
|
case GDK_ENTER_NOTIFY:
|
|
if (view->editing_event_num == -1) {
|
|
ECalendarViewEventData *data;
|
|
|
|
data = g_malloc (sizeof (ECalendarViewEventData));
|
|
|
|
pevent->x = ((GdkEventCrossing *) event)->x_root;
|
|
pevent->y = ((GdkEventCrossing *) event)->y_root;
|
|
pevent->tooltip = NULL;
|
|
|
|
data->cal_view = (ECalendarView *) view;
|
|
data->day = -1;
|
|
data->event_num = event_num;
|
|
data->get_view_event = (ECalendarViewEvent * (*)(ECalendarView *, int, gint)) tooltip_get_view_event;
|
|
pevent->timeout = g_timeout_add_full (
|
|
G_PRIORITY_DEFAULT, 500,
|
|
(GSourceFunc) e_calendar_view_get_tooltips,
|
|
data, (GDestroyNotify) g_free);
|
|
g_object_set_data ((GObject *)view, "tooltip-timeout", GUINT_TO_POINTER (pevent->timeout));
|
|
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
case GDK_MOTION_NOTIFY:
|
|
pevent->x = ((GdkEventMotion *) event)->x_root;
|
|
pevent->y = ((GdkEventMotion *) event)->y_root;
|
|
pevent->tooltip = (GtkWidget *)g_object_get_data (G_OBJECT (view), "tooltip-window");
|
|
|
|
if (pevent->tooltip) {
|
|
e_calendar_view_move_tip (pevent->tooltip, pevent->x + 16, pevent->y + 16);
|
|
}
|
|
|
|
return TRUE;
|
|
case GDK_LEAVE_NOTIFY:
|
|
case GDK_KEY_PRESS:
|
|
case GDK_BUTTON_PRESS:
|
|
tooltip_destroy (view, item);
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static const gchar *
|
|
get_comp_summary (ECalClient *client,
|
|
icalcomponent *icalcomp,
|
|
gboolean *free_text)
|
|
{
|
|
const gchar *my_summary, *location;
|
|
const gchar *summary;
|
|
gboolean my_free_text = FALSE;
|
|
|
|
g_return_val_if_fail (icalcomp != NULL && free_text != NULL, NULL);
|
|
|
|
my_summary = e_calendar_view_get_icalcomponent_summary (client, icalcomp, &my_free_text);
|
|
|
|
location = icalcomponent_get_location (icalcomp);
|
|
if (location && *location) {
|
|
*free_text = TRUE;
|
|
summary = g_strdup_printf ("%s (%s)", my_summary, location);
|
|
|
|
if (my_free_text)
|
|
g_free ((gchar *) my_summary);
|
|
} else {
|
|
*free_text = my_free_text;
|
|
summary = my_summary;
|
|
}
|
|
|
|
return summary;
|
|
}
|
|
|
|
static void
|
|
e_week_view_reshape_event_span (EWeekView *week_view,
|
|
gint event_num,
|
|
gint span_num)
|
|
{
|
|
ECalendarView *cal_view;
|
|
ECalModel *model;
|
|
ESourceRegistry *registry;
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
gint span_x, span_y, span_w, num_icons, icons_width, time_width;
|
|
gint min_text_x, max_text_w, width;
|
|
gboolean show_icons = TRUE, use_max_width = FALSE;
|
|
gboolean one_day_event;
|
|
ECalComponent *comp;
|
|
gdouble text_x, text_y, text_w, text_h;
|
|
gchar *text, *end_of_line;
|
|
gint line_len, text_width;
|
|
PangoFontDescription *font_desc;
|
|
PangoContext *pango_context;
|
|
PangoFontMetrics *font_metrics;
|
|
PangoLayout *layout;
|
|
|
|
cal_view = E_CALENDAR_VIEW (week_view);
|
|
model = e_calendar_view_get_model (cal_view);
|
|
|
|
registry = e_cal_model_get_registry (model);
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return;
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
|
|
return;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan,
|
|
event->spans_index + span_num);
|
|
comp = e_cal_component_new ();
|
|
e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
|
|
|
|
one_day_event = e_week_view_is_one_day_event (week_view, event_num);
|
|
|
|
/* If the span will not be visible destroy the canvas items and
|
|
* return. */
|
|
if (!e_week_view_get_span_position (week_view, event_num, span_num,
|
|
&span_x, &span_y, &span_w)) {
|
|
if (span->background_item)
|
|
g_object_run_dispose (G_OBJECT (span->background_item));
|
|
if (span->text_item)
|
|
g_object_run_dispose (G_OBJECT (span->text_item));
|
|
span->background_item = NULL;
|
|
span->text_item = NULL;
|
|
|
|
g_object_unref (comp);
|
|
return;
|
|
}
|
|
|
|
/* Set up Pango prerequisites */
|
|
font_desc = gtk_widget_get_style (GTK_WIDGET (week_view))->font_desc;
|
|
pango_context = gtk_widget_get_pango_context (GTK_WIDGET (week_view));
|
|
font_metrics = pango_context_get_metrics (pango_context, font_desc,
|
|
pango_context_get_language (pango_context));
|
|
layout = pango_layout_new (pango_context);
|
|
|
|
/* If we are editing a long event we don't show the icons and the EText
|
|
* item uses the maximum width available. */
|
|
if (!one_day_event && week_view->editing_event_num == event_num
|
|
&& week_view->editing_span_num == span_num) {
|
|
show_icons = FALSE;
|
|
use_max_width = TRUE;
|
|
}
|
|
|
|
/* Calculate how many icons we need to show. */
|
|
num_icons = 0;
|
|
if (show_icons) {
|
|
if (e_cal_component_has_alarms (comp))
|
|
num_icons++;
|
|
if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp))
|
|
num_icons++;
|
|
if (e_cal_component_has_attachments (comp))
|
|
num_icons++;
|
|
if (e_cal_component_has_attendees (comp))
|
|
num_icons++;
|
|
if (event->different_timezone)
|
|
num_icons++;
|
|
num_icons += cal_comp_util_get_n_icons (comp, NULL);
|
|
}
|
|
|
|
/* Create the background canvas item if necessary. */
|
|
if (!span->background_item) {
|
|
span->background_item =
|
|
gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root),
|
|
e_week_view_event_item_get_type (),
|
|
NULL);
|
|
}
|
|
|
|
g_object_set_data ((GObject *)span->background_item, "event-num", GINT_TO_POINTER (event_num));
|
|
g_signal_connect (
|
|
span->background_item, "event",
|
|
G_CALLBACK (tooltip_event_cb), week_view);
|
|
|
|
gnome_canvas_item_set (span->background_item,
|
|
"event_num", event_num,
|
|
"span_num", span_num,
|
|
NULL);
|
|
|
|
/* Create the text item if necessary. */
|
|
if (!span->text_item) {
|
|
const gchar *summary;
|
|
GtkWidget *widget;
|
|
GdkColor color;
|
|
gboolean free_text = FALSE;
|
|
|
|
widget = (GtkWidget *) week_view;
|
|
|
|
color = e_week_view_get_text_color (week_view, event, widget);
|
|
summary = get_comp_summary (event->comp_data->client, event->comp_data->icalcomp, &free_text);
|
|
|
|
span->text_item =
|
|
gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root),
|
|
e_text_get_type (),
|
|
"clip", TRUE,
|
|
"max_lines", 1,
|
|
"editable", TRUE,
|
|
"text", summary ? summary : "",
|
|
"use_ellipsis", TRUE,
|
|
"fill_color_gdk", &color,
|
|
"im_context", E_CANVAS (week_view->main_canvas)->im_context,
|
|
NULL);
|
|
|
|
if (free_text)
|
|
g_free ((gchar *) summary);
|
|
|
|
if (e_client_check_capability (E_CLIENT (event->comp_data->client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING)
|
|
&& e_cal_util_component_has_attendee (event->comp_data->icalcomp)) {
|
|
set_text_as_bold (event, span, registry);
|
|
}
|
|
g_object_set_data (G_OBJECT (span->text_item), "event-num", GINT_TO_POINTER (event_num));
|
|
g_signal_connect (
|
|
span->text_item, "event",
|
|
G_CALLBACK (e_week_view_on_text_item_event), week_view);
|
|
g_signal_emit_by_name (G_OBJECT (week_view),
|
|
"event_added", event);
|
|
|
|
}
|
|
|
|
/* Calculate the position of the text item.
|
|
* For events < 1 day it starts after the times & icons and ends at the
|
|
* right edge of the span.
|
|
* For events >= 1 day we need to determine whether times are shown at
|
|
* the start and end of the span, then try to center the text item with
|
|
* the icons in the middle, but making sure we don't go over the times.
|
|
*/
|
|
|
|
/* Calculate the space necessary to display a time, e.g. "13:00". */
|
|
time_width = e_week_view_get_time_string_width (week_view);
|
|
|
|
/* Calculate the space needed for the icons. */
|
|
icons_width = (E_WEEK_VIEW_ICON_WIDTH + E_WEEK_VIEW_ICON_X_PAD)
|
|
* num_icons - E_WEEK_VIEW_ICON_X_PAD + E_WEEK_VIEW_ICON_R_PAD;
|
|
|
|
/* The y position and height are the same for both event types. */
|
|
text_y = span_y + E_WEEK_VIEW_EVENT_BORDER_HEIGHT
|
|
+ E_WEEK_VIEW_EVENT_TEXT_Y_PAD;
|
|
|
|
text_h =
|
|
PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
|
|
PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
|
|
|
|
if (one_day_event) {
|
|
/* Note that 1-day events don't have a border. Although we
|
|
* still use the border height to position the events
|
|
* vertically so they still line up neatly (see above),
|
|
* we don't use the border width or edge padding at all. */
|
|
text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD;
|
|
|
|
switch (week_view->time_format) {
|
|
case E_WEEK_VIEW_TIME_BOTH_SMALL_MIN:
|
|
case E_WEEK_VIEW_TIME_BOTH:
|
|
/* These have 2 time strings with a small space between
|
|
* them and some space before the EText item. */
|
|
text_x += time_width * 2
|
|
+ E_WEEK_VIEW_EVENT_TIME_SPACING
|
|
+ E_WEEK_VIEW_EVENT_TIME_X_PAD;
|
|
break;
|
|
case E_WEEK_VIEW_TIME_START_SMALL_MIN:
|
|
case E_WEEK_VIEW_TIME_START:
|
|
/* These have just 1 time string with some space
|
|
* before the EText item. */
|
|
text_x += time_width + E_WEEK_VIEW_EVENT_TIME_X_PAD;
|
|
break;
|
|
case E_WEEK_VIEW_TIME_NONE:
|
|
break;
|
|
}
|
|
|
|
/* The icons_width includes space on the right of the icons. */
|
|
text_x += icons_width;
|
|
|
|
/* The width of the EText item extends right to the edge of the
|
|
* event, just inside the border. */
|
|
text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD - text_x;
|
|
|
|
} else {
|
|
if (use_max_width) {
|
|
/* When we are editing the event we use all the
|
|
* available width. */
|
|
text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD
|
|
+ E_WEEK_VIEW_EVENT_BORDER_WIDTH
|
|
+ E_WEEK_VIEW_EVENT_EDGE_X_PAD;
|
|
text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD
|
|
- E_WEEK_VIEW_EVENT_BORDER_WIDTH
|
|
- E_WEEK_VIEW_EVENT_EDGE_X_PAD - text_x;
|
|
} else {
|
|
text = NULL;
|
|
/* Get the width of the text of the event. This is a
|
|
* bit of a hack. It would be better if EText could
|
|
* tell us this. */
|
|
g_object_get (span->text_item, "text", &text, NULL);
|
|
text_width = 0;
|
|
if (text) {
|
|
/* It should only have one line of text in it.
|
|
* I'm not sure we need this any more. */
|
|
end_of_line = strchr (text, '\n');
|
|
if (end_of_line)
|
|
line_len = end_of_line - text;
|
|
else
|
|
line_len = strlen (text);
|
|
|
|
pango_layout_set_text (layout, text, line_len);
|
|
pango_layout_get_pixel_size (layout, &text_width, NULL);
|
|
g_free (text);
|
|
}
|
|
|
|
/* Add on the width of the icons and find the default
|
|
* position, which centers the icons + text. */
|
|
width = text_width + icons_width;
|
|
text_x = span_x + (span_w - width) / 2;
|
|
|
|
/* Now calculate the left-most valid position, and make
|
|
* sure we don't go to the left of that. */
|
|
min_text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD
|
|
+ E_WEEK_VIEW_EVENT_BORDER_WIDTH
|
|
+ E_WEEK_VIEW_EVENT_EDGE_X_PAD;
|
|
/* See if we will want to display the start time, and
|
|
* if so take that into account. */
|
|
if (event->start > week_view->day_starts[span->start_day])
|
|
min_text_x += time_width
|
|
+ E_WEEK_VIEW_EVENT_TIME_X_PAD;
|
|
|
|
/* Now make sure we don't go to the left of the minimum
|
|
* position. */
|
|
text_x = MAX (text_x, min_text_x);
|
|
|
|
/* Now calculate the largest valid width, using the
|
|
* calculated x position, and make sure we don't
|
|
* exceed that. */
|
|
max_text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD
|
|
- E_WEEK_VIEW_EVENT_BORDER_WIDTH
|
|
- E_WEEK_VIEW_EVENT_EDGE_X_PAD - text_x;
|
|
if (event->end < week_view->day_starts[span->start_day
|
|
+ span->num_days])
|
|
max_text_w -= time_width
|
|
+ E_WEEK_VIEW_EVENT_TIME_X_PAD;
|
|
|
|
text_w = MIN (width, max_text_w);
|
|
|
|
/* Now take out the space for the icons. */
|
|
text_x += icons_width;
|
|
text_w -= icons_width;
|
|
}
|
|
}
|
|
|
|
/* Make sure we don't try to use a negative width. */
|
|
text_w = MAX (text_w, 0);
|
|
|
|
gnome_canvas_item_set (span->text_item,
|
|
"clip_width", (gdouble) text_w,
|
|
"clip_height", (gdouble) text_h,
|
|
NULL);
|
|
e_canvas_item_move_absolute (span->text_item, text_x, text_y);
|
|
|
|
g_object_unref (comp);
|
|
g_object_unref (layout);
|
|
pango_font_metrics_unref (font_metrics);
|
|
}
|
|
|
|
gboolean
|
|
e_week_view_start_editing_event (EWeekView *week_view,
|
|
gint event_num,
|
|
gint span_num,
|
|
gchar *initial_text)
|
|
{
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
ETextEventProcessor *event_processor = NULL;
|
|
ETextEventProcessorCommand command;
|
|
ECalModelComponent *comp_data;
|
|
|
|
/* If we are already editing the event, just return. */
|
|
if (event_num == week_view->editing_event_num
|
|
&& span_num == week_view->editing_span_num)
|
|
return TRUE;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return FALSE;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return FALSE;
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
|
|
return FALSE;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan,
|
|
event->spans_index + span_num);
|
|
|
|
if (e_client_is_readonly (E_CLIENT (event->comp_data->client)))
|
|
return FALSE;
|
|
|
|
/* If the event is not shown, don't try to edit it. */
|
|
if (!span->text_item)
|
|
return FALSE;
|
|
|
|
if (week_view->editing_event_num >= 0) {
|
|
EWeekViewEvent *editing;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
|
|
return FALSE;
|
|
|
|
editing = &g_array_index (week_view->events, EWeekViewEvent, week_view->editing_event_num);
|
|
|
|
/* do not change to other part of same component - the event is spread into more days */
|
|
if (editing && event && editing->comp_data == event->comp_data)
|
|
return FALSE;
|
|
}
|
|
|
|
gnome_canvas_item_set (span->text_item,
|
|
"text", initial_text ? initial_text : icalcomponent_get_summary (event->comp_data->icalcomp),
|
|
NULL);
|
|
|
|
/* Save the comp_data value because we use that as our invariant */
|
|
comp_data = event->comp_data;
|
|
|
|
e_canvas_item_grab_focus (span->text_item, TRUE);
|
|
|
|
/* If the above focus caused things to redraw, then find the
|
|
* the event and the span again */
|
|
if (event_num < week_view->events->len)
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
if (event_num >= week_view->events->len || event->comp_data != comp_data) {
|
|
/* When got in because of other comp_data, then be sure we go through all events */
|
|
event_num = week_view->events->len;
|
|
|
|
/* Unfocussing can cause a removal but not a new
|
|
* addition so just run backwards through the
|
|
* events */
|
|
for (event_num--; event_num >= 0; event_num--) {
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
if (event->comp_data == comp_data)
|
|
break;
|
|
}
|
|
g_return_val_if_fail (event_num >= 0, FALSE);
|
|
}
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
|
|
return FALSE;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num);
|
|
|
|
/* Try to move the cursor to the end of the text. */
|
|
g_object_get (span->text_item, "event_processor", &event_processor, NULL);
|
|
if (event_processor) {
|
|
command.action = E_TEP_MOVE;
|
|
command.position = E_TEP_END_OF_BUFFER;
|
|
g_signal_emit_by_name (event_processor,
|
|
"command", &command);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* This stops any current edit. */
|
|
void
|
|
e_week_view_stop_editing_event (EWeekView *week_view)
|
|
{
|
|
GtkWidget *toplevel;
|
|
|
|
/* Check we are editing an event. */
|
|
if (week_view->editing_event_num == -1)
|
|
return;
|
|
|
|
/* Set focus to the toplevel so the item loses focus. */
|
|
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (week_view));
|
|
if (toplevel && GTK_IS_WINDOW (toplevel))
|
|
gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
|
|
}
|
|
|
|
/* Cancels the current edition by resetting the appointment's text to its original value */
|
|
static void
|
|
cancel_editing (EWeekView *week_view)
|
|
{
|
|
gint event_num, span_num;
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
const gchar *summary;
|
|
|
|
event_num = week_view->editing_event_num;
|
|
span_num = week_view->editing_span_num;
|
|
|
|
g_return_if_fail (event_num != -1);
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return;
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
|
|
return;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num);
|
|
|
|
/* Reset the text to what was in the component */
|
|
|
|
summary = icalcomponent_get_summary (event->comp_data->icalcomp);
|
|
g_object_set (span->text_item, "text", summary ? summary : "", NULL);
|
|
|
|
/* Stop editing */
|
|
e_week_view_stop_editing_event (week_view);
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_on_text_item_event (GnomeCanvasItem *item,
|
|
GdkEvent *gdkevent,
|
|
EWeekView *week_view)
|
|
{
|
|
EWeekViewEvent *event;
|
|
gint event_num, span_num;
|
|
gint nevent = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "event-num"));
|
|
EWeekViewEvent *pevent;
|
|
|
|
pevent = tooltip_get_view_event (week_view, -1, nevent);
|
|
|
|
#if 0
|
|
g_print ("In e_week_view_on_text_item_event\n");
|
|
#endif
|
|
|
|
switch (gdkevent->type) {
|
|
case GDK_KEY_PRESS:
|
|
tooltip_destroy (week_view, item);
|
|
if (!E_TEXT (item)->preedit_len && gdkevent && gdkevent->key.keyval == GDK_KEY_Return) {
|
|
/* We set the keyboard focus to the EDayView, so the
|
|
* EText item loses it and stops the edit. */
|
|
gtk_widget_grab_focus (GTK_WIDGET (week_view));
|
|
|
|
/* Stop the signal last or we will also stop any
|
|
* other events getting to the EText item. */
|
|
g_signal_stop_emission_by_name (G_OBJECT (item), "event");
|
|
return TRUE;
|
|
} else if (gdkevent->key.keyval == GDK_KEY_Escape) {
|
|
cancel_editing (week_view);
|
|
g_signal_stop_emission_by_name (G_OBJECT (item), "event");
|
|
/* focus should go to week view when stop editing */
|
|
gtk_widget_grab_focus (GTK_WIDGET (week_view));
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case GDK_2BUTTON_PRESS:
|
|
if (!e_week_view_find_event_from_item (week_view, item,
|
|
&event_num, &span_num))
|
|
return FALSE;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return FALSE;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent,
|
|
event_num);
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return FALSE;
|
|
|
|
/* if we started to editing new item on the canvas, then do not open editing dialog until it's saved,
|
|
* because the save of the event recalculates event numbers and you can edit different one */
|
|
if (!is_icalcomp_on_the_server (event->comp_data->icalcomp, event->comp_data->client))
|
|
return TRUE;
|
|
|
|
e_calendar_view_edit_appointment (E_CALENDAR_VIEW (week_view),
|
|
event->comp_data->client,
|
|
event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT);
|
|
|
|
g_signal_stop_emission_by_name (G_OBJECT (item), "event");
|
|
return TRUE;
|
|
case GDK_BUTTON_PRESS:
|
|
tooltip_destroy (week_view, item);
|
|
if (!e_week_view_find_event_from_item (week_view, item,
|
|
&event_num, &span_num))
|
|
return FALSE;
|
|
|
|
if (gdkevent->button.button == 3) {
|
|
EWeekViewEvent *e;
|
|
|
|
if (E_TEXT (item)->editing) {
|
|
e_week_view_stop_editing_event (week_view);
|
|
gtk_widget_grab_focus (GTK_WIDGET (week_view));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return FALSE;
|
|
|
|
e = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
if (!gtk_widget_has_focus (GTK_WIDGET (week_view)))
|
|
gtk_widget_grab_focus (GTK_WIDGET (week_view));
|
|
|
|
e_week_view_set_selected_time_range_visible (week_view, e->start, e->end);
|
|
|
|
e_week_view_show_popup_menu (week_view,
|
|
(GdkEventButton *) gdkevent,
|
|
event_num);
|
|
|
|
g_signal_stop_emission_by_name (G_OBJECT (item->canvas),
|
|
"button_press_event");
|
|
return TRUE;
|
|
}
|
|
|
|
if (gdkevent->button.button != 3) {
|
|
week_view->pressed_event_num = event_num;
|
|
week_view->pressed_span_num = span_num;
|
|
}
|
|
|
|
/* Only let the EText handle the event while editing. */
|
|
if (!E_TEXT (item)->editing) {
|
|
g_signal_stop_emission_by_name (G_OBJECT (item), "event");
|
|
|
|
if (gdkevent) {
|
|
week_view->drag_event_x = gdkevent->button.x;
|
|
week_view->drag_event_y = gdkevent->button.y;
|
|
} else
|
|
g_warning ("No GdkEvent");
|
|
|
|
/* FIXME: Remember the day offset from the start of
|
|
* the event, for DnD. */
|
|
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case GDK_BUTTON_RELEASE:
|
|
if (!E_TEXT (item)->editing) {
|
|
/* This shouldn't ever happen. */
|
|
if (!e_week_view_find_event_from_item (week_view,
|
|
item,
|
|
&event_num,
|
|
&span_num))
|
|
return FALSE;
|
|
|
|
if (week_view->pressed_event_num != -1
|
|
&& week_view->pressed_event_num == event_num
|
|
&& week_view->pressed_span_num == span_num) {
|
|
e_week_view_start_editing_event (week_view,
|
|
event_num,
|
|
span_num,
|
|
NULL);
|
|
week_view->pressed_event_num = -1;
|
|
}
|
|
|
|
/* Stop the signal last or we will also stop any
|
|
* other events getting to the EText item. */
|
|
g_signal_stop_emission_by_name (G_OBJECT (item), "event");
|
|
return TRUE;
|
|
}
|
|
week_view->pressed_event_num = -1;
|
|
break;
|
|
case GDK_ENTER_NOTIFY:
|
|
{
|
|
ECalendarViewEventData *data;
|
|
gint nspan;
|
|
|
|
if (week_view->editing_event_num != -1
|
|
|| !e_week_view_find_event_from_item (week_view, item, &nevent, &nspan))
|
|
return FALSE;
|
|
|
|
g_object_set_data ((GObject *)item, "event-num", GINT_TO_POINTER (nevent));
|
|
|
|
pevent = tooltip_get_view_event (week_view, -1, nevent);
|
|
|
|
data = g_malloc (sizeof (ECalendarViewEventData));
|
|
|
|
pevent->x = ((GdkEventCrossing *) gdkevent)->x_root;
|
|
pevent->y = ((GdkEventCrossing *) gdkevent)->y_root;
|
|
pevent->tooltip = NULL;
|
|
|
|
data->cal_view = (ECalendarView *) week_view;
|
|
data->day = -1;
|
|
data->event_num = nevent;
|
|
data->get_view_event = (ECalendarViewEvent * (*)(ECalendarView *, int, gint)) tooltip_get_view_event;
|
|
pevent->timeout = g_timeout_add_full (
|
|
G_PRIORITY_DEFAULT, 500,
|
|
(GSourceFunc) e_calendar_view_get_tooltips,
|
|
data, (GDestroyNotify) g_free);
|
|
g_object_set_data ((GObject *)week_view, "tooltip-timeout", GUINT_TO_POINTER (pevent->timeout));
|
|
|
|
return TRUE;
|
|
}
|
|
case GDK_LEAVE_NOTIFY:
|
|
tooltip_destroy (week_view, item);
|
|
|
|
return FALSE;
|
|
case GDK_MOTION_NOTIFY:
|
|
pevent->x = ((GdkEventMotion *) gdkevent)->x_root;
|
|
pevent->y = ((GdkEventMotion *) gdkevent)->y_root;
|
|
pevent->tooltip = (GtkWidget *)g_object_get_data (G_OBJECT (week_view), "tooltip-window");
|
|
|
|
if (pevent->tooltip) {
|
|
e_calendar_view_move_tip (pevent->tooltip, pevent->x + 16, pevent->y + 16);
|
|
}
|
|
return TRUE;
|
|
case GDK_FOCUS_CHANGE:
|
|
if (gdkevent->focus_change.in) {
|
|
e_week_view_on_editing_started (week_view, item);
|
|
} else {
|
|
e_week_view_on_editing_stopped (week_view, item);
|
|
}
|
|
|
|
return FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean e_week_view_event_move (ECalendarView *cal_view, ECalViewMoveDirection direction)
|
|
{
|
|
EWeekViewEvent *event;
|
|
gint event_num, adjust_days, current_start_day, current_end_day;
|
|
time_t start_dt, end_dt;
|
|
struct icaltimetype start_time,end_time;
|
|
EWeekView *week_view = E_WEEK_VIEW (cal_view);
|
|
gboolean is_all_day = FALSE;
|
|
|
|
event_num = week_view->editing_event_num;
|
|
adjust_days = 0;
|
|
|
|
/* If no item is being edited, just return. */
|
|
if (event_num == -1)
|
|
return FALSE;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return FALSE;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return FALSE;
|
|
|
|
end_dt = event->end;
|
|
start_time = icalcomponent_get_dtstart (event->comp_data->icalcomp);
|
|
end_time = icalcomponent_get_dtend (event->comp_data->icalcomp);
|
|
|
|
if (start_time.is_date && end_time.is_date)
|
|
is_all_day = TRUE;
|
|
|
|
current_end_day = e_week_view_get_day_offset_of_event (week_view,end_dt);
|
|
|
|
switch (direction) {
|
|
case E_CAL_VIEW_MOVE_UP:
|
|
adjust_days = e_week_view_get_adjust_days_for_move_up (week_view,current_end_day);
|
|
break;
|
|
case E_CAL_VIEW_MOVE_DOWN:
|
|
adjust_days = e_week_view_get_adjust_days_for_move_down (week_view,current_end_day);
|
|
break;
|
|
case E_CAL_VIEW_MOVE_LEFT:
|
|
adjust_days = e_week_view_get_adjust_days_for_move_left (week_view,current_end_day);
|
|
break;
|
|
case E_CAL_VIEW_MOVE_RIGHT:
|
|
adjust_days = e_week_view_get_adjust_days_for_move_right (week_view,current_end_day);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
icaltime_adjust (&start_time ,adjust_days,0,0,0);
|
|
icaltime_adjust (&end_time ,adjust_days,0,0,0);
|
|
start_dt = icaltime_as_timet_with_zone (start_time,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
end_dt = icaltime_as_timet_with_zone (end_time,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
|
|
current_start_day = e_week_view_get_day_offset_of_event (week_view,start_dt);
|
|
current_end_day = e_week_view_get_day_offset_of_event (week_view,end_dt);
|
|
if (is_all_day)
|
|
current_end_day--;
|
|
|
|
if (current_start_day < 0) {
|
|
return TRUE;
|
|
}
|
|
if (week_view->multi_week_view) {
|
|
if (current_end_day >= week_view->weeks_shown * 7) {
|
|
return TRUE;
|
|
}
|
|
}else {
|
|
if (current_end_day >= 7) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
e_week_view_change_event_time (week_view, start_dt, end_dt, is_all_day);
|
|
return TRUE;
|
|
}
|
|
|
|
static gint
|
|
e_week_view_get_day_offset_of_event (EWeekView *week_view,
|
|
time_t event_time)
|
|
{
|
|
time_t first_day = week_view->day_starts[0];
|
|
|
|
if (event_time - first_day < 0)
|
|
return -1;
|
|
else
|
|
return (event_time - first_day) / (24 * 60 * 60);
|
|
}
|
|
|
|
void
|
|
e_week_view_scroll_a_step (EWeekView *week_view,
|
|
ECalViewMoveDirection direction)
|
|
{
|
|
GtkAdjustment *adjustment;
|
|
GtkRange *range;
|
|
gdouble step_increment;
|
|
gdouble page_size;
|
|
gdouble new_value;
|
|
gdouble lower;
|
|
gdouble upper;
|
|
gdouble value;
|
|
|
|
range = GTK_RANGE (week_view->vscrollbar);
|
|
adjustment = gtk_range_get_adjustment (range);
|
|
|
|
step_increment = gtk_adjustment_get_step_increment (adjustment);
|
|
page_size = gtk_adjustment_get_page_size (adjustment);
|
|
lower = gtk_adjustment_get_lower (adjustment);
|
|
upper = gtk_adjustment_get_upper (adjustment);
|
|
value = gtk_adjustment_get_value (adjustment);
|
|
|
|
switch (direction) {
|
|
case E_CAL_VIEW_MOVE_UP:
|
|
new_value = value - step_increment;
|
|
break;
|
|
case E_CAL_VIEW_MOVE_DOWN:
|
|
new_value = value + step_increment;
|
|
break;
|
|
case E_CAL_VIEW_MOVE_PAGE_UP:
|
|
new_value = value - page_size;
|
|
break;
|
|
case E_CAL_VIEW_MOVE_PAGE_DOWN:
|
|
new_value = value + page_size;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
new_value = CLAMP (new_value, lower, upper - page_size);
|
|
gtk_adjustment_set_value (adjustment, new_value);
|
|
}
|
|
|
|
static void
|
|
e_week_view_change_event_time (EWeekView *week_view,
|
|
time_t start_dt,
|
|
time_t end_dt,
|
|
gboolean is_all_day)
|
|
{
|
|
EWeekViewEvent *event;
|
|
gint event_num;
|
|
ECalComponent *comp;
|
|
ECalComponentDateTime date;
|
|
struct icaltimetype itt;
|
|
ECalClient *client;
|
|
CalObjModType mod = CALOBJ_MOD_ALL;
|
|
GtkWindow *toplevel;
|
|
|
|
event_num = week_view->editing_event_num;
|
|
|
|
/* If no item is being edited, just return. */
|
|
if (event_num == -1)
|
|
return;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return;
|
|
|
|
client = event->comp_data->client;
|
|
|
|
/* We use a temporary shallow copy of the ico since we don't want to
|
|
* change the original ico here. Otherwise we would not detect that
|
|
* the event's time had changed in the "update_event" callback. */
|
|
comp = e_cal_component_new ();
|
|
e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
|
|
date.value = &itt;
|
|
/* FIXME: Should probably keep the timezone of the original start
|
|
* and end times. */
|
|
date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
|
|
*date.value = icaltime_from_timet_with_zone (start_dt, is_all_day,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
cal_comp_set_dtstart_with_oldzone (client, comp, &date);
|
|
*date.value = icaltime_from_timet_with_zone (end_dt, is_all_day,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
cal_comp_set_dtend_with_oldzone (client, comp, &date);
|
|
|
|
e_cal_component_commit_sequence (comp);
|
|
|
|
if (week_view->last_edited_comp_string != NULL) {
|
|
g_free (week_view->last_edited_comp_string);
|
|
week_view->last_edited_comp_string = NULL;
|
|
}
|
|
|
|
week_view->last_edited_comp_string = e_cal_component_get_as_string (comp);
|
|
|
|
if (e_cal_component_has_recurrences (comp)) {
|
|
if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
goto out;
|
|
}
|
|
|
|
if (mod == CALOBJ_MOD_ALL)
|
|
comp_util_sanitize_recurrence_master (comp, client);
|
|
|
|
if (mod == CALOBJ_MOD_THIS) {
|
|
e_cal_component_set_rdate_list (comp, NULL);
|
|
e_cal_component_set_rrule_list (comp, NULL);
|
|
e_cal_component_set_exdate_list (comp, NULL);
|
|
e_cal_component_set_exrule_list (comp, NULL);
|
|
}
|
|
} else if (e_cal_component_is_instance (comp))
|
|
mod = CALOBJ_MOD_THIS;
|
|
|
|
toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (week_view)));
|
|
|
|
e_cal_component_commit_sequence (comp);
|
|
|
|
e_calendar_view_modify_and_send (
|
|
E_CALENDAR_VIEW (week_view),
|
|
comp, client, mod, toplevel, TRUE);
|
|
|
|
out:
|
|
g_object_unref (comp);
|
|
}
|
|
|
|
static void
|
|
e_week_view_on_editing_started (EWeekView *week_view,
|
|
GnomeCanvasItem *item)
|
|
{
|
|
gint event_num, span_num;
|
|
|
|
if (!e_week_view_find_event_from_item (week_view, item,
|
|
&event_num, &span_num))
|
|
return;
|
|
|
|
#if 0
|
|
g_print ("In e_week_view_on_editing_started event_num:%i span_num:%i\n", event_num, span_num);
|
|
#endif
|
|
|
|
week_view->editing_event_num = event_num;
|
|
week_view->editing_span_num = span_num;
|
|
|
|
/* We need to reshape long events so the whole width is used while
|
|
* editing. */
|
|
if (!e_week_view_is_one_day_event (week_view, event_num)) {
|
|
e_week_view_reshape_event_span (week_view, event_num,
|
|
span_num);
|
|
}
|
|
|
|
g_signal_emit_by_name (week_view, "selection_changed");
|
|
}
|
|
|
|
static void
|
|
e_week_view_on_editing_stopped (EWeekView *week_view,
|
|
GnomeCanvasItem *item)
|
|
{
|
|
gint event_num, span_num;
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
gchar *text = NULL;
|
|
ECalComponent *comp;
|
|
ECalComponentText summary;
|
|
ECalClient *client;
|
|
const gchar *uid;
|
|
gboolean on_server;
|
|
|
|
/* Note: the item we are passed here isn't reliable, so we just stop
|
|
* the edit of whatever item was being edited. We also receive this
|
|
* event twice for some reason. */
|
|
event_num = week_view->editing_event_num;
|
|
span_num = week_view->editing_span_num;
|
|
|
|
/* If no item is being edited, just return. */
|
|
if (event_num == -1)
|
|
return;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return;
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
|
|
return;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan,
|
|
event->spans_index + span_num);
|
|
|
|
/* Reset the edit fields. */
|
|
week_view->editing_event_num = -1;
|
|
|
|
/* Check that the event is still valid. */
|
|
uid = icalcomponent_get_uid (event->comp_data->icalcomp);
|
|
if (!uid)
|
|
return;
|
|
|
|
text = NULL;
|
|
g_object_set (span->text_item, "handle_popup", FALSE, NULL);
|
|
g_object_get (span->text_item, "text", &text, NULL);
|
|
|
|
comp = e_cal_component_new ();
|
|
e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
|
|
|
|
client = event->comp_data->client;
|
|
on_server = cal_comp_is_on_server (comp, client);
|
|
|
|
if (string_is_empty (text) && !on_server) {
|
|
e_cal_component_get_uid (comp, &uid);
|
|
g_signal_handlers_disconnect_by_func (item, e_week_view_on_text_item_event, week_view);
|
|
e_week_view_foreach_event_with_uid (week_view, uid,
|
|
e_week_view_remove_event_cb, NULL);
|
|
week_view->event_destroyed = TRUE;
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
e_week_view_check_layout (week_view);
|
|
goto out;
|
|
}
|
|
|
|
/* Only update the summary if necessary. */
|
|
e_cal_component_get_summary (comp, &summary);
|
|
if (summary.value && !strcmp (text, summary.value)) {
|
|
gboolean free_text = FALSE;
|
|
const gchar *summary;
|
|
|
|
summary = get_comp_summary (event->comp_data->client, event->comp_data->icalcomp, &free_text);
|
|
g_object_set (span->text_item, "text", summary ? summary : "", NULL);
|
|
|
|
if (free_text)
|
|
g_free ((gchar *) summary);
|
|
|
|
if (!e_week_view_is_one_day_event (week_view, event_num))
|
|
e_week_view_reshape_event_span (week_view, event_num, span_num);
|
|
} else if (summary.value || !string_is_empty (text)) {
|
|
icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
|
|
|
|
summary.value = text;
|
|
summary.altrep = NULL;
|
|
e_cal_component_set_summary (comp, &summary);
|
|
e_cal_component_commit_sequence (comp);
|
|
|
|
if (!on_server) {
|
|
gchar *uid = NULL;
|
|
GError *error = NULL;
|
|
|
|
if (!e_cal_client_create_object_sync (client, icalcomp, &uid, NULL, &error)) {
|
|
g_message (G_STRLOC ": Could not create the object! %s", error ? error->message : "Unknown error");
|
|
uid = NULL;
|
|
} else {
|
|
if (uid)
|
|
icalcomponent_set_uid (icalcomp, uid);
|
|
|
|
e_calendar_view_emit_user_created (
|
|
E_CALENDAR_VIEW (week_view));
|
|
}
|
|
|
|
if (uid)
|
|
g_free (uid);
|
|
if (error)
|
|
g_error_free (error);
|
|
|
|
/* we remove the object since we either got the update from the server or failed */
|
|
e_week_view_remove_event_cb (week_view, event_num, NULL);
|
|
} else {
|
|
CalObjModType mod = CALOBJ_MOD_ALL;
|
|
GtkWindow *toplevel;
|
|
|
|
if (e_cal_component_has_recurrences (comp)) {
|
|
if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
|
|
goto out;
|
|
}
|
|
|
|
if (mod == CALOBJ_MOD_ALL)
|
|
comp_util_sanitize_recurrence_master (comp, client);
|
|
|
|
if (mod == CALOBJ_MOD_THIS) {
|
|
ECalComponentDateTime dt;
|
|
struct icaltimetype tt;
|
|
gchar *tzid;
|
|
|
|
e_cal_component_get_dtstart (comp, &dt);
|
|
if (dt.value->zone) {
|
|
tt = icaltime_from_timet_with_zone (
|
|
event->comp_data->instance_start,
|
|
dt.value->is_date,
|
|
dt.value->zone);
|
|
} else {
|
|
tt = icaltime_from_timet_with_zone (
|
|
event->comp_data->instance_start,
|
|
dt.value->is_date,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
}
|
|
tzid = g_strdup (dt.tzid);
|
|
e_cal_component_free_datetime (&dt);
|
|
dt.value = &tt;
|
|
dt.tzid = tzid;
|
|
e_cal_component_set_dtstart (comp, &dt);
|
|
g_free (tzid);
|
|
|
|
e_cal_component_get_dtend (comp, &dt);
|
|
if (dt.value->zone) {
|
|
tt = icaltime_from_timet_with_zone (
|
|
event->comp_data->instance_end,
|
|
dt.value->is_date,
|
|
dt.value->zone);
|
|
} else {
|
|
tt = icaltime_from_timet_with_zone (
|
|
event->comp_data->instance_end,
|
|
dt.value->is_date,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
}
|
|
tzid = g_strdup (dt.tzid);
|
|
e_cal_component_free_datetime (&dt);
|
|
dt.value = &tt;
|
|
dt.tzid = tzid;
|
|
e_cal_component_set_dtend (comp, &dt);
|
|
g_free (tzid);
|
|
|
|
e_cal_component_set_rdate_list (comp, NULL);
|
|
e_cal_component_set_rrule_list (comp, NULL);
|
|
e_cal_component_set_exdate_list (comp, NULL);
|
|
e_cal_component_set_exrule_list (comp, NULL);
|
|
|
|
e_cal_component_commit_sequence (comp);
|
|
}
|
|
} else if (e_cal_component_is_instance (comp))
|
|
mod = CALOBJ_MOD_THIS;
|
|
|
|
/* FIXME When sending here, what exactly should we send? */
|
|
toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (week_view)));
|
|
|
|
e_calendar_view_modify_and_send (
|
|
E_CALENDAR_VIEW (week_view),
|
|
comp, client, mod, toplevel, FALSE);
|
|
}
|
|
}
|
|
|
|
out:
|
|
|
|
g_free (text);
|
|
g_object_unref (comp);
|
|
|
|
g_signal_emit_by_name (week_view, "selection_changed");
|
|
}
|
|
|
|
gboolean
|
|
e_week_view_find_event_from_item (EWeekView *week_view,
|
|
GnomeCanvasItem *item,
|
|
gint *event_num_return,
|
|
gint *span_num_return)
|
|
{
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
gint event_num, span_num, num_events;
|
|
|
|
num_events = week_view->events->len;
|
|
for (event_num = 0; event_num < num_events; event_num++) {
|
|
event = &g_array_index (week_view->events, EWeekViewEvent,
|
|
event_num);
|
|
for (span_num = 0; span_num < event->num_spans; span_num++) {
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
|
|
continue;
|
|
|
|
span = &g_array_index (week_view->spans,
|
|
EWeekViewEventSpan,
|
|
event->spans_index + span_num);
|
|
if (span->text_item == item) {
|
|
*event_num_return = event_num;
|
|
*span_num_return = span_num;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Finds the index of the event with the given uid.
|
|
* Returns TRUE if an event with the uid was found.
|
|
* Note that for recurring events there may be several EWeekViewEvents, one
|
|
* for each instance, all with the same iCalObject and uid. So only use this
|
|
* function if you know the event doesn't recur or you are just checking to
|
|
* see if any events with the uid exist. */
|
|
static gboolean
|
|
e_week_view_find_event_from_uid (EWeekView *week_view,
|
|
ECalClient *client,
|
|
const gchar *uid,
|
|
const gchar *rid,
|
|
gint *event_num_return)
|
|
{
|
|
EWeekViewEvent *event;
|
|
gint event_num, num_events;
|
|
|
|
*event_num_return = -1;
|
|
if (!uid)
|
|
return FALSE;
|
|
|
|
num_events = week_view->events->len;
|
|
for (event_num = 0; event_num < num_events; event_num++) {
|
|
const gchar *u;
|
|
gchar *r = NULL;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent,
|
|
event_num);
|
|
|
|
if (!is_comp_data_valid (event))
|
|
continue;
|
|
|
|
if (event->comp_data->client != client)
|
|
continue;
|
|
|
|
u = icalcomponent_get_uid (event->comp_data->icalcomp);
|
|
if (u && !strcmp (uid, u)) {
|
|
if (rid && *rid) {
|
|
r = icaltime_as_ical_string_r (icalcomponent_get_recurrenceid (event->comp_data->icalcomp));
|
|
if (!r || !*r)
|
|
continue;
|
|
if (strcmp (rid, r) != 0) {
|
|
g_free (r);
|
|
continue;
|
|
}
|
|
g_free (r);
|
|
}
|
|
|
|
*event_num_return = event_num;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
e_week_view_is_one_day_event (EWeekView *week_view,
|
|
gint event_num)
|
|
{
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return FALSE;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
|
|
if (event->num_spans != 1)
|
|
return FALSE;
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index))
|
|
return FALSE;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan,
|
|
event->spans_index);
|
|
|
|
if (event->start == week_view->day_starts[span->start_day]
|
|
&& event->end == week_view->day_starts[span->start_day + 1])
|
|
return FALSE;
|
|
|
|
if (span->num_days == 1
|
|
&& event->start >= week_view->day_starts[span->start_day]
|
|
&& event->end <= week_view->day_starts[span->start_day + 1])
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
e_week_view_cursor_key_up (EWeekView *week_view)
|
|
{
|
|
EWeekViewClass *week_view_class;
|
|
|
|
week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
|
|
g_return_if_fail (week_view_class->cursor_key_up != NULL);
|
|
|
|
week_view_class->cursor_key_up (week_view);
|
|
}
|
|
|
|
static void
|
|
e_week_view_cursor_key_down (EWeekView *week_view)
|
|
{
|
|
EWeekViewClass *week_view_class;
|
|
|
|
week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
|
|
g_return_if_fail (week_view_class->cursor_key_down != NULL);
|
|
|
|
week_view_class->cursor_key_down (week_view);
|
|
}
|
|
|
|
static void
|
|
e_week_view_cursor_key_left (EWeekView *week_view)
|
|
{
|
|
EWeekViewClass *week_view_class;
|
|
|
|
week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
|
|
g_return_if_fail (week_view_class->cursor_key_left != NULL);
|
|
|
|
week_view_class->cursor_key_left (week_view);
|
|
}
|
|
|
|
static void
|
|
e_week_view_cursor_key_right (EWeekView *week_view)
|
|
{
|
|
EWeekViewClass *week_view_class;
|
|
|
|
week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
|
|
g_return_if_fail (week_view_class->cursor_key_right != NULL);
|
|
|
|
week_view_class->cursor_key_right (week_view);
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_add_new_event_in_selected_range (EWeekView *week_view,
|
|
const gchar *initial_text)
|
|
{
|
|
ECalClient *client;
|
|
ECalModel *model;
|
|
ECalComponent *comp;
|
|
icalcomponent *icalcomp;
|
|
gint event_num;
|
|
ECalComponentDateTime date;
|
|
struct icaltimetype itt;
|
|
time_t dtstart, dtend;
|
|
const gchar *uid;
|
|
AddEventData add_event_data;
|
|
EWeekViewEvent *wvevent;
|
|
EWeekViewEventSpan *span;
|
|
|
|
/* Check if the client is read only */
|
|
model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
|
|
client = e_cal_model_get_default_client (model);
|
|
if (e_client_is_readonly (E_CLIENT (client)))
|
|
return FALSE;
|
|
|
|
/* Add a new event covering the selected range. */
|
|
icalcomp = e_cal_model_create_component_with_defaults (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), TRUE);
|
|
if (!icalcomp)
|
|
return FALSE;
|
|
uid = icalcomponent_get_uid (icalcomp);
|
|
|
|
comp = e_cal_component_new ();
|
|
e_cal_component_set_icalcomponent (comp, icalcomp);
|
|
|
|
dtstart = week_view->day_starts[week_view->selection_start_day];
|
|
dtend = week_view->day_starts[week_view->selection_end_day + 1];
|
|
|
|
date.value = &itt;
|
|
date.tzid = NULL;
|
|
|
|
/* We use DATE values now, so we don't need the timezone. */
|
|
/*date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));*/
|
|
|
|
*date.value = icaltime_from_timet_with_zone (dtstart, TRUE,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
e_cal_component_set_dtstart (comp, &date);
|
|
|
|
*date.value = icaltime_from_timet_with_zone (dtend, TRUE,
|
|
e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
|
|
e_cal_component_set_dtend (comp, &date);
|
|
|
|
/* Editor default in week/month view */
|
|
e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
|
|
|
|
e_cal_component_set_categories (
|
|
comp, e_calendar_view_get_default_category (E_CALENDAR_VIEW (week_view)));
|
|
|
|
/* We add the event locally and start editing it. We don't send it
|
|
* to the server until the user finishes editing it. */
|
|
add_event_data.week_view = week_view;
|
|
add_event_data.comp_data = NULL;
|
|
e_week_view_add_event (comp, dtstart, dtend, TRUE, &add_event_data);
|
|
e_week_view_check_layout (week_view);
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
|
|
if (!e_week_view_find_event_from_uid (week_view, client, uid, NULL, &event_num)) {
|
|
g_warning ("Couldn't find event to start editing.\n");
|
|
g_object_unref (comp);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, event_num))
|
|
return FALSE;
|
|
|
|
wvevent = &g_array_index (week_view->events, EWeekViewEvent,
|
|
event_num);
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, wvevent->spans_index + 0))
|
|
return FALSE;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan,
|
|
wvevent->spans_index + 0);
|
|
|
|
/* If the event can't be fit on the screen, don't try to edit it. */
|
|
if (!span->text_item) {
|
|
e_week_view_foreach_event_with_uid (week_view, uid,
|
|
e_week_view_remove_event_cb, NULL);
|
|
g_object_unref (comp);
|
|
return FALSE;
|
|
} else {
|
|
e_week_view_start_editing_event (week_view, event_num, 0,
|
|
(gchar *) initial_text);
|
|
}
|
|
|
|
g_object_unref (comp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_do_key_press (GtkWidget *widget,
|
|
GdkEventKey *event)
|
|
{
|
|
EWeekView *week_view;
|
|
gchar *initial_text;
|
|
guint keyval;
|
|
gboolean stop_emission;
|
|
gboolean ret_val;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
week_view = E_WEEK_VIEW (widget);
|
|
keyval = event->keyval;
|
|
|
|
/* The Escape key aborts a resize operation. */
|
|
#if 0
|
|
if (week_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
|
|
if (event->keyval == GDK_KEY_Escape) {
|
|
e_week_view_abort_resize (week_view, event->time);
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
/* Handle the cursor keys for moving the selection */
|
|
stop_emission = FALSE;
|
|
if (!(event->state & GDK_SHIFT_MASK)
|
|
&& !(event->state & GDK_MOD1_MASK)) {
|
|
stop_emission = TRUE;
|
|
switch (keyval) {
|
|
case GDK_KEY_Page_Up:
|
|
if (!week_view->multi_week_view)
|
|
e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_UP);
|
|
else
|
|
e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_PAGE_UP);
|
|
break;
|
|
case GDK_KEY_Page_Down:
|
|
if (!week_view->multi_week_view)
|
|
e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_DOWN);
|
|
else
|
|
e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_PAGE_DOWN);
|
|
break;
|
|
case GDK_KEY_Up:
|
|
e_week_view_cursor_key_up (week_view);
|
|
break;
|
|
case GDK_KEY_Down:
|
|
e_week_view_cursor_key_down (week_view);
|
|
break;
|
|
case GDK_KEY_Left:
|
|
e_week_view_cursor_key_left (week_view);
|
|
break;
|
|
case GDK_KEY_Right:
|
|
e_week_view_cursor_key_right (week_view);
|
|
break;
|
|
default:
|
|
stop_emission = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (stop_emission)
|
|
return TRUE;
|
|
|
|
/*Navigation through days with arrow keys*/
|
|
if (((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK)
|
|
&&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
|
|
&&((event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK)) {
|
|
if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up)
|
|
return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_UP);
|
|
else if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
|
|
return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_DOWN);
|
|
else if (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left)
|
|
return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_LEFT);
|
|
else if (keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right)
|
|
return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_RIGHT);
|
|
}
|
|
|
|
if (week_view->selection_start_day == -1)
|
|
return FALSE;
|
|
|
|
/* We only want to start an edit with a return key or a simple
|
|
* character. */
|
|
if (event->keyval == GDK_KEY_Return) {
|
|
initial_text = NULL;
|
|
} else if (((event->keyval >= 0x20) && (event->keyval <= 0xFF)
|
|
&& (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
|
|
|| (event->length == 0)
|
|
|| (event->keyval == GDK_KEY_Tab)) {
|
|
return FALSE;
|
|
} else
|
|
initial_text = e_utf8_from_gtk_event_key (widget, event->keyval, event->string);
|
|
|
|
ret_val = e_week_view_add_new_event_in_selected_range (week_view, initial_text);
|
|
|
|
if (initial_text)
|
|
g_free (initial_text);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
static gint
|
|
e_week_view_get_adjust_days_for_move_up (EWeekView *week_view,
|
|
gint current_day)
|
|
{
|
|
if (week_view->multi_week_view)
|
|
return -7;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static gint
|
|
e_week_view_get_adjust_days_for_move_down (EWeekView *week_view,
|
|
gint current_day)
|
|
{
|
|
if (week_view->multi_week_view)
|
|
return 7;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static gint
|
|
e_week_view_get_adjust_days_for_move_left (EWeekView *week_view,
|
|
gint current_day)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
static gint
|
|
e_week_view_get_adjust_days_for_move_right (EWeekView *week_view,
|
|
gint current_day)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_key_press (GtkWidget *widget,
|
|
GdkEventKey *event)
|
|
{
|
|
gboolean handled = FALSE;
|
|
handled = e_week_view_do_key_press (widget, event);
|
|
|
|
/* if not handled, try key bindings */
|
|
if (!handled)
|
|
handled = GTK_WIDGET_CLASS (e_week_view_parent_class)->key_press_event (widget, event);
|
|
return handled;
|
|
}
|
|
|
|
void
|
|
e_week_view_show_popup_menu (EWeekView *week_view,
|
|
GdkEventButton *bevent,
|
|
gint event_num)
|
|
{
|
|
week_view->popup_event_num = event_num;
|
|
|
|
e_calendar_view_popup_event (E_CALENDAR_VIEW (week_view), bevent);
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_popup_menu (GtkWidget *widget)
|
|
{
|
|
EWeekView *week_view = E_WEEK_VIEW (widget);
|
|
e_week_view_show_popup_menu (week_view, NULL,
|
|
week_view->editing_event_num);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
e_week_view_jump_to_button_item (EWeekView *week_view,
|
|
GnomeCanvasItem *item)
|
|
{
|
|
gint day;
|
|
GnomeCalendar *calendar;
|
|
|
|
for (day = 0; day < E_WEEK_VIEW_MAX_WEEKS * 7; ++day) {
|
|
if (item == week_view->jump_buttons[day]) {
|
|
calendar = e_calendar_view_get_calendar (E_CALENDAR_VIEW (week_view));
|
|
if (calendar)
|
|
gnome_calendar_dayjump
|
|
(calendar,
|
|
week_view->day_starts[day]);
|
|
else
|
|
g_warning ("Calendar not set");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_on_jump_button_event (GnomeCanvasItem *item,
|
|
GdkEvent *event,
|
|
EWeekView *week_view)
|
|
{
|
|
gint day;
|
|
|
|
if (event->type == GDK_BUTTON_PRESS) {
|
|
e_week_view_jump_to_button_item (week_view, item);
|
|
return TRUE;
|
|
}
|
|
else if (event->type == GDK_KEY_PRESS) {
|
|
/* return, if Tab, Control or Alt is pressed */
|
|
if ((event->key.keyval == GDK_KEY_Tab) ||
|
|
(event->key.state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
|
|
return FALSE;
|
|
/* with a return key or a simple character (from 0x20 to 0xff),
|
|
* jump to the day
|
|
*/
|
|
if ((event->key.keyval == GDK_KEY_Return) ||
|
|
((event->key.keyval >= 0x20) &&
|
|
(event->key.keyval <= 0xFF))) {
|
|
e_week_view_jump_to_button_item (week_view, item);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (event->type == GDK_FOCUS_CHANGE) {
|
|
GdkEventFocus *focus_event = (GdkEventFocus *) event;
|
|
GdkPixbuf *pixbuf = NULL;
|
|
|
|
for (day = 0; day < E_WEEK_VIEW_MAX_WEEKS * 7; day++) {
|
|
if (item == week_view->jump_buttons[day])
|
|
break;
|
|
}
|
|
|
|
if (focus_event->in) {
|
|
week_view->focused_jump_button = day;
|
|
pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) jump_xpm_focused);
|
|
gnome_canvas_item_set (week_view->jump_buttons[day],
|
|
"GnomeCanvasPixbuf::pixbuf",
|
|
pixbuf, NULL);
|
|
}
|
|
else {
|
|
week_view->focused_jump_button = E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS;
|
|
pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) jump_xpm);
|
|
gnome_canvas_item_set (week_view->jump_buttons[day],
|
|
"GnomeCanvasPixbuf::pixbuf",
|
|
pixbuf, NULL);
|
|
}
|
|
if (pixbuf)
|
|
g_object_unref (pixbuf);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Converts an hour from 0-23 to the preferred time format, and returns the
|
|
* suffix to add and the width of it in the normal font. */
|
|
void
|
|
e_week_view_convert_time_to_display (EWeekView *week_view,
|
|
gint hour,
|
|
gint *display_hour,
|
|
const gchar **suffix,
|
|
gint *suffix_width)
|
|
{
|
|
ECalModel *model;
|
|
|
|
model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
|
|
|
|
/* Calculate the actual hour number to display. For 12-hour
|
|
* format we convert 0-23 to 12-11am/12-11pm. */
|
|
*display_hour = hour;
|
|
if (e_cal_model_get_use_24_hour_format (model)) {
|
|
*suffix = "";
|
|
*suffix_width = 0;
|
|
} else {
|
|
if (hour < 12) {
|
|
*suffix = week_view->am_string;
|
|
*suffix_width = week_view->am_string_width;
|
|
} else {
|
|
*display_hour -= 12;
|
|
*suffix = week_view->pm_string;
|
|
*suffix_width = week_view->pm_string_width;
|
|
}
|
|
|
|
/* 12-hour uses 12:00 rather than 0:00. */
|
|
if (*display_hour == 0)
|
|
*display_hour = 12;
|
|
}
|
|
}
|
|
|
|
gint
|
|
e_week_view_get_time_string_width (EWeekView *week_view)
|
|
{
|
|
ECalModel *model;
|
|
gint time_width;
|
|
|
|
model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
|
|
|
|
if (week_view->use_small_font && week_view->small_font_desc)
|
|
time_width = week_view->digit_width * 2
|
|
+ week_view->small_digit_width * 2;
|
|
else
|
|
time_width = week_view->digit_width * 4
|
|
+ week_view->colon_width;
|
|
|
|
if (!e_cal_model_get_use_24_hour_format (model))
|
|
time_width += MAX (week_view->am_string_width,
|
|
week_view->pm_string_width);
|
|
|
|
return time_width;
|
|
}
|
|
|
|
/* Queues a layout, unless one is already queued. */
|
|
static void
|
|
e_week_view_queue_layout (EWeekView *week_view)
|
|
{
|
|
if (week_view->layout_timeout_id == 0) {
|
|
week_view->layout_timeout_id = g_timeout_add (E_WEEK_VIEW_LAYOUT_TIMEOUT, e_week_view_layout_timeout_cb, week_view);
|
|
}
|
|
}
|
|
|
|
/* Removes any queued layout. */
|
|
static void
|
|
e_week_view_cancel_layout (EWeekView *week_view)
|
|
{
|
|
if (week_view->layout_timeout_id != 0) {
|
|
g_source_remove (week_view->layout_timeout_id);
|
|
week_view->layout_timeout_id = 0;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
e_week_view_layout_timeout_cb (gpointer data)
|
|
{
|
|
EWeekView *week_view = E_WEEK_VIEW (data);
|
|
|
|
gtk_widget_queue_draw (week_view->main_canvas);
|
|
e_week_view_check_layout (week_view);
|
|
|
|
week_view->layout_timeout_id = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Returns the number of selected events (0 or 1 at present). */
|
|
gint
|
|
e_week_view_get_num_events_selected (EWeekView *week_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), 0);
|
|
|
|
return (week_view->editing_event_num != -1) ? 1 : 0;
|
|
}
|
|
|
|
gboolean
|
|
e_week_view_is_jump_button_visible (EWeekView *week_view,
|
|
gint day)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
|
|
|
|
if ((day >= 0) && (day < E_WEEK_VIEW_MAX_WEEKS * 7))
|
|
return week_view->jump_buttons[day]->flags & GNOME_CANVAS_ITEM_VISIBLE;
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
e_week_view_paste_text (ECalendarView *cal_view)
|
|
{
|
|
EWeekViewEvent *event;
|
|
EWeekViewEventSpan *span;
|
|
EWeekView *week_view;
|
|
|
|
g_return_if_fail (E_IS_WEEK_VIEW (cal_view));
|
|
|
|
week_view = E_WEEK_VIEW (cal_view);
|
|
|
|
if (week_view->editing_event_num == -1 &&
|
|
!e_week_view_add_new_event_in_selected_range (week_view, NULL))
|
|
return;
|
|
|
|
if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
|
|
return;
|
|
|
|
event = &g_array_index (week_view->events, EWeekViewEvent,
|
|
week_view->editing_event_num);
|
|
|
|
if (!is_array_index_in_bounds (week_view->spans, event->spans_index + week_view->editing_span_num))
|
|
return;
|
|
|
|
span = &g_array_index (week_view->spans, EWeekViewEventSpan,
|
|
event->spans_index + week_view->editing_span_num);
|
|
|
|
if (span->text_item &&
|
|
E_IS_TEXT (span->text_item) &&
|
|
E_TEXT (span->text_item)->editing) {
|
|
e_text_paste_clipboard (E_TEXT (span->text_item));
|
|
}
|
|
}
|