[gtk/wip/baedert/for-master: 39/57] label: Modernize source file
- From: Timm Bäder <baedert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/baedert/for-master: 39/57] label: Modernize source file
- Date: Thu, 31 Dec 2020 07:08:30 +0000 (UTC)
commit 23d8d672d6c36e016fe8ba85b82f4d21a9567340
Author: Timm Bäder <mail baedert org>
Date: Fri Dec 25 12:05:28 2020 +0100
label: Modernize source file
Try to sort toplevel functions to minimize unnecessary function
prototypes at the beginning of the file, get rid of all tabs and
trailing whitespace.
gtk/gtklabel.c | 6180 ++++++++++++++++++++++++-------------------------
gtk/gtklabelprivate.h | 4 +-
2 files changed, 3019 insertions(+), 3165 deletions(-)
---
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 19b0386c05..ef655b29e1 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -407,168 +407,31 @@ static guint signals[LAST_SIGNAL] = { 0 };
static GQuark quark_mnemonics_visible_connected;
-static void gtk_label_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gtk_label_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec);
-static void gtk_label_finalize (GObject *object);
-static void gtk_label_dispose (GObject *object);
-static void gtk_label_size_allocate (GtkWidget *widget,
- int width,
- int height,
- int baseline);
-static void gtk_label_state_flags_changed (GtkWidget *widget,
- GtkStateFlags prev_state);
-static void gtk_label_css_changed (GtkWidget *widget,
- GtkCssStyleChange *change);
-static void gtk_label_snapshot (GtkWidget *widget,
- GtkSnapshot *snapshot);
-static gboolean gtk_label_focus (GtkWidget *widget,
- GtkDirectionType direction);
-
-static void gtk_label_unrealize (GtkWidget *widget);
-
-static void gtk_label_motion (GtkEventControllerMotion *controller,
- double x,
- double y,
- gpointer data);
-static void gtk_label_leave (GtkEventControllerMotion *controller,
- gpointer data);
-
-static gboolean gtk_label_grab_focus (GtkWidget *widget);
-
-static gboolean gtk_label_query_tooltip (GtkWidget *widget,
- int x,
- int y,
- gboolean keyboard_tip,
- GtkTooltip *tooltip);
-
-static void gtk_label_set_text_internal (GtkLabel *self,
- char *str);
-static gboolean gtk_label_set_label_internal (GtkLabel *self,
- const char *str);
-static gboolean gtk_label_set_use_markup_internal (GtkLabel *self,
- gboolean val);
-static gboolean gtk_label_set_use_underline_internal (GtkLabel *self,
- gboolean val);
static void gtk_label_set_markup_internal (GtkLabel *self,
- const char *str,
- gboolean with_uline);
+ const char *str,
+ gboolean with_uline);
static void gtk_label_recalculate (GtkLabel *self);
-static void gtk_label_root (GtkWidget *widget);
-static void gtk_label_unroot (GtkWidget *widget);
-static void gtk_label_popup_menu (GtkWidget *widget,
- const char *action_name,
- GVariant *parameters);
static void gtk_label_do_popup (GtkLabel *self,
double x,
double y);
-
static void gtk_label_ensure_select_info (GtkLabel *self);
static void gtk_label_clear_select_info (GtkLabel *self);
-static void gtk_label_update_cursor (GtkLabel *self);
static void gtk_label_clear_layout (GtkLabel *self);
static void gtk_label_ensure_layout (GtkLabel *self);
static void gtk_label_select_region_index (GtkLabel *self,
int anchor_index,
int end_index);
-
static void gtk_label_update_active_link (GtkWidget *widget,
double x,
double y);
-
-static gboolean gtk_label_mnemonic_activate (GtkWidget *widget,
- gboolean group_cycling);
static void gtk_label_setup_mnemonic (GtkLabel *self);
static void gtk_label_buildable_interface_init (GtkBuildableIface *iface);
-static gboolean gtk_label_buildable_custom_tag_start (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *child,
- const char *tagname,
- GtkBuildableParser *parser,
- gpointer *data);
-
-static void gtk_label_buildable_custom_finished (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *child,
- const char *tagname,
- gpointer user_data);
-
/* For selectable labels: */
static void gtk_label_move_cursor (GtkLabel *self,
- GtkMovementStep step,
- int count,
- gboolean extend_selection);
-static void gtk_label_copy_clipboard (GtkLabel *self);
-static void gtk_label_select_all (GtkLabel *self);
-static int gtk_label_move_forward_word (GtkLabel *self,
- int start);
-static int gtk_label_move_backward_word (GtkLabel *self,
- int start);
-
-/* For links: */
-static void gtk_label_clear_links (GtkLabel *self);
-static gboolean gtk_label_activate_link (GtkLabel *self,
- const char *uri);
-static void gtk_label_activate_current_link (GtkLabel *self);
-static void emit_activate_link (GtkLabel *self,
- GtkLabelLink *link);
-
-/* Event controller callbacks */
-static void gtk_label_click_gesture_pressed (GtkGestureClick *gesture,
- int n_press,
- double x,
- double y,
- GtkLabel *self);
-static void gtk_label_click_gesture_released (GtkGestureClick *gesture,
- int n_press,
- double x,
- double y,
- GtkLabel *self);
-static void gtk_label_drag_gesture_begin (GtkGestureDrag *gesture,
- double start_x,
- double start_y,
- GtkLabel *self);
-static void gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
- double offset_x,
- double offset_y,
- GtkLabel *self);
-
-/* Actions */
-
-static void gtk_label_activate_clipboard_copy (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-static void gtk_label_activate_selection_select_all (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-static void gtk_label_activate_link_open (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-static void gtk_label_activate_link_copy (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-static void gtk_label_nop (GtkWidget *self,
- const char *name,
- GVariant *parameter);
-
-static void gtk_label_update_actions (GtkLabel *self);
-
-static GtkSizeRequestMode gtk_label_get_request_mode (GtkWidget *widget);
-static void gtk_label_measure (GtkWidget *widget,
- GtkOrientation orientation,
- int for_size,
- int *minimum,
- int *natural,
- int *minimum_baseline,
- int *natural_baseline);
-
-
+ GtkMovementStep step,
+ int count,
+ gboolean extend_selection);
static GtkBuildableIface *buildable_parent_iface = NULL;
@@ -578,2987 +441,3168 @@ 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_class_init (GtkLabelClass *class)
+gtk_label_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- GObjectClass *gobject_class = G_OBJECT_CLASS (class);
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GtkLabel *self = GTK_LABEL (object);
- gobject_class->set_property = gtk_label_set_property;
- gobject_class->get_property = gtk_label_get_property;
- gobject_class->finalize = gtk_label_finalize;
- gobject_class->dispose = gtk_label_dispose;
+ 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;
+ }
+}
- widget_class->size_allocate = gtk_label_size_allocate;
- widget_class->state_flags_changed = gtk_label_state_flags_changed;
- widget_class->css_changed = gtk_label_css_changed;
- widget_class->query_tooltip = gtk_label_query_tooltip;
- widget_class->snapshot = gtk_label_snapshot;
- widget_class->unrealize = gtk_label_unrealize;
- widget_class->root = gtk_label_root;
- widget_class->unroot = gtk_label_unroot;
- widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
- widget_class->grab_focus = gtk_label_grab_focus;
- widget_class->focus = gtk_label_focus;
- widget_class->get_request_mode = gtk_label_get_request_mode;
- widget_class->measure = gtk_label_measure;
+static void
+gtk_label_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkLabel *self = GTK_LABEL (object);
- class->move_cursor = gtk_label_move_cursor;
- class->copy_clipboard = gtk_label_copy_clipboard;
- class->activate_link = gtk_label_activate_link;
+ 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;
+ }
+}
- /**
- * GtkLabel::move-cursor:
- * @entry: the object which received the signal
- * @step: the granularity of the move, as a #GtkMovementStep
- * @count: the number of @step units to move
- * @extend_selection: %TRUE if the move should extend the selection
- *
- * The ::move-cursor signal is a
- * [keybinding signal][GtkSignalAction]
- * which gets emitted when the user initiates a cursor movement.
- * If the cursor is not visible in @entry, this signal causes
- * the viewport to be moved instead.
- *
- * Applications should not connect to it, but may emit it with
- * g_signal_emit_by_name() if they need to control the cursor
- * programmatically.
- *
- * The default bindings for this signal come in two variants,
- * the variant with the Shift modifier extends the selection,
- * the variant without the Shift modifier does not.
- * There are too many key combinations to list them all here.
- * - Arrow keys move by individual characters/lines
- * - Ctrl-arrow key combinations move by words/paragraphs
- * - Home/End keys move to the ends of the buffer
- */
- 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);
+static void
+gtk_label_init (GtkLabel *self)
+{
+ self->width_chars = -1;
+ self->max_width_chars = -1;
+ self->label = g_strdup ("");
+ self->lines = -1;
- /**
- * GtkLabel::copy-clipboard:
- * @self: the object which received the signal
- *
- * The ::copy-clipboard signal is a
- * [keybinding signal][GtkSignalAction]
- * 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);
-
- /**
- * GtkLabel::activate-current-link:
- * @self: The label on which the signal was emitted
- *
- * A [keybinding signal][GtkSignalAction]
- * which gets emitted when the user activates a link in the label.
- *
- * Applications may also emit the signal with g_signal_emit_by_name()
- * if they need to control activation of URIs programmatically.
- *
- * The default bindings for this signal are all forms of the Enter key.
- */
- signals[ACTIVATE_CURRENT_LINK] =
- g_signal_new_class_handler (I_("activate-current-link"),
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (gtk_label_activate_current_link),
- NULL, NULL,
- NULL,
- G_TYPE_NONE, 0);
+ self->xalign = 0.5;
+ self->yalign = 0.5;
- /**
- * GtkLabel::activate-link:
- * @self: The label on which the signal was emitted
- * @uri: the URI that is activated
- *
- * The signal which gets emitted to activate a URI.
- * Applications may connect to it to override the default behaviour,
- * which is to call gtk_show_uri().
- *
- * Returns: %TRUE if the link has been activated
- */
- signals[ACTIVATE_LINK] =
- g_signal_new (I_("activate-link"),
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkLabelClass, activate_link),
- _gtk_boolean_handled_accumulator, NULL,
- _gtk_marshal_BOOLEAN__STRING,
- G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
+ self->jtype = GTK_JUSTIFY_LEFT;
+ self->wrap = FALSE;
+ self->wrap_mode = PANGO_WRAP_WORD;
+ self->ellipsize = PANGO_ELLIPSIZE_NONE;
- /**
- * GtkLabel:label:
- *
- * The contents of the label.
- *
- * If the string contains [Pango XML markup][PangoMarkupFormat], you will
- * have to set the #GtkLabel:use-markup property to %TRUE in order for the
- * label to display the markup attributes. See also gtk_label_set_markup()
- * for a convenience function that sets both this property and the
- * #GtkLabel:use-markup property at the same time.
- *
- * If the string contains underlines acting as mnemonics, you will have to
- * set the #GtkLabel:use-underline property to %TRUE in order for the label
- * to display them.
- */
- label_props[PROP_LABEL] =
- g_param_spec_string ("label",
- P_("Label"),
- P_("The text of the label"),
- "",
- GTK_PARAM_READWRITE);
+ self->use_underline = FALSE;
+ self->use_markup = FALSE;
- label_props[PROP_ATTRIBUTES] =
- g_param_spec_boxed ("attributes",
- P_("Attributes"),
- P_("A list of style attributes to apply to the text of the label"),
- PANGO_TYPE_ATTR_LIST,
- GTK_PARAM_READWRITE);
+ self->mnemonic_keyval = GDK_KEY_VoidSymbol;
+ self->layout = NULL;
+ self->text = g_strdup ("");
+ self->attrs = NULL;
- label_props[PROP_USE_MARKUP] =
- g_param_spec_boolean ("use-markup",
- P_("Use markup"),
- P_("The text of the label includes XML markup. See pango_parse_markup()"),
- FALSE,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ self->mnemonic_widget = NULL;
- label_props[PROP_USE_UNDERLINE] =
- g_param_spec_boolean ("use-underline",
- P_("Use underline"),
- P_("If set, an underline in the text indicates the next character should be used
for the mnemonic accelerator key"),
- FALSE,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ self->mnemonics_visible = FALSE;
+}
- label_props[PROP_JUSTIFY] =
- g_param_spec_enum ("justify",
- P_("Justification"),
- P_("The alignment of the lines in the text of the label relative to each other.
This does NOT affect the alignment of the label within its allocation. See GtkLabel:xalign for that"),
- GTK_TYPE_JUSTIFICATION,
- GTK_JUSTIFY_LEFT,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+static const GtkBuildableParser pango_parser =
+{
+ gtk_pango_attribute_start_element,
+};
- /**
- * GtkLabel:xalign:
- *
- * The xalign property determines the horizontal alignment of the label text
- * inside the labels size allocation. Compare this to #GtkWidget:halign,
- * which determines how the labels size allocation is positioned in the
- * space available for the label.
- */
- label_props[PROP_XALIGN] =
- g_param_spec_float ("xalign",
- P_("X align"),
- P_("The horizontal alignment, from 0 (left) to 1 (right). Reversed for RTL
layouts."),
- 0.0, 1.0,
- 0.5,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+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;
- /**
- * GtkLabel:yalign:
- *
- * The yalign property determines the vertical alignment of the label text
- * inside the labels size allocation. Compare this to #GtkWidget:valign,
- * which determines how the labels size allocation is positioned in the
- * space available for the label.
- */
- label_props[PROP_YALIGN] =
- g_param_spec_float ("yalign",
- P_("Y align"),
- P_("The vertical alignment, from 0 (top) to 1 (bottom)"),
- 0.0, 1.0,
- 0.5,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ if (strcmp (tagname, "attributes") == 0)
+ {
+ GtkPangoAttributeParserData *parser_data;
- label_props[PROP_WRAP] =
- g_param_spec_boolean ("wrap",
- P_("Line wrap"),
- P_("If set, wrap lines if the text becomes too wide"),
- FALSE,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ 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;
+}
- /**
- * GtkLabel:wrap-mode:
- *
- * If line wrapping is on (see the #GtkLabel:wrap property) this controls
- * how the line wrapping is done. The default is %PANGO_WRAP_WORD, which
- * means wrap on word boundaries.
- */
- label_props[PROP_WRAP_MODE] =
- g_param_spec_enum ("wrap-mode",
- P_("Line wrap mode"),
- P_("If wrap is set, controls how linewrapping is done"),
- PANGO_TYPE_WRAP_MODE,
- PANGO_WRAP_WORD,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+static void
+gtk_label_buildable_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const char *tagname,
+ gpointer user_data)
+{
+ GtkPangoAttributeParserData *data = user_data;
- label_props[PROP_SELECTABLE] =
- g_param_spec_boolean ("selectable",
- P_("Selectable"),
- P_("Whether the label text can be selected with the mouse"),
- FALSE,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ buildable_parent_iface->custom_finished (buildable, builder, child,
+ tagname, user_data);
- label_props[PROP_MNEMONIC_KEYVAL] =
- g_param_spec_uint ("mnemonic-keyval",
- P_("Mnemonic key"),
- P_("The mnemonic accelerator key for this label"),
- 0, G_MAXUINT,
- GDK_KEY_VoidSymbol,
- GTK_PARAM_READABLE);
+ if (strcmp (tagname, "attributes") == 0)
+ {
+ if (data->attrs)
+ {
+ gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
+ pango_attr_list_unref (data->attrs);
+ }
- label_props[PROP_MNEMONIC_WIDGET] =
- g_param_spec_object ("mnemonic-widget",
- P_("Mnemonic widget"),
- P_("The widget to be activated when the label’s mnemonic key is pressed"),
- GTK_TYPE_WIDGET,
- GTK_PARAM_READWRITE);
+ g_object_unref (data->object);
+ g_object_unref (data->builder);
+ g_slice_free (GtkPangoAttributeParserData, data);
+ }
+}
- /**
- * GtkLabel:ellipsize:
- *
- * The preferred place to ellipsize the string, if the label does
- * not have enough room to display the entire string, specified as a
- * #PangoEllipsizeMode.
- *
- * Note that setting this property to a value other than
- * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests
- * only enough space to display the ellipsis "...". In particular, this
- * means that ellipsizing labels do not work well in notebook tabs, unless
- * the #GtkNotebook tab-expand child property is set to %TRUE. Other ways
- * to set a label's width are gtk_widget_set_size_request() and
- * gtk_label_set_width_chars().
- */
- label_props[PROP_ELLIPSIZE] =
- g_param_spec_enum ("ellipsize",
- P_("Ellipsize"),
- P_("The preferred place to ellipsize the string, if the label does not have enough
room to display the entire string"),
- PANGO_TYPE_ELLIPSIZE_MODE,
- PANGO_ELLIPSIZE_NONE,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+static void
+gtk_label_buildable_interface_init (GtkBuildableIface *iface)
+{
+ buildable_parent_iface = g_type_interface_peek_parent (iface);
- /**
- * GtkLabel:width-chars:
- *
- * The desired width of the label, in characters. If this property is set to
- * -1, the width will be calculated automatically.
- *
- * See the section on [text layout][label-text-layout]
- * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
- * determine the width of ellipsized and wrapped labels.
- **/
- label_props[PROP_WIDTH_CHARS] =
- g_param_spec_int ("width-chars",
- P_("Width In Characters"),
- P_("The desired width of the label, in characters"),
- -1, G_MAXINT,
- -1,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
+ iface->custom_finished = gtk_label_buildable_custom_finished;
+}
- /**
- * GtkLabel:single-line-mode:
- *
- * Whether the label is in single line mode. In single line mode,
- * the height of the label does not depend on the actual text, it
- * is always set to ascent + descent of the font. This can be an
- * advantage in situations where resizing the label because of text
- * changes would be distracting, e.g. in a statusbar.
- **/
- label_props[PROP_SINGLE_LINE_MODE] =
- g_param_spec_boolean ("single-line-mode",
- P_("Single Line Mode"),
- P_("Whether the label is in single line mode"),
- FALSE,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+static void
+update_link_state (GtkLabel *self)
+{
+ GtkStateFlags state;
+ guint i;
- /**
- * GtkLabel:max-width-chars:
- *
- * The desired maximum width of the label, in characters. If this property
- * is set to -1, the width will be calculated automatically.
- *
- * See the section on [text layout][label-text-layout]
- * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
- * determine the width of ellipsized and wrapped labels.
- **/
- label_props[PROP_MAX_WIDTH_CHARS] =
- g_param_spec_int ("max-width-chars",
- P_("Maximum Width In Characters"),
- P_("The desired maximum width of the label, in characters"),
- -1, G_MAXINT,
- -1,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ if (!self->select_info)
+ return;
- /**
- * GtkLabel:lines:
- *
- * The number of lines to which an ellipsized, wrapping label
- * should be limited. This property has no effect if the
- * label is not wrapping or ellipsized. Set this property to
- * -1 if you don't want to limit the number of lines.
- */
- label_props[PROP_LINES] =
- g_param_spec_int ("lines",
- P_("Number of lines"),
- P_("The desired number of lines, when ellipsizing a wrapping label"),
- -1, G_MAXINT,
- -1,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ for (i = 0; i < self->select_info->n_links; i++)
+ {
+ const GtkLabelLink *link = &self->select_info->links[i];
- /**
- * GtkLabel:extra-menu:
- *
- * A menu model whose contents will be appended to
- * the context menu.
- */
- label_props[PROP_EXTRA_MENU] =
- g_param_spec_object ("extra-menu",
- P_("Extra menu"),
- P_("Menu model to append to the context menu"),
- G_TYPE_MENU_MODEL,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ 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);
+ }
+}
- g_object_class_install_properties (gobject_class, NUM_PROPERTIES, label_props);
+static void
+gtk_label_update_cursor (GtkLabel *self)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
- /**
- * GtkLabel|menu.popup:
- *
- * Opens the context menu.
- */
- gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_label_popup_menu);
+ if (!self->select_info)
+ return;
- /*
- * Key bindings
- */
+ 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);
+}
- gtk_widget_class_add_binding_action (widget_class,
- GDK_KEY_F10, GDK_SHIFT_MASK,
- "menu.popup",
- NULL);
- gtk_widget_class_add_binding_action (widget_class,
- GDK_KEY_Menu, 0,
- "menu.popup",
- NULL);
+static void
+gtk_label_state_flags_changed (GtkWidget *widget,
+ GtkStateFlags prev_state)
+{
+ GtkLabel *self = GTK_LABEL (widget);
- /* Moving the insertion point */
- add_move_binding (widget_class, GDK_KEY_Right, 0,
- GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+ if (self->select_info)
+ {
+ if (!gtk_widget_is_sensitive (widget))
+ gtk_label_select_region (self, 0, 0);
- add_move_binding (widget_class, GDK_KEY_Left, 0,
- GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+ gtk_label_update_cursor (self);
+ update_link_state (self);
+ }
- add_move_binding (widget_class, GDK_KEY_KP_Right, 0,
- GTK_MOVEMENT_VISUAL_POSITIONS, 1);
-
- add_move_binding (widget_class, GDK_KEY_KP_Left, 0,
- GTK_MOVEMENT_VISUAL_POSITIONS, -1);
-
- add_move_binding (widget_class, GDK_KEY_f, GDK_CONTROL_MASK,
- GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
-
- add_move_binding (widget_class, GDK_KEY_b, GDK_CONTROL_MASK,
- GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
-
- add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK,
- GTK_MOVEMENT_WORDS, 1);
+ if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
+}
- add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK,
- GTK_MOVEMENT_WORDS, -1);
+static void
+gtk_label_update_layout_attributes (GtkLabel *self,
+ PangoAttrList *style_attrs)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+ GtkCssStyle *style;
+ PangoAttrList *attrs;
- add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
- GTK_MOVEMENT_WORDS, 1);
+ if (self->layout == NULL)
+ {
+ pango_attr_list_unref (style_attrs);
+ return;
+ }
- add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
- GTK_MOVEMENT_WORDS, -1);
+ if (self->select_info && self->select_info->links)
+ {
+ guint i;
- /* select all */
- gtk_widget_class_add_binding (widget_class,
- GDK_KEY_a, GDK_CONTROL_MASK,
- (GtkShortcutFunc) gtk_label_select_all,
- NULL);
- gtk_widget_class_add_binding (widget_class,
- GDK_KEY_slash, GDK_CONTROL_MASK,
- (GtkShortcutFunc) gtk_label_select_all,
- NULL);
+ attrs = pango_attr_list_new ();
- /* unselect all */
- gtk_widget_class_add_binding_signal (widget_class,
- GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
- "move-cursor",
- "(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE);
+ 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;
- gtk_widget_class_add_binding_signal (widget_class,
- GDK_KEY_backslash, GDK_CONTROL_MASK,
- "move-cursor",
- "(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE);
+ 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;
- add_move_binding (widget_class, GDK_KEY_f, GDK_ALT_MASK,
- GTK_MOVEMENT_WORDS, 1);
+ attr->start_index = link->start;
+ attr->end_index = link->end;
+ pango_attr_list_insert (attrs, attr);
+ }
+ g_slist_free (attributes);
+ }
- add_move_binding (widget_class, GDK_KEY_b, GDK_ALT_MASK,
- GTK_MOVEMENT_WORDS, -1);
+ 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);
- add_move_binding (widget_class, GDK_KEY_Home, 0,
- GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+ pango_attr_list_unref (link_attrs);
+ }
+ }
+ else
+ attrs = NULL;
- add_move_binding (widget_class, GDK_KEY_End, 0,
- GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+ style = gtk_css_node_get_style (gtk_widget_get_css_node (widget));
+ if (!style_attrs)
+ style_attrs = gtk_css_style_get_pango_attributes (style);
- add_move_binding (widget_class, GDK_KEY_KP_Home, 0,
- GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+ if (style_attrs)
+ {
+ attrs = _gtk_pango_attr_list_merge (attrs, style_attrs);
+ pango_attr_list_unref (style_attrs);
+ }
- add_move_binding (widget_class, GDK_KEY_KP_End, 0,
- GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
-
- add_move_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK,
- GTK_MOVEMENT_BUFFER_ENDS, -1);
+ attrs = _gtk_pango_attr_list_merge (attrs, self->markup_attrs);
+ attrs = _gtk_pango_attr_list_merge (attrs, self->attrs);
- add_move_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK,
- GTK_MOVEMENT_BUFFER_ENDS, 1);
+ pango_layout_set_attributes (self->layout, attrs);
- add_move_binding (widget_class, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
- GTK_MOVEMENT_BUFFER_ENDS, -1);
+ pango_attr_list_unref (attrs);
+}
- add_move_binding (widget_class, GDK_KEY_KP_End, GDK_CONTROL_MASK,
- GTK_MOVEMENT_BUFFER_ENDS, 1);
+static void
+gtk_label_css_changed (GtkWidget *widget,
+ GtkCssStyleChange *change)
+{
+ GtkLabel *self = GTK_LABEL (widget);
+ gboolean attrs_affected;
+ PangoAttrList *new_attrs = NULL;
- /* copy */
- gtk_widget_class_add_binding_signal (widget_class,
- GDK_KEY_c, GDK_CONTROL_MASK,
- "copy-clipboard",
- NULL);
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->css_changed (widget, change);
- gtk_widget_class_add_binding_signal (widget_class,
- GDK_KEY_Return, 0,
- "activate-current-link",
- NULL);
- gtk_widget_class_add_binding_signal (widget_class,
- GDK_KEY_ISO_Enter, 0,
- "activate-current-link",
- NULL);
- gtk_widget_class_add_binding_signal (widget_class,
- GDK_KEY_KP_Enter, 0,
- "activate-current-link",
- NULL);
+ 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;
- gtk_widget_class_set_css_name (widget_class, I_("label"));
- gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_LABEL);
+ if (change == NULL || attrs_affected || (self->select_info && self->select_info->links))
+ {
+ gtk_label_update_layout_attributes (self, new_attrs);
- quark_mnemonics_visible_connected = g_quark_from_static_string ("gtk-label-mnemonics-visible-connected");
+ if (attrs_affected)
+ gtk_widget_queue_draw (widget);
+ }
+}
- /**
- * GtkLabel|clipboard.cut:
- *
- * Doesn't do anything, since text in labels can't be deleted.
- */
- gtk_widget_class_install_action (widget_class, "clipboard.cut", NULL,
- gtk_label_nop);
+static PangoDirection
+get_cursor_direction (GtkLabel *self)
+{
+ GSList *l;
- /**
- * GtkLabel|clipboard.copy:
- *
- * Copies the text to the clipboard.
- */
- gtk_widget_class_install_action (widget_class, "clipboard.copy", NULL,
- gtk_label_activate_clipboard_copy);
+ g_assert (self->select_info);
- /**
- * GtkLabel|clipboard.paste:
- *
- * Doesn't do anything, since text in labels can't be edited.
- */
- gtk_widget_class_install_action (widget_class, "clipboard.paste", NULL,
- gtk_label_nop);
+ gtk_label_ensure_layout (self);
- /**
- * GtkLabel|selection.delete:
- *
- * Doesn't do anything, since text in labels can't be deleted.
- */
- gtk_widget_class_install_action (widget_class, "selection.delete", NULL,
- gtk_label_nop);
+ for (l = pango_layout_get_lines_readonly (self->layout); l; l = l->next)
+ {
+ PangoLayoutLine *line = l->data;
- /**
- * GtkLabel|selection.select-all:
- *
- * Selects all of the text, if the label allows selection.
- */
- gtk_widget_class_install_action (widget_class, "selection.select-all", NULL,
- gtk_label_activate_selection_select_all);
-
- /**
- * GtkLabel|link.open:
- *
- * Opens the link, when activated on a link inside the label.
- */
- gtk_widget_class_install_action (widget_class, "link.open", NULL,
- gtk_label_activate_link_open);
+ /* 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;
+ }
- /**
- * GtkLabel|link.copy:
- *
- * Copies the link to the clipboard, when activated on a link
- * inside the label.
- */
- gtk_widget_class_install_action (widget_class, "link.copy", NULL,
- gtk_label_activate_link_copy);
+ return PANGO_DIRECTION_LTR;
}
-static void
-gtk_label_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+static GtkLabelLink *
+gtk_label_get_focus_link (GtkLabel *self,
+ int *out_index)
{
- GtkLabel *self = GTK_LABEL (object);
+ GtkLabelSelectionInfo *info = self->select_info;
+ int link_index;
- switch (prop_id)
+ 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)
{
- 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;
+ if (out_index)
+ *out_index = link_index;
+
+ return &info->links[link_index];
}
+
+nope:
+ if (out_index)
+ *out_index = -1;
+ return NULL;
}
-static void
-gtk_label_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+/**
+ * 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)
{
- GtkLabel *self = GTK_LABEL (object);
+ PangoLayout *copy;
- switch (prop_id)
+ if (existing_layout != NULL)
{
- 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;
+ if (existing_layout != self->layout)
+ {
+ pango_layout_set_width (existing_layout, width);
+ return existing_layout;
+ }
+
+ g_object_unref (existing_layout);
}
-}
-static void
-gtk_label_init (GtkLabel *self)
-{
- self->width_chars = -1;
- self->max_width_chars = -1;
- self->label = g_strdup ("");
- self->lines = -1;
+ gtk_label_ensure_layout (self);
- self->xalign = 0.5;
- self->yalign = 0.5;
+ if (pango_layout_get_width (self->layout) == width)
+ {
+ g_object_ref (self->layout);
+ return self->layout;
+ }
- self->jtype = GTK_JUSTIFY_LEFT;
- self->wrap = FALSE;
- self->wrap_mode = PANGO_WRAP_WORD;
- self->ellipsize = PANGO_ELLIPSIZE_NONE;
+ /* 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;
+ }
- self->use_underline = FALSE;
- self->use_markup = FALSE;
+ /* 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;
- self->mnemonic_keyval = GDK_KEY_VoidSymbol;
- self->layout = NULL;
- self->text = g_strdup ("");
- self->attrs = NULL;
+ if (width == -1)
+ return g_object_ref (self->layout);
- self->mnemonic_widget = NULL;
+ pango_layout_get_extents (self->layout, NULL, &rect);
+ if (rect.width <= width)
+ return g_object_ref (self->layout);
+ }
- self->mnemonics_visible = FALSE;
+ copy = pango_layout_copy (self->layout);
+ pango_layout_set_width (copy, width);
+ return copy;
}
-
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)
+get_height_for_width (GtkLabel *self,
+ int width,
+ int *minimum_height,
+ int *natural_height,
+ int *minimum_baseline,
+ int *natural_baseline)
{
- if (buildable_parent_iface->custom_tag_start (buildable, builder, child,
- tagname, parser, data))
- return TRUE;
-
- if (strcmp (tagname, "attributes") == 0)
- {
- GtkPangoAttributeParserData *parser_data;
+ PangoLayout *layout;
+ int text_height, baseline;
- 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;
-}
+ layout = gtk_label_get_measuring_layout (self, NULL, width * PANGO_SCALE);
-static void
-gtk_label_buildable_custom_finished (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *child,
- const char *tagname,
- gpointer user_data)
-{
- GtkPangoAttributeParserData *data = user_data;
+ pango_layout_get_pixel_size (layout, NULL, &text_height);
- buildable_parent_iface->custom_finished (buildable, builder, child,
- tagname, user_data);
+ *minimum_height = text_height;
+ *natural_height = text_height;
- if (strcmp (tagname, "attributes") == 0)
- {
- if (data->attrs)
- {
- gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
- pango_attr_list_unref (data->attrs);
- }
+ baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+ *minimum_baseline = baseline;
+ *natural_baseline = baseline;
- g_object_unref (data->object);
- g_object_unref (data->builder);
- g_slice_free (GtkPangoAttributeParserData, data);
- }
+ g_object_unref (layout);
}
-
-/**
- * gtk_label_new:
- * @str: (nullable): The text of the label
- *
- * Creates a new label with the given text inside it. You can
- * pass %NULL to get an empty label widget.
- *
- * Returns: the new #GtkLabel
- **/
-GtkWidget*
-gtk_label_new (const char *str)
+static int
+get_char_pixels (GtkWidget *self,
+ PangoLayout *layout)
{
- GtkLabel *self;
-
- self = g_object_new (GTK_TYPE_LABEL, NULL);
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ int char_width, digit_width;
- if (str && *str)
- gtk_label_set_text (self, str);
+ 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 GTK_WIDGET (self);
+ return MAX (char_width, digit_width);;
}
-/**
- * gtk_label_new_with_mnemonic:
- * @str: (nullable): The text of the label, with an underscore in front of the
- * mnemonic character
- *
- * Creates a new #GtkLabel, containing the text in @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
- * 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
- * and be activated by the mnemonic.
- *
- * Returns: the new #GtkLabel
- **/
-GtkWidget*
-gtk_label_new_with_mnemonic (const char *str)
+static void
+gtk_label_get_preferred_layout_size (GtkLabel *self,
+ PangoRectangle *smallest,
+ PangoRectangle *widest,
+ int *smallest_baseline,
+ int *widest_baseline)
{
- GtkLabel *self;
-
- self = g_object_new (GTK_TYPE_LABEL, NULL);
-
- if (str && *str)
- gtk_label_set_text_with_mnemonic (self, str);
+ PangoLayout *layout;
+ int char_pixels;
- return GTK_WIDGET (self);
-}
+ /* "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.
+ */
-static gboolean
-gtk_label_mnemonic_activate (GtkWidget *widget,
- gboolean group_cycling)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkWidget *parent;
+ /* Start off with the pixel extents of an as-wide-as-possible layout */
+ layout = gtk_label_get_measuring_layout (self, NULL, -1);
- if (self->mnemonic_widget)
- return gtk_widget_mnemonic_activate (self->mnemonic_widget, group_cycling);
+ if (self->width_chars > -1 || self->max_width_chars > -1)
+ char_pixels = get_char_pixels (GTK_WIDGET (self), layout);
+ else
+ char_pixels = 0;
- /* Try to find the widget to activate by traversing the
- * widget's ancestry.
- */
- parent = gtk_widget_get_parent (widget);
+ 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 (GTK_IS_NOTEBOOK (parent))
- return FALSE;
-
- while (parent)
+ if (self->ellipsize || self->wrap)
{
- 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);
+ /* 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);
- return FALSE;
-}
+ pango_layout_get_extents (layout, NULL, smallest);
+ smallest->width = MAX (smallest->width, char_pixels * self->width_chars);
+ smallest->x = smallest->y = 0;
-static void
-label_mnemonics_visible_changed (GtkWidget *widget,
- GParamSpec *pspec,
- gpointer data)
-{
- gboolean visible;
+ *smallest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
- g_object_get (widget, "mnemonics-visible", &visible, NULL);
- _gtk_label_mnemonics_visible_apply_recursively (widget, visible);
-}
+ 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;
-static void
-gtk_label_setup_mnemonic (GtkLabel *self)
-{
- GtkWidget *widget = GTK_WIDGET (self);
- GtkShortcut *shortcut;
- GtkNative *native;
- gboolean connected;
- gboolean mnemonics_visible;
+ *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+ }
- if (self->mnemonic_keyval == GDK_KEY_VoidSymbol)
- {
- if (self->mnemonic_controller)
+ if (widest->width < smallest->width)
{
- gtk_widget_remove_controller (widget, self->mnemonic_controller);
- self->mnemonic_controller = NULL;
+ *smallest = *widest;
+ *smallest_baseline = *widest_baseline;
}
- return;
- }
-
- if (self->mnemonic_controller == NULL)
- {
- self->mnemonic_controller = gtk_shortcut_controller_new ();
- gtk_event_controller_set_propagation_phase (self->mnemonic_controller, GTK_PHASE_CAPTURE);
- gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (self->mnemonic_controller),
GTK_SHORTCUT_SCOPE_MANAGED);
- shortcut = gtk_shortcut_new (gtk_mnemonic_trigger_new (self->mnemonic_keyval),
- g_object_ref (gtk_mnemonic_action_get ()));
- gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (self->mnemonic_controller), shortcut);
- gtk_widget_add_controller (GTK_WIDGET (self), self->mnemonic_controller);
}
else
{
- shortcut = g_list_model_get_item (G_LIST_MODEL (self->mnemonic_controller), 0);
- gtk_shortcut_set_trigger (shortcut, gtk_mnemonic_trigger_new (self->mnemonic_keyval));
- g_object_unref (shortcut);
+ *smallest = *widest;
+ *smallest_baseline = *widest_baseline;
}
- /* Connect to notify::mnemonics-visible of the root */
- native = gtk_widget_get_native (GTK_WIDGET (self));
- if (!GTK_IS_WINDOW (native) && !GTK_IS_POPOVER (native))
- return;
-
- /* always set up this widgets initial value */
- 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));
-
- if (!connected)
- {
- g_signal_connect (native,
- "notify::mnemonics-visible",
- G_CALLBACK (label_mnemonics_visible_changed),
- self);
- g_object_set_qdata (G_OBJECT (native),
- quark_mnemonics_visible_connected,
- GINT_TO_POINTER (1));
- }
+ g_object_unref (layout);
}
static void
-gtk_label_root (GtkWidget *widget)
-{
- GtkLabel *self = GTK_LABEL (widget);
-
- GTK_WIDGET_CLASS (gtk_label_parent_class)->root (widget);
+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_setup_mnemonic (self);
+ gtk_label_get_preferred_layout_size (self,
+ &smallest_rect, &widest_rect,
+ &smallest_baseline, &widest_baseline);
- /* The PangoContext is replaced when the display changes, so clear the layouts */
- gtk_label_clear_layout (GTK_LABEL (widget));
-}
+ widest_rect.width = PANGO_PIXELS_CEIL (widest_rect.width);
+ widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
-static void
-gtk_label_unroot (GtkWidget *widget)
-{
- GtkLabel *self = GTK_LABEL (widget);
+ smallest_rect.width = PANGO_PIXELS_CEIL (smallest_rect.width);
+ smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
- gtk_label_setup_mnemonic (self);
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ /* Normal desired width */
+ *minimum_size = smallest_rect.width;
+ *natural_size = widest_rect.width;
- GTK_WIDGET_CLASS (gtk_label_parent_class)->unroot (widget);
-}
+ if (minimum_baseline)
+ *minimum_baseline = -1;
-void
-_gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
- gboolean visible)
-{
- if (GTK_IS_LABEL (widget))
+ if (natural_baseline)
+ *natural_baseline = -1;
+ }
+ else /* GTK_ORIENTATION_VERTICAL */
{
- GtkLabel *self = GTK_LABEL (widget);
-
- if (self->mnemonics_visible != visible)
+ if (smallest_rect.height < widest_rect.height)
{
- self->mnemonics_visible = visible;
- gtk_label_recalculate (self);
+ *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
- {
- GtkWidget *child;
-
- for (child = gtk_widget_get_first_child (widget);
- child;
- child = gtk_widget_get_next_sibling (child))
+ else
{
- if (GTK_IS_NATIVE (child))
- continue;
-
- _gtk_label_mnemonics_visible_apply_recursively (child, visible);
+ *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
-label_mnemonic_widget_weak_notify (gpointer data,
- GObject *where_the_object_was)
+gtk_label_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
{
- GtkLabel *self = data;
+ GtkLabel *self = GTK_LABEL (widget);
- self->mnemonic_widget = NULL;
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_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);
}
-/**
- * gtk_label_set_mnemonic_widget:
- * @self: a #GtkLabel
- * @widget: (nullable): the target #GtkWidget, or %NULL to unset
- *
- * If the label has been set so that it has a mnemonic key (using
- * i.e. gtk_label_set_markup_with_mnemonic(),
- * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
- * or the “use_underline” property) the label can be associated with a
- * widget that is the target of the mnemonic. When the label is inside
- * a widget (like a #GtkButton or a #GtkNotebook tab) it is
- * automatically associated with the correct widget, but sometimes
- * (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
- * and toggle focus between the colliding widgets otherwise.
- **/
-void
-gtk_label_set_mnemonic_widget (GtkLabel *self,
- GtkWidget *widget)
+static void
+get_layout_location (GtkLabel *self,
+ int *xp,
+ int *yp)
{
- g_return_if_fail (GTK_IS_LABEL (self));
+ 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;
- if (widget)
- g_return_if_fail (GTK_IS_WIDGET (widget));
+ xalign = self->xalign;
+ yalign = self->yalign;
- if (self->mnemonic_widget)
- {
- gtk_widget_remove_mnemonic_label (self->mnemonic_widget, GTK_WIDGET (self));
- g_object_weak_unref (G_OBJECT (self->mnemonic_widget),
- label_mnemonic_widget_weak_notify,
- self);
- }
- self->mnemonic_widget = widget;
- if (self->mnemonic_widget)
- {
- g_object_weak_ref (G_OBJECT (self->mnemonic_widget),
- label_mnemonic_widget_weak_notify,
- self);
- gtk_widget_add_mnemonic_label (self->mnemonic_widget, GTK_WIDGET (self));
- }
+ if (_gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
+ xalign = 1.0 - xalign;
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_WIDGET]);
-}
+ pango_layout_get_pixel_extents (self->layout, NULL, &logical);
-/**
- * gtk_label_get_mnemonic_widget:
- * @self: a #GtkLabel
- *
- * Retrieves the target of the mnemonic (keyboard shortcut) of this
- * label. See gtk_label_set_mnemonic_widget().
- *
- * Returns: (nullable) (transfer none): the target of the label’s mnemonic,
- * or %NULL if none has been set and the default algorithm will be used.
- **/
-GtkWidget *
-gtk_label_get_mnemonic_widget (GtkLabel *self)
-{
- g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
+ layout_width = logical.width;
+ layout_height = logical.height;
- return self->mnemonic_widget;
-}
+ widget_width = gtk_widget_get_width (widget);
+ widget_height = gtk_widget_get_height (widget);
-/**
- * gtk_label_get_mnemonic_keyval:
- * @self: a #GtkLabel
- *
- * If the label has been set so that it has a mnemonic key this function
- * returns the keyval used for the mnemonic accelerator. If there is no
- * mnemonic set up it returns #GDK_KEY_VoidSymbol.
- *
- * Returns: GDK keyval usable for accelerators, or #GDK_KEY_VoidSymbol
- **/
-guint
-gtk_label_get_mnemonic_keyval (GtkLabel *self)
-{
- g_return_val_if_fail (GTK_IS_LABEL (self), GDK_KEY_VoidSymbol);
+ baseline = gtk_widget_get_allocated_baseline (widget);
- return self->mnemonic_keyval;
-}
+ x = floor ((xalign * (widget_width - layout_width)) - logical.x);
-static void
-gtk_label_set_text_internal (GtkLabel *self,
- char *str)
-{
- if (g_strcmp0 (self->text, str) == 0)
+ baseline_offset = 0;
+ if (baseline != -1)
{
- g_free (str);
- return;
+ 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 */
}
- g_free (self->text);
- self->text = str;
+ y = floor ((widget_height - layout_height) * yalign) + baseline_offset;
- gtk_accessible_update_property (GTK_ACCESSIBLE (self),
- GTK_ACCESSIBLE_PROPERTY_LABEL, self->text,
- -1);
+ if (xp)
+ *xp = x;
- gtk_label_select_region_index (self, 0, 0);
+ if (yp)
+ *yp = y;
}
-static gboolean
-gtk_label_set_label_internal (GtkLabel *self,
- const char *str)
+static void
+gtk_label_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
{
- if (g_strcmp0 (str, self->label) == 0)
- return FALSE;
-
- g_free (self->label);
- self->label = g_strdup (str ? str : "");
+ GtkLabel *self = GTK_LABEL (widget);
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_LABEL]);
+ 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);
+ }
- return TRUE;
+ if (self->popup_menu)
+ gtk_popover_present (GTK_POPOVER (self->popup_menu));
}
-static gboolean
-gtk_label_set_use_markup_internal (GtkLabel *self,
- gboolean val)
-{
- if (self->use_markup != val)
- {
- self->use_markup = val;
-
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_USE_MARKUP]);
- return TRUE;
- }
- return FALSE;
-}
+#define GRAPHENE_RECT_FROM_RECT(_r) (GRAPHENE_RECT_INIT ((_r)->x, (_r)->y, (_r)->width, (_r)->height))
-static gboolean
-gtk_label_set_use_underline_internal (GtkLabel *self,
- gboolean val)
+static void
+gtk_label_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
{
- if (self->use_underline != val)
- {
- self->use_underline = val;
+ GtkLabel *self = GTK_LABEL (widget);
+ GtkLabelSelectionInfo *info;
+ GtkStyleContext *context;
+ int lx, ly;
+ int width, height;
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_USE_UNDERLINE]);
+ if (!self->text || (*self->text == '\0'))
+ return;
- return TRUE;
- }
+ gtk_label_ensure_layout (self);
- return FALSE;
-}
+ context = _gtk_widget_get_style_context (widget);
+ get_layout_location (self, &lx, &ly);
-/* Calculates text, attrs and mnemonic_keyval from
- * label, use_underline and use_markup
- */
-static void
-gtk_label_recalculate (GtkLabel *self)
-{
- guint keyval = self->mnemonic_keyval;
+ gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
- gtk_label_clear_links (self);
- gtk_label_clear_layout (self);
- gtk_label_clear_select_info (self);
+ info = self->select_info;
+ if (!info)
+ return;
- if (self->use_markup || self->use_underline)
- gtk_label_set_markup_internal (self, self->label, self->use_underline);
- else
+ width = gtk_widget_get_width (widget);
+ height = gtk_widget_get_height (widget);
+
+ if (info->selection_anchor != info->selection_end)
{
- g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
+ int range[2];
+ cairo_region_t *range_clip;
+ cairo_rectangle_int_t clip_rect;
+ int i;
- gtk_label_set_text_internal (self, g_strdup (self->label));
- }
+ range[0] = MIN (info->selection_anchor, info->selection_end);
+ range[1] = MAX (info->selection_anchor, info->selection_end);
- if (!self->use_underline)
- self->mnemonic_keyval = GDK_KEY_VoidSymbol;
+ gtk_style_context_save_to_node (context, info->selection_node);
- if (keyval != self->mnemonic_keyval)
- {
- gtk_label_setup_mnemonic (self);
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_KEYVAL]);
+ 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;
- gtk_widget_queue_resize (GTK_WIDGET (self));
-}
+ if (info->selectable &&
+ gtk_widget_has_focus (widget) &&
+ gtk_widget_is_drawable (widget))
+ {
+ PangoDirection cursor_direction;
-/**
- * gtk_label_set_text:
- * @self: a #GtkLabel
- * @str: The text you want to set
- *
- * Sets the text within the #GtkLabel widget. It overwrites any text that
- * was there before.
- *
- * This function will clear any previously set mnemonic accelerators, and
- * set the #GtkLabel:use-underline property to %FALSE as a side effect.
- *
- * This function will set the #GtkLabel:use-markup property to %FALSE
- * as a side effect.
- *
- * See also: gtk_label_set_markup()
- **/
-void
-gtk_label_set_text (GtkLabel *self,
- const char *str)
-{
- gboolean changed;
+ cursor_direction = get_cursor_direction (self);
+ gtk_snapshot_render_insertion_cursor (snapshot, context,
+ lx, ly,
+ self->layout, self->select_info->selection_end,
+ cursor_direction);
+ }
- g_return_if_fail (GTK_IS_LABEL (self));
+ focus_link = gtk_label_get_focus_link (self, NULL);
+ active_link = info->active_link;
- g_object_freeze_notify (G_OBJECT (self));
+ if (active_link)
+ {
+ range[0] = active_link->start;
+ range[1] = active_link->end;
- changed = gtk_label_set_label_internal (self, str);
- changed = gtk_label_set_use_markup_internal (self, FALSE) || changed;
- changed = gtk_label_set_use_underline_internal (self, FALSE) || changed;
+ gtk_style_context_save_to_node (context, active_link->cssnode);
- if (changed)
- gtk_label_recalculate (self);
+ 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);
- g_object_thaw_notify (G_OBJECT (self));
-}
+ 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);
+ }
-/**
- * gtk_label_set_attributes:
- * @self: a #GtkLabel
- * @attrs: (nullable): a #PangoAttrList, or %NULL
- *
- * Sets a #PangoAttrList; the attributes in the list are applied to the
- * label text.
- *
- * The attributes set with this function will be applied
- * and merged with any other attributes previously effected by way
- * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
- * While it is not recommended to mix markup strings with manually set
- * attributes, if you must; know that the attributes will be applied
- * to the label after the markup string is parsed.
- **/
-void
-gtk_label_set_attributes (GtkLabel *self,
- PangoAttrList *attrs)
-{
- g_return_if_fail (GTK_IS_LABEL (self));
+ cairo_region_destroy (range_clip);
- if (!attrs && !self->attrs)
- return;
+ gtk_style_context_restore (context);
+ }
- if (attrs)
- pango_attr_list_ref (attrs);
+ if (focus_link && gtk_widget_has_visible_focus (widget))
+ {
+ range[0] = focus_link->start;
+ range[1] = focus_link->end;
- if (self->attrs)
- pango_attr_list_unref (self->attrs);
- self->attrs = attrs;
+ gtk_style_context_save_to_node (context, focus_link->cssnode);
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_ATTRIBUTES]);
+ range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
+ cairo_region_get_extents (range_clip, &rect);
- gtk_label_clear_layout (self);
- gtk_widget_queue_resize (GTK_WIDGET (self));
-}
+ gtk_snapshot_render_focus (snapshot, context, rect.x, rect.y, rect.width, rect.height);
-/**
- * gtk_label_get_attributes:
- * @self: a #GtkLabel
- *
- * Gets the attribute list that was set on the label using
- * gtk_label_set_attributes(), if any. This function does
- * not reflect attributes that come from the labels markup
- * (see gtk_label_set_markup()). If you want to get the
- * effective attributes for the label, use
- * pango_layout_get_attribute (gtk_label_get_layout (self)).
- *
- * Returns: (nullable) (transfer none): the attribute list, or %NULL
- * if none was set.
- **/
-PangoAttrList *
-gtk_label_get_attributes (GtkLabel *self)
-{
- g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
+ cairo_region_destroy (range_clip);
- return self->attrs;
+ gtk_style_context_restore (context);
+ }
+ }
}
-/**
- * gtk_label_set_label:
- * @self: a #GtkLabel
- * @str: the new text to set for the label
- *
- * Sets the text of the label. The label is interpreted as
- * including embedded underlines and/or Pango markup depending
- * on the values of the #GtkLabel:use-underline and
- * #GtkLabel:use-markup properties.
- **/
-void
-gtk_label_set_label (GtkLabel *self,
- const char *str)
+static GtkSizeRequestMode
+gtk_label_get_request_mode (GtkWidget *widget)
{
- g_return_if_fail (GTK_IS_LABEL (self));
-
- g_object_freeze_notify (G_OBJECT (self));
+ GtkLabel *self = GTK_LABEL (widget);
- if (gtk_label_set_label_internal (self, str))
- gtk_label_recalculate (self);
+ if (self->wrap)
+ return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
- g_object_thaw_notify (G_OBJECT (self));
+ return GTK_SIZE_REQUEST_CONSTANT_SIZE;
}
-/**
- * gtk_label_get_label:
- * @self: a #GtkLabel
- *
- * Fetches the text from a label widget including any embedded
- * underlines indicating mnemonics and Pango markup. (See
- * gtk_label_get_text()).
- *
- * Returns: the text of the label widget. This string is
- * owned by the widget and must not be modified or freed.
- **/
-const char *
-gtk_label_get_label (GtkLabel *self)
+static void
+gtk_label_dispose (GObject *object)
{
- g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
+ GtkLabel *self = GTK_LABEL (object);
- return self->label;
+ gtk_label_set_mnemonic_widget (self, NULL);
+
+ G_OBJECT_CLASS (gtk_label_parent_class)->dispose (object);
}
-typedef struct
+static void
+gtk_label_clear_links (GtkLabel *self)
{
- GtkLabel *label;
- GArray *links;
- GString *new_str;
- gsize text_len;
-} UriParserData;
+ guint i;
-static void
-start_element_handler (GMarkupParseContext *context,
- const char *element_name,
- const char **attribute_names,
- const char **attribute_values,
- gpointer user_data,
- GError **error)
-{
- UriParserData *pdata = user_data;
- GtkLabel *self = pdata->label;
+ if (!self->select_info)
+ return;
- if (strcmp (element_name, "a") == 0)
+ for (i = 0; i < self->select_info->n_links; i++)
{
- GtkLabelLink link;
- const char *uri = NULL;
- const char *title = NULL;
- const char *class = NULL;
- gboolean visited = FALSE;
- int line_number;
- int char_number;
- int i;
- GtkCssNode *widget_node;
- GtkStateFlags state;
-
- g_markup_parse_context_get_position (context, &line_number, &char_number);
+ 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");
+}
- for (i = 0; attribute_names[i] != NULL; i++)
- {
- const char *attr = attribute_names[i];
+static void
+gtk_label_finalize (GObject *object)
+{
+ GtkLabel *self = GTK_LABEL (object);
- if (strcmp (attr, "href") == 0)
- uri = attribute_values[i];
- else if (strcmp (attr, "title") == 0)
- title = attribute_values[i];
- else if (strcmp (attr, "class") == 0)
- class = attribute_values[i];
- else
- {
- g_set_error (error,
- G_MARKUP_ERROR,
- G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
- "Attribute '%s' is not allowed on the <a> tag "
- "on line %d char %d",
- attr, line_number, char_number);
- return;
- }
- }
+ g_free (self->label);
+ g_free (self->text);
- if (uri == NULL)
- {
- g_set_error (error,
- G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "Attribute 'href' was missing on the <a> tag "
- "on line %d char %d",
- line_number, char_number);
- return;
- }
+ g_clear_object (&self->layout);
+ g_clear_pointer (&self->attrs, pango_attr_list_unref);
+ g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
- visited = FALSE;
- if (self->select_info)
- {
- for (i = 0; i < self->select_info->n_links; i++)
- {
- const GtkLabelLink *l = &self->select_info->links[i];
+ if (self->select_info)
+ g_object_unref (self->select_info->provider);
- if (strcmp (uri, l->uri) == 0)
- {
- visited = l->visited;
- break;
- }
- }
- }
+ gtk_label_clear_links (self);
+ g_free (self->select_info);
- if (!pdata->links)
- pdata->links = g_array_new (FALSE, TRUE, sizeof (GtkLabelLink));
+ g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
+ g_clear_object (&self->extra_menu);
- link.uri = g_strdup (uri);
- link.title = g_strdup (title);
+ G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
+}
- widget_node = gtk_widget_get_css_node (GTK_WIDGET (pdata->label));
- link.cssnode = gtk_css_node_new ();
- gtk_css_node_set_name (link.cssnode, g_quark_from_static_string ("link"));
- gtk_css_node_set_parent (link.cssnode, widget_node);
- if (class)
- gtk_css_node_add_class (link.cssnode, g_quark_from_string (class));
+static void
+gtk_label_unrealize (GtkWidget *widget)
+{
+ GtkLabel *self = GTK_LABEL (widget);
- state = gtk_css_node_get_state (widget_node);
- if (visited)
- state |= GTK_STATE_FLAG_VISITED;
- else
- state |= GTK_STATE_FLAG_LINK;
- gtk_css_node_set_state (link.cssnode, state);
- g_object_unref (link.cssnode);
+ if (self->select_info &&
+ self->select_info->provider)
+ {
+ GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (widget);
- link.visited = visited;
- link.start = pdata->text_len;
- g_array_append_val (pdata->links, link);
+ if (gdk_clipboard_get_content (clipboard) == self->select_info->provider)
+ gdk_clipboard_set_content (clipboard, NULL);
}
- else
- {
- int i;
- g_string_append_c (pdata->new_str, '<');
- g_string_append (pdata->new_str, element_name);
+ GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
+}
- for (i = 0; attribute_names[i] != NULL; i++)
- {
- const char *attr = attribute_names[i];
- const char *value = attribute_values[i];
- char *newvalue;
+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;
- newvalue = g_markup_escape_text (value, -1);
+ if (!self->ellipsize)
+ return FALSE;
- g_string_append_c (pdata->new_str, ' ');
- g_string_append (pdata->new_str, attr);
- g_string_append (pdata->new_str, "=\"");
- g_string_append (pdata->new_str, newvalue);
- g_string_append_c (pdata->new_str, '\"');
+ gtk_label_ensure_layout (self);
- g_free (newvalue);
- }
- g_string_append_c (pdata->new_str, '>');
- }
-}
+ if (!pango_layout_is_ellipsized (self->layout))
+ return FALSE;
-static void
-end_element_handler (GMarkupParseContext *context,
- const char *element_name,
- gpointer user_data,
- GError **error)
-{
- UriParserData *pdata = user_data;
+ iter = pango_layout_get_iter (self->layout);
- if (!strcmp (element_name, "a"))
- {
- GtkLabelLink *link = &g_array_index (pdata->links, GtkLabelLink, pdata->links->len - 1);
- link->end = pdata->text_len;
- }
- else
- {
- g_string_append (pdata->new_str, "</");
- g_string_append (pdata->new_str, element_name);
- g_string_append_c (pdata->new_str, '>');
- }
-}
+ in_ellipsis = FALSE;
-static void
-text_handler (GMarkupParseContext *context,
- const char *text,
- gsize text_len,
- gpointer user_data,
- GError **error)
-{
- UriParserData *pdata = user_data;
- char *newtext;
+ do {
+ PangoLayoutRun *run;
- newtext = g_markup_escape_text (text, text_len);
- g_string_append (pdata->new_str, newtext);
- pdata->text_len += text_len;
- g_free (newtext);
-}
+ run = pango_layout_iter_get_run_readonly (iter);
+ if (run)
+ {
+ PangoItem *item;
-static const GMarkupParser markup_parser =
-{
- start_element_handler,
- end_element_handler,
- text_handler,
- NULL,
- NULL
-};
+ item = ((PangoGlyphItem*)run)->item;
-static gboolean
-xml_isspace (char c)
-{
- return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+ 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 void
-link_free (GtkLabelLink *link)
+static gboolean
+range_is_in_ellipsis (GtkLabel *self,
+ int range_start,
+ int range_end)
{
- gtk_css_node_set_parent (link->cssnode, NULL);
- g_free (link->uri);
- g_free (link->title);
+ return range_is_in_ellipsis_full (self, range_start, range_end, NULL, NULL);
}
static gboolean
-parse_uri_markup (GtkLabel *self,
- const char *str,
- char **new_str,
- GtkLabelLink **links,
- guint *out_n_links,
- GError **error)
+gtk_label_grab_focus (GtkWidget *widget)
{
- GMarkupParseContext *context;
- const char *p, *end;
- gsize length;
- UriParserData pdata;
-
- length = strlen (str);
- p = str;
- end = str + length;
+ GtkLabel *self = GTK_LABEL (widget);
+ gboolean select_on_focus;
+ GtkWidget *prev_focus;
- pdata.label = self;
- pdata.links = NULL;
- pdata.new_str = g_string_sized_new (length);
- pdata.text_len = 0;
+ if (self->select_info == NULL)
+ return FALSE;
- while (p != end && xml_isspace (*p))
- p++;
+ prev_focus = gtk_root_get_focus (gtk_widget_get_root (widget));
- context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
+ if (!GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget))
+ return FALSE;
- if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
+ if (self->select_info->selectable)
{
- if (!g_markup_parse_context_parse (context, str, length, error))
- goto failed;
+ 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 (!g_markup_parse_context_parse (context, "<markup>", 8, error))
- goto failed;
+ if (self->select_info->links && !self->in_click &&
+ !(prev_focus && gtk_widget_is_ancestor (prev_focus, widget)))
+ {
+ guint i;
- if (!g_markup_parse_context_parse (context, str, length, error))
- goto failed;
+ for (i = 0; i < self->select_info->n_links; i++)
+ {
+ const GtkLabelLink *link = &self->select_info->links[i];
- if (!g_markup_parse_context_parse (context, "</markup>", 9, error))
- goto failed;
+ 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;
+ }
+ }
+ }
}
- if (!g_markup_parse_context_end_parse (context, error))
- goto failed;
+ return TRUE;
+}
- g_markup_parse_context_free (context);
+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;
- *new_str = g_string_free (pdata.new_str, FALSE);
+ *index = 0;
- if (pdata.links)
- {
- *out_n_links = pdata.links->len;
- *links = (GtkLabelLink *)g_array_free (pdata.links, FALSE);
- }
- else
- {
- *links = NULL;
- }
+ gtk_label_ensure_layout (self);
+ get_layout_location (self, &lx, &ly);
- return TRUE;
+ /* Translate x/y to layout position */
+ x -= lx;
+ y -= ly;
-failed:
- g_markup_parse_context_free (context);
- g_string_free (pdata.new_str, TRUE);
+ x *= PANGO_SCALE;
+ y *= PANGO_SCALE;
- if (pdata.links)
- g_array_free (pdata.links, TRUE);
+ inside = pango_layout_xy_to_index (self->layout,
+ x, y,
+ index, &trailing);
- return FALSE;
+ 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 void
-gtk_label_ensure_has_tooltip (GtkLabel *self)
+static gboolean
+gtk_label_query_tooltip (GtkWidget *widget,
+ int x,
+ int y,
+ gboolean keyboard_tip,
+ GtkTooltip *tooltip)
{
- guint i;
- gboolean has_tooltip = FALSE;
+ GtkLabel *self = GTK_LABEL (widget);
+ GtkLabelSelectionInfo *info = self->select_info;
+ int index = -1;
- for (i = 0; i < self->select_info->n_links; i++)
+ if (info && info->links)
{
- const GtkLabelLink *link = &self->select_info->links[i];
+ 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 (link->title)
+ if (index != -1)
{
- has_tooltip = TRUE;
- break;
+ 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);
+ }
+ }
}
}
- gtk_widget_set_has_tooltip (GTK_WIDGET (self), has_tooltip);
+ return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
+ x, y,
+ keyboard_tip,
+ tooltip);
}
-/* Reads @text and extracts the accel key, if any.
- * @new_text will be set to the given text with the first _ removed.
- *
- * Returned will be the one underline attribute used for the mnemonic
- * */
-static void
-extract_mnemonic_keyval (const char *text,
- guint *out_accel_key,
- char **out_new_text,
- PangoAttribute **out_mnemonic_attribute)
+static gboolean
+gtk_label_focus (GtkWidget *widget,
+ GtkDirectionType direction)
{
- const gsize text_len = strlen (text);
- gunichar c;
- const char *p;
+ GtkLabel *self = GTK_LABEL (widget);
+ GtkLabelSelectionInfo *info = self->select_info;
+ GtkLabelLink *focus_link;
- p = text;
- for (;;)
+ if (!gtk_widget_is_focus (widget))
{
- const char *_index;
+ 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;
+ }
+ }
+ }
- c = g_utf8_get_char (p);
+ return TRUE;
+ }
- if (c == '\0')
- break;
+ return FALSE;
+ }
- if (c != '_')
- {
- p = g_utf8_next_char (p);
- continue;
- }
+ if (!info)
+ return FALSE;
- _index = p;
+ if (info->selectable)
+ {
+ int index;
- p = g_utf8_next_char (p);
- c = g_utf8_get_char (p);
+ if (info->selection_anchor != info->selection_end)
+ goto out;
- if (c != '_' && c != '\0')
- {
- const gsize byte_index = p - text - 1; /* Of the _ */
+ index = info->selection_anchor;
- /* c is the accel key */
- if (out_accel_key)
- *out_accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
- if (out_new_text)
+ if (direction == GTK_DIR_TAB_FORWARD)
+ {
+ guint i;
+ for (i = 0; i < info->n_links; i++)
{
- *out_new_text = g_malloc (text_len);
- memcpy (*out_new_text, text, byte_index);
- memcpy (*out_new_text + byte_index, p, text_len - byte_index);
- }
+ const GtkLabelLink *link = &info->links[i];
- if (out_mnemonic_attribute)
- {
- PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
- attr->start_index = _index - text;
- attr->end_index = p - text;
- *out_mnemonic_attribute = attr;
+ 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;
+ }
+ }
}
-
- return;
}
+ else if (direction == GTK_DIR_TAB_BACKWARD)
+ {
+ int i;
+ for (i = info->n_links - 1; i >= 0; i--)
+ {
+ GtkLabelLink *link = &info->links[i];
- p = g_utf8_next_char (p);
- }
+ 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;
+ }
+ }
+ }
+ }
- /* No accel key found */
- if (out_accel_key)
- *out_accel_key = GDK_KEY_VoidSymbol;
- if (out_new_text)
- *out_new_text = NULL;
- if (out_mnemonic_attribute)
- *out_mnemonic_attribute = NULL;
+ 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_set_markup_internal (GtkLabel *self,
- const char *str,
- gboolean with_uline)
+emit_activate_link (GtkLabel *self,
+ GtkLabelLink *link)
{
- char *text = NULL;
- GError *error = NULL;
- PangoAttrList *attrs = NULL;
- char *str_for_display = NULL;
- GtkLabelLink *links = NULL;
- guint n_links = 0;
- PangoAttribute *mnemonic_attr = NULL;
+ gboolean handled;
- if (!parse_uri_markup (self, str, &str_for_display, &links, &n_links, &error))
- goto error_set;
+ g_signal_emit (self, signals[ACTIVATE_LINK], 0, link->uri, &handled);
- if (links)
+ /* signal handler might have invalidated the layout */
+ if (!self->layout)
+ return;
+
+ if (handled && !link->visited &&
+ self->select_info && self->select_info->links)
{
- gtk_label_ensure_select_info (self);
- self->select_info->links = g_steal_pointer (&links);
- self->select_info->n_links = n_links;
- gtk_label_ensure_has_tooltip (self);
- gtk_widget_add_css_class (GTK_WIDGET (self), "link");
+ link->visited = TRUE;
+ update_link_state (self);
}
+}
- if (!with_uline)
- {
-no_uline:
- /* Extract the text to display */
- if (!pango_parse_markup (str_for_display, -1, 0, &attrs, &text, NULL, &error))
- goto error_set;
- }
- else /* Underline AND markup is a little more complicated... */
- {
- char *new_text = NULL;
- guint accel_keyval;
- gboolean auto_mnemonics = TRUE;
- gboolean do_mnemonics = self->mnemonics_visible &&
- (!auto_mnemonics || gtk_widget_is_sensitive (GTK_WIDGET (self))) &&
- (!self->mnemonic_widget || gtk_widget_is_sensitive (self->mnemonic_widget));
+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;
- /* Remove the mnemonic underline */
- extract_mnemonic_keyval (str_for_display,
- &accel_keyval,
- &new_text,
- NULL);
- if (!new_text) /* No underline found anyway */
- goto no_uline;
+ if (link)
+ emit_activate_link (self, link);
+}
- self->mnemonic_keyval = accel_keyval;
+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;
- /* Extract the text to display */
- if (!pango_parse_markup (new_text, -1, '_',
- do_mnemonics ? &attrs : NULL, &text, NULL, &error))
- goto error_set;
+ if (link)
+ {
+ GdkClipboard *clipboard;
- if (do_mnemonics)
- {
- /* text is now the final text, but we need to parse str_for_display once again
- * *with* the mnemonic underline so we can remove the markup tags and get the
- * proper attribute indices */
- char *text_for_accel;
+ clipboard = gtk_widget_get_clipboard (widget);
+ gdk_clipboard_set_text (clipboard, link->uri);
+ }
+ else
+ g_print ("no link ?!\n");
+}
- if (!pango_parse_markup (str_for_display, -1, 0, NULL, &text_for_accel, NULL, &error))
- {
- g_free (new_text);
- goto error_set;
- }
+static void
+gtk_label_activate_clipboard_copy (GtkWidget *widget,
+ const char *name,
+ GVariant *parameter)
+{
+ g_signal_emit_by_name (widget, "copy-clipboard");
+}
- extract_mnemonic_keyval (text_for_accel,
- NULL,
- NULL,
- &mnemonic_attr);
- g_free (text_for_accel);
- }
+static void
+gtk_label_select_all (GtkLabel *self)
+{
+ gtk_label_select_region_index (self, 0, strlen (self->text));
+}
- g_free (new_text);
- }
+static void
+gtk_label_activate_selection_select_all (GtkWidget *widget,
+ const char *name,
+ GVariant *parameter)
+{
+ gtk_label_select_all (GTK_LABEL (widget));
+}
- g_free (str_for_display);
+static void
+gtk_label_nop (GtkWidget *widget,
+ const char *name,
+ GVariant *parameter)
+{
+}
- if (text)
- gtk_label_set_text_internal (self, text);
+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);
- g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
- self->markup_attrs = attrs;
+ /* Try to find the widget to activate by traversing the
+ * widget's ancestry.
+ */
+ parent = gtk_widget_get_parent (widget);
- if (mnemonic_attr)
- pango_attr_list_insert_before (self->markup_attrs, mnemonic_attr);
+ if (GTK_IS_NOTEBOOK (parent))
+ return FALSE;
- return;
+ 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);
+ }
-error_set:
- g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
- str, error->message);
- g_error_free (error);
+ /* 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;
}
-/**
- * gtk_label_set_markup:
- * @self: a #GtkLabel
- * @str: a markup string (see [Pango markup format][PangoMarkupFormat])
- *
- * Parses @str which is marked up with the
- * [Pango text markup language][PangoMarkupFormat], setting the
- * label’s text and attribute list based on the parse results.
- *
- * If the @str is external data, you may need to escape it with
- * g_markup_escape_text() or g_markup_printf_escaped():
- *
- * |[<!-- language="C" -->
- * GtkWidget *self = gtk_label_new (NULL);
- * const char *str = "...";
- * const char *format = "<span style=\"italic\">\%s</span>";
- * char *markup;
- *
- * markup = g_markup_printf_escaped (format, str);
- * gtk_label_set_markup (GTK_LABEL (self), markup);
- * g_free (markup);
- * ]|
- *
- * This function will set the #GtkLabel:use-markup property to %TRUE as
- * a side effect.
- *
- * If you set the label contents using the #GtkLabel:label property you
- * should also ensure that you set the #GtkLabel:use-markup property
- * accordingly.
- *
- * See also: gtk_label_set_text()
- **/
-void
-gtk_label_set_markup (GtkLabel *self,
- const char *str)
+static void
+gtk_label_popup_menu (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameters)
{
- gboolean changed;
+ 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);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ gobject_class->set_property = gtk_label_set_property;
+ gobject_class->get_property = gtk_label_get_property;
+ gobject_class->finalize = gtk_label_finalize;
+ gobject_class->dispose = gtk_label_dispose;
+
+ widget_class->size_allocate = gtk_label_size_allocate;
+ widget_class->state_flags_changed = gtk_label_state_flags_changed;
+ widget_class->css_changed = gtk_label_css_changed;
+ widget_class->query_tooltip = gtk_label_query_tooltip;
+ widget_class->snapshot = gtk_label_snapshot;
+ widget_class->unrealize = gtk_label_unrealize;
+ widget_class->root = gtk_label_root;
+ widget_class->unroot = gtk_label_unroot;
+ widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
+ widget_class->grab_focus = gtk_label_grab_focus;
+ widget_class->focus = gtk_label_focus;
+ widget_class->get_request_mode = gtk_label_get_request_mode;
+ widget_class->measure = gtk_label_measure;
+
+ class->move_cursor = gtk_label_move_cursor;
+ class->copy_clipboard = gtk_label_copy_clipboard;
+ class->activate_link = gtk_label_activate_link;
+
+ /**
+ * GtkLabel::move-cursor:
+ * @entry: the object which received the signal
+ * @step: the granularity of the move, as a #GtkMovementStep
+ * @count: the number of @step units to move
+ * @extend_selection: %TRUE if the move should extend the selection
+ *
+ * The ::move-cursor signal is a
+ * [keybinding signal][GtkSignalAction]
+ * which gets emitted when the user initiates a cursor movement.
+ * If the cursor is not visible in @entry, this signal causes
+ * the viewport to be moved instead.
+ *
+ * Applications should not connect to it, but may emit it with
+ * g_signal_emit_by_name() if they need to control the cursor
+ * programmatically.
+ *
+ * The default bindings for this signal come in two variants,
+ * the variant with the Shift modifier extends the selection,
+ * the variant without the Shift modifier does not.
+ * There are too many key combinations to list them all here.
+ * - Arrow keys move by individual characters/lines
+ * - Ctrl-arrow key combinations move by words/paragraphs
+ * - Home/End keys move to the ends of the buffer
+ */
+ 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);
+
+ /**
+ * GtkLabel::copy-clipboard:
+ * @self: the object which received the signal
+ *
+ * The ::copy-clipboard signal is a
+ * [keybinding signal][GtkSignalAction]
+ * 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);
+
+ /**
+ * GtkLabel::activate-current-link:
+ * @self: The label on which the signal was emitted
+ *
+ * A [keybinding signal][GtkSignalAction]
+ * which gets emitted when the user activates a link in the label.
+ *
+ * Applications may also emit the signal with g_signal_emit_by_name()
+ * if they need to control activation of URIs programmatically.
+ *
+ * The default bindings for this signal are all forms of the Enter key.
+ */
+ signals[ACTIVATE_CURRENT_LINK] =
+ g_signal_new_class_handler (I_("activate-current-link"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (gtk_label_activate_current_link),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GtkLabel::activate-link:
+ * @self: The label on which the signal was emitted
+ * @uri: the URI that is activated
+ *
+ * The signal which gets emitted to activate a URI.
+ * Applications may connect to it to override the default behaviour,
+ * which is to call gtk_show_uri().
+ *
+ * Returns: %TRUE if the link has been activated
+ */
+ signals[ACTIVATE_LINK] =
+ g_signal_new (I_("activate-link"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkLabelClass, activate_link),
+ _gtk_boolean_handled_accumulator, NULL,
+ _gtk_marshal_BOOLEAN__STRING,
+ G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
+
+ /**
+ * GtkLabel:label:
+ *
+ * The contents of the label.
+ *
+ * If the string contains [Pango XML markup][PangoMarkupFormat], you will
+ * have to set the #GtkLabel:use-markup property to %TRUE in order for the
+ * label to display the markup attributes. See also gtk_label_set_markup()
+ * for a convenience function that sets both this property and the
+ * #GtkLabel:use-markup property at the same time.
+ *
+ * If the string contains underlines acting as mnemonics, you will have to
+ * set the #GtkLabel:use-underline property to %TRUE in order for the label
+ * to display them.
+ */
+ label_props[PROP_LABEL] =
+ g_param_spec_string ("label",
+ P_("Label"),
+ P_("The text of the label"),
+ "",
+ GTK_PARAM_READWRITE);
+
+ label_props[PROP_ATTRIBUTES] =
+ g_param_spec_boxed ("attributes",
+ P_("Attributes"),
+ P_("A list of style attributes to apply to the text of the label"),
+ PANGO_TYPE_ATTR_LIST,
+ GTK_PARAM_READWRITE);
+
+ label_props[PROP_USE_MARKUP] =
+ g_param_spec_boolean ("use-markup",
+ P_("Use markup"),
+ P_("The text of the label includes XML markup. See pango_parse_markup()"),
+ FALSE,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ label_props[PROP_USE_UNDERLINE] =
+ g_param_spec_boolean ("use-underline",
+ P_("Use underline"),
+ P_("If set, an underline in the text indicates the next character should be used
for the mnemonic accelerator key"),
+ FALSE,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ label_props[PROP_JUSTIFY] =
+ g_param_spec_enum ("justify",
+ P_("Justification"),
+ P_("The alignment of the lines in the text of the label relative to each other.
This does NOT affect the alignment of the label within its allocation. See GtkLabel:xalign for that"),
+ GTK_TYPE_JUSTIFICATION,
+ GTK_JUSTIFY_LEFT,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkLabel:xalign:
+ *
+ * The xalign property determines the horizontal alignment of the label text
+ * inside the labels size allocation. Compare this to #GtkWidget:halign,
+ * which determines how the labels size allocation is positioned in the
+ * space available for the label.
+ */
+ label_props[PROP_XALIGN] =
+ g_param_spec_float ("xalign",
+ P_("X align"),
+ P_("The horizontal alignment, from 0 (left) to 1 (right). Reversed for RTL
layouts."),
+ 0.0, 1.0,
+ 0.5,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkLabel:yalign:
+ *
+ * The yalign property determines the vertical alignment of the label text
+ * inside the labels size allocation. Compare this to #GtkWidget:valign,
+ * which determines how the labels size allocation is positioned in the
+ * space available for the label.
+ */
+ label_props[PROP_YALIGN] =
+ g_param_spec_float ("yalign",
+ P_("Y align"),
+ P_("The vertical alignment, from 0 (top) to 1 (bottom)"),
+ 0.0, 1.0,
+ 0.5,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ label_props[PROP_WRAP] =
+ g_param_spec_boolean ("wrap",
+ P_("Line wrap"),
+ P_("If set, wrap lines if the text becomes too wide"),
+ FALSE,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkLabel:wrap-mode:
+ *
+ * If line wrapping is on (see the #GtkLabel:wrap property) this controls
+ * how the line wrapping is done. The default is %PANGO_WRAP_WORD, which
+ * means wrap on word boundaries.
+ */
+ label_props[PROP_WRAP_MODE] =
+ g_param_spec_enum ("wrap-mode",
+ P_("Line wrap mode"),
+ P_("If wrap is set, controls how linewrapping is done"),
+ PANGO_TYPE_WRAP_MODE,
+ PANGO_WRAP_WORD,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ label_props[PROP_SELECTABLE] =
+ g_param_spec_boolean ("selectable",
+ P_("Selectable"),
+ P_("Whether the label text can be selected with the mouse"),
+ FALSE,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ label_props[PROP_MNEMONIC_KEYVAL] =
+ g_param_spec_uint ("mnemonic-keyval",
+ P_("Mnemonic key"),
+ P_("The mnemonic accelerator key for this label"),
+ 0, G_MAXUINT,
+ GDK_KEY_VoidSymbol,
+ GTK_PARAM_READABLE);
+
+ label_props[PROP_MNEMONIC_WIDGET] =
+ g_param_spec_object ("mnemonic-widget",
+ P_("Mnemonic widget"),
+ P_("The widget to be activated when the label’s mnemonic key is pressed"),
+ GTK_TYPE_WIDGET,
+ GTK_PARAM_READWRITE);
+
+ /**
+ * GtkLabel:ellipsize:
+ *
+ * The preferred place to ellipsize the string, if the label does
+ * not have enough room to display the entire string, specified as a
+ * #PangoEllipsizeMode.
+ *
+ * Note that setting this property to a value other than
+ * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests
+ * only enough space to display the ellipsis "...". In particular, this
+ * means that ellipsizing labels do not work well in notebook tabs, unless
+ * the #GtkNotebook tab-expand child property is set to %TRUE. Other ways
+ * to set a label's width are gtk_widget_set_size_request() and
+ * gtk_label_set_width_chars().
+ */
+ label_props[PROP_ELLIPSIZE] =
+ g_param_spec_enum ("ellipsize",
+ P_("Ellipsize"),
+ P_("The preferred place to ellipsize the string, if the label does not have enough
room to display the entire string"),
+ PANGO_TYPE_ELLIPSIZE_MODE,
+ PANGO_ELLIPSIZE_NONE,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
- g_return_if_fail (GTK_IS_LABEL (self));
+ /**
+ * GtkLabel:width-chars:
+ *
+ * The desired width of the label, in characters. If this property is set to
+ * -1, the width will be calculated automatically.
+ *
+ * See the section on [text layout][label-text-layout]
+ * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
+ * determine the width of ellipsized and wrapped labels.
+ **/
+ label_props[PROP_WIDTH_CHARS] =
+ g_param_spec_int ("width-chars",
+ P_("Width In Characters"),
+ P_("The desired width of the label, in characters"),
+ -1, G_MAXINT,
+ -1,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
- g_object_freeze_notify (G_OBJECT (self));
+ /**
+ * GtkLabel:single-line-mode:
+ *
+ * Whether the label is in single line mode. In single line mode,
+ * the height of the label does not depend on the actual text, it
+ * is always set to ascent + descent of the font. This can be an
+ * advantage in situations where resizing the label because of text
+ * changes would be distracting, e.g. in a statusbar.
+ **/
+ label_props[PROP_SINGLE_LINE_MODE] =
+ g_param_spec_boolean ("single-line-mode",
+ P_("Single Line Mode"),
+ P_("Whether the label is in single line mode"),
+ FALSE,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
- changed = gtk_label_set_label_internal (self, str);
- changed = gtk_label_set_use_markup_internal (self, TRUE) || changed;
- changed = gtk_label_set_use_underline_internal (self, FALSE) || changed;
+ /**
+ * GtkLabel:max-width-chars:
+ *
+ * The desired maximum width of the label, in characters. If this property
+ * is set to -1, the width will be calculated automatically.
+ *
+ * See the section on [text layout][label-text-layout]
+ * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
+ * determine the width of ellipsized and wrapped labels.
+ **/
+ label_props[PROP_MAX_WIDTH_CHARS] =
+ g_param_spec_int ("max-width-chars",
+ P_("Maximum Width In Characters"),
+ P_("The desired maximum width of the label, in characters"),
+ -1, G_MAXINT,
+ -1,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
- if (changed)
- gtk_label_recalculate (self);
+ /**
+ * GtkLabel:lines:
+ *
+ * The number of lines to which an ellipsized, wrapping label
+ * should be limited. This property has no effect if the
+ * label is not wrapping or ellipsized. Set this property to
+ * -1 if you don't want to limit the number of lines.
+ */
+ label_props[PROP_LINES] =
+ g_param_spec_int ("lines",
+ P_("Number of lines"),
+ P_("The desired number of lines, when ellipsizing a wrapping label"),
+ -1, G_MAXINT,
+ -1,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
- g_object_thaw_notify (G_OBJECT (self));
-}
+ /**
+ * GtkLabel:extra-menu:
+ *
+ * A menu model whose contents will be appended to
+ * the context menu.
+ */
+ label_props[PROP_EXTRA_MENU] =
+ g_param_spec_object ("extra-menu",
+ P_("Extra menu"),
+ P_("Menu model to append to the context menu"),
+ G_TYPE_MENU_MODEL,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
-/**
- * gtk_label_set_markup_with_mnemonic:
- * @self: a #GtkLabel
- * @str: a markup string (see
- * [Pango markup format][PangoMarkupFormat])
- *
- * Parses @str which is marked up with the
- * [Pango text markup language][PangoMarkupFormat],
- * setting the label’s text and attribute list based on the parse results.
- * If characters in @str are preceded by an underscore, they are underlined
- * indicating that they represent 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().
- */
-void
-gtk_label_set_markup_with_mnemonic (GtkLabel *self,
- const char *str)
-{
- gboolean changed;
+ g_object_class_install_properties (gobject_class, NUM_PROPERTIES, label_props);
- g_return_if_fail (GTK_IS_LABEL (self));
+ /**
+ * GtkLabel|menu.popup:
+ *
+ * Opens the context menu.
+ */
+ gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_label_popup_menu);
- g_object_freeze_notify (G_OBJECT (self));
+ /*
+ * Key bindings
+ */
- changed = gtk_label_set_label_internal (self, str);
- changed = gtk_label_set_use_markup_internal (self, TRUE) || changed;
- changed = gtk_label_set_use_underline_internal (self, TRUE) || changed;
+ gtk_widget_class_add_binding_action (widget_class,
+ GDK_KEY_F10, GDK_SHIFT_MASK,
+ "menu.popup",
+ NULL);
+ gtk_widget_class_add_binding_action (widget_class,
+ GDK_KEY_Menu, 0,
+ "menu.popup",
+ NULL);
- if (changed)
- gtk_label_recalculate (self);
+ /* Moving the insertion point */
+ add_move_binding (widget_class, GDK_KEY_Right, 0,
+ GTK_MOVEMENT_VISUAL_POSITIONS, 1);
- g_object_thaw_notify (G_OBJECT (self));
-}
+ add_move_binding (widget_class, GDK_KEY_Left, 0,
+ GTK_MOVEMENT_VISUAL_POSITIONS, -1);
-/**
- * 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.
- **/
-const char *
-gtk_label_get_text (GtkLabel *self)
-{
- g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
+ add_move_binding (widget_class, GDK_KEY_KP_Right, 0,
+ GTK_MOVEMENT_VISUAL_POSITIONS, 1);
- return self->text;
-}
+ add_move_binding (widget_class, GDK_KEY_KP_Left, 0,
+ GTK_MOVEMENT_VISUAL_POSITIONS, -1);
-/**
- * gtk_label_set_justify:
- * @self: a #GtkLabel
- * @jtype: a #GtkJustification
- *
- * Sets the alignment of the lines in the text of the label relative to
- * each other. %GTK_JUSTIFY_LEFT is the default value when the widget is
- * first created with gtk_label_new(). If you instead want to set the
- * alignment of the label as a whole, use gtk_widget_set_halign() instead.
- * gtk_label_set_justify() has no effect on labels containing only a
- * single line.
- */
-void
-gtk_label_set_justify (GtkLabel *self,
- GtkJustification jtype)
-{
- g_return_if_fail (GTK_IS_LABEL (self));
- g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
+ add_move_binding (widget_class, GDK_KEY_f, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
- if ((GtkJustification) self->jtype != jtype)
- {
- self->jtype = jtype;
+ add_move_binding (widget_class, GDK_KEY_b, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
- /* 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));
- }
-}
+ add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_WORDS, 1);
-/**
- * gtk_label_get_justify:
- * @self: a #GtkLabel
- *
- * Returns the justification of the label. See gtk_label_set_justify().
- *
- * Returns: #GtkJustification
- **/
-GtkJustification
-gtk_label_get_justify (GtkLabel *self)
-{
- g_return_val_if_fail (GTK_IS_LABEL (self), 0);
+ add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_WORDS, -1);
- return self->jtype;
-}
+ add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_WORDS, 1);
-/**
- * gtk_label_set_ellipsize:
- * @self: a #GtkLabel
- * @mode: a #PangoEllipsizeMode
- *
- * 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)
-{
- g_return_if_fail (GTK_IS_LABEL (self));
- g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
+ add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_WORDS, -1);
- if ((PangoEllipsizeMode) self->ellipsize != mode)
- {
- self->ellipsize = mode;
+ /* select all */
+ gtk_widget_class_add_binding (widget_class,
+ GDK_KEY_a, GDK_CONTROL_MASK,
+ (GtkShortcutFunc) gtk_label_select_all,
+ NULL);
+ gtk_widget_class_add_binding (widget_class,
+ GDK_KEY_slash, GDK_CONTROL_MASK,
+ (GtkShortcutFunc) gtk_label_select_all,
+ NULL);
- /* No real need to be this drastic, but easier than duplicating the code */
- gtk_label_clear_layout (self);
+ /* unselect all */
+ gtk_widget_class_add_binding_signal (widget_class,
+ GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+ "move-cursor",
+ "(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE);
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_ELLIPSIZE]);
- gtk_widget_queue_resize (GTK_WIDGET (self));
- }
-}
+ gtk_widget_class_add_binding_signal (widget_class,
+ GDK_KEY_backslash, GDK_CONTROL_MASK,
+ "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_label_get_ellipsize:
- * @self: a #GtkLabel
- *
- * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
- *
- * Returns: #PangoEllipsizeMode
- **/
-PangoEllipsizeMode
-gtk_label_get_ellipsize (GtkLabel *self)
-{
- g_return_val_if_fail (GTK_IS_LABEL (self), PANGO_ELLIPSIZE_NONE);
+ add_move_binding (widget_class, GDK_KEY_b, GDK_ALT_MASK,
+ GTK_MOVEMENT_WORDS, -1);
- return self->ellipsize;
-}
+ add_move_binding (widget_class, GDK_KEY_Home, 0,
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
-/**
- * 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)
-{
- g_return_if_fail (GTK_IS_LABEL (self));
+ add_move_binding (widget_class, GDK_KEY_End, 0,
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
- if (self->width_chars != n_chars)
- {
- self->width_chars = n_chars;
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WIDTH_CHARS]);
- gtk_widget_queue_resize (GTK_WIDGET (self));
- }
-}
+ add_move_binding (widget_class, GDK_KEY_KP_Home, 0,
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
-/**
- * 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
-gtk_label_get_width_chars (GtkLabel *self)
-{
- g_return_val_if_fail (GTK_IS_LABEL (self), -1);
+ add_move_binding (widget_class, GDK_KEY_KP_End, 0,
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
- return self->width_chars;
-}
+ add_move_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_BUFFER_ENDS, -1);
-/**
- * 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)
-{
- g_return_if_fail (GTK_IS_LABEL (self));
+ add_move_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_BUFFER_ENDS, 1);
- if (self->max_width_chars != n_chars)
- {
- self->max_width_chars = n_chars;
+ add_move_binding (widget_class, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_BUFFER_ENDS, -1);
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MAX_WIDTH_CHARS]);
- gtk_widget_queue_resize (GTK_WIDGET (self));
- }
-}
+ add_move_binding (widget_class, GDK_KEY_KP_End, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_BUFFER_ENDS, 1);
-/**
- * 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
-gtk_label_get_max_width_chars (GtkLabel *self)
-{
- g_return_val_if_fail (GTK_IS_LABEL (self), -1);
+ /* copy */
+ gtk_widget_class_add_binding_signal (widget_class,
+ GDK_KEY_c, GDK_CONTROL_MASK,
+ "copy-clipboard",
+ NULL);
- return self->max_width_chars;
-}
+ gtk_widget_class_add_binding_signal (widget_class,
+ GDK_KEY_Return, 0,
+ "activate-current-link",
+ NULL);
+ gtk_widget_class_add_binding_signal (widget_class,
+ GDK_KEY_ISO_Enter, 0,
+ "activate-current-link",
+ NULL);
+ gtk_widget_class_add_binding_signal (widget_class,
+ GDK_KEY_KP_Enter, 0,
+ "activate-current-link",
+ NULL);
-/**
- * gtk_label_set_wrap:
- * @self: a #GtkLabel
- * @wrap: the setting
- *
- * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
- * lines if text exceeds the widget’s size. %FALSE lets the text get cut off
- * by the edge of the widget if it exceeds the widget size.
- *
- * Note that setting line wrapping to %TRUE does not make the label
- * wrap at its parent container’s width, because GTK widgets
- * conceptually can’t make their requisition depend on the parent
- * container’s size. For a label that wraps at a specific position,
- * set the label’s width using gtk_widget_set_size_request().
- **/
-void
-gtk_label_set_wrap (GtkLabel *self,
- gboolean wrap)
-{
- g_return_if_fail (GTK_IS_LABEL (self));
+ gtk_widget_class_set_css_name (widget_class, I_("label"));
+ gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_LABEL);
- wrap = wrap != FALSE;
+ quark_mnemonics_visible_connected = g_quark_from_static_string ("gtk-label-mnemonics-visible-connected");
- if (self->wrap != wrap)
- {
- self->wrap = wrap;
+ /**
+ * GtkLabel|clipboard.cut:
+ *
+ * Doesn't do anything, since text in labels can't be deleted.
+ */
+ gtk_widget_class_install_action (widget_class, "clipboard.cut", NULL,
+ gtk_label_nop);
- gtk_label_clear_layout (self);
- gtk_widget_queue_resize (GTK_WIDGET (self));
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WRAP]);
- }
-}
+ /**
+ * GtkLabel|clipboard.copy:
+ *
+ * Copies the text to the clipboard.
+ */
+ gtk_widget_class_install_action (widget_class, "clipboard.copy", NULL,
+ gtk_label_activate_clipboard_copy);
-/**
- * gtk_label_get_wrap:
- * @self: a #GtkLabel
- *
- * Returns whether lines in the label are automatically wrapped.
- * See gtk_label_set_wrap().
- *
- * Returns: %TRUE if the lines of the label are automatically wrapped.
- */
-gboolean
-gtk_label_get_wrap (GtkLabel *self)
-{
- g_return_val_if_fail (GTK_IS_LABEL (self), FALSE);
+ /**
+ * GtkLabel|clipboard.paste:
+ *
+ * Doesn't do anything, since text in labels can't be edited.
+ */
+ gtk_widget_class_install_action (widget_class, "clipboard.paste", NULL,
+ gtk_label_nop);
- return self->wrap;
-}
+ /**
+ * GtkLabel|selection.delete:
+ *
+ * Doesn't do anything, since text in labels can't be deleted.
+ */
+ gtk_widget_class_install_action (widget_class, "selection.delete", NULL,
+ gtk_label_nop);
-/**
- * gtk_label_set_wrap_mode:
- * @self: a #GtkLabel
- * @wrap_mode: the line wrapping mode
- *
- * If line wrapping is on (see gtk_label_set_wrap()) this controls how
- * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
- * wrap on word boundaries.
- **/
-void
-gtk_label_set_wrap_mode (GtkLabel *self,
- PangoWrapMode wrap_mode)
-{
- g_return_if_fail (GTK_IS_LABEL (self));
+ /**
+ * GtkLabel|selection.select-all:
+ *
+ * Selects all of the text, if the label allows selection.
+ */
+ gtk_widget_class_install_action (widget_class, "selection.select-all", NULL,
+ gtk_label_activate_selection_select_all);
- if (self->wrap_mode != wrap_mode)
- {
- self->wrap_mode = wrap_mode;
- g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WRAP_MODE]);
+ /**
+ * GtkLabel|link.open:
+ *
+ * Opens the link, when activated on a link inside the label.
+ */
+ gtk_widget_class_install_action (widget_class, "link.open", NULL,
+ gtk_label_activate_link_open);
- gtk_widget_queue_resize (GTK_WIDGET (self));
- }
+ /**
+ * GtkLabel|link.copy:
+ *
+ * Copies the link to the clipboard, when activated on a link
+ * inside the label.
+ */
+ gtk_widget_class_install_action (widget_class, "link.copy", NULL,
+ gtk_label_activate_link_copy);
}
/**
- * gtk_label_get_wrap_mode:
- * @self: a #GtkLabel
+ * gtk_label_new:
+ * @str: (nullable): The text of the label
*
- * Returns line wrap mode used by the label. See gtk_label_set_wrap_mode().
+ * Creates a new label with the given text inside it. You can
+ * pass %NULL to get an empty label widget.
*
- * Returns: %TRUE if the lines of the label are automatically wrapped.
- */
-PangoWrapMode
-gtk_label_get_wrap_mode (GtkLabel *self)
+ * Returns: the new #GtkLabel
+ **/
+GtkWidget*
+gtk_label_new (const char *str)
{
- g_return_val_if_fail (GTK_IS_LABEL (self), FALSE);
-
- return self->wrap_mode;
-}
+ GtkLabel *self;
-static void
-gtk_label_dispose (GObject *object)
-{
- GtkLabel *self = GTK_LABEL (object);
+ self = g_object_new (GTK_TYPE_LABEL, NULL);
- gtk_label_set_mnemonic_widget (self, NULL);
+ if (str && *str)
+ gtk_label_set_text (self, str);
- G_OBJECT_CLASS (gtk_label_parent_class)->dispose (object);
+ return GTK_WIDGET (self);
}
-static void
-gtk_label_finalize (GObject *object)
+/**
+ * gtk_label_new_with_mnemonic:
+ * @str: (nullable): The text of the label, with an underscore in front of the
+ * mnemonic character
+ *
+ * Creates a new #GtkLabel, containing the text in @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
+ * 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
+ * and be activated by the mnemonic.
+ *
+ * Returns: the new #GtkLabel
+ **/
+GtkWidget*
+gtk_label_new_with_mnemonic (const char *str)
{
- 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);
+ GtkLabel *self;
- gtk_label_clear_links (self);
- g_free (self->select_info);
+ self = g_object_new (GTK_TYPE_LABEL, NULL);
- g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
- g_clear_object (&self->extra_menu);
+ if (str && *str)
+ gtk_label_set_text_with_mnemonic (self, str);
- G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
+ return GTK_WIDGET (self);
}
static void
-gtk_label_clear_layout (GtkLabel *self)
+label_mnemonics_visible_changed (GtkWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
{
- g_clear_object (&self->layout);
+ gboolean visible;
+
+ g_object_get (widget, "mnemonics-visible", &visible, NULL);
+ _gtk_label_mnemonics_visible_apply_recursively (widget, visible);
}
-/**
- * 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)
+static void
+gtk_label_setup_mnemonic (GtkLabel *self)
{
- PangoLayout *copy;
+ GtkWidget *widget = GTK_WIDGET (self);
+ GtkShortcut *shortcut;
+ GtkNative *native;
+ gboolean connected;
+ gboolean mnemonics_visible;
- if (existing_layout != NULL)
+ if (self->mnemonic_keyval == GDK_KEY_VoidSymbol)
{
- if (existing_layout != self->layout)
+ if (self->mnemonic_controller)
{
- pango_layout_set_width (existing_layout, width);
- return existing_layout;
+ gtk_widget_remove_controller (widget, self->mnemonic_controller);
+ self->mnemonic_controller = NULL;
}
+ return;
+ }
- g_object_unref (existing_layout);
+ if (self->mnemonic_controller == NULL)
+ {
+ self->mnemonic_controller = gtk_shortcut_controller_new ();
+ gtk_event_controller_set_propagation_phase (self->mnemonic_controller, GTK_PHASE_CAPTURE);
+ gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (self->mnemonic_controller),
GTK_SHORTCUT_SCOPE_MANAGED);
+ shortcut = gtk_shortcut_new (gtk_mnemonic_trigger_new (self->mnemonic_keyval),
+ g_object_ref (gtk_mnemonic_action_get ()));
+ gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (self->mnemonic_controller), shortcut);
+ gtk_widget_add_controller (GTK_WIDGET (self), self->mnemonic_controller);
+ }
+ else
+ {
+ shortcut = g_list_model_get_item (G_LIST_MODEL (self->mnemonic_controller), 0);
+ gtk_shortcut_set_trigger (shortcut, gtk_mnemonic_trigger_new (self->mnemonic_keyval));
+ g_object_unref (shortcut);
}
- gtk_label_ensure_layout (self);
+ /* Connect to notify::mnemonics-visible of the root */
+ native = gtk_widget_get_native (GTK_WIDGET (self));
+ if (!GTK_IS_WINDOW (native) && !GTK_IS_POPOVER (native))
+ return;
- if (pango_layout_get_width (self->layout) == width)
+ /* always set up this widgets initial value */
+ 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));
+
+ if (!connected)
{
- g_object_ref (self->layout);
- return self->layout;
+ g_signal_connect (native,
+ "notify::mnemonics-visible",
+ G_CALLBACK (label_mnemonics_visible_changed),
+ self);
+ g_object_set_qdata (G_OBJECT (native),
+ quark_mnemonics_visible_connected,
+ GINT_TO_POINTER (1));
}
+}
- /* 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)
+void
+_gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
+ gboolean visible)
+{
+ if (GTK_IS_LABEL (widget))
{
- g_object_ref (self->layout);
- pango_layout_set_width (self->layout, width);
- return self->layout;
- }
+ GtkLabel *self = GTK_LABEL (widget);
- /* 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))
+ if (self->mnemonics_visible != visible)
+ {
+ self->mnemonics_visible = visible;
+ gtk_label_recalculate (self);
+ }
+ }
+ else
{
- PangoRectangle rect;
+ GtkWidget *child;
- if (width == -1)
- return g_object_ref (self->layout);
+ for (child = gtk_widget_get_first_child (widget);
+ child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ if (GTK_IS_NATIVE (child))
+ continue;
- pango_layout_get_extents (self->layout, NULL, &rect);
- if (rect.width <= width)
- return g_object_ref (self->layout);
+ _gtk_label_mnemonics_visible_apply_recursively (child, visible);
+ }
}
+}
+static void
+label_mnemonic_widget_weak_notify (gpointer data,
+ GObject *where_the_object_was)
+{
+ GtkLabel *self = data;
- copy = pango_layout_copy (self->layout);
- pango_layout_set_width (copy, width);
- return copy;
+ self->mnemonic_widget = NULL;
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_WIDGET]);
}
-static void
-gtk_label_update_layout_attributes (GtkLabel *self,
- PangoAttrList *style_attrs)
+/**
+ * gtk_label_set_mnemonic_widget:
+ * @self: a #GtkLabel
+ * @widget: (nullable): the target #GtkWidget, or %NULL to unset
+ *
+ * If the label has been set so that it has a mnemonic key (using
+ * i.e. gtk_label_set_markup_with_mnemonic(),
+ * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
+ * or the “use_underline” property) the label can be associated with a
+ * widget that is the target of the mnemonic. When the label is inside
+ * a widget (like a #GtkButton or a #GtkNotebook tab) it is
+ * automatically associated with the correct widget, but sometimes
+ * (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
+ * and toggle focus between the colliding widgets otherwise.
+ **/
+void
+gtk_label_set_mnemonic_widget (GtkLabel *self,
+ GtkWidget *widget)
{
- GtkWidget *widget = GTK_WIDGET (self);
- GtkCssStyle *style;
- PangoAttrList *attrs;
+ g_return_if_fail (GTK_IS_LABEL (self));
- if (self->layout == NULL)
+ if (widget)
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ if (self->mnemonic_widget)
{
- pango_attr_list_unref (style_attrs);
- return;
+ gtk_widget_remove_mnemonic_label (self->mnemonic_widget, GTK_WIDGET (self));
+ g_object_weak_unref (G_OBJECT (self->mnemonic_widget),
+ label_mnemonic_widget_weak_notify,
+ self);
}
-
- if (self->select_info && self->select_info->links)
+ self->mnemonic_widget = widget;
+ if (self->mnemonic_widget)
{
- 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;
+ g_object_weak_ref (G_OBJECT (self->mnemonic_widget),
+ label_mnemonic_widget_weak_notify,
+ self);
+ gtk_widget_add_mnemonic_label (self->mnemonic_widget, GTK_WIDGET (self));
+ }
- attr->start_index = link->start;
- attr->end_index = link->end;
- pango_attr_list_insert (attrs, attr);
- }
- g_slist_free (attributes);
- }
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_WIDGET]);
+}
- 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);
+/**
+ * gtk_label_get_mnemonic_widget:
+ * @self: a #GtkLabel
+ *
+ * Retrieves the target of the mnemonic (keyboard shortcut) of this
+ * label. See gtk_label_set_mnemonic_widget().
+ *
+ * Returns: (nullable) (transfer none): the target of the label’s mnemonic,
+ * or %NULL if none has been set and the default algorithm will be used.
+ **/
+GtkWidget *
+gtk_label_get_mnemonic_widget (GtkLabel *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
- pango_attr_list_unref (link_attrs);
- }
- }
- else
- attrs = NULL;
+ return self->mnemonic_widget;
+}
- style = gtk_css_node_get_style (gtk_widget_get_css_node (widget));
- if (!style_attrs)
- style_attrs = gtk_css_style_get_pango_attributes (style);
+/**
+ * gtk_label_get_mnemonic_keyval:
+ * @self: a #GtkLabel
+ *
+ * If the label has been set so that it has a mnemonic key this function
+ * returns the keyval used for the mnemonic accelerator. If there is no
+ * mnemonic set up it returns #GDK_KEY_VoidSymbol.
+ *
+ * Returns: GDK keyval usable for accelerators, or #GDK_KEY_VoidSymbol
+ **/
+guint
+gtk_label_get_mnemonic_keyval (GtkLabel *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (self), GDK_KEY_VoidSymbol);
- if (style_attrs)
+ return self->mnemonic_keyval;
+}
+
+static void
+gtk_label_set_text_internal (GtkLabel *self,
+ char *str)
+{
+ if (g_strcmp0 (self->text, str) == 0)
{
- attrs = _gtk_pango_attr_list_merge (attrs, style_attrs);
- pango_attr_list_unref (style_attrs);
+ g_free (str);
+ return;
}
- attrs = _gtk_pango_attr_list_merge (attrs, self->markup_attrs);
- attrs = _gtk_pango_attr_list_merge (attrs, self->attrs);
+ g_free (self->text);
+ self->text = str;
- pango_layout_set_attributes (self->layout, attrs);
+ gtk_accessible_update_property (GTK_ACCESSIBLE (self),
+ GTK_ACCESSIBLE_PROPERTY_LABEL, self->text,
+ -1);
- pango_attr_list_unref (attrs);
+ gtk_label_select_region_index (self, 0, 0);
}
-static void
-gtk_label_ensure_layout (GtkLabel *self)
+static gboolean
+gtk_label_set_label_internal (GtkLabel *self,
+ const char *str)
{
- PangoAlignment align;
- gboolean rtl;
+ if (g_strcmp0 (str, self->label) == 0)
+ return FALSE;
- if (self->layout)
- return;
+ g_free (self->label);
+ self->label = g_strdup (str ? str : "");
- align = PANGO_ALIGN_LEFT; /* Quiet gcc */
- rtl = _gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
- self->layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), self->text);
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_LABEL]);
- gtk_label_update_layout_attributes (self, NULL);
+ return TRUE;
+}
- switch (self->jtype)
+static gboolean
+gtk_label_set_use_markup_internal (GtkLabel *self,
+ gboolean val)
+{
+ if (self->use_markup != val)
{
- case GTK_JUSTIFY_LEFT:
- align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
- break;
- case GTK_JUSTIFY_RIGHT:
- align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
- break;
- case GTK_JUSTIFY_CENTER:
- align = PANGO_ALIGN_CENTER;
- break;
- case GTK_JUSTIFY_FILL:
- align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
- pango_layout_set_justify (self->layout, TRUE);
- break;
- default:
- g_assert_not_reached();
- }
+ self->use_markup = val;
- pango_layout_set_alignment (self->layout, align);
- pango_layout_set_ellipsize (self->layout, self->ellipsize);
- pango_layout_set_wrap (self->layout, self->wrap_mode);
- pango_layout_set_single_paragraph_mode (self->layout, self->single_line_mode);
- if (self->lines > 0)
- pango_layout_set_height (self->layout, - self->lines);
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_USE_MARKUP]);
- if (self->ellipsize || self->wrap)
- pango_layout_set_width (self->layout, gtk_widget_get_width (GTK_WIDGET (self)) * PANGO_SCALE);
+ return TRUE;
+ }
+
+ return FALSE;
}
-static GtkSizeRequestMode
-gtk_label_get_request_mode (GtkWidget *widget)
+static gboolean
+gtk_label_set_use_underline_internal (GtkLabel *self,
+ gboolean val)
{
- GtkLabel *self = GTK_LABEL (widget);
+ if (self->use_underline != val)
+ {
+ self->use_underline = val;
- if (self->wrap)
- return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_USE_UNDERLINE]);
- return GTK_SIZE_REQUEST_CONSTANT_SIZE;
-}
+ return TRUE;
+ }
+ return FALSE;
+}
+/* Calculates text, attrs and mnemonic_keyval from
+ * label, use_underline and use_markup
+ */
static void
-get_height_for_width (GtkLabel *self,
- int width,
- int *minimum_height,
- int *natural_height,
- int *minimum_baseline,
- int *natural_baseline)
+gtk_label_recalculate (GtkLabel *self)
{
- PangoLayout *layout;
- int text_height, baseline;
+ guint keyval = self->mnemonic_keyval;
- layout = gtk_label_get_measuring_layout (self, NULL, width * PANGO_SCALE);
+ gtk_label_clear_links (self);
+ gtk_label_clear_layout (self);
+ gtk_label_clear_select_info (self);
- pango_layout_get_pixel_size (layout, NULL, &text_height);
+ if (self->use_markup || self->use_underline)
+ gtk_label_set_markup_internal (self, self->label, self->use_underline);
+ else
+ {
+ g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
- *minimum_height = text_height;
- *natural_height = text_height;
+ gtk_label_set_text_internal (self, g_strdup (self->label));
+ }
- baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
- *minimum_baseline = baseline;
- *natural_baseline = baseline;
+ if (!self->use_underline)
+ self->mnemonic_keyval = GDK_KEY_VoidSymbol;
- g_object_unref (layout);
+ if (keyval != self->mnemonic_keyval)
+ {
+ gtk_label_setup_mnemonic (self);
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_KEYVAL]);
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
}
-static int
-get_char_pixels (GtkWidget *self,
- PangoLayout *layout)
+/**
+ * gtk_label_set_text:
+ * @self: a #GtkLabel
+ * @str: The text you want to set
+ *
+ * Sets the text within the #GtkLabel widget. It overwrites any text that
+ * was there before.
+ *
+ * This function will clear any previously set mnemonic accelerators, and
+ * set the #GtkLabel:use-underline property to %FALSE as a side effect.
+ *
+ * This function will set the #GtkLabel:use-markup property to %FALSE
+ * as a side effect.
+ *
+ * See also: gtk_label_set_markup()
+ **/
+void
+gtk_label_set_text (GtkLabel *self,
+ const char *str)
{
- 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);
+ gboolean changed;
- return MAX (char_width, digit_width);;
-}
+ g_return_if_fail (GTK_IS_LABEL (self));
-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;
+ g_object_freeze_notify (G_OBJECT (self));
- /* "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.
- */
+ changed = gtk_label_set_label_internal (self, str);
+ changed = gtk_label_set_use_markup_internal (self, FALSE) || changed;
+ changed = gtk_label_set_use_underline_internal (self, FALSE) || changed;
- /* Start off with the pixel extents of an as-wide-as-possible layout */
- layout = gtk_label_get_measuring_layout (self, NULL, -1);
+ if (changed)
+ gtk_label_recalculate (self);
- if (self->width_chars > -1 || self->max_width_chars > -1)
- char_pixels = get_char_pixels (GTK_WIDGET (self), layout);
- else
- char_pixels = 0;
+ g_object_thaw_notify (G_OBJECT (self));
+}
- 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;
+/**
+ * gtk_label_set_attributes:
+ * @self: a #GtkLabel
+ * @attrs: (nullable): a #PangoAttrList, or %NULL
+ *
+ * Sets a #PangoAttrList; the attributes in the list are applied to the
+ * label text.
+ *
+ * The attributes set with this function will be applied
+ * and merged with any other attributes previously effected by way
+ * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
+ * While it is not recommended to mix markup strings with manually set
+ * attributes, if you must; know that the attributes will be applied
+ * to the label after the markup string is parsed.
+ **/
+void
+gtk_label_set_attributes (GtkLabel *self,
+ PangoAttrList *attrs)
+{
+ g_return_if_fail (GTK_IS_LABEL (self));
- 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);
+ if (!attrs && !self->attrs)
+ return;
- pango_layout_get_extents (layout, NULL, smallest);
- smallest->width = MAX (smallest->width, char_pixels * self->width_chars);
- smallest->x = smallest->y = 0;
+ if (attrs)
+ pango_attr_list_ref (attrs);
- *smallest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+ if (self->attrs)
+ pango_attr_list_unref (self->attrs);
+ self->attrs = attrs;
- 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;
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_ATTRIBUTES]);
- *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
- }
+ gtk_label_clear_layout (self);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
- if (widest->width < smallest->width)
- {
- *smallest = *widest;
- *smallest_baseline = *widest_baseline;
- }
- }
- else
- {
- *smallest = *widest;
- *smallest_baseline = *widest_baseline;
- }
+/**
+ * gtk_label_get_attributes:
+ * @self: a #GtkLabel
+ *
+ * Gets the attribute list that was set on the label using
+ * gtk_label_set_attributes(), if any. This function does
+ * not reflect attributes that come from the labels markup
+ * (see gtk_label_set_markup()). If you want to get the
+ * effective attributes for the label, use
+ * pango_layout_get_attribute (gtk_label_get_layout (self)).
+ *
+ * Returns: (nullable) (transfer none): the attribute list, or %NULL
+ * if none was set.
+ **/
+PangoAttrList *
+gtk_label_get_attributes (GtkLabel *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
- g_object_unref (layout);
+ return self->attrs;
}
-static void
-gtk_label_get_preferred_size (GtkWidget *widget,
- GtkOrientation orientation,
- int *minimum_size,
- int *natural_size,
- int *minimum_baseline,
- int *natural_baseline)
+/**
+ * gtk_label_set_label:
+ * @self: a #GtkLabel
+ * @str: the new text to set for the label
+ *
+ * Sets the text of the label. The label is interpreted as
+ * including embedded underlines and/or Pango markup depending
+ * on the values of the #GtkLabel:use-underline and
+ * #GtkLabel:use-markup properties.
+ **/
+void
+gtk_label_set_label (GtkLabel *self,
+ const char *str)
{
- 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);
+ g_return_if_fail (GTK_IS_LABEL (self));
- widest_rect.width = PANGO_PIXELS_CEIL (widest_rect.width);
- widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
+ g_object_freeze_notify (G_OBJECT (self));
- smallest_rect.width = PANGO_PIXELS_CEIL (smallest_rect.width);
- smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
+ if (gtk_label_set_label_internal (self, str))
+ gtk_label_recalculate (self);
- if (orientation == GTK_ORIENTATION_HORIZONTAL)
- {
- /* Normal desired width */
- *minimum_size = smallest_rect.width;
- *natural_size = widest_rect.width;
+ g_object_thaw_notify (G_OBJECT (self));
+}
- if (minimum_baseline)
- *minimum_baseline = -1;
+/**
+ * gtk_label_get_label:
+ * @self: a #GtkLabel
+ *
+ * Fetches the text from a label widget including any embedded
+ * underlines indicating mnemonics and Pango markup. (See
+ * gtk_label_get_text()).
+ *
+ * Returns: the text of the label widget. This string is
+ * owned by the widget and must not be modified or freed.
+ **/
+const char *
+gtk_label_get_label (GtkLabel *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
- 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;
- }
- }
+ return self->label;
}
+typedef struct
+{
+ GtkLabel *label;
+ GArray *links;
+ GString *new_str;
+ gsize text_len;
+} UriParserData;
+
static void
-gtk_label_measure (GtkWidget *widget,
- GtkOrientation orientation,
- int for_size,
- int *minimum,
- int *natural,
- int *minimum_baseline,
- int *natural_baseline)
+start_element_handler (GMarkupParseContext *context,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ gpointer user_data,
+ GError **error)
{
- GtkLabel *self = GTK_LABEL (widget);
+ UriParserData *pdata = user_data;
+ GtkLabel *self = pdata->label;
- if (orientation == GTK_ORIENTATION_VERTICAL && for_size != -1 && self->wrap)
+ if (strcmp (element_name, "a") == 0)
{
- gtk_label_clear_layout (self);
+ GtkLabelLink link;
+ const char *uri = NULL;
+ const char *title = NULL;
+ const char *class = NULL;
+ gboolean visited = FALSE;
+ int line_number;
+ int char_number;
+ int i;
+ GtkCssNode *widget_node;
+ GtkStateFlags state;
- 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);
-}
+ g_markup_parse_context_get_position (context, &line_number, &char_number);
-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;
+ for (i = 0; attribute_names[i] != NULL; i++)
+ {
+ const char *attr = attribute_names[i];
- xalign = self->xalign;
- yalign = self->yalign;
+ if (strcmp (attr, "href") == 0)
+ uri = attribute_values[i];
+ else if (strcmp (attr, "title") == 0)
+ title = attribute_values[i];
+ else if (strcmp (attr, "class") == 0)
+ class = attribute_values[i];
+ else
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+ "Attribute '%s' is not allowed on the <a> tag "
+ "on line %d char %d",
+ attr, line_number, char_number);
+ return;
+ }
+ }
- if (_gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
- xalign = 1.0 - xalign;
+ if (uri == NULL)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Attribute 'href' was missing on the <a> tag "
+ "on line %d char %d",
+ line_number, char_number);
+ return;
+ }
- pango_layout_get_pixel_extents (self->layout, NULL, &logical);
+ visited = FALSE;
+ if (self->select_info)
+ {
+ for (i = 0; i < self->select_info->n_links; i++)
+ {
+ const GtkLabelLink *l = &self->select_info->links[i];
+
+ if (strcmp (uri, l->uri) == 0)
+ {
+ visited = l->visited;
+ break;
+ }
+ }
+ }
- layout_width = logical.width;
- layout_height = logical.height;
+ if (!pdata->links)
+ pdata->links = g_array_new (FALSE, TRUE, sizeof (GtkLabelLink));
- widget_width = gtk_widget_get_width (widget);
- widget_height = gtk_widget_get_height (widget);
+ link.uri = g_strdup (uri);
+ link.title = g_strdup (title);
- baseline = gtk_widget_get_allocated_baseline (widget);
+ widget_node = gtk_widget_get_css_node (GTK_WIDGET (pdata->label));
+ link.cssnode = gtk_css_node_new ();
+ gtk_css_node_set_name (link.cssnode, g_quark_from_static_string ("link"));
+ gtk_css_node_set_parent (link.cssnode, widget_node);
+ if (class)
+ gtk_css_node_add_class (link.cssnode, g_quark_from_string (class));
- x = floor ((xalign * (widget_width - layout_width)) - logical.x);
+ state = gtk_css_node_get_state (widget_node);
+ if (visited)
+ state |= GTK_STATE_FLAG_VISITED;
+ else
+ state |= GTK_STATE_FLAG_LINK;
+ gtk_css_node_set_state (link.cssnode, state);
+ g_object_unref (link.cssnode);
- 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 */
+ link.visited = visited;
+ link.start = pdata->text_len;
+ g_array_append_val (pdata->links, link);
}
+ else
+ {
+ int i;
- y = floor ((widget_height - layout_height) * yalign) + baseline_offset;
+ g_string_append_c (pdata->new_str, '<');
+ g_string_append (pdata->new_str, element_name);
- if (xp)
- *xp = x;
+ for (i = 0; attribute_names[i] != NULL; i++)
+ {
+ const char *attr = attribute_names[i];
+ const char *value = attribute_values[i];
+ char *newvalue;
- if (yp)
- *yp = y;
-}
+ newvalue = g_markup_escape_text (value, -1);
-static void
-gtk_label_size_allocate (GtkWidget *widget,
- int width,
- int height,
- int baseline)
-{
- GtkLabel *self = GTK_LABEL (widget);
+ g_string_append_c (pdata->new_str, ' ');
+ g_string_append (pdata->new_str, attr);
+ g_string_append (pdata->new_str, "=\"");
+ g_string_append (pdata->new_str, newvalue);
+ g_string_append_c (pdata->new_str, '\"');
- 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);
+ g_free (newvalue);
+ }
+ g_string_append_c (pdata->new_str, '>');
}
-
- if (self->popup_menu)
- gtk_popover_present (GTK_POPOVER (self->popup_menu));
}
static void
-gtk_label_update_cursor (GtkLabel *self)
+end_element_handler (GMarkupParseContext *context,
+ const char *element_name,
+ gpointer user_data,
+ GError **error)
{
- GtkWidget *widget = GTK_WIDGET (self);
-
- if (!self->select_info)
- return;
+ UriParserData *pdata = user_data;
- if (gtk_widget_is_sensitive (widget))
+ if (!strcmp (element_name, "a"))
{
- 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);
+ GtkLabelLink *link = &g_array_index (pdata->links, GtkLabelLink, pdata->links->len - 1);
+ link->end = pdata->text_len;
}
else
- gtk_widget_set_cursor (widget, NULL);
+ {
+ g_string_append (pdata->new_str, "</");
+ g_string_append (pdata->new_str, element_name);
+ g_string_append_c (pdata->new_str, '>');
+ }
}
static void
-update_link_state (GtkLabel *self)
+text_handler (GMarkupParseContext *context,
+ const char *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
{
- GtkStateFlags state;
- guint i;
+ UriParserData *pdata = user_data;
+ char *newtext;
- if (!self->select_info)
- return;
+ newtext = g_markup_escape_text (text, text_len);
+ g_string_append (pdata->new_str, newtext);
+ pdata->text_len += text_len;
+ g_free (newtext);
+}
- for (i = 0; i < self->select_info->n_links; i++)
- {
- const GtkLabelLink *link = &self->select_info->links[i];
+static const GMarkupParser markup_parser =
+{
+ start_element_handler,
+ end_element_handler,
+ text_handler,
+ NULL,
+ NULL
+};
- 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 gboolean
+xml_isspace (char c)
+{
+ return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
}
-static void
-gtk_label_state_flags_changed (GtkWidget *widget,
- GtkStateFlags prev_state)
+static gboolean
+parse_uri_markup (GtkLabel *self,
+ const char *str,
+ char **new_str,
+ GtkLabelLink **links,
+ guint *out_n_links,
+ GError **error)
{
- GtkLabel *self = GTK_LABEL (widget);
-
- if (self->select_info)
- {
- if (!gtk_widget_is_sensitive (widget))
- gtk_label_select_region (self, 0, 0);
+ GMarkupParseContext *context;
+ const char *p, *end;
+ gsize length;
+ UriParserData pdata;
- gtk_label_update_cursor (self);
- update_link_state (self);
- }
+ length = strlen (str);
+ p = str;
+ end = str + length;
- if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
- GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
-}
+ pdata.label = self;
+ pdata.links = NULL;
+ pdata.new_str = g_string_sized_new (length);
+ pdata.text_len = 0;
-static void
-gtk_label_css_changed (GtkWidget *widget,
- GtkCssStyleChange *change)
-{
- GtkLabel *self = GTK_LABEL (widget);
- gboolean attrs_affected;
- PangoAttrList *new_attrs = NULL;
+ while (p != end && xml_isspace (*p))
+ p++;
- GTK_WIDGET_CLASS (gtk_label_parent_class)->css_changed (widget, change);
+ context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
- if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT_ATTRS))
+ if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
{
- 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;
+ if (!g_markup_parse_context_parse (context, str, length, error))
+ goto failed;
}
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 (!g_markup_parse_context_parse (context, "<markup>", 8, error))
+ goto failed;
- if (attrs_affected)
- gtk_widget_queue_draw (widget);
+ if (!g_markup_parse_context_parse (context, str, length, error))
+ goto failed;
+
+ if (!g_markup_parse_context_parse (context, "</markup>", 9, error))
+ goto failed;
}
-}
-static PangoDirection
-get_cursor_direction (GtkLabel *self)
-{
- GSList *l;
+ if (!g_markup_parse_context_end_parse (context, error))
+ goto failed;
- g_assert (self->select_info);
+ g_markup_parse_context_free (context);
- gtk_label_ensure_layout (self);
+ *new_str = g_string_free (pdata.new_str, FALSE);
- for (l = pango_layout_get_lines_readonly (self->layout); l; l = l->next)
+ if (pdata.links)
{
- 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;
+ *out_n_links = pdata.links->len;
+ *links = (GtkLabelLink *)g_array_free (pdata.links, FALSE);
+ }
+ else
+ {
+ *links = NULL;
}
- return PANGO_DIRECTION_LTR;
-}
+ return TRUE;
-static GtkLabelLink *
-gtk_label_get_focus_link (GtkLabel *self,
- int *out_index)
-{
- GtkLabelSelectionInfo *info = self->select_info;
- int link_index;
+failed:
+ g_markup_parse_context_free (context);
+ g_string_free (pdata.new_str, TRUE);
- if (!info ||
- info->selection_anchor != info->selection_end)
- goto nope;
+ if (pdata.links)
+ g_array_free (pdata.links, TRUE);
+
+ return FALSE;
+}
- link_index = _gtk_label_get_link_at (self, info->selection_anchor);
+static void
+gtk_label_ensure_has_tooltip (GtkLabel *self)
+{
+ guint i;
+ gboolean has_tooltip = FALSE;
- if (link_index != -1)
+ for (i = 0; i < self->select_info->n_links; i++)
{
- if (out_index)
- *out_index = link_index;
+ const GtkLabelLink *link = &self->select_info->links[i];
- return &info->links[link_index];
+ if (link->title)
+ {
+ has_tooltip = TRUE;
+ break;
+ }
}
-nope:
- if (out_index)
- *out_index = -1;
- return NULL;
+ gtk_widget_set_has_tooltip (GTK_WIDGET (self), has_tooltip);
}
-#define GRAPHENE_RECT_FROM_RECT(_r) (GRAPHENE_RECT_INIT ((_r)->x, (_r)->y, (_r)->width, (_r)->height))
-
+/* Reads @text and extracts the accel key, if any.
+ * @new_text will be set to the given text with the first _ removed.
+ *
+ * Returned will be the one underline attribute used for the mnemonic
+ * */
static void
-gtk_label_snapshot (GtkWidget *widget,
- GtkSnapshot *snapshot)
+extract_mnemonic_keyval (const char *text,
+ guint *out_accel_key,
+ char **out_new_text,
+ PangoAttribute **out_mnemonic_attribute)
{
- GtkLabel *self = GTK_LABEL (widget);
- GtkLabelSelectionInfo *info;
- GtkStyleContext *context;
- int lx, ly;
- int width, height;
-
- if (!self->text || (*self->text == '\0'))
- return;
+ const gsize text_len = strlen (text);
+ gunichar c;
+ const char *p;
- gtk_label_ensure_layout (self);
+ p = text;
+ for (;;)
+ {
+ const char *_index;
- context = _gtk_widget_get_style_context (widget);
- get_layout_location (self, &lx, &ly);
+ c = g_utf8_get_char (p);
- gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
+ if (c == '\0')
+ break;
- info = self->select_info;
- if (!info)
- return;
+ if (c != '_')
+ {
+ p = g_utf8_next_char (p);
+ continue;
+ }
- width = gtk_widget_get_width (widget);
- height = gtk_widget_get_height (widget);
+ _index = p;
- if (info->selection_anchor != info->selection_end)
- {
- int range[2];
- cairo_region_t *range_clip;
- cairo_rectangle_int_t clip_rect;
- int i;
+ p = g_utf8_next_char (p);
+ c = g_utf8_get_char (p);
- range[0] = MIN (info->selection_anchor, info->selection_end);
- range[1] = MAX (info->selection_anchor, info->selection_end);
+ if (c != '_' && c != '\0')
+ {
+ const gsize byte_index = p - text - 1; /* Of the _ */
- gtk_style_context_save_to_node (context, info->selection_node);
+ /* c is the accel key */
+ if (out_accel_key)
+ *out_accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
+ if (out_new_text)
+ {
+ *out_new_text = g_malloc (text_len);
+ memcpy (*out_new_text, text, byte_index);
+ memcpy (*out_new_text + byte_index, p, text_len - byte_index);
+ }
- 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);
+ if (out_mnemonic_attribute)
+ {
+ PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
+ attr->start_index = _index - text;
+ attr->end_index = p - text;
+ *out_mnemonic_attribute = attr;
+ }
- 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);
+ return;
}
- cairo_region_destroy (range_clip);
-
- gtk_style_context_restore (context);
+ p = g_utf8_next_char (p);
}
- else
+
+ /* No accel key found */
+ if (out_accel_key)
+ *out_accel_key = GDK_KEY_VoidSymbol;
+ if (out_new_text)
+ *out_new_text = NULL;
+ if (out_mnemonic_attribute)
+ *out_mnemonic_attribute = NULL;
+}
+
+static void
+gtk_label_set_markup_internal (GtkLabel *self,
+ const char *str,
+ gboolean with_uline)
+{
+ char *text = NULL;
+ GError *error = NULL;
+ PangoAttrList *attrs = NULL;
+ char *str_for_display = NULL;
+ GtkLabelLink *links = NULL;
+ guint n_links = 0;
+ PangoAttribute *mnemonic_attr = NULL;
+
+ if (!parse_uri_markup (self, str, &str_for_display, &links, &n_links, &error))
+ goto error_set;
+
+ if (links)
{
- GtkLabelLink *focus_link;
- GtkLabelLink *active_link;
- int range[2];
- cairo_region_t *range_clip;
- cairo_rectangle_int_t clip_rect;
- int i;
- GdkRectangle rect;
+ gtk_label_ensure_select_info (self);
+ self->select_info->links = g_steal_pointer (&links);
+ self->select_info->n_links = n_links;
+ gtk_label_ensure_has_tooltip (self);
+ gtk_widget_add_css_class (GTK_WIDGET (self), "link");
+ }
- if (info->selectable &&
- gtk_widget_has_focus (widget) &&
- gtk_widget_is_drawable (widget))
+ if (!with_uline)
+ {
+no_uline:
+ /* Extract the text to display */
+ if (!pango_parse_markup (str_for_display, -1, 0, &attrs, &text, NULL, &error))
+ goto error_set;
+ }
+ else /* Underline AND markup is a little more complicated... */
+ {
+ char *new_text = NULL;
+ guint accel_keyval;
+ gboolean auto_mnemonics = TRUE;
+ gboolean do_mnemonics = self->mnemonics_visible &&
+ (!auto_mnemonics || gtk_widget_is_sensitive (GTK_WIDGET (self))) &&
+ (!self->mnemonic_widget || gtk_widget_is_sensitive (self->mnemonic_widget));
+
+ /* Remove the mnemonic underline */
+ extract_mnemonic_keyval (str_for_display,
+ &accel_keyval,
+ &new_text,
+ NULL);
+ if (!new_text) /* No underline found anyway */
+ goto no_uline;
+
+ self->mnemonic_keyval = accel_keyval;
+
+ /* Extract the text to display */
+ if (!pango_parse_markup (new_text, -1, '_',
+ do_mnemonics ? &attrs : NULL, &text, NULL, &error))
+ goto error_set;
+
+ if (do_mnemonics)
{
- PangoDirection cursor_direction;
+ /* text is now the final text, but we need to parse str_for_display once again
+ * *with* the mnemonic underline so we can remove the markup tags and get the
+ * proper attribute indices */
+ char *text_for_accel;
- cursor_direction = get_cursor_direction (self);
- gtk_snapshot_render_insertion_cursor (snapshot, context,
- lx, ly,
- self->layout, self->select_info->selection_end,
- cursor_direction);
+ if (!pango_parse_markup (str_for_display, -1, 0, NULL, &text_for_accel, NULL, &error))
+ {
+ g_free (new_text);
+ goto error_set;
+ }
+
+ extract_mnemonic_keyval (text_for_accel,
+ NULL,
+ NULL,
+ &mnemonic_attr);
+ g_free (text_for_accel);
}
- focus_link = gtk_label_get_focus_link (self, NULL);
- active_link = info->active_link;
+ g_free (new_text);
+ }
- if (active_link)
- {
- range[0] = active_link->start;
- range[1] = active_link->end;
+ g_free (str_for_display);
- gtk_style_context_save_to_node (context, active_link->cssnode);
+ if (text)
+ gtk_label_set_text_internal (self, text);
- 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);
+ g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
+ self->markup_attrs = attrs;
- 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);
- }
+ if (mnemonic_attr)
+ pango_attr_list_insert_before (self->markup_attrs, mnemonic_attr);
- cairo_region_destroy (range_clip);
+ return;
- gtk_style_context_restore (context);
- }
+error_set:
+ g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
+ str, error->message);
+ g_error_free (error);
- if (focus_link && gtk_widget_has_visible_focus (widget))
- {
- range[0] = focus_link->start;
- range[1] = focus_link->end;
+}
+
+/**
+ * gtk_label_set_markup:
+ * @self: a #GtkLabel
+ * @str: a markup string (see [Pango markup format][PangoMarkupFormat])
+ *
+ * Parses @str which is marked up with the
+ * [Pango text markup language][PangoMarkupFormat], setting the
+ * label’s text and attribute list based on the parse results.
+ *
+ * If the @str is external data, you may need to escape it with
+ * g_markup_escape_text() or g_markup_printf_escaped():
+ *
+ * |[<!-- language="C" -->
+ * GtkWidget *self = gtk_label_new (NULL);
+ * const char *str = "...";
+ * const char *format = "<span style=\"italic\">\%s</span>";
+ * char *markup;
+ *
+ * markup = g_markup_printf_escaped (format, str);
+ * gtk_label_set_markup (GTK_LABEL (self), markup);
+ * g_free (markup);
+ * ]|
+ *
+ * This function will set the #GtkLabel:use-markup property to %TRUE as
+ * a side effect.
+ *
+ * If you set the label contents using the #GtkLabel:label property you
+ * should also ensure that you set the #GtkLabel:use-markup property
+ * accordingly.
+ *
+ * See also: gtk_label_set_text()
+ **/
+void
+gtk_label_set_markup (GtkLabel *self,
+ const char *str)
+{
+ gboolean changed;
- gtk_style_context_save_to_node (context, focus_link->cssnode);
+ g_return_if_fail (GTK_IS_LABEL (self));
- range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
- cairo_region_get_extents (range_clip, &rect);
+ g_object_freeze_notify (G_OBJECT (self));
- gtk_snapshot_render_focus (snapshot, context, rect.x, rect.y, rect.width, rect.height);
+ changed = gtk_label_set_label_internal (self, str);
+ changed = gtk_label_set_use_markup_internal (self, TRUE) || changed;
+ changed = gtk_label_set_use_underline_internal (self, FALSE) || changed;
- cairo_region_destroy (range_clip);
+ if (changed)
+ gtk_label_recalculate (self);
- gtk_style_context_restore (context);
- }
- }
+ g_object_thaw_notify (G_OBJECT (self));
}
/**
- * gtk_label_set_text_with_mnemonic:
+ * gtk_label_set_markup_with_mnemonic:
* @self: a #GtkLabel
- * @str: a string
+ * @str: a markup string (see
+ * [Pango markup format][PangoMarkupFormat])
*
- * Sets the label’s text from the string @str.
+ * Parses @str which is marked up with the
+ * [Pango text markup language][PangoMarkupFormat],
+ * setting the label’s text and attribute list based on the parse results.
* If characters in @str are preceded by an underscore, they are underlined
* indicating that they represent 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().
- **/
+ */
void
-gtk_label_set_text_with_mnemonic (GtkLabel *self,
- const char *str)
+gtk_label_set_markup_with_mnemonic (GtkLabel *self,
+ const char *str)
{
gboolean changed;
g_return_if_fail (GTK_IS_LABEL (self));
- g_return_if_fail (str != NULL);
g_object_freeze_notify (G_OBJECT (self));
changed = gtk_label_set_label_internal (self, str);
- changed = gtk_label_set_use_markup_internal (self, FALSE) || changed;
+ changed = gtk_label_set_use_markup_internal (self, TRUE) || changed;
changed = gtk_label_set_use_underline_internal (self, TRUE) || changed;
if (changed)
@@ -3567,344 +3611,433 @@ gtk_label_set_text_with_mnemonic (GtkLabel *self,
g_object_thaw_notify (G_OBJECT (self));
}
-static void
-gtk_label_unrealize (GtkWidget *widget)
+/**
+ * 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.
+ **/
+const char *
+gtk_label_get_text (GtkLabel *self)
{
- GtkLabel *self = GTK_LABEL (widget);
+ g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
- if (self->select_info &&
- self->select_info->provider)
+ return self->text;
+}
+
+/**
+ * gtk_label_set_justify:
+ * @self: a #GtkLabel
+ * @jtype: a #GtkJustification
+ *
+ * Sets the alignment of the lines in the text of the label relative to
+ * each other. %GTK_JUSTIFY_LEFT is the default value when the widget is
+ * first created with gtk_label_new(). If you instead want to set the
+ * alignment of the label as a whole, use gtk_widget_set_halign() instead.
+ * gtk_label_set_justify() has no effect on labels containing only a
+ * single line.
+ */
+void
+gtk_label_set_justify (GtkLabel *self,
+ GtkJustification jtype)
+{
+ g_return_if_fail (GTK_IS_LABEL (self));
+ g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
+
+ if ((GtkJustification) self->jtype != jtype)
{
- GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (widget);
+ self->jtype = jtype;
- if (gdk_clipboard_get_content (clipboard) == self->select_info->provider)
- gdk_clipboard_set_content (clipboard, NULL);
+ /* 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));
}
+}
- GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
+/**
+ * gtk_label_get_justify:
+ * @self: a #GtkLabel
+ *
+ * Returns the justification of the label. See gtk_label_set_justify().
+ *
+ * Returns: #GtkJustification
+ **/
+GtkJustification
+gtk_label_get_justify (GtkLabel *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (self), 0);
+
+ return self->jtype;
}
-static gboolean
-get_layout_index (GtkLabel *self,
- int x,
- int y,
- int *index)
+/**
+ * gtk_label_set_ellipsize:
+ * @self: a #GtkLabel
+ * @mode: a #PangoEllipsizeMode
+ *
+ * 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)
{
- int trailing = 0;
- const char *cluster;
- const char *cluster_end;
- gboolean inside;
- int lx, ly;
+ g_return_if_fail (GTK_IS_LABEL (self));
+ g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
- *index = 0;
+ if ((PangoEllipsizeMode) self->ellipsize != mode)
+ {
+ self->ellipsize = mode;
- gtk_label_ensure_layout (self);
- get_layout_location (self, &lx, &ly);
+ /* No real need to be this drastic, but easier than duplicating the code */
+ gtk_label_clear_layout (self);
- /* Translate x/y to layout position */
- x -= lx;
- y -= ly;
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_ELLIPSIZE]);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ }
+}
- x *= PANGO_SCALE;
- y *= PANGO_SCALE;
+/**
+ * gtk_label_get_ellipsize:
+ * @self: a #GtkLabel
+ *
+ * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
+ *
+ * Returns: #PangoEllipsizeMode
+ **/
+PangoEllipsizeMode
+gtk_label_get_ellipsize (GtkLabel *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (self), PANGO_ELLIPSIZE_NONE);
- inside = pango_layout_xy_to_index (self->layout,
- x, y,
- index, &trailing);
+ return self->ellipsize;
+}
- cluster = self->text + *index;
- cluster_end = cluster;
- while (trailing)
+/**
+ * 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)
+{
+ g_return_if_fail (GTK_IS_LABEL (self));
+
+ if (self->width_chars != n_chars)
{
- cluster_end = g_utf8_next_char (cluster_end);
- --trailing;
+ self->width_chars = n_chars;
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WIDTH_CHARS]);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
}
+}
- *index += (cluster_end - cluster);
+/**
+ * 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
+gtk_label_get_width_chars (GtkLabel *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (self), -1);
+
+ return self->width_chars;
+}
+
+/**
+ * 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)
+{
+ g_return_if_fail (GTK_IS_LABEL (self));
+
+ if (self->max_width_chars != n_chars)
+ {
+ self->max_width_chars = n_chars;
+
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MAX_WIDTH_CHARS]);
+ gtk_widget_queue_resize (GTK_WIDGET (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
+gtk_label_get_max_width_chars (GtkLabel *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (self), -1);
- return inside;
+ return self->max_width_chars;
}
-static gboolean
-range_is_in_ellipsis_full (GtkLabel *self,
- int range_start,
- int range_end,
- int *ellipsis_start,
- int *ellipsis_end)
+/**
+ * gtk_label_set_wrap:
+ * @self: a #GtkLabel
+ * @wrap: the setting
+ *
+ * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
+ * lines if text exceeds the widget’s size. %FALSE lets the text get cut off
+ * by the edge of the widget if it exceeds the widget size.
+ *
+ * Note that setting line wrapping to %TRUE does not make the label
+ * wrap at its parent container’s width, because GTK widgets
+ * conceptually can’t make their requisition depend on the parent
+ * container’s size. For a label that wraps at a specific position,
+ * set the label’s width using gtk_widget_set_size_request().
+ **/
+void
+gtk_label_set_wrap (GtkLabel *self,
+ gboolean wrap)
{
- PangoLayoutIter *iter;
- gboolean in_ellipsis;
-
- if (!self->ellipsize)
- return FALSE;
-
- gtk_label_ensure_layout (self);
-
- if (!pango_layout_is_ellipsized (self->layout))
- return FALSE;
+ g_return_if_fail (GTK_IS_LABEL (self));
- iter = pango_layout_get_iter (self->layout);
+ wrap = wrap != FALSE;
- in_ellipsis = FALSE;
+ if (self->wrap != wrap)
+ {
+ self->wrap = wrap;
- do {
- PangoLayoutRun *run;
+ gtk_label_clear_layout (self);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WRAP]);
+ }
+}
- run = pango_layout_iter_get_run_readonly (iter);
- if (run)
- {
- PangoItem *item;
+/**
+ * gtk_label_get_wrap:
+ * @self: a #GtkLabel
+ *
+ * Returns whether lines in the label are automatically wrapped.
+ * See gtk_label_set_wrap().
+ *
+ * Returns: %TRUE if the lines of the label are automatically wrapped.
+ */
+gboolean
+gtk_label_get_wrap (GtkLabel *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (self), FALSE);
- item = ((PangoGlyphItem*)run)->item;
+ return self->wrap;
+}
- 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));
+/**
+ * gtk_label_set_wrap_mode:
+ * @self: a #GtkLabel
+ * @wrap_mode: the line wrapping mode
+ *
+ * If line wrapping is on (see gtk_label_set_wrap()) this controls how
+ * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
+ * wrap on word boundaries.
+ **/
+void
+gtk_label_set_wrap_mode (GtkLabel *self,
+ PangoWrapMode wrap_mode)
+{
+ g_return_if_fail (GTK_IS_LABEL (self));
- pango_layout_iter_free (iter);
+ if (self->wrap_mode != wrap_mode)
+ {
+ self->wrap_mode = wrap_mode;
+ g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WRAP_MODE]);
- return in_ellipsis;
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ }
}
-static gboolean
-range_is_in_ellipsis (GtkLabel *self,
- int range_start,
- int range_end)
+/**
+ * gtk_label_get_wrap_mode:
+ * @self: a #GtkLabel
+ *
+ * Returns line wrap mode used by the label. See gtk_label_set_wrap_mode().
+ *
+ * Returns: %TRUE if the lines of the label are automatically wrapped.
+ */
+PangoWrapMode
+gtk_label_get_wrap_mode (GtkLabel *self)
{
- return range_is_in_ellipsis_full (self, range_start, range_end, NULL, NULL);
+ g_return_val_if_fail (GTK_IS_LABEL (self), FALSE);
+
+ return self->wrap_mode;
}
static void
-gtk_label_select_word (GtkLabel *self)
+gtk_label_clear_layout (GtkLabel *self)
{
- int min, max;
-
- int start_index = gtk_label_move_backward_word (self, self->select_info->selection_end);
- 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);
- max = MAX (self->select_info->selection_anchor,
- self->select_info->selection_end);
-
- min = MIN (min, start_index);
- max = MAX (max, end_index);
-
- gtk_label_select_region_index (self, min, max);
+ g_clear_object (&self->layout);
}
-static gboolean
-gtk_label_grab_focus (GtkWidget *widget)
+static void
+gtk_label_ensure_layout (GtkLabel *self)
{
- GtkLabel *self = GTK_LABEL (widget);
- gboolean select_on_focus;
- GtkWidget *prev_focus;
+ PangoAlignment align;
+ gboolean rtl;
- if (self->select_info == NULL)
- return FALSE;
+ if (self->layout)
+ return;
- prev_focus = gtk_root_get_focus (gtk_widget_get_root (widget));
+ align = PANGO_ALIGN_LEFT; /* Quiet gcc */
+ rtl = _gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
+ self->layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), self->text);
- if (!GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget))
- return FALSE;
+ gtk_label_update_layout_attributes (self, NULL);
- if (self->select_info->selectable)
+ switch (self->jtype)
{
- 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);
+ case GTK_JUSTIFY_LEFT:
+ align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
+ break;
+ case GTK_JUSTIFY_RIGHT:
+ align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
+ break;
+ case GTK_JUSTIFY_CENTER:
+ align = PANGO_ALIGN_CENTER;
+ break;
+ case GTK_JUSTIFY_FILL:
+ align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
+ pango_layout_set_justify (self->layout, TRUE);
+ break;
+ default:
+ g_assert_not_reached();
}
- 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;
- }
- }
- }
- }
+ pango_layout_set_alignment (self->layout, align);
+ pango_layout_set_ellipsize (self->layout, self->ellipsize);
+ pango_layout_set_wrap (self->layout, self->wrap_mode);
+ pango_layout_set_single_paragraph_mode (self->layout, self->single_line_mode);
+ if (self->lines > 0)
+ pango_layout_set_height (self->layout, - self->lines);
- return TRUE;
+ if (self->ellipsize || self->wrap)
+ pango_layout_set_width (self->layout, gtk_widget_get_width (GTK_WIDGET (self)) * PANGO_SCALE);
}
-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;
- }
- }
- }
+/**
+ * gtk_label_set_text_with_mnemonic:
+ * @self: a #GtkLabel
+ * @str: a string
+ *
+ * Sets the label’s text from the string @str.
+ * If characters in @str are preceded by an underscore, they are underlined
+ * indicating that they represent 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().
+ **/
+void
+gtk_label_set_text_with_mnemonic (GtkLabel *self,
+ const char *str)
+{
+ gboolean changed;
- return TRUE;
- }
+ g_return_if_fail (GTK_IS_LABEL (self));
+ g_return_if_fail (str != NULL);
- return FALSE;
- }
+ g_object_freeze_notify (G_OBJECT (self));
- if (!info)
- return FALSE;
+ changed = gtk_label_set_label_internal (self, str);
+ changed = gtk_label_set_use_markup_internal (self, FALSE) || changed;
+ changed = gtk_label_set_use_underline_internal (self, TRUE) || changed;
- if (info->selectable)
- {
- int index;
+ if (changed)
+ gtk_label_recalculate (self);
- if (info->selection_anchor != info->selection_end)
- goto out;
+ g_object_thaw_notify (G_OBJECT (self));
+}
- index = info->selection_anchor;
+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;
- if (direction == GTK_DIR_TAB_FORWARD)
- {
- guint i;
- for (i = 0; i < info->n_links; i++)
- {
- const GtkLabelLink *link = &info->links[i];
+ length = g_utf8_strlen (self->text, -1);
+ if (new_pos < length)
+ {
+ const PangoLogAttr *log_attrs;
+ int n_attrs;
- 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];
+ gtk_label_ensure_layout (self);
- 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;
- }
- }
- }
- }
+ log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
- goto out;
+ /* Find the next word end */
+ new_pos++;
+ while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
+ new_pos++;
}
- else
- {
- int focus_link_index;
- int new_index = -1;
- int i;
- if (info->n_links == 0)
- goto out;
+ return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
+}
- focus_link = gtk_label_get_focus_link (self, &focus_link_index);
+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 (!focus_link)
- goto out;
+ if (new_pos > 0)
+ {
+ const PangoLogAttr *log_attrs;
+ int n_attrs;
- switch (direction)
- {
- case GTK_DIR_TAB_FORWARD:
- if (focus_link)
- new_index = (focus_link_index + 1) % info->n_links;
- else
- new_index = 0;
+ gtk_label_ensure_layout (self);
- 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;
+ log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
- 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;
+ new_pos -= 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;
+ /* Find the previous word beginning */
+ while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
+ new_pos--;
+ }
- default:
- case GTK_DIR_UP:
- case GTK_DIR_DOWN:
- case GTK_DIR_LEFT:
- case GTK_DIR_RIGHT:
- goto out;
- }
+ return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
+}
- 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);
+static void
+gtk_label_select_word (GtkLabel *self)
+{
+ int min, max;
- return TRUE;
- }
- }
+ int start_index = gtk_label_move_backward_word (self, self->select_info->selection_end);
+ int end_index = gtk_label_move_forward_word (self, self->select_info->selection_end);
-out:
+ min = MIN (self->select_info->selection_anchor,
+ self->select_info->selection_end);
+ max = MAX (self->select_info->selection_anchor,
+ self->select_info->selection_end);
- return FALSE;
+ min = MIN (min, start_index);
+ max = MAX (max, end_index);
+
+ gtk_label_select_region_index (self, min, max);
}
static void
@@ -4139,7 +4272,7 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
if (info->in_drag)
{
if (gtk_drag_check_threshold (widget, info->drag_start_x, info->drag_start_y, x, y))
- {
+ {
GdkDrag *drag;
GdkSurface *surface;
GdkDevice *device;
@@ -4157,9 +4290,8 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
gtk_drag_icon_set_from_paintable (drag, get_selection_paintable (self), 0, 0);
g_object_unref (drag);
-
- info->in_drag = FALSE;
- }
+ info->in_drag = FALSE;
+ }
}
else
{
@@ -4211,6 +4343,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,
@@ -4530,9 +4690,9 @@ gtk_label_set_selectable (GtkLabel *self,
/**
* gtk_label_get_selectable:
* @self: a #GtkLabel
- *
+ *
* Gets the value set by gtk_label_set_selectable().
- *
+ *
* Returns: %TRUE if the user can copy text from the label
**/
gboolean
@@ -4678,10 +4838,10 @@ gtk_label_select_region (GtkLabel *self,
{
if (start_offset < 0)
start_offset = g_utf8_strlen (self->text, -1);
-
+
if (end_offset < 0)
end_offset = g_utf8_strlen (self->text, -1);
-
+
gtk_label_select_region_index (self,
g_utf8_offset_to_pointer (self->text, start_offset) - self->text,
g_utf8_offset_to_pointer (self->text, end_offset) - self->text);
@@ -4693,10 +4853,10 @@ gtk_label_select_region (GtkLabel *self,
* @self: a #GtkLabel
* @start: (out): return location for start of selection, as a character offset
* @end: (out): return location for end of selection, as a character offset
- *
+ *
* Gets the selected range of characters in the label, returning %TRUE
* if there’s a selection.
- *
+ *
* Returns: %TRUE if selection is non-empty
**/
gboolean
@@ -4721,7 +4881,7 @@ gtk_label_get_selection_bounds (GtkLabel *self,
int start_index, end_index;
int start_offset, end_offset;
int len;
-
+
start_index = MIN (self->select_info->selection_anchor,
self->select_info->selection_end);
end_index = MAX (self->select_info->selection_anchor,
@@ -4734,7 +4894,7 @@ gtk_label_get_selection_bounds (GtkLabel *self,
if (start_index > len)
start_index = len;
-
+
start_offset = g_utf8_strlen (self->text, start_index);
end_offset = g_utf8_strlen (self->text, end_index);
@@ -4744,7 +4904,7 @@ gtk_label_get_selection_bounds (GtkLabel *self,
start_offset = end_offset;
end_offset = tmp;
}
-
+
if (start)
*start = start_offset;
@@ -4759,7 +4919,7 @@ gtk_label_get_selection_bounds (GtkLabel *self,
/**
* gtk_label_get_layout:
* @self: a #GtkLabel
- *
+ *
* Gets the #PangoLayout used to display the label.
* The layout is useful to e.g. convert text positions to
* pixel positions, in combination with gtk_label_get_layout_offsets().
@@ -4989,11 +5149,10 @@ get_better_cursor (GtkLabel *self,
static int
gtk_label_move_logically (GtkLabel *self,
- int start,
- int count)
+ int start,
+ int count)
{
- int offset = g_utf8_pointer_to_offset (self->text,
- self->text + start);
+ int offset = g_utf8_pointer_to_offset (self->text, self->text + start);
if (self->text)
{
@@ -5008,21 +5167,21 @@ gtk_label_move_logically (GtkLabel *self,
log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
while (count > 0 && offset < length)
- {
- do
- offset++;
- while (offset < length && !log_attrs[offset].is_cursor_position);
-
- count--;
- }
+ {
+ do
+ offset++;
+ while (offset < length && !log_attrs[offset].is_cursor_position);
+
+ count--;
+ }
while (count < 0 && offset > 0)
- {
- do
- offset--;
- while (offset > 0 && !log_attrs[offset].is_cursor_position);
-
- count++;
- }
+ {
+ do
+ offset--;
+ while (offset > 0 && !log_attrs[offset].is_cursor_position);
+
+ count++;
+ }
}
return g_utf8_offset_to_pointer (self->text, offset) - self->text;
@@ -5030,13 +5189,13 @@ gtk_label_move_logically (GtkLabel *self,
static int
gtk_label_move_visually (GtkLabel *self,
- int start,
- int count)
+ int start,
+ int count)
{
int index;
index = start;
-
+
while (count != 0)
{
int new_index, new_trailing;
@@ -5046,8 +5205,8 @@ gtk_label_move_visually (GtkLabel *self,
gtk_label_ensure_layout (self);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (self)),
- "gtk-split-cursor", &split_cursor,
- NULL);
+ "gtk-split-cursor", &split_cursor,
+ NULL);
if (split_cursor)
strong = TRUE;
@@ -5071,80 +5230,26 @@ gtk_label_move_visually (GtkLabel *self,
}
if (count > 0)
- {
- pango_layout_move_cursor_visually (self->layout, strong, index, 0, 1, &new_index, &new_trailing);
- count--;
- }
+ {
+ pango_layout_move_cursor_visually (self->layout, strong, index, 0, 1, &new_index, &new_trailing);
+ count--;
+ }
else
- {
- pango_layout_move_cursor_visually (self->layout, strong, index, 0, -1, &new_index, &new_trailing);
- count++;
- }
+ {
+ pango_layout_move_cursor_visually (self->layout, strong, index, 0, -1, &new_index, &new_trailing);
+ count++;
+ }
if (new_index < 0 || new_index == G_MAXINT)
- break;
-
- 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);
+ break;
- new_pos -= 1;
+ index = new_index;
- /* Find the previous word beginning */
- while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
- new_pos--;
+ while (new_trailing--)
+ index = g_utf8_next_char (self->text + new_index) - self->text;
}
- return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
+ return index;
}
static void
@@ -5275,135 +5380,6 @@ gtk_label_move_cursor (GtkLabel *self,
gtk_label_select_region_index (self, new_pos, new_pos);
}
-static void
-gtk_label_copy_clipboard (GtkLabel *self)
-{
- if (self->text && self->select_info)
- {
- int start, end;
- int len;
- GdkClipboard *clipboard;
-
- start = MIN (self->select_info->selection_anchor,
- self->select_info->selection_end);
- end = MAX (self->select_info->selection_anchor,
- self->select_info->selection_end);
-
- len = strlen (self->text);
-
- if (end > len)
- end = len;
-
- if (start > len)
- start = len;
-
- clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self));
-
- if (start != end)
- {
- char *str = g_strndup (self->text + start, end - start);
- gdk_clipboard_set_text (clipboard, str);
- g_free (str);
- }
- else
- {
- GtkLabelLink *link;
-
- link = gtk_label_get_focus_link (self, NULL);
- if (link)
- gdk_clipboard_set_text (clipboard, link->uri);
- }
- }
-}
-
-static void
-gtk_label_select_all (GtkLabel *self)
-{
- gtk_label_select_region_index (self, 0, strlen (self->text));
-}
-
-static void
-gtk_label_activate_link_open (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkLabelLink *link = self->select_info->context_link;
-
- if (link)
- emit_activate_link (self, link);
-}
-
-static void
-gtk_label_activate_link_copy (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkLabelLink *link = self->select_info->context_link;
-
- if (link)
- {
- GdkClipboard *clipboard;
-
- clipboard = gtk_widget_get_clipboard (widget);
- gdk_clipboard_set_text (clipboard, link->uri);
- }
- else
- g_print ("no link ?!\n");
-}
-
-static void
-gtk_label_activate_clipboard_copy (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
- g_signal_emit_by_name (widget, "copy-clipboard");
-}
-
-static void
-gtk_label_activate_selection_select_all (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
- gtk_label_select_all (GTK_LABEL (widget));
-}
-
-static void
-gtk_label_nop (GtkWidget *widget,
- const char *name,
- GVariant *parameter)
-{
-}
-
-static void
-gtk_label_update_actions (GtkLabel *self)
-{
- GtkWidget *widget = GTK_WIDGET (self);
- gboolean has_selection;
- GtkLabelLink *link;
-
- if (self->select_info)
- {
- has_selection = self->select_info->selection_anchor != self->select_info->selection_end;
- link = self->select_info->active_link;
- }
- else
- {
- has_selection = FALSE;
- link = gtk_label_get_focus_link (self, NULL);
- }
-
- gtk_widget_action_set_enabled (widget, "clipboard.cut", FALSE);
- gtk_widget_action_set_enabled (widget, "clipboard.copy", has_selection);
- gtk_widget_action_set_enabled (widget, "clipboard.paste", FALSE);
- gtk_widget_action_set_enabled (widget, "selection.select-all",
- gtk_label_get_selectable (self));
- gtk_widget_action_set_enabled (widget, "selection.delete", FALSE);
- gtk_widget_action_set_enabled (widget, "link.open", !has_selection && link);
- gtk_widget_action_set_enabled (widget, "link.copy", !has_selection && link);
-}
-
static GMenuModel *
gtk_label_get_menu_model (GtkLabel *self)
{
@@ -5484,82 +5460,6 @@ gtk_label_do_popup (GtkLabel *self,
gtk_popover_popup (GTK_POPOVER (self->popup_menu));
}
-static void
-gtk_label_popup_menu (GtkWidget *widget,
- const char *action_name,
- GVariant *parameters)
-{
- GtkLabel *self = GTK_LABEL (widget);
-
- gtk_label_do_popup (self, -1, -1);
-}
-
-static void
-gtk_label_clear_links (GtkLabel *self)
-{
- guint i;
-
- if (!self->select_info)
- return;
-
- for (i = 0; i < self->select_info->n_links; i++)
- link_free (&self->select_info->links[i]);
- g_free (self->select_info->links);
- self->select_info->links = NULL;
- self->select_info->n_links = 0;
- self->select_info->active_link = NULL;
- gtk_widget_remove_css_class (GTK_WIDGET (self), "link");
-}
-
-static gboolean
-gtk_label_activate_link (GtkLabel *self,
- const char *uri)
-{
- GtkWidget *widget = GTK_WIDGET (self);
- GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
-
- if (!GTK_IS_WINDOW (toplevel))
- return FALSE;
-
- gtk_show_uri (GTK_WINDOW (toplevel), uri, GDK_CURRENT_TIME);
-
- return TRUE;
-}
-
-static void
-emit_activate_link (GtkLabel *self,
- GtkLabelLink *link)
-{
- gboolean handled;
-
- g_signal_emit (self, signals[ACTIVATE_LINK], 0, link->uri, &handled);
-
- /* signal handler might have invalidated the layout */
- if (!self->layout)
- return;
-
- if (handled && !link->visited &&
- self->select_info && self->select_info->links)
- {
- link->visited = TRUE;
- update_link_state (self);
- }
-}
-
-static void
-gtk_label_activate_current_link (GtkLabel *self)
-{
- GtkLabelLink *link;
- GtkWidget *widget = GTK_WIDGET (self);
-
- link = gtk_label_get_focus_link (self, NULL);
-
- if (link)
- emit_activate_link (self, link);
- else
- gtk_widget_activate_default (widget);
-}
-
/**
* gtk_label_get_current_uri:
* @self: a #GtkLabel
@@ -5596,52 +5496,6 @@ gtk_label_get_current_uri (GtkLabel *self)
return NULL;
}
-static gboolean
-gtk_label_query_tooltip (GtkWidget *widget,
- int x,
- int y,
- gboolean keyboard_tip,
- GtkTooltip *tooltip)
-{
- GtkLabel *self = GTK_LABEL (widget);
- GtkLabelSelectionInfo *info = self->select_info;
- int index = -1;
-
- if (info && info->links)
- {
- if (keyboard_tip)
- {
- if (info->selection_anchor == info->selection_end)
- index = info->selection_anchor;
- }
- else
- {
- if (!get_layout_index (self, x, y, &index))
- index = -1;
- }
-
- if (index != -1)
- {
- const int link_index = _gtk_label_get_link_at (self, index);
-
- if (link_index != -1)
- {
- const GtkLabelLink *link = &info->links[link_index];
-
- if (link->title)
- {
- gtk_tooltip_set_markup (tooltip, link->title);
- }
- }
- }
- }
-
- return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
- x, y,
- keyboard_tip,
- tooltip);
-}
-
int
_gtk_label_get_cursor_position (GtkLabel *self)
{
@@ -5862,7 +5716,7 @@ gtk_label_set_yalign (GtkLabel *self,
{
g_return_if_fail (GTK_IS_LABEL (self));
- yalign = CLAMP (yalign, 0.0, 1.0);
+ yalign = CLAMP (yalign, 0.0, 1.0);
if (self->yalign == yalign)
return;
diff --git a/gtk/gtklabelprivate.h b/gtk/gtklabelprivate.h
index 3533949e86..68ecc1a1e3 100644
--- a/gtk/gtklabelprivate.h
+++ b/gtk/gtklabelprivate.h
@@ -32,7 +32,7 @@ int _gtk_label_get_selection_bound (GtkLabel *label);
int _gtk_label_get_n_links (GtkLabel *label);
int _gtk_label_get_link_at (GtkLabel *label,
int pos);
-void _gtk_label_activate_link (GtkLabel *label,
+void _gtk_label_activate_link (GtkLabel *label,
int idx);
const char *_gtk_label_get_link_uri (GtkLabel *label,
int idx);
@@ -44,7 +44,7 @@ gboolean _gtk_label_get_link_visited (GtkLabel *label,
int idx);
gboolean _gtk_label_get_link_focused (GtkLabel *label,
int idx);
-
+
G_END_DECLS
#endif /* __GTK_LABEL_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]