summaryrefslogtreecommitdiff
path: root/gtk/gtklabel.c
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/gtklabel.c')
-rw-r--r--gtk/gtklabel.c3878
1 files changed, 1824 insertions, 2054 deletions
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 19b0386c05..1c8a31f231 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,1627 @@ 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 int
+_gtk_label_get_link_at (GtkLabel *self,
+ int pos)
+{
+ if (self->select_info)
+ {
+ guint i;
+
+ for (i = 0; i < self->select_info->n_links; i++)
+ {
+ const GtkLabelLink *link = &self->select_info->links[i];
+
+ if (link->start <= pos && pos < link->end)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+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 +2115,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 +2136,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 +2423,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 +2442,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 +2484,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 +2604,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 +2635,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,38 +2661,34 @@ gtk_label_new_with_mnemonic (const char *str)
return GTK_WIDGET (self);
}
-static gboolean
-gtk_label_mnemonic_activate (GtkWidget *widget,
- gboolean group_cycling)
+static void
+_gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
+ gboolean visible)
{
- 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_IS_LABEL (widget))
{
- 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);
+ GtkLabel *self = GTK_LABEL (widget);
+
+ if (self->mnemonics_visible != visible)
+ {
+ self->mnemonics_visible = visible;
+ gtk_label_recalculate (self);
+ }
}
+ else
+ {
+ GtkWidget *child;
- /* barf if there was nothing to activate */
- g_warning ("Couldn't find a target for a mnemonic activation.");
- gtk_widget_error_bell (widget);
+ for (child = gtk_widget_get_first_child (widget);
+ child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ if (GTK_IS_NATIVE (child))
+ continue;
- return FALSE;
+ _gtk_label_mnemonics_visible_apply_recursively (child, visible);
+ }
+ }
}
static void
@@ -1521,8 +2747,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)
{
@@ -1537,60 +2763,8 @@ 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)
-{
- if (GTK_IS_LABEL (widget))
- {
- GtkLabel *self = GTK_LABEL (widget);
-
- if (self->mnemonics_visible != visible)
- {
- self->mnemonics_visible = visible;
- gtk_label_recalculate (self);
- }
- }
- else
- {
- GtkWidget *child;
-
- for (child = gtk_widget_get_first_child (widget);
- child;
- child = gtk_widget_get_next_sibling (child))
- {
- if (GTK_IS_NATIVE (child))
- continue;
-
- _gtk_label_mnemonics_visible_apply_recursively (child, visible);
- }
- }
-}
-static void
label_mnemonic_widget_weak_notify (gpointer data,
- GObject *where_the_object_was)
+ GObject *where_the_object_was)
{
GtkLabel *self = data;
@@ -1613,14 +2787,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 +3280,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 +3530,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 +3635,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 +3665,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 +3676,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 +3703,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 +3745,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 +3765,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 +3783,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 +3804,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 +3909,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 +3959,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 +3991,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);
+ int new_pos = g_utf8_pointer_to_offset (self->text, self->text + start);
- 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;
+ 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 +4051,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 +4061,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 +4293,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 +4311,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 +4365,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 +4711,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 +4859,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 +4874,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 +4902,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 +4915,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 +4925,7 @@ gtk_label_get_selection_bounds (GtkLabel *self,
start_offset = end_offset;
end_offset = tmp;
}
-
+
if (start)
*start = start_offset;
@@ -4759,7 +4940,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 +5170,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 +5188,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 +5210,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 +5226,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 +5251,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 +5401,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 +5481,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 +5517,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)
{
@@ -5704,111 +5579,6 @@ gtk_label_get_lines (GtkLabel *self)
return self->lines;
}
-int
-_gtk_label_get_n_links (GtkLabel *self)
-{
- if (self->select_info)
- return self->select_info->n_links;
-
- return 0;
-}
-
-const char *
-_gtk_label_get_link_uri (GtkLabel *self,
- int idx)
-{
- if (self->select_info)
- return self->select_info->links[idx].uri;
-
- return NULL;
-}
-
-void
-_gtk_label_get_link_extent (GtkLabel *self,
- int idx,
- int *start,
- int *end)
-{
- if (self->select_info)
- {
- const GtkLabelLink *link = &self->select_info->links[idx];
-
- *start = link->start;
- *end = link->end;
- }
- else
- {
- *start = -1;
- *end = -1;
- }
-}
-
-int
-_gtk_label_get_link_at (GtkLabel *self,
- int pos)
-{
- if (self->select_info)
- {
- guint i;
-
- for (i = 0; i < self->select_info->n_links; i++)
- {
- const GtkLabelLink *link = &self->select_info->links[i];
-
- if (link->start <= pos && pos < link->end)
- return i;
- }
- }
-
- return -1;
-}
-
-void
-_gtk_label_activate_link (GtkLabel *self,
- int idx)
-{
- if (self->select_info)
- {
- GtkLabelLink *link = &self->select_info->links[idx];
-
- emit_activate_link (self, link);
- }
-}
-
-gboolean
-_gtk_label_get_link_visited (GtkLabel *self,
- int idx)
-{
- if (self->select_info)
- return self->select_info->links[idx].visited;
-
- return FALSE;
-}
-
-gboolean
-_gtk_label_get_link_focused (GtkLabel *self,
- int idx)
-{
- GtkLabelSelectionInfo *info = self->select_info;
-
- if (!info)
- return FALSE;
-
- if (info->selection_anchor != info->selection_end)
- return FALSE;
-
- if (idx >= 0 && idx < info->n_links)
- {
- const GtkLabelLink *link = &info->links[idx];
-
- if (link->start <= info->selection_anchor &&
- info->selection_anchor <= link->end)
- return TRUE;
- }
-
- return FALSE;
-}
-
/**
* gtk_label_set_xalign:
* @self: a #GtkLabel
@@ -5862,7 +5632,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;