* Boston, MA 02111-1307, USA.
*/
-#include <config.h>
+#include "config.h"
+
+#include <atk/atk.h>
#include "gtkassistant.h"
+#include "gtkaccessible.h"
#include "gtkbutton.h"
#include "gtkhbox.h"
+#include "gtkhbbox.h"
#include "gtkimage.h"
#include "gtklabel.h"
#include "gtksizegroup.h"
#include "gtkintl.h"
#include "gtkprivate.h"
+#include "gtkbuildable.h"
#include "gtkalias.h"
#define GTK_ASSISTANT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_ASSISTANT, GtkAssistantPrivate))
#define HEADER_SPACING 12
+#define ACTION_AREA_SPACING 12
typedef struct _GtkAssistantPage GtkAssistantPage;
{
GtkWidget *page;
GtkAssistantPageType type;
- gboolean complete;
+ guint complete : 1;
+ guint complete_set : 1;
GtkWidget *title;
GdkPixbuf *header_image;
GdkEventAny *event);
static gboolean gtk_assistant_expose (GtkWidget *widget,
GdkEventExpose *event);
+static gboolean gtk_assistant_focus (GtkWidget *widget,
+ GtkDirectionType direction);
static void gtk_assistant_add (GtkContainer *container,
GtkWidget *page);
static void gtk_assistant_remove (GtkContainer *container,
GValue *value,
GParamSpec *pspec);
+static AtkObject *gtk_assistant_get_accessible (GtkWidget *widget);
+
+static void gtk_assistant_buildable_interface_init (GtkBuildableIface *iface);
+static GObject *gtk_assistant_buildable_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *childname);
+static gboolean gtk_assistant_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GMarkupParser *parser,
+ gpointer *data);
+static void gtk_assistant_buildable_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer user_data);
+
+static GList* find_page (GtkAssistant *assistant,
+ GtkWidget *page);
+
enum
{
CHILD_PROP_0,
static guint signals [LAST_SIGNAL] = { 0 };
-G_DEFINE_TYPE (GtkAssistant, gtk_assistant, GTK_TYPE_WINDOW);
+G_DEFINE_TYPE_WITH_CODE (GtkAssistant, gtk_assistant, GTK_TYPE_WINDOW,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+ gtk_assistant_buildable_interface_init))
static void
widget_class->unmap = gtk_assistant_unmap;
widget_class->delete_event = gtk_assistant_delete_event;
widget_class->expose_event = gtk_assistant_expose;
+ widget_class->focus = gtk_assistant_focus;
+ widget_class->get_accessible = gtk_assistant_get_accessible;
container_class->add = gtk_assistant_add;
container_class->remove = gtk_assistant_remove;
* @assistant: the #GtkAssistant
* @page: the current page
*
- * The ::prepared signal is emitted when a new page is set as the assistant's
- * current page, before making the new page visible. A handler for this signal
+ * The ::prepare signal is emitted when a new page is set as the assistant's
+ * current page, before making the new page visible. A handler for this signal
* can do any preparation which are necessary before showing @page.
*
* Since: 2.10
* @assistant: the @GtkAssistant
*
* The ::apply signal is emitted when the apply button is clicked. The default
- * behavior of the #GtkAssistant is to switch to the page after the current page,
- * unless the current page is the last one.
+ * behavior of the #GtkAssistant is to switch to the page after the current
+ * page, unless the current page is the last one.
*
- * A handler for the ::apply signal should carry out the actions for which the
- * wizard has collected data. If the action takes a long time to complete, you
- * might consider to put a page displaying the progress of the operation after the
- * confirmation page with the apply button.
- *
- * Return value: %TRUE to suppress the default behavior
+ * A handler for the ::apply signal should carry out the actions for which
+ * the wizard has collected data. If the action takes a long time to complete,
+ * you might consider to put a page of type %GTK_ASSISTANT_PAGE_PROGRESS
+ * after the confirmation page and handle this operation within the
+ * #GtkAssistant::prepare signal of the progress page.
*
* Since: 2.10
*/
* GtkAssistant::close:
* @assistant: the #GtkAssistant
*
- * The ::close signal is emitted when the close button is clicked.
+ * The ::close signal is emitted either when the close button of
+ * a summary page is clicked, or when the apply button in the last
+ * page in the flow (of type %GTK_ASSISTANT_PAGE_CONFIRM) is clicked.
*
* Since: 2.10
*/
/**
* GtkAssistant:page-type:
*
- * The type of the assistant page.
+ * The type of the assistant page.
*
* Since: 2.10
*/
/**
* GtkAssistant:title:
*
- * The title that is displayed in the page header.
+ * The title that is displayed in the page header.
*
* If title and header-image are both %NULL, no header is displayed.
*
/**
* GtkAssistant:header-image:
*
- * The image that is displayed next to the page.
+ * The image that is displayed next to the page.
*
* Set this to %NULL to make the sidebar disappear.
*
page_info = (GtkAssistantPage *) page_node->data;
- while (!GTK_WIDGET_VISIBLE (page_info->page))
+ while (page_node && !GTK_WIDGET_VISIBLE (page_info->page))
{
page_node = page_node->next;
- page_info = (GtkAssistantPage *) page_node->data;
current_page++;
+
+ if (page_node)
+ page_info = (GtkAssistantPage *) page_node->data;
}
return current_page;
compute_last_button_state (GtkAssistant *assistant)
{
GtkAssistantPrivate *priv = assistant->priv;
- GtkAssistantPage *page_info;
+ GtkAssistantPage *page_info, *current_page_info;
gint count, page_num, n_pages;
count = 0;
page_num = gtk_assistant_get_current_page (assistant);
n_pages = gtk_assistant_get_n_pages (assistant);
- page_info = g_list_nth_data (priv->pages, page_num);
+ current_page_info = page_info = g_list_nth_data (priv->pages, page_num);
- while ((page_num > 0 && page_num < n_pages) &&
- (page_info->type == GTK_ASSISTANT_PAGE_CONTENT) &&
- (page_info->complete))
+ while (page_num >= 0 && page_num < n_pages &&
+ page_info->type == GTK_ASSISTANT_PAGE_CONTENT &&
+ (count == 0 || page_info->complete) &&
+ count < n_pages)
{
- page_num = (priv->forward_function) (page_num, priv->forward_function_data);
+ page_num = (priv->forward_function) (page_num, priv->forward_function_data);
page_info = g_list_nth_data (priv->pages, page_num);
+
count++;
}
- if (count > 1)
- gtk_widget_show (assistant->last);
+ /* make the last button visible if we can skip multiple
+ * pages and end on a confirmation or summary page
+ */
+ if (count > 1 && page_info &&
+ (page_info->type == GTK_ASSISTANT_PAGE_CONFIRM ||
+ page_info->type == GTK_ASSISTANT_PAGE_SUMMARY))
+ {
+ gtk_widget_show (assistant->last);
+ gtk_widget_set_sensitive (assistant->last,
+ current_page_info->complete);
+ }
else
gtk_widget_hide (assistant->last);
}
static void
-_set_assistant_header_image (GtkAssistant *assistant)
+set_assistant_header_image (GtkAssistant *assistant)
{
GtkAssistantPrivate *priv = assistant->priv;
}
static void
-_set_assistant_sidebar_image (GtkAssistant *assistant)
+set_assistant_sidebar_image (GtkAssistant *assistant)
{
GtkAssistantPrivate *priv = assistant->priv;
}
static void
-_set_assistant_buttons_state (GtkAssistant *assistant)
+set_assistant_buttons_state (GtkAssistant *assistant)
{
GtkAssistantPrivate *priv = assistant->priv;
+ if (!priv->current_page)
+ return;
+
switch (priv->current_page->type)
{
case GTK_ASSISTANT_PAGE_INTRO:
gtk_widget_set_sensitive (assistant->cancel, TRUE);
gtk_widget_set_sensitive (assistant->forward, priv->current_page->complete);
+ gtk_widget_grab_default (assistant->forward);
gtk_widget_show (assistant->cancel);
gtk_widget_show (assistant->forward);
gtk_widget_hide (assistant->back);
gtk_widget_set_sensitive (assistant->cancel, TRUE);
gtk_widget_set_sensitive (assistant->back, TRUE);
gtk_widget_set_sensitive (assistant->apply, priv->current_page->complete);
+ gtk_widget_grab_default (assistant->apply);
gtk_widget_show (assistant->cancel);
gtk_widget_show (assistant->back);
gtk_widget_show (assistant->apply);
gtk_widget_set_sensitive (assistant->cancel, TRUE);
gtk_widget_set_sensitive (assistant->back, TRUE);
gtk_widget_set_sensitive (assistant->forward, priv->current_page->complete);
+ gtk_widget_grab_default (assistant->forward);
gtk_widget_show (assistant->cancel);
gtk_widget_show (assistant->back);
gtk_widget_show (assistant->forward);
compute_last_button_state (assistant);
break;
case GTK_ASSISTANT_PAGE_SUMMARY:
- gtk_widget_set_sensitive (assistant->close, TRUE);
+ gtk_widget_set_sensitive (assistant->close, priv->current_page->complete);
+ gtk_widget_grab_default (assistant->close);
gtk_widget_show (assistant->close);
gtk_widget_hide (assistant->cancel);
gtk_widget_hide (assistant->back);
gtk_widget_set_sensitive (assistant->cancel, priv->current_page->complete);
gtk_widget_set_sensitive (assistant->back, priv->current_page->complete);
gtk_widget_set_sensitive (assistant->forward, priv->current_page->complete);
+ gtk_widget_grab_default (assistant->forward);
gtk_widget_show (assistant->cancel);
gtk_widget_show (assistant->back);
gtk_widget_show (assistant->forward);
}
static void
-_set_current_page (GtkAssistant *assistant,
- GtkAssistantPage *page)
+set_current_page (GtkAssistant *assistant,
+ GtkAssistantPage *page)
{
GtkAssistantPrivate *priv = assistant->priv;
GtkAssistantPage *old_page;
priv->current_page = page;
- _set_assistant_buttons_state (assistant);
- _set_assistant_header_image (assistant);
- _set_assistant_sidebar_image (assistant);
+ set_assistant_buttons_state (assistant);
+ set_assistant_header_image (assistant);
+ set_assistant_sidebar_image (assistant);
g_signal_emit (assistant, signals [PREPARE], 0, priv->current_page->page);
if (GTK_WIDGET_VISIBLE (priv->current_page->page) && GTK_WIDGET_MAPPED (assistant))
{
+ gtk_widget_set_child_visible (priv->current_page->page, TRUE);
gtk_widget_map (priv->current_page->page);
gtk_widget_map (priv->current_page->title);
}
if (old_page && GTK_WIDGET_MAPPED (old_page->page))
{
+ gtk_widget_set_child_visible (old_page->page, FALSE);
gtk_widget_unmap (old_page->page);
gtk_widget_unmap (old_page->title);
}
+ if (!gtk_widget_child_focus (priv->current_page->page, GTK_DIR_TAB_FORWARD))
+ {
+ GtkWidget *button[6];
+ gint i;
+
+ /* find the best button to focus */
+ button[0] = assistant->apply;
+ button[1] = assistant->close;
+ button[2] = assistant->forward;
+ button[3] = assistant->back;
+ button[4] = assistant->cancel;
+ button[5] = assistant->last;
+ for (i = 0; i < 6; i++)
+ {
+ if (GTK_WIDGET_VISIBLE (button[i]) && GTK_WIDGET_SENSITIVE (button[i]))
+ {
+ gtk_widget_grab_focus (button[i]);
+ break;
+ }
+ }
+ }
+
gtk_widget_queue_resize (GTK_WIDGET (assistant));
}
if (next_page >= 0 && next_page < n_pages)
{
priv->visited_pages = g_slist_prepend (priv->visited_pages, page_info);
- _set_current_page (assistant, g_list_nth_data (priv->pages, next_page));
+ set_current_page (assistant, g_list_nth_data (priv->pages, next_page));
return TRUE;
}
}
static void
-on_assistant_close (GtkWidget *widget, GtkAssistant *assistant)
+on_assistant_close (GtkWidget *widget,
+ GtkAssistant *assistant)
{
g_signal_emit (assistant, signals [CLOSE], 0, NULL);
}
static void
-on_assistant_apply (GtkWidget *widget, GtkAssistant *assistant)
+on_assistant_apply (GtkWidget *widget,
+ GtkAssistant *assistant)
{
- GtkAssistantPrivate *priv = assistant->priv;
gboolean success;
- success = compute_next_step (assistant);
+ g_signal_emit (assistant, signals [APPLY], 0);
- g_signal_emit (assistant, signals [APPLY], 0, priv->current_page->page);
+ success = compute_next_step (assistant);
/* if the assistant hasn't switched to another page, just emit
* the CLOSE signal, it't the last page in the assistant flow
*/
if (!success)
- g_signal_emit (assistant, signals [CLOSE], 0, priv->current_page->page);
+ g_signal_emit (assistant, signals [CLOSE], 0);
}
static void
-on_assistant_forward (GtkWidget *widget, GtkAssistant *assistant)
+on_assistant_forward (GtkWidget *widget,
+ GtkAssistant *assistant)
{
if (!compute_next_step (assistant))
g_critical ("Page flow is broken, you may want to end it with a page of "
}
static void
-on_assistant_back (GtkWidget *widget, GtkAssistant *assistant)
+on_assistant_back (GtkWidget *widget,
+ GtkAssistant *assistant)
{
GtkAssistantPrivate *priv = assistant->priv;
GtkAssistantPage *page_info;
while (page_info->type == GTK_ASSISTANT_PAGE_PROGRESS ||
!GTK_WIDGET_VISIBLE (page_info->page));
- _set_current_page (assistant, page_info);
+ set_current_page (assistant, page_info);
}
static void
-on_assistant_cancel (GtkWidget *widget, GtkAssistant *assistant)
+on_assistant_cancel (GtkWidget *widget,
+ GtkAssistant *assistant)
{
g_signal_emit (assistant, signals [CANCEL], 0, NULL);
}
static void
-on_assistant_last (GtkWidget *widget, GtkAssistant *assistant)
+on_assistant_last (GtkWidget *widget,
+ GtkAssistant *assistant)
{
GtkAssistantPrivate *priv = assistant->priv;
priv = assistant->priv = GTK_ASSISTANT_GET_PRIVATE (assistant);
+ gtk_container_set_reallocate_redraws (GTK_CONTAINER (assistant), TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (assistant), 12);
+
gtk_widget_push_composite_child ();
/* Header */
gtk_widget_set_parent (priv->sidebar_image, GTK_WIDGET (assistant));
gtk_widget_show (priv->sidebar_image);
- /* Action area */
- priv->action_area = gtk_hbox_new (FALSE, 12);
- gtk_container_set_border_width (GTK_CONTAINER (priv->action_area), 6);
+ /* Action area */
+ priv->action_area = gtk_hbox_new (FALSE, 6);
+
assistant->close = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
assistant->apply = gtk_button_new_from_stock (GTK_STOCK_APPLY);
assistant->forward = gtk_button_new_from_stock (GTK_STOCK_GO_FORWARD);
assistant->back = gtk_button_new_from_stock (GTK_STOCK_GO_BACK);
assistant->cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
assistant->last = gtk_button_new_from_stock (GTK_STOCK_GOTO_LAST);
+ GTK_WIDGET_SET_FLAGS (assistant->close, GTK_CAN_DEFAULT);
+ GTK_WIDGET_SET_FLAGS (assistant->apply, GTK_CAN_DEFAULT);
+ GTK_WIDGET_SET_FLAGS (assistant->forward, GTK_CAN_DEFAULT);
priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
gtk_size_group_add_widget (priv->size_group, assistant->close);
if (!alternative_button_order (assistant))
{
- gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->close, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->apply, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->forward, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->back, FALSE, FALSE, 0);
- gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->cancel, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->last, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->cancel, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->close, FALSE, FALSE, 0);
}
else
{
- gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->cancel, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->close, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->cancel, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->apply, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->forward, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->back, FALSE, FALSE, 0);
}
}
+static void
+on_page_notify_visibility (GtkWidget *widget,
+ GParamSpec *arg,
+ gpointer data)
+{
+ GtkAssistant *assistant = GTK_ASSISTANT (data);
+
+ /* update buttons state, flow may have changed */
+ if (GTK_WIDGET_MAPPED (assistant))
+ set_assistant_buttons_state (assistant);
+}
+
static void
remove_page (GtkAssistant *assistant,
GList *element)
{
GtkAssistantPrivate *priv = assistant->priv;
GtkAssistantPage *page_info;
+ GList *page_node;
page_info = element->data;
- /* If we are mapped and visible, we want to deal with changing the page. */
- if ((GTK_WIDGET_MAPPED (page_info->page)) && (page_info == priv->current_page))
- compute_next_step (assistant);
+ /* If this is the current page, we need to switch away. */
+ if (page_info == priv->current_page)
+ {
+ if (!compute_next_step (assistant))
+ {
+ /* The best we can do at this point is probably to pick the first
+ * visible page.
+ */
+ page_node = priv->pages;
+
+ while (page_node && !GTK_WIDGET_VISIBLE (((GtkAssistantPage *) page_node->data)->page))
+ page_node = page_node->next;
+
+ if (page_node == element)
+ page_node = page_node->next;
+
+ if (page_node)
+ priv->current_page = page_node->data;
+ else
+ priv->current_page = NULL;
+ }
+ }
priv->pages = g_list_remove_link (priv->pages, element);
-
priv->visited_pages = g_slist_remove_all (priv->visited_pages, page_info);
+
+ g_signal_handlers_disconnect_by_func (page_info->page, on_page_notify_visibility, assistant);
gtk_widget_unparent (page_info->page);
if (page_info->header_image)
g_object_unref (page_info->sidebar_image);
gtk_widget_destroy (page_info->title);
- g_free (page_info);
+ g_slice_free (GtkAssistantPage, page_info);
g_list_free_1 (element);
}
gtk_widget_size_request (priv->action_area, &child_requisition);
width = MAX (width, child_requisition.width);
- height += child_requisition.height;
+ height += child_requisition.height + ACTION_AREA_SPACING;
width += GTK_CONTAINER (widget)->border_width * 2 + content_padding * 2;
height += GTK_CONTAINER (widget)->border_width * 2 + content_padding * 2;
/* Header */
gtk_widget_get_child_requisition (priv->header_image, &header_requisition);
- header_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width + header_padding;
- header_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width + header_padding;
+ header_allocation.x = GTK_CONTAINER (widget)->border_width + header_padding;
+ header_allocation.y = GTK_CONTAINER (widget)->border_width + header_padding;
header_allocation.width = allocation->width - 2 * GTK_CONTAINER (widget)->border_width - 2 * header_padding;
header_allocation.height = header_requisition.height;
gtk_widget_size_allocate (priv->header_image, &header_allocation);
/* Action area */
- child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
- child_allocation.y = allocation->y + allocation->height -
+ child_allocation.x = GTK_CONTAINER (widget)->border_width;
+ child_allocation.y = allocation->height -
GTK_CONTAINER (widget)->border_width - priv->action_area->requisition.height;
child_allocation.width = allocation->width - 2 * GTK_CONTAINER (widget)->border_width;
child_allocation.height = priv->action_area->requisition.height;
/* Sidebar */
if (rtl)
- child_allocation.x = allocation->x + allocation->width -
+ child_allocation.x = allocation->width -
GTK_CONTAINER (widget)->border_width - priv->sidebar_image->requisition.width;
else
- child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
+ child_allocation.x = GTK_CONTAINER (widget)->border_width;
- child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width +
+ child_allocation.y = GTK_CONTAINER (widget)->border_width +
priv->header_image->allocation.height + 2 * header_padding;
child_allocation.width = priv->sidebar_image->requisition.width;
child_allocation.height = allocation->height - 2 * GTK_CONTAINER (widget)->border_width -
gtk_widget_size_allocate (priv->sidebar_image, &child_allocation);
/* Pages */
- child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width + content_padding;
- child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width +
+ child_allocation.x = GTK_CONTAINER (widget)->border_width + content_padding;
+ child_allocation.y = GTK_CONTAINER (widget)->border_width +
priv->header_image->allocation.height + 2 * header_padding + content_padding;
child_allocation.width = allocation->width - 2 * GTK_CONTAINER (widget)->border_width - 2 * content_padding;
child_allocation.height = allocation->height - 2 * GTK_CONTAINER (widget)->border_width -
- priv->header_image->allocation.height - 2 * header_padding - priv->action_area->allocation.height - 2 * content_padding;
+ priv->header_image->allocation.height - 2 * header_padding - ACTION_AREA_SPACING - priv->action_area->allocation.height - 2 * content_padding;
if (GTK_WIDGET_VISIBLE (priv->sidebar_image))
{
GtkAssistant *assistant = GTK_ASSISTANT (widget);
GtkAssistantPrivate *priv = assistant->priv;
GList *page_node;
+ GtkAssistantPage *page;
GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
gtk_widget_map (priv->sidebar_image);
/* if there's no default page, pick the first one */
- if (!priv->current_page && priv->pages)
+ page = NULL;
+ if (!priv->current_page)
{
page_node = priv->pages;
- while (!GTK_WIDGET_VISIBLE (((GtkAssistantPage *) page_node->data)->page))
+ while (page_node && !GTK_WIDGET_VISIBLE (((GtkAssistantPage *) page_node->data)->page))
page_node = page_node->next;
if (page_node)
- priv->current_page = page_node->data;
+ page = page_node->data;
}
- if (priv->current_page &&
- GTK_WIDGET_VISIBLE (priv->current_page->page) &&
- !GTK_WIDGET_MAPPED (priv->current_page->page))
- {
- _set_assistant_buttons_state ((GtkAssistant *) widget);
- _set_assistant_header_image ((GtkAssistant*) widget);
- _set_assistant_sidebar_image ((GtkAssistant*) widget);
-
- g_signal_emit (widget, signals [PREPARE], 0, priv->current_page->page);
- gtk_widget_map (priv->current_page->page);
- gtk_widget_map (priv->current_page->title);
- }
+ if (page &&
+ GTK_WIDGET_VISIBLE (page->page) &&
+ !GTK_WIDGET_MAPPED (page->page))
+ set_current_page (assistant, page);
GTK_WIDGET_CLASS (gtk_assistant_parent_class)->map (widget);
}
{
GtkAssistant *assistant = GTK_ASSISTANT (widget);
GtkAssistantPrivate *priv = assistant->priv;
- gint header_padding, content_padding;
+ gint border_width, header_padding, content_padding;
cairo_t *cr;
gint content_x, content_width;
gboolean rtl;
cr = gdk_cairo_create (widget->window);
rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
gtk_widget_style_get (widget,
"header-padding", &header_padding,
/* colored box */
gdk_cairo_set_source_color (cr, &widget->style->bg[GTK_STATE_SELECTED]);
cairo_rectangle (cr,
- 0, 0,
- widget->allocation.width,
- widget->allocation.height - priv->action_area->allocation.height);
+ border_width,
+ border_width,
+ widget->allocation.width - 2 * border_width,
+ widget->allocation.height - priv->action_area->allocation.height - 2 * border_width - ACTION_AREA_SPACING);
cairo_fill (cr);
/* content box */
- content_x = content_padding;
- content_width = widget->allocation.width - 2 * content_padding;
+ content_x = content_padding + border_width;
+ content_width = widget->allocation.width - 2 * content_padding - 2 * border_width;
if (GTK_WIDGET_VISIBLE (priv->sidebar_image))
{
cairo_rectangle (cr,
content_x,
- priv->header_image->allocation.height + content_padding + 2 * header_padding,
+ priv->header_image->allocation.height + content_padding + 2 * header_padding + border_width,
content_width,
- widget->allocation.height - priv->action_area->allocation.height -
- priv->header_image->allocation.height - 2 * content_padding - 2 * header_padding);
+ widget->allocation.height - 2 * border_width - priv->action_area->allocation.height -
+ priv->header_image->allocation.height - 2 * content_padding - 2 * header_padding - ACTION_AREA_SPACING);
cairo_fill (cr);
cairo_destroy (cr);
return FALSE;
}
+static gboolean
+gtk_assistant_focus (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ GtkAssistantPrivate *priv;
+ GtkContainer *container;
+
+ container = GTK_CONTAINER (widget);
+ priv = GTK_ASSISTANT (widget)->priv;
+
+ /* we only have to care about 2 widgets, action area and the current page */
+ if (container->focus_child == priv->action_area)
+ {
+ if (!gtk_widget_child_focus (priv->action_area, direction) &&
+ (priv->current_page == NULL ||
+ !gtk_widget_child_focus (priv->current_page->page, direction)))
+ {
+ /* if we're leaving the action area and the current page hasn't
+ any focusable widget, clear focus and go back to the action area */
+ gtk_container_set_focus_child (GTK_CONTAINER (priv->action_area), NULL);
+ gtk_widget_child_focus (priv->action_area, direction);
+ }
+ }
+ else
+ {
+ if ((priv->current_page == NULL ||
+ !gtk_widget_child_focus (priv->current_page->page, direction)) &&
+ !gtk_widget_child_focus (priv->action_area, direction))
+ {
+ /* if we're leaving the current page and there isn't nothing focusable
+ in the action area, try to clear focus and go back to the page */
+ gtk_window_set_focus (GTK_WINDOW (widget), NULL);
+ if (priv->current_page != NULL)
+ gtk_widget_child_focus (priv->current_page->page, direction);
+ }
+ }
+
+ return TRUE;
+}
+
static void
gtk_assistant_add (GtkContainer *container,
GtkWidget *page)
{
- g_return_if_fail (GTK_IS_WIDGET (page));
-
gtk_assistant_append_page (GTK_ASSISTANT (container), page);
}
gtk_assistant_remove (GtkContainer *container,
GtkWidget *page)
{
- GtkAssistant *assistant;
+ GtkAssistant *assistant = (GtkAssistant*) container;
GList *element;
- assistant = (GtkAssistant*) container;
-
element = find_page (assistant, page);
if (element)
* initial page is != to 0
*/
if (GTK_WIDGET_MAPPED (assistant))
- priv->visited_pages = g_slist_prepend (priv->visited_pages, page);
+ priv->visited_pages = g_slist_prepend (priv->visited_pages,
+ priv->current_page);
- _set_current_page (assistant, page);
+ set_current_page (assistant, page);
}
/**
g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
g_return_val_if_fail (page->parent == NULL, 0);
- g_return_val_if_fail (!GTK_WIDGET_TOPLEVEL (page), 0);
+ g_return_val_if_fail (!gtk_widget_is_toplevel (page), 0);
priv = assistant->priv;
- page_info = g_new0 (GtkAssistantPage, 1);
+ page_info = g_slice_new0 (GtkAssistantPage);
page_info->page = page;
page_info->title = gtk_label_new (NULL);
+ g_signal_connect (G_OBJECT (page), "notify::visible",
+ G_CALLBACK (on_page_notify_visibility), assistant);
+
gtk_misc_set_alignment (GTK_MISC (page_info->title), 0.,0.5);
set_title_colors (GTK_WIDGET (assistant), page_info->title);
set_title_font (GTK_WIDGET (assistant), page_info->title);
priv->pages = g_list_insert (priv->pages, page_info, position);
+ gtk_widget_set_child_visible (page_info->page, FALSE);
gtk_widget_set_parent (page_info->page, GTK_WIDGET (assistant));
gtk_widget_set_parent (page_info->title, GTK_WIDGET (assistant));
priv->forward_function_data = assistant;
priv->forward_data_destroy = NULL;
}
+
+ /* Page flow has possibly changed, so the
+ buttons state might need to change too */
+ set_assistant_buttons_state (assistant);
}
/**
* gtk_assistant_add_action_widget:
- * @dialog: a #GtkAssistant
+ * @assistant: a #GtkAssistant
* @child: a #GtkWidget
*
* Adds a widget to the action area of a #GtkAssistant.
/**
* gtk_assistant_remove_action_widget:
- * @dialog: a #GtkAssistant
+ * @assistant: a #GtkAssistant
* @child: a #GtkWidget
*
* Removes a widget from the action area of a #GtkAssistant.
/**
* gtk_assistant_set_page_title:
* @assistant: a #GtkAssistant
- * @page: a page of @assitant
+ * @page: a page of @assistant
* @title: the new title for @page
*
* Sets a title for @page. The title is displayed in the header
GtkWidget *page,
const gchar *title)
{
- GtkAssistantPrivate *priv;
GtkAssistantPage *page_info;
GList *child;
g_return_if_fail (GTK_IS_ASSISTANT (assistant));
g_return_if_fail (GTK_IS_WIDGET (page));
- priv = assistant->priv;
child = find_page (assistant, page);
g_return_if_fail (child != NULL);
gtk_assistant_get_page_title (GtkAssistant *assistant,
GtkWidget *page)
{
- GtkAssistantPrivate *priv;
GtkAssistantPage *page_info;
GList *child;
g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
- priv = assistant->priv;
child = find_page (assistant, page);
g_return_val_if_fail (child != NULL, NULL);
/**
* gtk_assistant_set_page_type:
* @assistant: a #GtkAssistant
- * @page: a page of @assitant
+ * @page: a page of @assistant
* @type: the new type for @page
*
* Sets the page type for @page. The page type determines the page
{
page_info->type = type;
+ /* backwards compatibility to the era before fixing bug 604289 */
+ if (type == GTK_ASSISTANT_PAGE_SUMMARY && !page_info->complete_set)
+ {
+ gtk_assistant_set_page_complete (assistant, page, TRUE);
+ page_info->complete_set = FALSE;
+ }
+
/* Always set buttons state, a change in a future page
might change current page buttons */
- if (priv->current_page)
- _set_assistant_buttons_state (assistant);
+ set_assistant_buttons_state (assistant);
gtk_widget_child_notify (page, "page-type");
}
gtk_assistant_get_page_type (GtkAssistant *assistant,
GtkWidget *page)
{
- GtkAssistantPrivate *priv;
GtkAssistantPage *page_info;
GList *child;
g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), GTK_ASSISTANT_PAGE_CONTENT);
g_return_val_if_fail (GTK_IS_WIDGET (page), GTK_ASSISTANT_PAGE_CONTENT);
- priv = assistant->priv;
child = find_page (assistant, page);
g_return_val_if_fail (child != NULL, GTK_ASSISTANT_PAGE_CONTENT);
/**
* gtk_assistant_set_page_header_image:
* @assistant: a #GtkAssistant
- * @page: a page of @assitant
- * @pixbuf: the new header image @page
- *
+ * @page: a page of @assistant
+ * @pixbuf: (allow-none): the new header image @page
+ *
* Sets a header image for @page. This image is displayed in the header
* area of the assistant when @page is the current page.
*
page_info->header_image = g_object_ref (pixbuf);
if (page_info == priv->current_page)
- _set_assistant_header_image (assistant);
+ set_assistant_header_image (assistant);
gtk_widget_child_notify (page, "header-image");
}
gtk_assistant_get_page_header_image (GtkAssistant *assistant,
GtkWidget *page)
{
- GtkAssistantPrivate *priv;
GtkAssistantPage *page_info;
GList *child;
g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
- priv = assistant->priv;
child = find_page (assistant, page);
g_return_val_if_fail (child != NULL, NULL);
/**
* gtk_assistant_set_page_side_image:
* @assistant: a #GtkAssistant
- * @page: a page of @assitant
- * @pixbuf: the new header image @page
- *
+ * @page: a page of @assistant
+ * @pixbuf: (allow-none): the new header image @page
+ *
* Sets a header image for @page. This image is displayed in the side
* area of the assistant when @page is the current page.
*
page_info->sidebar_image = g_object_ref (pixbuf);
if (page_info == priv->current_page)
- _set_assistant_sidebar_image (assistant);
+ set_assistant_sidebar_image (assistant);
gtk_widget_child_notify (page, "sidebar-image");
}
gtk_assistant_get_page_side_image (GtkAssistant *assistant,
GtkWidget *page)
{
- GtkAssistantPrivate *priv;
GtkAssistantPage *page_info;
GList *child;
g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
- priv = assistant->priv;
child = find_page (assistant, page);
g_return_val_if_fail (child != NULL, NULL);
/**
* gtk_assistant_set_page_complete:
* @assistant: a #GtkAssistant
- * @page: a page of @assitant
- * @pixbuf: the new header image @page
+ * @page: a page of @assistant
+ * @complete: the completeness status of the page
*
* Sets whether @page contents are complete. This will make
* @assistant update the buttons state to be able to continue the task.
if (complete != page_info->complete)
{
page_info->complete = complete;
+ page_info->complete_set = TRUE;
/* Always set buttons state, a change in a future page
might change current page buttons */
- if (priv->current_page)
- {
- /* Always set buttons state, a change in a future page
- might change current page buttons */
- _set_assistant_buttons_state (assistant);
- }
+ set_assistant_buttons_state (assistant);
gtk_widget_child_notify (page, "complete");
}
* @assistant: a #GtkAssistant
* @page: a page of @assistant
*
- * Gets whether @page is complete..
+ * Gets whether @page is complete.
*
* Return value: %TRUE if @page is complete.
*
gtk_assistant_get_page_complete (GtkAssistant *assistant,
GtkWidget *page)
{
- GtkAssistantPrivate *priv;
GtkAssistantPage *page_info;
GList *child;
g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), FALSE);
g_return_val_if_fail (GTK_IS_WIDGET (page), FALSE);
- priv = assistant->priv;
child = find_page (assistant, page);
g_return_val_if_fail (child != NULL, FALSE);
return page_info->complete;
}
+/**
+ * gtk_assistant_update_buttons_state:
+ * @assistant: a #GtkAssistant
+ *
+ * Forces @assistant to recompute the buttons state.
+ *
+ * GTK+ automatically takes care of this in most situations,
+ * e.g. when the user goes to a different page, or when the
+ * visibility or completeness of a page changes.
+ *
+ * One situation where it can be necessary to call this
+ * function is when changing a value on the current page
+ * affects the future page flow of the assistant.
+ *
+ * Since: 2.10
+ **/
+void
+gtk_assistant_update_buttons_state (GtkAssistant *assistant)
+{
+ g_return_if_fail (GTK_IS_ASSISTANT (assistant));
+
+ set_assistant_buttons_state (assistant);
+}
+
+
+
+/* accessible implementation */
+
+static gint
+gtk_assistant_accessible_get_n_children (AtkObject *accessible)
+{
+ GtkAssistant *assistant;
+ GtkWidget *widget;
+
+ widget = GTK_ACCESSIBLE (accessible)->widget;
+
+ if (!widget)
+ return 0;
+
+ assistant = GTK_ASSISTANT (widget);
+
+ return g_list_length (assistant->priv->pages) + 1;
+}
+
+
+static AtkObject *
+gtk_assistant_accessible_ref_child (AtkObject *accessible,
+ gint index)
+{
+ GtkAssistant *assistant;
+ GtkAssistantPrivate *priv;
+ GtkWidget *widget, *child;
+ gint n_pages;
+ AtkObject *obj;
+ const gchar *title;
+
+ widget = GTK_ACCESSIBLE (accessible)->widget;
+ if (!widget)
+ return NULL;
+
+ assistant = GTK_ASSISTANT (widget);
+ priv = assistant->priv;
+ n_pages = g_list_length (priv->pages);
+
+ if (index < 0)
+ return NULL;
+ else if (index < n_pages)
+ {
+ GtkAssistantPage *page = g_list_nth_data (priv->pages, index);
+
+ child = page->page;
+ title = gtk_assistant_get_page_title (assistant, child);
+ }
+ else if (index == n_pages)
+ {
+ child = priv->action_area;
+ title = NULL;
+ }
+ else
+ return NULL;
+
+ obj = gtk_widget_get_accessible (child);
+
+ if (title)
+ atk_object_set_name (obj, title);
+
+ return g_object_ref (obj);
+}
+
+static void
+gtk_assistant_accessible_class_init (AtkObjectClass *class)
+{
+ class->get_n_children = gtk_assistant_accessible_get_n_children;
+ class->ref_child = gtk_assistant_accessible_ref_child;
+}
+
+static GType
+gtk_assistant_accessible_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ /*
+ * Figure out the size of the class and instance
+ * we are deriving from
+ */
+ AtkObjectFactory *factory;
+ GType derived_type;
+ GTypeQuery query;
+ GType derived_atk_type;
+
+ derived_type = g_type_parent (GTK_TYPE_ASSISTANT);
+ factory = atk_registry_get_factory (atk_get_default_registry (),
+ derived_type);
+ derived_atk_type = atk_object_factory_get_accessible_type (factory);
+ g_type_query (derived_atk_type, &query);
+
+ type = g_type_register_static_simple (derived_atk_type,
+ I_("GtkAssistantAccessible"),
+ query.class_size,
+ (GClassInitFunc) gtk_assistant_accessible_class_init,
+ query.instance_size,
+ NULL, 0);
+ }
+
+ return type;
+}
+
+static AtkObject *
+gtk_assistant_accessible_new (GObject *obj)
+{
+ AtkObject *accessible;
+
+ g_return_val_if_fail (GTK_IS_ASSISTANT (obj), NULL);
+
+ accessible = g_object_new (gtk_assistant_accessible_get_type (), NULL);
+ atk_object_initialize (accessible, obj);
+
+ return accessible;
+}
+
+static GType
+gtk_assistant_accessible_factory_get_accessible_type (void)
+{
+ return gtk_assistant_accessible_get_type ();
+}
+
+static AtkObject*
+gtk_assistant_accessible_factory_create_accessible (GObject *obj)
+{
+ return gtk_assistant_accessible_new (obj);
+}
+
+static void
+gtk_assistant_accessible_factory_class_init (AtkObjectFactoryClass *class)
+{
+ class->create_accessible = gtk_assistant_accessible_factory_create_accessible;
+ class->get_accessible_type = gtk_assistant_accessible_factory_get_accessible_type;
+}
+
+static GType
+gtk_assistant_accessible_factory_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ type = g_type_register_static_simple (ATK_TYPE_OBJECT_FACTORY,
+ I_("GtkAssistantAccessibleFactory"),
+ sizeof (AtkObjectFactoryClass),
+ (GClassInitFunc) gtk_assistant_accessible_factory_class_init,
+ sizeof (AtkObjectFactory),
+ NULL, 0);
+ }
+
+ return type;
+}
+
+static AtkObject *
+gtk_assistant_get_accessible (GtkWidget *widget)
+{
+ static gboolean first_time = TRUE;
+
+ if (first_time)
+ {
+ AtkObjectFactory *factory;
+ AtkRegistry *registry;
+ GType derived_type;
+ GType derived_atk_type;
+
+ /*
+ * Figure out whether accessibility is enabled by looking at the
+ * type of the accessible object which would be created for
+ * the parent type of GtkAssistant.
+ */
+ derived_type = g_type_parent (GTK_TYPE_ASSISTANT);
+
+ registry = atk_get_default_registry ();
+ factory = atk_registry_get_factory (registry,
+ derived_type);
+ derived_atk_type = atk_object_factory_get_accessible_type (factory);
+ if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
+ {
+ atk_registry_set_factory_type (registry,
+ GTK_TYPE_ASSISTANT,
+ gtk_assistant_accessible_factory_get_type ());
+ }
+ first_time = FALSE;
+ }
+
+ return GTK_WIDGET_CLASS (gtk_assistant_parent_class)->get_accessible (widget);
+}
+
+
+static GtkBuildableIface *parent_buildable_iface;
+
+static void
+gtk_assistant_buildable_interface_init (GtkBuildableIface *iface)
+{
+ parent_buildable_iface = g_type_interface_peek_parent (iface);
+ iface->get_internal_child = gtk_assistant_buildable_get_internal_child;
+ iface->custom_tag_start = gtk_assistant_buildable_custom_tag_start;
+ iface->custom_finished = gtk_assistant_buildable_custom_finished;
+}
+
+static GObject *
+gtk_assistant_buildable_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *childname)
+{
+ if (strcmp (childname, "action_area") == 0)
+ return G_OBJECT (GTK_ASSISTANT (buildable)->priv->action_area);
+
+ return parent_buildable_iface->get_internal_child (buildable,
+ builder,
+ childname);
+}
+
+gboolean
+gtk_assistant_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GMarkupParser *parser,
+ gpointer *data)
+{
+ return parent_buildable_iface->custom_tag_start (buildable, builder, child,
+ tagname, parser, data);
+}
+
+static void
+gtk_assistant_buildable_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer user_data)
+{
+ parent_buildable_iface->custom_finished (buildable, builder, child,
+ tagname, user_data);
+}
+
#define __GTK_ASSISTANT_C__
#include "gtkaliasdef.c"