/* * gtkpanel.c - implementation of the `Panels' GTK layout container. */ #include "gtkpanel.h" static void panels_init(Panels *panels); static void panels_class_init(PanelsClass *klass); static void panels_map(GtkWidget *widget); static void panels_unmap(GtkWidget *widget); static void panels_draw(GtkWidget *widget, GdkRectangle *area); static gint panels_expose(GtkWidget *widget, GdkEventExpose *event); static void panels_base_add(GtkContainer *container, GtkWidget *widget); static void panels_remove(GtkContainer *container, GtkWidget *widget); static void panels_forall(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); static GtkType panels_child_type(GtkContainer *container); static void panels_size_request(GtkWidget *widget, GtkRequisition *req); static void panels_size_allocate(GtkWidget *widget, GtkAllocation *alloc); static GtkContainerClass *parent_class = NULL; GtkType panels_get_type(void) { static GtkType panels_type = 0; if (!panels_type) { static const GtkTypeInfo panels_info = { "Panels", sizeof(Panels), sizeof(PanelsClass), (GtkClassInitFunc) panels_class_init, (GtkObjectInitFunc) panels_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; panels_type = gtk_type_unique(GTK_TYPE_CONTAINER, &panels_info); } return panels_type; } static void panels_class_init(PanelsClass *klass) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkContainerClass *container_class; object_class = (GtkObjectClass *)klass; widget_class = (GtkWidgetClass *)klass; container_class = (GtkContainerClass *)klass; parent_class = gtk_type_class(GTK_TYPE_CONTAINER); /* * FIXME: do we have to do all this faffing with set_arg, * get_arg and child_arg_type? Ick. */ widget_class->map = panels_map; widget_class->unmap = panels_unmap; widget_class->draw = panels_draw; widget_class->expose_event = panels_expose; widget_class->size_request = panels_size_request; widget_class->size_allocate = panels_size_allocate; container_class->add = panels_base_add; container_class->remove = panels_remove; container_class->forall = panels_forall; container_class->child_type = panels_child_type; } static void panels_init(Panels *panels) { GTK_WIDGET_SET_FLAGS(panels, GTK_NO_WINDOW); panels->children = NULL; } /* * These appear to be thoroughly tedious functions; the only reason * we have to reimplement them at all is because we defined our own * format for our GList of children... */ static void panels_map(GtkWidget *widget) { Panels *panels; GtkWidget *child; GList *children; g_return_if_fail(widget != NULL); g_return_if_fail(IS_PANELS(widget)); panels = PANELS(widget); GTK_WIDGET_SET_FLAGS(panels, GTK_MAPPED); for (children = panels->children; children && (child = children->data); children = children->next) { if (child && GTK_WIDGET_VISIBLE(child) && !GTK_WIDGET_MAPPED(child)) gtk_widget_map(child); } } static void panels_unmap(GtkWidget *widget) { Panels *panels; GtkWidget *child; GList *children; g_return_if_fail(widget != NULL); g_return_if_fail(IS_PANELS(widget)); panels = PANELS(widget); GTK_WIDGET_UNSET_FLAGS(panels, GTK_MAPPED); for (children = panels->children; children && (child = children->data); children = children->next) { if (child && GTK_WIDGET_VISIBLE(child) && GTK_WIDGET_MAPPED(child)) gtk_widget_unmap(child); } } static void panels_draw(GtkWidget *widget, GdkRectangle *area) { Panels *panels; GtkWidget *child; GList *children; GdkRectangle child_area; g_return_if_fail(widget != NULL); g_return_if_fail(IS_PANELS(widget)); if (GTK_WIDGET_DRAWABLE(widget)) { panels = PANELS(widget); for (children = panels->children; children && (child = children->data); children = children->next) { if (child && GTK_WIDGET_DRAWABLE(child) && gtk_widget_intersect(child, area, &child_area)) gtk_widget_draw(child, &child_area); } } } static gint panels_expose(GtkWidget *widget, GdkEventExpose *event) { Panels *panels; GtkWidget *child; GList *children; GdkEventExpose child_event; g_return_val_if_fail(widget != NULL, FALSE); g_return_val_if_fail(IS_PANELS(widget), FALSE); g_return_val_if_fail(event != NULL, FALSE); if (GTK_WIDGET_DRAWABLE(widget)) { panels = PANELS(widget); child_event = *event; for (children = panels->children; children && (child = children->data); children = children->next) { if (child && GTK_WIDGET_DRAWABLE(child) && GTK_WIDGET_NO_WINDOW(child) && gtk_widget_intersect(child, &event->area, &child_event.area)) gtk_widget_event(child, (GdkEvent *)&child_event); } } return FALSE; } static void panels_base_add(GtkContainer *container, GtkWidget *widget) { Panels *panels; g_return_if_fail(container != NULL); g_return_if_fail(IS_PANELS(container)); g_return_if_fail(widget != NULL); panels = PANELS(container); panels_add(panels, widget); } static void panels_remove(GtkContainer *container, GtkWidget *widget) { Panels *panels; GtkWidget *child; GList *children; gboolean was_visible; g_return_if_fail(container != NULL); g_return_if_fail(IS_PANELS(container)); g_return_if_fail(widget != NULL); panels = PANELS(container); for (children = panels->children; children && (child = children->data); children = children->next) { if (child != widget) continue; was_visible = GTK_WIDGET_VISIBLE(widget); gtk_widget_unparent(widget); panels->children = g_list_remove_link(panels->children, children); g_list_free(children); if (was_visible) gtk_widget_queue_resize(GTK_WIDGET(container)); break; } } static void panels_forall(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { Panels *panels; GtkWidget *child; GList *children, *next; g_return_if_fail(container != NULL); g_return_if_fail(IS_PANELS(container)); g_return_if_fail(callback != NULL); panels = PANELS(container); for (children = panels->children; children && (child = children->data); children = next) { /* * We can't wait until after the callback to assign * `children = children->next', because the callback might * be gtk_widget_destroy, which would remove the link * `children' from the list! So instead we must get our * hands on the value of the `next' pointer _before_ the * callback. */ next = children->next; if (child) callback(child, callback_data); } } static GtkType panels_child_type(GtkContainer *container) { return GTK_TYPE_WIDGET; } GtkWidget *panels_new(void) { Panels *panels; panels = gtk_type_new(panels_get_type()); return GTK_WIDGET(panels); } void panels_add(Panels *panels, GtkWidget *child) { g_return_if_fail(panels != NULL); g_return_if_fail(IS_PANELS(panels)); g_return_if_fail(child != NULL); g_return_if_fail(child->parent == NULL); panels->children = g_list_append(panels->children, child); gtk_widget_set_parent(child, GTK_WIDGET(panels)); if (GTK_WIDGET_REALIZED(panels)) gtk_widget_realize(child); if (GTK_WIDGET_VISIBLE(panels)) { if (GTK_WIDGET_MAPPED(panels)) gtk_widget_map(child); gtk_widget_queue_resize(child); } } void panels_switch_to(Panels *panels, GtkWidget *target) { GtkWidget *child = NULL; GList *children; gboolean changed = FALSE; g_return_if_fail(panels != NULL); g_return_if_fail(IS_PANELS(panels)); g_return_if_fail(target != NULL); g_return_if_fail(target->parent == GTK_WIDGET(panels)); for (children = panels->children; children && (child = children->data); children = children->next) { if (!child) continue; if (child == target) { if (!GTK_WIDGET_VISIBLE(child)) { gtk_widget_show(child); changed = TRUE; } } else { if (GTK_WIDGET_VISIBLE(child)) { gtk_widget_hide(child); changed = TRUE; } } } if (changed) gtk_widget_queue_resize(child); } /* * Now here comes the interesting bit. The actual layout part is * done in the following two functions: * * panels_size_request() examines the list of widgets held in the * Panels, and returns a requisition stating the absolute minimum * size it can bear to be. * * panels_size_allocate() is given an allocation telling it what * size the whole container is going to be, and it calls * gtk_widget_size_allocate() on all of its (visible) children to * set their size and position relative to the top left of the * container. */ static void panels_size_request(GtkWidget *widget, GtkRequisition *req) { Panels *panels; GtkWidget *child; GList *children; g_return_if_fail(widget != NULL); g_return_if_fail(IS_PANELS(widget)); g_return_if_fail(req != NULL); panels = PANELS(widget); req->width = 0; req->height = 0; for (children = panels->children; children && (child = children->data); children = children->next) { GtkRequisition creq; gtk_widget_size_request(child, &creq); if (req->width < creq.width) req->width = creq.width; if (req->height < creq.height) req->height = creq.height; } req->width += 2*GTK_CONTAINER(panels)->border_width; req->height += 2*GTK_CONTAINER(panels)->border_width; } static void panels_size_allocate(GtkWidget *widget, GtkAllocation *alloc) { Panels *panels; GtkWidget *child; GList *children; gint border; g_return_if_fail(widget != NULL); g_return_if_fail(IS_PANELS(widget)); g_return_if_fail(alloc != NULL); panels = PANELS(widget); widget->allocation = *alloc; border = GTK_CONTAINER(panels)->border_width; for (children = panels->children; children && (child = children->data); children = children->next) { GtkAllocation call; call.x = alloc->x + border; call.width = alloc->width - 2*border; call.y = alloc->y + border; call.height = alloc->height - 2*border; gtk_widget_size_allocate(child, &call); } }