summaryrefslogtreecommitdiff
path: root/gtk/gtklabel.c
diff options
context:
space:
mode:
authorTimm Bäder <mail@baedert.org>2020-12-25 12:05:28 +0100
committerTimm Bäder <mail@baedert.org>2021-01-03 11:01:28 +0100
commitd8fa9de93a1cac64eb30060a2c2319896341ae1a (patch)
treeef4b0bfc1b26c37e61e69a4837fd3c7cc1304f1e /gtk/gtklabel.c
parentafabbc49576d3b1c0187108f9d9625785e195f2b (diff)
downloadgtk+-d8fa9de93a1cac64eb30060a2c2319896341ae1a.tar.gz
label: Modernize source file
Try to sort toplevel functions to minimize unnecessary function prototypes at the beginning of the file, get rid of all tabs and trailing whitespace.
Diffstat (limited to 'gtk/gtklabel.c')
-rw-r--r--gtk/gtklabel.c3708
1 files changed, 1781 insertions, 1927 deletions
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 19b0386c05..ef655b29e1 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -407,168 +407,31 @@ static guint signals[LAST_SIGNAL] = { 0 };
static GQuark quark_mnemonics_visible_connected;
-static void gtk_label_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gtk_label_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec);
-static void gtk_label_finalize (GObject *object);
-static void gtk_label_dispose (GObject *object);
-static void gtk_label_size_allocate (GtkWidget *widget,
- int width,
- int height,
- int baseline);
-static void gtk_label_state_flags_changed (GtkWidget *widget,
- GtkStateFlags prev_state);
-static void gtk_label_css_changed (GtkWidget *widget,
- GtkCssStyleChange *change);
-static void gtk_label_snapshot (GtkWidget *widget,
- GtkSnapshot *snapshot);
-static gboolean gtk_label_focus (GtkWidget *widget,
- GtkDirectionType direction);
-
-static void gtk_label_unrealize (GtkWidget *widget);
-
-static void gtk_label_motion (GtkEventControllerMotion *controller,
- double x,
- double y,
- gpointer data);
-static void gtk_label_leave (GtkEventControllerMotion *controller,
- gpointer data);
-
-static gboolean gtk_label_grab_focus (GtkWidget *widget);
-
-static gboolean gtk_label_query_tooltip (GtkWidget *widget,
- int x,
- int y,
- gboolean keyboard_tip,
- GtkTooltip *tooltip);
-
-static void gtk_label_set_text_internal (GtkLabel *self,
- char *str);
-static gboolean gtk_label_set_label_internal (GtkLabel *self,
- const char *str);
-static gboolean gtk_label_set_use_markup_internal (GtkLabel *self,
- gboolean val);
-static gboolean gtk_label_set_use_underline_internal (GtkLabel *self,
- gboolean val);
static void gtk_label_set_markup_internal (GtkLabel *self,
- const char *str,
- gboolean with_uline);
+ const char *str,
+ gboolean with_uline);
static void gtk_label_recalculate (GtkLabel *self);
-static void gtk_label_root (GtkWidget *widget);
-static void gtk_label_unroot (GtkWidget *widget);
-static void gtk_label_popup_menu (GtkWidget *widget,
- const char *action_name,
- GVariant *parameters);
static void gtk_label_do_popup (GtkLabel *self,
double x,
double y);
-
static void gtk_label_ensure_select_info (GtkLabel *self);
static void gtk_label_clear_select_info (GtkLabel *self);
-static void gtk_label_update_cursor (GtkLabel *self);
static void gtk_label_clear_layout (GtkLabel *self);
static void gtk_label_ensure_layout (GtkLabel *self);
static void gtk_label_select_region_index (GtkLabel *self,
int anchor_index,
int end_index);
-
static void gtk_label_update_active_link (GtkWidget *widget,
double x,
double y);
-
-static gboolean gtk_label_mnemonic_activate (GtkWidget *widget,
- gboolean group_cycling);
static void gtk_label_setup_mnemonic (GtkLabel *self);
static void gtk_label_buildable_interface_init (GtkBuildableIface *iface);
-static gboolean gtk_label_buildable_custom_tag_start (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *child,
- const char *tagname,
- GtkBuildableParser *parser,
- gpointer *data);
-
-static void gtk_label_buildable_custom_finished (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *child,
- const char *tagname,
- gpointer user_data);
-
/* For selectable labels: */
static void gtk_label_move_cursor (GtkLabel *self,
- GtkMovementStep step,
- int count,
- gboolean extend_selection);
-static void gtk_label_copy_clipboard (GtkLabel *self);
-static void gtk_label_select_all (GtkLabel *self);
-static int gtk_label_move_forward_word (GtkLabel *self,
- int start);
-static int gtk_label_move_backward_word (GtkLabel *self,
- int start);
-
-/* For links: */
-static void gtk_label_clear_links (GtkLabel *self);
-static gboolean gtk_label_activate_link (GtkLabel *self,
- const char *uri);
-static void gtk_label_activate_current_link (GtkLabel *self);
-static void emit_activate_link (GtkLabel *self,
- GtkLabelLink *link);
-
-/* Event controller callbacks */
-static void gtk_label_click_gesture_pressed (GtkGestureClick *gesture,
- int n_press,
- double x,
- double y,
- GtkLabel *self);
-static void gtk_label_click_gesture_released (GtkGestureClick *gesture,
- int n_press,
- double x,
- double y,
- GtkLabel *self);
-static void gtk_label_drag_gesture_begin (GtkGestureDrag *gesture,
- double start_x,
- double start_y,
- GtkLabel *self);
-static void gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
- double offset_x,
- double offset_y,
- GtkLabel *self);
-
-/* Actions */
-
-static void gtk_label_activate_clipboard_copy (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-static void gtk_label_activate_selection_select_all (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-static void gtk_label_activate_link_open (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-static void gtk_label_activate_link_copy (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-static void gtk_label_nop (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-
-static void gtk_label_update_actions (GtkLabel *self);
-
-static GtkSizeRequestMode gtk_label_get_request_mode (GtkWidget *widget);
-static void gtk_label_measure (GtkWidget *widget,
- GtkOrientation orientation,
- int for_size,
- int *minimum,
- int *natural,
- int *minimum_baseline,
- int *natural_baseline);
-
-
+ GtkMovementStep step,
+ int count,
+ gboolean extend_selection);
static GtkBuildableIface *buildable_parent_iface = NULL;
@@ -578,26 +441,1607 @@ G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_WIDGET,
static void
add_move_binding (GtkWidgetClass *widget_class,
- guint keyval,
- guint modmask,
- GtkMovementStep step,
- int count)
+ guint keyval,
+ guint modmask,
+ GtkMovementStep step,
+ int count)
{
g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
-
+
gtk_widget_class_add_binding_signal (widget_class,
keyval, modmask,
- "move-cursor",
+ "move-cursor",
"(iib)", step, count, FALSE);
/* Selection-extending version */
gtk_widget_class_add_binding_signal (widget_class,
keyval, modmask | GDK_SHIFT_MASK,
- "move-cursor",
+ "move-cursor",
"(iib)", step, count, TRUE);
}
static void
+gtk_label_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkLabel *self = GTK_LABEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ gtk_label_set_label (self, g_value_get_string (value));
+ break;
+ case PROP_ATTRIBUTES:
+ gtk_label_set_attributes (self, g_value_get_boxed (value));
+ break;
+ case PROP_USE_MARKUP:
+ gtk_label_set_use_markup (self, g_value_get_boolean (value));
+ break;
+ case PROP_USE_UNDERLINE:
+ gtk_label_set_use_underline (self, g_value_get_boolean (value));
+ break;
+ case PROP_JUSTIFY:
+ gtk_label_set_justify (self, g_value_get_enum (value));
+ break;
+ case PROP_WRAP:
+ gtk_label_set_wrap (self, g_value_get_boolean (value));
+ break;
+ case PROP_WRAP_MODE:
+ gtk_label_set_wrap_mode (self, g_value_get_enum (value));
+ break;
+ case PROP_SELECTABLE:
+ gtk_label_set_selectable (self, g_value_get_boolean (value));
+ break;
+ case PROP_MNEMONIC_WIDGET:
+ gtk_label_set_mnemonic_widget (self, (GtkWidget*) g_value_get_object (value));
+ break;
+ case PROP_ELLIPSIZE:
+ gtk_label_set_ellipsize (self, g_value_get_enum (value));
+ break;
+ case PROP_WIDTH_CHARS:
+ gtk_label_set_width_chars (self, g_value_get_int (value));
+ break;
+ case PROP_SINGLE_LINE_MODE:
+ gtk_label_set_single_line_mode (self, g_value_get_boolean (value));
+ break;
+ case PROP_MAX_WIDTH_CHARS:
+ gtk_label_set_max_width_chars (self, g_value_get_int (value));
+ break;
+ case PROP_LINES:
+ gtk_label_set_lines (self, g_value_get_int (value));
+ break;
+ case PROP_XALIGN:
+ gtk_label_set_xalign (self, g_value_get_float (value));
+ break;
+ case PROP_YALIGN:
+ gtk_label_set_yalign (self, g_value_get_float (value));
+ break;
+ case PROP_EXTRA_MENU:
+ gtk_label_set_extra_menu (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_label_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkLabel *self = GTK_LABEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ g_value_set_string (value, self->label);
+ break;
+ case PROP_ATTRIBUTES:
+ g_value_set_boxed (value, self->attrs);
+ break;
+ case PROP_USE_MARKUP:
+ g_value_set_boolean (value, self->use_markup);
+ break;
+ case PROP_USE_UNDERLINE:
+ g_value_set_boolean (value, self->use_underline);
+ break;
+ case PROP_JUSTIFY:
+ g_value_set_enum (value, self->jtype);
+ break;
+ case PROP_WRAP:
+ g_value_set_boolean (value, self->wrap);
+ break;
+ case PROP_WRAP_MODE:
+ g_value_set_enum (value, self->wrap_mode);
+ break;
+ case PROP_SELECTABLE:
+ g_value_set_boolean (value, gtk_label_get_selectable (self));
+ break;
+ case PROP_MNEMONIC_KEYVAL:
+ g_value_set_uint (value, self->mnemonic_keyval);
+ break;
+ case PROP_MNEMONIC_WIDGET:
+ g_value_set_object (value, (GObject*) self->mnemonic_widget);
+ break;
+ case PROP_ELLIPSIZE:
+ g_value_set_enum (value, self->ellipsize);
+ break;
+ case PROP_WIDTH_CHARS:
+ g_value_set_int (value, gtk_label_get_width_chars (self));
+ break;
+ case PROP_SINGLE_LINE_MODE:
+ g_value_set_boolean (value, gtk_label_get_single_line_mode (self));
+ break;
+ case PROP_MAX_WIDTH_CHARS:
+ g_value_set_int (value, gtk_label_get_max_width_chars (self));
+ break;
+ case PROP_LINES:
+ g_value_set_int (value, gtk_label_get_lines (self));
+ break;
+ case PROP_XALIGN:
+ g_value_set_float (value, gtk_label_get_xalign (self));
+ break;
+ case PROP_YALIGN:
+ g_value_set_float (value, gtk_label_get_yalign (self));
+ break;
+ case PROP_EXTRA_MENU:
+ g_value_set_object (value, gtk_label_get_extra_menu (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_label_init (GtkLabel *self)
+{
+ self->width_chars = -1;
+ self->max_width_chars = -1;
+ self->label = g_strdup ("");
+ self->lines = -1;
+
+ self->xalign = 0.5;
+ self->yalign = 0.5;
+
+ self->jtype = GTK_JUSTIFY_LEFT;
+ self->wrap = FALSE;
+ self->wrap_mode = PANGO_WRAP_WORD;
+ self->ellipsize = PANGO_ELLIPSIZE_NONE;
+
+ self->use_underline = FALSE;
+ self->use_markup = FALSE;
+
+ self->mnemonic_keyval = GDK_KEY_VoidSymbol;
+ self->layout = NULL;
+ self->text = g_strdup ("");
+ self->attrs = NULL;
+
+ self->mnemonic_widget = NULL;
+
+ self->mnemonics_visible = FALSE;
+}
+
+static const GtkBuildableParser pango_parser =
+{
+ gtk_pango_attribute_start_element,
+};
+
+static gboolean
+gtk_label_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const char *tagname,
+ GtkBuildableParser *parser,
+ gpointer *data)
+{
+ if (buildable_parent_iface->custom_tag_start (buildable, builder, child,
+ tagname, parser, data))
+ return TRUE;
+
+ if (strcmp (tagname, "attributes") == 0)
+ {
+ GtkPangoAttributeParserData *parser_data;
+
+ parser_data = g_slice_new0 (GtkPangoAttributeParserData);
+ parser_data->builder = g_object_ref (builder);
+ parser_data->object = (GObject *) g_object_ref (buildable);
+ *parser = pango_parser;
+ *data = parser_data;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+gtk_label_buildable_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const char *tagname,
+ gpointer user_data)
+{
+ GtkPangoAttributeParserData *data = user_data;
+
+ buildable_parent_iface->custom_finished (buildable, builder, child,
+ tagname, user_data);
+
+ if (strcmp (tagname, "attributes") == 0)
+ {
+ if (data->attrs)
+ {
+ gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
+ pango_attr_list_unref (data->attrs);
+ }
+
+ g_object_unref (data->object);
+ g_object_unref (data->builder);
+ g_slice_free (GtkPangoAttributeParserData, data);
+ }
+}
+
+static void
+gtk_label_buildable_interface_init (GtkBuildableIface *iface)
+{
+ buildable_parent_iface = g_type_interface_peek_parent (iface);
+
+ iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
+ iface->custom_finished = gtk_label_buildable_custom_finished;
+}
+
+static void
+update_link_state (GtkLabel *self)
+{
+ GtkStateFlags state;
+ guint i;
+
+ if (!self->select_info)
+ return;
+
+ for (i = 0; i < self->select_info->n_links; i++)
+ {
+ const GtkLabelLink *link = &self->select_info->links[i];
+
+ state = gtk_widget_get_state_flags (GTK_WIDGET (self));
+ if (link->visited)
+ state |= GTK_STATE_FLAG_VISITED;
+ else
+ state |= GTK_STATE_FLAG_LINK;
+ if (link == self->select_info->active_link)
+ {
+ if (self->select_info->link_clicked)
+ state |= GTK_STATE_FLAG_ACTIVE;
+ else
+ state |= GTK_STATE_FLAG_PRELIGHT;
+ }
+ gtk_css_node_set_state (link->cssnode, state);
+ }
+}
+
+static void
+gtk_label_update_cursor (GtkLabel *self)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+
+ if (!self->select_info)
+ return;
+
+ if (gtk_widget_is_sensitive (widget))
+ {
+ if (self->select_info->active_link)
+ gtk_widget_set_cursor_from_name (widget, "pointer");
+ else if (self->select_info->selectable)
+ gtk_widget_set_cursor_from_name (widget, "text");
+ else
+ gtk_widget_set_cursor (widget, NULL);
+ }
+ else
+ gtk_widget_set_cursor (widget, NULL);
+}
+
+static void
+gtk_label_state_flags_changed (GtkWidget *widget,
+ GtkStateFlags prev_state)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+
+ if (self->select_info)
+ {
+ if (!gtk_widget_is_sensitive (widget))
+ gtk_label_select_region (self, 0, 0);
+
+ gtk_label_update_cursor (self);
+ update_link_state (self);
+ }
+
+ if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
+}
+
+static void
+gtk_label_update_layout_attributes (GtkLabel *self,
+ PangoAttrList *style_attrs)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+ GtkCssStyle *style;
+ PangoAttrList *attrs;
+
+ if (self->layout == NULL)
+ {
+ pango_attr_list_unref (style_attrs);
+ return;
+ }
+
+ if (self->select_info && self->select_info->links)
+ {
+ guint i;
+
+ attrs = pango_attr_list_new ();
+
+ for (i = 0; i < self->select_info->n_links; i++)
+ {
+ const GtkLabelLink *link = &self->select_info->links[i];
+ const GdkRGBA *link_color;
+ PangoAttrList *link_attrs;
+ PangoAttribute *attr;
+
+ style = gtk_css_node_get_style (link->cssnode);
+ link_attrs = gtk_css_style_get_pango_attributes (style);
+ if (link_attrs)
+ {
+ GSList *attributes = pango_attr_list_get_attributes (link_attrs);
+ GSList *l;
+ for (l = attributes; l; l = l->next)
+ {
+ attr = l->data;
+
+ attr->start_index = link->start;
+ attr->end_index = link->end;
+ pango_attr_list_insert (attrs, attr);
+ }
+ g_slist_free (attributes);
+ }
+
+ link_color = gtk_css_color_value_get_rgba (style->core->color);
+ attr = pango_attr_foreground_new (link_color->red * 65535,
+ link_color->green * 65535,
+ link_color->blue * 65535);
+ attr->start_index = link->start;
+ attr->end_index = link->end;
+ pango_attr_list_insert (attrs, attr);
+
+ pango_attr_list_unref (link_attrs);
+ }
+ }
+ else
+ attrs = NULL;
+
+ style = gtk_css_node_get_style (gtk_widget_get_css_node (widget));
+ if (!style_attrs)
+ style_attrs = gtk_css_style_get_pango_attributes (style);
+
+ if (style_attrs)
+ {
+ attrs = _gtk_pango_attr_list_merge (attrs, style_attrs);
+ pango_attr_list_unref (style_attrs);
+ }
+
+ attrs = _gtk_pango_attr_list_merge (attrs, self->markup_attrs);
+ attrs = _gtk_pango_attr_list_merge (attrs, self->attrs);
+
+ pango_layout_set_attributes (self->layout, attrs);
+
+ pango_attr_list_unref (attrs);
+}
+
+static void
+gtk_label_css_changed (GtkWidget *widget,
+ GtkCssStyleChange *change)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ gboolean attrs_affected;
+ PangoAttrList *new_attrs = NULL;
+
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->css_changed (widget, change);
+
+ if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT_ATTRS))
+ {
+ new_attrs = gtk_css_style_get_pango_attributes (gtk_css_style_change_get_new_style (change));
+ attrs_affected = (self->layout && pango_layout_get_attributes (self->layout)) ||
+ new_attrs;
+ }
+ else
+ attrs_affected = FALSE;
+
+ if (change == NULL || attrs_affected || (self->select_info && self->select_info->links))
+ {
+ gtk_label_update_layout_attributes (self, new_attrs);
+
+ if (attrs_affected)
+ gtk_widget_queue_draw (widget);
+ }
+}
+
+static PangoDirection
+get_cursor_direction (GtkLabel *self)
+{
+ GSList *l;
+
+ g_assert (self->select_info);
+
+ gtk_label_ensure_layout (self);
+
+ for (l = pango_layout_get_lines_readonly (self->layout); l; l = l->next)
+ {
+ PangoLayoutLine *line = l->data;
+
+ /* If self->select_info->selection_end is at the very end of
+ * the line, we don't know if the cursor is on this line or
+ * the next without looking ahead at the next line. (End
+ * of paragraph is different from line break.) But it's
+ * definitely in this paragraph, which is good enough
+ * to figure out the resolved direction.
+ */
+ if (line->start_index + line->length >= self->select_info->selection_end)
+ return line->resolved_dir;
+ }
+
+ return PANGO_DIRECTION_LTR;
+}
+
+static GtkLabelLink *
+gtk_label_get_focus_link (GtkLabel *self,
+ int *out_index)
+{
+ GtkLabelSelectionInfo *info = self->select_info;
+ int link_index;
+
+ if (!info ||
+ info->selection_anchor != info->selection_end)
+ goto nope;
+
+ link_index = _gtk_label_get_link_at (self, info->selection_anchor);
+
+ if (link_index != -1)
+ {
+ if (out_index)
+ *out_index = link_index;
+
+ return &info->links[link_index];
+ }
+
+nope:
+ if (out_index)
+ *out_index = -1;
+ return NULL;
+}
+
+/**
+ * gtk_label_get_measuring_layout:
+ * @self: the label
+ * @existing_layout: %NULL or an existing layout already in use.
+ * @width: the width to measure with in pango units, or -1 for infinite
+ *
+ * Gets a layout that can be used for measuring sizes. The returned
+ * layout will be identical to the label’s layout except for the
+ * layout’s width, which will be set to @width. Do not modify the returned
+ * layout.
+ *
+ * Returns: a new reference to a pango layout
+ **/
+static PangoLayout *
+gtk_label_get_measuring_layout (GtkLabel *self,
+ PangoLayout *existing_layout,
+ int width)
+{
+ PangoLayout *copy;
+
+ if (existing_layout != NULL)
+ {
+ if (existing_layout != self->layout)
+ {
+ pango_layout_set_width (existing_layout, width);
+ return existing_layout;
+ }
+
+ g_object_unref (existing_layout);
+ }
+
+ gtk_label_ensure_layout (self);
+
+ if (pango_layout_get_width (self->layout) == width)
+ {
+ g_object_ref (self->layout);
+ return self->layout;
+ }
+
+ /* We can use the label's own layout if we're not allocated a size yet,
+ * because we don't need it to be properly setup at that point.
+ * This way we can make use of caching upon the label's creation.
+ */
+ if (gtk_widget_get_width (GTK_WIDGET (self)) <= 1)
+ {
+ g_object_ref (self->layout);
+ pango_layout_set_width (self->layout, width);
+ return self->layout;
+ }
+
+ /* oftentimes we want to measure a width that is far wider than the current width,
+ * even though the layout would not change if we made it wider. In that case, we
+ * can just return the current layout, because for measuring purposes, it will be
+ * identical.
+ */
+ if (!pango_layout_is_wrapped (self->layout) &&
+ !pango_layout_is_ellipsized (self->layout))
+ {
+ PangoRectangle rect;
+
+ if (width == -1)
+ return g_object_ref (self->layout);
+
+ pango_layout_get_extents (self->layout, NULL, &rect);
+ if (rect.width <= width)
+ return g_object_ref (self->layout);
+ }
+
+ copy = pango_layout_copy (self->layout);
+ pango_layout_set_width (copy, width);
+ return copy;
+}
+
+static void
+get_height_for_width (GtkLabel *self,
+ int width,
+ int *minimum_height,
+ int *natural_height,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ PangoLayout *layout;
+ int text_height, baseline;
+
+ layout = gtk_label_get_measuring_layout (self, NULL, width * PANGO_SCALE);
+
+ pango_layout_get_pixel_size (layout, NULL, &text_height);
+
+ *minimum_height = text_height;
+ *natural_height = text_height;
+
+ baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+ *minimum_baseline = baseline;
+ *natural_baseline = baseline;
+
+ g_object_unref (layout);
+}
+
+static int
+get_char_pixels (GtkWidget *self,
+ PangoLayout *layout)
+{
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ int char_width, digit_width;
+
+ context = pango_layout_get_context (layout);
+ metrics = pango_context_get_metrics (context,
+ pango_context_get_font_description (context),
+ pango_context_get_language (context));
+ char_width = pango_font_metrics_get_approximate_char_width (metrics);
+ digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
+ pango_font_metrics_unref (metrics);
+
+ return MAX (char_width, digit_width);;
+}
+
+static void
+gtk_label_get_preferred_layout_size (GtkLabel *self,
+ PangoRectangle *smallest,
+ PangoRectangle *widest,
+ int *smallest_baseline,
+ int *widest_baseline)
+{
+ PangoLayout *layout;
+ int char_pixels;
+
+ /* "width-chars" Hard-coded minimum width:
+ * - minimum size should be MAX (width-chars, strlen ("..."));
+ * - natural size should be MAX (width-chars, strlen (self->text));
+ *
+ * "max-width-chars" User specified maximum size requisition
+ * - minimum size should be MAX (width-chars, 0)
+ * - natural size should be MIN (max-width-chars, strlen (self->text))
+ *
+ * For ellipsizing labels; if max-width-chars is specified: either it is used as
+ * a minimum size or the label text as a minimum size (natural size still overflows).
+ *
+ * For wrapping labels; A reasonable minimum size is useful to naturally layout
+ * interfaces automatically. In this case if no "width-chars" is specified, the minimum
+ * width will default to the wrap guess that gtk_label_ensure_layout() does.
+ */
+
+ /* Start off with the pixel extents of an as-wide-as-possible layout */
+ layout = gtk_label_get_measuring_layout (self, NULL, -1);
+
+ if (self->width_chars > -1 || self->max_width_chars > -1)
+ char_pixels = get_char_pixels (GTK_WIDGET (self), layout);
+ else
+ char_pixels = 0;
+
+ pango_layout_get_extents (layout, NULL, widest);
+ widest->width = MAX (widest->width, char_pixels * self->width_chars);
+ widest->x = widest->y = 0;
+ *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+
+ if (self->ellipsize || self->wrap)
+ {
+ /* a layout with width 0 will be as small as humanly possible */
+ layout = gtk_label_get_measuring_layout (self,
+ layout,
+ self->width_chars > -1 ? char_pixels * self->width_chars
+ : 0);
+
+ pango_layout_get_extents (layout, NULL, smallest);
+ smallest->width = MAX (smallest->width, char_pixels * self->width_chars);
+ smallest->x = smallest->y = 0;
+
+ *smallest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+
+ if (self->max_width_chars > -1 && widest->width > char_pixels * self->max_width_chars)
+ {
+ layout = gtk_label_get_measuring_layout (self,
+ layout,
+ MAX (smallest->width, char_pixels * self->max_width_chars));
+ pango_layout_get_extents (layout, NULL, widest);
+ widest->width = MAX (widest->width, char_pixels * self->width_chars);
+ widest->x = widest->y = 0;
+
+ *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+ }
+
+ if (widest->width < smallest->width)
+ {
+ *smallest = *widest;
+ *smallest_baseline = *widest_baseline;
+ }
+ }
+ else
+ {
+ *smallest = *widest;
+ *smallest_baseline = *widest_baseline;
+ }
+
+ g_object_unref (layout);
+}
+
+static void
+gtk_label_get_preferred_size (GtkWidget *widget,
+ GtkOrientation orientation,
+ int *minimum_size,
+ int *natural_size,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ PangoRectangle widest_rect;
+ PangoRectangle smallest_rect;
+ int smallest_baseline;
+ int widest_baseline;
+
+ gtk_label_get_preferred_layout_size (self,
+ &smallest_rect, &widest_rect,
+ &smallest_baseline, &widest_baseline);
+
+ widest_rect.width = PANGO_PIXELS_CEIL (widest_rect.width);
+ widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
+
+ smallest_rect.width = PANGO_PIXELS_CEIL (smallest_rect.width);
+ smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ /* Normal desired width */
+ *minimum_size = smallest_rect.width;
+ *natural_size = widest_rect.width;
+
+ if (minimum_baseline)
+ *minimum_baseline = -1;
+
+ if (natural_baseline)
+ *natural_baseline = -1;
+ }
+ else /* GTK_ORIENTATION_VERTICAL */
+ {
+ if (smallest_rect.height < widest_rect.height)
+ {
+ *minimum_size = smallest_rect.height;
+ *natural_size = widest_rect.height;
+ if (minimum_baseline)
+ *minimum_baseline = smallest_baseline;
+ if (natural_baseline)
+ *natural_baseline = widest_baseline;
+ }
+ else
+ {
+ *minimum_size = widest_rect.height;
+ *natural_size = smallest_rect.height;
+ if (minimum_baseline)
+ *minimum_baseline = widest_baseline;
+ if (natural_baseline)
+ *natural_baseline = smallest_baseline;
+ }
+ }
+}
+
+
+static void
+gtk_label_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+
+ if (orientation == GTK_ORIENTATION_VERTICAL && for_size != -1 && self->wrap)
+ {
+ gtk_label_clear_layout (self);
+
+ get_height_for_width (self, for_size, minimum, natural, minimum_baseline, natural_baseline);
+ }
+ else
+ gtk_label_get_preferred_size (widget, orientation, minimum, natural, minimum_baseline, natural_baseline);
+}
+
+static void
+get_layout_location (GtkLabel *self,
+ int *xp,
+ int *yp)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+ int layout_width, layout_height, x, y;
+ float xalign, yalign;
+ PangoRectangle logical;
+ int baseline, layout_baseline, baseline_offset;
+ int widget_width, widget_height;
+
+ xalign = self->xalign;
+ yalign = self->yalign;
+
+ if (_gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
+ xalign = 1.0 - xalign;
+
+ pango_layout_get_pixel_extents (self->layout, NULL, &logical);
+
+ layout_width = logical.width;
+ layout_height = logical.height;
+
+ widget_width = gtk_widget_get_width (widget);
+ widget_height = gtk_widget_get_height (widget);
+
+ baseline = gtk_widget_get_allocated_baseline (widget);
+
+ x = floor ((xalign * (widget_width - layout_width)) - logical.x);
+
+ baseline_offset = 0;
+ if (baseline != -1)
+ {
+ layout_baseline = pango_layout_get_baseline (self->layout) / PANGO_SCALE;
+ baseline_offset = baseline - layout_baseline;
+ yalign = 0.0; /* Can't support yalign while baseline aligning */
+ }
+
+ y = floor ((widget_height - layout_height) * yalign) + baseline_offset;
+
+ if (xp)
+ *xp = x;
+
+ if (yp)
+ *yp = y;
+}
+
+static void
+gtk_label_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+
+ if (self->layout)
+ {
+ if (self->ellipsize || self->wrap)
+ pango_layout_set_width (self->layout, width * PANGO_SCALE);
+ else
+ pango_layout_set_width (self->layout, -1);
+ }
+
+ if (self->popup_menu)
+ gtk_popover_present (GTK_POPOVER (self->popup_menu));
+}
+
+
+
+#define GRAPHENE_RECT_FROM_RECT(_r) (GRAPHENE_RECT_INIT ((_r)->x, (_r)->y, (_r)->width, (_r)->height))
+
+static void
+gtk_label_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ GtkLabelSelectionInfo *info;
+ GtkStyleContext *context;
+ int lx, ly;
+ int width, height;
+
+ if (!self->text || (*self->text == '\0'))
+ return;
+
+ gtk_label_ensure_layout (self);
+
+ context = _gtk_widget_get_style_context (widget);
+ get_layout_location (self, &lx, &ly);
+
+ gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
+
+ info = self->select_info;
+ if (!info)
+ return;
+
+ width = gtk_widget_get_width (widget);
+ height = gtk_widget_get_height (widget);
+
+ if (info->selection_anchor != info->selection_end)
+ {
+ int range[2];
+ cairo_region_t *range_clip;
+ cairo_rectangle_int_t clip_rect;
+ int i;
+
+ range[0] = MIN (info->selection_anchor, info->selection_end);
+ range[1] = MAX (info->selection_anchor, info->selection_end);
+
+ gtk_style_context_save_to_node (context, info->selection_node);
+
+ range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
+ for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
+ {
+ cairo_region_get_rectangle (range_clip, i, &clip_rect);
+
+ gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
+ gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
+ gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
+ gtk_snapshot_pop (snapshot);
+ }
+
+ cairo_region_destroy (range_clip);
+
+ gtk_style_context_restore (context);
+ }
+ else
+ {
+ GtkLabelLink *focus_link;
+ GtkLabelLink *active_link;
+ int range[2];
+ cairo_region_t *range_clip;
+ cairo_rectangle_int_t clip_rect;
+ int i;
+ GdkRectangle rect;
+
+ if (info->selectable &&
+ gtk_widget_has_focus (widget) &&
+ gtk_widget_is_drawable (widget))
+ {
+ PangoDirection cursor_direction;
+
+ cursor_direction = get_cursor_direction (self);
+ gtk_snapshot_render_insertion_cursor (snapshot, context,
+ lx, ly,
+ self->layout, self->select_info->selection_end,
+ cursor_direction);
+ }
+
+ focus_link = gtk_label_get_focus_link (self, NULL);
+ active_link = info->active_link;
+
+ if (active_link)
+ {
+ range[0] = active_link->start;
+ range[1] = active_link->end;
+
+ gtk_style_context_save_to_node (context, active_link->cssnode);
+
+ range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
+ for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
+ {
+ cairo_region_get_rectangle (range_clip, i, &clip_rect);
+
+ gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
+ gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
+ gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
+ gtk_snapshot_pop (snapshot);
+ }
+
+ cairo_region_destroy (range_clip);
+
+ gtk_style_context_restore (context);
+ }
+
+ if (focus_link && gtk_widget_has_visible_focus (widget))
+ {
+ range[0] = focus_link->start;
+ range[1] = focus_link->end;
+
+ gtk_style_context_save_to_node (context, focus_link->cssnode);
+
+ range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
+ cairo_region_get_extents (range_clip, &rect);
+
+ gtk_snapshot_render_focus (snapshot, context, rect.x, rect.y, rect.width, rect.height);
+
+ cairo_region_destroy (range_clip);
+
+ gtk_style_context_restore (context);
+ }
+ }
+}
+
+static GtkSizeRequestMode
+gtk_label_get_request_mode (GtkWidget *widget)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+
+ if (self->wrap)
+ return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+
+ return GTK_SIZE_REQUEST_CONSTANT_SIZE;
+}
+
+static void
+gtk_label_dispose (GObject *object)
+{
+ GtkLabel *self = GTK_LABEL (object);
+
+ gtk_label_set_mnemonic_widget (self, NULL);
+
+ G_OBJECT_CLASS (gtk_label_parent_class)->dispose (object);
+}
+
+static void
+gtk_label_clear_links (GtkLabel *self)
+{
+ guint i;
+
+ if (!self->select_info)
+ return;
+
+ for (i = 0; i < self->select_info->n_links; i++)
+ {
+ const GtkLabelLink *link = &self->select_info->links[i];
+ gtk_css_node_set_parent (link->cssnode, NULL);
+ g_free (link->uri);
+ g_free (link->title);
+ }
+ g_free (self->select_info->links);
+ self->select_info->links = NULL;
+ self->select_info->n_links = 0;
+ self->select_info->active_link = NULL;
+ gtk_widget_remove_css_class (GTK_WIDGET (self), "link");
+}
+
+static void
+gtk_label_finalize (GObject *object)
+{
+ GtkLabel *self = GTK_LABEL (object);
+
+ g_free (self->label);
+ g_free (self->text);
+
+ g_clear_object (&self->layout);
+ g_clear_pointer (&self->attrs, pango_attr_list_unref);
+ g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
+
+ if (self->select_info)
+ g_object_unref (self->select_info->provider);
+
+ gtk_label_clear_links (self);
+ g_free (self->select_info);
+
+ g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
+ g_clear_object (&self->extra_menu);
+
+ G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
+}
+
+static void
+gtk_label_unrealize (GtkWidget *widget)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+
+ if (self->select_info &&
+ self->select_info->provider)
+ {
+ GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (widget);
+
+ if (gdk_clipboard_get_content (clipboard) == self->select_info->provider)
+ gdk_clipboard_set_content (clipboard, NULL);
+ }
+
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
+}
+
+static gboolean
+range_is_in_ellipsis_full (GtkLabel *self,
+ int range_start,
+ int range_end,
+ int *ellipsis_start,
+ int *ellipsis_end)
+{
+ PangoLayoutIter *iter;
+ gboolean in_ellipsis;
+
+ if (!self->ellipsize)
+ return FALSE;
+
+ gtk_label_ensure_layout (self);
+
+ if (!pango_layout_is_ellipsized (self->layout))
+ return FALSE;
+
+ iter = pango_layout_get_iter (self->layout);
+
+ in_ellipsis = FALSE;
+
+ do {
+ PangoLayoutRun *run;
+
+ run = pango_layout_iter_get_run_readonly (iter);
+ if (run)
+ {
+ PangoItem *item;
+
+ item = ((PangoGlyphItem*)run)->item;
+
+ if (item->offset <= range_start && range_end <= item->offset + item->length)
+ {
+ if (item->analysis.flags & PANGO_ANALYSIS_FLAG_IS_ELLIPSIS)
+ {
+ if (ellipsis_start)
+ *ellipsis_start = item->offset;
+ if (ellipsis_end)
+ *ellipsis_end = item->offset + item->length;
+ in_ellipsis = TRUE;
+ }
+ break;
+ }
+ else if (item->offset + item->length >= range_end)
+ break;
+ }
+ } while (pango_layout_iter_next_run (iter));
+
+ pango_layout_iter_free (iter);
+
+ return in_ellipsis;
+}
+
+static gboolean
+range_is_in_ellipsis (GtkLabel *self,
+ int range_start,
+ int range_end)
+{
+ return range_is_in_ellipsis_full (self, range_start, range_end, NULL, NULL);
+}
+
+static gboolean
+gtk_label_grab_focus (GtkWidget *widget)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ gboolean select_on_focus;
+ GtkWidget *prev_focus;
+
+ if (self->select_info == NULL)
+ return FALSE;
+
+ prev_focus = gtk_root_get_focus (gtk_widget_get_root (widget));
+
+ if (!GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget))
+ return FALSE;
+
+ if (self->select_info->selectable)
+ {
+ g_object_get (gtk_widget_get_settings (widget),
+ "gtk-label-select-on-focus",
+ &select_on_focus,
+ NULL);
+
+ if (select_on_focus && !self->in_click &&
+ !(prev_focus && gtk_widget_is_ancestor (prev_focus, widget)))
+ gtk_label_select_region (self, 0, -1);
+ }
+ else
+ {
+ if (self->select_info->links && !self->in_click &&
+ !(prev_focus && gtk_widget_is_ancestor (prev_focus, widget)))
+ {
+ guint i;
+
+ for (i = 0; i < self->select_info->n_links; i++)
+ {
+ const GtkLabelLink *link = &self->select_info->links[i];
+
+ if (!range_is_in_ellipsis (self, link->start, link->end))
+ {
+ self->select_info->selection_anchor = link->start;
+ self->select_info->selection_end = link->start;
+ break;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+get_layout_index (GtkLabel *self,
+ int x,
+ int y,
+ int *index)
+{
+ int trailing = 0;
+ const char *cluster;
+ const char *cluster_end;
+ gboolean inside;
+ int lx, ly;
+
+ *index = 0;
+
+ gtk_label_ensure_layout (self);
+ get_layout_location (self, &lx, &ly);
+
+ /* Translate x/y to layout position */
+ x -= lx;
+ y -= ly;
+
+ x *= PANGO_SCALE;
+ y *= PANGO_SCALE;
+
+ inside = pango_layout_xy_to_index (self->layout,
+ x, y,
+ index, &trailing);
+
+ cluster = self->text + *index;
+ cluster_end = cluster;
+ while (trailing)
+ {
+ cluster_end = g_utf8_next_char (cluster_end);
+ --trailing;
+ }
+
+ *index += (cluster_end - cluster);
+
+ return inside;
+}
+
+static gboolean
+gtk_label_query_tooltip (GtkWidget *widget,
+ int x,
+ int y,
+ gboolean keyboard_tip,
+ GtkTooltip *tooltip)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ GtkLabelSelectionInfo *info = self->select_info;
+ int index = -1;
+
+ if (info && info->links)
+ {
+ if (keyboard_tip)
+ {
+ if (info->selection_anchor == info->selection_end)
+ index = info->selection_anchor;
+ }
+ else
+ {
+ if (!get_layout_index (self, x, y, &index))
+ index = -1;
+ }
+
+ if (index != -1)
+ {
+ const int link_index = _gtk_label_get_link_at (self, index);
+
+ if (link_index != -1)
+ {
+ const GtkLabelLink *link = &info->links[link_index];
+
+ if (link->title)
+ {
+ gtk_tooltip_set_markup (tooltip, link->title);
+ }
+ }
+ }
+ }
+
+ return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
+ x, y,
+ keyboard_tip,
+ tooltip);
+}
+
+static gboolean
+gtk_label_focus (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ GtkLabelSelectionInfo *info = self->select_info;
+ GtkLabelLink *focus_link;
+
+ if (!gtk_widget_is_focus (widget))
+ {
+ gtk_widget_grab_focus (widget);
+ if (info)
+ {
+ focus_link = gtk_label_get_focus_link (self, NULL);
+ if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
+ {
+ int i;
+ for (i = info->n_links - 1; i >= 0; i--)
+ {
+ focus_link = &info->links[i];
+ if (!range_is_in_ellipsis (self, focus_link->start, focus_link->end))
+ {
+ info->selection_anchor = focus_link->start;
+ info->selection_end = focus_link->start;
+ }
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ if (!info)
+ return FALSE;
+
+ if (info->selectable)
+ {
+ int index;
+
+ if (info->selection_anchor != info->selection_end)
+ goto out;
+
+ index = info->selection_anchor;
+
+ if (direction == GTK_DIR_TAB_FORWARD)
+ {
+ guint i;
+ for (i = 0; i < info->n_links; i++)
+ {
+ const GtkLabelLink *link = &info->links[i];
+
+ if (link->start > index)
+ {
+ if (!range_is_in_ellipsis (self, link->start, link->end))
+ {
+ gtk_label_select_region_index (self, link->start, link->start);
+ return TRUE;
+ }
+ }
+ }
+ }
+ else if (direction == GTK_DIR_TAB_BACKWARD)
+ {
+ int i;
+ for (i = info->n_links - 1; i >= 0; i--)
+ {
+ GtkLabelLink *link = &info->links[i];
+
+ if (link->end < index)
+ {
+ if (!range_is_in_ellipsis (self, link->start, link->end))
+ {
+ gtk_label_select_region_index (self, link->start, link->start);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ goto out;
+ }
+ else
+ {
+ int focus_link_index;
+ int new_index = -1;
+ int i;
+
+ if (info->n_links == 0)
+ goto out;
+
+ focus_link = gtk_label_get_focus_link (self, &focus_link_index);
+
+ if (!focus_link)
+ goto out;
+
+ switch (direction)
+ {
+ case GTK_DIR_TAB_FORWARD:
+ if (focus_link)
+ new_index = (focus_link_index + 1) % info->n_links;
+ else
+ new_index = 0;
+
+ for (i = new_index; i < info->n_links; i++)
+ {
+ const GtkLabelLink *link = &info->links[i];
+ if (!range_is_in_ellipsis (self, link->start, link->end))
+ break;
+ }
+ break;
+
+ case GTK_DIR_TAB_BACKWARD:
+ if (focus_link)
+ new_index = focus_link_index == 0 ? info->n_links - 1 : focus_link_index - 1;
+ else
+ new_index = info->n_links - 1;
+
+ for (i = new_index; i >= 0; i--)
+ {
+ const GtkLabelLink *link = &info->links[i];
+ if (!range_is_in_ellipsis (self, link->start, link->end))
+ break;
+ }
+ break;
+
+ default:
+ case GTK_DIR_UP:
+ case GTK_DIR_DOWN:
+ case GTK_DIR_LEFT:
+ case GTK_DIR_RIGHT:
+ goto out;
+ }
+
+ if (new_index != -1)
+ {
+ focus_link = &info->links[new_index];
+ info->selection_anchor = focus_link->start;
+ info->selection_end = focus_link->start;
+ gtk_widget_queue_draw (widget);
+
+ return TRUE;
+ }
+ }
+
+out:
+
+ return FALSE;
+}
+
+static void
+emit_activate_link (GtkLabel *self,
+ GtkLabelLink *link)
+{
+ gboolean handled;
+
+ g_signal_emit (self, signals[ACTIVATE_LINK], 0, link->uri, &handled);
+
+ /* signal handler might have invalidated the layout */
+ if (!self->layout)
+ return;
+
+ if (handled && !link->visited &&
+ self->select_info && self->select_info->links)
+ {
+ link->visited = TRUE;
+ update_link_state (self);
+ }
+}
+
+static void
+gtk_label_activate_link_open (GtkWidget *widget,
+ const char *name,
+ GVariant *parameter)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ GtkLabelLink *link = self->select_info->context_link;
+
+ if (link)
+ emit_activate_link (self, link);
+}
+
+static void
+gtk_label_activate_link_copy (GtkWidget *widget,
+ const char *name,
+ GVariant *parameter)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ GtkLabelLink *link = self->select_info->context_link;
+
+ if (link)
+ {
+ GdkClipboard *clipboard;
+
+ clipboard = gtk_widget_get_clipboard (widget);
+ gdk_clipboard_set_text (clipboard, link->uri);
+ }
+ else
+ g_print ("no link ?!\n");
+}
+
+static void
+gtk_label_activate_clipboard_copy (GtkWidget *widget,
+ const char *name,
+ GVariant *parameter)
+{
+ g_signal_emit_by_name (widget, "copy-clipboard");
+}
+
+static void
+gtk_label_select_all (GtkLabel *self)
+{
+ gtk_label_select_region_index (self, 0, strlen (self->text));
+}
+
+static void
+gtk_label_activate_selection_select_all (GtkWidget *widget,
+ const char *name,
+ GVariant *parameter)
+{
+ gtk_label_select_all (GTK_LABEL (widget));
+}
+
+static void
+gtk_label_nop (GtkWidget *widget,
+ const char *name,
+ GVariant *parameter)
+{
+}
+
+static gboolean
+gtk_label_mnemonic_activate (GtkWidget *widget,
+ gboolean group_cycling)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ GtkWidget *parent;
+
+ if (self->mnemonic_widget)
+ return gtk_widget_mnemonic_activate (self->mnemonic_widget, group_cycling);
+
+ /* Try to find the widget to activate by traversing the
+ * widget's ancestry.
+ */
+ parent = gtk_widget_get_parent (widget);
+
+ if (GTK_IS_NOTEBOOK (parent))
+ return FALSE;
+
+ while (parent)
+ {
+ if (gtk_widget_get_can_focus (parent) ||
+ (!group_cycling && gtk_widget_can_activate (parent)) ||
+ GTK_IS_NOTEBOOK (gtk_widget_get_parent (parent)))
+ return gtk_widget_mnemonic_activate (parent, group_cycling);
+ parent = gtk_widget_get_parent (parent);
+ }
+
+ /* barf if there was nothing to activate */
+ g_warning ("Couldn't find a target for a mnemonic activation.");
+ gtk_widget_error_bell (widget);
+
+ return FALSE;
+}
+
+static void
+gtk_label_popup_menu (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameters)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+
+ gtk_label_do_popup (self, -1, -1);
+}
+
+static void
+gtk_label_root (GtkWidget *widget)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->root (widget);
+
+ gtk_label_setup_mnemonic (self);
+
+ /* The PangoContext is replaced when the display changes, so clear the layouts */
+ gtk_label_clear_layout (GTK_LABEL (widget));
+}
+
+static void
+gtk_label_unroot (GtkWidget *widget)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+
+ gtk_label_setup_mnemonic (self);
+
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->unroot (widget);
+}
+
+static gboolean
+gtk_label_activate_link (GtkLabel *self,
+ const char *uri)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+ GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
+
+ if (!GTK_IS_WINDOW (toplevel))
+ return FALSE;
+
+ gtk_show_uri (GTK_WINDOW (toplevel), uri, GDK_CURRENT_TIME);
+
+ return TRUE;
+}
+
+static void
+gtk_label_activate_current_link (GtkLabel *self)
+{
+ GtkLabelLink *link;
+ GtkWidget *widget = GTK_WIDGET (self);
+
+ link = gtk_label_get_focus_link (self, NULL);
+
+ if (link)
+ emit_activate_link (self, link);
+ else
+ gtk_widget_activate_default (widget);
+}
+
+static void
+gtk_label_copy_clipboard (GtkLabel *self)
+{
+ if (self->text && self->select_info)
+ {
+ int start, end;
+ int len;
+ GdkClipboard *clipboard;
+
+ start = MIN (self->select_info->selection_anchor,
+ self->select_info->selection_end);
+ end = MAX (self->select_info->selection_anchor,
+ self->select_info->selection_end);
+
+ len = strlen (self->text);
+
+ if (end > len)
+ end = len;
+
+ if (start > len)
+ start = len;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self));
+
+ if (start != end)
+ {
+ char *str = g_strndup (self->text + start, end - start);
+ gdk_clipboard_set_text (clipboard, str);
+ g_free (str);
+ }
+ else
+ {
+ GtkLabelLink *link;
+
+ link = gtk_label_get_focus_link (self, NULL);
+ if (link)
+ gdk_clipboard_set_text (clipboard, link->uri);
+ }
+ }
+}
+
+static void
gtk_label_class_init (GtkLabelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
@@ -651,17 +2095,17 @@ gtk_label_class_init (GtkLabelClass *class)
* - Ctrl-arrow key combinations move by words/paragraphs
* - Home/End keys move to the ends of the buffer
*/
- signals[MOVE_CURSOR] =
+ signals[MOVE_CURSOR] =
g_signal_new (I_("move-cursor"),
- G_OBJECT_CLASS_TYPE (gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
- NULL, NULL,
- _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
- G_TYPE_NONE, 3,
- GTK_TYPE_MOVEMENT_STEP,
- G_TYPE_INT,
- G_TYPE_BOOLEAN);
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
+ NULL, NULL,
+ _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
+ G_TYPE_NONE, 3,
+ GTK_TYPE_MOVEMENT_STEP,
+ G_TYPE_INT,
+ G_TYPE_BOOLEAN);
/**
* GtkLabel::copy-clipboard:
@@ -672,16 +2116,16 @@ gtk_label_class_init (GtkLabelClass *class)
* which gets emitted to copy the selection to the clipboard.
*
* The default binding for this signal is Ctrl-c.
- */
+ */
signals[COPY_CLIPBOARD] =
g_signal_new (I_("copy-clipboard"),
- G_OBJECT_CLASS_TYPE (gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
- NULL, NULL,
- NULL,
- G_TYPE_NONE, 0);
-
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
/**
* GtkLabel::activate-current-link:
* @self: The label on which the signal was emitted
@@ -959,7 +2403,7 @@ gtk_label_class_init (GtkLabelClass *class)
/**
* GtkLabel|menu.popup:
*
- * Opens the context menu.
+ * Opens the context menu.
*/
gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_label_popup_menu);
@@ -978,34 +2422,34 @@ gtk_label_class_init (GtkLabelClass *class)
/* Moving the insertion point */
add_move_binding (widget_class, GDK_KEY_Right, 0,
- GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+ GTK_MOVEMENT_VISUAL_POSITIONS, 1);
add_move_binding (widget_class, GDK_KEY_Left, 0,
- GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+ GTK_MOVEMENT_VISUAL_POSITIONS, -1);
add_move_binding (widget_class, GDK_KEY_KP_Right, 0,
- GTK_MOVEMENT_VISUAL_POSITIONS, 1);
-
+ GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+
add_move_binding (widget_class, GDK_KEY_KP_Left, 0,
- GTK_MOVEMENT_VISUAL_POSITIONS, -1);
-
+ GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+
add_move_binding (widget_class, GDK_KEY_f, GDK_CONTROL_MASK,
- GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
-
+ GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
+
add_move_binding (widget_class, GDK_KEY_b, GDK_CONTROL_MASK,
- GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
-
+ GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
+
add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK,
- GTK_MOVEMENT_WORDS, 1);
+ GTK_MOVEMENT_WORDS, 1);
add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK,
- GTK_MOVEMENT_WORDS, -1);
+ GTK_MOVEMENT_WORDS, -1);
add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
- GTK_MOVEMENT_WORDS, 1);
+ GTK_MOVEMENT_WORDS, 1);
add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
- GTK_MOVEMENT_WORDS, -1);
+ GTK_MOVEMENT_WORDS, -1);
/* select all */
gtk_widget_class_add_binding (widget_class,
@@ -1020,61 +2464,61 @@ gtk_label_class_init (GtkLabelClass *class)
/* unselect all */
gtk_widget_class_add_binding_signal (widget_class,
GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
- "move-cursor",
+ "move-cursor",
"(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE);
gtk_widget_class_add_binding_signal (widget_class,
GDK_KEY_backslash, GDK_CONTROL_MASK,
- "move-cursor",
+ "move-cursor",
"(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE);
add_move_binding (widget_class, GDK_KEY_f, GDK_ALT_MASK,
- GTK_MOVEMENT_WORDS, 1);
+ GTK_MOVEMENT_WORDS, 1);
add_move_binding (widget_class, GDK_KEY_b, GDK_ALT_MASK,
- GTK_MOVEMENT_WORDS, -1);
+ GTK_MOVEMENT_WORDS, -1);
add_move_binding (widget_class, GDK_KEY_Home, 0,
- GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
add_move_binding (widget_class, GDK_KEY_End, 0,
- GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
add_move_binding (widget_class, GDK_KEY_KP_Home, 0,
- GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
add_move_binding (widget_class, GDK_KEY_KP_End, 0,
- GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
-
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+
add_move_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK,
- GTK_MOVEMENT_BUFFER_ENDS, -1);
+ GTK_MOVEMENT_BUFFER_ENDS, -1);
add_move_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK,
- GTK_MOVEMENT_BUFFER_ENDS, 1);
+ GTK_MOVEMENT_BUFFER_ENDS, 1);
add_move_binding (widget_class, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
- GTK_MOVEMENT_BUFFER_ENDS, -1);
+ GTK_MOVEMENT_BUFFER_ENDS, -1);
add_move_binding (widget_class, GDK_KEY_KP_End, GDK_CONTROL_MASK,
- GTK_MOVEMENT_BUFFER_ENDS, 1);
+ GTK_MOVEMENT_BUFFER_ENDS, 1);
/* copy */
gtk_widget_class_add_binding_signal (widget_class,
GDK_KEY_c, GDK_CONTROL_MASK,
- "copy-clipboard",
+ "copy-clipboard",
NULL);
gtk_widget_class_add_binding_signal (widget_class,
GDK_KEY_Return, 0,
- "activate-current-link",
+ "activate-current-link",
NULL);
gtk_widget_class_add_binding_signal (widget_class,
GDK_KEY_ISO_Enter, 0,
- "activate-current-link",
+ "activate-current-link",
NULL);
gtk_widget_class_add_binding_signal (widget_class,
GDK_KEY_KP_Enter, 0,
- "activate-current-link",
+ "activate-current-link",
NULL);
gtk_widget_class_set_css_name (widget_class, I_("label"));
@@ -1140,240 +2584,6 @@ gtk_label_class_init (GtkLabelClass *class)
gtk_label_activate_link_copy);
}
-static void
-gtk_label_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GtkLabel *self = GTK_LABEL (object);
-
- switch (prop_id)
- {
- case PROP_LABEL:
- gtk_label_set_label (self, g_value_get_string (value));
- break;
- case PROP_ATTRIBUTES:
- gtk_label_set_attributes (self, g_value_get_boxed (value));
- break;
- case PROP_USE_MARKUP:
- gtk_label_set_use_markup (self, g_value_get_boolean (value));
- break;
- case PROP_USE_UNDERLINE:
- gtk_label_set_use_underline (self, g_value_get_boolean (value));
- break;
- case PROP_JUSTIFY:
- gtk_label_set_justify (self, g_value_get_enum (value));
- break;
- case PROP_WRAP:
- gtk_label_set_wrap (self, g_value_get_boolean (value));
- break;
- case PROP_WRAP_MODE:
- gtk_label_set_wrap_mode (self, g_value_get_enum (value));
- break;
- case PROP_SELECTABLE:
- gtk_label_set_selectable (self, g_value_get_boolean (value));
- break;
- case PROP_MNEMONIC_WIDGET:
- gtk_label_set_mnemonic_widget (self, (GtkWidget*) g_value_get_object (value));
- break;
- case PROP_ELLIPSIZE:
- gtk_label_set_ellipsize (self, g_value_get_enum (value));
- break;
- case PROP_WIDTH_CHARS:
- gtk_label_set_width_chars (self, g_value_get_int (value));
- break;
- case PROP_SINGLE_LINE_MODE:
- gtk_label_set_single_line_mode (self, g_value_get_boolean (value));
- break;
- case PROP_MAX_WIDTH_CHARS:
- gtk_label_set_max_width_chars (self, g_value_get_int (value));
- break;
- case PROP_LINES:
- gtk_label_set_lines (self, g_value_get_int (value));
- break;
- case PROP_XALIGN:
- gtk_label_set_xalign (self, g_value_get_float (value));
- break;
- case PROP_YALIGN:
- gtk_label_set_yalign (self, g_value_get_float (value));
- break;
- case PROP_EXTRA_MENU:
- gtk_label_set_extra_menu (self, g_value_get_object (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gtk_label_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GtkLabel *self = GTK_LABEL (object);
-
- switch (prop_id)
- {
- case PROP_LABEL:
- g_value_set_string (value, self->label);
- break;
- case PROP_ATTRIBUTES:
- g_value_set_boxed (value, self->attrs);
- break;
- case PROP_USE_MARKUP:
- g_value_set_boolean (value, self->use_markup);
- break;
- case PROP_USE_UNDERLINE:
- g_value_set_boolean (value, self->use_underline);
- break;
- case PROP_JUSTIFY:
- g_value_set_enum (value, self->jtype);
- break;
- case PROP_WRAP:
- g_value_set_boolean (value, self->wrap);
- break;
- case PROP_WRAP_MODE:
- g_value_set_enum (value, self->wrap_mode);
- break;
- case PROP_SELECTABLE:
- g_value_set_boolean (value, gtk_label_get_selectable (self));
- break;
- case PROP_MNEMONIC_KEYVAL:
- g_value_set_uint (value, self->mnemonic_keyval);
- break;
- case PROP_MNEMONIC_WIDGET:
- g_value_set_object (value, (GObject*) self->mnemonic_widget);
- break;
- case PROP_ELLIPSIZE:
- g_value_set_enum (value, self->ellipsize);
- break;
- case PROP_WIDTH_CHARS:
- g_value_set_int (value, gtk_label_get_width_chars (self));
- break;
- case PROP_SINGLE_LINE_MODE:
- g_value_set_boolean (value, gtk_label_get_single_line_mode (self));
- break;
- case PROP_MAX_WIDTH_CHARS:
- g_value_set_int (value, gtk_label_get_max_width_chars (self));
- break;
- case PROP_LINES:
- g_value_set_int (value, gtk_label_get_lines (self));
- break;
- case PROP_XALIGN:
- g_value_set_float (value, gtk_label_get_xalign (self));
- break;
- case PROP_YALIGN:
- g_value_set_float (value, gtk_label_get_yalign (self));
- break;
- case PROP_EXTRA_MENU:
- g_value_set_object (value, gtk_label_get_extra_menu (self));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gtk_label_init (GtkLabel *self)
-{
- self->width_chars = -1;
- self->max_width_chars = -1;
- self->label = g_strdup ("");
- self->lines = -1;
-
- self->xalign = 0.5;
- self->yalign = 0.5;
-
- self->jtype = GTK_JUSTIFY_LEFT;
- self->wrap = FALSE;
- self->wrap_mode = PANGO_WRAP_WORD;
- self->ellipsize = PANGO_ELLIPSIZE_NONE;
-
- self->use_underline = FALSE;
- self->use_markup = FALSE;
-
- self->mnemonic_keyval = GDK_KEY_VoidSymbol;
- self->layout = NULL;
- self->text = g_strdup ("");
- self->attrs = NULL;
-
- self->mnemonic_widget = NULL;
-
- self->mnemonics_visible = FALSE;
-}
-
-
-static void
-gtk_label_buildable_interface_init (GtkBuildableIface *iface)
-{
- buildable_parent_iface = g_type_interface_peek_parent (iface);
-
- iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
- iface->custom_finished = gtk_label_buildable_custom_finished;
-}
-
-static const GtkBuildableParser pango_parser =
-{
- gtk_pango_attribute_start_element,
-};
-
-static gboolean
-gtk_label_buildable_custom_tag_start (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *child,
- const char *tagname,
- GtkBuildableParser *parser,
- gpointer *data)
-{
- if (buildable_parent_iface->custom_tag_start (buildable, builder, child,
- tagname, parser, data))
- return TRUE;
-
- if (strcmp (tagname, "attributes") == 0)
- {
- GtkPangoAttributeParserData *parser_data;
-
- parser_data = g_slice_new0 (GtkPangoAttributeParserData);
- parser_data->builder = g_object_ref (builder);
- parser_data->object = (GObject *) g_object_ref (buildable);
- *parser = pango_parser;
- *data = parser_data;
- return TRUE;
- }
- return FALSE;
-}
-
-static void
-gtk_label_buildable_custom_finished (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *child,
- const char *tagname,
- gpointer user_data)
-{
- GtkPangoAttributeParserData *data = user_data;
-
- buildable_parent_iface->custom_finished (buildable, builder, child,
- tagname, user_data);
-
- if (strcmp (tagname, "attributes") == 0)
- {
- if (data->attrs)
- {
- gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
- pango_attr_list_unref (data->attrs);
- }
-
- g_object_unref (data->object);
- g_object_unref (data->builder);
- g_slice_free (GtkPangoAttributeParserData, data);
- }
-}
-
-
/**
* gtk_label_new:
* @str: (nullable): The text of the label
@@ -1405,15 +2615,15 @@ gtk_label_new (const char *str)
*
* If characters in @str are preceded by an underscore, they are
* underlined. If you need a literal underscore character in a label, use
- * '__' (two underscores). The first underlined character represents a
- * keyboard accelerator called a mnemonic. The mnemonic key can be used
+ * '__' (two underscores). The first underlined character represents a
+ * keyboard accelerator called a mnemonic. The mnemonic key can be used
* to activate another widget, chosen automatically, or explicitly using
* gtk_label_set_mnemonic_widget().
- *
- * If gtk_label_set_mnemonic_widget() is not called, then the first
- * activatable ancestor of the #GtkLabel will be chosen as the mnemonic
- * widget. For instance, if the label is inside a button or menu item,
- * the button or menu item will automatically become the mnemonic widget
+ *
+ * If gtk_label_set_mnemonic_widget() is not called, then the first
+ * activatable ancestor of the #GtkLabel will be chosen as the mnemonic
+ * widget. For instance, if the label is inside a button or menu item,
+ * the button or menu item will automatically become the mnemonic widget
* and be activated by the mnemonic.
*
* Returns: the new #GtkLabel
@@ -1431,40 +2641,6 @@ gtk_label_new_with_mnemonic (const char *str)
return GTK_WIDGET (self);
}
-static gboolean
-gtk_label_mnemonic_activate (GtkWidget *widget,
- gboolean group_cycling)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkWidget *parent;
-
- if (self->mnemonic_widget)
- return gtk_widget_mnemonic_activate (self->mnemonic_widget, group_cycling);
-
- /* Try to find the widget to activate by traversing the
- * widget's ancestry.
- */
- parent = gtk_widget_get_parent (widget);
-
- if (GTK_IS_NOTEBOOK (parent))
- return FALSE;
-
- while (parent)
- {
- if (gtk_widget_get_can_focus (parent) ||
- (!group_cycling && gtk_widget_can_activate (parent)) ||
- GTK_IS_NOTEBOOK (gtk_widget_get_parent (parent)))
- return gtk_widget_mnemonic_activate (parent, group_cycling);
- parent = gtk_widget_get_parent (parent);
- }
-
- /* barf if there was nothing to activate */
- g_warning ("Couldn't find a target for a mnemonic activation.");
- gtk_widget_error_bell (widget);
-
- return FALSE;
-}
-
static void
label_mnemonics_visible_changed (GtkWidget *widget,
GParamSpec *pspec,
@@ -1521,8 +2697,8 @@ gtk_label_setup_mnemonic (GtkLabel *self)
g_object_get (native, "mnemonics-visible", &mnemonics_visible, NULL);
self->mnemonics_visible = mnemonics_visible;
- connected =
- GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (native), quark_mnemonics_visible_connected));
+ connected = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (native),
+ quark_mnemonics_visible_connected));
if (!connected)
{
@@ -1536,29 +2712,6 @@ gtk_label_setup_mnemonic (GtkLabel *self)
}
}
-static void
-gtk_label_root (GtkWidget *widget)
-{
- GtkLabel *self = GTK_LABEL (widget);
-
- GTK_WIDGET_CLASS (gtk_label_parent_class)->root (widget);
-
- gtk_label_setup_mnemonic (self);
-
- /* The PangoContext is replaced when the display changes, so clear the layouts */
- gtk_label_clear_layout (GTK_LABEL (widget));
-}
-
-static void
-gtk_label_unroot (GtkWidget *widget)
-{
- GtkLabel *self = GTK_LABEL (widget);
-
- gtk_label_setup_mnemonic (self);
-
- GTK_WIDGET_CLASS (gtk_label_parent_class)->unroot (widget);
-}
-
void
_gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
gboolean visible)
@@ -1590,7 +2743,7 @@ _gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
}
static void
label_mnemonic_widget_weak_notify (gpointer data,
- GObject *where_the_object_was)
+ GObject *where_the_object_was)
{
GtkLabel *self = data;
@@ -1613,14 +2766,14 @@ label_mnemonic_widget_weak_notify (gpointer data,
* (i.e. when the target is a #GtkEntry next to the label) you need to
* set it explicitly using this function.
*
- * The target widget will be accelerated by emitting the
- * GtkWidget::mnemonic-activate signal on it. The default handler for
- * this signal will activate the widget if there are no mnemonic collisions
+ * The target widget will be accelerated by emitting the
+ * GtkWidget::mnemonic-activate signal on it. The default handler for
+ * this signal will activate the widget if there are no mnemonic collisions
* and toggle focus between the colliding widgets otherwise.
**/
void
gtk_label_set_mnemonic_widget (GtkLabel *self,
- GtkWidget *widget)
+ GtkWidget *widget)
{
g_return_if_fail (GTK_IS_LABEL (self));
@@ -2106,14 +3259,6 @@ xml_isspace (char c)
return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
}
-static void
-link_free (GtkLabelLink *link)
-{
- gtk_css_node_set_parent (link->cssnode, NULL);
- g_free (link->uri);
- g_free (link->title);
-}
-
static gboolean
parse_uri_markup (GtkLabel *self,
const char *str,
@@ -2364,7 +3509,6 @@ no_uline:
if (text)
gtk_label_set_text_internal (self, text);
-
g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
self->markup_attrs = attrs;
@@ -2470,11 +3614,11 @@ gtk_label_set_markup_with_mnemonic (GtkLabel *self,
/**
* gtk_label_get_text:
* @self: a #GtkLabel
- *
+ *
* Fetches the text from a label widget, as displayed on the
* screen. This does not include any embedded underlines
* indicating mnemonics or Pango markup. (See gtk_label_get_label())
- *
+ *
* Returns: the text in the label widget. This is the internal
* string used by the label, and must not be modified.
**/
@@ -2500,7 +3644,7 @@ gtk_label_get_text (GtkLabel *self)
*/
void
gtk_label_set_justify (GtkLabel *self,
- GtkJustification jtype)
+ GtkJustification jtype)
{
g_return_if_fail (GTK_IS_LABEL (self));
g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
@@ -2511,7 +3655,7 @@ gtk_label_set_justify (GtkLabel *self,
/* No real need to be this drastic, but easier than duplicating the code */
gtk_label_clear_layout (self);
-
+
g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_JUSTIFY]);
gtk_widget_queue_resize (GTK_WIDGET (self));
}
@@ -2538,12 +3682,12 @@ gtk_label_get_justify (GtkLabel *self)
* @self: a #GtkLabel
* @mode: a #PangoEllipsizeMode
*
- * Sets the mode used to ellipsize (add an ellipsis: "...") to the text
+ * Sets the mode used to ellipsize (add an ellipsis: "...") to the text
* if there is not enough space to render the entire string.
**/
void
gtk_label_set_ellipsize (GtkLabel *self,
- PangoEllipsizeMode mode)
+ PangoEllipsizeMode mode)
{
g_return_if_fail (GTK_IS_LABEL (self));
g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
@@ -2580,12 +3724,12 @@ gtk_label_get_ellipsize (GtkLabel *self)
* gtk_label_set_width_chars:
* @self: a #GtkLabel
* @n_chars: the new desired width, in characters.
- *
+ *
* Sets the desired width in characters of @label to @n_chars.
**/
void
gtk_label_set_width_chars (GtkLabel *self,
- int n_chars)
+ int n_chars)
{
g_return_if_fail (GTK_IS_LABEL (self));
@@ -2600,10 +3744,10 @@ gtk_label_set_width_chars (GtkLabel *self,
/**
* gtk_label_get_width_chars:
* @self: a #GtkLabel
- *
+ *
* Retrieves the desired width of @label, in characters. See
* gtk_label_set_width_chars().
- *
+ *
* Returns: the width of the label in characters.
**/
int
@@ -2618,12 +3762,12 @@ gtk_label_get_width_chars (GtkLabel *self)
* gtk_label_set_max_width_chars:
* @self: a #GtkLabel
* @n_chars: the new desired maximum width, in characters.
- *
+ *
* Sets the desired maximum width in characters of @label to @n_chars.
**/
void
gtk_label_set_max_width_chars (GtkLabel *self,
- int n_chars)
+ int n_chars)
{
g_return_if_fail (GTK_IS_LABEL (self));
@@ -2639,10 +3783,10 @@ gtk_label_set_max_width_chars (GtkLabel *self,
/**
* gtk_label_get_max_width_chars:
* @self: a #GtkLabel
- *
+ *
* Retrieves the desired maximum width of @label, in characters. See
* gtk_label_set_width_chars().
- *
+ *
* Returns: the maximum width of the label in characters.
**/
int
@@ -2744,194 +3888,11 @@ gtk_label_get_wrap_mode (GtkLabel *self)
}
static void
-gtk_label_dispose (GObject *object)
-{
- GtkLabel *self = GTK_LABEL (object);
-
- gtk_label_set_mnemonic_widget (self, NULL);
-
- G_OBJECT_CLASS (gtk_label_parent_class)->dispose (object);
-}
-
-static void
-gtk_label_finalize (GObject *object)
-{
- GtkLabel *self = GTK_LABEL (object);
-
- g_free (self->label);
- g_free (self->text);
-
- g_clear_object (&self->layout);
- g_clear_pointer (&self->attrs, pango_attr_list_unref);
- g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
-
- if (self->select_info)
- g_object_unref (self->select_info->provider);
-
- gtk_label_clear_links (self);
- g_free (self->select_info);
-
- g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
- g_clear_object (&self->extra_menu);
-
- G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
-}
-
-static void
gtk_label_clear_layout (GtkLabel *self)
{
g_clear_object (&self->layout);
}
-/**
- * gtk_label_get_measuring_layout:
- * @self: the label
- * @existing_layout: %NULL or an existing layout already in use.
- * @width: the width to measure with in pango units, or -1 for infinite
- *
- * Gets a layout that can be used for measuring sizes. The returned
- * layout will be identical to the label’s layout except for the
- * layout’s width, which will be set to @width. Do not modify the returned
- * layout.
- *
- * Returns: a new reference to a pango layout
- **/
-static PangoLayout *
-gtk_label_get_measuring_layout (GtkLabel *self,
- PangoLayout *existing_layout,
- int width)
-{
- PangoLayout *copy;
-
- if (existing_layout != NULL)
- {
- if (existing_layout != self->layout)
- {
- pango_layout_set_width (existing_layout, width);
- return existing_layout;
- }
-
- g_object_unref (existing_layout);
- }
-
- gtk_label_ensure_layout (self);
-
- if (pango_layout_get_width (self->layout) == width)
- {
- g_object_ref (self->layout);
- return self->layout;
- }
-
- /* We can use the label's own layout if we're not allocated a size yet,
- * because we don't need it to be properly setup at that point.
- * This way we can make use of caching upon the label's creation.
- */
- if (gtk_widget_get_width (GTK_WIDGET (self)) <= 1)
- {
- g_object_ref (self->layout);
- pango_layout_set_width (self->layout, width);
- return self->layout;
- }
-
- /* oftentimes we want to measure a width that is far wider than the current width,
- * even though the layout would not change if we made it wider. In that case, we
- * can just return the current layout, because for measuring purposes, it will be
- * identical.
- */
- if (!pango_layout_is_wrapped (self->layout) &&
- !pango_layout_is_ellipsized (self->layout))
- {
- PangoRectangle rect;
-
- if (width == -1)
- return g_object_ref (self->layout);
-
- pango_layout_get_extents (self->layout, NULL, &rect);
- if (rect.width <= width)
- return g_object_ref (self->layout);
- }
-
- copy = pango_layout_copy (self->layout);
- pango_layout_set_width (copy, width);
- return copy;
-}
-
-static void
-gtk_label_update_layout_attributes (GtkLabel *self,
- PangoAttrList *style_attrs)
-{
- GtkWidget *widget = GTK_WIDGET (self);
- GtkCssStyle *style;
- PangoAttrList *attrs;
-
- if (self->layout == NULL)
- {
- pango_attr_list_unref (style_attrs);
- return;
- }
-
- if (self->select_info && self->select_info->links)
- {
- guint i;
-
- attrs = pango_attr_list_new ();
-
- for (i = 0; i < self->select_info->n_links; i++)
- {
- const GtkLabelLink *link = &self->select_info->links[i];
- const GdkRGBA *link_color;
- PangoAttrList *link_attrs;
- PangoAttribute *attr;
-
- style = gtk_css_node_get_style (link->cssnode);
- link_attrs = gtk_css_style_get_pango_attributes (style);
- if (link_attrs)
- {
- GSList *attributes = pango_attr_list_get_attributes (link_attrs);
- GSList *l;
- for (l = attributes; l; l = l->next)
- {
- attr = l->data;
-
- attr->start_index = link->start;
- attr->end_index = link->end;
- pango_attr_list_insert (attrs, attr);
- }
- g_slist_free (attributes);
- }
-
- link_color = gtk_css_color_value_get_rgba (style->core->color);
- attr = pango_attr_foreground_new (link_color->red * 65535,
- link_color->green * 65535,
- link_color->blue * 65535);
- attr->start_index = link->start;
- attr->end_index = link->end;
- pango_attr_list_insert (attrs, attr);
-
- pango_attr_list_unref (link_attrs);
- }
- }
- else
- attrs = NULL;
-
- style = gtk_css_node_get_style (gtk_widget_get_css_node (widget));
- if (!style_attrs)
- style_attrs = gtk_css_style_get_pango_attributes (style);
-
- if (style_attrs)
- {
- attrs = _gtk_pango_attr_list_merge (attrs, style_attrs);
- pango_attr_list_unref (style_attrs);
- }
-
- attrs = _gtk_pango_attr_list_merge (attrs, self->markup_attrs);
- attrs = _gtk_pango_attr_list_merge (attrs, self->attrs);
-
- pango_layout_set_attributes (self->layout, attrs);
-
- pango_attr_list_unref (attrs);
-}
-
static void
gtk_label_ensure_layout (GtkLabel *self)
{
@@ -2977,564 +3938,6 @@ gtk_label_ensure_layout (GtkLabel *self)
pango_layout_set_width (self->layout, gtk_widget_get_width (GTK_WIDGET (self)) * PANGO_SCALE);
}
-static GtkSizeRequestMode
-gtk_label_get_request_mode (GtkWidget *widget)
-{
- GtkLabel *self = GTK_LABEL (widget);
-
- if (self->wrap)
- return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
-
- return GTK_SIZE_REQUEST_CONSTANT_SIZE;
-}
-
-
-static void
-get_height_for_width (GtkLabel *self,
- int width,
- int *minimum_height,
- int *natural_height,
- int *minimum_baseline,
- int *natural_baseline)
-{
- PangoLayout *layout;
- int text_height, baseline;
-
- layout = gtk_label_get_measuring_layout (self, NULL, width * PANGO_SCALE);
-
- pango_layout_get_pixel_size (layout, NULL, &text_height);
-
- *minimum_height = text_height;
- *natural_height = text_height;
-
- baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
- *minimum_baseline = baseline;
- *natural_baseline = baseline;
-
- g_object_unref (layout);
-}
-
-static int
-get_char_pixels (GtkWidget *self,
- PangoLayout *layout)
-{
- PangoContext *context;
- PangoFontMetrics *metrics;
- int char_width, digit_width;
-
- context = pango_layout_get_context (layout);
- metrics = pango_context_get_metrics (context,
- pango_context_get_font_description (context),
- pango_context_get_language (context));
- char_width = pango_font_metrics_get_approximate_char_width (metrics);
- digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
- pango_font_metrics_unref (metrics);
-
- return MAX (char_width, digit_width);;
-}
-
-static void
-gtk_label_get_preferred_layout_size (GtkLabel *self,
- PangoRectangle *smallest,
- PangoRectangle *widest,
- int *smallest_baseline,
- int *widest_baseline)
-{
- PangoLayout *layout;
- int char_pixels;
-
- /* "width-chars" Hard-coded minimum width:
- * - minimum size should be MAX (width-chars, strlen ("..."));
- * - natural size should be MAX (width-chars, strlen (self->text));
- *
- * "max-width-chars" User specified maximum size requisition
- * - minimum size should be MAX (width-chars, 0)
- * - natural size should be MIN (max-width-chars, strlen (self->text))
- *
- * For ellipsizing labels; if max-width-chars is specified: either it is used as
- * a minimum size or the label text as a minimum size (natural size still overflows).
- *
- * For wrapping labels; A reasonable minimum size is useful to naturally layout
- * interfaces automatically. In this case if no "width-chars" is specified, the minimum
- * width will default to the wrap guess that gtk_label_ensure_layout() does.
- */
-
- /* Start off with the pixel extents of an as-wide-as-possible layout */
- layout = gtk_label_get_measuring_layout (self, NULL, -1);
-
- if (self->width_chars > -1 || self->max_width_chars > -1)
- char_pixels = get_char_pixels (GTK_WIDGET (self), layout);
- else
- char_pixels = 0;
-
- pango_layout_get_extents (layout, NULL, widest);
- widest->width = MAX (widest->width, char_pixels * self->width_chars);
- widest->x = widest->y = 0;
- *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
-
- if (self->ellipsize || self->wrap)
- {
- /* a layout with width 0 will be as small as humanly possible */
- layout = gtk_label_get_measuring_layout (self,
- layout,
- self->width_chars > -1 ? char_pixels * self->width_chars
- : 0);
-
- pango_layout_get_extents (layout, NULL, smallest);
- smallest->width = MAX (smallest->width, char_pixels * self->width_chars);
- smallest->x = smallest->y = 0;
-
- *smallest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
-
- if (self->max_width_chars > -1 && widest->width > char_pixels * self->max_width_chars)
- {
- layout = gtk_label_get_measuring_layout (self,
- layout,
- MAX (smallest->width, char_pixels * self->max_width_chars));
- pango_layout_get_extents (layout, NULL, widest);
- widest->width = MAX (widest->width, char_pixels * self->width_chars);
- widest->x = widest->y = 0;
-
- *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
- }
-
- if (widest->width < smallest->width)
- {
- *smallest = *widest;
- *smallest_baseline = *widest_baseline;
- }
- }
- else
- {
- *smallest = *widest;
- *smallest_baseline = *widest_baseline;
- }
-
- g_object_unref (layout);
-}
-
-static void
-gtk_label_get_preferred_size (GtkWidget *widget,
- GtkOrientation orientation,
- int *minimum_size,
- int *natural_size,
- int *minimum_baseline,
- int *natural_baseline)
-{
- GtkLabel *self = GTK_LABEL (widget);
- PangoRectangle widest_rect;
- PangoRectangle smallest_rect;
- int smallest_baseline;
- int widest_baseline;
-
- gtk_label_get_preferred_layout_size (self,
- &smallest_rect, &widest_rect,
- &smallest_baseline, &widest_baseline);
-
- widest_rect.width = PANGO_PIXELS_CEIL (widest_rect.width);
- widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
-
- smallest_rect.width = PANGO_PIXELS_CEIL (smallest_rect.width);
- smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
-
- if (orientation == GTK_ORIENTATION_HORIZONTAL)
- {
- /* Normal desired width */
- *minimum_size = smallest_rect.width;
- *natural_size = widest_rect.width;
-
- if (minimum_baseline)
- *minimum_baseline = -1;
-
- if (natural_baseline)
- *natural_baseline = -1;
- }
- else /* GTK_ORIENTATION_VERTICAL */
- {
- if (smallest_rect.height < widest_rect.height)
- {
- *minimum_size = smallest_rect.height;
- *natural_size = widest_rect.height;
- if (minimum_baseline)
- *minimum_baseline = smallest_baseline;
- if (natural_baseline)
- *natural_baseline = widest_baseline;
- }
- else
- {
- *minimum_size = widest_rect.height;
- *natural_size = smallest_rect.height;
- if (minimum_baseline)
- *minimum_baseline = widest_baseline;
- if (natural_baseline)
- *natural_baseline = smallest_baseline;
- }
- }
-}
-
-static void
-gtk_label_measure (GtkWidget *widget,
- GtkOrientation orientation,
- int for_size,
- int *minimum,
- int *natural,
- int *minimum_baseline,
- int *natural_baseline)
-{
- GtkLabel *self = GTK_LABEL (widget);
-
- if (orientation == GTK_ORIENTATION_VERTICAL && for_size != -1 && self->wrap)
- {
- gtk_label_clear_layout (self);
-
- get_height_for_width (self, for_size, minimum, natural, minimum_baseline, natural_baseline);
- }
- else
- gtk_label_get_preferred_size (widget, orientation, minimum, natural, minimum_baseline, natural_baseline);
-}
-
-static void
-get_layout_location (GtkLabel *self,
- int *xp,
- int *yp)
-{
- GtkWidget *widget = GTK_WIDGET (self);
- int layout_width, layout_height, x, y;
- float xalign, yalign;
- PangoRectangle logical;
- int baseline, layout_baseline, baseline_offset;
- int widget_width, widget_height;
-
- xalign = self->xalign;
- yalign = self->yalign;
-
- if (_gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
- xalign = 1.0 - xalign;
-
- pango_layout_get_pixel_extents (self->layout, NULL, &logical);
-
- layout_width = logical.width;
- layout_height = logical.height;
-
- widget_width = gtk_widget_get_width (widget);
- widget_height = gtk_widget_get_height (widget);
-
- baseline = gtk_widget_get_allocated_baseline (widget);
-
- x = floor ((xalign * (widget_width - layout_width)) - logical.x);
-
- baseline_offset = 0;
- if (baseline != -1)
- {
- layout_baseline = pango_layout_get_baseline (self->layout) / PANGO_SCALE;
- baseline_offset = baseline - layout_baseline;
- yalign = 0.0; /* Can't support yalign while baseline aligning */
- }
-
- y = floor ((widget_height - layout_height) * yalign) + baseline_offset;
-
- if (xp)
- *xp = x;
-
- if (yp)
- *yp = y;
-}
-
-static void
-gtk_label_size_allocate (GtkWidget *widget,
- int width,
- int height,
- int baseline)
-{
- GtkLabel *self = GTK_LABEL (widget);
-
- if (self->layout)
- {
- if (self->ellipsize || self->wrap)
- pango_layout_set_width (self->layout, width * PANGO_SCALE);
- else
- pango_layout_set_width (self->layout, -1);
- }
-
- if (self->popup_menu)
- gtk_popover_present (GTK_POPOVER (self->popup_menu));
-}
-
-static void
-gtk_label_update_cursor (GtkLabel *self)
-{
- GtkWidget *widget = GTK_WIDGET (self);
-
- if (!self->select_info)
- return;
-
- if (gtk_widget_is_sensitive (widget))
- {
- if (self->select_info->active_link)
- gtk_widget_set_cursor_from_name (widget, "pointer");
- else if (self->select_info->selectable)
- gtk_widget_set_cursor_from_name (widget, "text");
- else
- gtk_widget_set_cursor (widget, NULL);
- }
- else
- gtk_widget_set_cursor (widget, NULL);
-}
-
-static void
-update_link_state (GtkLabel *self)
-{
- GtkStateFlags state;
- guint i;
-
- if (!self->select_info)
- return;
-
- for (i = 0; i < self->select_info->n_links; i++)
- {
- const GtkLabelLink *link = &self->select_info->links[i];
-
- state = gtk_widget_get_state_flags (GTK_WIDGET (self));
- if (link->visited)
- state |= GTK_STATE_FLAG_VISITED;
- else
- state |= GTK_STATE_FLAG_LINK;
- if (link == self->select_info->active_link)
- {
- if (self->select_info->link_clicked)
- state |= GTK_STATE_FLAG_ACTIVE;
- else
- state |= GTK_STATE_FLAG_PRELIGHT;
- }
- gtk_css_node_set_state (link->cssnode, state);
- }
-}
-
-static void
-gtk_label_state_flags_changed (GtkWidget *widget,
- GtkStateFlags prev_state)
-{
- GtkLabel *self = GTK_LABEL (widget);
-
- if (self->select_info)
- {
- if (!gtk_widget_is_sensitive (widget))
- gtk_label_select_region (self, 0, 0);
-
- gtk_label_update_cursor (self);
- update_link_state (self);
- }
-
- if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
- GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
-}
-
-static void
-gtk_label_css_changed (GtkWidget *widget,
- GtkCssStyleChange *change)
-{
- GtkLabel *self = GTK_LABEL (widget);
- gboolean attrs_affected;
- PangoAttrList *new_attrs = NULL;
-
- GTK_WIDGET_CLASS (gtk_label_parent_class)->css_changed (widget, change);
-
- if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT_ATTRS))
- {
- new_attrs = gtk_css_style_get_pango_attributes (gtk_css_style_change_get_new_style (change));
- attrs_affected = (self->layout && pango_layout_get_attributes (self->layout)) ||
- new_attrs;
- }
- else
- attrs_affected = FALSE;
-
- if (change == NULL || attrs_affected || (self->select_info && self->select_info->links))
- {
- gtk_label_update_layout_attributes (self, new_attrs);
-
- if (attrs_affected)
- gtk_widget_queue_draw (widget);
- }
-}
-
-static PangoDirection
-get_cursor_direction (GtkLabel *self)
-{
- GSList *l;
-
- g_assert (self->select_info);
-
- gtk_label_ensure_layout (self);
-
- for (l = pango_layout_get_lines_readonly (self->layout); l; l = l->next)
- {
- PangoLayoutLine *line = l->data;
-
- /* If self->select_info->selection_end is at the very end of
- * the line, we don't know if the cursor is on this line or
- * the next without looking ahead at the next line. (End
- * of paragraph is different from line break.) But it's
- * definitely in this paragraph, which is good enough
- * to figure out the resolved direction.
- */
- if (line->start_index + line->length >= self->select_info->selection_end)
- return line->resolved_dir;
- }
-
- return PANGO_DIRECTION_LTR;
-}
-
-static GtkLabelLink *
-gtk_label_get_focus_link (GtkLabel *self,
- int *out_index)
-{
- GtkLabelSelectionInfo *info = self->select_info;
- int link_index;
-
- if (!info ||
- info->selection_anchor != info->selection_end)
- goto nope;
-
- link_index = _gtk_label_get_link_at (self, info->selection_anchor);
-
- if (link_index != -1)
- {
- if (out_index)
- *out_index = link_index;
-
- return &info->links[link_index];
- }
-
-nope:
- if (out_index)
- *out_index = -1;
- return NULL;
-}
-
-#define GRAPHENE_RECT_FROM_RECT(_r) (GRAPHENE_RECT_INIT ((_r)->x, (_r)->y, (_r)->width, (_r)->height))
-
-static void
-gtk_label_snapshot (GtkWidget *widget,
- GtkSnapshot *snapshot)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkLabelSelectionInfo *info;
- GtkStyleContext *context;
- int lx, ly;
- int width, height;
-
- if (!self->text || (*self->text == '\0'))
- return;
-
- gtk_label_ensure_layout (self);
-
- context = _gtk_widget_get_style_context (widget);
- get_layout_location (self, &lx, &ly);
-
- gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
-
- info = self->select_info;
- if (!info)
- return;
-
- width = gtk_widget_get_width (widget);
- height = gtk_widget_get_height (widget);
-
- if (info->selection_anchor != info->selection_end)
- {
- int range[2];
- cairo_region_t *range_clip;
- cairo_rectangle_int_t clip_rect;
- int i;
-
- range[0] = MIN (info->selection_anchor, info->selection_end);
- range[1] = MAX (info->selection_anchor, info->selection_end);
-
- gtk_style_context_save_to_node (context, info->selection_node);
-
- range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
- for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
- {
- cairo_region_get_rectangle (range_clip, i, &clip_rect);
-
- gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
- gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
- gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
- gtk_snapshot_pop (snapshot);
- }
-
- cairo_region_destroy (range_clip);
-
- gtk_style_context_restore (context);
- }
- else
- {
- GtkLabelLink *focus_link;
- GtkLabelLink *active_link;
- int range[2];
- cairo_region_t *range_clip;
- cairo_rectangle_int_t clip_rect;
- int i;
- GdkRectangle rect;
-
- if (info->selectable &&
- gtk_widget_has_focus (widget) &&
- gtk_widget_is_drawable (widget))
- {
- PangoDirection cursor_direction;
-
- cursor_direction = get_cursor_direction (self);
- gtk_snapshot_render_insertion_cursor (snapshot, context,
- lx, ly,
- self->layout, self->select_info->selection_end,
- cursor_direction);
- }
-
- focus_link = gtk_label_get_focus_link (self, NULL);
- active_link = info->active_link;
-
- if (active_link)
- {
- range[0] = active_link->start;
- range[1] = active_link->end;
-
- gtk_style_context_save_to_node (context, active_link->cssnode);
-
- range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
- for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
- {
- cairo_region_get_rectangle (range_clip, i, &clip_rect);
-
- gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
- gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
- gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
- gtk_snapshot_pop (snapshot);
- }
-
- cairo_region_destroy (range_clip);
-
- gtk_style_context_restore (context);
- }
-
- if (focus_link && gtk_widget_has_visible_focus (widget))
- {
- range[0] = focus_link->start;
- range[1] = focus_link->end;
-
- gtk_style_context_save_to_node (context, focus_link->cssnode);
-
- range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
- cairo_region_get_extents (range_clip, &rect);
-
- gtk_snapshot_render_focus (snapshot, context, rect.x, rect.y, rect.width, rect.height);
-
- cairo_region_destroy (range_clip);
-
- gtk_style_context_restore (context);
- }
- }
-}
-
/**
* gtk_label_set_text_with_mnemonic:
* @self: a #GtkLabel
@@ -3567,124 +3970,55 @@ gtk_label_set_text_with_mnemonic (GtkLabel *self,
g_object_thaw_notify (G_OBJECT (self));
}
-static void
-gtk_label_unrealize (GtkWidget *widget)
+static int
+gtk_label_move_forward_word (GtkLabel *self,
+ int start)
{
- GtkLabel *self = GTK_LABEL (widget);
+ int new_pos = g_utf8_pointer_to_offset (self->text, self->text + start);
+ int length;
- if (self->select_info &&
- self->select_info->provider)
+ length = g_utf8_strlen (self->text, -1);
+ if (new_pos < length)
{
- GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (widget);
-
- if (gdk_clipboard_get_content (clipboard) == self->select_info->provider)
- gdk_clipboard_set_content (clipboard, NULL);
- }
-
- GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
-}
-
-static gboolean
-get_layout_index (GtkLabel *self,
- int x,
- int y,
- int *index)
-{
- int trailing = 0;
- const char *cluster;
- const char *cluster_end;
- gboolean inside;
- int lx, ly;
-
- *index = 0;
-
- gtk_label_ensure_layout (self);
- get_layout_location (self, &lx, &ly);
-
- /* Translate x/y to layout position */
- x -= lx;
- y -= ly;
+ const PangoLogAttr *log_attrs;
+ int n_attrs;
- x *= PANGO_SCALE;
- y *= PANGO_SCALE;
+ gtk_label_ensure_layout (self);
- inside = pango_layout_xy_to_index (self->layout,
- x, y,
- index, &trailing);
+ log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
- cluster = self->text + *index;
- cluster_end = cluster;
- while (trailing)
- {
- cluster_end = g_utf8_next_char (cluster_end);
- --trailing;
+ /* Find the next word end */
+ new_pos++;
+ while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
+ new_pos++;
}
- *index += (cluster_end - cluster);
-
- return inside;
+ return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
}
-static gboolean
-range_is_in_ellipsis_full (GtkLabel *self,
- int range_start,
- int range_end,
- int *ellipsis_start,
- int *ellipsis_end)
+static int
+gtk_label_move_backward_word (GtkLabel *self,
+ int start)
{
- PangoLayoutIter *iter;
- gboolean in_ellipsis;
-
- if (!self->ellipsize)
- return FALSE;
-
- gtk_label_ensure_layout (self);
-
- if (!pango_layout_is_ellipsized (self->layout))
- return FALSE;
-
- iter = pango_layout_get_iter (self->layout);
+ int new_pos = g_utf8_pointer_to_offset (self->text, self->text + start);
- in_ellipsis = FALSE;
-
- do {
- PangoLayoutRun *run;
-
- run = pango_layout_iter_get_run_readonly (iter);
- if (run)
- {
- PangoItem *item;
+ if (new_pos > 0)
+ {
+ const PangoLogAttr *log_attrs;
+ int n_attrs;
- item = ((PangoGlyphItem*)run)->item;
+ gtk_label_ensure_layout (self);
- if (item->offset <= range_start && range_end <= item->offset + item->length)
- {
- if (item->analysis.flags & PANGO_ANALYSIS_FLAG_IS_ELLIPSIS)
- {
- if (ellipsis_start)
- *ellipsis_start = item->offset;
- if (ellipsis_end)
- *ellipsis_end = item->offset + item->length;
- in_ellipsis = TRUE;
- }
- break;
- }
- else if (item->offset + item->length >= range_end)
- break;
- }
- } while (pango_layout_iter_next_run (iter));
+ log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
- pango_layout_iter_free (iter);
+ new_pos -= 1;
- return in_ellipsis;
-}
+ /* Find the previous word beginning */
+ while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
+ new_pos--;
+ }
-static gboolean
-range_is_in_ellipsis (GtkLabel *self,
- int range_start,
- int range_end)
-{
- return range_is_in_ellipsis_full (self, range_start, range_end, NULL, NULL);
+ return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
}
static void
@@ -3696,9 +4030,9 @@ gtk_label_select_word (GtkLabel *self)
int end_index = gtk_label_move_forward_word (self, self->select_info->selection_end);
min = MIN (self->select_info->selection_anchor,
- self->select_info->selection_end);
+ self->select_info->selection_end);
max = MAX (self->select_info->selection_anchor,
- self->select_info->selection_end);
+ self->select_info->selection_end);
min = MIN (min, start_index);
max = MAX (max, end_index);
@@ -3706,207 +4040,6 @@ gtk_label_select_word (GtkLabel *self)
gtk_label_select_region_index (self, min, max);
}
-static gboolean
-gtk_label_grab_focus (GtkWidget *widget)
-{
- GtkLabel *self = GTK_LABEL (widget);
- gboolean select_on_focus;
- GtkWidget *prev_focus;
-
- if (self->select_info == NULL)
- return FALSE;
-
- prev_focus = gtk_root_get_focus (gtk_widget_get_root (widget));
-
- if (!GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget))
- return FALSE;
-
- if (self->select_info->selectable)
- {
- g_object_get (gtk_widget_get_settings (widget),
- "gtk-label-select-on-focus",
- &select_on_focus,
- NULL);
-
- if (select_on_focus && !self->in_click &&
- !(prev_focus && gtk_widget_is_ancestor (prev_focus, widget)))
- gtk_label_select_region (self, 0, -1);
- }
- else
- {
- if (self->select_info->links && !self->in_click &&
- !(prev_focus && gtk_widget_is_ancestor (prev_focus, widget)))
- {
- guint i;
-
- for (i = 0; i < self->select_info->n_links; i++)
- {
- const GtkLabelLink *link = &self->select_info->links[i];
-
- if (!range_is_in_ellipsis (self, link->start, link->end))
- {
- self->select_info->selection_anchor = link->start;
- self->select_info->selection_end = link->start;
- break;
- }
- }
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-gtk_label_focus (GtkWidget *widget,
- GtkDirectionType direction)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkLabelSelectionInfo *info = self->select_info;
- GtkLabelLink *focus_link;
-
- if (!gtk_widget_is_focus (widget))
- {
- gtk_widget_grab_focus (widget);
- if (info)
- {
- focus_link = gtk_label_get_focus_link (self, NULL);
- if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
- {
- int i;
- for (i = info->n_links - 1; i >= 0; i--)
- {
- focus_link = &info->links[i];
- if (!range_is_in_ellipsis (self, focus_link->start, focus_link->end))
- {
- info->selection_anchor = focus_link->start;
- info->selection_end = focus_link->start;
- }
- }
- }
-
- return TRUE;
- }
-
- return FALSE;
- }
-
- if (!info)
- return FALSE;
-
- if (info->selectable)
- {
- int index;
-
- if (info->selection_anchor != info->selection_end)
- goto out;
-
- index = info->selection_anchor;
-
- if (direction == GTK_DIR_TAB_FORWARD)
- {
- guint i;
- for (i = 0; i < info->n_links; i++)
- {
- const GtkLabelLink *link = &info->links[i];
-
- if (link->start > index)
- {
- if (!range_is_in_ellipsis (self, link->start, link->end))
- {
- gtk_label_select_region_index (self, link->start, link->start);
- return TRUE;
- }
- }
- }
- }
- else if (direction == GTK_DIR_TAB_BACKWARD)
- {
- int i;
- for (i = info->n_links - 1; i >= 0; i--)
- {
- GtkLabelLink *link = &info->links[i];
-
- if (link->end < index)
- {
- if (!range_is_in_ellipsis (self, link->start, link->end))
- {
- gtk_label_select_region_index (self, link->start, link->start);
- return TRUE;
- }
- }
- }
- }
-
- goto out;
- }
- else
- {
- int focus_link_index;
- int new_index = -1;
- int i;
-
- if (info->n_links == 0)
- goto out;
-
- focus_link = gtk_label_get_focus_link (self, &focus_link_index);
-
- if (!focus_link)
- goto out;
-
- switch (direction)
- {
- case GTK_DIR_TAB_FORWARD:
- if (focus_link)
- new_index = (focus_link_index + 1) % info->n_links;
- else
- new_index = 0;
-
- for (i = new_index; i < info->n_links; i++)
- {
- const GtkLabelLink *link = &info->links[i];
- if (!range_is_in_ellipsis (self, link->start, link->end))
- break;
- }
- break;
-
- case GTK_DIR_TAB_BACKWARD:
- if (focus_link)
- new_index = focus_link_index == 0 ? info->n_links - 1 : focus_link_index - 1;
- else
- new_index = info->n_links - 1;
-
- for (i = new_index; i >= 0; i--)
- {
- const GtkLabelLink *link = &info->links[i];
- if (!range_is_in_ellipsis (self, link->start, link->end))
- break;
- }
- break;
-
- default:
- case GTK_DIR_UP:
- case GTK_DIR_DOWN:
- case GTK_DIR_LEFT:
- case GTK_DIR_RIGHT:
- goto out;
- }
-
- if (new_index != -1)
- {
- focus_link = &info->links[new_index];
- info->selection_anchor = focus_link->start;
- info->selection_end = focus_link->start;
- gtk_widget_queue_draw (widget);
-
- return TRUE;
- }
- }
-
-out:
-
- return FALSE;
-}
-
static void
gtk_label_click_gesture_pressed (GtkGestureClick *gesture,
int n_press,
@@ -4139,7 +4272,7 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
if (info->in_drag)
{
if (gtk_drag_check_threshold (widget, info->drag_start_x, info->drag_start_y, x, y))
- {
+ {
GdkDrag *drag;
GdkSurface *surface;
GdkDevice *device;
@@ -4157,9 +4290,8 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
gtk_drag_icon_set_from_paintable (drag, get_selection_paintable (self), 0, 0);
g_object_unref (drag);
-
- info->in_drag = FALSE;
- }
+ info->in_drag = FALSE;
+ }
}
else
{
@@ -4212,6 +4344,34 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
}
static void
+gtk_label_update_actions (GtkLabel *self)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+ gboolean has_selection;
+ GtkLabelLink *link;
+
+ if (self->select_info)
+ {
+ has_selection = self->select_info->selection_anchor != self->select_info->selection_end;
+ link = self->select_info->active_link;
+ }
+ else
+ {
+ has_selection = FALSE;
+ link = gtk_label_get_focus_link (self, NULL);
+ }
+
+ gtk_widget_action_set_enabled (widget, "clipboard.cut", FALSE);
+ gtk_widget_action_set_enabled (widget, "clipboard.copy", has_selection);
+ gtk_widget_action_set_enabled (widget, "clipboard.paste", FALSE);
+ gtk_widget_action_set_enabled (widget, "selection.select-all",
+ gtk_label_get_selectable (self));
+ gtk_widget_action_set_enabled (widget, "selection.delete", FALSE);
+ gtk_widget_action_set_enabled (widget, "link.open", !has_selection && link);
+ gtk_widget_action_set_enabled (widget, "link.copy", !has_selection && link);
+}
+
+static void
gtk_label_update_active_link (GtkWidget *widget,
double x,
double y)
@@ -4530,9 +4690,9 @@ gtk_label_set_selectable (GtkLabel *self,
/**
* gtk_label_get_selectable:
* @self: a #GtkLabel
- *
+ *
* Gets the value set by gtk_label_set_selectable().
- *
+ *
* Returns: %TRUE if the user can copy text from the label
**/
gboolean
@@ -4678,10 +4838,10 @@ gtk_label_select_region (GtkLabel *self,
{
if (start_offset < 0)
start_offset = g_utf8_strlen (self->text, -1);
-
+
if (end_offset < 0)
end_offset = g_utf8_strlen (self->text, -1);
-
+
gtk_label_select_region_index (self,
g_utf8_offset_to_pointer (self->text, start_offset) - self->text,
g_utf8_offset_to_pointer (self->text, end_offset) - self->text);
@@ -4693,10 +4853,10 @@ gtk_label_select_region (GtkLabel *self,
* @self: a #GtkLabel
* @start: (out): return location for start of selection, as a character offset
* @end: (out): return location for end of selection, as a character offset
- *
+ *
* Gets the selected range of characters in the label, returning %TRUE
* if there’s a selection.
- *
+ *
* Returns: %TRUE if selection is non-empty
**/
gboolean
@@ -4721,7 +4881,7 @@ gtk_label_get_selection_bounds (GtkLabel *self,
int start_index, end_index;
int start_offset, end_offset;
int len;
-
+
start_index = MIN (self->select_info->selection_anchor,
self->select_info->selection_end);
end_index = MAX (self->select_info->selection_anchor,
@@ -4734,7 +4894,7 @@ gtk_label_get_selection_bounds (GtkLabel *self,
if (start_index > len)
start_index = len;
-
+
start_offset = g_utf8_strlen (self->text, start_index);
end_offset = g_utf8_strlen (self->text, end_index);
@@ -4744,7 +4904,7 @@ gtk_label_get_selection_bounds (GtkLabel *self,
start_offset = end_offset;
end_offset = tmp;
}
-
+
if (start)
*start = start_offset;
@@ -4759,7 +4919,7 @@ gtk_label_get_selection_bounds (GtkLabel *self,
/**
* gtk_label_get_layout:
* @self: a #GtkLabel
- *
+ *
* Gets the #PangoLayout used to display the label.
* The layout is useful to e.g. convert text positions to
* pixel positions, in combination with gtk_label_get_layout_offsets().
@@ -4989,11 +5149,10 @@ get_better_cursor (GtkLabel *self,
static int
gtk_label_move_logically (GtkLabel *self,
- int start,
- int count)
+ int start,
+ int count)
{
- int offset = g_utf8_pointer_to_offset (self->text,
- self->text + start);
+ int offset = g_utf8_pointer_to_offset (self->text, self->text + start);
if (self->text)
{
@@ -5008,21 +5167,21 @@ gtk_label_move_logically (GtkLabel *self,
log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
while (count > 0 && offset < length)
- {
- do
- offset++;
- while (offset < length && !log_attrs[offset].is_cursor_position);
-
- count--;
- }
+ {
+ do
+ offset++;
+ while (offset < length && !log_attrs[offset].is_cursor_position);
+
+ count--;
+ }
while (count < 0 && offset > 0)
- {
- do
- offset--;
- while (offset > 0 && !log_attrs[offset].is_cursor_position);
-
- count++;
- }
+ {
+ do
+ offset--;
+ while (offset > 0 && !log_attrs[offset].is_cursor_position);
+
+ count++;
+ }
}
return g_utf8_offset_to_pointer (self->text, offset) - self->text;
@@ -5030,13 +5189,13 @@ gtk_label_move_logically (GtkLabel *self,
static int
gtk_label_move_visually (GtkLabel *self,
- int start,
- int count)
+ int start,
+ int count)
{
int index;
index = start;
-
+
while (count != 0)
{
int new_index, new_trailing;
@@ -5046,8 +5205,8 @@ gtk_label_move_visually (GtkLabel *self,
gtk_label_ensure_layout (self);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (self)),
- "gtk-split-cursor", &split_cursor,
- NULL);
+ "gtk-split-cursor", &split_cursor,
+ NULL);
if (split_cursor)
strong = TRUE;
@@ -5071,80 +5230,26 @@ gtk_label_move_visually (GtkLabel *self,
}
if (count > 0)
- {
- pango_layout_move_cursor_visually (self->layout, strong, index, 0, 1, &new_index, &new_trailing);
- count--;
- }
+ {
+ pango_layout_move_cursor_visually (self->layout, strong, index, 0, 1, &new_index, &new_trailing);
+ count--;
+ }
else
- {
- pango_layout_move_cursor_visually (self->layout, strong, index, 0, -1, &new_index, &new_trailing);
- count++;
- }
+ {
+ pango_layout_move_cursor_visually (self->layout, strong, index, 0, -1, &new_index, &new_trailing);
+ count++;
+ }
if (new_index < 0 || new_index == G_MAXINT)
- break;
+ break;
index = new_index;
-
- while (new_trailing--)
- index = g_utf8_next_char (self->text + new_index) - self->text;
- }
-
- return index;
-}
-
-static int
-gtk_label_move_forward_word (GtkLabel *self,
- int start)
-{
- int new_pos = g_utf8_pointer_to_offset (self->text,
- self->text + start);
- int length;
-
- length = g_utf8_strlen (self->text, -1);
- if (new_pos < length)
- {
- const PangoLogAttr *log_attrs;
- int n_attrs;
-
- gtk_label_ensure_layout (self);
-
- log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
-
- /* Find the next word end */
- new_pos++;
- while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
- new_pos++;
- }
-
- return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
-}
-
-
-static int
-gtk_label_move_backward_word (GtkLabel *self,
- int start)
-{
- int new_pos = g_utf8_pointer_to_offset (self->text,
- self->text + start);
-
- if (new_pos > 0)
- {
- const PangoLogAttr *log_attrs;
- int n_attrs;
-
- gtk_label_ensure_layout (self);
-
- log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
-
- new_pos -= 1;
- /* Find the previous word beginning */
- while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
- new_pos--;
+ while (new_trailing--)
+ index = g_utf8_next_char (self->text + new_index) - self->text;
}
- return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
+ return index;
}
static void
@@ -5275,135 +5380,6 @@ gtk_label_move_cursor (GtkLabel *self,
gtk_label_select_region_index (self, new_pos, new_pos);
}
-static void
-gtk_label_copy_clipboard (GtkLabel *self)
-{
- if (self->text && self->select_info)
- {
- int start, end;
- int len;
- GdkClipboard *clipboard;
-
- start = MIN (self->select_info->selection_anchor,
- self->select_info->selection_end);
- end = MAX (self->select_info->selection_anchor,
- self->select_info->selection_end);
-
- len = strlen (self->text);
-
- if (end > len)
- end = len;
-
- if (start > len)
- start = len;
-
- clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self));
-
- if (start != end)
- {
- char *str = g_strndup (self->text + start, end - start);
- gdk_clipboard_set_text (clipboard, str);
- g_free (str);
- }
- else
- {
- GtkLabelLink *link;
-
- link = gtk_label_get_focus_link (self, NULL);
- if (link)
- gdk_clipboard_set_text (clipboard, link->uri);
- }
- }
-}
-
-static void
-gtk_label_select_all (GtkLabel *self)
-{
- gtk_label_select_region_index (self, 0, strlen (self->text));
-}
-
-static void
-gtk_label_activate_link_open (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkLabelLink *link = self->select_info->context_link;
-
- if (link)
- emit_activate_link (self, link);
-}
-
-static void
-gtk_label_activate_link_copy (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkLabelLink *link = self->select_info->context_link;
-
- if (link)
- {
- GdkClipboard *clipboard;
-
- clipboard = gtk_widget_get_clipboard (widget);
- gdk_clipboard_set_text (clipboard, link->uri);
- }
- else
- g_print ("no link ?!\n");
-}
-
-static void
-gtk_label_activate_clipboard_copy (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
- g_signal_emit_by_name (widget, "copy-clipboard");
-}
-
-static void
-gtk_label_activate_selection_select_all (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
- gtk_label_select_all (GTK_LABEL (widget));
-}
-
-static void
-gtk_label_nop (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
-}
-
-static void
-gtk_label_update_actions (GtkLabel *self)
-{
- GtkWidget *widget = GTK_WIDGET (self);
- gboolean has_selection;
- GtkLabelLink *link;
-
- if (self->select_info)
- {
- has_selection = self->select_info->selection_anchor != self->select_info->selection_end;
- link = self->select_info->active_link;
- }
- else
- {
- has_selection = FALSE;
- link = gtk_label_get_focus_link (self, NULL);
- }
-
- gtk_widget_action_set_enabled (widget, "clipboard.cut", FALSE);
- gtk_widget_action_set_enabled (widget, "clipboard.copy", has_selection);
- gtk_widget_action_set_enabled (widget, "clipboard.paste", FALSE);
- gtk_widget_action_set_enabled (widget, "selection.select-all",
- gtk_label_get_selectable (self));
- gtk_widget_action_set_enabled (widget, "selection.delete", FALSE);
- gtk_widget_action_set_enabled (widget, "link.open", !has_selection && link);
- gtk_widget_action_set_enabled (widget, "link.copy", !has_selection && link);
-}
-
static GMenuModel *
gtk_label_get_menu_model (GtkLabel *self)
{
@@ -5484,82 +5460,6 @@ gtk_label_do_popup (GtkLabel *self,
gtk_popover_popup (GTK_POPOVER (self->popup_menu));
}
-static void
-gtk_label_popup_menu (GtkWidget *widget,
- const char *action_name,
- GVariant *parameters)
-{
- GtkLabel *self = GTK_LABEL (widget);
-
- gtk_label_do_popup (self, -1, -1);
-}
-
-static void
-gtk_label_clear_links (GtkLabel *self)
-{
- guint i;
-
- if (!self->select_info)
- return;
-
- for (i = 0; i < self->select_info->n_links; i++)
- link_free (&self->select_info->links[i]);
- g_free (self->select_info->links);
- self->select_info->links = NULL;
- self->select_info->n_links = 0;
- self->select_info->active_link = NULL;
- gtk_widget_remove_css_class (GTK_WIDGET (self), "link");
-}
-
-static gboolean
-gtk_label_activate_link (GtkLabel *self,
- const char *uri)
-{
- GtkWidget *widget = GTK_WIDGET (self);
- GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
-
- if (!GTK_IS_WINDOW (toplevel))
- return FALSE;
-
- gtk_show_uri (GTK_WINDOW (toplevel), uri, GDK_CURRENT_TIME);
-
- return TRUE;
-}
-
-static void
-emit_activate_link (GtkLabel *self,
- GtkLabelLink *link)
-{
- gboolean handled;
-
- g_signal_emit (self, signals[ACTIVATE_LINK], 0, link->uri, &handled);
-
- /* signal handler might have invalidated the layout */
- if (!self->layout)
- return;
-
- if (handled && !link->visited &&
- self->select_info && self->select_info->links)
- {
- link->visited = TRUE;
- update_link_state (self);
- }
-}
-
-static void
-gtk_label_activate_current_link (GtkLabel *self)
-{
- GtkLabelLink *link;
- GtkWidget *widget = GTK_WIDGET (self);
-
- link = gtk_label_get_focus_link (self, NULL);
-
- if (link)
- emit_activate_link (self, link);
- else
- gtk_widget_activate_default (widget);
-}
-
/**
* gtk_label_get_current_uri:
* @self: a #GtkLabel
@@ -5596,52 +5496,6 @@ gtk_label_get_current_uri (GtkLabel *self)
return NULL;
}
-static gboolean
-gtk_label_query_tooltip (GtkWidget *widget,
- int x,
- int y,
- gboolean keyboard_tip,
- GtkTooltip *tooltip)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkLabelSelectionInfo *info = self->select_info;
- int index = -1;
-
- if (info && info->links)
- {
- if (keyboard_tip)
- {
- if (info->selection_anchor == info->selection_end)
- index = info->selection_anchor;
- }
- else
- {
- if (!get_layout_index (self, x, y, &index))
- index = -1;
- }
-
- if (index != -1)
- {
- const int link_index = _gtk_label_get_link_at (self, index);
-
- if (link_index != -1)
- {
- const GtkLabelLink *link = &info->links[link_index];
-
- if (link->title)
- {
- gtk_tooltip_set_markup (tooltip, link->title);
- }
- }
- }
- }
-
- return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
- x, y,
- keyboard_tip,
- tooltip);
-}
-
int
_gtk_label_get_cursor_position (GtkLabel *self)
{
@@ -5862,7 +5716,7 @@ gtk_label_set_yalign (GtkLabel *self,
{
g_return_if_fail (GTK_IS_LABEL (self));
- yalign = CLAMP (yalign, 0.0, 1.0);
+ yalign = CLAMP (yalign, 0.0, 1.0);
if (self->yalign == yalign)
return;