Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
weekday-picker.c:120:29 | clang-analyzer | The result of the '<<' expression is undefined | ||
weekday-picker.c:120:29 | clang-analyzer | The result of the '<<' expression is undefined |
1 /*
2 * Evolution calendar - Week day picker widget
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Authors:
19 * Federico Mena-Quintero <federico@ximian.com>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30 #include <gtk/gtk.h>
31 #include <gdk/gdkkeysyms.h>
32 #include <glib/gi18n.h>
33 #include <libgnomecanvas/libgnomecanvas.h>
34 #include <e-util/e-util.h>
35 #include "weekday-picker.h"
36
37 #define PADDING 2
38
39 #define WEEKDAY_PICKER_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), TYPE_WEEKDAY_PICKER, WeekdayPickerPrivate))
42
43 /* Private part of the WeekdayPicker structure */
44 struct _WeekdayPickerPrivate {
45 /* Selected days; see weekday_picker_set_days() */
46 guint8 day_mask;
47
48 /* Blocked days; these cannot be modified */
49 guint8 blocked_day_mask;
50
51 /* Day that defines the start of the week; 0 = Sunday, ..., 6 = Saturday */
52 gint week_start_day;
53
54 /* Current keyboard focus day */
55 gint focus_day;
56
57 /* Metrics */
58 gint font_ascent, font_descent;
59 gint max_letter_width;
60
61 /* Components */
62 GnomeCanvasItem *boxes[7];
63 GnomeCanvasItem *labels[7];
64 };
65
66 /* Signal IDs */
67 enum {
68 CHANGED,
69 LAST_SIGNAL
70 };
71
72 static guint wp_signals[LAST_SIGNAL];
73
74 G_DEFINE_TYPE (WeekdayPicker, weekday_picker, GNOME_TYPE_CANVAS)
75
76 static gchar *
77 get_day_text (gint day_index)
78 {
79 GDateWeekday weekday;
80
81 /* Convert from tm_wday to GDateWeekday. */
82 weekday = (day_index == 0) ? G_DATE_SUNDAY : day_index;
83
84 return g_strdup (e_get_weekday_name (weekday, TRUE));
85 }
86
87 static void
88 colorize_items (WeekdayPicker *wp)
89 {
90 WeekdayPickerPrivate *priv;
91 GdkColor *outline, *focus_outline;
92 GdkColor *fill, *sel_fill;
93 GdkColor *text_fill, *sel_text_fill;
94 GtkStateType state;
95 GtkStyle *style;
96 gint i;
97
98 priv = wp->priv;
99
100 state = gtk_widget_get_state (GTK_WIDGET (wp));
101 style = gtk_widget_get_style (GTK_WIDGET (wp));
102
103 outline = &style->fg[state];
104 focus_outline = &style->bg[state];
105
106 fill = &style->base[state];
107 text_fill = &style->fg[state];
108
109 sel_fill = &style->bg[GTK_STATE_SELECTED];
110 sel_text_fill = &style->fg[GTK_STATE_SELECTED];
111
112 for (i = 0; i < 7; i++) {
113 gint day;
114 GdkColor *f, *t, *o;
115
116 day = i + priv->week_start_day;
117 if (day >= 7)
118 day -= 7;
119
120 if (priv->day_mask & (0x1 << day)) {
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
121 f = sel_fill;
122 t = sel_text_fill;
123 } else {
124 f = fill;
125 t = text_fill;
126 }
127
128 if (day == priv->focus_day)
129 o = focus_outline;
130 else
131 o = outline;
132
133 gnome_canvas_item_set (
134 priv->boxes[i],
135 "fill_color_gdk", f,
136 "outline_color_gdk", o,
137 NULL);
138
139 gnome_canvas_item_set (
140 priv->labels[i],
141 "fill_color_gdk", t,
142 NULL);
143 }
144 }
145
146 static void
147 configure_items (WeekdayPicker *wp)
148 {
149 WeekdayPickerPrivate *priv;
150 GtkAllocation allocation;
151 gint width, height;
152 gint box_width;
153 gint i;
154
155 priv = wp->priv;
156
157 gtk_widget_get_allocation (GTK_WIDGET (wp), &allocation);
158
159 width = allocation.width;
160 height = allocation.height;
161
162 box_width = (width - 1) / 7;
163
164 for (i = 0; i < 7; i++) {
165 gchar *c;
166 gint day;
167
168 day = i + priv->week_start_day;
169 if (day >= 7)
170 day -= 7;
171
172 gnome_canvas_item_set (
173 priv->boxes[i],
174 "x1", (gdouble) (i * box_width),
175 "y1", (gdouble) 0,
176 "x2", (gdouble) ((i + 1) * box_width),
177 "y2", (gdouble) (height - 1),
178 "line_width", 0.0,
179 NULL);
180
181 c = get_day_text (day);
182 gnome_canvas_item_set (
183 priv->labels[i],
184 "text", c,
185 "x", (gdouble) (i * box_width) + PADDING,
186 "y", (gdouble) (1 + PADDING),
187 NULL);
188 g_free (c);
189 }
190
191 colorize_items (wp);
192 }
193
194 static void
195 weekday_picker_realize (GtkWidget *widget)
196 {
197 WeekdayPicker *wp;
198
199 wp = WEEKDAY_PICKER (widget);
200
201 /* Chain up to parent's realize() method. */
202 GTK_WIDGET_CLASS (weekday_picker_parent_class)->realize (widget);
203
204 configure_items (wp);
205 }
206
207 static void
208 weekday_picker_get_preferred_width (GtkWidget *widget,
209 gint *minimum_width,
210 gint *natural_width)
211 {
212 WeekdayPicker *wp;
213 WeekdayPickerPrivate *priv;
214
215 wp = WEEKDAY_PICKER (widget);
216 priv = wp->priv;
217
218 *minimum_width = *natural_width =
219 (priv->max_letter_width + 2 * PADDING + 1) * 7 + 1;
220 }
221
222 static void
223 weekday_picker_get_preferred_height (GtkWidget *widget,
224 gint *minimum_height,
225 gint *natural_height)
226 {
227 WeekdayPicker *wp;
228 WeekdayPickerPrivate *priv;
229
230 wp = WEEKDAY_PICKER (widget);
231 priv = wp->priv;
232
233 *minimum_height = *natural_height =
234 (priv->font_ascent + priv->font_descent + 2 * PADDING + 2);
235 }
236
237 static void
238 weekday_picker_size_allocate (GtkWidget *widget,
239 GtkAllocation *allocation)
240 {
241 GtkWidgetClass *widget_class;
242 WeekdayPicker *wp;
243
244 wp = WEEKDAY_PICKER (widget);
245
246 /* Chain up to parent's size_allocate() method. */
247 widget_class = GTK_WIDGET_CLASS (weekday_picker_parent_class);
248 widget_class->size_allocate (widget, allocation);
249
250 gnome_canvas_set_scroll_region (
251 GNOME_CANVAS (wp), 0, 0,
252 allocation->width, allocation->height);
253
254 configure_items (wp);
255 }
256
257 static void
258 weekday_picker_style_set (GtkWidget *widget,
259 GtkStyle *previous_style)
260 {
261 GtkWidgetClass *widget_class;
262 WeekdayPicker *wp;
263 WeekdayPickerPrivate *priv;
264 gint max_width;
265 gint i;
266 PangoFontDescription *font_desc;
267 PangoContext *pango_context;
268 PangoFontMetrics *font_metrics;
269 PangoLayout *layout;
270
271 wp = WEEKDAY_PICKER (widget);
272 priv = wp->priv;
273
274 /* Set up Pango prerequisites */
275 font_desc = gtk_widget_get_style (widget)->font_desc;
276 pango_context = gtk_widget_get_pango_context (widget);
277 font_metrics = pango_context_get_metrics (
278 pango_context, font_desc,
279 pango_context_get_language (pango_context));
280 layout = pango_layout_new (pango_context);
281
282 priv->font_ascent =
283 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics));
284 priv->font_descent =
285 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
286
287 max_width = 0;
288
289 for (i = 0; i < 7; i++) {
290 gchar *c;
291 gint w;
292
293 c = get_day_text (i);
294 pango_layout_set_text (layout, c, strlen (c));
295 pango_layout_get_pixel_size (layout, &w, NULL);
296 g_free (c);
297
298 if (w > max_width)
299 max_width = w;
300 }
301
302 priv->max_letter_width = max_width;
303
304 configure_items (wp);
305 g_object_unref (layout);
306 pango_font_metrics_unref (font_metrics);
307
308 /* Chain up to parent's style_set() method. */
309 widget_class = GTK_WIDGET_CLASS (weekday_picker_parent_class);
310 widget_class->style_set (widget, previous_style);
311 }
312
313 static gboolean
314 weekday_picker_focus (GtkWidget *widget,
315 GtkDirectionType direction)
316 {
317 WeekdayPicker *wp;
318 WeekdayPickerPrivate *priv;
319
320 g_return_val_if_fail (widget != NULL, FALSE);
321 g_return_val_if_fail (IS_WEEKDAY_PICKER (widget), FALSE);
322 wp = WEEKDAY_PICKER (widget);
323 priv = wp->priv;
324
325 if (!gtk_widget_get_can_focus (widget))
326 return FALSE;
327
328 if (gtk_widget_has_focus (widget)) {
329 priv->focus_day = -1;
330 colorize_items (wp);
331 return FALSE;
332 }
333
334 priv->focus_day = priv->week_start_day;
335 gnome_canvas_item_grab_focus (priv->boxes[priv->focus_day]);
336 colorize_items (wp);
337
338 return TRUE;
339 }
340 static void
341 weekday_picker_class_init (WeekdayPickerClass *class)
342 {
343 GtkWidgetClass *widget_class;
344
345 g_type_class_add_private (class, sizeof (WeekdayPickerPrivate));
346
347 widget_class = GTK_WIDGET_CLASS (class);
348 widget_class->realize = weekday_picker_realize;
349 widget_class->get_preferred_width = weekday_picker_get_preferred_width;
350 widget_class->get_preferred_height = weekday_picker_get_preferred_height;
351 widget_class->size_allocate = weekday_picker_size_allocate;
352 widget_class->style_set = weekday_picker_style_set;
353 widget_class->focus = weekday_picker_focus;
354
355 class->changed = NULL;
356
357 wp_signals[CHANGED] = g_signal_new (
358 "changed",
359 G_TYPE_FROM_CLASS (class),
360 G_SIGNAL_RUN_FIRST,
361 G_STRUCT_OFFSET (WeekdayPickerClass, changed),
362 NULL, NULL,
363 g_cclosure_marshal_VOID__VOID,
364 G_TYPE_NONE, 0);
365 }
366
367 static void
368 day_clicked (WeekdayPicker *wp,
369 gint index)
370 {
371 WeekdayPickerPrivate *priv = wp->priv;
372 guint8 day_mask;
373
374 if (priv->blocked_day_mask & (0x1 << index))
375 return;
376
377 if (priv->day_mask & (0x1 << index))
378 day_mask = priv->day_mask & ~(0x1 << index);
379 else
380 day_mask = priv->day_mask | (0x1 << index);
381
382 weekday_picker_set_days (wp, day_mask);
383 }
384
385 static gint
386 handle_key_press_event (WeekdayPicker *wp,
387 GdkEvent *event)
388 {
389 WeekdayPickerPrivate *priv = wp->priv;
390 guint keyval = event->key.keyval;
391
392 if (priv->focus_day == -1)
393 priv->focus_day = priv->week_start_day;
394
395 switch (keyval) {
396 case GDK_KEY_Up:
397 case GDK_KEY_Right:
398 priv->focus_day += 1;
399 break;
400 case GDK_KEY_Down:
401 case GDK_KEY_Left:
402 priv->focus_day -= 1;
403 break;
404 case GDK_KEY_space:
405 case GDK_KEY_Return:
406 day_clicked (wp, priv->focus_day);
407 return TRUE;
408 default:
409 return FALSE;
410 }
411
412 if (priv->focus_day > 6)
413 priv->focus_day = 0;
414 if (priv->focus_day < 0)
415 priv->focus_day = 6;
416
417 colorize_items (wp);
418 gnome_canvas_item_grab_focus (priv->boxes[priv->focus_day]);
419 return TRUE;
420 }
421
422 /* Event handler for the day items */
423 static gint
424 day_event_cb (GnomeCanvasItem *item,
425 GdkEvent *event,
426 gpointer data)
427 {
428 WeekdayPicker *wp;
429 WeekdayPickerPrivate *priv;
430 gint i;
431
432 wp = WEEKDAY_PICKER (data);
433 priv = wp->priv;
434
435 if (event->type == GDK_KEY_PRESS)
436 return handle_key_press_event (wp, event);
437
438 if (!(event->type == GDK_BUTTON_PRESS && event->button.button == 1))
439 return FALSE;
440
441 /* Find which box was clicked */
442
443 for (i = 0; i < 7; i++)
444 if (priv->boxes[i] == item || priv->labels[i] == item)
445 break;
446
447 g_return_val_if_fail (i != 7, TRUE);
448
449 i += priv->week_start_day;
450 if (i >= 7)
451 i -= 7;
452
453 priv->focus_day = i;
454 gnome_canvas_item_grab_focus (priv->boxes[i]);
455 day_clicked (wp, i);
456 return TRUE;
457 }
458
459 /* Creates the canvas items for the weekday picker. The items are empty until
460 * they are configured elsewhere.
461 */
462 static void
463 create_items (WeekdayPicker *wp)
464 {
465 WeekdayPickerPrivate *priv;
466 GnomeCanvasGroup *parent;
467 gint i;
468
469 priv = wp->priv;
470
471 parent = gnome_canvas_root (GNOME_CANVAS (wp));
472
473 for (i = 0; i < 7; i++) {
474 priv->boxes[i] = gnome_canvas_item_new (
475 parent,
476 GNOME_TYPE_CANVAS_RECT,
477 NULL);
478 g_signal_connect (
479 priv->boxes[i], "event",
480 G_CALLBACK (day_event_cb), wp);
481
482 priv->labels[i] = gnome_canvas_item_new (
483 parent,
484 GNOME_TYPE_CANVAS_TEXT,
485 NULL);
486 g_signal_connect (
487 priv->labels[i], "event",
488 G_CALLBACK (day_event_cb), wp);
489 }
490 }
491
492 static void
493 weekday_picker_init (WeekdayPicker *wp)
494 {
495 wp->priv = WEEKDAY_PICKER_GET_PRIVATE (wp);
496
497 create_items (wp);
498 wp->priv->focus_day = -1;
499 }
500
501 /**
502 * weekday_picker_new:
503 * @void:
504 *
505 * Creates a new weekday picker widget.
506 *
507 * Return value: A newly-created weekday picker.
508 **/
509 GtkWidget *
510 weekday_picker_new (void)
511 {
512 return g_object_new (TYPE_WEEKDAY_PICKER, NULL);
513 }
514
515 /**
516 * weekday_picker_set_days:
517 * @wp: A weekday picker.
518 * @day_mask: Bitmask with the days to be selected.
519 *
520 * Sets the days that are selected in a weekday picker. In the @day_mask,
521 * Sunday is bit 0, Monday is bit 1, etc.
522 **/
523 void
524 weekday_picker_set_days (WeekdayPicker *wp,
525 guint8 day_mask)
526 {
527 g_return_if_fail (IS_WEEKDAY_PICKER (wp));
528
529 wp->priv->day_mask = day_mask;
530 colorize_items (wp);
531
532 g_signal_emit (wp, wp_signals[CHANGED], 0);
533 }
534
535 /**
536 * weekday_picker_get_days:
537 * @wp: A weekday picker.
538 *
539 * Queries the days that are selected in a weekday picker.
540 *
541 * Return value: Bit mask of selected days. Sunday is bit 0, Monday is bit 1,
542 * etc.
543 **/
544 guint8
545 weekday_picker_get_days (WeekdayPicker *wp)
546 {
547 g_return_val_if_fail (IS_WEEKDAY_PICKER (wp), 0);
548
549 return wp->priv->day_mask;
550 }
551
552 /**
553 * weekday_picker_set_blocked_days:
554 * @wp: A weekday picker.
555 * @blocked_day_mask: Bitmask with the days to be blocked.
556 *
557 * Sets the days that the weekday picker will prevent from being modified by the
558 * user. The @blocked_day_mask is specified in the same way as in
559 * weekday_picker_set_days().
560 **/
561 void
562 weekday_picker_set_blocked_days (WeekdayPicker *wp,
563 guint8 blocked_day_mask)
564 {
565 g_return_if_fail (IS_WEEKDAY_PICKER (wp));
566
567 wp->priv->blocked_day_mask = blocked_day_mask;
568 }
569
570 /**
571 * weekday_picker_get_blocked_days:
572 * @wp: A weekday picker.
573 *
574 * Queries the set of days that the weekday picker prevents from being modified
575 * by the user.
576 *
577 * Return value: Bit mask of blocked days, with the same format as that returned
578 * by weekday_picker_get_days().
579 **/
580 guint
581 weekday_picker_get_blocked_days (WeekdayPicker *wp)
582 {
583 g_return_val_if_fail (IS_WEEKDAY_PICKER (wp), 0);
584
585 return wp->priv->blocked_day_mask;
586 }
587
588 /**
589 * weekday_picker_set_week_start_day:
590 * @wp: A weekday picker.
591 * @week_start_day: Index of the day that defines the start of the week; 0 is
592 * Sunday, 1 is Monday, etc.
593 *
594 * Sets the day that defines the start of the week for a weekday picker.
595 **/
596 void
597 weekday_picker_set_week_start_day (WeekdayPicker *wp,
598 gint week_start_day)
599 {
600 g_return_if_fail (IS_WEEKDAY_PICKER (wp));
601 g_return_if_fail (week_start_day >= 0 && week_start_day < 7);
602
603 wp->priv->week_start_day = week_start_day;
604
605 configure_items (wp);
606 }
607
608 /**
609 * weekday_picker_get_week_start_day:
610 * @wp: A weekday picker.
611 *
612 * Queries the day that defines the start of the week in a weekday picker.
613 *
614 * Return value: Index of the day that defines the start of the week. See
615 * weekday_picker_set_week_start_day() to see how this is represented.
616 **/
617 gint
618 weekday_picker_get_week_start_day (WeekdayPicker *wp)
619 {
620 g_return_val_if_fail (IS_WEEKDAY_PICKER (wp), -1);
621
622 return wp->priv->week_start_day;
623 }