diff options
Diffstat (limited to 'gtk/gtkwidget.c')
-rw-r--r-- | gtk/gtkwidget.c | 586 |
1 files changed, 525 insertions, 61 deletions
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 234ad3ae03..b8abd70b45 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -150,6 +150,9 @@ static gint gtk_widget_real_key_release_event (GtkWidget *widget, GdkEventKey *event); static void gtk_widget_style_set (GtkWidget *widget, GtkStyle *previous_style); + +static void gtk_widget_redraw_queue_remove (GtkWidget *widget); + static GdkColormap* gtk_widget_peek_colormap (void); static GdkVisual* gtk_widget_peek_visual (void); @@ -199,7 +202,8 @@ static const gchar *shape_info_key = "gtk-shape-info"; static const gchar *colormap_key = "gtk-colormap"; static const gchar *visual_key = "gtk-visual"; - +static const gchar *rc_style_key = "gtk-rc-style"; +static guint rc_style_key_id = 0; /***************************************** * gtk_widget_get_type: @@ -1210,6 +1214,7 @@ gtk_widget_unparent (GtkWidget *widget) * should eventually move into some gtk_window_unparent_branch() or * similar function. */ + toplevel = gtk_widget_get_toplevel (widget); if (GTK_CONTAINER (widget->parent)->focus_child == widget) { @@ -1241,6 +1246,9 @@ gtk_widget_unparent (GtkWidget *widget) gtk_window_set_default (GTK_WINDOW (toplevel), NULL); } + if (GTK_WIDGET_REDRAW_PENDING (widget)) + gtk_widget_redraw_queue_remove (widget); + if (GTK_IS_RESIZE_CONTAINER (widget)) gtk_container_clear_resize_widgets (GTK_CONTAINER (widget)); @@ -1308,15 +1316,10 @@ gtk_widget_unparent (GtkWidget *widget) toplevel = toplevel->parent; } - if (widget->window && GTK_WIDGET_NO_WINDOW (widget) && GTK_WIDGET_DRAWABLE (widget)) - gdk_window_clear_area (widget->window, - widget->allocation.x, - widget->allocation.y, - widget->allocation.width, - widget->allocation.height); + gtk_widget_queue_clear (widget); /* Reset the width and height here, to force reallocation if we * get added back to a new parent. This won't work if our new @@ -1605,12 +1608,9 @@ gtk_widget_unrealize (GtkWidget *widget) { g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_WIDGET (widget)); - + if (GTK_WIDGET_REDRAW_PENDING (widget)) - { - gtk_widget_redraw_queue = g_slist_remove (gtk_widget_redraw_queue, widget); - GTK_PRIVATE_UNSET_FLAG (widget, GTK_REDRAW_PENDING); - } + gtk_widget_redraw_queue_remove (widget); if (GTK_WIDGET_HAS_SHAPE_MASK (widget)) gtk_widget_shape_combine_mask (widget, NULL, -1, -1); @@ -1630,45 +1630,495 @@ gtk_widget_unrealize (GtkWidget *widget) * results: *****************************************/ -static gint -gtk_widget_idle_draw (gpointer data) +typedef struct _GtkDrawData GtkDrawData; +struct _GtkDrawData { + GdkRectangle rect; + GdkWindow *window; +}; + +static GMemChunk *draw_data_mem_chunk = NULL; +static GSList *draw_data_free_list = NULL; +static const gchar *draw_data_key = "gtk-draw-data"; +static guint draw_data_key_id = 0; + +static gint gtk_widget_idle_draw (gpointer data); + +static void +gtk_widget_queue_draw_data (GtkWidget *widget, + gint x, + gint y, + gint width, + gint height, + GdkWindow *window) { - while (gtk_widget_redraw_queue) - gtk_widget_draw (gtk_widget_redraw_queue->data, NULL); + GSList *node; + GtkDrawData *data; + + g_return_if_fail (widget != NULL); - return FALSE; + if ((width != 0) && (height != 0) && GTK_WIDGET_DRAWABLE (widget)) + { + if (!draw_data_key_id) + draw_data_key_id = g_quark_from_static_string (draw_data_key); + + if (draw_data_free_list) + { + node = draw_data_free_list; + data = node->data; + draw_data_free_list = draw_data_free_list->next; + } + else + { + if (!draw_data_mem_chunk) + draw_data_mem_chunk = g_mem_chunk_create (GtkDrawData, 64, + G_ALLOC_ONLY); + data = g_chunk_new (GtkDrawData, draw_data_mem_chunk); + node = g_slist_alloc(); + node->data = data; + } + + data->rect.x = x; + data->rect.y = y; + + if ((width < 0) || (height < 0)) + { + data->rect.width = 0; + data->rect.height = 0; + } + else + { + data->rect.width = width; + data->rect.height = height; + } + data->window = window; + + if ((width < 0) || (height < 0)) + { + GSList *draw_data_list = + gtk_object_get_data_by_id (GTK_OBJECT (widget), + draw_data_key_id); + if (draw_data_list) + draw_data_free_list = g_slist_concat (draw_data_list, + draw_data_free_list); + node->next = NULL; + } + else + node->next = gtk_object_get_data_by_id (GTK_OBJECT (widget), + draw_data_key_id); + + if (!GTK_WIDGET_REDRAW_PENDING (widget)) + { + GTK_PRIVATE_SET_FLAG (widget, GTK_REDRAW_PENDING); + if (gtk_widget_redraw_queue == NULL) + gtk_idle_add_priority (GTK_PRIORITY_INTERNAL, + gtk_widget_idle_draw, + NULL); + gtk_widget_redraw_queue = g_slist_prepend (gtk_widget_redraw_queue, widget); + } + + gtk_object_set_data_by_id (GTK_OBJECT (widget), draw_data_key_id, node); + } } -void +void +gtk_widget_queue_draw_area (GtkWidget *widget, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gtk_widget_queue_draw_data (widget, x, y, width, height, NULL); +} + +void gtk_widget_queue_draw (GtkWidget *widget) { + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gtk_widget_queue_draw_data (widget, 0, 0, -1, -1, NULL); +} + +void +gtk_widget_queue_clear_area (GtkWidget *widget, + gint x, + gint y, + gint width, + gint height) +{ GtkWidget *parent; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_WIDGET (widget)); - - if (GTK_WIDGET_DRAWABLE (widget)) + + /* Find the correct widget */ + + if (GTK_WIDGET_NO_WINDOW (widget)) { - /* We queue the redraw if: - * a) the widget is not already queued for redraw and - * b) non of the widgets ancestors are queued for redraw. - */ parent = widget; - while (parent) + while (parent && GTK_WIDGET_NO_WINDOW (parent)) + parent = parent->parent; + + if (parent) + gtk_widget_queue_draw_data (parent, x, y, width, height, widget->window); + } + else + { + gint wx, wy, wwidth, wheight; + /* Translate widget relative to window-relative */ + + gdk_window_get_position (widget->window, &wx, &wy); + x -= wx - widget->allocation.x; + y -= wy - widget->allocation.y; + + gdk_window_get_size (widget->window, &wwidth, &wheight); + + if (x < 0) { - if (GTK_WIDGET_REDRAW_PENDING (parent)) - return; - parent = parent->parent; + width += x; x = 0; } + if (y < 0) + { + height += y; y = 0; + } + if (x + width > wwidth) + width = wwidth - x; + if (y + height > wheight) + height = wheight - y; + + gtk_widget_queue_draw_data (widget, x, y, width, height, NULL); + } +} + +static void +gtk_widget_redraw_queue_remove (GtkWidget *widget) +{ + GSList *draw_data_list; + GSList *tmp_list; + + g_return_if_fail (GTK_WIDGET_REDRAW_PENDING (widget)); + + gtk_widget_redraw_queue = g_slist_remove (gtk_widget_redraw_queue, widget); + + draw_data_list = gtk_object_get_data_by_id (GTK_OBJECT (widget), + draw_data_key_id); + tmp_list = g_slist_last (draw_data_list); + if (tmp_list) + { + tmp_list->next = draw_data_free_list; + draw_data_free_list = draw_data_list; + } + + gtk_object_set_data_by_id (GTK_OBJECT (widget), + draw_data_key_id, + NULL); + + GTK_PRIVATE_UNSET_FLAG (widget, GTK_REDRAW_PENDING); +} + +void +gtk_widget_queue_clear (GtkWidget *widget) +{ + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if (GTK_WIDGET_NO_WINDOW (widget)) + gtk_widget_queue_clear_area (widget, widget->allocation.x, + widget->allocation.y, + widget->allocation.width, + widget->allocation.height); + else + gtk_widget_queue_clear_area (widget, 0, 0, + widget->allocation.width, + widget->allocation.height); +} + +static gint +gtk_widget_draw_data_combine (GtkDrawData *parent, GtkDrawData *child) +{ + gint parent_x2, parent_y2; + gint child_x2, child_y2; + + /* Check for intersection */ + + parent_x2 = parent->rect.x + parent->rect.width; + child_x2 = child->rect.x + child->rect.width; + parent_y2 = parent->rect.y + parent->rect.height; + child_y2 = child->rect.y + child->rect.height; + + if ((child->rect.x > parent_x2) || (parent->rect.x > child_x2) || + (child->rect.y > parent_y2) || (parent->rect.y > child_y2)) + return FALSE; + else + { + parent->rect.x = MIN (parent->rect.x, child->rect.x); + parent->rect.y = MIN (parent->rect.y, child->rect.y); + parent->rect.width = MAX (parent_x2, child_x2) - parent->rect.x; + parent->rect.height = MAX (parent_y2, child_y2) - parent->rect.y; + } + + return TRUE; +} + +/* Take a rectangle with respect to window, and translate it + * to coordinates relative to widget's allocation, clipping through + * intermediate windows. Returns whether translation failed. If the + * translation failed, we have something like a handlebox, where + * the child widget's GdkWindow is not a child of the parents GdkWindow. + */ +static gboolean +gtk_widget_clip_rect (GtkWidget *widget, + GdkWindow *window, + GdkRectangle *rect, + gint *x_offset, + gint *y_offset) +{ + gint x,y, width, height; + + while (window && (window != widget->window)) + { + gdk_window_get_position (window, &x, &y); + rect->x += x; + if (x_offset) + *x_offset += x; + rect->y += y; + if (y_offset) + *y_offset += y; - GTK_PRIVATE_SET_FLAG (widget, GTK_REDRAW_PENDING); - if (gtk_widget_redraw_queue == NULL) - gtk_idle_add_priority (GTK_PRIORITY_INTERNAL, - gtk_widget_idle_draw, - NULL); + window = gdk_window_get_parent (window); + if (!window) + return FALSE; - gtk_widget_redraw_queue = g_slist_prepend (gtk_widget_redraw_queue, widget); + gdk_window_get_size (window, &width, &height); + + if (rect->x < 0) + { + rect->width += rect->x; + rect->x = 0; + } + if (rect->y < 0) + { + rect->height += rect->y; + rect->y = 0; + } + if (rect->x + rect->width > width) + rect->width = width - rect->x; + if (rect->y + rect->height > height) + rect->height = height - rect->y; } + + if (!window) + return FALSE; + + if (!GTK_WIDGET_NO_WINDOW (widget)) + { + if (gdk_window_get_toplevel (window) != window) + { + gdk_window_get_position (window, &x, &y); + rect->x += x - widget->allocation.x; + if (x_offset) + *x_offset += x - widget->allocation.x; + rect->y += y - widget->allocation.y; + if (y_offset) + *y_offset += y - widget->allocation.y; + } + } + + return TRUE; +} + +static gint +gtk_widget_idle_draw (gpointer data) +{ + GSList *widget_list; + GSList *draw_data_list; + GtkWidget *widget; + + /* Translate all draw requests to be allocation-relative */ + widget_list = gtk_widget_redraw_queue; + while (widget_list) + { + widget = widget_list->data; + draw_data_list = gtk_object_get_data_by_id (GTK_OBJECT (widget), + draw_data_key_id); + + while (draw_data_list) + { + gboolean full_allocation = FALSE; + GtkDrawData *data = draw_data_list->data; + + if (data->window) + { + /* If the translation fails, we have a handlebox, + * so redraw the whole widget. Could be done better? + */ + full_allocation = !gtk_widget_clip_rect (widget, + data->window, + &data->rect, + NULL, NULL); + data->window = NULL; + } + else + { + if ((data->rect.width == 0) && (data->rect.height == 0)) + { + if (GTK_WIDGET_NO_WINDOW (widget)) + { + data->rect.x = widget->allocation.x; + data->rect.y = widget->allocation.y; + } + else + { + data->rect.x = 0; + data->rect.y = 0; + } + data->rect.width = widget->allocation.width; + data->rect.height = widget->allocation.height; + } + } + + draw_data_list = draw_data_list->next; + } + + widget_list = widget_list->next; + } + + /* Coalesce redraws + */ + widget_list = gtk_widget_redraw_queue; + while (widget_list) + { + GSList *prev_node = NULL; + widget = widget_list->data; + draw_data_list = gtk_object_get_data_by_id (GTK_OBJECT (widget), + draw_data_key_id); + + while (draw_data_list) + { + gint x_offset, y_offset; + GtkDrawData *data = draw_data_list->data; + GSList *parent_list = draw_data_list->next; + GtkWidget *parent; + GdkWindow *window; + + x_offset = 0; + y_offset = 0; + + parent = widget; + while (parent) + { + while (parent_list) + { + if (gtk_widget_draw_data_combine (parent_list->data, data)) + { + GSList *tmp; + if (prev_node) + prev_node->next = draw_data_list->next; + else + gtk_object_set_data_by_id (GTK_OBJECT (widget), + draw_data_key_id, + draw_data_list->next); + + tmp = draw_data_list->next; + draw_data_list->next = draw_data_free_list; + draw_data_free_list = draw_data_list; + draw_data_list = tmp; + + goto next_rect; + } + + parent_list = parent_list->next; + } + + window = parent->window; + + if (parent->parent && parent->parent->window != window) + { + if (!GTK_WIDGET_NO_WINDOW (parent)) + { + gint x, y; + gdk_window_get_position (window, &x, &y); + data->rect.x -= x - parent->allocation.x; + x_offset -= x - parent->allocation.x; + data->rect.y -= y - parent->allocation.y; + y_offset -= y - parent->allocation.y; + } + /* If we can't translate the rectangle, stop trying to + * merge. (This occurs for a handlebox) + */ + if (!gtk_widget_clip_rect (parent->parent, window, &data->rect, + &x_offset, &y_offset)) + parent = NULL; + } + + if (parent) + parent = parent->parent; + + if (parent && GTK_WIDGET_REDRAW_PENDING (parent)) + parent_list = gtk_object_get_data_by_id (GTK_OBJECT (parent), + draw_data_key_id); + else + parent_list = NULL; + } + + /* OK, this rectangle stays around. But take advantage + * of the work we've done to clip it to the visible area - + * rect.width/height have already been appropriately + * decreased + */ + data->rect.x -= x_offset; + data->rect.y -= y_offset; + + + prev_node = draw_data_list; + + draw_data_list = draw_data_list->next; + next_rect: + continue; + } + widget_list = widget_list->next; + } + + /* Process the draws */ + + widget_list = gtk_widget_redraw_queue; + + while (widget_list) + { + widget = widget_list->data; + draw_data_list = gtk_object_get_data_by_id (GTK_OBJECT (widget), + draw_data_key_id); + gtk_object_set_data_by_id (GTK_OBJECT (widget), + draw_data_key_id, + NULL); + + GTK_PRIVATE_UNSET_FLAG (widget, GTK_REDRAW_PENDING); + + while (draw_data_list) + { + GtkDrawData *data = draw_data_list->data; + gtk_widget_draw (widget, &data->rect); + + if (draw_data_list->next) + draw_data_list = draw_data_list->next; + else + { + draw_data_list->next = draw_data_free_list; + draw_data_free_list = draw_data_list; + break; + } + } + + widget_list = widget_list->next; + } + + g_slist_free (gtk_widget_redraw_queue); + gtk_widget_redraw_queue = NULL; + + return FALSE; } void @@ -1703,14 +2153,6 @@ gtk_widget_draw (GtkWidget *widget, g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_WIDGET (widget)); - if (GTK_WIDGET_REDRAW_PENDING (widget)) - { - gtk_widget_redraw_queue = g_slist_remove (gtk_widget_redraw_queue, widget); - GTK_PRIVATE_UNSET_FLAG (widget, GTK_REDRAW_PENDING); - - area = NULL; - } - if (GTK_WIDGET_DRAWABLE (widget)) { if (!area) @@ -2139,9 +2581,13 @@ gtk_widget_event (GtkWidget *widget, signal_num = CLIENT_EVENT; break; case GDK_EXPOSE: - /* there is no sense in providing a widget with bogus expose events + /* there is no sense in providing a widget with bogus expose events. + * Widgets that are going to be resized don't need to be + * exposed, since they will be redrawn anyways. */ - if (!event->any.window) + if (!event->any.window || + GTK_WIDGET_RESIZE_NEEDED (widget) || + (widget->parent && GTK_WIDGET_RESIZE_NEEDED (widget->parent))) { gtk_widget_unref (widget); return TRUE; @@ -2534,7 +2980,9 @@ gtk_widget_set_state (GtkWidget *widget, data.parent_sensitive = TRUE; gtk_widget_propagate_state (widget, &data); - gtk_widget_queue_draw (widget); + + if (GTK_WIDGET_DRAWABLE (widget)) + gtk_widget_queue_clear (widget); } } @@ -2581,7 +3029,8 @@ gtk_widget_set_sensitive (GtkWidget *widget, data.parent_sensitive = TRUE; gtk_widget_propagate_state (widget, &data); - gtk_widget_queue_draw (widget); + if (GTK_WIDGET_DRAWABLE (widget)) + gtk_widget_queue_clear (widget); } /***************************************** @@ -2745,6 +3194,24 @@ gtk_widget_get_style (GtkWidget *widget) return widget->style; } +void +gtk_widget_modify_style (GtkWidget *widget, + GtkRcStyle *style) +{ + GtkRcStyle *old_style; + + if (!rc_style_key_id) + rc_style_key_id = g_quark_from_static_string (rc_style_key); + + old_style = gtk_object_get_data_by_id (GTK_OBJECT (widget), rc_style_key_id); + + if (style != old_style) + gtk_object_set_data_by_id_full (GTK_OBJECT (widget), + rc_style_key_id, + style, + (GtkDestroyNotify)gtk_rc_style_unref); +} + static void gtk_widget_style_set (GtkWidget *widget, GtkStyle *previous_style) @@ -2764,6 +3231,7 @@ gtk_widget_set_style_internal (GtkWidget *widget, gboolean initial_emission) { g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_WIDGET (widget)); g_return_if_fail (style != NULL); if (widget->style != style) @@ -2771,6 +3239,8 @@ gtk_widget_set_style_internal (GtkWidget *widget, GtkStyle *previous_style; if (GTK_WIDGET_REALIZED (widget)) + gtk_reset_widget_shapes(widget); + if (GTK_WIDGET_REALIZED (widget)) gtk_style_detach (widget->style); previous_style = widget->style; @@ -2796,7 +3266,7 @@ gtk_widget_set_style_internal (GtkWidget *widget, (old_requisition.height != widget->requisition.height)) gtk_widget_queue_resize (widget); else if (GTK_WIDGET_DRAWABLE (widget)) - gtk_widget_queue_draw (widget); + gtk_widget_queue_clear (widget); } } else if (initial_emission) @@ -3699,11 +4169,7 @@ gtk_widget_real_unmap (GtkWidget *widget) GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); if (GTK_WIDGET_NO_WINDOW (widget)) - gdk_window_clear_area (widget->window, - widget->allocation.x, - widget->allocation.y, - widget->allocation.width, - widget->allocation.height); + gtk_widget_queue_clear (widget); else gdk_window_hide (widget->window); } @@ -3825,7 +4291,7 @@ gtk_widget_real_size_allocate (GtkWidget *widget, { g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_WIDGET (widget)); - + if (GTK_WIDGET_NO_WINDOW (widget) && GTK_WIDGET_MAPPED (widget) && ((widget->allocation.x != allocation->x) || @@ -3834,19 +4300,17 @@ gtk_widget_real_size_allocate (GtkWidget *widget, (widget->allocation.height != allocation->height)) && (widget->allocation.width != 0) && (widget->allocation.height != 0)) - gdk_window_clear_area (widget->window, - widget->allocation.x, - widget->allocation.y, - widget->allocation.width, - widget->allocation.height); + gtk_widget_queue_clear (widget); widget->allocation = *allocation; if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)) - gdk_window_move_resize (widget->window, - allocation->x, allocation->y, - allocation->width, allocation->height); + { + gdk_window_move_resize (widget->window, + allocation->x, allocation->y, + allocation->width, allocation->height); + } } /***************************************** |