/* Full day widget for gncal * * Copyright (C) 1998 The Free Software Foundation * * Author: Federico Mena */ #include #include "gncal-full-day.h" #define TEXT_BORDER 2 #define MIN_WIDTH 200 static void gncal_full_day_class_init (GncalFullDayClass *class); static void gncal_full_day_init (GncalFullDay *fullday); static void gncal_full_day_realize (GtkWidget *widget); static void gncal_full_day_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gncal_full_day_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gint gncal_full_day_expose (GtkWidget *widget, GdkEventExpose *event); static GtkContainerClass *parent_class; guint gncal_full_day_get_type (void) { static guint full_day_type = 0; if (!full_day_type) { GtkTypeInfo full_day_info = { "GncalFullDay", sizeof (GncalFullDay), sizeof (GncalFullDayClass), (GtkClassInitFunc) gncal_full_day_class_init, (GtkObjectInitFunc) gncal_full_day_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL }; full_day_type = gtk_type_unique (gtk_container_get_type (), &full_day_info); } return full_day_type; } static void gncal_full_day_class_init (GncalFullDayClass *class) { GtkWidgetClass *widget_class; GtkContainerClass *container_class; widget_class = (GtkWidgetClass *) class; container_class = (GtkContainerClass *) class; parent_class = gtk_type_class (gtk_container_get_type ()); widget_class->realize = gncal_full_day_realize; widget_class->size_request = gncal_full_day_size_request; widget_class->size_allocate = gncal_full_day_size_allocate; widget_class->expose_event = gncal_full_day_expose; } static void gncal_full_day_init (GncalFullDay *fullday) { GTK_WIDGET_UNSET_FLAGS (fullday, GTK_NO_WINDOW); fullday->calendar = NULL; fullday->lower = 0; fullday->upper = 0; fullday->interval = 30; /* 30 minutes by default */ } GtkWidget * gncal_full_day_new (GnomeCalendar *calendar, time_t lower, time_t upper) { GncalFullDay *fullday; g_return_val_if_fail (calendar != NULL, NULL); fullday = gtk_type_new (gncal_full_day_get_type ()); fullday->calendar = calendar; gncal_full_day_set_bounds (fullday, lower, upper); return GTK_WIDGET (fullday); } static void gncal_full_day_realize (GtkWidget *widget) { GdkWindowAttr attributes; gint attributes_mask; g_return_if_fail (widget != NULL); g_return_if_fail (GNCAL_IS_FULL_DAY (widget)); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); attributes.window_type = GDK_WINDOW_CHILD; attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK); attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gdk_window_set_user_data (widget->window, widget); widget->style = gtk_style_attach (widget->style, widget->window); gdk_window_set_background (widget->window, &widget->style->bg[GTK_STATE_PRELIGHT]); } static int get_tm_bounds (GncalFullDay *fullday, struct tm *lower, struct tm *upper) { struct tm tm_lower, tm_upper; int lmin, umin; /* Lower */ tm_lower = *localtime (&fullday->lower); if ((tm_lower.tm_min % fullday->interval) != 0) { tm_lower.tm_min -= tm_lower.tm_min % fullday->interval; /* round down */ mktime (&tm_lower); } /* Upper */ tm_upper = *localtime (&fullday->upper); if ((tm_upper.tm_min % fullday->interval) != 0) { tm_upper.tm_min += fullday->interval - (tm_upper.tm_min % fullday->interval); /* round up */ mktime (&tm_upper); } if (lower) *lower = tm_lower; if (upper) *upper = tm_upper; lmin = 60 * tm_lower.tm_hour + tm_lower.tm_min; umin = 60 * tm_upper.tm_hour + tm_upper.tm_min; if (umin == 0) /* midnight of next day? */ umin = 60 * 24; return (umin - lmin) / fullday->interval; /* number of rows in view */ } static int calc_labels_width (GncalFullDay *fullday) { struct tm cur, upper; time_t tim, time_upper; int width, max_w; char buf[256]; get_tm_bounds (fullday, &cur, &upper); max_w = 0; tim = mktime (&cur); time_upper = mktime (&upper); while (tim < time_upper) { strftime (buf, 256, "%X", &cur); width = gdk_string_width (GTK_WIDGET (fullday)->style->font, buf); if (width > max_w) max_w = width; cur.tm_min += fullday->interval; tim = mktime (&cur); } return max_w; } static void gncal_full_day_size_request (GtkWidget *widget, GtkRequisition *requisition) { GncalFullDay *fullday; int labels_width; int rows; g_return_if_fail (widget != NULL); g_return_if_fail (GNCAL_IS_FULL_DAY (widget)); g_return_if_fail (requisition != NULL); fullday = GNCAL_FULL_DAY (widget); /* Border and min width */ labels_width = calc_labels_width (fullday); requisition->width = 2 * widget->style->klass->xthickness + 4 * TEXT_BORDER + labels_width + MIN_WIDTH; requisition->height = 2 * widget->style->klass->ythickness; /* Rows */ rows = get_tm_bounds (fullday, NULL, NULL); requisition->height += (rows * (2 * TEXT_BORDER + widget->style->font->ascent + widget->style->font->descent) + (rows - 1)); /* division lines */ } static void gncal_full_day_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { g_return_if_fail (widget != NULL); g_return_if_fail (GNCAL_IS_FULL_DAY (widget)); g_return_if_fail (allocation != NULL); widget->allocation = *allocation; if (GTK_WIDGET_REALIZED (widget)) gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height); /* FIXME: adjust children */ } static void paint_back (GncalFullDay *fullday, GdkRectangle *area) { GtkWidget *widget; GdkRectangle rect, dest; int x1, y1, width, height; int labels_width, division_x; int rows, row_height; int i, y; struct tm tm; char buf[256]; widget = GTK_WIDGET (fullday); x1 = widget->style->klass->xthickness; y1 = widget->style->klass->ythickness; width = widget->allocation.width - 2 * x1; height = widget->allocation.height - 2 * y1; /* Clear and paint frame shadow */ gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height); gtk_draw_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_IN, 0, 0, widget->allocation.width, widget->allocation.height); /* Clear space for labels */ labels_width = calc_labels_width (fullday); rect.x = x1; rect.y = y1; rect.width = 2 * TEXT_BORDER + labels_width; rect.height = height; if (gdk_rectangle_intersect (&rect, area, &dest)) gdk_draw_rectangle (widget->window, widget->style->bg_gc[GTK_STATE_NORMAL], TRUE, dest.x, dest.y, dest.width, dest.height); /* Vertical division */ division_x = x1 + 2 * TEXT_BORDER + labels_width; gtk_draw_vline (widget->style, widget->window, GTK_STATE_NORMAL, y1, y1 + height - 1, division_x); /* Horizontal divisions */ rows = get_tm_bounds (fullday, &tm, NULL); row_height = height / rows; /* includes division line */ y = row_height; for (i = 1; i < rows; i++) { gdk_draw_line (widget->window, widget->style->black_gc, x1, y, x1 + width - 1, y); y += row_height; } /* Labels */ y = y1 + ((row_height - 1) - (widget->style->font->ascent + widget->style->font->descent)) / 2; for (i = 0; i < rows; i++) { mktime (&tm); if (gdk_rectangle_intersect (&rect, area, &dest)) { strftime (buf, 256, "%X", &tm); gdk_draw_string (widget->window, widget->style->font, widget->style->fg_gc[GTK_STATE_NORMAL], x1 + TEXT_BORDER, y + widget->style->font->ascent, buf); } rect.y += row_height; y += row_height; tm.tm_min += fullday->interval; } } static gint gncal_full_day_expose (GtkWidget *widget, GdkEventExpose *event) { GncalFullDay *fullday; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GNCAL_IS_FULL_DAY (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (!GTK_WIDGET_DRAWABLE (widget)) return FALSE; fullday = GNCAL_FULL_DAY (widget); if (event->window == widget->window) paint_back (fullday, &event->area); /* FIXME: paint handles in windows if event->window == blah blah */ return FALSE; } void gncal_full_day_update (GncalFullDay *fullday) { g_return_if_fail (fullday != NULL); g_return_if_fail (GNCAL_IS_FULL_DAY (fullday)); /* FIXME */ } void gncal_full_day_set_bounds (GncalFullDay *fullday, time_t lower, time_t upper) { g_return_if_fail (fullday != NULL); g_return_if_fail (GNCAL_IS_FULL_DAY (fullday)); if ((lower != fullday->lower) || (upper != fullday->upper)) { fullday->lower = lower; fullday->upper = upper; gncal_full_day_update (fullday); } }