selectable labels
- From: Havoc Pennington <hp redhat com>
- To: gtk-devel-list gnome org
- Subject: selectable labels
- Date: 13 Feb 2001 19:03:33 -0500
Hi,
By popular demand, it's gtk_label_set_selectable()!
This exciting patch lets you copy from a label (if the programmer
turns it on). Still missing from the patch is a right-click menu,
though it will somewhat oddly have only the single item "copy" in it.
Will commit soon if no comments.
Havoc
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.1719
diff -u -u -r1.1719 ChangeLog
--- ChangeLog 2001/02/13 16:22:09 1.1719
+++ ChangeLog 2001/02/13 23:59:23
@@ -1,3 +1,21 @@
+2001-02-13 Havoc Pennington <hp redhat com>
+
+ * gtk/testgtk.c (create_labels): Add test for selectable
+
+ * gtk/gtkentry.c (gtk_entry_draw_text): Use new GDK API to draw
+ the selection stuff. This code is kind of broken since it doesn't
+ use the theme engine.
+
+ * gdk/gdkpango.c (gdk_pango_layout_line_get_clip_region):
+ fix infinite loop and y offset problem
+ (gdk_draw_layout_line_with_colors): fix foreground color handling
+
+ * gtk/gtklabel.h, gtk/gtklabel.c: Implement a "selectable" flag
+ that makes the label selectable.
+
+ * gtk/gtklabel.c (gtk_label_style_set): recreate the label's
+ layout when the style is set, since fonts etc. could have changed.
+
2001-02-13 Alexander Larsson <alla lysator liu se>
* gdk/linux-fb/gdkdrawable-fb2.c (gdk_fb_clip_region):
Index: gdk/gdkpango.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkpango.c,v
retrieving revision 1.13
diff -u -u -r1.13 gdkpango.c
--- gdk/gdkpango.c 2001/02/13 05:44:45 1.13
+++ gdk/gdkpango.c 2001/02/13 23:59:23
@@ -259,7 +259,7 @@
tmp.green = foreground->green;
}
- fg_gc = gdk_pango_get_gc (context, fg_set ? &fg_color : NULL,
+ fg_gc = gdk_pango_get_gc (context, fg_set ? &tmp : NULL,
stipple, gc);
}
else
@@ -696,16 +696,17 @@
for (j=0; j < n_pixel_ranges; j++)
{
GdkRectangle rect;
-
+
rect.x = x_origin + pixel_ranges[2*j] / PANGO_SCALE;
- rect.y = y_origin - logical_rect.y / PANGO_SCALE;
+ rect.y = y_origin + logical_rect.y / PANGO_SCALE;
rect.width = (pixel_ranges[2*j + 1] - pixel_ranges[2*j]) / PANGO_SCALE;
rect.height = logical_rect.height / PANGO_SCALE;
-
+
gdk_region_union_with_rect (clip_region, &rect);
}
g_free (pixel_ranges);
+ ++i;
}
return clip_region;
Index: gtk/gtkentry.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkentry.c,v
retrieving revision 1.106
diff -u -u -r1.106 gtkentry.c
--- gtk/gtkentry.c 2001/02/13 05:44:47 1.106
+++ gtk/gtkentry.c 2001/02/13 23:59:23
@@ -1986,37 +1986,27 @@
if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos))
{
- gint *ranges;
- gint n_ranges, i;
- gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
- gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
- GdkRegion *clip_region = gdk_region_new ();
+ gint range[2];
+ GdkRegion *clip_region;
+
+ range[0] = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
+ range[1] = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
- pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
+ clip_region = gdk_pango_layout_get_clip_region (layout,
+ INNER_BORDER - entry->scroll_offset, y_pos,
+ range, 1);
- for (i=0; i < n_ranges; i++)
- {
- GdkRectangle rect;
-
- rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
- rect.y = y_pos;
- rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
- rect.height = logical_rect.height / PANGO_SCALE;
-
- gdk_draw_rectangle (entry->text_area, widget->style->bg_gc [GTK_STATE_SELECTED], TRUE,
- rect.x, rect.y, rect.width, rect.height);
-
- gdk_region_union_with_rect (clip_region, &rect);
- }
-
gdk_gc_set_clip_region (widget->style->fg_gc [GTK_STATE_SELECTED], clip_region);
- gdk_draw_layout (entry->text_area, widget->style->fg_gc [GTK_STATE_SELECTED],
- INNER_BORDER - entry->scroll_offset, y_pos,
- layout);
+ gdk_draw_layout_with_colors (entry->text_area,
+ widget->style->fg_gc [GTK_STATE_SELECTED],
+ INNER_BORDER - entry->scroll_offset, y_pos,
+ layout,
+ &widget->style->fg[GTK_STATE_SELECTED],
+ &widget->style->bg[GTK_STATE_SELECTED]);
+
gdk_gc_set_clip_region (widget->style->fg_gc [GTK_STATE_SELECTED], NULL);
gdk_region_destroy (clip_region);
- g_free (ranges);
}
g_object_unref (G_OBJECT (layout));
Index: gtk/gtklabel.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtklabel.c,v
retrieving revision 1.71
diff -u -u -r1.71 gtklabel.c
--- gtk/gtklabel.c 2001/02/13 04:56:06 1.71
+++ gtk/gtklabel.c 2001/02/13 23:59:24
@@ -27,9 +27,16 @@
#include <string.h>
#include "gtklabel.h"
#include "gdk/gdkkeysyms.h"
+#include "gtkclipboard.h"
#include "gdk/gdki18n.h"
#include <pango/pango.h>
+struct _GtkLabelSelectionInfo
+{
+ GdkWindow *window;
+ gint selection_anchor;
+ gint selection_end;
+};
enum {
ARG_0,
@@ -50,15 +57,39 @@
static void gtk_label_finalize (GObject *object);
static void gtk_label_size_request (GtkWidget *widget,
GtkRequisition *requisition);
+static void gtk_label_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
static void gtk_label_style_set (GtkWidget *widget,
GtkStyle *previous_style);
static void gtk_label_direction_changed (GtkWidget *widget,
GtkTextDirection previous_dir);
static gint gtk_label_expose (GtkWidget *widget,
GdkEventExpose *event);
+
+static void gtk_label_realize (GtkWidget *widget);
+static void gtk_label_unrealize (GtkWidget *widget);
+static void gtk_label_map (GtkWidget *widget);
+static void gtk_label_unmap (GtkWidget *widget);
+static gint gtk_label_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_label_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_label_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+
+static void gtk_label_create_window (GtkLabel *label);
+static void gtk_label_destroy_window (GtkLabel *label);
+static void gtk_label_clear_layout (GtkLabel *label);
+static void gtk_label_ensure_layout (GtkLabel *label,
+ gint *widthp,
+ gint *heightp);
+static void gtk_label_select_region_index (GtkLabel *label,
+ gint anchor_index,
+ gint end_index);
-static GtkMiscClass *parent_class = NULL;
+GtkMiscClass *parent_class = NULL;
+
GtkType
gtk_label_get_type (void)
{
@@ -108,9 +139,17 @@
object_class->get_arg = gtk_label_get_arg;
widget_class->size_request = gtk_label_size_request;
+ widget_class->size_allocate = gtk_label_size_allocate;
widget_class->style_set = gtk_label_style_set;
widget_class->direction_changed = gtk_label_direction_changed;
widget_class->expose_event = gtk_label_expose;
+ widget_class->realize = gtk_label_realize;
+ widget_class->unrealize = gtk_label_unrealize;
+ widget_class->map = gtk_label_map;
+ widget_class->unmap = gtk_label_unmap;
+ widget_class->button_press_event = gtk_label_button_press;
+ widget_class->button_release_event = gtk_label_button_release;
+ widget_class->motion_notify_event = gtk_label_motion;
}
static void
@@ -207,11 +246,10 @@
g_free (label->label);
label->label = str;
- if (label->layout)
- {
- g_object_unref (G_OBJECT (label->layout));
- label->layout = NULL;
- }
+
+ gtk_label_clear_layout (label);
+
+ gtk_label_select_region_index (label, 0, 0);
gtk_widget_queue_resize (GTK_WIDGET (label));
}
@@ -426,6 +464,8 @@
if (label->attrs)
pango_attr_list_unref (label->attrs);
+
+ g_free (label->select_info);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -469,17 +509,26 @@
}
static void
-gtk_label_size_request (GtkWidget *widget,
- GtkRequisition *requisition)
+gtk_label_clear_layout (GtkLabel *label)
{
- GtkLabel *label;
+ if (label->layout)
+ {
+ g_object_unref (G_OBJECT (label->layout));
+ label->layout = NULL;
+ }
+}
+
+static void
+gtk_label_ensure_layout (GtkLabel *label,
+ gint *widthp,
+ gint *heightp)
+{
+ GtkWidget *widget;
PangoRectangle logical_rect;
-
- g_return_if_fail (GTK_IS_LABEL (widget));
- g_return_if_fail (requisition != NULL);
-
- label = GTK_LABEL (widget);
+ gint width, height;
+ widget = GTK_WIDGET (label);
+
/*
* There are a number of conditions which will necessitate re-filling
* our text:
@@ -501,8 +550,8 @@
* don't think it's really that slow.
*/
- requisition->width = label->misc.xpad * 2;
- requisition->height = label->misc.ypad * 2;
+ width = label->misc.xpad * 2;
+ height = label->misc.ypad * 2;
if (!label->layout)
{
@@ -512,7 +561,7 @@
label->layout = gtk_widget_create_pango_layout (widget, label->label);
/* FIXME move to a model where the pattern isn't stored
- * permanently, and just modifes or creates the AttrList
+ * permanently, and just modifies or creates the AttrList
*/
if (label->attrs)
attrs = pango_attr_list_copy (label->attrs);
@@ -521,7 +570,7 @@
if (label->pattern)
gtk_label_pattern_to_attrs (label, attrs);
-
+
if (attrs)
{
pango_layout_set_attributes (label->layout, attrs);
@@ -564,8 +613,8 @@
pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
pango_layout_get_extents (label->layout, NULL, &logical_rect);
- requisition->width += aux_info->width;
- requisition->height += PANGO_PIXELS (logical_rect.height);
+ width += aux_info->width;
+ height += PANGO_PIXELS (logical_rect.height);
}
else
{
@@ -632,17 +681,61 @@
}
pango_layout_set_width (label->layout, width);
- requisition->width += PANGO_PIXELS (real_width);
- requisition->height += PANGO_PIXELS (height);
+ width += PANGO_PIXELS (real_width);
+ height += PANGO_PIXELS (height);
}
}
else /* !label->wrap */
{
pango_layout_set_width (label->layout, -1);
pango_layout_get_extents (label->layout, NULL, &logical_rect);
+
+ width += PANGO_PIXELS (logical_rect.width);
+ height += PANGO_PIXELS (logical_rect.height);
+ }
+
+ if (widthp)
+ *widthp = width;
- requisition->width += PANGO_PIXELS (logical_rect.width);
- requisition->height += PANGO_PIXELS (logical_rect.height);
+ if (heightp)
+ *heightp = height;
+}
+
+static void
+gtk_label_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GtkLabel *label;
+ gint width, height;
+
+ g_return_if_fail (GTK_IS_LABEL (widget));
+ g_return_if_fail (requisition != NULL);
+
+ label = GTK_LABEL (widget);
+
+ gtk_label_ensure_layout (label, &width, &height);
+
+ requisition->width = width;
+ requisition->height = height;
+}
+
+static void
+gtk_label_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkLabel *label;
+
+ label = GTK_LABEL (widget);
+
+ (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+
+ if (label->select_info && label->select_info->window)
+ {
+ gdk_window_move_resize (label->select_info->window,
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
}
}
@@ -651,13 +744,13 @@
GtkStyle *previous_style)
{
GtkLabel *label;
-
+
g_return_if_fail (GTK_IS_LABEL (widget));
label = GTK_LABEL (widget);
- if (previous_style && label->layout)
- pango_layout_context_changed (label->layout);
+ /* We have to clear the layout, fonts etc. may have changed */
+ gtk_label_clear_layout (label);
}
static void
@@ -703,43 +796,59 @@
}
#endif
+static void
+get_layout_location (GtkLabel *label,
+ gint *xp,
+ gint *yp)
+{
+ GtkMisc *misc;
+ GtkWidget *widget;
+ gfloat xalign;
+ gint x, y;
+
+ misc = GTK_MISC (label);
+ widget = GTK_WIDGET (label);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ xalign = misc->xalign;
+ else
+ xalign = 1.0 - misc->xalign;
+
+ x = floor (widget->allocation.x + (gint)misc->xpad
+ + ((widget->allocation.width - widget->requisition.width) * xalign)
+ + 0.5);
+
+ y = floor (widget->allocation.y + (gint)misc->ypad
+ + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
+ + 0.5);
+
+
+ if (xp)
+ *xp = x;
+
+ if (yp)
+ *yp = y;
+}
+
static gint
gtk_label_expose (GtkWidget *widget,
GdkEventExpose *event)
{
GtkLabel *label;
- GtkMisc *misc;
gint x, y;
- gfloat xalign;
g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
label = GTK_LABEL (widget);
- /* if label->layout is NULL it means we got a set_text since
- * our last size request, so a resize should be queued,
- * which means a full expose is in the queue anyway.
- */
+ gtk_label_ensure_layout (label, NULL, NULL);
+
if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
- label->layout && label->label && (*label->label != '\0'))
+ label->label && (*label->label != '\0'))
{
- misc = GTK_MISC (widget);
+ get_layout_location (label, &x, &y);
- if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
- xalign = misc->xalign;
- else
- xalign = 1. - misc->xalign;
-
- x = floor (widget->allocation.x + (gint)misc->xpad
- + ((widget->allocation.width - widget->requisition.width) * xalign)
- + 0.5);
-
- y = floor (widget->allocation.y + (gint)misc->ypad
- + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
- + 0.5);
-
-
gtk_paint_layout (widget->style,
widget->window,
GTK_WIDGET_STATE (widget),
@@ -748,6 +857,45 @@
"label",
x, y,
label->layout);
+
+ if (label->select_info &&
+ (label->select_info->selection_anchor !=
+ label->select_info->selection_end))
+ {
+ gint range[2];
+ GdkRegion *clip;
+
+ range[0] = label->select_info->selection_anchor;
+ range[1] = label->select_info->selection_end;
+
+ if (range[0] > range[1])
+ {
+ gint tmp = range[0];
+ range[0] = range[1];
+ range[1] = tmp;
+ }
+
+ clip = gdk_pango_layout_get_clip_region (label->layout,
+ x, y,
+ range,
+ 1);
+
+ /* FIXME should use gtk_paint, but it can't use a clip
+ * region
+ */
+
+ gdk_gc_set_clip_region (widget->style->white_gc, clip);
+
+ gdk_draw_layout_with_colors (widget->window,
+ widget->style->white_gc,
+ x, y,
+ label->layout,
+ &widget->style->fg[GTK_STATE_SELECTED],
+ &widget->style->bg[GTK_STATE_SELECTED]);
+
+ gdk_gc_set_clip_region (widget->style->white_gc, NULL);
+ gdk_region_destroy (clip);
+ }
}
return TRUE;
@@ -836,3 +984,430 @@
return accel_key;
}
+
+static void
+gtk_label_realize (GtkWidget *widget)
+{
+ GtkLabel *label;
+
+ label = GTK_LABEL (widget);
+
+ (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
+
+ if (label->select_info)
+ gtk_label_create_window (label);
+}
+
+static void
+gtk_label_unrealize (GtkWidget *widget)
+{
+ GtkLabel *label;
+
+ label = GTK_LABEL (widget);
+
+ if (label->select_info)
+ gtk_label_destroy_window (label);
+
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+gtk_label_map (GtkWidget *widget)
+{
+ GtkLabel *label;
+
+ label = GTK_LABEL (widget);
+
+ (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
+
+ if (label->select_info)
+ gdk_window_show (label->select_info->window);
+}
+
+static void
+gtk_label_unmap (GtkWidget *widget)
+{
+ GtkLabel *label;
+
+ label = GTK_LABEL (widget);
+
+ if (label->select_info)
+ gdk_window_hide (label->select_info->window);
+
+ (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
+}
+
+static void
+window_to_layout_coords (GtkLabel *label,
+ gint *x,
+ gint *y)
+{
+ gint lx, ly;
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (label);
+
+ /* get layout location in widget->window coords */
+ get_layout_location (label, &lx, &ly);
+
+ if (x)
+ {
+ *x += widget->allocation.x; /* go to widget->window */
+ *x -= lx; /* go to layout */
+ }
+
+ if (y)
+ {
+ *y += widget->allocation.y; /* go to widget->window */
+ *y -= ly; /* go to layout */
+ }
+}
+
+static void
+layout_to_window_coords (GtkLabel *label,
+ gint *x,
+ gint *y)
+{
+ gint lx, ly;
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (label);
+
+ /* get layout location in widget->window coords */
+ get_layout_location (label, &lx, &ly);
+
+ if (x)
+ {
+ *x += lx; /* go to widget->window */
+ *x -= widget->allocation.x; /* go to selection window */
+ }
+
+ if (y)
+ {
+ *y += ly; /* go to widget->window */
+ *y -= widget->allocation.y; /* go to selection window */
+ }
+}
+
+static void
+get_layout_index (GtkLabel *label,
+ gint x,
+ gint y,
+ gint *index)
+{
+ gint trailing = 0;
+ const gchar *cluster;
+ const gchar *cluster_end;
+
+ *index = 0;
+
+ gtk_label_ensure_layout (label, NULL, NULL);
+
+ window_to_layout_coords (label, &x, &y);
+
+ x *= PANGO_SCALE;
+ y *= PANGO_SCALE;
+
+ pango_layout_xy_to_index (label->layout,
+ x, y,
+ index, &trailing);
+
+
+ cluster = label->label + *index;
+ cluster_end = cluster;
+ while (trailing)
+ {
+ cluster_end = g_utf8_next_char (cluster_end);
+ --trailing;
+ }
+
+ *index += (cluster_end - cluster);
+}
+
+static gint
+gtk_label_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkLabel *label;
+ gint index = 0;
+
+ label = GTK_LABEL (widget);
+
+ if (label->select_info == NULL)
+ return FALSE;
+
+ if (event->button != 1)
+ return FALSE;
+
+ get_layout_index (label, event->x, event->y, &index);
+
+ if ((label->select_info->selection_anchor !=
+ label->select_info->selection_end) &&
+ (event->state & GDK_SHIFT_MASK))
+ {
+ /* extend (same as motion) */
+ if (index < label->select_info->selection_end)
+ gtk_label_select_region_index (label,
+ index,
+ label->select_info->selection_end);
+ else
+ gtk_label_select_region_index (label,
+ label->select_info->selection_anchor,
+ index);
+
+ /* ensure the anchor is opposite index */
+ if (index == label->select_info->selection_anchor)
+ {
+ gint tmp = label->select_info->selection_end;
+ label->select_info->selection_end = label->select_info->selection_anchor;
+ label->select_info->selection_anchor = tmp;
+ }
+ }
+ else
+ {
+ /* start a replacement */
+ gtk_label_select_region_index (label, index, index);
+ }
+
+ return TRUE;
+}
+
+static gint
+gtk_label_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+
+{
+ GtkLabel *label;
+
+ label = GTK_LABEL (widget);
+
+ if (label->select_info == NULL)
+ return FALSE;
+
+ if (event->button != 1)
+ return FALSE;
+
+ /* The goal here is to return TRUE iff we ate the
+ * button press to start selecting.
+ */
+
+ return TRUE;
+}
+
+static gint
+gtk_label_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkLabel *label;
+ gint index;
+ gint x, y;
+
+ label = GTK_LABEL (widget);
+
+ if (label->select_info == NULL)
+ return FALSE;
+
+ if ((event->state & GDK_BUTTON1_MASK) == 0)
+ return FALSE;
+
+ gdk_window_get_pointer (label->select_info->window,
+ &x, &y, NULL);
+
+ get_layout_index (label, x, y, &index);
+
+ gtk_label_select_region_index (label,
+ label->select_info->selection_anchor,
+ index);
+
+ return TRUE;
+}
+
+static void
+gtk_label_create_window (GtkLabel *label)
+{
+ GtkWidget *widget;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_assert (label->select_info);
+ g_assert (GTK_WIDGET_REALIZED (label));
+
+ if (label->select_info->window)
+ return;
+
+ widget = GTK_WIDGET (label);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.window_type = GDK_WINDOW_TEMP;
+ attributes.wclass = GDK_INPUT_ONLY;
+ attributes.override_redirect = TRUE;
+ attributes.event_mask = gtk_widget_get_events (widget) |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK;
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
+
+ label->select_info->window = gdk_window_new (widget->window,
+ &attributes, attributes_mask);
+ gdk_window_set_user_data (label->select_info->window, widget);
+}
+
+static void
+gtk_label_destroy_window (GtkLabel *label)
+{
+ g_assert (label->select_info);
+
+ if (label->select_info->window == NULL)
+ return;
+
+ gdk_window_set_user_data (label->select_info->window, NULL);
+ gdk_window_destroy (label->select_info->window);
+ label->select_info->window = NULL;
+}
+
+void
+gtk_label_set_selectable (GtkLabel *label,
+ gboolean setting)
+{
+ g_return_if_fail (GTK_IS_LABEL (label));
+
+ setting = setting != FALSE;
+
+ if (setting)
+ {
+ if (label->select_info == NULL)
+ {
+ label->select_info = g_new (GtkLabelSelectionInfo, 1);
+
+ label->select_info->window = NULL;
+ label->select_info->selection_anchor = 0;
+ label->select_info->selection_end = 0;
+
+ if (GTK_WIDGET_REALIZED (label))
+ gtk_label_create_window (label);
+
+ if (GTK_WIDGET_MAPPED (label))
+ gdk_window_show (label->select_info->window);
+ }
+ }
+ else
+ {
+ if (label->select_info)
+ {
+ if (label->select_info->window)
+ gtk_label_destroy_window (label);
+
+ g_free (label->select_info);
+
+ label->select_info = NULL;
+ }
+ }
+}
+
+gboolean
+gtk_label_get_selectable (GtkLabel *label)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
+
+ return label->select_info != NULL;
+}
+
+static void
+get_text_callback (GtkClipboard *clipboard,
+ GtkSelectionData *selection_data,
+ guint info,
+ gpointer user_data_or_owner)
+{
+ GtkLabel *label;
+ gchar *str;
+
+ label = GTK_LABEL (user_data_or_owner);
+
+ if ((label->select_info->selection_anchor !=
+ label->select_info->selection_end) &&
+ label->label)
+ {
+ gint start, end;
+
+ start = MIN (label->select_info->selection_anchor,
+ label->select_info->selection_end);
+ end = MAX (label->select_info->selection_anchor,
+ label->select_info->selection_end);
+
+ str = g_strndup (label->label + start,
+ end - start);
+
+ gtk_selection_data_set_text (selection_data,
+ str);
+
+ g_free (str);
+ }
+}
+
+static void
+clear_text_callback (GtkClipboard *clipboard,
+ gpointer user_data_or_owner)
+{
+ GtkLabel *label;
+
+ label = GTK_LABEL (user_data_or_owner);
+
+ if (label->select_info)
+ {
+ label->select_info->selection_anchor = 0;
+ label->select_info->selection_end = 0;
+
+ gtk_label_clear_layout (label);
+ gtk_widget_queue_draw (GTK_WIDGET (label));
+ }
+}
+
+static void
+gtk_label_select_region_index (GtkLabel *label,
+ gint anchor_index,
+ gint end_index)
+{
+ static const GtkTargetEntry targets[] = {
+ { "STRING", 0, 0 },
+ { "TEXT", 0, 0 },
+ { "COMPOUND_TEXT", 0, 0 },
+ { "UTF8_STRING", 0, 0 }
+ };
+
+ g_return_if_fail (GTK_IS_LABEL (label));
+
+ if (label->select_info)
+ {
+ GtkClipboard *clipboard;
+
+ label->select_info->selection_anchor = anchor_index;
+ label->select_info->selection_end = end_index;
+
+ clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+
+ gtk_clipboard_set_with_owner (clipboard,
+ targets,
+ G_N_ELEMENTS (targets),
+ get_text_callback,
+ clear_text_callback,
+ G_OBJECT (label));
+
+ gtk_label_clear_layout (label);
+ gtk_widget_queue_draw (GTK_WIDGET (label));
+ }
+}
+
+void
+gtk_label_select_region (GtkLabel *label,
+ gint start_offset,
+ gint end_offset)
+{
+ /* FIXME */
+
+
+}
+
Index: gtk/gtklabel.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtklabel.h,v
retrieving revision 1.23
diff -u -u -r1.23 gtklabel.h
--- gtk/gtklabel.h 2001/02/03 01:09:40 1.23
+++ gtk/gtklabel.h 2001/02/13 23:59:24
@@ -47,19 +47,24 @@
typedef struct _GtkLabel GtkLabel;
typedef struct _GtkLabelClass GtkLabelClass;
+typedef struct _GtkLabelSelectionInfo GtkLabelSelectionInfo;
+
struct _GtkLabel
{
GtkMisc misc;
+ /*< private >*/
+
gchar *label;
gchar *pattern;
guint jtype : 2;
guint wrap : 1;
- /*< private >*/
PangoLayout *layout;
PangoAttrList *attrs;
+
+ GtkLabelSelectionInfo *select_info;
};
struct _GtkLabelClass
@@ -94,6 +99,14 @@
*/
guint gtk_label_parse_uline (GtkLabel *label,
const gchar *string);
+
+void gtk_label_set_selectable (GtkLabel *label,
+ gboolean setting);
+gboolean gtk_label_get_selectable (GtkLabel *label);
+
+void gtk_label_select_region (GtkLabel *label,
+ gint start_offset,
+ gint end_offset);
#ifndef GTK_DISABLE_COMPAT_H
# define gtk_label_set gtk_label_set_text
Index: gtk/testgtk.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/testgtk.c,v
retrieving revision 1.217
diff -u -u -r1.217 testgtk.c
--- gtk/testgtk.c 2001/02/13 05:44:47 1.217
+++ gtk/testgtk.c 2001/02/13 23:59:24
@@ -2004,6 +2004,59 @@
return button;
}
+static void
+set_selectable_recursive (GtkWidget *widget,
+ gboolean setting)
+{
+ if (GTK_IS_CONTAINER (widget))
+ {
+ GList *children;
+ GList *tmp;
+
+ children = gtk_container_children (GTK_CONTAINER (widget));
+ tmp = children;
+ while (tmp)
+ {
+ set_selectable_recursive (tmp->data, setting);
+
+ tmp = tmp->next;
+ }
+ g_list_free (children);
+ }
+ else if (GTK_IS_LABEL (widget))
+ {
+ gtk_label_set_selectable (GTK_LABEL (widget), setting);
+ }
+}
+
+static void
+selectable_toggled (GtkWidget *toggle,
+ GtkWidget *widget)
+{
+ set_selectable_recursive (widget,
+ GTK_TOGGLE_BUTTON (toggle)->active);
+}
+
+static GtkWidget*
+create_selectable_control (GtkWidget *widget)
+{
+ GtkWidget *button;
+
+ button = gtk_toggle_button_new_with_label ("Selectable");
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ FALSE);
+
+ gtk_signal_connect (GTK_OBJECT (button),
+ "toggled",
+ GTK_SIGNAL_FUNC (selectable_toggled),
+ widget);
+
+ gtk_widget_show_all (button);
+
+ return button;
+}
+
void create_labels (void)
{
static GtkWidget *window = NULL;
@@ -2032,6 +2085,10 @@
gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
button = create_sensitivity_control (hbox);
+
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ button = create_selectable_control (hbox);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]