1 /*
2  *
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful, but
8  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
10  * for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public License
13  * along with this program; if not, see <http://www.gnu.org/licenses/>.
14  *
15  *
16  * Authors:
17  *		Damon Chaplin <damon@ximian.com>
18  *
19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20  *
21  */
22 
23 #ifndef E_DAY_VIEW_H
24 #define E_DAY_VIEW_H
25 
26 #include <time.h>
27 #include <gtk/gtk.h>
28 #include <libgnomecanvas/libgnomecanvas.h>
29 
30 #include "e-calendar-view.h"
31 
32 /*
33  * EDayView - displays the Day & Work-Week views of the calendar.
34  */
35 
36 /* Standard GObject macros */
37 #define E_TYPE_DAY_VIEW \
38 	(e_day_view_get_type ())
39 #define E_DAY_VIEW(obj) \
40 	(G_TYPE_CHECK_INSTANCE_CAST \
41 	((obj), E_TYPE_DAY_VIEW, EDayView))
42 #define E_DAY_VIEW_CLASS(cls) \
43 	(G_TYPE_CHECK_CLASS_CAST \
44 	((cls), E_TYPE_DAY_VIEW, EDayViewClass))
45 #define E_IS_DAY_VIEW(obj) \
46 	(G_TYPE_CHECK_INSTANCE_TYPE \
47 	((obj), E_TYPE_DAY_VIEW))
48 #define E_IS_DAY_VIEW_CLASS(cls) \
49 	(G_TYPE_CHECK_CLASS_TYPE \
50 	((cls), E_TYPE_DAY_VIEW))
51 #define E_DAY_VIEW_GET_CLASS(obj) \
52 	(G_TYPE_INSTANCE_GET_CLASS \
53 	((obj), E_TYPE_DAY_VIEW, EDayViewClass))
54 
55 /* The maximum number of days shown. We use the week view for anything more
56  * than about 9 days. */
57 #define E_DAY_VIEW_MAX_DAYS		10
58 
59 /* This is used as a special code to signify a long event instead of the day
60  * of a normal event. */
61 #define E_DAY_VIEW_LONG_EVENT		E_DAY_VIEW_MAX_DAYS
62 
63 /* The maximum number of columns of appointments within a day in multi-day
64  * view. */
65 #define E_DAY_VIEW_MULTI_DAY_MAX_COLUMNS 6
66 
67 /* minimum width of the event in one-day view in pixels */
68 #define E_DAY_VIEW_MIN_DAY_COL_WIDTH	60
69 
70 /* The width of the gap between appointments. This should be at least
71  * E_DAY_VIEW_BAR_WIDTH, since in the top canvas we use this space to draw
72  * the triangle to represent continuing events. */
73 #define E_DAY_VIEW_GAP_WIDTH		7
74 
75 /* The width of the bars down the left of each column and appointment.
76  * This includes the borders on each side of it. */
77 #define E_DAY_VIEW_BAR_WIDTH		7
78 
79 /* The height of the horizontal bar above & beneath the selected event.
80  * This includes the borders on the top and bottom. */
81 #define E_DAY_VIEW_BAR_HEIGHT		6
82 
83 /* The size of the reminder & recurrence icons, and padding around them. */
84 #define E_DAY_VIEW_ICON_WIDTH		16
85 #define E_DAY_VIEW_ICON_HEIGHT		16
86 #define E_DAY_VIEW_ICON_X_PAD		1
87 #define E_DAY_VIEW_ICON_Y_PAD		1
88 
89 /* The space between the icons and the long event text. */
90 #define E_DAY_VIEW_LONG_EVENT_ICON_R_PAD	1
91 
92 /* The size of the border around the event. */
93 #define E_DAY_VIEW_EVENT_BORDER_WIDTH	1
94 #define E_DAY_VIEW_EVENT_BORDER_HEIGHT	1
95 
96 /* The padding on each side of the event text. */
97 #define E_DAY_VIEW_EVENT_X_PAD		2
98 #define E_DAY_VIEW_EVENT_Y_PAD		1
99 
100 /* The padding on each side of the event text for events in the top canvas. */
101 #define E_DAY_VIEW_LONG_EVENT_X_PAD	2
102 #define E_DAY_VIEW_LONG_EVENT_Y_PAD	2
103 
104 /* The size of the border around the long events in the top canvas. */
105 #define E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH	1
106 #define E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT	1
107 
108 /* The space between the time and the icon/text in the top canvas. */
109 #define E_DAY_VIEW_LONG_EVENT_TIME_X_PAD	2
110 
111 /* The gap between rows in the top canvas. */
112 #define E_DAY_VIEW_TOP_CANVAS_Y_GAP	2
113 
114 G_BEGIN_DECLS
115 
116 /* These are used to specify the type of an appointment. They match those
117  * used in EMeetingTimeSelector. */
118 typedef enum {
119 	E_DAY_VIEW_BUSY_TENTATIVE = 0,
120 	E_DAY_VIEW_BUSY_OUT_OF_OFFICE = 1,
121 	E_DAY_VIEW_BUSY_BUSY = 2,
122 
123 	E_DAY_VIEW_BUSY_LAST = 3
124 } EDayViewBusyType;
125 
126 /* This is used to specify the format used when displaying the dates.
127  * The full format is like 'Thursday 12 September'. The abbreviated format is
128  * like 'Thu 12 Sep'. The no weekday format is like '12 Sep'. The short format
129  * is like '12'. The actual format used is determined in
130  * e_day_view_recalc_cell_sizes (), once we know the font being used. */
131 typedef enum {
132 	E_DAY_VIEW_DATE_FULL,
133 	E_DAY_VIEW_DATE_ABBREVIATED,
134 	E_DAY_VIEW_DATE_NO_WEEKDAY,
135 	E_DAY_VIEW_DATE_SHORT
136 } EDayViewDateFormat;
137 
138 /* These index our colors array. */
139 typedef enum {
140 	E_DAY_VIEW_COLOR_BG_WORKING,
141 	E_DAY_VIEW_COLOR_BG_NOT_WORKING,
142 	E_DAY_VIEW_COLOR_BG_SELECTED,
143 	E_DAY_VIEW_COLOR_BG_SELECTED_UNFOCUSSED,
144 	E_DAY_VIEW_COLOR_BG_GRID,
145 	E_DAY_VIEW_COLOR_BG_MULTIDAY_TODAY,
146 
147 	E_DAY_VIEW_COLOR_BG_TOP_CANVAS,
148 	E_DAY_VIEW_COLOR_BG_TOP_CANVAS_SELECTED,
149 	E_DAY_VIEW_COLOR_BG_TOP_CANVAS_GRID,
150 
151 	E_DAY_VIEW_COLOR_EVENT_VBAR,
152 	E_DAY_VIEW_COLOR_EVENT_BACKGROUND,
153 	E_DAY_VIEW_COLOR_EVENT_BORDER,
154 
155 	E_DAY_VIEW_COLOR_LONG_EVENT_BACKGROUND,
156 	E_DAY_VIEW_COLOR_LONG_EVENT_BORDER,
157 
158 	E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE,
159 
160 	E_DAY_VIEW_COLOR_LAST
161 } EDayViewColors;
162 
163 /* These specify which part of the selection we are dragging, if any. */
164 typedef enum {
165 	E_DAY_VIEW_DRAG_START,
166 	E_DAY_VIEW_DRAG_END
167 } EDayViewDragPosition;
168 
169 typedef struct _EDayViewEvent EDayViewEvent;
170 struct _EDayViewEvent {
171 	E_CALENDAR_VIEW_EVENT_FIELDS
172 
173 	/* For events in the main canvas, this contains the start column.
174 	 * For long events in the top canvas, this is its row. */
175 	guint8 start_row_or_col;
176 
177 	/* For events in the main canvas, this is the number of columns that
178 	 * it covers. For long events this is set to 1 if the event is shown.
179 	 * For both types of events this is set to 0 if the event is not shown,
180 	 * i.e. it couldn't fit into the display. Currently long events are
181 	 * always shown as we just increase the height of the top canvas. */
182 	guint8 num_columns;
183 };
184 
185 typedef struct _EDayView EDayView;
186 typedef struct _EDayViewClass EDayViewClass;
187 typedef struct _EDayViewPrivate EDayViewPrivate;
188 
189 struct _EDayView {
190 	ECalendarView parent;
191 	EDayViewPrivate *priv;
192 
193 	/* The top canvas where the dates are shown. */
194 	GtkWidget *top_dates_canvas;
195 	GnomeCanvasItem *top_dates_canvas_item;
196 
197 	/* The top canvas where the dates and long appointments are shown. */
198 	GtkWidget *top_canvas;
199 	GnomeCanvasItem *top_canvas_item;
200 
201 	/* scrollbar for top_canvas */
202 	GtkWidget *tc_vscrollbar;
203 
204 	/* horizontal scrollbar for main_canvas */
205 	GtkWidget *mc_hscrollbar;
206 
207 	/* The main canvas where the rest of the appointments are shown. */
208 	GtkWidget *main_canvas;
209 	GnomeCanvasItem *main_canvas_item;
210 
211 	/* The canvas displaying the times of the day. */
212 	GtkWidget *time_canvas;
213 	GnomeCanvasItem *time_canvas_item;
214 
215 	GtkWidget *vscrollbar;
216 
217 	/* label showing week number in upper-left corner */
218 	GtkWidget *week_number_label;
219 
220 	/* The start and end of the days shown. */
221 	time_t lower;
222 	time_t upper;
223 
224 	/* The start of each day & an extra element to hold the last time. */
225 	time_t day_starts[E_DAY_VIEW_MAX_DAYS + 1];
226 
227 	/* An array of EDayViewEvent elements for the top view and each day. */
228 	GArray *long_events;
229 	GArray *events[E_DAY_VIEW_MAX_DAYS];
230 
231 	/* These are set to FALSE whenever an event in the corresponding array
232 	 * is changed. Any function that needs the events sorted calls
233 	 * e_day_view_ensure_events_sorted (). */
234 	gboolean long_events_sorted;
235 	gboolean events_sorted[E_DAY_VIEW_MAX_DAYS];
236 
237 	/* This is TRUE if we need to relayout the events before drawing. */
238 	gboolean long_events_need_layout;
239 	gboolean need_layout[E_DAY_VIEW_MAX_DAYS];
240 
241 	/* This is TRUE if we need to reshape the canvas items, but a full
242 	 * layout is not necessary. */
243 	gboolean long_events_need_reshape;
244 	gboolean need_reshape[E_DAY_VIEW_MAX_DAYS];
245 
246 	/* The ID of the timeout function for doing a new layout. */
247 	gint layout_timeout_id;
248 
249 	/* The number of rows needed, depending on the times shown and the
250 	 * minutes per row. */
251 	gint rows;
252 
253 	/* The height of each row. */
254 	gint row_height;
255 
256 	/* The number of rows in the top display. */
257 	gint rows_in_top_display;
258 
259 	/* The height of each row in the top canvas. */
260 	gint top_row_height;
261 
262 	/* The first and last times shown in the display. The last time isn't
263 	 * included in the range. Default is 0:00-24:00 */
264 	gint first_hour_shown;
265 	gint first_minute_shown;
266 	gint last_hour_shown;
267 	gint last_minute_shown;
268 
269 	/* Whether we use show event end times in the main canvas. */
270 	gboolean show_event_end_times;
271 
272 	/* This is set to TRUE when the widget is created, so it scrolls to
273 	 * the start of the working day when first shown. */
274 	gboolean scroll_to_work_day;
275 
276 	/* This is the width & offset of each of the day columns in the
277 	 * display. */
278 	gint day_widths[E_DAY_VIEW_MAX_DAYS + 1];
279 	gint day_offsets[E_DAY_VIEW_MAX_DAYS + 1];
280 
281 	/* An array holding the number of columns in each row, in each day.
282 	 * Note that there are a maximum of 12 * 24 rows (when a row is 5 mins)
283 	 * but we don't always have that many rows. */
284 	guint8 cols_per_row[E_DAY_VIEW_MAX_DAYS][12 * 24];
285 	/* The maximum number of columns from all rows in cols_per_row */
286 	gint max_cols;
287 
288 	/* Sizes of the various time strings. */
289 	gint small_hour_widths[24];
290 	gint max_small_hour_width;
291 	gint max_minute_width;
292 	gint colon_width;
293 	gint digit_width;	/* Size of '0' character. */
294 
295 	/* This specifies how we are displaying the dates at the top. */
296 	EDayViewDateFormat date_format;
297 
298 	/* These are the longest month & weekday names in the current font.
299 	 * Months are 0 to 11. Weekdays are 0 (Sun) to 6 (Sat). */
300 	gint longest_month_name;
301 	gint longest_abbreviated_month_name;
302 	gint longest_weekday_name;
303 	gint longest_abbreviated_weekday_name;
304 
305 	/* The large font used to display the hours. I don't think we need a
306 	 * fontset since we only display numbers. */
307 	PangoFontDescription *large_font_desc;
308 	PangoFontDescription *small_font_desc;
309 
310 	/* The icons. */
311 	GdkPixbuf *reminder_icon;
312 	GdkPixbuf *recurrence_icon;
313 	GdkPixbuf *timezone_icon;
314 	GdkPixbuf *meeting_icon;
315 	GdkPixbuf *attach_icon;
316 
317 	/* Colors for drawing. */
318 	GdkColor colors[E_DAY_VIEW_COLOR_LAST];
319 
320 	/* The normal & resizing cursors. */
321 	GdkCursor *normal_cursor;
322 	GdkCursor *move_cursor;
323 	GdkCursor *resize_width_cursor;
324 	GdkCursor *resize_height_cursor;
325 
326 	/* This remembers the last cursor set on the window. */
327 	GdkCursor *last_cursor_set_in_top_canvas;
328 	GdkCursor *last_cursor_set_in_main_canvas;
329 
330 	/*
331 	 * Editing, Selection & Dragging data
332 	 */
333 
334 	/* The horizontal bars to resize events in the main canvas. */
335 	GnomeCanvasItem *main_canvas_top_resize_bar_item;
336 	GnomeCanvasItem *main_canvas_bottom_resize_bar_item;
337 
338 	/* The event currently being edited. The day is -1 if no event is
339 	 being edited, or E_DAY_VIEW_LONG_EVENT if a long event is edited. */
340 	gint editing_event_day;
341 	gint editing_event_num;
342 
343 	/* This is a GnomeCanvasRect which is placed around an item while it
344 	 * is being resized, so we can raise it above all other EText items. */
345 	GnomeCanvasItem *resize_long_event_rect_item;
346 	GnomeCanvasItem *resize_rect_item;
347 	GnomeCanvasItem *resize_bar_item;
348 
349 	/* The event for which a popup menu is being displayed, as above. */
350 	gint popup_event_day;
351 	gint popup_event_num;
352 
353 	/* The currently selected region. If selection_start_day is -1 there is
354 	 * no current selection. If start_row or end_row is -1 then the
355 	 * selection is in the top canvas. */
356 	gint selection_start_day;
357 	gint selection_end_day;
358 	gint selection_start_row;
359 	gint selection_end_row;
360 
361 	/* This is TRUE if the selection is currently being dragged using the
362 	 * mouse. */
363 	gboolean selection_is_being_dragged;
364 
365 	/* This specifies which end of the selection is being dragged. */
366 	EDayViewDragPosition selection_drag_pos;
367 
368 	/* This is TRUE if the selection is in the top canvas only (i.e. if the
369 	 * last motion event was in the top canvas). */
370 	gboolean selection_in_top_canvas;
371 
372 	/* The last mouse position, relative to the main canvas window.
373 	 * Used when auto-scrolling to update the selection. */
374 	gint last_mouse_x;
375 	gint last_mouse_y;
376 
377 	/* Auto-scroll info for when selecting an area or dragging an item. */
378 	gint auto_scroll_timeout_id;
379 	gint auto_scroll_delay;
380 	gboolean auto_scroll_up;
381 
382 	/* These are used for the resize bars. */
383 	gint resize_bars_event_day;
384 	gint resize_bars_event_num;
385 
386 	/* These are used when resizing events. */
387 	gint resize_event_day;
388 	gint resize_event_num;
389 	ECalendarViewPosition resize_drag_pos;
390 	gint resize_start_row;
391 	gint resize_end_row;
392 
393 	/* This is used to remember the last edited event. */
394 	gchar *last_edited_comp_string;
395 
396 	/* This is the event the mouse button was pressed on. If the button
397 	 * is released we start editing it, but if the mouse is dragged we set
398 	 * this to -1. */
399 	gint pressed_event_day;
400 	gint pressed_event_num;
401 
402 	/* These are used when dragging events. If drag_event_day is not -1 we
403 	 * know that we are dragging one of the EDayView events around. */
404 	gint drag_event_day;
405 	gint drag_event_num;
406 
407 	/* The last mouse position when dragging, in the entire canvas. */
408 	gint drag_event_x;
409 	gint drag_event_y;
410 
411 	/* The offset of the mouse from the top of the event, in rows.
412 	 * In the top canvas this is the offset from the left, in days. */
413 	gint drag_event_offset;
414 
415 	/* The last day & row dragged to, so we know when we need to update
416 	 * the dragged event's position. */
417 	gint drag_last_day;
418 	gint drag_last_row;
419 
420 	/* This is a GnomeCanvasRect which is placed around an item while it
421 	 * is being resized, so we can raise it above all other EText items. */
422 	GnomeCanvasItem *drag_long_event_rect_item;
423 	GnomeCanvasItem *drag_long_event_item;
424 	GnomeCanvasItem *drag_rect_item;
425 	GnomeCanvasItem *drag_bar_item;
426 	GnomeCanvasItem *drag_item;
427 
428 	/* Grabbed pointer device while dragging. */
429 	GdkDevice *grabbed_pointer;
430 
431 	/* "am" and "pm" in the current locale, and their widths. */
432 	gchar *am_string;
433 	gchar *pm_string;
434 	gint am_string_width;
435 	gint pm_string_width;
436 
437 	/* remember last selected interval when click and restore on double click,
438 	 * if we double clicked inside that interval. */
439 	guint32 bc_event_time;
440 	time_t before_click_dtstart;
441 	time_t before_click_dtend;
442 
443 	gboolean requires_update;
444 };
445 
446 struct _EDayViewClass {
447 	ECalendarViewClass parent_class;
448 };
449 
450 GType		e_day_view_get_type		(void) G_GNUC_CONST;
451 ECalendarView *	e_day_view_new			(ECalModel *model);
452 
453 gboolean	e_day_view_get_draw_flat_events	(EDayView *day_view);
454 void		e_day_view_set_draw_flat_events	(EDayView *day_view,
455 						 gboolean draw_flat_events);
456 
457 /* Whether we are displaying a work-week, in which case the display always
458  * starts on the first day of the working week. */
459 gboolean	e_day_view_get_work_week_view	(EDayView *day_view);
460 void		e_day_view_set_work_week_view	(EDayView *day_view,
461 						 gboolean  work_week_view);
462 
463 /* The number of days shown in the EDayView, from 1 to 7. This is normally
464  * either 1 or 5 (for the Work-Week view). */
465 gint		e_day_view_get_days_shown	(EDayView *day_view);
466 void		e_day_view_set_days_shown	(EDayView *day_view,
467 						 gint days_shown);
468 
469 /* Whether we display the Marcus Bains Line in the main canvas and time
470  * canvas. */
471 void		e_day_view_marcus_bains_update	(EDayView *day_view);
472 gboolean	e_day_view_marcus_bains_get_show_line
473 						(EDayView *day_view);
474 void		e_day_view_marcus_bains_set_show_line
475 						(EDayView *day_view,
476 						 gboolean show_line);
477 const gchar *	e_day_view_marcus_bains_get_day_view_color
478 						(EDayView *day_view);
479 void		e_day_view_marcus_bains_set_day_view_color
480 						(EDayView *day_view,
481 						 const gchar *day_view_color);
482 const gchar *	e_day_view_marcus_bains_get_time_bar_color
483 						(EDayView *day_view);
484 void		e_day_view_marcus_bains_set_time_bar_color
485 						(EDayView *day_view,
486 						 const gchar *time_bar_color);
487 
488 /* Whether we display event end times in the main canvas. */
489 gboolean	e_day_view_get_show_event_end_times
490 						(EDayView *day_view);
491 void		e_day_view_set_show_event_end_times
492 						(EDayView *day_view,
493 						 gboolean show);
494 
495 void		e_day_view_delete_occurrence	(EDayView *day_view);
496 
497 /* Returns the number of selected events (0 or 1 at present). */
498 gint		e_day_view_get_num_events_selected
499 						(EDayView *day_view);
500 
501 /*
502  * Internal functions called by the associated canvas items.
503  */
504 void		e_day_view_check_layout		(EDayView *day_view);
505 gint		e_day_view_convert_time_to_row	(EDayView *day_view,
506 						 gint hour,
507 						 gint minute);
508 gint		e_day_view_convert_time_to_position
509 						(EDayView *day_view,
510 						 gint hour,
511 						 gint minute);
512 gboolean	e_day_view_get_event_rows	(EDayView *day_view,
513 						 gint day,
514 						 gint event_num,
515 						 gint *start_row_out,
516 						 gint *end_row_out);
517 gboolean	e_day_view_get_event_position	(EDayView *day_view,
518 						 gint day,
519 						 gint event_num,
520 						 gint *item_x,
521 						 gint *item_y,
522 						 gint *item_w,
523 						 gint *item_h);
524 gboolean	e_day_view_get_long_event_position
525 						(EDayView *day_view,
526 						 gint event_num,
527 						 gint *start_day,
528 						 gint *end_day,
529 						 gint *item_x,
530 						 gint *item_y,
531 						 gint *item_w,
532 						 gint *item_h);
533 
534 void		e_day_view_start_selection	(EDayView *day_view,
535 						 gint day,
536 						 gint row);
537 void		e_day_view_update_selection	(EDayView *day_view,
538 						 gint day,
539 						 gint row);
540 void		e_day_view_finish_selection	(EDayView *day_view);
541 
542 void		e_day_view_check_auto_scroll	(EDayView *day_view,
543 						 gint event_x,
544 						 gint event_y);
545 void		e_day_view_stop_auto_scroll	(EDayView *day_view);
546 
547 void		e_day_view_convert_time_to_display
548 						(EDayView *day_view,
549 						 gint hour,
550 						 gint *display_hour,
551 						 const gchar **suffix,
552 						 gint *suffix_width);
553 gint		e_day_view_get_time_string_width
554 						(EDayView *day_view);
555 
556 gint		e_day_view_event_sort_func	(gconstpointer arg1,
557 						 gconstpointer arg2);
558 
559 gboolean	e_day_view_find_event_from_item	(EDayView *day_view,
560 						 GnomeCanvasItem *item,
561 						 gint *day_return,
562 						 gint *event_num_return);
563 void		e_day_view_update_calendar_selection_time
564 						(EDayView *day_view);
565 void		e_day_view_ensure_rows_visible	(EDayView *day_view,
566 						 gint start_row,
567 						 gint end_row);
568 
569 gboolean	e_day_view_is_editing		(EDayView *day_view);
570 
571 void		e_day_view_update_timezone_name_labels
572 						(EDayView *day_view);
573 
574 G_END_DECLS
575 
576 #endif /* E_DAY_VIEW_H */
577