/* gnome-druid.c * Copyright (C) 1999 J. Arthur Random * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "gnome-druid.h" enum { CANCEL, LAST_SIGNAL }; static void gnome_druid_init (GnomeDruid *druid); static void gnome_druid_class_init (GnomeDruidClass *klass); static void gnome_druid_destroy (GtkObject *object); static void gnome_druid_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gnome_druid_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void gnome_druid_map (GtkWidget *widget); static void gnome_druid_unmap (GtkWidget *widget); static void gnome_druid_add (GtkContainer *widget, GtkWidget *page); static void gnome_druid_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); static void gnome_druid_back_callback (GtkWidget *button, GnomeDruid *druid); static void gnome_druid_next_callback (GtkWidget *button, GnomeDruid *druid); static void gnome_druid_cancel_callback (GtkWidget *button, GtkWidget *druid); static GtkContainerClass *parent_class = NULL; static guint druid_signals[LAST_SIGNAL] = { 0 }; GtkType gnome_druid_get_type (void) { static GtkType druid_type = 0; if (!druid_type) { static const GtkTypeInfo druid_info = { "GnomeDruid", sizeof (GnomeDruid), sizeof (GnomeDruidClass), (GtkClassInitFunc) gnome_druid_class_init, (GtkObjectInitFunc) gnome_druid_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; druid_type = gtk_type_unique (gtk_container_get_type (), &druid_info); } return druid_type; } static void gnome_druid_class_init (GnomeDruidClass *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_container_get_type ()); druid_signals[CANCEL] = gtk_signal_new ("cancel", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (GnomeDruidClass, cancel), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); gtk_object_class_add_signals (object_class, druid_signals, LAST_SIGNAL); object_class->destroy = gnome_druid_destroy; widget_class->size_request = gnome_druid_size_request; widget_class->size_allocate = gnome_druid_size_allocate; widget_class->map = gnome_druid_map; widget_class->unmap = gnome_druid_unmap; container_class->forall = gnome_druid_forall; container_class->add = gnome_druid_add; } static void gnome_druid_init (GnomeDruid *druid) { GtkWidget *pixmap; /* set up the buttons */ GTK_WIDGET_SET_FLAGS (GTK_WIDGET (druid), GTK_NO_WINDOW); pixmap = gnome_stock_pixmap_widget(NULL, GNOME_STOCK_BUTTON_PREV); druid->back = gnome_pixmap_button (pixmap, _("Back")); GTK_WIDGET_SET_FLAGS (druid->back, GTK_CAN_DEFAULT); druid->next = gnome_stock_or_ordinary_button (GNOME_STOCK_BUTTON_NEXT); GTK_WIDGET_SET_FLAGS (druid->next, GTK_CAN_DEFAULT); druid->cancel = gnome_stock_or_ordinary_button (GNOME_STOCK_BUTTON_CANCEL); GTK_WIDGET_SET_FLAGS (druid->cancel, GTK_CAN_DEFAULT); pixmap = gnome_stock_pixmap_widget(NULL, GNOME_STOCK_BUTTON_APPLY); druid->finish = gnome_pixmap_button (pixmap, _("Finish")); GTK_WIDGET_SET_FLAGS (druid->finish, GTK_CAN_DEFAULT); gtk_widget_set_parent (druid->back, GTK_WIDGET (druid)); gtk_widget_set_parent (druid->next, GTK_WIDGET (druid)); gtk_widget_set_parent (druid->cancel, GTK_WIDGET (druid)); gtk_widget_set_parent (druid->finish, GTK_WIDGET (druid)); gtk_widget_show (druid->back); gtk_widget_show (druid->next); gtk_widget_show (druid->cancel); gtk_widget_show (druid->finish); /* other flags */ druid->current = NULL; druid->children = NULL; druid->show_finish = FALSE; gtk_signal_connect (GTK_OBJECT (druid->back), "clicked", gnome_druid_back_callback, druid); gtk_signal_connect (GTK_OBJECT (druid->next), "clicked", gnome_druid_next_callback, druid); gtk_signal_connect (GTK_OBJECT (druid->cancel), "clicked", gnome_druid_cancel_callback, druid); gtk_signal_connect (GTK_OBJECT (druid->finish), "clicked", gnome_druid_next_callback, druid); } static void gnome_druid_destroy (GtkObject *object) { GnomeDruid *druid; g_return_if_fail (object != NULL); g_return_if_fail (GNOME_IS_DRUID (object)); druid = GNOME_DRUID (object); g_list_free (druid->children); } static void gnome_druid_size_request (GtkWidget *widget, GtkRequisition *requisition) { guint16 temp_width, temp_height; GList *list; GnomeDruid *druid; GtkRequisition child_requisition; GnomeDruidPage *child; g_return_if_fail (widget != NULL); g_return_if_fail (GNOME_IS_DRUID (widget)); druid = GNOME_DRUID (widget); temp_height = temp_width = 0; /* We find the maximum size of all children widgets */ for (list = druid->children; list; list = list->next) { child = GNOME_DRUID_PAGE (list->data); if (GTK_WIDGET_VISIBLE (child)) { gtk_widget_size_request (GTK_WIDGET (child), &child_requisition); temp_width = MAX (temp_width, child_requisition.width); temp_height = MAX (temp_height, child_requisition.height); } } requisition->width = temp_width + 2 * GNOME_PAD_SMALL; requisition->height = temp_height + 2 * GNOME_PAD_SMALL; /* In an Attempt to show how the widgets are packed, * here's a little diagram. * * ------------- [ Back ] [ Next ] [ Cancel ] * \ * This part needs to be at least 1 button width. * In addition, there is 1/4 X Button width between Cancel and Next, * and a GNOME_PAD_SMALL between Next and Back. */ /* our_button width is temp_width and temp_height */ temp_height = 0; temp_width = 0; gtk_widget_size_request (druid->back, &child_requisition); temp_width = MAX (temp_width, child_requisition.width); temp_height = MAX (temp_height, child_requisition.height); gtk_widget_size_request (druid->next, &child_requisition); temp_width = MAX (temp_width, child_requisition.width); temp_height = MAX (temp_height, child_requisition.height); gtk_widget_size_request (druid->cancel, &child_requisition); temp_width = MAX (temp_width, child_requisition.width); temp_height = MAX (temp_height, child_requisition.height); gtk_widget_size_request (druid->finish, &child_requisition); temp_width = MAX (temp_width, child_requisition.width); temp_height = MAX (temp_height, child_requisition.height); temp_width += GNOME_PAD_SMALL * 2; temp_height += GNOME_PAD_SMALL; /* FIXME. do we need to do something with the buttons requisition? */ temp_width = temp_width * 17/4 + GNOME_PAD_SMALL * 3; /* pick which is bigger, the buttons, or the GnomeDruidPages */ requisition->width = MAX (temp_width, requisition->width); requisition->height += temp_height + GNOME_PAD_SMALL * 2; /* And finally, put the side padding in */ requisition->width += GNOME_PAD_SMALL *2; } static void gnome_druid_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GnomeDruid *druid; GtkAllocation child_allocation; gint button_height; GList *list; g_return_if_fail (widget != NULL); g_return_if_fail (GNOME_IS_DRUID (widget)); druid = GNOME_DRUID (widget); widget->allocation = *allocation; /* deal with the buttons */ child_allocation.width = child_allocation.height = 0; child_allocation.width = druid->back->requisition.width; child_allocation.height = druid->back->requisition.height; child_allocation.width = MAX (child_allocation.width, druid->next->requisition.width); child_allocation.height = MAX (child_allocation.height, druid->next->requisition.height); child_allocation.width = MAX (child_allocation.width, druid->cancel->requisition.width); child_allocation.height = MAX (child_allocation.height, druid->cancel->requisition.height); child_allocation.height += GNOME_PAD_SMALL; button_height = child_allocation.height; child_allocation.width += 2 * GNOME_PAD_SMALL; child_allocation.x = allocation->x + allocation->width - GNOME_PAD_SMALL - child_allocation.width; child_allocation.y = allocation->y + allocation->height - GNOME_PAD_SMALL - child_allocation.height; gtk_widget_size_allocate (druid->cancel, &child_allocation); child_allocation.x -= (child_allocation.width * 5 / 4); gtk_widget_size_allocate (druid->next, &child_allocation); gtk_widget_size_allocate (druid->finish, &child_allocation); child_allocation.x -= (GNOME_PAD_SMALL + child_allocation.width); gtk_widget_size_allocate (druid->back, &child_allocation); /* Put up the GnomeDruidPage */ child_allocation.x = GNOME_PAD_SMALL; child_allocation.y = GNOME_PAD_SMALL; child_allocation.width = ((allocation->width - 2* GNOME_PAD_SMALL) > 0) ? (allocation->width - 2* GNOME_PAD_SMALL):0; child_allocation.height = ((allocation->height - 3 * GNOME_PAD_SMALL - button_height) > 0) ? (allocation->height - 3 * GNOME_PAD_SMALL - button_height):0; for (list = druid->children; list; list=list->next) { if (GTK_WIDGET_VISIBLE (list->data)) { gtk_widget_size_allocate (GTK_WIDGET (list->data), &child_allocation); } } } static void gnome_druid_map (GtkWidget *widget) { GnomeDruid *druid; g_return_if_fail (widget != NULL); g_return_if_fail (GNOME_IS_DRUID (widget)); druid = GNOME_DRUID (widget); GTK_WIDGET_SET_FLAGS (druid, GTK_MAPPED); gtk_widget_map (druid->back); if (druid->show_finish) gtk_widget_map (druid->finish); else gtk_widget_map (druid->next); gtk_widget_map (druid->cancel); if (druid->current && GTK_WIDGET_VISIBLE (druid->current) && !GTK_WIDGET_MAPPED (druid->current)) { gtk_widget_map (GTK_WIDGET (druid->current)); } } static void gnome_druid_unmap (GtkWidget *widget) { GnomeDruid *druid; g_return_if_fail (widget != NULL); g_return_if_fail (GNOME_IS_DRUID (widget)); druid = GNOME_DRUID (widget); GTK_WIDGET_UNSET_FLAGS (druid, GTK_MAPPED); gtk_widget_unmap (druid->back); if (druid->show_finish) gtk_widget_unmap (druid->finish); else gtk_widget_unmap (druid->next); gtk_widget_unmap (druid->cancel); if (druid->current && GTK_WIDGET_VISIBLE (druid->current) && GTK_WIDGET_MAPPED (druid->current)) gtk_widget_unmap (GTK_WIDGET (druid->current)); } static void gnome_druid_add (GtkContainer *widget, GtkWidget *page) { g_return_if_fail (widget != NULL); g_return_if_fail (GNOME_IS_DRUID (widget)); g_return_if_fail (page != NULL); g_return_if_fail (GNOME_IS_DRUID_PAGE (page)); gnome_druid_append_page (GNOME_DRUID (widget), GNOME_DRUID_PAGE (page)); } static void gnome_druid_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { GnomeDruid *druid; GnomeDruidPage *child; GList *children; g_return_if_fail (container != NULL); g_return_if_fail (GNOME_IS_DRUID (container)); g_return_if_fail (callback != NULL); druid = GNOME_DRUID (container); children = druid->children; while (children) { child = children->data; children = children->next; (* callback) (GTK_WIDGET (child), callback_data); } if (include_internals) { (* callback) (druid->back, callback_data); (* callback) (druid->next, callback_data); (* callback) (druid->cancel, callback_data); (* callback) (druid->finish, callback_data); } } static void gnome_druid_back_callback (GtkWidget *button, GnomeDruid *druid) { GList *list; g_return_if_fail (druid->current != NULL); if (gnome_druid_page_back (druid->current)) return; /* Make sure that we have a next list item */ list = g_list_find (druid->children, druid->current); g_return_if_fail (list->prev != NULL); gnome_druid_set_page (druid, GNOME_DRUID_PAGE (list->prev->data)); } static void gnome_druid_next_callback (GtkWidget *button, GnomeDruid *druid) { GList *list; g_return_if_fail (druid->current != NULL); if (druid->show_finish == FALSE) { if (gnome_druid_page_next (druid->current)) return; /* Make sure that we have a next list item */ /* FIXME: we want to find the next VISIBLE one... */ list = g_list_find (druid->children, druid->current); g_return_if_fail (list->next != NULL); gnome_druid_set_page (druid, GNOME_DRUID_PAGE (list->next->data)); } else { gnome_druid_page_finish (druid->current); } } static void gnome_druid_cancel_callback (GtkWidget *button, GtkWidget *druid) { if (GNOME_DRUID (druid)->current) { if (gnome_druid_page_cancel (GNOME_DRUID (druid)->current)) return; gtk_signal_emit (GTK_OBJECT (druid), druid_signals [CANCEL]); } } /* Public Functions */ GtkWidget * gnome_druid_new (void) { return GTK_WIDGET (gtk_type_new (gnome_druid_get_type ())); } /** * gnome_druid_set_buttons_sensitive * @druid: A Druid. * @back_sensitive: The sensitivity of the back button. * @next_sensitive: The sensitivity of the next button. * @cancel_sensitive: The sensitivity of the cancel button. * * Description: Sets the sensitivity of the @druid's control-buttons. If the * variables are TRUE, then they will be clickable. This function is used * primarily by the actual GnomeDruidPage widgets. **/ void gnome_druid_set_buttons_sensitive (GnomeDruid *druid, gboolean back_sensitive, gboolean next_sensitive, gboolean cancel_sensitive) { g_return_if_fail (druid != NULL); g_return_if_fail (GNOME_IS_DRUID (druid)); gtk_widget_set_sensitive (druid->back, back_sensitive); gtk_widget_set_sensitive (druid->next, next_sensitive); gtk_widget_set_sensitive (druid->cancel, cancel_sensitive); } /** * gnome_druid_set_show_finish * @druid: A Druid widget. # @show_finish: If TRUE, then the "Cancel" button is changed to be "Finish" * * Description: Sets the text on the last button on the @druid. If @show_finish * is TRUE, then the text becomes "Finish". If @show_finish is FALSE, then the * text becomes "Cancel". **/ void gnome_druid_set_show_finish (GnomeDruid *druid, gboolean show_finish) { g_return_if_fail (druid != NULL); g_return_if_fail (GNOME_IS_DRUID (druid)); if (show_finish) { if (GTK_WIDGET_MAPPED (druid->next)) { gtk_widget_unmap (druid->next); gtk_widget_map (druid->finish); } } else { if (GTK_WIDGET_MAPPED (druid->finish)) { gtk_widget_unmap (druid->finish); gtk_widget_map (druid->next); } } druid->show_finish = show_finish; } /** * gnome_druid_prepend_page: * @druid: A Druid widget. * @page: The page to be inserted. * * Description: This will prepend a GnomeDruidPage into the internal list of * pages that the @druid has. **/ void gnome_druid_prepend_page (GnomeDruid *druid, GnomeDruidPage *page) { g_return_if_fail (druid != NULL); g_return_if_fail (GNOME_IS_DRUID (druid)); g_return_if_fail (page != NULL); g_return_if_fail (GNOME_IS_DRUID_PAGE (page)); gnome_druid_insert_page (druid, NULL, page); } /** * gnome_druid_insert_page: * @druid: A Druid widget. * @back_page: The page prior to the page to be inserted. * @page: The page to insert. * * Description: This will insert @page after @back_page into the list of * internal pages that the @druid has. If @back_page is not present in the list * or NULL, @page will be prepended to the list. **/ void gnome_druid_insert_page (GnomeDruid *druid, GnomeDruidPage *back_page, GnomeDruidPage *page) { GList *list; g_return_if_fail (druid != NULL); g_return_if_fail (GNOME_IS_DRUID (druid)); g_return_if_fail (page != NULL); g_return_if_fail (GNOME_IS_DRUID_PAGE (page)); list = g_list_find (druid->children, back_page); if (list == NULL) druid->children = g_list_prepend (druid->children, page); else { GList *new_el = g_list_alloc (); new_el->next = list->next; new_el->prev = list; if (new_el->next) new_el->next->prev = new_el; new_el->prev->next = new_el; new_el->data = (gpointer) page; } gtk_widget_set_parent (GTK_WIDGET (page), GTK_WIDGET (druid)); } /** * gnome_druid_append_page: * @druid: A Druid widget. * @page: The page to be appended. * * Description: This will append @page onto the end of the internal list. **/ void gnome_druid_append_page (GnomeDruid *druid, GnomeDruidPage *page) { GList *list; g_return_if_fail (druid != NULL); g_return_if_fail (GNOME_IS_DRUID (druid)); g_return_if_fail (page != NULL); g_return_if_fail (GNOME_IS_DRUID_PAGE (page)); list = g_list_last (druid->children); if (list) { gnome_druid_insert_page (druid, GNOME_DRUID_PAGE (list->data), page); } else { gnome_druid_insert_page (druid, NULL, page); } } /** * gnome_druid_set_page: * @druid: A Druid widget. * @page: The page to be brought to the foreground. * * Description: This will make @page the currently showing page in the druid. * @page must already be in the druid. **/ void gnome_druid_set_page (GnomeDruid *druid, GnomeDruidPage *page) { GList *list; GtkWidget *old = NULL; g_return_if_fail (druid != NULL); g_return_if_fail (GNOME_IS_DRUID (druid)); g_return_if_fail (page != NULL); g_return_if_fail (GNOME_IS_DRUID_PAGE (page)); if (druid->current == page) return; list = g_list_find (druid->children, page); g_return_if_fail (list != NULL); if ((druid->current) && (GTK_WIDGET_VISIBLE (druid->current)) && (GTK_WIDGET_MAPPED (druid))) { old = GTK_WIDGET (druid->current); } druid->current = GNOME_DRUID_PAGE (list->data); gnome_druid_page_prepare (druid->current); if (GTK_WIDGET_VISIBLE (druid->current) && (GTK_WIDGET_MAPPED (druid))) { gtk_widget_map (GTK_WIDGET (druid->current)); if (old) gtk_widget_unmap (old); } }