evolution-3.6.4/calendar/gui/weekday-picker.c

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)) {
The result of the '<<' expression is undefined
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

The result of the '<<' expression is undefined
(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 }