No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-meeting-time-sel-item.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-meeting-time-sel-item.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * EMeetingTimeSelectorItem - A GnomeCanvasItem which is used for both the main
3 * display canvas and the top display (with the dates, times & All Attendees).
4 * I didn't make these separate GnomeCanvasItems since they share a lot of
5 * code.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) version 3.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with the program; if not, see <http://www.gnu.org/licenses/>
19 *
20 * Authors:
21 * Damon Chaplin <damon@gtk.org>
22 *
23 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <time.h>
31 #include <glib/gi18n.h>
32
33 #include "e-util/e-datetime-format.h"
34
35 #include "calendar-config.h"
36 #include "e-meeting-time-sel-item.h"
37 #include "e-meeting-time-sel.h"
38
39 /* Initially the grid lines were drawn at the bottom of cells, but this didn't
40 * line up well with the GtkEntry widgets, which in the default theme draw a
41 * black shadow line across the top. So I've switched our code to draw the
42 * lines across the top of cells. */
43 #define E_MEETING_TIME_SELECTOR_DRAW_GRID_LINES_AT_BOTTOM 0
44
45 static void e_meeting_time_selector_item_dispose (GObject *object);
46
47 static void e_meeting_time_selector_item_set_property (GObject *object,
48 guint property_id,
49 const GValue *value,
50 GParamSpec *pspec);
51 static void e_meeting_time_selector_item_update (GnomeCanvasItem *item,
52 const cairo_matrix_t *i2c,
53 gint flags);
54 static void e_meeting_time_selector_item_draw (GnomeCanvasItem *item,
55 cairo_t *cr,
56 gint x, gint y,
57 gint width, gint height);
58 static GnomeCanvasItem *e_meeting_time_selector_item_point (GnomeCanvasItem *item,
59 double x, double y,
60 gint cx, gint cy);
61 static gint e_meeting_time_selector_item_event (GnomeCanvasItem *item,
62 GdkEvent *event);
63 static gint e_meeting_time_selector_item_button_press (EMeetingTimeSelectorItem *mts_item,
64 GdkEvent *event);
65 static gint e_meeting_time_selector_item_button_release (EMeetingTimeSelectorItem *mts_item,
66 GdkEvent *event);
67 static gint e_meeting_time_selector_item_motion_notify (EMeetingTimeSelectorItem *mts_item,
68 GdkEvent *event);
69
70 static void e_meeting_time_selector_item_paint_day_top (EMeetingTimeSelectorItem *mts_item,
71 cairo_t *cr,
72 GDate *date,
73 gint x, gint scroll_y,
74 gint width, gint height);
75 static void e_meeting_time_selector_item_paint_all_attendees_busy_periods (EMeetingTimeSelectorItem *mts_item, cairo_t *cr, GDate *date, gint x, gint y, gint width, gint height);
76 static void e_meeting_time_selector_item_paint_day (EMeetingTimeSelectorItem *mts_item,
77 cairo_t *cr,
78 GDate *date,
79 gint x, gint scroll_y,
80 gint width, gint height);
81 static void e_meeting_time_selector_item_paint_busy_periods (EMeetingTimeSelectorItem *mts_item, cairo_t *cr, GDate *date, gint x, gint scroll_y, gint width, gint height);
82 static gint e_meeting_time_selector_item_find_first_busy_period (EMeetingTimeSelectorItem *mts_item, GDate *date, gint row);
83 static void e_meeting_time_selector_item_paint_attendee_busy_periods (EMeetingTimeSelectorItem *mts_item, cairo_t *cr, gint row, gint x, gint y, gint width, gint first_period, EMeetingFreeBusyType busy_type);
84
85 static EMeetingTimeSelectorPosition e_meeting_time_selector_item_get_drag_position (EMeetingTimeSelectorItem *mts_item, gint x, gint y);
86 static gboolean e_meeting_time_selector_item_calculate_busy_range (EMeetingTimeSelector *mts,
87 gint row,
88 gint x,
89 gint width,
90 gint *start_x,
91 gint *end_x);
92
93 enum {
94 PROP_0,
95 PROP_MEETING_TIME_SELECTOR
96 };
97
98 G_DEFINE_TYPE (EMeetingTimeSelectorItem, e_meeting_time_selector_item, GNOME_TYPE_CANVAS_ITEM)
99
100 static void
101 e_meeting_time_selector_item_class_init (EMeetingTimeSelectorItemClass *class)
102 {
103 GObjectClass *object_class;
104 GnomeCanvasItemClass *item_class;
105
106 object_class = G_OBJECT_CLASS (class);
107 object_class->dispose = e_meeting_time_selector_item_dispose;
108 object_class->set_property = e_meeting_time_selector_item_set_property;
109
110 item_class = GNOME_CANVAS_ITEM_CLASS (class);
111 item_class->update = e_meeting_time_selector_item_update;
112 item_class->draw = e_meeting_time_selector_item_draw;
113 item_class->point = e_meeting_time_selector_item_point;
114 item_class->event = e_meeting_time_selector_item_event;
115
116 g_object_class_install_property (
117 object_class,
118 PROP_MEETING_TIME_SELECTOR,
119 g_param_spec_pointer (
120 "meeting_time_selector",
121 NULL,
122 NULL,
123 G_PARAM_WRITABLE));
124 }
125
126 static void
127 e_meeting_time_selector_item_init (EMeetingTimeSelectorItem *mts_item)
128 {
129 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (mts_item);
130
131 mts_item->mts = NULL;
132
133 /* Create the cursors. */
134 mts_item->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR);
135 mts_item->resize_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
136 mts_item->busy_cursor = gdk_cursor_new (GDK_WATCH);
137 mts_item->last_cursor_set = NULL;
138
139 item->x1 = 0;
140 item->y1 = 0;
141 item->x2 = 0;
142 item->y2 = 0;
143 }
144
145 static void
146 e_meeting_time_selector_item_dispose (GObject *object)
147 {
148 EMeetingTimeSelectorItem *mts_item;
149
150 mts_item = E_MEETING_TIME_SELECTOR_ITEM (object);
151
152 if (mts_item->normal_cursor) {
153 g_object_unref (mts_item->normal_cursor);
154 mts_item->normal_cursor = NULL;
155 }
156 if (mts_item->resize_cursor) {
157 g_object_unref (mts_item->resize_cursor);
158 mts_item->resize_cursor = NULL;
159 }
160 if (mts_item->busy_cursor) {
161 g_object_unref (mts_item->busy_cursor);
162 mts_item->busy_cursor = NULL;
163 }
164
165 G_OBJECT_CLASS (e_meeting_time_selector_item_parent_class)->dispose (object);
166 }
167
168 static void
169 e_meeting_time_selector_item_set_property (GObject *object,
170 guint property_id,
171 const GValue *value,
172 GParamSpec *pspec)
173 {
174 EMeetingTimeSelectorItem *mts_item;
175
176 mts_item = E_MEETING_TIME_SELECTOR_ITEM (object);
177
178 switch (property_id) {
179 case PROP_MEETING_TIME_SELECTOR:
180 mts_item->mts = g_value_get_pointer (value);
181 return;
182 }
183
184 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
185 }
186
187 static void
188 e_meeting_time_selector_item_update (GnomeCanvasItem *item,
189 const cairo_matrix_t *i2c,
190 gint flags)
191 {
192 if (GNOME_CANVAS_ITEM_CLASS (e_meeting_time_selector_item_parent_class)->update)
193 (* GNOME_CANVAS_ITEM_CLASS (e_meeting_time_selector_item_parent_class)->update) (item, i2c, flags);
194
195 /* The grid covers the entire canvas area. */
196 item->x1 = 0;
197 item->y1 = 0;
198 item->x2 = INT_MAX;
199 item->y2 = INT_MAX;
200 }
201
202 /*
203 * DRAWING ROUTINES - functions to paint the canvas item.
204 */
205
206 static void
207 draw_strikeout_box (EMeetingTimeSelectorItem *mts_item,
208 cairo_t *cr,
209 gint x,
210 gint y,
211 gint width,
212 gint height)
213 {
214 GnomeCanvas *canvas = GNOME_CANVAS_ITEM (mts_item)->canvas;
215 EMeetingTimeSelector *mts = mts_item->mts;
216
217 cairo_save (cr);
218
219 cairo_rectangle (cr, x, y, width, height);
220 cairo_clip (cr);
221
222 cairo_translate (cr, -canvas->draw_xofs, -canvas->draw_yofs);
223 cairo_set_source (cr, mts->no_info_pattern);
224 cairo_paint (cr);
225
226 cairo_restore (cr);
227 }
228
229 static void
230 e_meeting_time_selector_item_draw (GnomeCanvasItem *item,
231 cairo_t *cr,
232 gint x,
233 gint y,
234 gint width,
235 gint height)
236 {
237 EMeetingTimeSelector *mts;
238 EMeetingTimeSelectorItem *mts_item;
239 EMeetingAttendee *ia;
240 gint day_x, meeting_start_x, meeting_end_x, bar_y, bar_height;
241 gint row, row_y, start_x, end_x;
242 GDate date, last_date, current_date;
243 gboolean is_display_top, show_meeting_time;
244
245 mts_item = E_MEETING_TIME_SELECTOR_ITEM (item);
246 mts = mts_item->mts;
247 g_return_if_fail (mts != NULL);
248
249 is_display_top = (GTK_WIDGET (item->canvas) == mts->display_top)
250 ? TRUE : FALSE;
251
252 /* Calculate the first and last visible days and positions. */
253 e_meeting_time_selector_calculate_day_and_position (
254 mts, x,
255 &date, &day_x);
256 e_meeting_time_selector_calculate_day_and_position (
257 mts, x + width,
258 &last_date, NULL);
259
260 /* For the top display draw the 'All Attendees' row background. */
261 cairo_save (cr);
262 if (is_display_top) {
263 gdk_cairo_set_source_color (cr, &mts->all_attendees_bg_color);
264 cairo_rectangle (
265 cr, 0, mts->row_height * 2 - y,
266 width, mts->row_height);
267 cairo_fill (cr);
268 } else {
269 gdk_cairo_set_source_color (cr, &mts->bg_color);
270 cairo_rectangle (cr, 0, 0, width, height);
271 cairo_fill (cr);
272 }
273 cairo_restore (cr);
274
275 /* Calculate the x coordinates of the meeting time. */
276 show_meeting_time = e_meeting_time_selector_get_meeting_time_positions (mts, &meeting_start_x, &meeting_end_x);
277
278 /* Draw the meeting time background. */
279 if (show_meeting_time
280 && (meeting_end_x - 1 >= x) && (meeting_start_x + 1 < x + width)
281 && (meeting_end_x - meeting_start_x > 2)) {
282 cairo_save (cr);
283 gdk_cairo_set_source_color (cr, &mts->meeting_time_bg_color);
284 if (is_display_top) {
285 cairo_rectangle (
286 cr, meeting_start_x + 1 - x, mts->row_height * 2 - y,
287 meeting_end_x - meeting_start_x - 2, mts->row_height);
288 cairo_fill (cr);
289 } else {
290 cairo_rectangle (
291 cr, meeting_start_x + 1 - x, 0,
292 meeting_end_x - meeting_start_x - 2, height);
293 cairo_fill (cr);
294 }
295 cairo_restore (cr);
296 }
297
298 /* For the main display draw the no-info pattern background for attendee's
299 * that have no calendar information. */
300 if (!is_display_top) {
301 gdk_cairo_set_source_color (cr, &mts->grid_color);
302 row = y / mts->row_height;
303 row_y = row * mts->row_height - y;
304 while (row < e_meeting_store_count_actual_attendees (mts->model) && row_y < height) {
305 ia = e_meeting_store_find_attendee_at_row (mts->model, row);
306
307 if (e_meeting_attendee_get_has_calendar_info (ia)) {
308 if (e_meeting_time_selector_item_calculate_busy_range (mts, row, x, width, &start_x, &end_x)) {
309 if (start_x >= width || end_x <= 0) {
310 draw_strikeout_box (mts_item, cr, 0, row_y, width, mts->row_height);
311 } else {
312 if (start_x >= 0) {
313 draw_strikeout_box (mts_item, cr, 0, row_y, start_x, mts->row_height);
314 cairo_move_to (cr, start_x, row_y);
315 cairo_line_to (cr, start_x, row_y + mts->row_height);
316 cairo_stroke (cr);
317 }
318 if (end_x <= width) {
319 draw_strikeout_box (mts_item, cr, end_x, row_y, width - end_x, mts->row_height);
320 cairo_move_to (cr, end_x, row_y);
321 cairo_line_to (cr, end_x, row_y + mts->row_height);
322 cairo_stroke (cr);
323 }
324 }
325 }
326 } else {
327 draw_strikeout_box (mts_item, cr, 0, row_y, width, mts->row_height);
328 }
329 row++;
330 row_y += mts->row_height;
331 }
332 }
333
334 /* Now paint the visible days one by one. */
335 current_date = date;
336 for (;;) {
337 /* Currently we use the same GnomeCanvasItem class for the
338 * top display and the main display. We may use separate
339 * classes in future if necessary. */
340 if (is_display_top)
341 e_meeting_time_selector_item_paint_day_top (mts_item, cr, ¤t_date, day_x, y, width, height);
342 else
343 e_meeting_time_selector_item_paint_day (mts_item, cr, ¤t_date, day_x, y, width, height);
344
345 day_x += mts_item->mts->day_width;
346 if (g_date_compare (¤t_date, &last_date) == 0)
347 break;
348 g_date_add_days (¤t_date, 1);
349 }
350
351 /* Draw the busy periods. */
352 if (is_display_top)
353 e_meeting_time_selector_item_paint_all_attendees_busy_periods (mts_item, cr, &date, x, y, width, height);
354 else
355 e_meeting_time_selector_item_paint_busy_periods (mts_item, cr, &date, x, y, width, height);
356
357 /* Draw the currently-selected meeting time vertical bars. */
358 if (show_meeting_time) {
359 if (is_display_top) {
360 bar_y = mts->row_height * 2 - y;
361 bar_height = mts->row_height;
362 } else {
363 bar_y = 0;
364 bar_height = height;
365 }
366
367 cairo_save (cr);
368 gdk_cairo_set_source_color (cr, &mts->grid_color);
369
370 if ((meeting_start_x + 2 >= x)
371 && (meeting_start_x - 2 < x + width)) {
372 cairo_rectangle (
373 cr, meeting_start_x - 2 - x, bar_y,
374 5, bar_height);
375 cairo_fill (cr);
376 }
377
378 if ((meeting_end_x + 2 >= x)
379 && (meeting_end_x - 2 < x + width)) {
380 cairo_rectangle (
381 cr, meeting_end_x - 2 - x, bar_y,
382 5, bar_height);
383 cairo_fill (cr);
384
385 }
386 cairo_restore (cr);
387 }
388 }
389
390 static void
391 e_meeting_time_selector_item_paint_day_top (EMeetingTimeSelectorItem *mts_item,
392 cairo_t *cr,
393 GDate *date,
394 gint x,
395 gint scroll_y,
396 gint width,
397 gint height)
398 {
399 EMeetingTimeSelector *mts;
400 gint y, grid_x;
401 gchar *str;
402 gint hour, hour_x, hour_y;
403 PangoLayout *layout;
404 struct tm tm_time;
405
406 cairo_save (cr);
407
408 cairo_set_line_width (cr, 1.0);
409
410 mts = mts_item->mts;
411
412 layout = gtk_widget_create_pango_layout (GTK_WIDGET (mts), NULL);
413
414 /* Draw the horizontal lines. */
415 y = mts->row_height - 1 - scroll_y;
416
417 gdk_cairo_set_source_color (cr, &mts->grid_color);
418 cairo_move_to (cr, x + 0.5, y + 0.5);
419 cairo_rel_line_to (cr, mts->day_width - 1, 0);
420 cairo_stroke (cr);
421
422 gdk_cairo_set_source_color (cr, &mts->grid_shadow_color);
423 cairo_move_to (cr, x + 0.5, y + 1.5);
424 cairo_rel_line_to (cr, mts->day_width - 1, 0);
425 cairo_stroke (cr);
426
427 gdk_cairo_set_source_color (cr, &mts->grid_color);
428 y += mts->row_height;
429 cairo_move_to (cr, x + 0.5, y + 0.5);
430 cairo_rel_line_to (cr, mts->day_width - 1, 0);
431 y += mts->row_height;
432 cairo_move_to (cr, x + 0.5, y + 0.5);
433 cairo_rel_line_to (cr, mts->day_width - 1, 0);
434
435 /* Draw the vertical grid lines. */
436 for (grid_x = mts->col_width - 1;
437 grid_x < mts->day_width - mts->col_width;
438 grid_x += mts->col_width) {
439 cairo_move_to (cr, x + grid_x + 0.5, mts->row_height * 2 - 1 - scroll_y + 0.5);
440 cairo_line_to (cr, x + grid_x + 0.5, height + 0.5);
441 }
442 grid_x = mts->day_width - 2;
443 cairo_move_to (cr, x + grid_x + 0.5, 0.5);
444 cairo_rel_line_to (cr, 0, height);
445 grid_x++;
446 cairo_move_to (cr, x + grid_x + 0.5, 0.5);
447 cairo_rel_line_to (cr, 0, height);
448 cairo_stroke (cr);
449
450 g_date_to_struct_tm (date, &tm_time);
451 str = e_datetime_format_format_tm ("calendar", "table", DTFormatKindDate, &tm_time);
452
453 g_return_if_fail (str != NULL);
454
455 /* Draw the date. Set a clipping rectangle so we don't draw over the
456 * next day. */
457 if (mts->date_format == E_MEETING_TIME_SELECTOR_DATE_ABBREVIATED_DAY
458 && !e_datetime_format_includes_day_name ("calendar", "table", DTFormatKindDate)) {
459 gchar buffer[128];
460 gchar *tmp;
461
462 g_date_strftime (buffer, sizeof (buffer), "%a", date);
463
464 tmp = str;
465 str = g_strconcat (buffer, " ", str, NULL);
466 g_free (tmp);
467 }
468
469 cairo_save (cr);
470
471 cairo_rectangle (cr, x, -scroll_y, mts->day_width - 2, mts->row_height - 2);
472 cairo_clip (cr);
473
474 pango_layout_set_text (layout, str, -1);
475 cairo_move_to (cr, x + 2, 4 - scroll_y);
476 pango_cairo_show_layout (cr, layout);
477
478 cairo_restore (cr);
479
480 /* Draw the hours. */
481 hour = mts->first_hour_shown;
482 hour_x = x + 2;
483 hour_y = mts->row_height + 4 - scroll_y;
484 while (hour < mts->last_hour_shown) {
485 if (e_meeting_time_selector_get_use_24_hour_format (mts))
486 pango_layout_set_text (layout, EMeetingTimeSelectorHours[hour], -1);
487 else
488 pango_layout_set_text (layout, EMeetingTimeSelectorHours12[hour], -1);
489
490 cairo_move_to (cr, hour_x, hour_y);
491 pango_cairo_show_layout (cr, layout);
492
493 hour += mts->zoomed_out ? 3 : 1;
494 hour_x += mts->col_width;
495 }
496
497 g_object_unref (layout);
498 cairo_restore (cr);
499 g_free (str);
500 }
501
502 /* This paints the colored bars representing busy periods for the combined
503 * list of attendees. For now we just paint the bars for each attendee of
504 * each other. If we want to speed it up we could optimise it later. */
505 static void
506 e_meeting_time_selector_item_paint_all_attendees_busy_periods (EMeetingTimeSelectorItem *mts_item,
507 cairo_t *cr,
508 GDate *date,
509 gint x,
510 gint scroll_y,
511 gint width,
512 gint height)
513 {
514 EMeetingTimeSelector *mts;
515 EMeetingFreeBusyType busy_type;
516 gint row, y;
517 gint *first_periods;
518
519 mts = mts_item->mts;
520
521 /* Calculate the y coordinate to paint the row at in the drawable. */
522 y = 2 * mts->row_height - scroll_y - 1;
523
524 /* Get the first visible busy periods for all the attendees. */
525 first_periods = g_new (gint, e_meeting_store_count_actual_attendees (mts->model));
526 for (row = 0; row < e_meeting_store_count_actual_attendees (mts->model); row++)
527 first_periods[row] = e_meeting_time_selector_item_find_first_busy_period (mts_item, date, row);
528
529 for (busy_type = 0;
530 busy_type < E_MEETING_FREE_BUSY_LAST;
531 busy_type++) {
532 gdk_cairo_set_source_color (cr, &mts->busy_colors[busy_type]);
533 for (row = 0; row < e_meeting_store_count_actual_attendees (mts->model); row++) {
534 if (first_periods[row] == -1)
535 continue;
536 e_meeting_time_selector_item_paint_attendee_busy_periods (mts_item, cr, x, y, width, row, first_periods[row], busy_type);
537 }
538 }
539
540 g_free (first_periods);
541 }
542
543 static void
544 e_meeting_time_selector_item_paint_day (EMeetingTimeSelectorItem *mts_item,
545 cairo_t *cr,
546 GDate *date,
547 gint x,
548 gint scroll_y,
549 gint width,
550 gint height)
551 {
552 EMeetingTimeSelector *mts;
553 gint grid_x, grid_y, attendee_index, unused_y;
554
555 cairo_save (cr);
556 cairo_set_line_width (cr, 1.0);
557
558 mts = mts_item->mts;
559
560 /* Draw the grid lines. The grid lines around unused rows are drawn in
561 * a different color. */
562
563 /* Draw the horizontal grid lines. */
564 attendee_index = scroll_y / mts->row_height;
565 #if E_MEETING_TIME_SELECTOR_DRAW_GRID_LINES_AT_BOTTOM
566 for (grid_y = mts->row_height - 1 - (scroll_y % mts->row_height);
567 #else
568 for (grid_y = - (scroll_y % mts->row_height);
569 #endif
570 grid_y < height;
571 grid_y += mts->row_height)
572 {
573 if (attendee_index <= e_meeting_store_count_actual_attendees (mts->model)) {
574 gdk_cairo_set_source_color (cr, &mts->grid_color);
575 } else {
576 gdk_cairo_set_source_color (cr, &mts->grid_unused_color);
577 }
578 cairo_move_to (cr, 0, grid_y);
579 cairo_rel_line_to (cr, width, 0);
580 cairo_stroke (cr);
581 attendee_index++;
582 }
583
584 /* Draw the vertical grid lines. */
585 unused_y = (e_meeting_store_count_actual_attendees (mts->model) * mts->row_height) - scroll_y;
586 if (unused_y >= 0) {
587 gdk_cairo_set_source_color (cr, &mts->grid_color);
588 for (grid_x = mts->col_width - 1;
589 grid_x < mts->day_width - mts->col_width;
590 grid_x += mts->col_width)
591 {
592 cairo_move_to (cr, x + grid_x, 0);
593 cairo_line_to (cr, x + grid_x, unused_y - 1);
594 }
595 cairo_stroke (cr);
596
597 cairo_rectangle (
598 cr,
599 x + mts->day_width - 2, 0,
600 2, unused_y);
601 cairo_fill (cr);
602 }
603
604 if (unused_y < height) {
605 gdk_cairo_set_source_color (cr, &mts->grid_unused_color);
606 for (grid_x = mts->col_width - 1;
607 grid_x < mts->day_width - mts->col_width;
608 grid_x += mts->col_width)
609 {
610 cairo_move_to (cr, x + grid_x, unused_y);
611 cairo_line_to (cr, x + grid_x, height);
612 }
613 cairo_stroke (cr);
614
615 cairo_rectangle (
616 cr,
617 x + mts->day_width - 2, unused_y,
618 2, height - unused_y);
619 cairo_fill (cr);
620 }
621
622 cairo_restore (cr);
623 }
624
625 /* This paints the colored bars representing busy periods for the individual
626 * attendees. */
627 static void
628 e_meeting_time_selector_item_paint_busy_periods (EMeetingTimeSelectorItem *mts_item,
629 cairo_t *cr,
630 GDate *date,
631 gint x,
632 gint scroll_y,
633 gint width,
634 gint height)
635 {
636 EMeetingTimeSelector *mts;
637 EMeetingFreeBusyType busy_type;
638 gint row, y, first_period;
639
640 mts = mts_item->mts;
641
642 /* Calculate the first visible attendee row. */
643 row = scroll_y / mts->row_height;
644
645 /* Calculate the y coordinate to paint the row at in the drawable. */
646 y = row * mts->row_height - scroll_y;
647
648 /* Step through the attendees painting the busy periods. */
649 while (y < height && row < e_meeting_store_count_actual_attendees (mts->model)) {
650
651 /* Find the first visible busy period. */
652 first_period = e_meeting_time_selector_item_find_first_busy_period (mts_item, date, row);
653 if (first_period != -1) {
654 /* Paint the different types of busy periods, in
655 * reverse order of precedence, so the highest
656 * precedences are displayed. */
657 for (busy_type = 0;
658 busy_type < E_MEETING_FREE_BUSY_LAST;
659 busy_type++) {
660 gdk_cairo_set_source_color (cr, &mts->busy_colors[busy_type]);
661 e_meeting_time_selector_item_paint_attendee_busy_periods (mts_item, cr, x, y, width, row, first_period, busy_type);
662 }
663 }
664 y += mts->row_height;
665 row++;
666 }
667 }
668
669 /* This subtracts the attendees longest_period_in_days from the given date,
670 * and does a binary search of the attendee's busy periods array to find the
671 * first one which could possible end on the given day or later.
672 * If none are found it returns -1. */
673 static gint
674 e_meeting_time_selector_item_find_first_busy_period (EMeetingTimeSelectorItem *mts_item,
675 GDate *date,
676 gint row)
677 {
678 EMeetingTimeSelector *mts;
679 EMeetingAttendee *ia;
680 const GArray *busy_periods;
681 EMeetingFreeBusyPeriod *period;
682 gint period_num;
683
684 mts = mts_item->mts;
685
686 ia = e_meeting_store_find_attendee_at_row (mts->model, row);
687
688 period_num = e_meeting_attendee_find_first_busy_period (ia, date);
689 if (period_num == -1)
690 return -1;
691
692 /* Check if the period starts after the end of the current canvas
693 * scroll area. */
694 busy_periods = e_meeting_attendee_get_busy_periods (ia);
695 period = &g_array_index (busy_periods, EMeetingFreeBusyPeriod, period_num);
696 if (g_date_compare (&mts->last_date_shown, &period->start.date) < 0)
697 return -1;
698
699 return period_num;
700 }
701
702 /* This paints the visible busy periods for one attendee which are of a certain
703 * busy type, e.g out of office. It is passed the index of the first visible
704 * busy period of the attendee and continues until it runs off the screen. */
705 static void
706 e_meeting_time_selector_item_paint_attendee_busy_periods (EMeetingTimeSelectorItem *mts_item,
707 cairo_t *cr,
708 gint x,
709 gint y,
710 gint width,
711 gint row,
712 gint first_period,
713 EMeetingFreeBusyType busy_type)
714 {
715 EMeetingTimeSelector *mts;
716 EMeetingAttendee *ia;
717 const GArray *busy_periods;
718 EMeetingFreeBusyPeriod *period;
719 gint period_num, x1, x2, x2_within_day, x2_within_col;
720
721 mts = mts_item->mts;
722
723 ia = e_meeting_store_find_attendee_at_row (mts->model, row);
724
725 busy_periods = e_meeting_attendee_get_busy_periods (ia);
726 for (period_num = first_period;
727 period_num < busy_periods->len;
728 period_num++) {
729 period = &g_array_index (busy_periods, EMeetingFreeBusyPeriod, period_num);
730
731 if (period->busy_type != busy_type)
732 continue;
733
734 /* Convert the period start and end times to x coordinates. */
735 x1 = e_meeting_time_selector_calculate_time_position (mts, &period->start);
736 /* If the period is off the right of the area being drawn, we
737 * are finished. */
738 if (x1 >= x + width)
739 return;
740
741 x2 = e_meeting_time_selector_calculate_time_position (mts, &period->end);
742 /* If the period is off the left edge of the area skip it. */
743 if (x2 <= x)
744 continue;
745
746 /* We paint from x1 to x2 - 1, so that for example a time
747 * from 5:00-6:00 is distinct from 6:00-7:00.
748 * We never finish on a grid line separating days, and we only
749 * ever paint on a normal vertical grid line if the period is
750 * only 1 pixel wide. */
751 x2_within_day = x2 % mts->day_width;
752 if (x2_within_day == 0) {
753 x2 -= 2;
754 } else if (x2_within_day == mts->day_width - 1) {
755 x2 -= 1;
756 } else {
757 x2_within_col = x2_within_day % mts->col_width;
758 if (x2_within_col == 0 && x2 > x1 + 1)
759 x2 -= 1;
760 }
761
762 /* Paint the rectangle. We leave a gap of 2 pixels at the
763 * top and bottom, remembering that the grid is painted along
764 * the top/bottom line of each row. */
765 if (x2 - x1 > 0) {
766 #if E_MEETING_TIME_SELECTOR_DRAW_GRID_LINES_AT_BOTTOM
767 cairo_rectangle (
768 cr, x1 - x, y + 2,
769 x2 - x1, mts->row_height - 5);
770 #else
771 cairo_rectangle (
772 cr, x1 - x, y + 3,
773 x2 - x1, mts->row_height - 5);
774 #endif
775 cairo_fill (cr);
776 }
777 }
778 }
779
780 /*
781 * CANVAS ITEM ROUTINES - functions to be a GnomeCanvasItem.
782 */
783
784 static GnomeCanvasItem *
785 e_meeting_time_selector_item_point (GnomeCanvasItem *item,
786 gdouble x,
787 gdouble y,
788 gint cx,
789 gint cy)
790 {
791 return item;
792 }
793
794 static gint
795 e_meeting_time_selector_item_event (GnomeCanvasItem *item,
796 GdkEvent *event)
797 {
798 EMeetingTimeSelectorItem *mts_item;
799
800 mts_item = E_MEETING_TIME_SELECTOR_ITEM (item);
801
802 switch (event->type) {
803 case GDK_BUTTON_PRESS:
804 return e_meeting_time_selector_item_button_press (
805 mts_item,
806 event);
807 case GDK_BUTTON_RELEASE:
808 return e_meeting_time_selector_item_button_release (
809 mts_item,
810 event);
811 case GDK_MOTION_NOTIFY:
812 return e_meeting_time_selector_item_motion_notify (
813 mts_item,
814 event);
815 default:
816 break;
817 }
818
819 return FALSE;
820 }
821
822 /* This handles all button press events for the item. If the cursor is over
823 * one of the meeting time vertical bars we start a drag. If not we set the
824 * meeting time to the nearest half-hour interval.
825 * Note that GnomeCanvas converts the event coords to world coords,
826 * i.e. relative to the entire canvas scroll area. */
827 static gint
828 e_meeting_time_selector_item_button_press (EMeetingTimeSelectorItem *mts_item,
829 GdkEvent *event)
830 {
831 EMeetingTimeSelector *mts;
832 EMeetingTime start_time, end_time;
833 EMeetingTimeSelectorPosition position;
834 GDate *start_date, *end_date;
835 gint x, y;
836
837 mts = mts_item->mts;
838 x = (gint) event->button.x;
839 y = (gint) event->button.y;
840
841 /* Check if we are starting a drag of the vertical meeting time bars.*/
842 position = e_meeting_time_selector_item_get_drag_position (
843 mts_item,
844 x, y);
845 if (position != E_MEETING_TIME_SELECTOR_POS_NONE) {
846 if (gnome_canvas_item_grab (GNOME_CANVAS_ITEM (mts_item),
847 GDK_POINTER_MOTION_MASK
848 | GDK_BUTTON_RELEASE_MASK,
849 mts_item->resize_cursor,
850 event->button.time) == 0 /*Success */) {
851 mts->dragging_position = position;
852 return TRUE;
853 }
854 }
855
856 /* Convert the x coordinate into a EMeetingTimeSelectorTime. */
857 e_meeting_time_selector_calculate_time (mts, x, &start_time);
858 start_date = &start_time.date;
859 end_date = &end_time.date;
860
861 /* Find the nearest half-hour or hour interval, depending on whether
862 * zoomed_out is set. */
863 if (!mts->all_day) {
864 gint astart_year, astart_month, astart_day, astart_hour, astart_minute;
865 gint aend_year, aend_month, aend_day, aend_hour, aend_minute;
866 gint hdiff, mdiff;
867 GDate asdate, aedate;
868
869 e_meeting_time_selector_get_meeting_time (
870 mts_item->mts,
871 &astart_year,
872 &astart_month,
873 &astart_day,
874 &astart_hour,
875 &astart_minute,
876 &aend_year,
877 &aend_month,
878 &aend_day,
879 &aend_hour,
880 &aend_minute);
881 if (mts->zoomed_out)
882 start_time.minute = 0;
883 else
884 start_time.minute -= start_time.minute % 30;
885
886 g_date_set_dmy (&asdate, astart_day, astart_month, astart_year);
887 g_date_set_dmy (&aedate, aend_day, aend_month, aend_year);
888 end_time = start_time;
889 mdiff = end_time.minute + aend_minute - astart_minute;
890 hdiff = end_time.hour + aend_hour - astart_hour + (24 * g_date_days_between (&asdate, &aedate));
891 while (mdiff < 0) {
892 mdiff += 60;
893 hdiff -= 1;
894 }
895 while (mdiff > 60) {
896 mdiff -= 60;
897 hdiff += 1;
898 }
899 while (hdiff < 0) {
900 hdiff += 24;
901 g_date_subtract_days (end_date, 1);
902 }
903 while (hdiff >= 24) {
904 hdiff -= 24;
905 g_date_add_days (end_date, 1);
906 }
907 end_time.minute = mdiff;
908 end_time.hour = hdiff;
909 } else {
910 start_time.hour = 0;
911 start_time.minute = 0;
912 end_time = start_time;
913 g_date_add_days (&end_time.date, 1);
914 }
915
916 /* Fix any overflows. */
917 e_meeting_time_selector_fix_time_overflows (&end_time);
918
919 /* Set the new meeting time. */
920 e_meeting_time_selector_set_meeting_time (
921 mts_item->mts,
922 g_date_get_year (start_date),
923 g_date_get_month (start_date),
924 g_date_get_day (start_date),
925 start_time.hour,
926 start_time.minute,
927 g_date_get_year (end_date),
928 g_date_get_month (end_date),
929 g_date_get_day (end_date),
930 end_time.hour,
931 end_time.minute);
932
933 return FALSE;
934 }
935
936 /* This handles all button release events for the item. If we were dragging,
937 * we finish the drag. */
938 static gint
939 e_meeting_time_selector_item_button_release (EMeetingTimeSelectorItem *mts_item,
940 GdkEvent *event)
941 {
942 EMeetingTimeSelector *mts;
943
944 mts = mts_item->mts;
945
946 /* Reset any drag. */
947 if (mts->dragging_position != E_MEETING_TIME_SELECTOR_POS_NONE) {
948 mts->dragging_position = E_MEETING_TIME_SELECTOR_POS_NONE;
949 e_meeting_time_selector_remove_timeout (mts);
950 gnome_canvas_item_ungrab (
951 GNOME_CANVAS_ITEM (mts_item),
952 event->button.time);
953 }
954
955 return FALSE;
956 }
957
958 /* This handles all motion notify events for the item. If button1 is pressed
959 * we check if a drag is in progress. If not, we set the cursor if we are over
960 * the meeting time vertical bars. Note that GnomeCanvas doesn't use motion
961 * hints, which may affect performance. */
962 static gint
963 e_meeting_time_selector_item_motion_notify (EMeetingTimeSelectorItem *mts_item,
964 GdkEvent *event)
965 {
966 EMeetingTimeSelector *mts;
967 EMeetingTimeSelectorPosition position;
968 GdkCursor *cursor;
969 gint x, y;
970
971 mts = mts_item->mts;
972 x = (gint) event->motion.x;
973 y = (gint) event->motion.y;
974
975 if (mts->dragging_position != E_MEETING_TIME_SELECTOR_POS_NONE) {
976 e_meeting_time_selector_drag_meeting_time (mts, x);
977 return TRUE;
978 }
979
980 position = e_meeting_time_selector_item_get_drag_position (
981 mts_item,
982 x, y);
983
984 /* Determine which cursor should be used. */
985 if (position != E_MEETING_TIME_SELECTOR_POS_NONE)
986 cursor = mts_item->resize_cursor;
987 /* If the Main window shows busy cursor show the same */
988 else if (mts_item->mts->last_cursor_set == GDK_WATCH)
989 cursor = mts_item->busy_cursor;
990 else
991 cursor = mts_item->normal_cursor;
992
993 /* Only set the cursor if it is different to the last one we set. */
994 if (mts_item->last_cursor_set != cursor) {
995 GdkWindow *window;
996 GnomeCanvas *canvas;
997
998 mts_item->last_cursor_set = cursor;
999
1000 canvas = GNOME_CANVAS_ITEM (mts_item)->canvas;
1001 window = gtk_widget_get_window (GTK_WIDGET (canvas));
1002 gdk_window_set_cursor (window, cursor);
1003 }
1004
1005 return FALSE;
1006 }
1007
1008 static EMeetingTimeSelectorPosition
1009 e_meeting_time_selector_item_get_drag_position (EMeetingTimeSelectorItem *mts_item,
1010 gint x,
1011 gint y)
1012 {
1013 EMeetingTimeSelector *mts;
1014 gboolean is_display_top;
1015 gint meeting_start_x, meeting_end_x;
1016
1017 mts = mts_item->mts;
1018
1019 is_display_top = (GTK_WIDGET (GNOME_CANVAS_ITEM (mts_item)->canvas) == mts->display_top) ? TRUE : FALSE;
1020
1021 if (is_display_top && y < mts->row_height * 2)
1022 return E_MEETING_TIME_SELECTOR_POS_NONE;
1023
1024 if (!e_meeting_time_selector_get_meeting_time_positions (mts, &meeting_start_x, &meeting_end_x))
1025 return E_MEETING_TIME_SELECTOR_POS_NONE;
1026
1027 if (x >= meeting_end_x - 2 && x <= meeting_end_x + 2)
1028 return E_MEETING_TIME_SELECTOR_POS_END;
1029
1030 if (x >= meeting_start_x - 2 && x <= meeting_start_x + 2)
1031 return E_MEETING_TIME_SELECTOR_POS_START;
1032
1033 return E_MEETING_TIME_SELECTOR_POS_NONE;
1034 }
1035
1036 static gboolean
1037 e_meeting_time_selector_item_calculate_busy_range (EMeetingTimeSelector *mts,
1038 gint row,
1039 gint x,
1040 gint width,
1041 gint *start_x,
1042 gint *end_x)
1043 {
1044 EMeetingAttendee *ia;
1045 EMeetingTime busy_periods_start;
1046 EMeetingTime busy_periods_end;
1047
1048 ia = e_meeting_store_find_attendee_at_row (mts->model, row);
1049 busy_periods_start = e_meeting_attendee_get_start_busy_range (ia);
1050 busy_periods_end = e_meeting_attendee_get_end_busy_range (ia);
1051
1052 *start_x = -1;
1053 *end_x = -1;
1054
1055 if (!g_date_valid (&busy_periods_start.date)
1056 || !g_date_valid (&busy_periods_end.date))
1057 return FALSE;
1058
1059 *start_x = e_meeting_time_selector_calculate_time_position (mts, &busy_periods_start) - x - 1;
1060
1061 *end_x = e_meeting_time_selector_calculate_time_position (mts, &busy_periods_end) - x;
1062
1063 return TRUE;
1064 }
1065
1066 void
1067 e_meeting_time_selector_item_set_normal_cursor (EMeetingTimeSelectorItem *mts_item)
1068 {
1069 GnomeCanvas *canvas;
1070 GdkWindow *window;
1071
1072 g_return_if_fail (IS_E_MEETING_TIME_SELECTOR_ITEM (mts_item));
1073
1074 canvas = GNOME_CANVAS_ITEM (mts_item)->canvas;
1075 window = gtk_widget_get_window (GTK_WIDGET (canvas));
1076 if (window)
1077 gdk_window_set_cursor (window, mts_item->normal_cursor);
1078 }