// 3 september 2015 #define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_32 #define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_32 #define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_4 #define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4 #include #define dateTimePickerType (dateTimePicker_get_type()) #define dateTimePicker(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), dateTimePickerType, dateTimePicker)) #define isDateTimePicker(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), dateTimePickerType)) #define dateTimePickerClass(class) (G_TYPE_CHECK_CLASS_CAST((class), dateTimePickerType, dateTimePickerClass)) #define isDateTimePickerClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), dateTimePickerType)) #define dateTimePickerGetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), dateTimePickerType, dateTimePickerClass)) typedef struct dateTimePicker dateTimePicker; typedef struct dateTimePickerClass dateTimePickerClass; struct dateTimePicker { GtkContainer parent_instance; }; struct dateTimePickerClass { GtkContainerClass parent_class; }; G_DEFINE_TYPE(dateTimePicker, dateTimePicker, GTK_TYPE_CONTAINER) static void dateTimePicker_init(dateTimePicker *d) { GtkStyleContext *context; gtk_widget_set_has_window(GTK_WIDGET(d), FALSE); context = gtk_widget_get_style_context(GTK_WIDGET(d)); gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY); } static void dateTimePicker_dispose(GObject *obj) { G_OBJECT_CLASS(dateTimePicker_parent_class)->dispose(obj); } static void dateTimePicker_finalize(GObject *obj) { G_OBJECT_CLASS(dateTimePicker_parent_class)->finalize(obj); } // All of this is to imitate GtkEntry without actually using its code // TODO when updating to GTK+ 3.10, copy its imitations too // This is a hard-coded value that has remained unchanged since 3.4. #define minEntryWidth 150 struct metrics { gint ascent; gint descent; gboolean interiorFocus; gint focusWidth; GtkBorder border; }; static struct metrics metrics(dateTimePicker *d) { struct metrics m; GtkStyleContext *scontext; PangoContext *pcontext; GtkStateFlags stateflags; PangoFontMetrics *pmetrics; GtkBorder padding; scontext = gtk_widget_get_style_context(GTK_WIDGET(d)); pcontext = gtk_widget_get_pango_context(GTK_WIDGET(d)); stateflags = gtk_widget_get_state_flags(GTK_WIDGET(d)); pmetrics = pango_context_get_metrics(pcontext, gtk_style_context_get_font(scontext, stateflags), pango_context_get_language(pcontext)); m.ascent = pango_font_metrics_get_ascent(pmetrics); m.descent = pango_font_metrics_get_descent(pmetrics); pango_font_metrics_unref(pmetrics); gtk_widget_style_get(GTK_WIDGET(d), "interior-focus", &(m.interiorFocus), "focus-line-width", &(m.focusWidth), NULL); gtk_style_context_get_border(scontext, 0, &(m.border)); gtk_style_context_get_padding(scontext, 0, &padding); m.border.left += padding.left; m.border.top += padding.top; m.border.right += padding.right; m.border.bottom += padding.bottom; if (!m.interiorFocus) { m.border.left += m.focusWidth; m.border.top += m.focusWidth; m.border.right += m.focusWidth; m.border.bottom += m.focusWidth; } return m; } static void dateTimePicker_get_preferred_width(GtkWidget *w, gint *minimum, gint *natural) { dateTimePicker *d = dateTimePicker(w); struct metrics m; m = metrics(d); // TODO base width on content *minimum = minEntryWidth + m.border.left + m.border.right; *natural = *minimum; } static void dateTimePicker_get_preferred_height(GtkWidget *w, gint *minimum, gint *natural) { dateTimePicker *d = dateTimePicker(w); struct metrics m; m = metrics(d); // TODO base width on content *minimum = PANGO_PIXELS(m.ascent + m.descent) + m.border.top + m.border.bottom; *natural = *minimum; } static void dateTimePicker_size_allocate(GtkWidget *w, GtkAllocation *allocation) { gtk_widget_set_allocation(w, allocation); } static gint dateTimePicker_draw(GtkWidget *w, cairo_t *cr) { dateTimePicker *d = dateTimePicker(w); GtkStyleContext *context; gint x, y, width, height; struct metrics m; GtkAllocation allocation; GtkRequisition minimum; gint minHeight; context = gtk_widget_get_style_context(GTK_WIDGET(d)); x = 0; y = 0; m = metrics(d); // draw background, shadow, focus, etc. cairo_save(cr); gtk_widget_get_allocation(GTK_WIDGET(d), &allocation); gtk_widget_get_preferred_size(GTK_WIDGET(d), &minimum, NULL); minHeight = minimum.height - gtk_widget_get_margin_top(GTK_WIDGET(d)) - gtk_widget_get_margin_bottom(GTK_WIDGET(d)); x = 0; y = (allocation.height - minHeight) / 2; width = allocation.width; height = minHeight; cairo_translate(cr, x, y); x = 0; y = 0; if (gtk_widget_has_focus(GTK_WIDGET(d)) & !m.interiorFocus) { x += m.focusWidth; y += m.focusWidth; width -= 2 * m.focusWidth; height -= 2 * m.focusWidth; } gtk_render_background(context, cr, x, y, width, height); gtk_render_frame(context, cr, x, y, width, height); if (gtk_widget_has_visible_focus(GTK_WIDGET(d)) & !m.interiorFocus) { x -= m.focusWidth; y -= m.focusWidth; width += 2 * m.focusWidth; height += 2 * m.focusWidth; // TODO 0,0? this was removed in 3.14... gtk_render_focus(context, cr, 0, 0, width, height); } cairo_restore(cr); return FALSE; } static void dateTimePicker_class_init(dateTimePickerClass *class) { G_OBJECT_CLASS(class)->dispose = dateTimePicker_dispose; G_OBJECT_CLASS(class)->finalize = dateTimePicker_finalize; GTK_WIDGET_CLASS(class)->get_preferred_width = dateTimePicker_get_preferred_width; GTK_WIDGET_CLASS(class)->get_preferred_height = dateTimePicker_get_preferred_height; GTK_WIDGET_CLASS(class)->size_allocate = dateTimePicker_size_allocate; GTK_WIDGET_CLASS(class)->draw = dateTimePicker_draw; // GTK_CONTAINER_CLASS(class)->forall = dateTimePicker_forall; } // #qo pkg-config: gtk+-3.0 int main(void) { GtkWidget *mainwin; GtkWidget *dtp; gtk_init(NULL, NULL); mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_container_set_border_width(GTK_CONTAINER(mainwin), 12); g_signal_connect(mainwin, "destroy", G_CALLBACK(gtk_main_quit), NULL); dtp = GTK_WIDGET(g_object_new(dateTimePickerType, NULL)); gtk_container_add(GTK_CONTAINER(mainwin), dtp); gtk_widget_show_all(mainwin); gtk_main(); return 0; }