1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
16  */
17 
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24 
25 #include "config.h"
26 
27 #include <math.h>
28 #include <string.h>
29 
30 #include "gtklabel.h"
31 #include "gtklabelprivate.h"
32 #include "gtkaccellabel.h"
33 #include "gtkbindings.h"
34 #include "gtkbuildable.h"
35 #include "gtkbuilderprivate.h"
36 #include "gtkclipboard.h"
37 #include "gtkcssshadowsvalueprivate.h"
38 #include "gtkcssstylepropertyprivate.h"
39 #include "gtkdnd.h"
40 #include "gtkimage.h"
41 #include "gtkintl.h"
42 #include "gtkmain.h"
43 #include "gtkmarshalers.h"
44 #include "gtkmenuitem.h"
45 #include "gtkmenushellprivate.h"
46 #include "gtknotebook.h"
47 #include "gtkpango.h"
48 #include "gtkprivate.h"
49 #include "gtkseparatormenuitem.h"
50 #include "gtkshow.h"
51 #include "gtkstylecontextprivate.h"
52 #include "gtktextutil.h"
53 #include "gtktooltip.h"
54 #include "gtktypebuiltins.h"
55 #include "gtkwidgetprivate.h"
56 #include "gtkwindow.h"
57 #include "gtkcssnodeprivate.h"
58 #include "gtkcsscustomgadgetprivate.h"
59 #include "gtkwidgetprivate.h"
60 
61 #include "a11y/gtklabelaccessibleprivate.h"
62 
63 /* this is in case rint() is not provided by the compiler,
64  * such as in the case of C89 compilers, like MSVC
65  */
66 #include "fallback-c89.c"
67 
68 /**
69  * SECTION:gtklabel
70  * @Short_description: A widget that displays a small to medium amount of text
71  * @Title: GtkLabel
72  *
73  * The #GtkLabel widget displays a small amount of text. As the name
74  * implies, most labels are used to label another widget such as a
75  * #GtkButton, a #GtkMenuItem, or a #GtkComboBox.
76  *
77  * # CSS nodes
78  *
79  * |[<!-- language="plain" -->
80  * label
81  * ├── [selection]
82  * ├── [link]
83  * ┊
84  * ╰── [link]
85  * ]|
86  *
87  * GtkLabel has a single CSS node with the name label. A wide variety
88  * of style classes may be applied to labels, such as .title, .subtitle,
89  * .dim-label, etc. In the #GtkShortcutsWindow, labels are used wth the
90  * .keycap style class.
91  *
92  * If the label has a selection, it gets a subnode with name selection.
93  *
94  * If the label has links, there is one subnode per link. These subnodes
95  * carry the link or visited state depending on whether they have been
96  * visited.
97  *
98  * # GtkLabel as GtkBuildable
99  *
100  * The GtkLabel implementation of the GtkBuildable interface supports a
101  * custom `<attributes>` element, which supports any number of `<attribute>`
102  * elements. The `<attribute>` element has attributes named “name“, “value“,
103  * “start“ and “end“ and allows you to specify #PangoAttribute values for
104  * this label.
105  *
106  * An example of a UI definition fragment specifying Pango attributes:
107  *
108  * |[<!-- language="xml" -->
109  * <object class="GtkLabel">
110  *   <attributes>
111  *     <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
112  *     <attribute name="background" value="red" start="5" end="10"/>
113  *   </attributes>
114  * </object>
115  * ]|
116  *
117  * The start and end attributes specify the range of characters to which the
118  * Pango attribute applies. If start and end are not specified, the attribute is
119  * applied to the whole text. Note that specifying ranges does not make much
120  * sense with translatable attributes. Use markup embedded in the translatable
121  * content instead.
122  *
123  * # Mnemonics
124  *
125  * Labels may contain “mnemonics”. Mnemonics are
126  * underlined characters in the label, used for keyboard navigation.
127  * Mnemonics are created by providing a string with an underscore before
128  * the mnemonic character, such as `"_File"`, to the
129  * functions gtk_label_new_with_mnemonic() or
130  * gtk_label_set_text_with_mnemonic().
131  *
132  * Mnemonics automatically activate any activatable widget the label is
133  * inside, such as a #GtkButton; if the label is not inside the
134  * mnemonic’s target widget, you have to tell the label about the target
135  * using gtk_label_set_mnemonic_widget(). Here’s a simple example where
136  * the label is inside a button:
137  *
138  * |[<!-- language="C" -->
139  *   // Pressing Alt+H will activate this button
140  *   GtkWidget *button = gtk_button_new ();
141  *   GtkWidget *label = gtk_label_new_with_mnemonic ("_Hello");
142  *   gtk_container_add (GTK_CONTAINER (button), label);
143  * ]|
144  *
145  * There’s a convenience function to create buttons with a mnemonic label
146  * already inside:
147  *
148  * |[<!-- language="C" -->
149  *   // Pressing Alt+H will activate this button
150  *   GtkWidget *button = gtk_button_new_with_mnemonic ("_Hello");
151  * ]|
152  *
153  * To create a mnemonic for a widget alongside the label, such as a
154  * #GtkEntry, you have to point the label at the entry with
155  * gtk_label_set_mnemonic_widget():
156  *
157  * |[<!-- language="C" -->
158  *   // Pressing Alt+H will focus the entry
159  *   GtkWidget *entry = gtk_entry_new ();
160  *   GtkWidget *label = gtk_label_new_with_mnemonic ("_Hello");
161  *   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
162  * ]|
163  *
164  * # Markup (styled text)
165  *
166  * To make it easy to format text in a label (changing colors,
167  * fonts, etc.), label text can be provided in a simple
168  * [markup format][PangoMarkupFormat].
169  *
170  * Here’s how to create a label with a small font:
171  * |[<!-- language="C" -->
172  *   GtkWidget *label = gtk_label_new (NULL);
173  *   gtk_label_set_markup (GTK_LABEL (label), "<small>Small text</small>");
174  * ]|
175  *
176  * (See [complete documentation][PangoMarkupFormat] of available
177  * tags in the Pango manual.)
178  *
179  * The markup passed to gtk_label_set_markup() must be valid; for example,
180  * literal <, > and & characters must be escaped as &lt;, &gt;, and &amp;.
181  * If you pass text obtained from the user, file, or a network to
182  * gtk_label_set_markup(), you’ll want to escape it with
183  * g_markup_escape_text() or g_markup_printf_escaped().
184  *
185  * Markup strings are just a convenient way to set the #PangoAttrList on
186  * a label; gtk_label_set_attributes() may be a simpler way to set
187  * attributes in some cases. Be careful though; #PangoAttrList tends to
188  * cause internationalization problems, unless you’re applying attributes
189  * to the entire string (i.e. unless you set the range of each attribute
190  * to [0, %G_MAXINT)). The reason is that specifying the start_index and
191  * end_index for a #PangoAttribute requires knowledge of the exact string
192  * being displayed, so translations will cause problems.
193  *
194  * # Selectable labels
195  *
196  * Labels can be made selectable with gtk_label_set_selectable().
197  * Selectable labels allow the user to copy the label contents to
198  * the clipboard. Only labels that contain useful-to-copy information
199  * — such as error messages — should be made selectable.
200  *
201  * # Text layout # {#label-text-layout}
202  *
203  * A label can contain any number of paragraphs, but will have
204  * performance problems if it contains more than a small number.
205  * Paragraphs are separated by newlines or other paragraph separators
206  * understood by Pango.
207  *
208  * Labels can automatically wrap text if you call
209  * gtk_label_set_line_wrap().
210  *
211  * gtk_label_set_justify() sets how the lines in a label align
212  * with one another. If you want to set how the label as a whole
213  * aligns in its available space, see the #GtkWidget:halign and
214  * #GtkWidget:valign properties.
215  *
216  * The #GtkLabel:width-chars and #GtkLabel:max-width-chars properties
217  * can be used to control the size allocation of ellipsized or wrapped
218  * labels. For ellipsizing labels, if either is specified (and less
219  * than the actual text size), it is used as the minimum width, and the actual
220  * text size is used as the natural width of the label. For wrapping labels,
221  * width-chars is used as the minimum width, if specified, and max-width-chars
222  * is used as the natural width. Even if max-width-chars specified, wrapping
223  * labels will be rewrapped to use all of the available width.
224  *
225  * Note that the interpretation of #GtkLabel:width-chars and
226  * #GtkLabel:max-width-chars has changed a bit with the introduction of
227  * [width-for-height geometry management.][geometry-management]
228  *
229  * # Links
230  *
231  * Since 2.18, GTK+ supports markup for clickable hyperlinks in addition
232  * to regular Pango markup. The markup for links is borrowed from HTML,
233  * using the `<a>` with “href“ and “title“ attributes. GTK+ renders links
234  * similar to the way they appear in web browsers, with colored, underlined
235  * text. The “title“ attribute is displayed as a tooltip on the link.
236  *
237  * An example looks like this:
238  *
239  * |[<!-- language="C" -->
240  * const gchar *text =
241  * "Go to the"
242  * "<a href=\"http://www.gtk.org title=\"&lt;i&gt;Our&lt;/i&gt; website\">"
243  * "GTK+ website</a> for more...";
244  * GtkWidget *label = gtk_label_new (NULL);
245  * gtk_label_set_markup (GTK_LABEL (label), text);
246  * ]|
247  *
248  * It is possible to implement custom handling for links and their tooltips with
249  * the #GtkLabel::activate-link signal and the gtk_label_get_current_uri() function.
250  */
251 
252 struct _GtkLabelPrivate
253 {
254   GtkLabelSelectionInfo *select_info;
255   GtkWidget *mnemonic_widget;
256   GtkWindow *mnemonic_window;
257   GtkCssGadget *gadget;
258 
259   PangoAttrList *attrs;
260   PangoAttrList *markup_attrs;
261   PangoLayout   *layout;
262 
263   gchar   *label;
264   gchar   *text;
265 
266   gdouble  angle;
267   gfloat   xalign;
268   gfloat   yalign;
269 
270   guint    mnemonics_visible  : 1;
271   guint    jtype              : 2;
272   guint    wrap               : 1;
273   guint    use_underline      : 1;
274   guint    use_markup         : 1;
275   guint    ellipsize          : 3;
276   guint    single_line_mode   : 1;
277   guint    have_transform     : 1;
278   guint    in_click           : 1;
279   guint    wrap_mode          : 3;
280   guint    pattern_set        : 1;
281   guint    track_links        : 1;
282 
283   guint    mnemonic_keyval;
284 
285   gint     width_chars;
286   gint     max_width_chars;
287   gint     lines;
288 };
289 
290 /* Notes about the handling of links:
291  *
292  * Links share the GtkLabelSelectionInfo struct with selectable labels.
293  * There are some new fields for links. The links field contains the list
294  * of GtkLabelLink structs that describe the links which are embedded in
295  * the label. The active_link field points to the link under the mouse
296  * pointer. For keyboard navigation, the “focus” link is determined by
297  * finding the link which contains the selection_anchor position.
298  * The link_clicked field is used with button press and release events
299  * to ensure that pressing inside a link and releasing outside of it
300  * does not activate the link.
301  *
302  * Links are rendered with the #GTK_STATE_FLAG_LINK/#GTK_STATE_FLAG_VISITED
303  * state flags. When the mouse pointer is over a link, the pointer is changed
304  * to indicate the link.
305  *
306  * Labels with links accept keyboard focus, and it is possible to move
307  * the focus between the embedded links using Tab/Shift-Tab. The focus
308  * is indicated by a focus rectangle that is drawn around the link text.
309  * Pressing Enter activates the focused link, and there is a suitable
310  * context menu for links that can be opened with the Menu key. Pressing
311  * Control-C copies the link URI to the clipboard.
312  *
313  * In selectable labels with links, link functionality is only available
314  * when the selection is empty.
315  */
316 typedef struct
317 {
318   gchar *uri;
319   gchar *title;     /* the title attribute, used as tooltip */
320 
321   GtkCssNode *cssnode;
322 
323   gboolean visited; /* get set when the link is activated; this flag
324                      * gets preserved over later set_markup() calls
325                      */
326   gint start;       /* position of the link in the PangoLayout */
327   gint end;
328 } GtkLabelLink;
329 
330 struct _GtkLabelSelectionInfo
331 {
332   GdkWindow *window;
333   gint selection_anchor;
334   gint selection_end;
335   GtkWidget *popup_menu;
336   GtkCssNode *selection_node;
337 
338   GList *links;
339   GtkLabelLink *active_link;
340 
341   GtkGesture *drag_gesture;
342   GtkGesture *multipress_gesture;
343 
344   gint drag_start_x;
345   gint drag_start_y;
346 
347   guint in_drag      : 1;
348   guint select_words : 1;
349   guint selectable   : 1;
350   guint link_clicked : 1;
351 };
352 
353 enum {
354   MOVE_CURSOR,
355   COPY_CLIPBOARD,
356   POPULATE_POPUP,
357   ACTIVATE_LINK,
358   ACTIVATE_CURRENT_LINK,
359   LAST_SIGNAL
360 };
361 
362 enum {
363   PROP_0,
364   PROP_LABEL,
365   PROP_ATTRIBUTES,
366   PROP_USE_MARKUP,
367   PROP_USE_UNDERLINE,
368   PROP_JUSTIFY,
369   PROP_PATTERN,
370   PROP_WRAP,
371   PROP_WRAP_MODE,
372   PROP_SELECTABLE,
373   PROP_MNEMONIC_KEYVAL,
374   PROP_MNEMONIC_WIDGET,
375   PROP_CURSOR_POSITION,
376   PROP_SELECTION_BOUND,
377   PROP_ELLIPSIZE,
378   PROP_WIDTH_CHARS,
379   PROP_SINGLE_LINE_MODE,
380   PROP_ANGLE,
381   PROP_MAX_WIDTH_CHARS,
382   PROP_TRACK_VISITED_LINKS,
383   PROP_LINES,
384   PROP_XALIGN,
385   PROP_YALIGN,
386   NUM_PROPERTIES
387 };
388 
389 static GParamSpec *label_props[NUM_PROPERTIES] = { NULL, };
390 
391 /* When rotating ellipsizable text we want the natural size to request
392  * more to ensure the label wont ever ellipsize in an allocation of full natural size.
393  * */
394 #define ROTATION_ELLIPSIZE_PADDING 2
395 
396 static guint signals[LAST_SIGNAL] = { 0 };
397 
398 static GQuark quark_shortcuts_connected;
399 static GQuark quark_mnemonic_menu;
400 static GQuark quark_mnemonics_visible_connected;
401 static GQuark quark_gtk_signal;
402 static GQuark quark_link;
403 
404 static void gtk_label_set_property      (GObject          *object,
405 					 guint             prop_id,
406 					 const GValue     *value,
407 					 GParamSpec       *pspec);
408 static void gtk_label_get_property      (GObject          *object,
409 					 guint             prop_id,
410 					 GValue           *value,
411 					 GParamSpec       *pspec);
412 static void gtk_label_finalize          (GObject          *object);
413 static void gtk_label_destroy           (GtkWidget        *widget);
414 static void gtk_label_size_allocate     (GtkWidget        *widget,
415                                          GtkAllocation    *allocation);
416 static void gtk_label_state_flags_changed   (GtkWidget        *widget,
417                                              GtkStateFlags     prev_state);
418 static void gtk_label_style_updated     (GtkWidget        *widget);
419 static gboolean gtk_label_draw          (GtkWidget        *widget,
420                                          cairo_t          *cr);
421 static gboolean gtk_label_focus         (GtkWidget         *widget,
422                                          GtkDirectionType   direction);
423 
424 static void gtk_label_realize           (GtkWidget        *widget);
425 static void gtk_label_unrealize         (GtkWidget        *widget);
426 static void gtk_label_map               (GtkWidget        *widget);
427 static void gtk_label_unmap             (GtkWidget        *widget);
428 
429 static gboolean gtk_label_motion            (GtkWidget        *widget,
430 					     GdkEventMotion   *event);
431 static gboolean gtk_label_leave_notify      (GtkWidget        *widget,
432                                              GdkEventCrossing *event);
433 
434 static void     gtk_label_grab_focus        (GtkWidget        *widget);
435 
436 static gboolean gtk_label_query_tooltip     (GtkWidget        *widget,
437                                              gint              x,
438                                              gint              y,
439                                              gboolean          keyboard_tip,
440                                              GtkTooltip       *tooltip);
441 
442 static void gtk_label_set_text_internal          (GtkLabel      *label,
443 						  gchar         *str);
444 static void gtk_label_set_label_internal         (GtkLabel      *label,
445 						  gchar         *str);
446 static gboolean gtk_label_set_use_markup_internal    (GtkLabel  *label,
447                                                       gboolean   val);
448 static gboolean gtk_label_set_use_underline_internal (GtkLabel  *label,
449                                                       gboolean   val);
450 static void gtk_label_set_uline_text_internal    (GtkLabel      *label,
451 						  const gchar   *str);
452 static void gtk_label_set_pattern_internal       (GtkLabel      *label,
453 				                  const gchar   *pattern,
454                                                   gboolean       is_mnemonic);
455 static void gtk_label_set_markup_internal        (GtkLabel      *label,
456 						  const gchar   *str,
457 						  gboolean       with_uline);
458 static void gtk_label_recalculate                (GtkLabel      *label);
459 static void gtk_label_hierarchy_changed          (GtkWidget     *widget,
460 						  GtkWidget     *old_toplevel);
461 static void gtk_label_screen_changed             (GtkWidget     *widget,
462 						  GdkScreen     *old_screen);
463 static gboolean gtk_label_popup_menu             (GtkWidget     *widget);
464 
465 static void gtk_label_create_window       (GtkLabel *label);
466 static void gtk_label_destroy_window      (GtkLabel *label);
467 static void gtk_label_ensure_select_info  (GtkLabel *label);
468 static void gtk_label_clear_select_info   (GtkLabel *label);
469 static void gtk_label_update_cursor       (GtkLabel *label);
470 static void gtk_label_clear_layout        (GtkLabel *label);
471 static void gtk_label_ensure_layout       (GtkLabel *label);
472 static void gtk_label_select_region_index (GtkLabel *label,
473                                            gint      anchor_index,
474                                            gint      end_index);
475 
476 static void gtk_label_update_active_link  (GtkWidget *widget,
477                                            gdouble    x,
478                                            gdouble    y);
479 
480 static gboolean gtk_label_mnemonic_activate (GtkWidget         *widget,
481 					     gboolean           group_cycling);
482 static void     gtk_label_setup_mnemonic    (GtkLabel          *label,
483 					     guint              last_key);
484 static void     gtk_label_drag_data_get     (GtkWidget         *widget,
485 					     GdkDragContext    *context,
486 					     GtkSelectionData  *selection_data,
487 					     guint              info,
488 					     guint              time);
489 
490 static void     gtk_label_buildable_interface_init     (GtkBuildableIface *iface);
491 static gboolean gtk_label_buildable_custom_tag_start   (GtkBuildable     *buildable,
492 							GtkBuilder       *builder,
493 							GObject          *child,
494 							const gchar      *tagname,
495 							GMarkupParser    *parser,
496 							gpointer         *data);
497 
498 static void     gtk_label_buildable_custom_finished    (GtkBuildable     *buildable,
499 							GtkBuilder       *builder,
500 							GObject          *child,
501 							const gchar      *tagname,
502 							gpointer          user_data);
503 
504 
505 static void connect_mnemonics_visible_notify    (GtkLabel   *label);
506 static gboolean      separate_uline_pattern     (const gchar  *str,
507                                                  guint        *accel_key,
508                                                  gchar       **new_str,
509                                                  gchar       **pattern);
510 
511 
512 /* For selectable labels: */
513 static void gtk_label_move_cursor        (GtkLabel        *label,
514 					  GtkMovementStep  step,
515 					  gint             count,
516 					  gboolean         extend_selection);
517 static void gtk_label_copy_clipboard     (GtkLabel        *label);
518 static void gtk_label_select_all         (GtkLabel        *label);
519 static void gtk_label_do_popup           (GtkLabel        *label,
520 					  const GdkEvent  *event);
521 static gint gtk_label_move_forward_word  (GtkLabel        *label,
522 					  gint             start);
523 static gint gtk_label_move_backward_word (GtkLabel        *label,
524 					  gint             start);
525 
526 /* For links: */
527 static void          gtk_label_clear_links      (GtkLabel  *label);
528 static gboolean      gtk_label_activate_link    (GtkLabel    *label,
529                                                  const gchar *uri);
530 static void          gtk_label_activate_current_link (GtkLabel *label);
531 static GtkLabelLink *gtk_label_get_current_link (GtkLabel  *label);
532 static void          emit_activate_link         (GtkLabel     *label,
533                                                  GtkLabelLink *link);
534 
535 /* Event controller callbacks */
536 static void   gtk_label_multipress_gesture_pressed  (GtkGestureMultiPress *gesture,
537                                                      gint                  n_press,
538                                                      gdouble               x,
539                                                      gdouble               y,
540                                                      GtkLabel             *label);
541 static void   gtk_label_multipress_gesture_released (GtkGestureMultiPress *gesture,
542                                                      gint                  n_press,
543                                                      gdouble               x,
544                                                      gdouble               y,
545                                                      GtkLabel             *label);
546 static void   gtk_label_drag_gesture_begin          (GtkGestureDrag *gesture,
547                                                      gdouble         start_x,
548                                                      gdouble         start_y,
549                                                      GtkLabel       *label);
550 static void   gtk_label_drag_gesture_update         (GtkGestureDrag *gesture,
551                                                      gdouble         offset_x,
552                                                      gdouble         offset_y,
553                                                      GtkLabel       *label);
554 
555 static GtkSizeRequestMode gtk_label_get_request_mode                (GtkWidget           *widget);
556 static void               gtk_label_get_preferred_width             (GtkWidget           *widget,
557                                                                      gint                *minimum_size,
558                                                                      gint                *natural_size);
559 static void               gtk_label_get_preferred_height            (GtkWidget           *widget,
560                                                                      gint                *minimum_size,
561                                                                      gint                *natural_size);
562 static void               gtk_label_get_preferred_width_for_height  (GtkWidget           *widget,
563                                                                      gint                 height,
564                                                                      gint                *minimum_width,
565                                                                      gint                *natural_width);
566 static void               gtk_label_get_preferred_height_for_width  (GtkWidget           *widget,
567                                                                      gint                 width,
568                                                                      gint                *minimum_height,
569                                                                      gint                *natural_height);
570 static void    gtk_label_get_preferred_height_and_baseline_for_width (GtkWidget          *widget,
571 								      gint                width,
572 								      gint               *minimum_height,
573 								      gint               *natural_height,
574 								      gint               *minimum_baseline,
575 								      gint               *natural_baseline);
576 
577 static void     gtk_label_measure (GtkCssGadget   *gadget,
578                                    GtkOrientation  orientation,
579                                    int             for_size,
580                                    int            *minimum,
581                                    int            *natural,
582                                    int            *minimum_baseline,
583                                    int            *natural_baseline,
584                                    gpointer        unused);
585 static gboolean gtk_label_render  (GtkCssGadget   *gadget,
586                                    cairo_t        *cr,
587                                    int             x,
588                                    int             y,
589                                    int             width,
590                                    int             height,
591                                    gpointer        data);
592 
593 static GtkBuildableIface *buildable_parent_iface = NULL;
594 
595 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
G_DEFINE_TYPE_WITH_CODE(GtkLabel,gtk_label,GTK_TYPE_MISC,G_ADD_PRIVATE (GtkLabel)G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_label_buildable_interface_init))596 G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
597                          G_ADD_PRIVATE (GtkLabel)
598                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
599                                                 gtk_label_buildable_interface_init))
600 G_GNUC_END_IGNORE_DEPRECATIONS
601 
602 static void
603 add_move_binding (GtkBindingSet  *binding_set,
604 		  guint           keyval,
605 		  guint           modmask,
606 		  GtkMovementStep step,
607 		  gint            count)
608 {
609   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
610 
611   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
612 				"move-cursor", 3,
613 				G_TYPE_ENUM, step,
614 				G_TYPE_INT, count,
615 				G_TYPE_BOOLEAN, FALSE);
616 
617   /* Selection-extending version */
618   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
619 				"move-cursor", 3,
620 				G_TYPE_ENUM, step,
621 				G_TYPE_INT, count,
622 				G_TYPE_BOOLEAN, TRUE);
623 }
624 
625 static void
gtk_label_class_init(GtkLabelClass * class)626 gtk_label_class_init (GtkLabelClass *class)
627 {
628   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
629   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
630   GtkBindingSet *binding_set;
631 
632   gobject_class->set_property = gtk_label_set_property;
633   gobject_class->get_property = gtk_label_get_property;
634   gobject_class->finalize = gtk_label_finalize;
635 
636   widget_class->destroy = gtk_label_destroy;
637   widget_class->size_allocate = gtk_label_size_allocate;
638   widget_class->state_flags_changed = gtk_label_state_flags_changed;
639   widget_class->style_updated = gtk_label_style_updated;
640   widget_class->query_tooltip = gtk_label_query_tooltip;
641   widget_class->draw = gtk_label_draw;
642   widget_class->realize = gtk_label_realize;
643   widget_class->unrealize = gtk_label_unrealize;
644   widget_class->map = gtk_label_map;
645   widget_class->unmap = gtk_label_unmap;
646   widget_class->motion_notify_event = gtk_label_motion;
647   widget_class->leave_notify_event = gtk_label_leave_notify;
648   widget_class->hierarchy_changed = gtk_label_hierarchy_changed;
649   widget_class->screen_changed = gtk_label_screen_changed;
650   widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
651   widget_class->drag_data_get = gtk_label_drag_data_get;
652   widget_class->grab_focus = gtk_label_grab_focus;
653   widget_class->popup_menu = gtk_label_popup_menu;
654   widget_class->focus = gtk_label_focus;
655   widget_class->get_request_mode = gtk_label_get_request_mode;
656   widget_class->get_preferred_width = gtk_label_get_preferred_width;
657   widget_class->get_preferred_height = gtk_label_get_preferred_height;
658   widget_class->get_preferred_width_for_height = gtk_label_get_preferred_width_for_height;
659   widget_class->get_preferred_height_for_width = gtk_label_get_preferred_height_for_width;
660   widget_class->get_preferred_height_and_baseline_for_width = gtk_label_get_preferred_height_and_baseline_for_width;
661 
662   class->move_cursor = gtk_label_move_cursor;
663   class->copy_clipboard = gtk_label_copy_clipboard;
664   class->activate_link = gtk_label_activate_link;
665 
666   /**
667    * GtkLabel::move-cursor:
668    * @entry: the object which received the signal
669    * @step: the granularity of the move, as a #GtkMovementStep
670    * @count: the number of @step units to move
671    * @extend_selection: %TRUE if the move should extend the selection
672    *
673    * The ::move-cursor signal is a
674    * [keybinding signal][GtkBindingSignal]
675    * which gets emitted when the user initiates a cursor movement.
676    * If the cursor is not visible in @entry, this signal causes
677    * the viewport to be moved instead.
678    *
679    * Applications should not connect to it, but may emit it with
680    * g_signal_emit_by_name() if they need to control the cursor
681    * programmatically.
682    *
683    * The default bindings for this signal come in two variants,
684    * the variant with the Shift modifier extends the selection,
685    * the variant without the Shift modifer does not.
686    * There are too many key combinations to list them all here.
687    * - Arrow keys move by individual characters/lines
688    * - Ctrl-arrow key combinations move by words/paragraphs
689    * - Home/End keys move to the ends of the buffer
690    */
691   signals[MOVE_CURSOR] =
692     g_signal_new (I_("move-cursor"),
693 		  G_OBJECT_CLASS_TYPE (gobject_class),
694 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
695 		  G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
696 		  NULL, NULL,
697 		  _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
698 		  G_TYPE_NONE, 3,
699 		  GTK_TYPE_MOVEMENT_STEP,
700 		  G_TYPE_INT,
701 		  G_TYPE_BOOLEAN);
702 
703    /**
704    * GtkLabel::copy-clipboard:
705    * @label: the object which received the signal
706    *
707    * The ::copy-clipboard signal is a
708    * [keybinding signal][GtkBindingSignal]
709    * which gets emitted to copy the selection to the clipboard.
710    *
711    * The default binding for this signal is Ctrl-c.
712    */
713   signals[COPY_CLIPBOARD] =
714     g_signal_new (I_("copy-clipboard"),
715 		  G_OBJECT_CLASS_TYPE (gobject_class),
716 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
717 		  G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
718 		  NULL, NULL,
719 		  NULL,
720 		  G_TYPE_NONE, 0);
721 
722   /**
723    * GtkLabel::populate-popup:
724    * @label: The label on which the signal is emitted
725    * @menu: the menu that is being populated
726    *
727    * The ::populate-popup signal gets emitted before showing the
728    * context menu of the label. Note that only selectable labels
729    * have context menus.
730    *
731    * If you need to add items to the context menu, connect
732    * to this signal and append your menuitems to the @menu.
733    */
734   signals[POPULATE_POPUP] =
735     g_signal_new (I_("populate-popup"),
736 		  G_OBJECT_CLASS_TYPE (gobject_class),
737 		  G_SIGNAL_RUN_LAST,
738 		  G_STRUCT_OFFSET (GtkLabelClass, populate_popup),
739 		  NULL, NULL,
740 		  NULL,
741 		  G_TYPE_NONE, 1,
742 		  GTK_TYPE_MENU);
743 
744     /**
745      * GtkLabel::activate-current-link:
746      * @label: The label on which the signal was emitted
747      *
748      * A [keybinding signal][GtkBindingSignal]
749      * which gets emitted when the user activates a link in the label.
750      *
751      * Applications may also emit the signal with g_signal_emit_by_name()
752      * if they need to control activation of URIs programmatically.
753      *
754      * The default bindings for this signal are all forms of the Enter key.
755      *
756      * Since: 2.18
757      */
758     signals[ACTIVATE_CURRENT_LINK] =
759       g_signal_new_class_handler (I_("activate-current-link"),
760                                   G_TYPE_FROM_CLASS (gobject_class),
761                                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
762                                   G_CALLBACK (gtk_label_activate_current_link),
763                                   NULL, NULL,
764                                   NULL,
765                                   G_TYPE_NONE, 0);
766 
767     /**
768      * GtkLabel::activate-link:
769      * @label: The label on which the signal was emitted
770      * @uri: the URI that is activated
771      *
772      * The signal which gets emitted to activate a URI.
773      * Applications may connect to it to override the default behaviour,
774      * which is to call gtk_show_uri_on_window().
775      *
776      * Returns: %TRUE if the link has been activated
777      *
778      * Since: 2.18
779      */
780     signals[ACTIVATE_LINK] =
781       g_signal_new (I_("activate-link"),
782                     G_TYPE_FROM_CLASS (gobject_class),
783                     G_SIGNAL_RUN_LAST,
784                     G_STRUCT_OFFSET (GtkLabelClass, activate_link),
785                     _gtk_boolean_handled_accumulator, NULL,
786                     _gtk_marshal_BOOLEAN__STRING,
787                     G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
788 
789   /**
790    * GtkLabel:label:
791    *
792    * The contents of the label.
793    *
794    * If the string contains [Pango XML markup][PangoMarkupFormat], you will
795    * have to set the #GtkLabel:use-markup property to %TRUE in order for the
796    * label to display the markup attributes. See also gtk_label_set_markup()
797    * for a convenience function that sets both this property and the
798    * #GtkLabel:use-markup property at the same time.
799    *
800    * If the string contains underlines acting as mnemonics, you will have to
801    * set the #GtkLabel:use-underline property to %TRUE in order for the label
802    * to display them.
803    */
804   label_props[PROP_LABEL] =
805       g_param_spec_string ("label",
806                            P_("Label"),
807                            P_("The text of the label"),
808                            "",
809                            GTK_PARAM_READWRITE);
810 
811   label_props[PROP_ATTRIBUTES] =
812       g_param_spec_boxed ("attributes",
813                           P_("Attributes"),
814                           P_("A list of style attributes to apply to the text of the label"),
815                           PANGO_TYPE_ATTR_LIST,
816                           GTK_PARAM_READWRITE);
817 
818   label_props[PROP_USE_MARKUP] =
819       g_param_spec_boolean ("use-markup",
820                             P_("Use markup"),
821                             P_("The text of the label includes XML markup. See pango_parse_markup()"),
822                             FALSE,
823                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
824 
825   label_props[PROP_USE_UNDERLINE] =
826       g_param_spec_boolean ("use-underline",
827                             P_("Use underline"),
828                             P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
829                             FALSE,
830                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
831 
832   label_props[PROP_JUSTIFY] =
833       g_param_spec_enum ("justify",
834                          P_("Justification"),
835                          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"),
836                          GTK_TYPE_JUSTIFICATION,
837                          GTK_JUSTIFY_LEFT,
838                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
839 
840   /**
841    * GtkLabel:xalign:
842    *
843    * The xalign property determines the horizontal aligment of the label text
844    * inside the labels size allocation. Compare this to #GtkWidget:halign,
845    * which determines how the labels size allocation is positioned in the
846    * space available for the label.
847    *
848    * Since: 3.16
849    */
850   label_props[PROP_XALIGN] =
851       g_param_spec_float ("xalign",
852                           P_("X align"),
853                           P_("The horizontal alignment, from 0 (left) to 1 (right). Reversed for RTL layouts."),
854                           0.0, 1.0,
855                           0.5,
856                           GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
857 
858   /**
859    * GtkLabel:yalign:
860    *
861    * The yalign property determines the vertical aligment of the label text
862    * inside the labels size allocation. Compare this to #GtkWidget:valign,
863    * which determines how the labels size allocation is positioned in the
864    * space available for the label.
865    *
866    * Since: 3.16
867    */
868   label_props[PROP_YALIGN] =
869       g_param_spec_float ("yalign",
870                           P_("Y align"),
871                           P_("The vertical alignment, from 0 (top) to 1 (bottom)"),
872                           0.0, 1.0,
873                           0.5,
874                           GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
875 
876   label_props[PROP_PATTERN] =
877       g_param_spec_string ("pattern",
878                            P_("Pattern"),
879                            P_("A string with _ characters in positions correspond to characters in the text to underline"),
880                            NULL,
881                            GTK_PARAM_WRITABLE);
882 
883   label_props[PROP_WRAP] =
884       g_param_spec_boolean ("wrap",
885                             P_("Line wrap"),
886                             P_("If set, wrap lines if the text becomes too wide"),
887                             FALSE,
888                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
889 
890   /**
891    * GtkLabel:wrap-mode:
892    *
893    * If line wrapping is on (see the #GtkLabel:wrap property) this controls
894    * how the line wrapping is done. The default is %PANGO_WRAP_WORD, which
895    * means wrap on word boundaries.
896    *
897    * Since: 2.10
898    */
899   label_props[PROP_WRAP_MODE] =
900       g_param_spec_enum ("wrap-mode",
901                          P_("Line wrap mode"),
902                          P_("If wrap is set, controls how linewrapping is done"),
903                          PANGO_TYPE_WRAP_MODE,
904                          PANGO_WRAP_WORD,
905                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
906 
907   label_props[PROP_SELECTABLE] =
908       g_param_spec_boolean ("selectable",
909                             P_("Selectable"),
910                             P_("Whether the label text can be selected with the mouse"),
911                             FALSE,
912                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
913 
914   label_props[PROP_MNEMONIC_KEYVAL] =
915       g_param_spec_uint ("mnemonic-keyval",
916                          P_("Mnemonic key"),
917                          P_("The mnemonic accelerator key for this label"),
918                          0, G_MAXUINT,
919                          GDK_KEY_VoidSymbol,
920                          GTK_PARAM_READABLE);
921 
922   label_props[PROP_MNEMONIC_WIDGET] =
923       g_param_spec_object ("mnemonic-widget",
924                            P_("Mnemonic widget"),
925                            P_("The widget to be activated when the label's mnemonic key is pressed"),
926                            GTK_TYPE_WIDGET,
927                            GTK_PARAM_READWRITE);
928 
929   label_props[PROP_CURSOR_POSITION] =
930       g_param_spec_int ("cursor-position",
931                         P_("Cursor Position"),
932                         P_("The current position of the insertion cursor in chars"),
933                         0, G_MAXINT,
934                         0,
935                         GTK_PARAM_READABLE);
936 
937   label_props[PROP_SELECTION_BOUND] =
938       g_param_spec_int ("selection-bound",
939                         P_("Selection Bound"),
940                         P_("The position of the opposite end of the selection from the cursor in chars"),
941                         0, G_MAXINT,
942                         0,
943                         GTK_PARAM_READABLE);
944 
945   /**
946    * GtkLabel:ellipsize:
947    *
948    * The preferred place to ellipsize the string, if the label does
949    * not have enough room to display the entire string, specified as a
950    * #PangoEllipsizeMode.
951    *
952    * Note that setting this property to a value other than
953    * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests
954    * only enough space to display the ellipsis "...". In particular, this
955    * means that ellipsizing labels do not work well in notebook tabs, unless
956    * the #GtkNotebook tab-expand child property is set to %TRUE. Other ways
957    * to set a label's width are gtk_widget_set_size_request() and
958    * gtk_label_set_width_chars().
959    *
960    * Since: 2.6
961    */
962   label_props[PROP_ELLIPSIZE] =
963       g_param_spec_enum ("ellipsize",
964                          P_("Ellipsize"),
965                          P_("The preferred place to ellipsize the string, if the label does not have enough room to display the entire string"),
966                          PANGO_TYPE_ELLIPSIZE_MODE,
967                          PANGO_ELLIPSIZE_NONE,
968                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
969 
970   /**
971    * GtkLabel:width-chars:
972    *
973    * The desired width of the label, in characters. If this property is set to
974    * -1, the width will be calculated automatically.
975    *
976    * See the section on [text layout][label-text-layout]
977    * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
978    * determine the width of ellipsized and wrapped labels.
979    *
980    * Since: 2.6
981    **/
982   label_props[PROP_WIDTH_CHARS] =
983       g_param_spec_int ("width-chars",
984                         P_("Width In Characters"),
985                         P_("The desired width of the label, in characters"),
986                         -1, G_MAXINT,
987                         -1,
988                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
989 
990   /**
991    * GtkLabel:single-line-mode:
992    *
993    * Whether the label is in single line mode. In single line mode,
994    * the height of the label does not depend on the actual text, it
995    * is always set to ascent + descent of the font. This can be an
996    * advantage in situations where resizing the label because of text
997    * changes would be distracting, e.g. in a statusbar.
998    *
999    * Since: 2.6
1000    **/
1001   label_props[PROP_SINGLE_LINE_MODE] =
1002       g_param_spec_boolean ("single-line-mode",
1003                             P_("Single Line Mode"),
1004                             P_("Whether the label is in single line mode"),
1005                             FALSE,
1006                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1007 
1008   /**
1009    * GtkLabel:angle:
1010    *
1011    * The angle that the baseline of the label makes with the horizontal,
1012    * in degrees, measured counterclockwise. An angle of 90 reads from
1013    * from bottom to top, an angle of 270, from top to bottom. Ignored
1014    * if the label is selectable.
1015    *
1016    * Since: 2.6
1017    **/
1018   label_props[PROP_ANGLE] =
1019       g_param_spec_double ("angle",
1020                            P_("Angle"),
1021                            P_("Angle at which the label is rotated"),
1022                            0.0, 360.0,
1023                            0.0,
1024                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1025 
1026   /**
1027    * GtkLabel:max-width-chars:
1028    *
1029    * The desired maximum width of the label, in characters. If this property
1030    * is set to -1, the width will be calculated automatically.
1031    *
1032    * See the section on [text layout][label-text-layout]
1033    * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
1034    * determine the width of ellipsized and wrapped labels.
1035    *
1036    * Since: 2.6
1037    **/
1038   label_props[PROP_MAX_WIDTH_CHARS] =
1039       g_param_spec_int ("max-width-chars",
1040                         P_("Maximum Width In Characters"),
1041                         P_("The desired maximum width of the label, in characters"),
1042                         -1, G_MAXINT,
1043                         -1,
1044                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1045 
1046   /**
1047    * GtkLabel:track-visited-links:
1048    *
1049    * Set this property to %TRUE to make the label track which links
1050    * have been visited. It will then apply the #GTK_STATE_FLAG_VISITED
1051    * when rendering this link, in addition to #GTK_STATE_FLAG_LINK.
1052    *
1053    * Since: 2.18
1054    */
1055   label_props[PROP_TRACK_VISITED_LINKS] =
1056       g_param_spec_boolean ("track-visited-links",
1057                             P_("Track visited links"),
1058                             P_("Whether visited links should be tracked"),
1059                             TRUE,
1060                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1061 
1062   /**
1063    * GtkLabel:lines:
1064    *
1065    * The number of lines to which an ellipsized, wrapping label
1066    * should be limited. This property has no effect if the
1067    * label is not wrapping or ellipsized. Set this property to
1068    * -1 if you don't want to limit the number of lines.
1069    *
1070    * Since: 3.10
1071    */
1072   label_props[PROP_LINES] =
1073       g_param_spec_int ("lines",
1074                         P_("Number of lines"),
1075                         P_("The desired number of lines, when ellipsizing a wrapping label"),
1076                         -1, G_MAXINT,
1077                         -1,
1078                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1079 
1080   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, label_props);
1081 
1082   /*
1083    * Key bindings
1084    */
1085   binding_set = gtk_binding_set_by_class (class);
1086 
1087   /* Moving the insertion point */
1088   add_move_binding (binding_set, GDK_KEY_Right, 0,
1089 		    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1090 
1091   add_move_binding (binding_set, GDK_KEY_Left, 0,
1092 		    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1093 
1094   add_move_binding (binding_set, GDK_KEY_KP_Right, 0,
1095 		    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1096 
1097   add_move_binding (binding_set, GDK_KEY_KP_Left, 0,
1098 		    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1099 
1100   add_move_binding (binding_set, GDK_KEY_f, GDK_CONTROL_MASK,
1101 		    GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
1102 
1103   add_move_binding (binding_set, GDK_KEY_b, GDK_CONTROL_MASK,
1104 		    GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
1105 
1106   add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
1107 		    GTK_MOVEMENT_WORDS, 1);
1108 
1109   add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
1110 		    GTK_MOVEMENT_WORDS, -1);
1111 
1112   add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
1113 		    GTK_MOVEMENT_WORDS, 1);
1114 
1115   add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
1116 		    GTK_MOVEMENT_WORDS, -1);
1117 
1118   /* select all */
1119   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
1120 				"move-cursor", 3,
1121 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1122 				G_TYPE_INT, -1,
1123 				G_TYPE_BOOLEAN, FALSE);
1124 
1125   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
1126 				"move-cursor", 3,
1127 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1128 				G_TYPE_INT, 1,
1129 				G_TYPE_BOOLEAN, TRUE);
1130 
1131   gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
1132 				"move-cursor", 3,
1133 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1134 				G_TYPE_INT, -1,
1135 				G_TYPE_BOOLEAN, FALSE);
1136 
1137   gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
1138 				"move-cursor", 3,
1139 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1140 				G_TYPE_INT, 1,
1141 				G_TYPE_BOOLEAN, TRUE);
1142 
1143   /* unselect all */
1144   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1145 				"move-cursor", 3,
1146 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1147 				G_TYPE_INT, 0,
1148 				G_TYPE_BOOLEAN, FALSE);
1149 
1150   gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK,
1151 				"move-cursor", 3,
1152 				G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
1153 				G_TYPE_INT, 0,
1154 				G_TYPE_BOOLEAN, FALSE);
1155 
1156   add_move_binding (binding_set, GDK_KEY_f, GDK_MOD1_MASK,
1157 		    GTK_MOVEMENT_WORDS, 1);
1158 
1159   add_move_binding (binding_set, GDK_KEY_b, GDK_MOD1_MASK,
1160 		    GTK_MOVEMENT_WORDS, -1);
1161 
1162   add_move_binding (binding_set, GDK_KEY_Home, 0,
1163 		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1164 
1165   add_move_binding (binding_set, GDK_KEY_End, 0,
1166 		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1167 
1168   add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
1169 		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1170 
1171   add_move_binding (binding_set, GDK_KEY_KP_End, 0,
1172 		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1173 
1174   add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
1175 		    GTK_MOVEMENT_BUFFER_ENDS, -1);
1176 
1177   add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
1178 		    GTK_MOVEMENT_BUFFER_ENDS, 1);
1179 
1180   add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
1181 		    GTK_MOVEMENT_BUFFER_ENDS, -1);
1182 
1183   add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
1184 		    GTK_MOVEMENT_BUFFER_ENDS, 1);
1185 
1186   /* copy */
1187   gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
1188 				"copy-clipboard", 0);
1189 
1190   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
1191 				"activate-current-link", 0);
1192   gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
1193 				"activate-current-link", 0);
1194   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
1195 				"activate-current-link", 0);
1196 
1197   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LABEL_ACCESSIBLE);
1198 
1199   gtk_widget_class_set_css_name (widget_class, "label");
1200 
1201   quark_shortcuts_connected = g_quark_from_static_string ("gtk-label-shortcuts-connected");
1202   quark_mnemonic_menu = g_quark_from_static_string ("gtk-mnemonic-menu");
1203   quark_mnemonics_visible_connected = g_quark_from_static_string ("gtk-label-mnemonics-visible-connected");
1204   quark_gtk_signal = g_quark_from_static_string ("gtk-signal");
1205   quark_link = g_quark_from_static_string ("link");
1206 }
1207 
1208 static void
gtk_label_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1209 gtk_label_set_property (GObject      *object,
1210 			guint         prop_id,
1211 			const GValue *value,
1212 			GParamSpec   *pspec)
1213 {
1214   GtkLabel *label = GTK_LABEL (object);
1215 
1216   switch (prop_id)
1217     {
1218     case PROP_LABEL:
1219       gtk_label_set_label (label, g_value_get_string (value));
1220       break;
1221     case PROP_ATTRIBUTES:
1222       gtk_label_set_attributes (label, g_value_get_boxed (value));
1223       break;
1224     case PROP_USE_MARKUP:
1225       gtk_label_set_use_markup (label, g_value_get_boolean (value));
1226       break;
1227     case PROP_USE_UNDERLINE:
1228       gtk_label_set_use_underline (label, g_value_get_boolean (value));
1229       break;
1230     case PROP_JUSTIFY:
1231       gtk_label_set_justify (label, g_value_get_enum (value));
1232       break;
1233     case PROP_PATTERN:
1234       gtk_label_set_pattern (label, g_value_get_string (value));
1235       break;
1236     case PROP_WRAP:
1237       gtk_label_set_line_wrap (label, g_value_get_boolean (value));
1238       break;
1239     case PROP_WRAP_MODE:
1240       gtk_label_set_line_wrap_mode (label, g_value_get_enum (value));
1241       break;
1242     case PROP_SELECTABLE:
1243       gtk_label_set_selectable (label, g_value_get_boolean (value));
1244       break;
1245     case PROP_MNEMONIC_WIDGET:
1246       gtk_label_set_mnemonic_widget (label, (GtkWidget*) g_value_get_object (value));
1247       break;
1248     case PROP_ELLIPSIZE:
1249       gtk_label_set_ellipsize (label, g_value_get_enum (value));
1250       break;
1251     case PROP_WIDTH_CHARS:
1252       gtk_label_set_width_chars (label, g_value_get_int (value));
1253       break;
1254     case PROP_SINGLE_LINE_MODE:
1255       gtk_label_set_single_line_mode (label, g_value_get_boolean (value));
1256       break;
1257     case PROP_ANGLE:
1258       gtk_label_set_angle (label, g_value_get_double (value));
1259       break;
1260     case PROP_MAX_WIDTH_CHARS:
1261       gtk_label_set_max_width_chars (label, g_value_get_int (value));
1262       break;
1263     case PROP_TRACK_VISITED_LINKS:
1264       gtk_label_set_track_visited_links (label, g_value_get_boolean (value));
1265       break;
1266     case PROP_LINES:
1267       gtk_label_set_lines (label, g_value_get_int (value));
1268       break;
1269     case PROP_XALIGN:
1270       gtk_label_set_xalign (label, g_value_get_float (value));
1271       break;
1272     case PROP_YALIGN:
1273       gtk_label_set_yalign (label, g_value_get_float (value));
1274       break;
1275     default:
1276       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1277       break;
1278     }
1279 }
1280 
1281 static void
gtk_label_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1282 gtk_label_get_property (GObject     *object,
1283 			guint        prop_id,
1284 			GValue      *value,
1285 			GParamSpec  *pspec)
1286 {
1287   GtkLabel *label = GTK_LABEL (object);
1288   GtkLabelPrivate *priv = label->priv;
1289 
1290   switch (prop_id)
1291     {
1292     case PROP_LABEL:
1293       g_value_set_string (value, priv->label);
1294       break;
1295     case PROP_ATTRIBUTES:
1296       g_value_set_boxed (value, priv->attrs);
1297       break;
1298     case PROP_USE_MARKUP:
1299       g_value_set_boolean (value, priv->use_markup);
1300       break;
1301     case PROP_USE_UNDERLINE:
1302       g_value_set_boolean (value, priv->use_underline);
1303       break;
1304     case PROP_JUSTIFY:
1305       g_value_set_enum (value, priv->jtype);
1306       break;
1307     case PROP_WRAP:
1308       g_value_set_boolean (value, priv->wrap);
1309       break;
1310     case PROP_WRAP_MODE:
1311       g_value_set_enum (value, priv->wrap_mode);
1312       break;
1313     case PROP_SELECTABLE:
1314       g_value_set_boolean (value, gtk_label_get_selectable (label));
1315       break;
1316     case PROP_MNEMONIC_KEYVAL:
1317       g_value_set_uint (value, priv->mnemonic_keyval);
1318       break;
1319     case PROP_MNEMONIC_WIDGET:
1320       g_value_set_object (value, (GObject*) priv->mnemonic_widget);
1321       break;
1322     case PROP_CURSOR_POSITION:
1323       g_value_set_int (value, _gtk_label_get_cursor_position (label));
1324       break;
1325     case PROP_SELECTION_BOUND:
1326       g_value_set_int (value, _gtk_label_get_selection_bound (label));
1327       break;
1328     case PROP_ELLIPSIZE:
1329       g_value_set_enum (value, priv->ellipsize);
1330       break;
1331     case PROP_WIDTH_CHARS:
1332       g_value_set_int (value, gtk_label_get_width_chars (label));
1333       break;
1334     case PROP_SINGLE_LINE_MODE:
1335       g_value_set_boolean (value, gtk_label_get_single_line_mode (label));
1336       break;
1337     case PROP_ANGLE:
1338       g_value_set_double (value, gtk_label_get_angle (label));
1339       break;
1340     case PROP_MAX_WIDTH_CHARS:
1341       g_value_set_int (value, gtk_label_get_max_width_chars (label));
1342       break;
1343     case PROP_TRACK_VISITED_LINKS:
1344       g_value_set_boolean (value, gtk_label_get_track_visited_links (label));
1345       break;
1346     case PROP_LINES:
1347       g_value_set_int (value, gtk_label_get_lines (label));
1348       break;
1349     case PROP_XALIGN:
1350       g_value_set_float (value, gtk_label_get_xalign (label));
1351       break;
1352     case PROP_YALIGN:
1353       g_value_set_float (value, gtk_label_get_yalign (label));
1354       break;
1355     default:
1356       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1357       break;
1358     }
1359 }
1360 
1361 static void
gtk_label_init(GtkLabel * label)1362 gtk_label_init (GtkLabel *label)
1363 {
1364   GtkLabelPrivate *priv;
1365 
1366   label->priv = gtk_label_get_instance_private (label);
1367   priv = label->priv;
1368 
1369   gtk_widget_set_has_window (GTK_WIDGET (label), FALSE);
1370 
1371   priv->width_chars = -1;
1372   priv->max_width_chars = -1;
1373   priv->label = g_strdup ("");
1374   priv->lines = -1;
1375 
1376   priv->xalign = 0.5;
1377   priv->yalign = 0.5;
1378 
1379   priv->jtype = GTK_JUSTIFY_LEFT;
1380   priv->wrap = FALSE;
1381   priv->wrap_mode = PANGO_WRAP_WORD;
1382   priv->ellipsize = PANGO_ELLIPSIZE_NONE;
1383 
1384   priv->use_underline = FALSE;
1385   priv->use_markup = FALSE;
1386   priv->pattern_set = FALSE;
1387   priv->track_links = TRUE;
1388 
1389   priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
1390   priv->layout = NULL;
1391   priv->text = g_strdup ("");
1392   priv->attrs = NULL;
1393 
1394   priv->mnemonic_widget = NULL;
1395   priv->mnemonic_window = NULL;
1396 
1397   priv->mnemonics_visible = TRUE;
1398 
1399   priv->gadget = gtk_css_custom_gadget_new_for_node (gtk_widget_get_css_node (GTK_WIDGET (label)),
1400                                                      GTK_WIDGET (label),
1401                                                      gtk_label_measure,
1402                                                      NULL,
1403                                                      gtk_label_render,
1404                                                      NULL,
1405                                                      NULL);
1406 }
1407 
1408 
1409 static void
gtk_label_buildable_interface_init(GtkBuildableIface * iface)1410 gtk_label_buildable_interface_init (GtkBuildableIface *iface)
1411 {
1412   buildable_parent_iface = g_type_interface_peek_parent (iface);
1413 
1414   iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
1415   iface->custom_finished = gtk_label_buildable_custom_finished;
1416 }
1417 
1418 typedef struct {
1419   GtkBuilder    *builder;
1420   GObject       *object;
1421   PangoAttrList *attrs;
1422 } PangoParserData;
1423 
1424 static PangoAttribute *
attribute_from_text(GtkBuilder * builder,const gchar * name,const gchar * value,GError ** error)1425 attribute_from_text (GtkBuilder   *builder,
1426 		     const gchar  *name,
1427 		     const gchar  *value,
1428 		     GError      **error)
1429 {
1430   PangoAttribute *attribute = NULL;
1431   PangoAttrType   type;
1432   PangoLanguage  *language;
1433   PangoFontDescription *font_desc;
1434   GdkColor       *color;
1435   GValue          val = G_VALUE_INIT;
1436 
1437   if (!gtk_builder_value_from_string_type (builder, PANGO_TYPE_ATTR_TYPE, name, &val, error))
1438     return NULL;
1439 
1440   type = g_value_get_enum (&val);
1441   g_value_unset (&val);
1442 
1443   switch (type)
1444     {
1445       /* PangoAttrLanguage */
1446     case PANGO_ATTR_LANGUAGE:
1447       if ((language = pango_language_from_string (value)))
1448 	{
1449 	  attribute = pango_attr_language_new (language);
1450 	  g_value_init (&val, G_TYPE_INT);
1451 	}
1452       break;
1453       /* PangoAttrInt */
1454     case PANGO_ATTR_STYLE:
1455       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STYLE, value, &val, error))
1456 	attribute = pango_attr_style_new (g_value_get_enum (&val));
1457       break;
1458     case PANGO_ATTR_WEIGHT:
1459       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_WEIGHT, value, &val, error))
1460 	attribute = pango_attr_weight_new (g_value_get_enum (&val));
1461       break;
1462     case PANGO_ATTR_VARIANT:
1463       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_VARIANT, value, &val, error))
1464 	attribute = pango_attr_variant_new (g_value_get_enum (&val));
1465       break;
1466     case PANGO_ATTR_STRETCH:
1467       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_STRETCH, value, &val, error))
1468 	attribute = pango_attr_stretch_new (g_value_get_enum (&val));
1469       break;
1470     case PANGO_ATTR_UNDERLINE:
1471       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_UNDERLINE, value, &val, NULL))
1472 	attribute = pango_attr_underline_new (g_value_get_enum (&val));
1473       else
1474         {
1475           /* XXX: allow boolean for backwards compat, so ignore error */
1476           /* Deprecate this somehow */
1477           g_value_unset (&val);
1478           if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1479             attribute = pango_attr_underline_new (g_value_get_boolean (&val));
1480         }
1481       break;
1482     case PANGO_ATTR_STRIKETHROUGH:
1483       if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1484 	attribute = pango_attr_strikethrough_new (g_value_get_boolean (&val));
1485       break;
1486     case PANGO_ATTR_GRAVITY:
1487       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY, value, &val, error))
1488 	attribute = pango_attr_gravity_new (g_value_get_enum (&val));
1489       break;
1490     case PANGO_ATTR_GRAVITY_HINT:
1491       if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_GRAVITY_HINT, value, &val, error))
1492 	attribute = pango_attr_gravity_hint_new (g_value_get_enum (&val));
1493       break;
1494       /* PangoAttrString */
1495     case PANGO_ATTR_FAMILY:
1496       attribute = pango_attr_family_new (value);
1497       g_value_init (&val, G_TYPE_INT);
1498       break;
1499 
1500       /* PangoAttrSize */
1501     case PANGO_ATTR_SIZE:
1502       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error))
1503 	attribute = pango_attr_size_new (g_value_get_int (&val));
1504       break;
1505     case PANGO_ATTR_ABSOLUTE_SIZE:
1506       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error))
1507 	attribute = pango_attr_size_new_absolute (g_value_get_int (&val));
1508       break;
1509 
1510       /* PangoAttrFontDesc */
1511     case PANGO_ATTR_FONT_DESC:
1512       if ((font_desc = pango_font_description_from_string (value)))
1513 	{
1514 	  attribute = pango_attr_font_desc_new (font_desc);
1515 	  pango_font_description_free (font_desc);
1516 	  g_value_init (&val, G_TYPE_INT);
1517 	}
1518       break;
1519 
1520 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1521 
1522       /* PangoAttrColor */
1523     case PANGO_ATTR_FOREGROUND:
1524       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, value, &val, error))
1525 	{
1526 	  color = g_value_get_boxed (&val);
1527 	  attribute = pango_attr_foreground_new (color->red, color->green, color->blue);
1528 	}
1529       break;
1530     case PANGO_ATTR_BACKGROUND:
1531       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, value, &val, error))
1532 	{
1533 	  color = g_value_get_boxed (&val);
1534 	  attribute = pango_attr_background_new (color->red, color->green, color->blue);
1535 	}
1536       break;
1537     case PANGO_ATTR_UNDERLINE_COLOR:
1538       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, value, &val, error))
1539 	{
1540 	  color = g_value_get_boxed (&val);
1541 	  attribute = pango_attr_underline_color_new (color->red, color->green, color->blue);
1542 	}
1543       break;
1544     case PANGO_ATTR_STRIKETHROUGH_COLOR:
1545       if (gtk_builder_value_from_string_type (builder, GDK_TYPE_COLOR, value, &val, error))
1546 	{
1547 	  color = g_value_get_boxed (&val);
1548 	  attribute = pango_attr_strikethrough_color_new (color->red, color->green, color->blue);
1549 	}
1550       break;
1551 
1552 G_GNUC_END_IGNORE_DEPRECATIONS
1553 
1554       /* PangoAttrShape */
1555     case PANGO_ATTR_SHAPE:
1556       /* Unsupported for now */
1557       break;
1558       /* PangoAttrFloat */
1559     case PANGO_ATTR_SCALE:
1560       if (gtk_builder_value_from_string_type (builder, G_TYPE_DOUBLE, value, &val, error))
1561 	attribute = pango_attr_scale_new (g_value_get_double (&val));
1562       break;
1563     case PANGO_ATTR_LETTER_SPACING:
1564       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error))
1565         attribute = pango_attr_letter_spacing_new (g_value_get_int (&val));
1566       break;
1567     case PANGO_ATTR_RISE:
1568       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error))
1569         attribute = pango_attr_rise_new (g_value_get_int (&val));
1570       break;
1571     case PANGO_ATTR_FALLBACK:
1572       if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
1573         attribute = pango_attr_fallback_new (g_value_get_boolean (&val));
1574       break;
1575     case PANGO_ATTR_FONT_FEATURES:
1576       attribute = pango_attr_font_features_new (value);
1577       break;
1578     case PANGO_ATTR_FOREGROUND_ALPHA:
1579       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error))
1580         attribute = pango_attr_foreground_alpha_new ((guint16)g_value_get_int (&val));
1581       break;
1582     case PANGO_ATTR_BACKGROUND_ALPHA:
1583       if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, value, &val, error))
1584         attribute = pango_attr_background_alpha_new ((guint16)g_value_get_int (&val));
1585       break;
1586     case PANGO_ATTR_INVALID:
1587     default:
1588       break;
1589     }
1590 
1591   g_value_unset (&val);
1592 
1593   return attribute;
1594 }
1595 
1596 
1597 static void
pango_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** names,const gchar ** values,gpointer user_data,GError ** error)1598 pango_start_element (GMarkupParseContext *context,
1599 		     const gchar         *element_name,
1600 		     const gchar        **names,
1601 		     const gchar        **values,
1602 		     gpointer             user_data,
1603 		     GError             **error)
1604 {
1605   PangoParserData *data = (PangoParserData*)user_data;
1606 
1607   if (strcmp (element_name, "attribute") == 0)
1608     {
1609       PangoAttribute *attr = NULL;
1610       const gchar *name = NULL;
1611       const gchar *value = NULL;
1612       const gchar *start = NULL;
1613       const gchar *end = NULL;
1614       guint start_val = 0;
1615       guint end_val = G_MAXUINT;
1616       GValue val = G_VALUE_INIT;
1617 
1618       if (!_gtk_builder_check_parent (data->builder, context, "attributes", error))
1619         return;
1620 
1621       if (!g_markup_collect_attributes (element_name, names, values, error,
1622                                         G_MARKUP_COLLECT_STRING, "name", &name,
1623                                         G_MARKUP_COLLECT_STRING, "value", &value,
1624                                         G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "start", &start,
1625                                         G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "end", &end,
1626                                         G_MARKUP_COLLECT_INVALID))
1627         {
1628           _gtk_builder_prefix_error (data->builder, context, error);
1629           return;
1630         }
1631 
1632       if (start)
1633         {
1634           if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT, start, &val, error))
1635             {
1636               _gtk_builder_prefix_error (data->builder, context, error);
1637               return;
1638             }
1639           start_val = g_value_get_uint (&val);
1640           g_value_unset (&val);
1641         }
1642 
1643       if (end)
1644         {
1645           if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_UINT, end, &val, error))
1646             {
1647               _gtk_builder_prefix_error (data->builder, context, error);
1648               return;
1649             }
1650           end_val = g_value_get_uint (&val);
1651           g_value_unset (&val);
1652         }
1653 
1654       attr = attribute_from_text (data->builder, name, value, error);
1655       if (!attr)
1656         {
1657           _gtk_builder_prefix_error (data->builder, context, error);
1658           return;
1659         }
1660 
1661       attr->start_index = start_val;
1662       attr->end_index = end_val;
1663 
1664       if (!data->attrs)
1665         data->attrs = pango_attr_list_new ();
1666 
1667       pango_attr_list_insert (data->attrs, attr);
1668     }
1669   else if (strcmp (element_name, "attributes") == 0)
1670     {
1671       if (!_gtk_builder_check_parent (data->builder, context, "object", error))
1672         return;
1673 
1674       if (!g_markup_collect_attributes (element_name, names, values, error,
1675                                         G_MARKUP_COLLECT_INVALID, NULL, NULL,
1676                                         G_MARKUP_COLLECT_INVALID))
1677         _gtk_builder_prefix_error (data->builder, context, error);
1678     }
1679   else
1680     {
1681       _gtk_builder_error_unhandled_tag (data->builder, context,
1682                                         "GtkContainer", element_name,
1683                                         error);
1684     }
1685 }
1686 
1687 static const GMarkupParser pango_parser =
1688   {
1689     pango_start_element,
1690   };
1691 
1692 static gboolean
gtk_label_buildable_custom_tag_start(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,GMarkupParser * parser,gpointer * data)1693 gtk_label_buildable_custom_tag_start (GtkBuildable     *buildable,
1694 				      GtkBuilder       *builder,
1695 				      GObject          *child,
1696 				      const gchar      *tagname,
1697 				      GMarkupParser    *parser,
1698 				      gpointer         *data)
1699 {
1700   if (buildable_parent_iface->custom_tag_start (buildable, builder, child,
1701 						tagname, parser, data))
1702     return TRUE;
1703 
1704   if (strcmp (tagname, "attributes") == 0)
1705     {
1706       PangoParserData *parser_data;
1707 
1708       parser_data = g_slice_new0 (PangoParserData);
1709       parser_data->builder = g_object_ref (builder);
1710       parser_data->object = G_OBJECT (g_object_ref (buildable));
1711       *parser = pango_parser;
1712       *data = parser_data;
1713       return TRUE;
1714     }
1715   return FALSE;
1716 }
1717 
1718 static void
gtk_label_buildable_custom_finished(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,gpointer user_data)1719 gtk_label_buildable_custom_finished (GtkBuildable *buildable,
1720 				     GtkBuilder   *builder,
1721 				     GObject      *child,
1722 				     const gchar  *tagname,
1723 				     gpointer      user_data)
1724 {
1725   PangoParserData *data;
1726 
1727   buildable_parent_iface->custom_finished (buildable, builder, child,
1728 					   tagname, user_data);
1729 
1730   if (strcmp (tagname, "attributes") == 0)
1731     {
1732       data = (PangoParserData*)user_data;
1733 
1734       if (data->attrs)
1735 	{
1736 	  gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
1737 	  pango_attr_list_unref (data->attrs);
1738 	}
1739 
1740       g_object_unref (data->object);
1741       g_object_unref (data->builder);
1742       g_slice_free (PangoParserData, data);
1743     }
1744 }
1745 
1746 
1747 /**
1748  * gtk_label_new:
1749  * @str: (nullable): The text of the label
1750  *
1751  * Creates a new label with the given text inside it. You can
1752  * pass %NULL to get an empty label widget.
1753  *
1754  * Returns: the new #GtkLabel
1755  **/
1756 GtkWidget*
gtk_label_new(const gchar * str)1757 gtk_label_new (const gchar *str)
1758 {
1759   GtkLabel *label;
1760 
1761   label = g_object_new (GTK_TYPE_LABEL, NULL);
1762 
1763   if (str && *str)
1764     gtk_label_set_text (label, str);
1765 
1766   return GTK_WIDGET (label);
1767 }
1768 
1769 /**
1770  * gtk_label_new_with_mnemonic:
1771  * @str: (nullable): The text of the label, with an underscore in front of the
1772  *       mnemonic character
1773  *
1774  * Creates a new #GtkLabel, containing the text in @str.
1775  *
1776  * If characters in @str are preceded by an underscore, they are
1777  * underlined. If you need a literal underscore character in a label, use
1778  * '__' (two underscores). The first underlined character represents a
1779  * keyboard accelerator called a mnemonic. The mnemonic key can be used
1780  * to activate another widget, chosen automatically, or explicitly using
1781  * gtk_label_set_mnemonic_widget().
1782  *
1783  * If gtk_label_set_mnemonic_widget() is not called, then the first
1784  * activatable ancestor of the #GtkLabel will be chosen as the mnemonic
1785  * widget. For instance, if the label is inside a button or menu item,
1786  * the button or menu item will automatically become the mnemonic widget
1787  * and be activated by the mnemonic.
1788  *
1789  * Returns: the new #GtkLabel
1790  **/
1791 GtkWidget*
gtk_label_new_with_mnemonic(const gchar * str)1792 gtk_label_new_with_mnemonic (const gchar *str)
1793 {
1794   GtkLabel *label;
1795 
1796   label = g_object_new (GTK_TYPE_LABEL, NULL);
1797 
1798   if (str && *str)
1799     gtk_label_set_text_with_mnemonic (label, str);
1800 
1801   return GTK_WIDGET (label);
1802 }
1803 
1804 static gboolean
gtk_label_mnemonic_activate(GtkWidget * widget,gboolean group_cycling)1805 gtk_label_mnemonic_activate (GtkWidget *widget,
1806 			     gboolean   group_cycling)
1807 {
1808   GtkLabel *label = GTK_LABEL (widget);
1809   GtkLabelPrivate *priv = label->priv;
1810   GtkWidget *parent;
1811 
1812   if (priv->mnemonic_widget)
1813     return gtk_widget_mnemonic_activate (priv->mnemonic_widget, group_cycling);
1814 
1815   /* Try to find the widget to activate by traversing the
1816    * widget's ancestry.
1817    */
1818   parent = gtk_widget_get_parent (widget);
1819 
1820   if (GTK_IS_NOTEBOOK (parent))
1821     return FALSE;
1822 
1823   while (parent)
1824     {
1825       if (gtk_widget_get_can_focus (parent) ||
1826 	  (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) ||
1827           GTK_IS_NOTEBOOK (gtk_widget_get_parent (parent)) ||
1828 	  GTK_IS_MENU_ITEM (parent))
1829 	return gtk_widget_mnemonic_activate (parent, group_cycling);
1830       parent = gtk_widget_get_parent (parent);
1831     }
1832 
1833   /* barf if there was nothing to activate */
1834   g_warning ("Couldn't find a target for a mnemonic activation.");
1835   gtk_widget_error_bell (widget);
1836 
1837   return FALSE;
1838 }
1839 
1840 static void
gtk_label_setup_mnemonic(GtkLabel * label,guint last_key)1841 gtk_label_setup_mnemonic (GtkLabel *label,
1842 			  guint     last_key)
1843 {
1844   GtkLabelPrivate *priv = label->priv;
1845   GtkWidget *widget = GTK_WIDGET (label);
1846   GtkWidget *toplevel;
1847   GtkWidget *mnemonic_menu;
1848 
1849   mnemonic_menu = g_object_get_qdata (G_OBJECT (label), quark_mnemonic_menu);
1850 
1851   if (last_key != GDK_KEY_VoidSymbol)
1852     {
1853       if (priv->mnemonic_window)
1854 	{
1855 	  gtk_window_remove_mnemonic  (priv->mnemonic_window,
1856 				       last_key,
1857 				       widget);
1858 	  priv->mnemonic_window = NULL;
1859 	}
1860       if (mnemonic_menu)
1861 	{
1862 	  _gtk_menu_shell_remove_mnemonic (GTK_MENU_SHELL (mnemonic_menu),
1863 					   last_key,
1864 					   widget);
1865 	  mnemonic_menu = NULL;
1866 	}
1867     }
1868 
1869   if (priv->mnemonic_keyval == GDK_KEY_VoidSymbol)
1870       goto done;
1871 
1872   connect_mnemonics_visible_notify (GTK_LABEL (widget));
1873 
1874   toplevel = gtk_widget_get_toplevel (widget);
1875   if (gtk_widget_is_toplevel (toplevel))
1876     {
1877       GtkWidget *menu_shell;
1878 
1879       menu_shell = gtk_widget_get_ancestor (widget,
1880 					    GTK_TYPE_MENU_SHELL);
1881 
1882       if (menu_shell)
1883 	{
1884 	  _gtk_menu_shell_add_mnemonic (GTK_MENU_SHELL (menu_shell),
1885 					priv->mnemonic_keyval,
1886 					widget);
1887 	  mnemonic_menu = menu_shell;
1888 	}
1889 
1890       if (!GTK_IS_MENU (menu_shell))
1891 	{
1892 	  gtk_window_add_mnemonic (GTK_WINDOW (toplevel),
1893 				   priv->mnemonic_keyval,
1894 				   widget);
1895 	  priv->mnemonic_window = GTK_WINDOW (toplevel);
1896 	}
1897     }
1898 
1899  done:
1900   g_object_set_qdata (G_OBJECT (label), quark_mnemonic_menu, mnemonic_menu);
1901 }
1902 
1903 static void
gtk_label_hierarchy_changed(GtkWidget * widget,GtkWidget * old_toplevel)1904 gtk_label_hierarchy_changed (GtkWidget *widget,
1905 			     GtkWidget *old_toplevel)
1906 {
1907   GtkLabel *label = GTK_LABEL (widget);
1908   GtkLabelPrivate *priv = label->priv;
1909 
1910   gtk_label_setup_mnemonic (label, priv->mnemonic_keyval);
1911 }
1912 
1913 static void
label_shortcut_setting_apply(GtkLabel * label)1914 label_shortcut_setting_apply (GtkLabel *label)
1915 {
1916   gtk_label_recalculate (label);
1917   if (GTK_IS_ACCEL_LABEL (label))
1918     gtk_accel_label_refetch (GTK_ACCEL_LABEL (label));
1919 }
1920 
1921 static void
label_shortcut_setting_traverse_container(GtkWidget * widget,gpointer data)1922 label_shortcut_setting_traverse_container (GtkWidget *widget,
1923                                            gpointer   data)
1924 {
1925   if (GTK_IS_LABEL (widget))
1926     label_shortcut_setting_apply (GTK_LABEL (widget));
1927   else if (GTK_IS_CONTAINER (widget))
1928     gtk_container_forall (GTK_CONTAINER (widget),
1929                           label_shortcut_setting_traverse_container, data);
1930 }
1931 
1932 static void
label_shortcut_setting_changed(GtkSettings * settings)1933 label_shortcut_setting_changed (GtkSettings *settings)
1934 {
1935   GList *list, *l;
1936 
1937   list = gtk_window_list_toplevels ();
1938 
1939   for (l = list; l ; l = l->next)
1940     {
1941       GtkWidget *widget = l->data;
1942 
1943       if (gtk_widget_get_settings (widget) == settings)
1944         gtk_container_forall (GTK_CONTAINER (widget),
1945                               label_shortcut_setting_traverse_container, NULL);
1946     }
1947 
1948   g_list_free (list);
1949 }
1950 
1951 static void
mnemonics_visible_apply(GtkWidget * widget,gboolean mnemonics_visible)1952 mnemonics_visible_apply (GtkWidget *widget,
1953                          gboolean   mnemonics_visible)
1954 {
1955   GtkLabel *label = GTK_LABEL (widget);
1956   GtkLabelPrivate *priv = label->priv;
1957 
1958   mnemonics_visible = mnemonics_visible != FALSE;
1959 
1960   if (priv->mnemonics_visible != mnemonics_visible)
1961     {
1962       priv->mnemonics_visible = mnemonics_visible;
1963 
1964       gtk_label_recalculate (label);
1965     }
1966 }
1967 
1968 static void
label_mnemonics_visible_traverse_container(GtkWidget * widget,gpointer data)1969 label_mnemonics_visible_traverse_container (GtkWidget *widget,
1970                                             gpointer   data)
1971 {
1972   gboolean mnemonics_visible = GPOINTER_TO_INT (data);
1973 
1974   _gtk_label_mnemonics_visible_apply_recursively (widget, mnemonics_visible);
1975 }
1976 
1977 void
_gtk_label_mnemonics_visible_apply_recursively(GtkWidget * widget,gboolean mnemonics_visible)1978 _gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
1979                                                 gboolean   mnemonics_visible)
1980 {
1981   if (GTK_IS_LABEL (widget))
1982     mnemonics_visible_apply (widget, mnemonics_visible);
1983   else if (GTK_IS_CONTAINER (widget))
1984     gtk_container_forall (GTK_CONTAINER (widget),
1985                           label_mnemonics_visible_traverse_container,
1986                           GINT_TO_POINTER (mnemonics_visible));
1987 }
1988 
1989 static void
label_mnemonics_visible_changed(GtkWindow * window,GParamSpec * pspec,gpointer data)1990 label_mnemonics_visible_changed (GtkWindow  *window,
1991                                  GParamSpec *pspec,
1992                                  gpointer    data)
1993 {
1994   gboolean mnemonics_visible;
1995 
1996   g_object_get (window, "mnemonics-visible", &mnemonics_visible, NULL);
1997 
1998   gtk_container_forall (GTK_CONTAINER (window),
1999                         label_mnemonics_visible_traverse_container,
2000                         GINT_TO_POINTER (mnemonics_visible));
2001 }
2002 
2003 static void
gtk_label_screen_changed(GtkWidget * widget,GdkScreen * old_screen)2004 gtk_label_screen_changed (GtkWidget *widget,
2005 			  GdkScreen *old_screen)
2006 {
2007   GtkSettings *settings;
2008   gboolean shortcuts_connected;
2009 
2010   /* The PangoContext is replaced when the screen changes, so clear the layouts */
2011   gtk_label_clear_layout (GTK_LABEL (widget));
2012 
2013   if (!gtk_widget_has_screen (widget))
2014     return;
2015 
2016   settings = gtk_widget_get_settings (widget);
2017 
2018   shortcuts_connected =
2019     GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (settings), quark_shortcuts_connected));
2020 
2021   if (! shortcuts_connected)
2022     {
2023       g_signal_connect (settings, "notify::gtk-enable-mnemonics",
2024                         G_CALLBACK (label_shortcut_setting_changed),
2025                         NULL);
2026       g_signal_connect (settings, "notify::gtk-enable-accels",
2027                         G_CALLBACK (label_shortcut_setting_changed),
2028                         NULL);
2029 
2030       g_object_set_qdata (G_OBJECT (settings), quark_shortcuts_connected,
2031                          GINT_TO_POINTER (TRUE));
2032     }
2033 
2034   label_shortcut_setting_apply (GTK_LABEL (widget));
2035 }
2036 
2037 
2038 static void
label_mnemonic_widget_weak_notify(gpointer data,GObject * where_the_object_was)2039 label_mnemonic_widget_weak_notify (gpointer      data,
2040 				   GObject      *where_the_object_was)
2041 {
2042   GtkLabel *label = data;
2043   GtkLabelPrivate *priv = label->priv;
2044 
2045   priv->mnemonic_widget = NULL;
2046   g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_MNEMONIC_WIDGET]);
2047 }
2048 
2049 /**
2050  * gtk_label_set_mnemonic_widget:
2051  * @label: a #GtkLabel
2052  * @widget: (nullable): the target #GtkWidget, or %NULL to unset
2053  *
2054  * If the label has been set so that it has an mnemonic key (using
2055  * i.e. gtk_label_set_markup_with_mnemonic(),
2056  * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
2057  * or the “use_underline” property) the label can be associated with a
2058  * widget that is the target of the mnemonic. When the label is inside
2059  * a widget (like a #GtkButton or a #GtkNotebook tab) it is
2060  * automatically associated with the correct widget, but sometimes
2061  * (i.e. when the target is a #GtkEntry next to the label) you need to
2062  * set it explicitly using this function.
2063  *
2064  * The target widget will be accelerated by emitting the
2065  * GtkWidget::mnemonic-activate signal on it. The default handler for
2066  * this signal will activate the widget if there are no mnemonic collisions
2067  * and toggle focus between the colliding widgets otherwise.
2068  **/
2069 void
gtk_label_set_mnemonic_widget(GtkLabel * label,GtkWidget * widget)2070 gtk_label_set_mnemonic_widget (GtkLabel  *label,
2071 			       GtkWidget *widget)
2072 {
2073   GtkLabelPrivate *priv;
2074 
2075   g_return_if_fail (GTK_IS_LABEL (label));
2076 
2077   priv = label->priv;
2078 
2079   if (widget)
2080     g_return_if_fail (GTK_IS_WIDGET (widget));
2081 
2082   if (priv->mnemonic_widget)
2083     {
2084       gtk_widget_remove_mnemonic_label (priv->mnemonic_widget, GTK_WIDGET (label));
2085       g_object_weak_unref (G_OBJECT (priv->mnemonic_widget),
2086 			   label_mnemonic_widget_weak_notify,
2087 			   label);
2088     }
2089   priv->mnemonic_widget = widget;
2090   if (priv->mnemonic_widget)
2091     {
2092       g_object_weak_ref (G_OBJECT (priv->mnemonic_widget),
2093 		         label_mnemonic_widget_weak_notify,
2094 		         label);
2095       gtk_widget_add_mnemonic_label (priv->mnemonic_widget, GTK_WIDGET (label));
2096     }
2097 
2098   g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_MNEMONIC_WIDGET]);
2099 }
2100 
2101 /**
2102  * gtk_label_get_mnemonic_widget:
2103  * @label: a #GtkLabel
2104  *
2105  * Retrieves the target of the mnemonic (keyboard shortcut) of this
2106  * label. See gtk_label_set_mnemonic_widget().
2107  *
2108  * Returns: (nullable) (transfer none): the target of the label’s mnemonic,
2109  *     or %NULL if none has been set and the default algorithm will be used.
2110  **/
2111 GtkWidget *
gtk_label_get_mnemonic_widget(GtkLabel * label)2112 gtk_label_get_mnemonic_widget (GtkLabel *label)
2113 {
2114   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2115 
2116   return label->priv->mnemonic_widget;
2117 }
2118 
2119 /**
2120  * gtk_label_get_mnemonic_keyval:
2121  * @label: a #GtkLabel
2122  *
2123  * If the label has been set so that it has an mnemonic key this function
2124  * returns the keyval used for the mnemonic accelerator. If there is no
2125  * mnemonic set up it returns #GDK_KEY_VoidSymbol.
2126  *
2127  * Returns: GDK keyval usable for accelerators, or #GDK_KEY_VoidSymbol
2128  **/
2129 guint
gtk_label_get_mnemonic_keyval(GtkLabel * label)2130 gtk_label_get_mnemonic_keyval (GtkLabel *label)
2131 {
2132   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_KEY_VoidSymbol);
2133 
2134   return label->priv->mnemonic_keyval;
2135 }
2136 
2137 static void
gtk_label_set_text_internal(GtkLabel * label,gchar * str)2138 gtk_label_set_text_internal (GtkLabel *label,
2139                              gchar    *str)
2140 {
2141   GtkLabelPrivate *priv = label->priv;
2142 
2143   if (g_strcmp0 (priv->text, str) == 0)
2144     {
2145       g_free (str);
2146       return;
2147     }
2148 
2149   _gtk_label_accessible_text_deleted (label);
2150   g_free (priv->text);
2151   priv->text = str;
2152 
2153   _gtk_label_accessible_text_inserted (label);
2154 
2155   gtk_label_select_region_index (label, 0, 0);
2156 }
2157 
2158 static void
gtk_label_set_label_internal(GtkLabel * label,gchar * str)2159 gtk_label_set_label_internal (GtkLabel *label,
2160 			      gchar    *str)
2161 {
2162   GtkLabelPrivate *priv = label->priv;
2163 
2164   g_free (priv->label);
2165 
2166   priv->label = str;
2167 
2168   g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_LABEL]);
2169 }
2170 
2171 static gboolean
gtk_label_set_use_markup_internal(GtkLabel * label,gboolean val)2172 gtk_label_set_use_markup_internal (GtkLabel *label,
2173                                    gboolean  val)
2174 {
2175   GtkLabelPrivate *priv = label->priv;
2176 
2177   val = val != FALSE;
2178   if (priv->use_markup != val)
2179     {
2180       priv->use_markup = val;
2181 
2182       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_USE_MARKUP]);
2183 
2184       return TRUE;
2185     }
2186 
2187   return FALSE;
2188 }
2189 
2190 static gboolean
gtk_label_set_use_underline_internal(GtkLabel * label,gboolean val)2191 gtk_label_set_use_underline_internal (GtkLabel *label,
2192                                       gboolean  val)
2193 {
2194   GtkLabelPrivate *priv = label->priv;
2195 
2196   val = val != FALSE;
2197   if (priv->use_underline != val)
2198     {
2199       priv->use_underline = val;
2200 
2201       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_USE_UNDERLINE]);
2202 
2203       return TRUE;
2204     }
2205 
2206   return FALSE;
2207 }
2208 
2209 /* Calculates text, attrs and mnemonic_keyval from
2210  * label, use_underline and use_markup
2211  */
2212 static void
gtk_label_recalculate(GtkLabel * label)2213 gtk_label_recalculate (GtkLabel *label)
2214 {
2215   GtkLabelPrivate *priv = label->priv;
2216   guint keyval = priv->mnemonic_keyval;
2217 
2218   gtk_label_clear_links (label);
2219 
2220   if (priv->use_markup)
2221     gtk_label_set_markup_internal (label, priv->label, priv->use_underline);
2222   else if (priv->use_underline)
2223     gtk_label_set_uline_text_internal (label, priv->label);
2224   else
2225     {
2226       if (!priv->pattern_set)
2227         {
2228           if (priv->markup_attrs)
2229             pango_attr_list_unref (priv->markup_attrs);
2230           priv->markup_attrs = NULL;
2231         }
2232       gtk_label_set_text_internal (label, g_strdup (priv->label));
2233     }
2234 
2235   if (!priv->use_underline)
2236     priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
2237 
2238   if (keyval != priv->mnemonic_keyval)
2239     {
2240       gtk_label_setup_mnemonic (label, keyval);
2241       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_MNEMONIC_KEYVAL]);
2242     }
2243 
2244   gtk_label_clear_layout (label);
2245   gtk_label_clear_select_info (label);
2246   gtk_widget_queue_resize (GTK_WIDGET (label));
2247 }
2248 
2249 /**
2250  * gtk_label_set_text:
2251  * @label: a #GtkLabel
2252  * @str: The text you want to set
2253  *
2254  * Sets the text within the #GtkLabel widget. It overwrites any text that
2255  * was there before.
2256  *
2257  * This function will clear any previously set mnemonic accelerators, and
2258  * set the #GtkLabel:use-underline property to %FALSE as a side effect.
2259  *
2260  * This function will set the #GtkLabel:use-markup property to %FALSE
2261  * as a side effect.
2262  *
2263  * See also: gtk_label_set_markup()
2264  **/
2265 void
gtk_label_set_text(GtkLabel * label,const gchar * str)2266 gtk_label_set_text (GtkLabel    *label,
2267 		    const gchar *str)
2268 {
2269   g_return_if_fail (GTK_IS_LABEL (label));
2270 
2271   g_object_freeze_notify (G_OBJECT (label));
2272 
2273   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2274   gtk_label_set_use_markup_internal (label, FALSE);
2275   gtk_label_set_use_underline_internal (label, FALSE);
2276 
2277   gtk_label_recalculate (label);
2278 
2279   g_object_thaw_notify (G_OBJECT (label));
2280 }
2281 
2282 /**
2283  * gtk_label_set_attributes:
2284  * @label: a #GtkLabel
2285  * @attrs: (nullable): a #PangoAttrList, or %NULL
2286  *
2287  * Sets a #PangoAttrList; the attributes in the list are applied to the
2288  * label text.
2289  *
2290  * The attributes set with this function will be applied
2291  * and merged with any other attributes previously effected by way
2292  * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
2293  * While it is not recommended to mix markup strings with manually set
2294  * attributes, if you must; know that the attributes will be applied
2295  * to the label after the markup string is parsed.
2296  **/
2297 void
gtk_label_set_attributes(GtkLabel * label,PangoAttrList * attrs)2298 gtk_label_set_attributes (GtkLabel         *label,
2299                           PangoAttrList    *attrs)
2300 {
2301   GtkLabelPrivate *priv = label->priv;
2302 
2303   g_return_if_fail (GTK_IS_LABEL (label));
2304 
2305   if (attrs)
2306     pango_attr_list_ref (attrs);
2307 
2308   if (priv->attrs)
2309     pango_attr_list_unref (priv->attrs);
2310   priv->attrs = attrs;
2311 
2312   g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_ATTRIBUTES]);
2313 
2314   gtk_label_clear_layout (label);
2315   gtk_widget_queue_resize (GTK_WIDGET (label));
2316 }
2317 
2318 /**
2319  * gtk_label_get_attributes:
2320  * @label: a #GtkLabel
2321  *
2322  * Gets the attribute list that was set on the label using
2323  * gtk_label_set_attributes(), if any. This function does
2324  * not reflect attributes that come from the labels markup
2325  * (see gtk_label_set_markup()). If you want to get the
2326  * effective attributes for the label, use
2327  * pango_layout_get_attribute (gtk_label_get_layout (label)).
2328  *
2329  * Returns: (nullable) (transfer none): the attribute list, or %NULL
2330  *     if none was set.
2331  **/
2332 PangoAttrList *
gtk_label_get_attributes(GtkLabel * label)2333 gtk_label_get_attributes (GtkLabel *label)
2334 {
2335   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2336 
2337   return label->priv->attrs;
2338 }
2339 
2340 /**
2341  * gtk_label_set_label:
2342  * @label: a #GtkLabel
2343  * @str: the new text to set for the label
2344  *
2345  * Sets the text of the label. The label is interpreted as
2346  * including embedded underlines and/or Pango markup depending
2347  * on the values of the #GtkLabel:use-underline and
2348  * #GtkLabel:use-markup properties.
2349  **/
2350 void
gtk_label_set_label(GtkLabel * label,const gchar * str)2351 gtk_label_set_label (GtkLabel    *label,
2352 		     const gchar *str)
2353 {
2354   g_return_if_fail (GTK_IS_LABEL (label));
2355 
2356   g_object_freeze_notify (G_OBJECT (label));
2357 
2358   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2359   gtk_label_recalculate (label);
2360 
2361   g_object_thaw_notify (G_OBJECT (label));
2362 }
2363 
2364 /**
2365  * gtk_label_get_label:
2366  * @label: a #GtkLabel
2367  *
2368  * Fetches the text from a label widget including any embedded
2369  * underlines indicating mnemonics and Pango markup. (See
2370  * gtk_label_get_text()).
2371  *
2372  * Returns: the text of the label widget. This string is
2373  *   owned by the widget and must not be modified or freed.
2374  **/
2375 const gchar *
gtk_label_get_label(GtkLabel * label)2376 gtk_label_get_label (GtkLabel *label)
2377 {
2378   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2379 
2380   return label->priv->label;
2381 }
2382 
2383 typedef struct
2384 {
2385   GtkLabel *label;
2386   GList *links;
2387   GString *new_str;
2388   gsize text_len;
2389 } UriParserData;
2390 
2391 static void
start_element_handler(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)2392 start_element_handler (GMarkupParseContext  *context,
2393                        const gchar          *element_name,
2394                        const gchar         **attribute_names,
2395                        const gchar         **attribute_values,
2396                        gpointer              user_data,
2397                        GError              **error)
2398 {
2399   GtkLabelPrivate *priv;
2400   UriParserData *pdata = user_data;
2401 
2402   if (strcmp (element_name, "a") == 0)
2403     {
2404       GtkLabelLink *link;
2405       const gchar *uri = NULL;
2406       const gchar *title = NULL;
2407       gboolean visited = FALSE;
2408       gint line_number;
2409       gint char_number;
2410       gint i;
2411       GtkCssNode *widget_node;
2412       GtkStateFlags state;
2413 
2414       g_markup_parse_context_get_position (context, &line_number, &char_number);
2415 
2416       for (i = 0; attribute_names[i] != NULL; i++)
2417         {
2418           const gchar *attr = attribute_names[i];
2419 
2420           if (strcmp (attr, "href") == 0)
2421             uri = attribute_values[i];
2422           else if (strcmp (attr, "title") == 0)
2423             title = attribute_values[i];
2424           else
2425             {
2426               g_set_error (error,
2427                            G_MARKUP_ERROR,
2428                            G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
2429                            "Attribute '%s' is not allowed on the <a> tag "
2430                            "on line %d char %d",
2431                             attr, line_number, char_number);
2432               return;
2433             }
2434         }
2435 
2436       if (uri == NULL)
2437         {
2438           g_set_error (error,
2439                        G_MARKUP_ERROR,
2440                        G_MARKUP_ERROR_INVALID_CONTENT,
2441                        "Attribute 'href' was missing on the <a> tag "
2442                        "on line %d char %d",
2443                        line_number, char_number);
2444           return;
2445         }
2446 
2447       visited = FALSE;
2448       priv = pdata->label->priv;
2449       if (priv->track_links && priv->select_info)
2450         {
2451           GList *l;
2452           for (l = priv->select_info->links; l; l = l->next)
2453             {
2454               link = l->data;
2455               if (strcmp (uri, link->uri) == 0)
2456                 {
2457                   visited = link->visited;
2458                   break;
2459                 }
2460             }
2461         }
2462 
2463       link = g_new0 (GtkLabelLink, 1);
2464       link->uri = g_strdup (uri);
2465       link->title = g_strdup (title);
2466 
2467       widget_node = gtk_widget_get_css_node (GTK_WIDGET (pdata->label));
2468       link->cssnode = gtk_css_node_new ();
2469       gtk_css_node_set_name (link->cssnode, I_("link"));
2470       gtk_css_node_set_parent (link->cssnode, widget_node);
2471       state = gtk_css_node_get_state (widget_node);
2472       if (visited)
2473         state |= GTK_STATE_FLAG_VISITED;
2474       else
2475         state |= GTK_STATE_FLAG_LINK;
2476       gtk_css_node_set_state (link->cssnode, state);
2477       g_object_unref (link->cssnode);
2478 
2479       link->visited = visited;
2480       link->start = pdata->text_len;
2481       pdata->links = g_list_prepend (pdata->links, link);
2482     }
2483   else
2484     {
2485       gint i;
2486 
2487       g_string_append_c (pdata->new_str, '<');
2488       g_string_append (pdata->new_str, element_name);
2489 
2490       for (i = 0; attribute_names[i] != NULL; i++)
2491         {
2492           const gchar *attr  = attribute_names[i];
2493           const gchar *value = attribute_values[i];
2494           gchar *newvalue;
2495 
2496           newvalue = g_markup_escape_text (value, -1);
2497 
2498           g_string_append_c (pdata->new_str, ' ');
2499           g_string_append (pdata->new_str, attr);
2500           g_string_append (pdata->new_str, "=\"");
2501           g_string_append (pdata->new_str, newvalue);
2502           g_string_append_c (pdata->new_str, '\"');
2503 
2504           g_free (newvalue);
2505         }
2506       g_string_append_c (pdata->new_str, '>');
2507     }
2508 }
2509 
2510 static void
end_element_handler(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)2511 end_element_handler (GMarkupParseContext  *context,
2512                      const gchar          *element_name,
2513                      gpointer              user_data,
2514                      GError              **error)
2515 {
2516   UriParserData *pdata = user_data;
2517 
2518   if (!strcmp (element_name, "a"))
2519     {
2520       GtkLabelLink *link = pdata->links->data;
2521       link->end = pdata->text_len;
2522     }
2523   else
2524     {
2525       g_string_append (pdata->new_str, "</");
2526       g_string_append (pdata->new_str, element_name);
2527       g_string_append_c (pdata->new_str, '>');
2528     }
2529 }
2530 
2531 static void
text_handler(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)2532 text_handler (GMarkupParseContext  *context,
2533               const gchar          *text,
2534               gsize                 text_len,
2535               gpointer              user_data,
2536               GError              **error)
2537 {
2538   UriParserData *pdata = user_data;
2539   gchar *newtext;
2540 
2541   newtext = g_markup_escape_text (text, text_len);
2542   g_string_append (pdata->new_str, newtext);
2543   pdata->text_len += text_len;
2544   g_free (newtext);
2545 }
2546 
2547 static const GMarkupParser markup_parser =
2548 {
2549   start_element_handler,
2550   end_element_handler,
2551   text_handler,
2552   NULL,
2553   NULL
2554 };
2555 
2556 static gboolean
xml_isspace(gchar c)2557 xml_isspace (gchar c)
2558 {
2559   return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
2560 }
2561 
2562 static void
link_free(GtkLabelLink * link)2563 link_free (GtkLabelLink *link)
2564 {
2565   gtk_css_node_set_parent (link->cssnode, NULL);
2566   g_free (link->uri);
2567   g_free (link->title);
2568   g_free (link);
2569 }
2570 
2571 
2572 static gboolean
parse_uri_markup(GtkLabel * label,const gchar * str,gchar ** new_str,GList ** links,GError ** error)2573 parse_uri_markup (GtkLabel     *label,
2574                   const gchar  *str,
2575                   gchar       **new_str,
2576                   GList       **links,
2577                   GError      **error)
2578 {
2579   GMarkupParseContext *context = NULL;
2580   const gchar *p, *end;
2581   gboolean needs_root = TRUE;
2582   gsize length;
2583   UriParserData pdata;
2584 
2585   length = strlen (str);
2586   p = str;
2587   end = str + length;
2588 
2589   pdata.label = label;
2590   pdata.links = NULL;
2591   pdata.new_str = g_string_sized_new (length);
2592   pdata.text_len = 0;
2593 
2594   while (p != end && xml_isspace (*p))
2595     p++;
2596 
2597   if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
2598     needs_root = FALSE;
2599 
2600   context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
2601 
2602   if (needs_root)
2603     {
2604       if (!g_markup_parse_context_parse (context, "<markup>", -1, error))
2605         goto failed;
2606     }
2607 
2608   if (!g_markup_parse_context_parse (context, str, length, error))
2609     goto failed;
2610 
2611   if (needs_root)
2612     {
2613       if (!g_markup_parse_context_parse (context, "</markup>", -1, error))
2614         goto failed;
2615     }
2616 
2617   if (!g_markup_parse_context_end_parse (context, error))
2618     goto failed;
2619 
2620   g_markup_parse_context_free (context);
2621 
2622   *new_str = g_string_free (pdata.new_str, FALSE);
2623   *links = pdata.links;
2624 
2625   return TRUE;
2626 
2627 failed:
2628   g_markup_parse_context_free (context);
2629   g_string_free (pdata.new_str, TRUE);
2630   g_list_free_full (pdata.links, (GDestroyNotify) link_free);
2631 
2632   return FALSE;
2633 }
2634 
2635 static void
gtk_label_ensure_has_tooltip(GtkLabel * label)2636 gtk_label_ensure_has_tooltip (GtkLabel *label)
2637 {
2638   GtkLabelPrivate *priv = label->priv;
2639   GList *l;
2640   gboolean has_tooltip = FALSE;
2641 
2642   for (l = priv->select_info->links; l; l = l->next)
2643     {
2644       GtkLabelLink *link = l->data;
2645       if (link->title)
2646         {
2647           has_tooltip = TRUE;
2648           break;
2649         }
2650     }
2651 
2652   gtk_widget_set_has_tooltip (GTK_WIDGET (label), has_tooltip);
2653 }
2654 
2655 static void
gtk_label_set_markup_internal(GtkLabel * label,const gchar * str,gboolean with_uline)2656 gtk_label_set_markup_internal (GtkLabel    *label,
2657                                const gchar *str,
2658                                gboolean     with_uline)
2659 {
2660   GtkLabelPrivate *priv = label->priv;
2661   gchar *text = NULL;
2662   GError *error = NULL;
2663   PangoAttrList *attrs = NULL;
2664   gunichar accel_char = 0;
2665   gchar *str_for_display = NULL;
2666   gchar *str_for_accel = NULL;
2667   GList *links = NULL;
2668 
2669   if (!parse_uri_markup (label, str, &str_for_display, &links, &error))
2670     {
2671       g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
2672                  str, error->message);
2673       g_error_free (error);
2674       return;
2675     }
2676 
2677   str_for_accel = g_strdup (str_for_display);
2678 
2679   if (links)
2680     {
2681       gtk_label_ensure_select_info (label);
2682       priv->select_info->links = g_list_reverse (links);
2683       _gtk_label_accessible_update_links (label);
2684       gtk_label_ensure_has_tooltip (label);
2685     }
2686 
2687   if (with_uline)
2688     {
2689       gboolean enable_mnemonics = TRUE;
2690       gboolean auto_mnemonics = TRUE;
2691 
2692       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2693                     "gtk-enable-mnemonics", &enable_mnemonics,
2694                     NULL);
2695 
2696       if (!(enable_mnemonics && priv->mnemonics_visible &&
2697             (!auto_mnemonics ||
2698              (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2699               (!priv->mnemonic_widget ||
2700                gtk_widget_is_sensitive (priv->mnemonic_widget))))))
2701         {
2702           gchar *tmp;
2703           gchar *pattern;
2704           guint key;
2705 
2706           if (separate_uline_pattern (str_for_display, &key, &tmp, &pattern))
2707             {
2708               g_free (str_for_display);
2709               str_for_display = tmp;
2710               g_free (pattern);
2711             }
2712         }
2713     }
2714 
2715   /* Extract the text to display */
2716   if (!pango_parse_markup (str_for_display,
2717                            -1,
2718                            with_uline ? '_' : 0,
2719                            &attrs,
2720                            &text,
2721                            NULL,
2722                            &error))
2723     {
2724       g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
2725                  str_for_display, error->message);
2726       g_free (str_for_display);
2727       g_free (str_for_accel);
2728       g_error_free (error);
2729       return;
2730     }
2731 
2732   /* Extract the accelerator character */
2733   if (with_uline && !pango_parse_markup (str_for_accel,
2734 					 -1,
2735 					 '_',
2736 					 NULL,
2737 					 NULL,
2738 					 &accel_char,
2739 					 &error))
2740     {
2741       g_warning ("Failed to set text from markup due to error parsing markup: %s",
2742                  error->message);
2743       g_free (str_for_display);
2744       g_free (str_for_accel);
2745       g_error_free (error);
2746       return;
2747     }
2748 
2749   g_free (str_for_display);
2750   g_free (str_for_accel);
2751 
2752   if (text)
2753     gtk_label_set_text_internal (label, text);
2754 
2755   if (attrs)
2756     {
2757       if (priv->markup_attrs)
2758 	pango_attr_list_unref (priv->markup_attrs);
2759       priv->markup_attrs = attrs;
2760     }
2761 
2762   if (accel_char != 0)
2763     priv->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
2764   else
2765     priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
2766 }
2767 
2768 /**
2769  * gtk_label_set_markup:
2770  * @label: a #GtkLabel
2771  * @str: a markup string (see [Pango markup format][PangoMarkupFormat])
2772  *
2773  * Parses @str which is marked up with the
2774  * [Pango text markup language][PangoMarkupFormat], setting the
2775  * label’s text and attribute list based on the parse results.
2776  *
2777  * If the @str is external data, you may need to escape it with
2778  * g_markup_escape_text() or g_markup_printf_escaped():
2779  *
2780  * |[<!-- language="C" -->
2781  * GtkWidget *label = gtk_label_new (NULL);
2782  * const char *str = "some text";
2783  * const char *format = "<span style=\"italic\">\%s</span>";
2784  * char *markup;
2785  *
2786  * markup = g_markup_printf_escaped (format, str);
2787  * gtk_label_set_markup (GTK_LABEL (label), markup);
2788  * g_free (markup);
2789  * ]|
2790  *
2791  * This function will set the #GtkLabel:use-markup property to %TRUE as
2792  * a side effect.
2793  *
2794  * If you set the label contents using the #GtkLabel:label property you
2795  * should also ensure that you set the #GtkLabel:use-markup property
2796  * accordingly.
2797  *
2798  * See also: gtk_label_set_text()
2799  **/
2800 void
gtk_label_set_markup(GtkLabel * label,const gchar * str)2801 gtk_label_set_markup (GtkLabel    *label,
2802                       const gchar *str)
2803 {
2804   g_return_if_fail (GTK_IS_LABEL (label));
2805 
2806   g_object_freeze_notify (G_OBJECT (label));
2807 
2808   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2809   gtk_label_set_use_markup_internal (label, TRUE);
2810   gtk_label_set_use_underline_internal (label, FALSE);
2811 
2812   gtk_label_recalculate (label);
2813 
2814   g_object_thaw_notify (G_OBJECT (label));
2815 }
2816 
2817 /**
2818  * gtk_label_set_markup_with_mnemonic:
2819  * @label: a #GtkLabel
2820  * @str: a markup string (see
2821  *     [Pango markup format][PangoMarkupFormat])
2822  *
2823  * Parses @str which is marked up with the
2824  * [Pango text markup language][PangoMarkupFormat],
2825  * setting the label’s text and attribute list based on the parse results.
2826  * If characters in @str are preceded by an underscore, they are underlined
2827  * indicating that they represent a keyboard accelerator called a mnemonic.
2828  *
2829  * The mnemonic key can be used to activate another widget, chosen
2830  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
2831  */
2832 void
gtk_label_set_markup_with_mnemonic(GtkLabel * label,const gchar * str)2833 gtk_label_set_markup_with_mnemonic (GtkLabel    *label,
2834                                     const gchar *str)
2835 {
2836   g_return_if_fail (GTK_IS_LABEL (label));
2837 
2838   g_object_freeze_notify (G_OBJECT (label));
2839 
2840   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
2841   gtk_label_set_use_markup_internal (label, TRUE);
2842   gtk_label_set_use_underline_internal (label, TRUE);
2843 
2844   gtk_label_recalculate (label);
2845 
2846   g_object_thaw_notify (G_OBJECT (label));
2847 }
2848 
2849 /**
2850  * gtk_label_get_text:
2851  * @label: a #GtkLabel
2852  *
2853  * Fetches the text from a label widget, as displayed on the
2854  * screen. This does not include any embedded underlines
2855  * indicating mnemonics or Pango markup. (See gtk_label_get_label())
2856  *
2857  * Returns: the text in the label widget. This is the internal
2858  *   string used by the label, and must not be modified.
2859  **/
2860 const gchar *
gtk_label_get_text(GtkLabel * label)2861 gtk_label_get_text (GtkLabel *label)
2862 {
2863   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
2864 
2865   return label->priv->text;
2866 }
2867 
2868 static PangoAttrList *
gtk_label_pattern_to_attrs(GtkLabel * label,const gchar * pattern)2869 gtk_label_pattern_to_attrs (GtkLabel      *label,
2870 			    const gchar   *pattern)
2871 {
2872   GtkLabelPrivate *priv = label->priv;
2873   const char *start;
2874   const char *p = priv->text;
2875   const char *q = pattern;
2876   PangoAttrList *attrs;
2877 
2878   attrs = pango_attr_list_new ();
2879 
2880   while (1)
2881     {
2882       while (*p && *q && *q != '_')
2883 	{
2884 	  p = g_utf8_next_char (p);
2885 	  q++;
2886 	}
2887       start = p;
2888       while (*p && *q && *q == '_')
2889 	{
2890 	  p = g_utf8_next_char (p);
2891 	  q++;
2892 	}
2893 
2894       if (p > start)
2895 	{
2896 	  PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
2897 	  attr->start_index = start - priv->text;
2898 	  attr->end_index = p - priv->text;
2899 
2900 	  pango_attr_list_insert (attrs, attr);
2901 	}
2902       else
2903 	break;
2904     }
2905 
2906   return attrs;
2907 }
2908 
2909 static void
gtk_label_set_pattern_internal(GtkLabel * label,const gchar * pattern,gboolean is_mnemonic)2910 gtk_label_set_pattern_internal (GtkLabel    *label,
2911 				const gchar *pattern,
2912                                 gboolean     is_mnemonic)
2913 {
2914   GtkLabelPrivate *priv = label->priv;
2915   PangoAttrList *attrs;
2916   gboolean enable_mnemonics = TRUE;
2917   gboolean auto_mnemonics = TRUE;
2918 
2919   if (priv->pattern_set)
2920     return;
2921 
2922   if (is_mnemonic)
2923     {
2924       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2925                     "gtk-enable-mnemonics", &enable_mnemonics,
2926                     NULL);
2927 
2928       if (enable_mnemonics && priv->mnemonics_visible && pattern &&
2929           (!auto_mnemonics ||
2930            (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
2931             (!priv->mnemonic_widget ||
2932              gtk_widget_is_sensitive (priv->mnemonic_widget)))))
2933         attrs = gtk_label_pattern_to_attrs (label, pattern);
2934       else
2935         attrs = NULL;
2936     }
2937   else
2938     attrs = gtk_label_pattern_to_attrs (label, pattern);
2939 
2940   if (priv->markup_attrs)
2941     pango_attr_list_unref (priv->markup_attrs);
2942   priv->markup_attrs = attrs;
2943 }
2944 
2945 /**
2946  * gtk_label_set_pattern:
2947  * @label: The #GtkLabel you want to set the pattern to.
2948  * @pattern: The pattern as described above.
2949  *
2950  * The pattern of underlines you want under the existing text within the
2951  * #GtkLabel widget.  For example if the current text of the label says
2952  * “FooBarBaz” passing a pattern of “___   ___” will underline
2953  * “Foo” and “Baz” but not “Bar”.
2954  */
2955 void
gtk_label_set_pattern(GtkLabel * label,const gchar * pattern)2956 gtk_label_set_pattern (GtkLabel	   *label,
2957 		       const gchar *pattern)
2958 {
2959   GtkLabelPrivate *priv;
2960 
2961   g_return_if_fail (GTK_IS_LABEL (label));
2962 
2963   priv = label->priv;
2964 
2965   priv->pattern_set = FALSE;
2966 
2967   if (pattern)
2968     {
2969       gtk_label_set_pattern_internal (label, pattern, FALSE);
2970       priv->pattern_set = TRUE;
2971     }
2972   else
2973     gtk_label_recalculate (label);
2974 
2975   gtk_label_clear_layout (label);
2976   gtk_widget_queue_resize (GTK_WIDGET (label));
2977 }
2978 
2979 
2980 /**
2981  * gtk_label_set_justify:
2982  * @label: a #GtkLabel
2983  * @jtype: a #GtkJustification
2984  *
2985  * Sets the alignment of the lines in the text of the label relative to
2986  * each other. %GTK_JUSTIFY_LEFT is the default value when the widget is
2987  * first created with gtk_label_new(). If you instead want to set the
2988  * alignment of the label as a whole, use gtk_widget_set_halign() instead.
2989  * gtk_label_set_justify() has no effect on labels containing only a
2990  * single line.
2991  */
2992 void
gtk_label_set_justify(GtkLabel * label,GtkJustification jtype)2993 gtk_label_set_justify (GtkLabel        *label,
2994 		       GtkJustification jtype)
2995 {
2996   GtkLabelPrivate *priv;
2997 
2998   g_return_if_fail (GTK_IS_LABEL (label));
2999   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
3000 
3001   priv = label->priv;
3002 
3003   if ((GtkJustification) priv->jtype != jtype)
3004     {
3005       priv->jtype = jtype;
3006 
3007       /* No real need to be this drastic, but easier than duplicating the code */
3008       gtk_label_clear_layout (label);
3009 
3010       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_JUSTIFY]);
3011       gtk_widget_queue_resize (GTK_WIDGET (label));
3012     }
3013 }
3014 
3015 /**
3016  * gtk_label_get_justify:
3017  * @label: a #GtkLabel
3018  *
3019  * Returns the justification of the label. See gtk_label_set_justify().
3020  *
3021  * Returns: #GtkJustification
3022  **/
3023 GtkJustification
gtk_label_get_justify(GtkLabel * label)3024 gtk_label_get_justify (GtkLabel *label)
3025 {
3026   g_return_val_if_fail (GTK_IS_LABEL (label), 0);
3027 
3028   return label->priv->jtype;
3029 }
3030 
3031 /**
3032  * gtk_label_set_ellipsize:
3033  * @label: a #GtkLabel
3034  * @mode: a #PangoEllipsizeMode
3035  *
3036  * Sets the mode used to ellipsize (add an ellipsis: "...") to the text
3037  * if there is not enough space to render the entire string.
3038  *
3039  * Since: 2.6
3040  **/
3041 void
gtk_label_set_ellipsize(GtkLabel * label,PangoEllipsizeMode mode)3042 gtk_label_set_ellipsize (GtkLabel          *label,
3043 			 PangoEllipsizeMode mode)
3044 {
3045   GtkLabelPrivate *priv;
3046 
3047   g_return_if_fail (GTK_IS_LABEL (label));
3048   g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
3049 
3050   priv = label->priv;
3051 
3052   if ((PangoEllipsizeMode) priv->ellipsize != mode)
3053     {
3054       priv->ellipsize = mode;
3055 
3056       /* No real need to be this drastic, but easier than duplicating the code */
3057       gtk_label_clear_layout (label);
3058 
3059       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_ELLIPSIZE]);
3060       gtk_widget_queue_resize (GTK_WIDGET (label));
3061     }
3062 }
3063 
3064 /**
3065  * gtk_label_get_ellipsize:
3066  * @label: a #GtkLabel
3067  *
3068  * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
3069  *
3070  * Returns: #PangoEllipsizeMode
3071  *
3072  * Since: 2.6
3073  **/
3074 PangoEllipsizeMode
gtk_label_get_ellipsize(GtkLabel * label)3075 gtk_label_get_ellipsize (GtkLabel *label)
3076 {
3077   g_return_val_if_fail (GTK_IS_LABEL (label), PANGO_ELLIPSIZE_NONE);
3078 
3079   return label->priv->ellipsize;
3080 }
3081 
3082 /**
3083  * gtk_label_set_width_chars:
3084  * @label: a #GtkLabel
3085  * @n_chars: the new desired width, in characters.
3086  *
3087  * Sets the desired width in characters of @label to @n_chars.
3088  *
3089  * Since: 2.6
3090  **/
3091 void
gtk_label_set_width_chars(GtkLabel * label,gint n_chars)3092 gtk_label_set_width_chars (GtkLabel *label,
3093 			   gint      n_chars)
3094 {
3095   GtkLabelPrivate *priv;
3096 
3097   g_return_if_fail (GTK_IS_LABEL (label));
3098 
3099   priv = label->priv;
3100 
3101   if (priv->width_chars != n_chars)
3102     {
3103       priv->width_chars = n_chars;
3104       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_WIDTH_CHARS]);
3105       gtk_widget_queue_resize (GTK_WIDGET (label));
3106     }
3107 }
3108 
3109 /**
3110  * gtk_label_get_width_chars:
3111  * @label: a #GtkLabel
3112  *
3113  * Retrieves the desired width of @label, in characters. See
3114  * gtk_label_set_width_chars().
3115  *
3116  * Returns: the width of the label in characters.
3117  *
3118  * Since: 2.6
3119  **/
3120 gint
gtk_label_get_width_chars(GtkLabel * label)3121 gtk_label_get_width_chars (GtkLabel *label)
3122 {
3123   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
3124 
3125   return label->priv->width_chars;
3126 }
3127 
3128 /**
3129  * gtk_label_set_max_width_chars:
3130  * @label: a #GtkLabel
3131  * @n_chars: the new desired maximum width, in characters.
3132  *
3133  * Sets the desired maximum width in characters of @label to @n_chars.
3134  *
3135  * Since: 2.6
3136  **/
3137 void
gtk_label_set_max_width_chars(GtkLabel * label,gint n_chars)3138 gtk_label_set_max_width_chars (GtkLabel *label,
3139 			       gint      n_chars)
3140 {
3141   GtkLabelPrivate *priv;
3142 
3143   g_return_if_fail (GTK_IS_LABEL (label));
3144 
3145   priv = label->priv;
3146 
3147   if (priv->max_width_chars != n_chars)
3148     {
3149       priv->max_width_chars = n_chars;
3150 
3151       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_MAX_WIDTH_CHARS]);
3152       gtk_widget_queue_resize (GTK_WIDGET (label));
3153     }
3154 }
3155 
3156 /**
3157  * gtk_label_get_max_width_chars:
3158  * @label: a #GtkLabel
3159  *
3160  * Retrieves the desired maximum width of @label, in characters. See
3161  * gtk_label_set_width_chars().
3162  *
3163  * Returns: the maximum width of the label in characters.
3164  *
3165  * Since: 2.6
3166  **/
3167 gint
gtk_label_get_max_width_chars(GtkLabel * label)3168 gtk_label_get_max_width_chars (GtkLabel *label)
3169 {
3170   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
3171 
3172   return label->priv->max_width_chars;
3173 }
3174 
3175 /**
3176  * gtk_label_set_line_wrap:
3177  * @label: a #GtkLabel
3178  * @wrap: the setting
3179  *
3180  * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
3181  * lines if text exceeds the widget’s size. %FALSE lets the text get cut off
3182  * by the edge of the widget if it exceeds the widget size.
3183  *
3184  * Note that setting line wrapping to %TRUE does not make the label
3185  * wrap at its parent container’s width, because GTK+ widgets
3186  * conceptually can’t make their requisition depend on the parent
3187  * container’s size. For a label that wraps at a specific position,
3188  * set the label’s width using gtk_widget_set_size_request().
3189  **/
3190 void
gtk_label_set_line_wrap(GtkLabel * label,gboolean wrap)3191 gtk_label_set_line_wrap (GtkLabel *label,
3192 			 gboolean  wrap)
3193 {
3194   GtkLabelPrivate *priv;
3195 
3196   g_return_if_fail (GTK_IS_LABEL (label));
3197 
3198   priv = label->priv;
3199 
3200   wrap = wrap != FALSE;
3201 
3202   if (priv->wrap != wrap)
3203     {
3204       priv->wrap = wrap;
3205 
3206       gtk_label_clear_layout (label);
3207       gtk_widget_queue_resize (GTK_WIDGET (label));
3208       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_WRAP]);
3209     }
3210 }
3211 
3212 /**
3213  * gtk_label_get_line_wrap:
3214  * @label: a #GtkLabel
3215  *
3216  * Returns whether lines in the label are automatically wrapped.
3217  * See gtk_label_set_line_wrap().
3218  *
3219  * Returns: %TRUE if the lines of the label are automatically wrapped.
3220  */
3221 gboolean
gtk_label_get_line_wrap(GtkLabel * label)3222 gtk_label_get_line_wrap (GtkLabel *label)
3223 {
3224   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3225 
3226   return label->priv->wrap;
3227 }
3228 
3229 /**
3230  * gtk_label_set_line_wrap_mode:
3231  * @label: a #GtkLabel
3232  * @wrap_mode: the line wrapping mode
3233  *
3234  * If line wrapping is on (see gtk_label_set_line_wrap()) this controls how
3235  * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
3236  * wrap on word boundaries.
3237  *
3238  * Since: 2.10
3239  **/
3240 void
gtk_label_set_line_wrap_mode(GtkLabel * label,PangoWrapMode wrap_mode)3241 gtk_label_set_line_wrap_mode (GtkLabel *label,
3242 			      PangoWrapMode wrap_mode)
3243 {
3244   GtkLabelPrivate *priv;
3245 
3246   g_return_if_fail (GTK_IS_LABEL (label));
3247 
3248   priv = label->priv;
3249 
3250   if (priv->wrap_mode != wrap_mode)
3251     {
3252       priv->wrap_mode = wrap_mode;
3253       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_WRAP_MODE]);
3254 
3255       gtk_widget_queue_resize (GTK_WIDGET (label));
3256     }
3257 }
3258 
3259 /**
3260  * gtk_label_get_line_wrap_mode:
3261  * @label: a #GtkLabel
3262  *
3263  * Returns line wrap mode used by the label. See gtk_label_set_line_wrap_mode().
3264  *
3265  * Returns: %TRUE if the lines of the label are automatically wrapped.
3266  *
3267  * Since: 2.10
3268  */
3269 PangoWrapMode
gtk_label_get_line_wrap_mode(GtkLabel * label)3270 gtk_label_get_line_wrap_mode (GtkLabel *label)
3271 {
3272   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
3273 
3274   return label->priv->wrap_mode;
3275 }
3276 
3277 static void
gtk_label_destroy(GtkWidget * widget)3278 gtk_label_destroy (GtkWidget *widget)
3279 {
3280   GtkLabel *label = GTK_LABEL (widget);
3281 
3282   gtk_label_set_mnemonic_widget (label, NULL);
3283 
3284   GTK_WIDGET_CLASS (gtk_label_parent_class)->destroy (widget);
3285 }
3286 
3287 static void
gtk_label_finalize(GObject * object)3288 gtk_label_finalize (GObject *object)
3289 {
3290   GtkLabel *label = GTK_LABEL (object);
3291   GtkLabelPrivate *priv = label->priv;
3292 
3293   g_free (priv->label);
3294   g_free (priv->text);
3295 
3296   g_clear_object (&priv->layout);
3297   g_clear_pointer (&priv->attrs, pango_attr_list_unref);
3298   g_clear_pointer (&priv->markup_attrs, pango_attr_list_unref);
3299 
3300   if (priv->select_info)
3301     {
3302       g_object_unref (priv->select_info->drag_gesture);
3303       g_object_unref (priv->select_info->multipress_gesture);
3304     }
3305 
3306   gtk_label_clear_links (label);
3307   g_free (priv->select_info);
3308 
3309   g_clear_object (&priv->gadget);
3310 
3311   G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
3312 }
3313 
3314 static void
gtk_label_clear_layout(GtkLabel * label)3315 gtk_label_clear_layout (GtkLabel *label)
3316 {
3317   g_clear_object (&label->priv->layout);
3318 }
3319 
3320 /**
3321  * gtk_label_get_measuring_layout:
3322  * @label: the label
3323  * @existing_layout: %NULL or an existing layout already in use.
3324  * @width: the width to measure with in pango units, or -1 for infinite
3325  *
3326  * Gets a layout that can be used for measuring sizes. The returned
3327  * layout will be identical to the label’s layout except for the
3328  * layout’s width, which will be set to @width. Do not modify the returned
3329  * layout.
3330  *
3331  * Returns: a new reference to a pango layout
3332  **/
3333 static PangoLayout *
gtk_label_get_measuring_layout(GtkLabel * label,PangoLayout * existing_layout,int width)3334 gtk_label_get_measuring_layout (GtkLabel *   label,
3335                                 PangoLayout *existing_layout,
3336                                 int          width)
3337 {
3338   GtkLabelPrivate *priv = label->priv;
3339   PangoRectangle rect;
3340   PangoLayout *copy;
3341 
3342   if (existing_layout != NULL)
3343     {
3344       if (existing_layout != priv->layout)
3345         {
3346           pango_layout_set_width (existing_layout, width);
3347           return existing_layout;
3348         }
3349 
3350       g_object_unref (existing_layout);
3351     }
3352 
3353   gtk_label_ensure_layout (label);
3354 
3355   if (pango_layout_get_width (priv->layout) == width)
3356     {
3357       g_object_ref (priv->layout);
3358       return priv->layout;
3359     }
3360 
3361   /* We can use the label's own layout if we're not allocated a size yet,
3362    * because we don't need it to be properly setup at that point.
3363    * This way we can make use of caching upon the label's creation.
3364    */
3365   if (gtk_widget_get_allocated_width (GTK_WIDGET (label)) <= 1)
3366     {
3367       g_object_ref (priv->layout);
3368       pango_layout_set_width (priv->layout, width);
3369       return priv->layout;
3370     }
3371 
3372   /* oftentimes we want to measure a width that is far wider than the current width,
3373    * even though the layout would not change if we made it wider. In that case, we
3374    * can just return the current layout, because for measuring purposes, it will be
3375    * identical.
3376    */
3377   pango_layout_get_extents (priv->layout, NULL, &rect);
3378   if ((width == -1 || rect.width <= width) &&
3379       !pango_layout_is_wrapped (priv->layout) &&
3380       !pango_layout_is_ellipsized (priv->layout))
3381     {
3382       g_object_ref (priv->layout);
3383       return priv->layout;
3384     }
3385 
3386   copy = pango_layout_copy (priv->layout);
3387   pango_layout_set_width (copy, width);
3388   return copy;
3389 }
3390 
3391 static void
gtk_label_update_layout_width(GtkLabel * label)3392 gtk_label_update_layout_width (GtkLabel *label)
3393 {
3394   GtkLabelPrivate *priv = label->priv;
3395   GtkWidget *widget = GTK_WIDGET (label);
3396 
3397   g_assert (priv->layout);
3398 
3399   if (priv->ellipsize || priv->wrap)
3400     {
3401       GtkAllocation allocation;
3402       int xpad, ypad;
3403       PangoRectangle logical;
3404       gint width, height;
3405 
3406       gtk_css_gadget_get_content_allocation (priv->gadget, &allocation, NULL);
3407 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3408       gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3409 G_GNUC_END_IGNORE_DEPRECATIONS
3410 
3411       width = allocation.width - 2 * xpad;
3412       height = allocation.height - 2 * ypad;
3413 
3414       if (priv->have_transform)
3415         {
3416           PangoContext *context = gtk_widget_get_pango_context (widget);
3417           const PangoMatrix *matrix = pango_context_get_matrix (context);
3418           const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
3419           const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
3420 
3421           pango_layout_set_width (priv->layout, -1);
3422           pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
3423 
3424           if (fabs (dy) < 0.01)
3425             {
3426               if (logical.width > width)
3427                 pango_layout_set_width (priv->layout, width * PANGO_SCALE);
3428             }
3429           else if (fabs (dx) < 0.01)
3430             {
3431               if (logical.width > height)
3432                 pango_layout_set_width (priv->layout, height * PANGO_SCALE);
3433             }
3434           else
3435             {
3436               gdouble x0, y0, x1, y1, length;
3437               gboolean vertical;
3438               gint cy;
3439 
3440               x0 = width / 2;
3441               y0 = dx ? x0 * dy / dx : G_MAXDOUBLE;
3442               vertical = fabs (y0) > height / 2;
3443 
3444               if (vertical)
3445                 {
3446                   y0 = height/2;
3447                   x0 = dy ? y0 * dx / dy : G_MAXDOUBLE;
3448                 }
3449 
3450               length = 2 * sqrt (x0 * x0 + y0 * y0);
3451               pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
3452               pango_layout_get_pixel_size (priv->layout, NULL, &cy);
3453 
3454               x1 = +dy * cy/2;
3455               y1 = -dx * cy/2;
3456 
3457               if (vertical)
3458                 {
3459                   y0 = height/2 + y1 - y0;
3460                   x0 = -y0 * dx/dy;
3461                 }
3462               else
3463                 {
3464                   x0 = width/2 + x1 - x0;
3465                   y0 = -x0 * dy/dx;
3466                 }
3467 
3468               length = length - sqrt (x0 * x0 + y0 * y0) * 2;
3469               pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
3470             }
3471         }
3472       else
3473         {
3474           pango_layout_set_width (priv->layout, width * PANGO_SCALE);
3475         }
3476     }
3477   else
3478     {
3479       pango_layout_set_width (priv->layout, -1);
3480     }
3481 }
3482 
3483 static void
gtk_label_update_layout_attributes(GtkLabel * label)3484 gtk_label_update_layout_attributes (GtkLabel *label)
3485 {
3486   GtkLabelPrivate *priv = label->priv;
3487   GtkWidget *widget = GTK_WIDGET (label);
3488   GtkStyleContext *context;
3489   PangoAttrList *attrs;
3490   PangoAttrList *style_attrs;
3491 
3492   if (priv->layout == NULL)
3493     return;
3494 
3495   context = gtk_widget_get_style_context (widget);
3496 
3497   if (priv->select_info && priv->select_info->links)
3498     {
3499       GdkRGBA link_color;
3500       PangoAttribute *attribute;
3501       GList *list;
3502 
3503       attrs = pango_attr_list_new ();
3504 
3505       for (list = priv->select_info->links; list; list = list->next)
3506         {
3507           GtkLabelLink *link = list->data;
3508 
3509           attribute = pango_attr_underline_new (TRUE);
3510           attribute->start_index = link->start;
3511           attribute->end_index = link->end;
3512           pango_attr_list_insert (attrs, attribute);
3513 
3514           gtk_style_context_save_to_node (context, link->cssnode);
3515           gtk_style_context_get_color (context, gtk_style_context_get_state (context), &link_color);
3516           gtk_style_context_restore (context);
3517 
3518           attribute = pango_attr_foreground_new (link_color.red * 65535,
3519                                                  link_color.green * 65535,
3520                                                  link_color.blue * 65535);
3521           attribute->start_index = link->start;
3522           attribute->end_index = link->end;
3523           pango_attr_list_insert (attrs, attribute);
3524         }
3525     }
3526   else if (priv->markup_attrs && priv->attrs)
3527     attrs = pango_attr_list_new ();
3528   else
3529     attrs = NULL;
3530 
3531   style_attrs = _gtk_style_context_get_pango_attributes (context);
3532 
3533   attrs = _gtk_pango_attr_list_merge (attrs, style_attrs);
3534   attrs = _gtk_pango_attr_list_merge (attrs, priv->markup_attrs);
3535   attrs = _gtk_pango_attr_list_merge (attrs, priv->attrs);
3536 
3537   pango_layout_set_attributes (priv->layout, attrs);
3538 
3539   if (attrs)
3540     pango_attr_list_unref (attrs);
3541   if (style_attrs)
3542     pango_attr_list_unref (style_attrs);
3543 }
3544 
3545 static void
gtk_label_ensure_layout(GtkLabel * label)3546 gtk_label_ensure_layout (GtkLabel *label)
3547 {
3548   GtkLabelPrivate *priv = label->priv;
3549   GtkWidget *widget;
3550   gboolean rtl;
3551 
3552   widget = GTK_WIDGET (label);
3553 
3554   rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3555 
3556   if (!priv->layout)
3557     {
3558       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
3559       gdouble angle = gtk_label_get_angle (label);
3560 
3561       if (angle != 0.0 && !priv->select_info)
3562 	{
3563           PangoMatrix matrix = PANGO_MATRIX_INIT;
3564 
3565 	  /* We rotate the standard singleton PangoContext for the widget,
3566 	   * depending on the fact that it's meant pretty much exclusively
3567 	   * for our use.
3568 	   */
3569 	  pango_matrix_rotate (&matrix, angle);
3570 
3571 	  pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
3572 
3573 	  priv->have_transform = TRUE;
3574 	}
3575       else
3576 	{
3577 	  if (priv->have_transform)
3578 	    pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
3579 
3580 	  priv->have_transform = FALSE;
3581 	}
3582 
3583       priv->layout = gtk_widget_create_pango_layout (widget, priv->text);
3584 
3585       gtk_label_update_layout_attributes (label);
3586 
3587       switch (priv->jtype)
3588 	{
3589 	case GTK_JUSTIFY_LEFT:
3590 	  align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3591 	  break;
3592 	case GTK_JUSTIFY_RIGHT:
3593 	  align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
3594 	  break;
3595 	case GTK_JUSTIFY_CENTER:
3596 	  align = PANGO_ALIGN_CENTER;
3597 	  break;
3598 	case GTK_JUSTIFY_FILL:
3599 	  align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
3600 	  pango_layout_set_justify (priv->layout, TRUE);
3601 	  break;
3602 	default:
3603 	  g_assert_not_reached();
3604 	}
3605 
3606       pango_layout_set_alignment (priv->layout, align);
3607       pango_layout_set_ellipsize (priv->layout, priv->ellipsize);
3608       pango_layout_set_wrap (priv->layout, priv->wrap_mode);
3609       pango_layout_set_single_paragraph_mode (priv->layout, priv->single_line_mode);
3610       if (priv->lines > 0)
3611         pango_layout_set_height (priv->layout, - priv->lines);
3612 
3613       gtk_label_update_layout_width (label);
3614     }
3615 }
3616 
3617 static GtkSizeRequestMode
gtk_label_get_request_mode(GtkWidget * widget)3618 gtk_label_get_request_mode (GtkWidget *widget)
3619 {
3620   GtkLabel *label = GTK_LABEL (widget);
3621   gdouble angle;
3622 
3623   angle = gtk_label_get_angle (label);
3624 
3625   if (label->priv->wrap)
3626     return (angle == 90 || angle == 270)
3627            ? GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT
3628            : GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
3629 
3630   return GTK_SIZE_REQUEST_CONSTANT_SIZE;
3631 }
3632 
3633 
3634 static void
get_size_for_allocation(GtkLabel * label,gint allocation,gint * minimum_size,gint * natural_size,gint * minimum_baseline,gint * natural_baseline)3635 get_size_for_allocation (GtkLabel *label,
3636                          gint      allocation,
3637                          gint     *minimum_size,
3638                          gint     *natural_size,
3639 			 gint     *minimum_baseline,
3640                          gint     *natural_baseline)
3641 {
3642   PangoLayout *layout;
3643   gint text_height, baseline;
3644 
3645   layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE);
3646 
3647   pango_layout_get_pixel_size (layout, NULL, &text_height);
3648 
3649   *minimum_size = text_height;
3650   *natural_size = text_height;
3651 
3652   if (minimum_baseline || natural_baseline)
3653     {
3654       baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
3655       *minimum_baseline = baseline;
3656       *natural_baseline = baseline;
3657     }
3658 
3659   g_object_unref (layout);
3660 }
3661 
3662 static gint
get_char_pixels(GtkWidget * label,PangoLayout * layout)3663 get_char_pixels (GtkWidget   *label,
3664                  PangoLayout *layout)
3665 {
3666   PangoContext *context;
3667   PangoFontMetrics *metrics;
3668   gint char_width, digit_width;
3669 
3670   context = pango_layout_get_context (layout);
3671   metrics = pango_context_get_metrics (context,
3672                                        pango_context_get_font_description (context),
3673                                        pango_context_get_language (context));
3674   char_width = pango_font_metrics_get_approximate_char_width (metrics);
3675   digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
3676   pango_font_metrics_unref (metrics);
3677 
3678   return MAX (char_width, digit_width);;
3679 }
3680 
3681 static void
gtk_label_get_preferred_layout_size(GtkLabel * label,PangoRectangle * smallest,PangoRectangle * widest)3682 gtk_label_get_preferred_layout_size (GtkLabel *label,
3683                                      PangoRectangle *smallest,
3684                                      PangoRectangle *widest)
3685 {
3686   GtkLabelPrivate *priv = label->priv;
3687   PangoLayout *layout;
3688   gint char_pixels;
3689 
3690   /* "width-chars" Hard-coded minimum width:
3691    *    - minimum size should be MAX (width-chars, strlen ("..."));
3692    *    - natural size should be MAX (width-chars, strlen (priv->text));
3693    *
3694    * "max-width-chars" User specified maximum size requisition
3695    *    - minimum size should be MAX (width-chars, 0)
3696    *    - natural size should be MIN (max-width-chars, strlen (priv->text))
3697    *
3698    *    For ellipsizing labels; if max-width-chars is specified: either it is used as
3699    *    a minimum size or the label text as a minimum size (natural size still overflows).
3700    *
3701    *    For wrapping labels; A reasonable minimum size is useful to naturally layout
3702    *    interfaces automatically. In this case if no "width-chars" is specified, the minimum
3703    *    width will default to the wrap guess that gtk_label_ensure_layout() does.
3704    */
3705 
3706   /* Start off with the pixel extents of an as-wide-as-possible layout */
3707   layout = gtk_label_get_measuring_layout (label, NULL, -1);
3708 
3709   if (priv->width_chars > -1 || priv->max_width_chars > -1)
3710     char_pixels = get_char_pixels (GTK_WIDGET (label), layout);
3711   else
3712     char_pixels = 0;
3713 
3714   pango_layout_get_extents (layout, NULL, widest);
3715   widest->width = MAX (widest->width, char_pixels * priv->width_chars);
3716   widest->x = widest->y = 0;
3717 
3718   if (priv->ellipsize || priv->wrap)
3719     {
3720       /* a layout with width 0 will be as small as humanly possible */
3721       layout = gtk_label_get_measuring_layout (label,
3722                                                layout,
3723                                                priv->width_chars > -1 ? char_pixels * priv->width_chars
3724                                                                       : 0);
3725 
3726       pango_layout_get_extents (layout, NULL, smallest);
3727       smallest->width = MAX (smallest->width, char_pixels * priv->width_chars);
3728       smallest->x = smallest->y = 0;
3729 
3730       if (priv->max_width_chars > -1 && widest->width > char_pixels * priv->max_width_chars)
3731         {
3732           layout = gtk_label_get_measuring_layout (label,
3733                                                    layout,
3734                                                    MAX (smallest->width, char_pixels * priv->max_width_chars));
3735           pango_layout_get_extents (layout, NULL, widest);
3736           widest->width = MAX (widest->width, char_pixels * priv->width_chars);
3737           widest->x = widest->y = 0;
3738         }
3739     }
3740   else
3741     {
3742       *smallest = *widest;
3743     }
3744 
3745   if (widest->width < smallest->width)
3746     *smallest = *widest;
3747 
3748   g_object_unref (layout);
3749 }
3750 
3751 static void
gtk_label_get_preferred_size(GtkWidget * widget,GtkOrientation orientation,gint * minimum_size,gint * natural_size,gint * minimum_baseline,gint * natural_baseline)3752 gtk_label_get_preferred_size (GtkWidget      *widget,
3753                               GtkOrientation  orientation,
3754                               gint           *minimum_size,
3755                               gint           *natural_size,
3756 			      gint           *minimum_baseline,
3757 			      gint           *natural_baseline)
3758 {
3759   GtkLabel      *label = GTK_LABEL (widget);
3760   GtkLabelPrivate  *priv = label->priv;
3761   gint xpad, ypad;
3762   PangoRectangle widest_rect;
3763   PangoRectangle smallest_rect;
3764 
3765 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3766   gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3767 G_GNUC_END_IGNORE_DEPRECATIONS
3768 
3769   gtk_label_get_preferred_layout_size (label, &smallest_rect, &widest_rect);
3770 
3771   /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
3772   if (priv->have_transform)
3773     {
3774       PangoContext *context;
3775       const PangoMatrix *matrix;
3776 
3777       context = pango_layout_get_context (priv->layout);
3778       matrix = pango_context_get_matrix (context);
3779 
3780       pango_matrix_transform_rectangle (matrix, &widest_rect);
3781       pango_matrix_transform_rectangle (matrix, &smallest_rect);
3782 
3783       /* Bump the size in case of ellipsize to ensure pango has
3784        * enough space in the angles (note, we could alternatively set the
3785        * layout to not ellipsize when we know we have been allocated our
3786        * full size, or it may be that pango needs a fix here).
3787        */
3788       if (priv->ellipsize && priv->angle != 0 && priv->angle != 90 &&
3789           priv->angle != 180 && priv->angle != 270 && priv->angle != 360)
3790         {
3791           /* For some reason we only need this at about 110 degrees, and only
3792            * when gaining in height
3793            */
3794           widest_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3795           widest_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3796           smallest_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3797           smallest_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
3798         }
3799     }
3800 
3801   widest_rect.width  = PANGO_PIXELS_CEIL (widest_rect.width);
3802   widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
3803 
3804   smallest_rect.width  = PANGO_PIXELS_CEIL (smallest_rect.width);
3805   smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
3806 
3807   if (orientation == GTK_ORIENTATION_HORIZONTAL)
3808     {
3809       /* Note, we cant use get_size_for_allocation() when rotating
3810        * ellipsized labels.
3811        */
3812       if (!(priv->ellipsize && priv->have_transform) &&
3813           (priv->angle == 90 || priv->angle == 270))
3814         {
3815           /* Doing a h4w request on a rotated label here, return the
3816            * required width for the minimum height.
3817            */
3818           get_size_for_allocation (label,
3819                                    smallest_rect.height,
3820                                    minimum_size, natural_size,
3821 				   NULL, NULL);
3822 
3823         }
3824       else
3825         {
3826           /* Normal desired width */
3827           *minimum_size = smallest_rect.width;
3828           *natural_size = widest_rect.width;
3829         }
3830 
3831       *minimum_size += xpad * 2;
3832       *natural_size += xpad * 2;
3833 
3834       if (minimum_baseline)
3835         *minimum_baseline = -1;
3836 
3837       if (natural_baseline)
3838         *natural_baseline = -1;
3839     }
3840   else /* GTK_ORIENTATION_VERTICAL */
3841     {
3842       /* Note, we cant use get_size_for_allocation() when rotating
3843        * ellipsized labels.
3844        */
3845       if (!(priv->ellipsize && priv->have_transform) &&
3846           (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
3847         {
3848           /* Doing a w4h request on a label here, return the required
3849            * height for the minimum width.
3850            */
3851           get_size_for_allocation (label,
3852                                    widest_rect.width,
3853                                    minimum_size, natural_size,
3854 				   minimum_baseline, natural_baseline);
3855 
3856 	  if (priv->angle == 180)
3857 	    {
3858 	      if (minimum_baseline)
3859 		*minimum_baseline = *minimum_size - *minimum_baseline;
3860 	      if (natural_baseline)
3861 		*natural_baseline = *natural_size - *natural_baseline;
3862 	    }
3863         }
3864       else
3865         {
3866           /* A vertically rotated label does w4h, so return the base
3867            * desired height (text length)
3868            */
3869           *minimum_size = MIN (smallest_rect.height, widest_rect.height);
3870           *natural_size = MAX (smallest_rect.height, widest_rect.height);
3871         }
3872 
3873       *minimum_size += ypad * 2;
3874       *natural_size += ypad * 2;
3875     }
3876 }
3877 
3878 static void
gtk_label_measure(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer unused)3879 gtk_label_measure (GtkCssGadget   *gadget,
3880                    GtkOrientation  orientation,
3881                    int             for_size,
3882                    int            *minimum,
3883                    int            *natural,
3884                    int            *minimum_baseline,
3885                    int            *natural_baseline,
3886                    gpointer        unused)
3887 {
3888   GtkWidget *widget;
3889   GtkLabel *label;
3890   GtkLabelPrivate *priv;
3891   gint xpad, ypad;
3892 
3893   widget = gtk_css_gadget_get_owner (gadget);
3894   label = GTK_LABEL (widget);
3895   priv = label->priv;
3896 
3897 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3898   gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
3899 G_GNUC_END_IGNORE_DEPRECATIONS
3900 
3901   if ((orientation == GTK_ORIENTATION_VERTICAL && for_size != -1 && priv->wrap && (priv->angle == 0 || priv->angle == 180 || priv->angle == 360)) ||
3902       (orientation == GTK_ORIENTATION_HORIZONTAL && priv->wrap && (priv->angle == 90 || priv->angle == 270)))
3903     {
3904       gint size;
3905 
3906       if (priv->wrap)
3907         gtk_label_clear_layout (label);
3908 
3909       if (orientation == GTK_ORIENTATION_HORIZONTAL)
3910         size = MAX (1, for_size) - 2 * ypad;
3911       else
3912         size = MAX (1, for_size) - 2 * xpad;
3913 
3914       get_size_for_allocation (label, size, minimum, natural, minimum_baseline, natural_baseline);
3915 
3916       if (orientation == GTK_ORIENTATION_HORIZONTAL)
3917         {
3918           *minimum += 2 * xpad;
3919           *natural += 2 * xpad;
3920         }
3921       else
3922         {
3923           *minimum += 2 * ypad;
3924           *natural += 2 * ypad;
3925         }
3926     }
3927   else
3928     gtk_label_get_preferred_size (widget, orientation, minimum, natural, minimum_baseline, natural_baseline);
3929 }
3930 
3931 static void
gtk_label_get_preferred_width(GtkWidget * widget,gint * minimum_size,gint * natural_size)3932 gtk_label_get_preferred_width (GtkWidget *widget,
3933                                gint      *minimum_size,
3934                                gint      *natural_size)
3935 {
3936   gtk_css_gadget_get_preferred_size (GTK_LABEL (widget)->priv->gadget,
3937                                      GTK_ORIENTATION_HORIZONTAL,
3938                                      -1,
3939                                      minimum_size, natural_size,
3940                                      NULL, NULL);
3941 }
3942 
3943 static void
gtk_label_get_preferred_height(GtkWidget * widget,gint * minimum_size,gint * natural_size)3944 gtk_label_get_preferred_height (GtkWidget *widget,
3945                                 gint      *minimum_size,
3946                                 gint      *natural_size)
3947 {
3948   gtk_css_gadget_get_preferred_size (GTK_LABEL (widget)->priv->gadget,
3949                                      GTK_ORIENTATION_VERTICAL,
3950                                      -1,
3951                                      minimum_size, natural_size,
3952                                      NULL, NULL);
3953 }
3954 
3955 static void
gtk_label_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum_width,gint * natural_width)3956 gtk_label_get_preferred_width_for_height (GtkWidget *widget,
3957                                           gint       height,
3958                                           gint      *minimum_width,
3959                                           gint      *natural_width)
3960 {
3961   gtk_css_gadget_get_preferred_size (GTK_LABEL (widget)->priv->gadget,
3962                                      GTK_ORIENTATION_HORIZONTAL,
3963                                      height,
3964                                      minimum_width, natural_width,
3965                                      NULL, NULL);
3966 }
3967 
3968 static void
gtk_label_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum_height,gint * natural_height)3969 gtk_label_get_preferred_height_for_width (GtkWidget *widget,
3970                                           gint       width,
3971                                           gint      *minimum_height,
3972                                           gint      *natural_height)
3973 {
3974   gtk_css_gadget_get_preferred_size (GTK_LABEL (widget)->priv->gadget,
3975                                      GTK_ORIENTATION_VERTICAL,
3976                                      width,
3977                                      minimum_height, natural_height,
3978                                      NULL, NULL);
3979 }
3980 
3981 static void
gtk_label_get_preferred_height_and_baseline_for_width(GtkWidget * widget,gint width,gint * minimum_height,gint * natural_height,gint * minimum_baseline,gint * natural_baseline)3982 gtk_label_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
3983 						       gint       width,
3984 						       gint      *minimum_height,
3985 						       gint      *natural_height,
3986 						       gint      *minimum_baseline,
3987 						       gint      *natural_baseline)
3988 {
3989   gtk_css_gadget_get_preferred_size (GTK_LABEL (widget)->priv->gadget,
3990                                      GTK_ORIENTATION_VERTICAL,
3991                                      width,
3992                                      minimum_height, natural_height,
3993                                      minimum_baseline, natural_baseline);
3994 }
3995 
3996 static void
get_layout_location(GtkLabel * label,gint * xp,gint * yp)3997 get_layout_location (GtkLabel  *label,
3998                      gint      *xp,
3999                      gint      *yp)
4000 {
4001   GtkAllocation allocation;
4002   GtkWidget *widget;
4003   GtkLabelPrivate *priv;
4004   gint xpad, ypad;
4005   gint req_width, x, y;
4006   gint req_height;
4007   gfloat xalign, yalign;
4008   PangoRectangle logical;
4009   gint baseline, layout_baseline, baseline_offset;
4010 
4011   widget = GTK_WIDGET (label);
4012   priv   = label->priv;
4013 
4014   xalign = priv->xalign;
4015   yalign = priv->yalign;
4016 
4017 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4018   gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
4019 G_GNUC_END_IGNORE_DEPRECATIONS
4020 
4021   if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
4022     xalign = 1.0 - xalign;
4023 
4024   pango_layout_get_extents (priv->layout, NULL, &logical);
4025 
4026   if (priv->have_transform)
4027     {
4028       PangoContext *context = gtk_widget_get_pango_context (widget);
4029       const PangoMatrix *matrix = pango_context_get_matrix (context);
4030       pango_matrix_transform_rectangle (matrix, &logical);
4031     }
4032 
4033   pango_extents_to_pixels (&logical, NULL);
4034 
4035   req_width  = logical.width;
4036   req_height = logical.height;
4037 
4038   req_width  += 2 * xpad;
4039   req_height += 2 * ypad;
4040 
4041   gtk_css_gadget_get_content_allocation (priv->gadget,
4042                                          &allocation,
4043                                          &baseline);
4044 
4045   x = floor (allocation.x + xpad + xalign * (allocation.width - req_width) - logical.x);
4046 
4047   baseline_offset = 0;
4048   if (baseline != -1 && !priv->have_transform)
4049     {
4050       layout_baseline = pango_layout_get_baseline (priv->layout) / PANGO_SCALE;
4051       baseline_offset = baseline - layout_baseline;
4052       yalign = 0.0; /* Can't support yalign while baseline aligning */
4053     }
4054 
4055   /* bgo#315462 - For single-line labels, *do* align the requisition with
4056    * respect to the allocation, even if we are under-allocated.  For multi-line
4057    * labels, always show the top of the text when they are under-allocated.  The
4058    * rationale is this:
4059    *
4060    * - Single-line labels appear in GtkButtons, and it is very easy to get them
4061    *   to be smaller than their requisition.  The button may clip the label, but
4062    *   the label will still be able to show most of itself and the focus
4063    *   rectangle.  Also, it is fairly easy to read a single line of clipped text.
4064    *
4065    * - Multi-line labels should not be clipped to showing "something in the
4066    *   middle".  You want to read the first line, at least, to get some context.
4067    */
4068   if (pango_layout_get_line_count (priv->layout) == 1)
4069     y = floor (allocation.y + ypad + (allocation.height - req_height) * yalign) - logical.y + baseline_offset;
4070   else
4071     y = floor (allocation.y + ypad + MAX ((allocation.height - req_height) * yalign, 0)) - logical.y + baseline_offset;
4072 
4073   if (xp)
4074     *xp = x;
4075 
4076   if (yp)
4077     *yp = y;
4078 }
4079 
4080 static void
gtk_label_get_ink_rect(GtkLabel * label,GdkRectangle * rect)4081 gtk_label_get_ink_rect (GtkLabel     *label,
4082                         GdkRectangle *rect)
4083 {
4084   GtkLabelPrivate *priv = label->priv;
4085   GtkStyleContext *context;
4086   PangoRectangle ink_rect;
4087   GtkBorder extents;
4088   int x, y;
4089 
4090   gtk_label_ensure_layout (label);
4091   get_layout_location (label, &x, &y);
4092   pango_layout_get_pixel_extents (priv->layout, &ink_rect, NULL);
4093   context = gtk_widget_get_style_context (GTK_WIDGET (label));
4094   _gtk_css_shadows_value_get_extents (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_TEXT_SHADOW), &extents);
4095 
4096   rect->x = x + ink_rect.x - extents.left;
4097   rect->width = ink_rect.width + extents.left + extents.right;
4098   rect->y = y + ink_rect.y - extents.top;
4099   rect->height = ink_rect.height + extents.top + extents.bottom;
4100 }
4101 
4102 static void
gtk_label_size_allocate(GtkWidget * widget,GtkAllocation * allocation)4103 gtk_label_size_allocate (GtkWidget     *widget,
4104                          GtkAllocation *allocation)
4105 {
4106   GtkLabel *label = GTK_LABEL (widget);
4107   GtkLabelPrivate *priv = label->priv;
4108   GdkRectangle clip_rect, clip;
4109 
4110   GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
4111 
4112   gtk_css_gadget_allocate (priv->gadget,
4113                            allocation,
4114                            gtk_widget_get_allocated_baseline (widget),
4115                            &clip);
4116 
4117   if (priv->layout)
4118     gtk_label_update_layout_width (label);
4119 
4120   if (priv->select_info && priv->select_info->window)
4121     gdk_window_move_resize (priv->select_info->window,
4122                             allocation->x,
4123                             allocation->y,
4124                             allocation->width,
4125                             allocation->height);
4126 
4127   gtk_label_get_ink_rect (label, &clip_rect);
4128   gdk_rectangle_union (&clip_rect, &clip, &clip_rect);
4129   _gtk_widget_set_simple_clip (widget, &clip_rect);
4130 }
4131 
4132 static void
gtk_label_update_cursor(GtkLabel * label)4133 gtk_label_update_cursor (GtkLabel *label)
4134 {
4135   GtkLabelPrivate *priv = label->priv;
4136   GtkWidget *widget;
4137 
4138   if (!priv->select_info)
4139     return;
4140 
4141   widget = GTK_WIDGET (label);
4142 
4143   if (gtk_widget_get_realized (widget))
4144     {
4145       GdkDisplay *display;
4146       GdkCursor *cursor;
4147 
4148       if (gtk_widget_is_sensitive (widget))
4149         {
4150           display = gtk_widget_get_display (widget);
4151 
4152           if (priv->select_info->active_link)
4153             cursor = gdk_cursor_new_from_name (display, "pointer");
4154           else if (priv->select_info->selectable)
4155             cursor = gdk_cursor_new_from_name (display, "text");
4156           else
4157             cursor = NULL;
4158         }
4159       else
4160         cursor = NULL;
4161 
4162       gdk_window_set_cursor (priv->select_info->window, cursor);
4163 
4164       if (cursor)
4165         g_object_unref (cursor);
4166     }
4167 }
4168 
4169 static void
update_link_state(GtkLabel * label)4170 update_link_state (GtkLabel *label)
4171 {
4172   GtkLabelPrivate *priv = label->priv;
4173   GList *l;
4174   GtkStateFlags state;
4175 
4176   if (!priv->select_info)
4177     return;
4178 
4179   for (l = priv->select_info->links; l; l = l->next)
4180     {
4181       GtkLabelLink *link = l->data;
4182 
4183       state = gtk_widget_get_state_flags (GTK_WIDGET (label));
4184       if (link->visited)
4185         state |= GTK_STATE_FLAG_VISITED;
4186       else
4187         state |= GTK_STATE_FLAG_LINK;
4188       if (link == priv->select_info->active_link)
4189         {
4190           if (priv->select_info->link_clicked)
4191             state |= GTK_STATE_FLAG_ACTIVE;
4192           else
4193             state |= GTK_STATE_FLAG_PRELIGHT;
4194         }
4195       gtk_css_node_set_state (link->cssnode, state);
4196     }
4197 }
4198 
4199 static void
gtk_label_state_flags_changed(GtkWidget * widget,GtkStateFlags prev_state)4200 gtk_label_state_flags_changed (GtkWidget     *widget,
4201                                GtkStateFlags  prev_state)
4202 {
4203   GtkLabel *label = GTK_LABEL (widget);
4204   GtkLabelPrivate *priv = label->priv;
4205 
4206   if (priv->select_info)
4207     {
4208       if (!gtk_widget_is_sensitive (widget))
4209         gtk_label_select_region (label, 0, 0);
4210 
4211       gtk_label_update_cursor (label);
4212       update_link_state (label);
4213     }
4214 
4215   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
4216     GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
4217 }
4218 
4219 static void
gtk_label_style_updated(GtkWidget * widget)4220 gtk_label_style_updated (GtkWidget *widget)
4221 {
4222   GtkLabel *label = GTK_LABEL (widget);
4223   GtkLabelPrivate *priv = label->priv;
4224   GtkStyleContext *context;
4225   GtkCssStyleChange *change;
4226 
4227   GTK_WIDGET_CLASS (gtk_label_parent_class)->style_updated (widget);
4228 
4229   context = gtk_widget_get_style_context (widget);
4230   change = gtk_style_context_get_change (context);
4231 
4232   if (change == NULL || gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_FONT))
4233     {
4234       gtk_label_clear_layout (GTK_LABEL (widget));
4235       gtk_widget_queue_resize (label);
4236     }
4237 
4238   if (change == NULL || gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT_ATTRS) ||
4239       (priv->select_info && priv->select_info->links))
4240     gtk_label_update_layout_attributes (label);
4241 }
4242 
4243 static PangoDirection
get_cursor_direction(GtkLabel * label)4244 get_cursor_direction (GtkLabel *label)
4245 {
4246   GtkLabelPrivate *priv = label->priv;
4247   GSList *l;
4248 
4249   g_assert (priv->select_info);
4250 
4251   gtk_label_ensure_layout (label);
4252 
4253   for (l = pango_layout_get_lines_readonly (priv->layout); l; l = l->next)
4254     {
4255       PangoLayoutLine *line = l->data;
4256 
4257       /* If priv->select_info->selection_end is at the very end of
4258        * the line, we don't know if the cursor is on this line or
4259        * the next without looking ahead at the next line. (End
4260        * of paragraph is different from line break.) But it's
4261        * definitely in this paragraph, which is good enough
4262        * to figure out the resolved direction.
4263        */
4264        if (line->start_index + line->length >= priv->select_info->selection_end)
4265 	return line->resolved_dir;
4266     }
4267 
4268   return PANGO_DIRECTION_LTR;
4269 }
4270 
4271 static GtkLabelLink *
gtk_label_get_focus_link(GtkLabel * label)4272 gtk_label_get_focus_link (GtkLabel *label)
4273 {
4274   GtkLabelPrivate *priv = label->priv;
4275   GtkLabelSelectionInfo *info = priv->select_info;
4276   GList *l;
4277 
4278   if (!info)
4279     return NULL;
4280 
4281   if (info->selection_anchor != info->selection_end)
4282     return NULL;
4283 
4284   for (l = info->links; l; l = l->next)
4285     {
4286       GtkLabelLink *link = l->data;
4287       if (link->start <= info->selection_anchor &&
4288           info->selection_anchor <= link->end)
4289         return link;
4290     }
4291 
4292   return NULL;
4293 }
4294 
4295 static gboolean
gtk_label_draw(GtkWidget * widget,cairo_t * cr)4296 gtk_label_draw (GtkWidget *widget,
4297                 cairo_t   *cr)
4298 {
4299   gtk_css_gadget_draw (GTK_LABEL (widget)->priv->gadget, cr);
4300 
4301   return FALSE;
4302 }
4303 
4304 static void layout_to_window_coords (GtkLabel *label,
4305                                      gint     *x,
4306                                      gint     *y);
4307 
4308 static gboolean
gtk_label_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)4309 gtk_label_render (GtkCssGadget *gadget,
4310                   cairo_t      *cr,
4311                   int           x,
4312                   int           y,
4313                   int           width,
4314                   int           height,
4315                   gpointer      data)
4316 {
4317   GtkWidget *widget;
4318   GtkLabel *label;
4319   GtkLabelPrivate *priv;
4320   GtkLabelSelectionInfo *info;
4321   GtkStyleContext *context;
4322   gint lx, ly;
4323 
4324   widget = gtk_css_gadget_get_owner (gadget);
4325   label = GTK_LABEL (widget);
4326   priv = label->priv;
4327   info = priv->select_info;
4328 
4329   gtk_label_ensure_layout (label);
4330 
4331   context = gtk_widget_get_style_context (widget);
4332 
4333   if (GTK_IS_ACCEL_LABEL (widget))
4334     {
4335       guint ac_width = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
4336       width -= ac_width;
4337       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
4338         x += ac_width;
4339     }
4340 
4341   if (priv->text && (*priv->text != '\0'))
4342     {
4343       lx = ly = 0;
4344       layout_to_window_coords (label, &lx, &ly);
4345 
4346       gtk_render_layout (context, cr, lx, ly, priv->layout);
4347 
4348       if (info && (info->selection_anchor != info->selection_end))
4349         {
4350           gint range[2];
4351           cairo_region_t *clip;
4352 
4353           range[0] = info->selection_anchor;
4354           range[1] = info->selection_end;
4355 
4356           if (range[0] > range[1])
4357             {
4358               gint tmp = range[0];
4359               range[0] = range[1];
4360               range[1] = tmp;
4361             }
4362 
4363           clip = gdk_pango_layout_get_clip_region (priv->layout, lx, ly, range, 1);
4364 
4365           cairo_save (cr);
4366           gtk_style_context_save_to_node (context, info->selection_node);
4367 
4368           gdk_cairo_region (cr, clip);
4369           cairo_clip (cr);
4370 
4371           gtk_render_background (context, cr, x, y, width, height);
4372           gtk_render_layout (context, cr, lx, ly, priv->layout);
4373 
4374           gtk_style_context_restore (context);
4375           cairo_restore (cr);
4376           cairo_region_destroy (clip);
4377         }
4378       else if (info)
4379         {
4380           GtkLabelLink *focus_link;
4381           GtkLabelLink *active_link;
4382           gint range[2];
4383           cairo_region_t *clip;
4384           GdkRectangle rect;
4385 
4386           if (info->selectable &&
4387               gtk_widget_has_focus (widget) &&
4388               gtk_widget_is_drawable (widget))
4389             {
4390               PangoDirection cursor_direction;
4391 
4392               cursor_direction = get_cursor_direction (label);
4393               gtk_render_insertion_cursor (context, cr,
4394                                            lx, ly,
4395                                            priv->layout, priv->select_info->selection_end,
4396                                            cursor_direction);
4397             }
4398 
4399           focus_link = gtk_label_get_focus_link (label);
4400           active_link = info->active_link;
4401 
4402           if (active_link)
4403             {
4404               range[0] = active_link->start;
4405               range[1] = active_link->end;
4406 
4407               cairo_save (cr);
4408               gtk_style_context_save_to_node (context, active_link->cssnode);
4409 
4410               clip = gdk_pango_layout_get_clip_region (priv->layout, lx, ly, range, 1);
4411 
4412               gdk_cairo_region (cr, clip);
4413               cairo_clip (cr);
4414               cairo_region_destroy (clip);
4415 
4416               gtk_render_background (context, cr, x, y, width, height);
4417               gtk_render_layout (context, cr, lx, ly, priv->layout);
4418 
4419               gtk_style_context_restore (context);
4420               cairo_restore (cr);
4421             }
4422 
4423           if (focus_link && gtk_widget_has_visible_focus (widget))
4424             {
4425               range[0] = focus_link->start;
4426               range[1] = focus_link->end;
4427 
4428               clip = gdk_pango_layout_get_clip_region (priv->layout, lx, ly, range, 1);
4429               cairo_region_get_extents (clip, &rect);
4430 
4431               gtk_render_focus (context, cr, rect.x, rect.y, rect.width, rect.height);
4432 
4433               cairo_region_destroy (clip);
4434             }
4435         }
4436     }
4437 
4438   return FALSE;
4439 }
4440 
4441 static gboolean
separate_uline_pattern(const gchar * str,guint * accel_key,gchar ** new_str,gchar ** pattern)4442 separate_uline_pattern (const gchar  *str,
4443                         guint        *accel_key,
4444                         gchar       **new_str,
4445                         gchar       **pattern)
4446 {
4447   gboolean underscore;
4448   const gchar *src;
4449   gchar *dest;
4450   gchar *pattern_dest;
4451 
4452   *accel_key = GDK_KEY_VoidSymbol;
4453   *new_str = g_new (gchar, strlen (str) + 1);
4454   *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
4455 
4456   underscore = FALSE;
4457 
4458   src = str;
4459   dest = *new_str;
4460   pattern_dest = *pattern;
4461 
4462   while (*src)
4463     {
4464       gunichar c;
4465       const gchar *next_src;
4466 
4467       c = g_utf8_get_char (src);
4468       if (c == (gunichar)-1)
4469 	{
4470 	  g_warning ("Invalid input string");
4471 	  g_free (*new_str);
4472 	  g_free (*pattern);
4473 
4474 	  return FALSE;
4475 	}
4476       next_src = g_utf8_next_char (src);
4477 
4478       if (underscore)
4479 	{
4480 	  if (c == '_')
4481 	    *pattern_dest++ = ' ';
4482 	  else
4483 	    {
4484 	      *pattern_dest++ = '_';
4485 	      if (*accel_key == GDK_KEY_VoidSymbol)
4486 		*accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
4487 	    }
4488 
4489 	  while (src < next_src)
4490 	    *dest++ = *src++;
4491 
4492 	  underscore = FALSE;
4493 	}
4494       else
4495 	{
4496 	  if (c == '_')
4497 	    {
4498 	      underscore = TRUE;
4499 	      src = next_src;
4500 	    }
4501 	  else
4502 	    {
4503 	      while (src < next_src)
4504 		*dest++ = *src++;
4505 
4506 	      *pattern_dest++ = ' ';
4507 	    }
4508 	}
4509     }
4510 
4511   *dest = 0;
4512   *pattern_dest = 0;
4513 
4514   return TRUE;
4515 }
4516 
4517 static void
gtk_label_set_uline_text_internal(GtkLabel * label,const gchar * str)4518 gtk_label_set_uline_text_internal (GtkLabel    *label,
4519 				   const gchar *str)
4520 {
4521   GtkLabelPrivate *priv = label->priv;
4522   guint accel_key = GDK_KEY_VoidSymbol;
4523   gchar *new_str;
4524   gchar *pattern;
4525 
4526   g_return_if_fail (GTK_IS_LABEL (label));
4527   g_return_if_fail (str != NULL);
4528 
4529   /* Split text into the base text and a separate pattern
4530    * of underscores.
4531    */
4532   if (!separate_uline_pattern (str, &accel_key, &new_str, &pattern))
4533     return;
4534 
4535   gtk_label_set_text_internal (label, new_str);
4536   gtk_label_set_pattern_internal (label, pattern, TRUE);
4537   priv->mnemonic_keyval = accel_key;
4538 
4539   g_free (pattern);
4540 }
4541 
4542 /**
4543  * gtk_label_set_text_with_mnemonic:
4544  * @label: a #GtkLabel
4545  * @str: a string
4546  *
4547  * Sets the label’s text from the string @str.
4548  * If characters in @str are preceded by an underscore, they are underlined
4549  * indicating that they represent a keyboard accelerator called a mnemonic.
4550  * The mnemonic key can be used to activate another widget, chosen
4551  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
4552  **/
4553 void
gtk_label_set_text_with_mnemonic(GtkLabel * label,const gchar * str)4554 gtk_label_set_text_with_mnemonic (GtkLabel    *label,
4555 				  const gchar *str)
4556 {
4557   g_return_if_fail (GTK_IS_LABEL (label));
4558   g_return_if_fail (str != NULL);
4559 
4560   g_object_freeze_notify (G_OBJECT (label));
4561 
4562   gtk_label_set_label_internal (label, g_strdup (str));
4563   gtk_label_set_use_markup_internal (label, FALSE);
4564   gtk_label_set_use_underline_internal (label, TRUE);
4565 
4566   gtk_label_recalculate (label);
4567 
4568   g_object_thaw_notify (G_OBJECT (label));
4569 }
4570 
4571 static void
gtk_label_realize(GtkWidget * widget)4572 gtk_label_realize (GtkWidget *widget)
4573 {
4574   GtkLabel *label = GTK_LABEL (widget);
4575   GtkLabelPrivate *priv = label->priv;
4576 
4577   GTK_WIDGET_CLASS (gtk_label_parent_class)->realize (widget);
4578 
4579   if (priv->select_info)
4580     gtk_label_create_window (label);
4581 }
4582 
4583 static void
gtk_label_unrealize(GtkWidget * widget)4584 gtk_label_unrealize (GtkWidget *widget)
4585 {
4586   GtkLabel *label = GTK_LABEL (widget);
4587   GtkLabelPrivate *priv = label->priv;
4588 
4589   if (priv->select_info)
4590     gtk_label_destroy_window (label);
4591 
4592   GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
4593 }
4594 
4595 static void
gtk_label_map(GtkWidget * widget)4596 gtk_label_map (GtkWidget *widget)
4597 {
4598   GtkLabel *label = GTK_LABEL (widget);
4599   GtkLabelPrivate *priv = label->priv;
4600 
4601   GTK_WIDGET_CLASS (gtk_label_parent_class)->map (widget);
4602 
4603   if (priv->select_info)
4604     gdk_window_show (priv->select_info->window);
4605 }
4606 
4607 static void
gtk_label_unmap(GtkWidget * widget)4608 gtk_label_unmap (GtkWidget *widget)
4609 {
4610   GtkLabel *label = GTK_LABEL (widget);
4611   GtkLabelPrivate *priv = label->priv;
4612 
4613   if (priv->select_info)
4614     {
4615       gdk_window_hide (priv->select_info->window);
4616 
4617       if (priv->select_info->popup_menu)
4618         {
4619           gtk_widget_destroy (priv->select_info->popup_menu);
4620           priv->select_info->popup_menu = NULL;
4621         }
4622     }
4623 
4624   GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap (widget);
4625 }
4626 
4627 static void
window_to_layout_coords(GtkLabel * label,gint * x,gint * y)4628 window_to_layout_coords (GtkLabel *label,
4629                          gint     *x,
4630                          gint     *y)
4631 {
4632   GtkAllocation allocation;
4633   gint lx, ly;
4634 
4635   /* get layout location in widget->window coords */
4636   get_layout_location (label, &lx, &ly);
4637   gtk_widget_get_allocation (GTK_WIDGET (label), &allocation);
4638 
4639   *x += allocation.x; /* go to widget->window */
4640   *x -= lx;                   /* go to layout */
4641 
4642   *y += allocation.y; /* go to widget->window */
4643   *y -= ly;                   /* go to layout */
4644 }
4645 
4646 static void
layout_to_window_coords(GtkLabel * label,gint * x,gint * y)4647 layout_to_window_coords (GtkLabel *label,
4648                          gint     *x,
4649                          gint     *y)
4650 {
4651   gint lx, ly;
4652   GtkAllocation allocation;
4653 
4654   /* get layout location in widget->window coords */
4655   get_layout_location (label, &lx, &ly);
4656   gtk_widget_get_allocation (GTK_WIDGET (label), &allocation);
4657 
4658   *x += lx;           /* go to widget->window */
4659   *x -= allocation.x; /* go to selection window */
4660 
4661   *y += ly;           /* go to widget->window */
4662   *y -= allocation.y; /* go to selection window */
4663 }
4664 
4665 static gboolean
get_layout_index(GtkLabel * label,gint x,gint y,gint * index)4666 get_layout_index (GtkLabel *label,
4667                   gint      x,
4668                   gint      y,
4669                   gint     *index)
4670 {
4671   GtkLabelPrivate *priv = label->priv;
4672   gint trailing = 0;
4673   const gchar *cluster;
4674   const gchar *cluster_end;
4675   gboolean inside;
4676 
4677   *index = 0;
4678 
4679   gtk_label_ensure_layout (label);
4680 
4681   window_to_layout_coords (label, &x, &y);
4682 
4683   x *= PANGO_SCALE;
4684   y *= PANGO_SCALE;
4685 
4686   inside = pango_layout_xy_to_index (priv->layout,
4687                                      x, y,
4688                                      index, &trailing);
4689 
4690   cluster = priv->text + *index;
4691   cluster_end = cluster;
4692   while (trailing)
4693     {
4694       cluster_end = g_utf8_next_char (cluster_end);
4695       --trailing;
4696     }
4697 
4698   *index += (cluster_end - cluster);
4699 
4700   return inside;
4701 }
4702 
4703 static gboolean
range_is_in_ellipsis_full(GtkLabel * label,gint range_start,gint range_end,gint * ellipsis_start,gint * ellipsis_end)4704 range_is_in_ellipsis_full (GtkLabel *label,
4705                            gint      range_start,
4706                            gint      range_end,
4707                            gint     *ellipsis_start,
4708                            gint     *ellipsis_end)
4709 {
4710   GtkLabelPrivate *priv = label->priv;
4711   PangoLayoutIter *iter;
4712   gboolean in_ellipsis;
4713 
4714   if (!priv->ellipsize)
4715     return FALSE;
4716 
4717   gtk_label_ensure_layout (label);
4718 
4719   if (!pango_layout_is_ellipsized (priv->layout))
4720     return FALSE;
4721 
4722   iter = pango_layout_get_iter (priv->layout);
4723 
4724   in_ellipsis = FALSE;
4725 
4726   do {
4727     PangoLayoutRun *run;
4728 
4729     run = pango_layout_iter_get_run_readonly (iter);
4730     if (run)
4731       {
4732         PangoItem *item;
4733 
4734         item = ((PangoGlyphItem*)run)->item;
4735 
4736         if (item->offset <= range_start && range_end <= item->offset + item->length)
4737           {
4738             if (item->analysis.flags & PANGO_ANALYSIS_FLAG_IS_ELLIPSIS)
4739               {
4740                 if (ellipsis_start)
4741                   *ellipsis_start = item->offset;
4742                 if (ellipsis_end)
4743                   *ellipsis_end = item->offset + item->length;
4744                 in_ellipsis = TRUE;
4745               }
4746             break;
4747           }
4748         else if (item->offset + item->length >= range_end)
4749           break;
4750       }
4751   } while (pango_layout_iter_next_run (iter));
4752 
4753   pango_layout_iter_free (iter);
4754 
4755   return in_ellipsis;
4756 }
4757 
4758 static gboolean
range_is_in_ellipsis(GtkLabel * label,gint range_start,gint range_end)4759 range_is_in_ellipsis (GtkLabel *label,
4760                       gint      range_start,
4761                       gint      range_end)
4762 {
4763   return range_is_in_ellipsis_full (label, range_start, range_end, NULL, NULL);
4764 }
4765 
4766 static void
gtk_label_select_word(GtkLabel * label)4767 gtk_label_select_word (GtkLabel *label)
4768 {
4769   GtkLabelPrivate *priv = label->priv;
4770   gint min, max;
4771 
4772   gint start_index = gtk_label_move_backward_word (label, priv->select_info->selection_end);
4773   gint end_index = gtk_label_move_forward_word (label, priv->select_info->selection_end);
4774 
4775   min = MIN (priv->select_info->selection_anchor,
4776 	     priv->select_info->selection_end);
4777   max = MAX (priv->select_info->selection_anchor,
4778 	     priv->select_info->selection_end);
4779 
4780   min = MIN (min, start_index);
4781   max = MAX (max, end_index);
4782 
4783   gtk_label_select_region_index (label, min, max);
4784 }
4785 
4786 static void
gtk_label_grab_focus(GtkWidget * widget)4787 gtk_label_grab_focus (GtkWidget *widget)
4788 {
4789   GtkLabel *label = GTK_LABEL (widget);
4790   GtkLabelPrivate *priv = label->priv;
4791   gboolean select_on_focus;
4792   GtkLabelLink *link;
4793   GList *l;
4794 
4795   if (priv->select_info == NULL)
4796     return;
4797 
4798   GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
4799 
4800   if (priv->select_info->selectable)
4801     {
4802       g_object_get (gtk_widget_get_settings (widget),
4803                     "gtk-label-select-on-focus",
4804                     &select_on_focus,
4805                     NULL);
4806 
4807       if (select_on_focus && !priv->in_click)
4808         gtk_label_select_region (label, 0, -1);
4809     }
4810   else
4811     {
4812       if (priv->select_info->links && !priv->in_click)
4813         {
4814           for (l = priv->select_info->links; l; l = l->next)
4815             {
4816               link = l->data;
4817               if (!range_is_in_ellipsis (label, link->start, link->end))
4818                 {
4819                   priv->select_info->selection_anchor = link->start;
4820                   priv->select_info->selection_end = link->start;
4821                   _gtk_label_accessible_focus_link_changed (label);
4822                   break;
4823                 }
4824             }
4825         }
4826     }
4827 }
4828 
4829 static gboolean
gtk_label_focus(GtkWidget * widget,GtkDirectionType direction)4830 gtk_label_focus (GtkWidget        *widget,
4831                  GtkDirectionType  direction)
4832 {
4833   GtkLabel *label = GTK_LABEL (widget);
4834   GtkLabelPrivate *priv = label->priv;
4835   GtkLabelSelectionInfo *info = priv->select_info;
4836   GtkLabelLink *focus_link;
4837   GList *l;
4838 
4839   if (!gtk_widget_is_focus (widget))
4840     {
4841       gtk_widget_grab_focus (widget);
4842       if (info)
4843         {
4844           focus_link = gtk_label_get_focus_link (label);
4845           if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
4846             {
4847               for (l = g_list_last (info->links); l; l = l->prev)
4848                 {
4849                   focus_link = l->data;
4850                   if (!range_is_in_ellipsis (label, focus_link->start, focus_link->end))
4851                     {
4852                       info->selection_anchor = focus_link->start;
4853                       info->selection_end = focus_link->start;
4854                       _gtk_label_accessible_focus_link_changed (label);
4855                     }
4856                 }
4857             }
4858         }
4859 
4860       return TRUE;
4861     }
4862 
4863   if (!info)
4864     return FALSE;
4865 
4866   if (info->selectable)
4867     {
4868       gint index;
4869 
4870       if (info->selection_anchor != info->selection_end)
4871         goto out;
4872 
4873       index = info->selection_anchor;
4874 
4875       if (direction == GTK_DIR_TAB_FORWARD)
4876         for (l = info->links; l; l = l->next)
4877           {
4878             GtkLabelLink *link = l->data;
4879 
4880             if (link->start > index)
4881               {
4882                 if (!range_is_in_ellipsis (label, link->start, link->end))
4883                   {
4884                     gtk_label_select_region_index (label, link->start, link->start);
4885                     _gtk_label_accessible_focus_link_changed (label);
4886                     return TRUE;
4887                   }
4888               }
4889           }
4890       else if (direction == GTK_DIR_TAB_BACKWARD)
4891         for (l = g_list_last (info->links); l; l = l->prev)
4892           {
4893             GtkLabelLink *link = l->data;
4894 
4895             if (link->end < index)
4896               {
4897                 if (!range_is_in_ellipsis (label, link->start, link->end))
4898                   {
4899                     gtk_label_select_region_index (label, link->start, link->start);
4900                     _gtk_label_accessible_focus_link_changed (label);
4901                     return TRUE;
4902                   }
4903               }
4904           }
4905 
4906       goto out;
4907     }
4908   else
4909     {
4910       focus_link = gtk_label_get_focus_link (label);
4911       switch (direction)
4912         {
4913         case GTK_DIR_TAB_FORWARD:
4914           if (focus_link)
4915             {
4916               l = g_list_find (info->links, focus_link);
4917               l = l->next;
4918             }
4919           else
4920             l = info->links;
4921           for (; l; l = l->next)
4922             {
4923               GtkLabelLink *link = l->data;
4924               if (!range_is_in_ellipsis (label, link->start, link->end))
4925                 break;
4926             }
4927           break;
4928 
4929         case GTK_DIR_TAB_BACKWARD:
4930           if (focus_link)
4931             {
4932               l = g_list_find (info->links, focus_link);
4933               l = l->prev;
4934             }
4935           else
4936             l = g_list_last (info->links);
4937           for (; l; l = l->prev)
4938             {
4939               GtkLabelLink *link = l->data;
4940               if (!range_is_in_ellipsis (label, link->start, link->end))
4941                 break;
4942             }
4943           break;
4944 
4945         default:
4946           goto out;
4947         }
4948 
4949       if (l)
4950         {
4951           focus_link = l->data;
4952           info->selection_anchor = focus_link->start;
4953           info->selection_end = focus_link->start;
4954           _gtk_label_accessible_focus_link_changed (label);
4955           gtk_widget_queue_draw (widget);
4956 
4957           return TRUE;
4958         }
4959     }
4960 
4961 out:
4962 
4963   return FALSE;
4964 }
4965 
4966 static void
gtk_label_multipress_gesture_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble widget_x,gdouble widget_y,GtkLabel * label)4967 gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
4968                                       gint                  n_press,
4969                                       gdouble               widget_x,
4970                                       gdouble               widget_y,
4971                                       GtkLabel             *label)
4972 {
4973   GtkLabelPrivate *priv = label->priv;
4974   GtkLabelSelectionInfo *info = priv->select_info;
4975   GtkWidget *widget = GTK_WIDGET (label);
4976   GdkEventSequence *sequence;
4977   const GdkEvent *event;
4978   guint button;
4979 
4980   if (info == NULL)
4981     {
4982       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
4983       return;
4984     }
4985 
4986   button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
4987   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
4988   event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
4989   gtk_label_update_active_link (widget, widget_x, widget_y);
4990 
4991   gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
4992 
4993   if (info->active_link)
4994     {
4995       if (gdk_event_triggers_context_menu (event))
4996         {
4997           info->link_clicked = 1;
4998           update_link_state (label);
4999           gtk_label_do_popup (label, event);
5000           return;
5001         }
5002       else if (button == GDK_BUTTON_PRIMARY)
5003         {
5004           info->link_clicked = 1;
5005           update_link_state (label);
5006           gtk_widget_queue_draw (widget);
5007           if (!info->selectable)
5008             return;
5009         }
5010     }
5011 
5012   if (!info->selectable)
5013     {
5014       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
5015       return;
5016     }
5017 
5018   info->in_drag = FALSE;
5019   info->select_words = FALSE;
5020 
5021   if (gdk_event_triggers_context_menu (event))
5022     gtk_label_do_popup (label, event);
5023   else if (button == GDK_BUTTON_PRIMARY)
5024     {
5025       if (!gtk_widget_has_focus (widget))
5026         {
5027           priv->in_click = TRUE;
5028           gtk_widget_grab_focus (widget);
5029           priv->in_click = FALSE;
5030         }
5031 
5032       if (n_press == 3)
5033         gtk_label_select_region_index (label, 0, strlen (priv->text));
5034       else if (n_press == 2)
5035         {
5036           info->select_words = TRUE;
5037           gtk_label_select_word (label);
5038         }
5039     }
5040   else
5041     {
5042       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
5043       return;
5044     }
5045 
5046   if (n_press >= 3)
5047     gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
5048 }
5049 
5050 static void
gtk_label_multipress_gesture_released(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,GtkLabel * label)5051 gtk_label_multipress_gesture_released (GtkGestureMultiPress *gesture,
5052                                        gint                  n_press,
5053                                        gdouble               x,
5054                                        gdouble               y,
5055                                        GtkLabel             *label)
5056 {
5057   GtkLabelPrivate *priv = label->priv;
5058   GtkLabelSelectionInfo *info = priv->select_info;
5059   GdkEventSequence *sequence;
5060   gint index;
5061 
5062   if (info == NULL)
5063     return;
5064 
5065   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
5066 
5067   if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
5068     return;
5069 
5070   if (n_press != 1)
5071     return;
5072 
5073   if (info->in_drag)
5074     {
5075       info->in_drag = 0;
5076       get_layout_index (label, x, y, &index);
5077       gtk_label_select_region_index (label, index, index);
5078     }
5079   else if (info->active_link &&
5080            info->selection_anchor == info->selection_end &&
5081            info->link_clicked)
5082     {
5083       emit_activate_link (label, info->active_link);
5084       info->link_clicked = 0;
5085     }
5086 }
5087 
5088 static void
connect_mnemonics_visible_notify(GtkLabel * label)5089 connect_mnemonics_visible_notify (GtkLabel *label)
5090 {
5091   GtkLabelPrivate *priv = label->priv;
5092   GtkWidget *toplevel;
5093   gboolean connected;
5094 
5095   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
5096 
5097   if (!GTK_IS_WINDOW (toplevel))
5098     return;
5099 
5100   /* always set up this widgets initial value */
5101   priv->mnemonics_visible =
5102     gtk_window_get_mnemonics_visible (GTK_WINDOW (toplevel));
5103 
5104   connected =
5105     GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (toplevel), quark_mnemonics_visible_connected));
5106 
5107   if (!connected)
5108     {
5109       g_signal_connect (toplevel,
5110                         "notify::mnemonics-visible",
5111                         G_CALLBACK (label_mnemonics_visible_changed),
5112                         label);
5113       g_object_set_qdata (G_OBJECT (toplevel),
5114                           quark_mnemonics_visible_connected,
5115                           GINT_TO_POINTER (1));
5116     }
5117 }
5118 
5119 static void
drag_begin_cb(GtkWidget * widget,GdkDragContext * context,gpointer data)5120 drag_begin_cb (GtkWidget      *widget,
5121                GdkDragContext *context,
5122                gpointer        data)
5123 {
5124   GtkLabel *label = GTK_LABEL (widget);
5125   GtkLabelPrivate *priv = label->priv;
5126   cairo_surface_t *surface = NULL;
5127 
5128   g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
5129 
5130   if ((priv->select_info->selection_anchor !=
5131        priv->select_info->selection_end) &&
5132       priv->text)
5133     {
5134       gint start, end;
5135       gint len;
5136 
5137       start = MIN (priv->select_info->selection_anchor,
5138                    priv->select_info->selection_end);
5139       end = MAX (priv->select_info->selection_anchor,
5140                  priv->select_info->selection_end);
5141 
5142       len = strlen (priv->text);
5143 
5144       if (end > len)
5145         end = len;
5146 
5147       if (start > len)
5148         start = len;
5149 
5150       surface = _gtk_text_util_create_drag_icon (widget,
5151                                                  priv->text + start,
5152                                                  end - start);
5153     }
5154 
5155   if (surface)
5156     {
5157       gtk_drag_set_icon_surface (context, surface);
5158       cairo_surface_destroy (surface);
5159     }
5160   else
5161     {
5162       gtk_drag_set_icon_default (context);
5163     }
5164 }
5165 
5166 static void
gtk_label_drag_gesture_begin(GtkGestureDrag * gesture,gdouble start_x,gdouble start_y,GtkLabel * label)5167 gtk_label_drag_gesture_begin (GtkGestureDrag *gesture,
5168                               gdouble         start_x,
5169                               gdouble         start_y,
5170                               GtkLabel       *label)
5171 {
5172   GtkLabelPrivate *priv = label->priv;
5173   GtkLabelSelectionInfo *info = priv->select_info;
5174   GdkModifierType state_mask;
5175   GdkEventSequence *sequence;
5176   const GdkEvent *event;
5177   gint min, max, index;
5178 
5179   if (!info || !info->selectable)
5180     {
5181       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
5182       return;
5183     }
5184 
5185   get_layout_index (label, start_x, start_y, &index);
5186   min = MIN (info->selection_anchor, info->selection_end);
5187   max = MAX (info->selection_anchor, info->selection_end);
5188 
5189   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
5190   event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
5191   gdk_event_get_state (event, &state_mask);
5192 
5193   if ((info->selection_anchor != info->selection_end) &&
5194       (state_mask & GDK_SHIFT_MASK))
5195     {
5196       if (index > min && index < max)
5197         {
5198           /* truncate selection, but keep it as big as possible */
5199           if (index - min > max - index)
5200             max = index;
5201           else
5202             min = index;
5203         }
5204       else
5205         {
5206           /* extend (same as motion) */
5207           min = MIN (min, index);
5208           max = MAX (max, index);
5209         }
5210 
5211       /* ensure the anchor is opposite index */
5212       if (index == min)
5213         {
5214           gint tmp = min;
5215           min = max;
5216           max = tmp;
5217         }
5218 
5219       gtk_label_select_region_index (label, min, max);
5220     }
5221   else
5222     {
5223       if (min < max && min <= index && index <= max)
5224         {
5225           info->in_drag = TRUE;
5226           info->drag_start_x = start_x;
5227           info->drag_start_y = start_y;
5228         }
5229       else
5230         /* start a replacement */
5231         gtk_label_select_region_index (label, index, index);
5232     }
5233 }
5234 
5235 static void
gtk_label_drag_gesture_update(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,GtkLabel * label)5236 gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
5237                                gdouble         offset_x,
5238                                gdouble         offset_y,
5239                                GtkLabel       *label)
5240 {
5241   GtkLabelPrivate *priv = label->priv;
5242   GtkLabelSelectionInfo *info = priv->select_info;
5243   GtkWidget *widget = GTK_WIDGET (label);
5244   GdkEventSequence *sequence;
5245   gdouble x, y;
5246   gint index;
5247 
5248   if (info == NULL || !info->selectable)
5249     return;
5250 
5251   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
5252   gtk_gesture_get_point (GTK_GESTURE (gesture), sequence, &x, &y);
5253 
5254   if (info->in_drag)
5255     {
5256       if (gtk_drag_check_threshold (widget,
5257 				    info->drag_start_x,
5258 				    info->drag_start_y,
5259 				    x, y))
5260 	{
5261 	  GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
5262           const GdkEvent *event;
5263 
5264           event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
5265 	  gtk_target_list_add_text_targets (target_list, 0);
5266 
5267           g_signal_connect (widget, "drag-begin",
5268                             G_CALLBACK (drag_begin_cb), NULL);
5269 	  gtk_drag_begin_with_coordinates (widget, target_list,
5270                                            GDK_ACTION_COPY,
5271                                            1, (GdkEvent*) event,
5272                                            info->drag_start_x,
5273                                            info->drag_start_y);
5274 
5275 	  info->in_drag = FALSE;
5276 
5277 	  gtk_target_list_unref (target_list);
5278 	}
5279     }
5280   else
5281     {
5282       get_layout_index (label, x, y, &index);
5283 
5284       if (index != info->selection_anchor)
5285         gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
5286 
5287       if (info->select_words)
5288         {
5289           gint min, max;
5290           gint old_min, old_max;
5291           gint anchor, end;
5292 
5293           min = gtk_label_move_backward_word (label, index);
5294           max = gtk_label_move_forward_word (label, index);
5295 
5296           anchor = info->selection_anchor;
5297           end = info->selection_end;
5298 
5299           old_min = MIN (anchor, end);
5300           old_max = MAX (anchor, end);
5301 
5302           if (min < old_min)
5303             {
5304               anchor = min;
5305               end = old_max;
5306             }
5307           else if (old_max < max)
5308             {
5309               anchor = max;
5310               end = old_min;
5311             }
5312           else if (anchor == old_min)
5313             {
5314               if (anchor != min)
5315                 anchor = max;
5316             }
5317           else
5318             {
5319               if (anchor != max)
5320                 anchor = min;
5321             }
5322 
5323           gtk_label_select_region_index (label, anchor, end);
5324         }
5325       else
5326         gtk_label_select_region_index (label, info->selection_anchor, index);
5327     }
5328 }
5329 
5330 static void
gtk_label_update_active_link(GtkWidget * widget,gdouble x,gdouble y)5331 gtk_label_update_active_link (GtkWidget *widget,
5332                               gdouble    x,
5333                               gdouble    y)
5334 {
5335   GtkLabel *label = GTK_LABEL (widget);
5336   GtkLabelPrivate *priv = label->priv;
5337   GtkLabelSelectionInfo *info = priv->select_info;
5338   gint index;
5339 
5340   if (info == NULL)
5341     return;
5342 
5343   if (info->links && !info->in_drag)
5344     {
5345       GList *l;
5346       GtkLabelLink *link;
5347       gboolean found = FALSE;
5348 
5349       if (info->selection_anchor == info->selection_end)
5350         {
5351           if (get_layout_index (label, x, y, &index))
5352             {
5353               for (l = info->links; l != NULL; l = l->next)
5354                 {
5355                   link = l->data;
5356                   if (index >= link->start && index <= link->end)
5357                     {
5358                       if (!range_is_in_ellipsis (label, link->start, link->end))
5359                         found = TRUE;
5360                       break;
5361                     }
5362                 }
5363             }
5364         }
5365 
5366       if (found)
5367         {
5368           if (info->active_link != link)
5369             {
5370               info->link_clicked = 0;
5371               info->active_link = link;
5372               update_link_state (label);
5373               gtk_label_update_cursor (label);
5374               gtk_widget_queue_draw (widget);
5375             }
5376         }
5377       else
5378         {
5379           if (info->active_link != NULL)
5380             {
5381               info->link_clicked = 0;
5382               info->active_link = NULL;
5383               update_link_state (label);
5384               gtk_label_update_cursor (label);
5385               gtk_widget_queue_draw (widget);
5386             }
5387         }
5388     }
5389 }
5390 
5391 static gboolean
gtk_label_motion(GtkWidget * widget,GdkEventMotion * event)5392 gtk_label_motion (GtkWidget      *widget,
5393                   GdkEventMotion *event)
5394 {
5395   gdouble x, y;
5396 
5397   gdk_event_get_coords ((GdkEvent *) event, &x, &y);
5398   gtk_label_update_active_link (widget, x, y);
5399 
5400   return GTK_WIDGET_CLASS (gtk_label_parent_class)->motion_notify_event (widget, event);
5401 }
5402 
5403 static gboolean
gtk_label_leave_notify(GtkWidget * widget,GdkEventCrossing * event)5404 gtk_label_leave_notify (GtkWidget        *widget,
5405                         GdkEventCrossing *event)
5406 {
5407   GtkLabel *label = GTK_LABEL (widget);
5408   GtkLabelPrivate *priv = label->priv;
5409 
5410   if (priv->select_info)
5411     {
5412       priv->select_info->active_link = NULL;
5413       gtk_label_update_cursor (label);
5414       gtk_widget_queue_draw (widget);
5415     }
5416 
5417   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event)
5418     return GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event (widget, event);
5419 
5420  return FALSE;
5421 }
5422 
5423 static void
gtk_label_create_window(GtkLabel * label)5424 gtk_label_create_window (GtkLabel *label)
5425 {
5426   GtkLabelPrivate *priv = label->priv;
5427   GtkAllocation allocation;
5428   GtkWidget *widget;
5429   GdkWindowAttr attributes;
5430   gint attributes_mask;
5431 
5432   g_assert (priv->select_info);
5433   widget = GTK_WIDGET (label);
5434   g_assert (gtk_widget_get_realized (widget));
5435 
5436   if (priv->select_info->window)
5437     return;
5438 
5439   gtk_widget_get_allocation (widget, &allocation);
5440 
5441   attributes.x = allocation.x;
5442   attributes.y = allocation.y;
5443   attributes.width = allocation.width;
5444   attributes.height = allocation.height;
5445   attributes.window_type = GDK_WINDOW_CHILD;
5446   attributes.wclass = GDK_INPUT_ONLY;
5447   attributes.override_redirect = TRUE;
5448   attributes.event_mask = gtk_widget_get_events (widget) |
5449     GDK_BUTTON_PRESS_MASK        |
5450     GDK_BUTTON_RELEASE_MASK      |
5451     GDK_LEAVE_NOTIFY_MASK        |
5452     GDK_BUTTON_MOTION_MASK       |
5453     GDK_POINTER_MOTION_MASK;
5454   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
5455   if (gtk_widget_is_sensitive (widget) && priv->select_info->selectable)
5456     {
5457       attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
5458 						      GDK_XTERM);
5459       attributes_mask |= GDK_WA_CURSOR;
5460     }
5461 
5462 
5463   priv->select_info->window = gdk_window_new (gtk_widget_get_window (widget),
5464                                                &attributes, attributes_mask);
5465   gtk_widget_register_window (widget, priv->select_info->window);
5466 
5467   if (attributes_mask & GDK_WA_CURSOR)
5468     g_object_unref (attributes.cursor);
5469 }
5470 
5471 static void
gtk_label_destroy_window(GtkLabel * label)5472 gtk_label_destroy_window (GtkLabel *label)
5473 {
5474   GtkLabelPrivate *priv = label->priv;
5475 
5476   g_assert (priv->select_info);
5477 
5478   if (priv->select_info->window == NULL)
5479     return;
5480 
5481   gtk_widget_unregister_window (GTK_WIDGET (label), priv->select_info->window);
5482   gdk_window_destroy (priv->select_info->window);
5483   priv->select_info->window = NULL;
5484 }
5485 
5486 static void
gtk_label_ensure_select_info(GtkLabel * label)5487 gtk_label_ensure_select_info (GtkLabel *label)
5488 {
5489   GtkLabelPrivate *priv = label->priv;
5490 
5491   if (priv->select_info == NULL)
5492     {
5493       priv->select_info = g_new0 (GtkLabelSelectionInfo, 1);
5494 
5495       gtk_widget_set_can_focus (GTK_WIDGET (label), TRUE);
5496 
5497       if (gtk_widget_get_realized (GTK_WIDGET (label)))
5498 	gtk_label_create_window (label);
5499 
5500       if (gtk_widget_get_mapped (GTK_WIDGET (label)))
5501         gdk_window_show (priv->select_info->window);
5502 
5503       priv->select_info->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (label));
5504       g_signal_connect (priv->select_info->drag_gesture, "drag-begin",
5505                         G_CALLBACK (gtk_label_drag_gesture_begin), label);
5506       g_signal_connect (priv->select_info->drag_gesture, "drag-update",
5507                         G_CALLBACK (gtk_label_drag_gesture_update), label);
5508       gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (priv->select_info->drag_gesture), TRUE);
5509 
5510       priv->select_info->multipress_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (label));
5511       g_signal_connect (priv->select_info->multipress_gesture, "pressed",
5512                         G_CALLBACK (gtk_label_multipress_gesture_pressed), label);
5513       g_signal_connect (priv->select_info->multipress_gesture, "released",
5514                         G_CALLBACK (gtk_label_multipress_gesture_released), label);
5515       gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->select_info->multipress_gesture), 0);
5516       gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (priv->select_info->multipress_gesture), TRUE);
5517     }
5518 }
5519 
5520 static void
gtk_label_clear_select_info(GtkLabel * label)5521 gtk_label_clear_select_info (GtkLabel *label)
5522 {
5523   GtkLabelPrivate *priv = label->priv;
5524 
5525   if (priv->select_info == NULL)
5526     return;
5527 
5528   if (!priv->select_info->selectable && !priv->select_info->links)
5529     {
5530       gtk_label_destroy_window (label);
5531 
5532       g_object_unref (priv->select_info->drag_gesture);
5533       g_object_unref (priv->select_info->multipress_gesture);
5534 
5535       g_free (priv->select_info);
5536       priv->select_info = NULL;
5537 
5538       gtk_widget_set_can_focus (GTK_WIDGET (label), FALSE);
5539     }
5540 }
5541 
5542 /**
5543  * gtk_label_set_selectable:
5544  * @label: a #GtkLabel
5545  * @setting: %TRUE to allow selecting text in the label
5546  *
5547  * Selectable labels allow the user to select text from the label, for
5548  * copy-and-paste.
5549  **/
5550 void
gtk_label_set_selectable(GtkLabel * label,gboolean setting)5551 gtk_label_set_selectable (GtkLabel *label,
5552                           gboolean  setting)
5553 {
5554   GtkLabelPrivate *priv;
5555   gboolean old_setting;
5556 
5557   g_return_if_fail (GTK_IS_LABEL (label));
5558 
5559   priv = label->priv;
5560 
5561   setting = setting != FALSE;
5562   old_setting = priv->select_info && priv->select_info->selectable;
5563 
5564   if (setting)
5565     {
5566       gtk_label_ensure_select_info (label);
5567       priv->select_info->selectable = TRUE;
5568       gtk_label_update_cursor (label);
5569     }
5570   else
5571     {
5572       if (old_setting)
5573         {
5574           /* unselect, to give up the selection */
5575           gtk_label_select_region (label, 0, 0);
5576 
5577           priv->select_info->selectable = FALSE;
5578           gtk_label_clear_select_info (label);
5579           gtk_label_update_cursor (label);
5580         }
5581     }
5582   if (setting != old_setting)
5583     {
5584       g_object_freeze_notify (G_OBJECT (label));
5585       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_SELECTABLE]);
5586       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_CURSOR_POSITION]);
5587       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_SELECTION_BOUND]);
5588       g_object_thaw_notify (G_OBJECT (label));
5589       gtk_widget_queue_draw (GTK_WIDGET (label));
5590     }
5591 }
5592 
5593 /**
5594  * gtk_label_get_selectable:
5595  * @label: a #GtkLabel
5596  *
5597  * Gets the value set by gtk_label_set_selectable().
5598  *
5599  * Returns: %TRUE if the user can copy text from the label
5600  **/
5601 gboolean
gtk_label_get_selectable(GtkLabel * label)5602 gtk_label_get_selectable (GtkLabel *label)
5603 {
5604   GtkLabelPrivate *priv;
5605 
5606   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5607 
5608   priv = label->priv;
5609 
5610   return priv->select_info && priv->select_info->selectable;
5611 }
5612 
5613 /**
5614  * gtk_label_set_angle:
5615  * @label: a #GtkLabel
5616  * @angle: the angle that the baseline of the label makes with
5617  *   the horizontal, in degrees, measured counterclockwise
5618  *
5619  * Sets the angle of rotation for the label. An angle of 90 reads from
5620  * from bottom to top, an angle of 270, from top to bottom. The angle
5621  * setting for the label is ignored if the label is selectable,
5622  * wrapped, or ellipsized.
5623  *
5624  * Since: 2.6
5625  **/
5626 void
gtk_label_set_angle(GtkLabel * label,gdouble angle)5627 gtk_label_set_angle (GtkLabel *label,
5628 		     gdouble   angle)
5629 {
5630   GtkLabelPrivate *priv;
5631 
5632   g_return_if_fail (GTK_IS_LABEL (label));
5633 
5634   priv = label->priv;
5635 
5636   /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
5637    * double property ranges are inclusive, and changing 360 to 0 would
5638    * make a property editor behave strangely.
5639    */
5640   if (angle < 0 || angle > 360.0)
5641     angle = angle - 360. * floor (angle / 360.);
5642 
5643   if (priv->angle != angle)
5644     {
5645       priv->angle = angle;
5646 
5647       gtk_label_clear_layout (label);
5648       gtk_widget_queue_resize (GTK_WIDGET (label));
5649 
5650       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_ANGLE]);
5651     }
5652 }
5653 
5654 /**
5655  * gtk_label_get_angle:
5656  * @label: a #GtkLabel
5657  *
5658  * Gets the angle of rotation for the label. See
5659  * gtk_label_set_angle().
5660  *
5661  * Returns: the angle of rotation for the label
5662  *
5663  * Since: 2.6
5664  **/
5665 gdouble
gtk_label_get_angle(GtkLabel * label)5666 gtk_label_get_angle  (GtkLabel *label)
5667 {
5668   g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
5669 
5670   return label->priv->angle;
5671 }
5672 
5673 static void
gtk_label_set_selection_text(GtkLabel * label,GtkSelectionData * selection_data)5674 gtk_label_set_selection_text (GtkLabel         *label,
5675 			      GtkSelectionData *selection_data)
5676 {
5677   GtkLabelPrivate *priv = label->priv;
5678 
5679   if (priv->select_info &&
5680       (priv->select_info->selection_anchor !=
5681        priv->select_info->selection_end) &&
5682       priv->text)
5683     {
5684       gint start, end;
5685       gint len;
5686 
5687       start = MIN (priv->select_info->selection_anchor,
5688                    priv->select_info->selection_end);
5689       end = MAX (priv->select_info->selection_anchor,
5690                  priv->select_info->selection_end);
5691 
5692       len = strlen (priv->text);
5693 
5694       if (end > len)
5695         end = len;
5696 
5697       if (start > len)
5698         start = len;
5699 
5700       gtk_selection_data_set_text (selection_data,
5701 				   priv->text + start,
5702 				   end - start);
5703     }
5704 }
5705 
5706 static void
gtk_label_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)5707 gtk_label_drag_data_get (GtkWidget        *widget,
5708 			 GdkDragContext   *context,
5709 			 GtkSelectionData *selection_data,
5710 			 guint             info,
5711 			 guint             time)
5712 {
5713   gtk_label_set_selection_text (GTK_LABEL (widget), selection_data);
5714 }
5715 
5716 static void
get_text_callback(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer user_data_or_owner)5717 get_text_callback (GtkClipboard     *clipboard,
5718                    GtkSelectionData *selection_data,
5719                    guint             info,
5720                    gpointer          user_data_or_owner)
5721 {
5722   gtk_label_set_selection_text (GTK_LABEL (user_data_or_owner), selection_data);
5723 }
5724 
5725 static void
clear_text_callback(GtkClipboard * clipboard,gpointer user_data_or_owner)5726 clear_text_callback (GtkClipboard     *clipboard,
5727                      gpointer          user_data_or_owner)
5728 {
5729   GtkLabel *label;
5730   GtkLabelPrivate *priv;
5731 
5732   label = GTK_LABEL (user_data_or_owner);
5733   priv = label->priv;
5734 
5735   if (priv->select_info)
5736     {
5737       priv->select_info->selection_anchor = priv->select_info->selection_end;
5738 
5739       gtk_widget_queue_draw (GTK_WIDGET (label));
5740     }
5741 }
5742 
5743 static void
gtk_label_select_region_index(GtkLabel * label,gint anchor_index,gint end_index)5744 gtk_label_select_region_index (GtkLabel *label,
5745                                gint      anchor_index,
5746                                gint      end_index)
5747 {
5748   GtkLabelPrivate *priv;
5749 
5750   g_return_if_fail (GTK_IS_LABEL (label));
5751 
5752   priv = label->priv;
5753 
5754   if (priv->select_info && priv->select_info->selectable)
5755     {
5756       GtkClipboard *clipboard;
5757       gint s, e;
5758 
5759       /* Ensure that we treat an ellipsized region like a single
5760        * character with respect to selection.
5761        */
5762       if (anchor_index < end_index)
5763         {
5764           if (range_is_in_ellipsis_full (label, anchor_index, anchor_index + 1, &s, &e))
5765             {
5766               if (priv->select_info->selection_anchor == s)
5767                 anchor_index = e;
5768               else
5769                 anchor_index = s;
5770             }
5771           if (range_is_in_ellipsis_full (label, end_index - 1, end_index, &s, &e))
5772             {
5773               if (priv->select_info->selection_end == e)
5774                 end_index = s;
5775               else
5776                 end_index = e;
5777             }
5778         }
5779       else if (end_index < anchor_index)
5780         {
5781           if (range_is_in_ellipsis_full (label, end_index, end_index + 1, &s, &e))
5782             {
5783               if (priv->select_info->selection_end == s)
5784                 end_index = e;
5785               else
5786                 end_index = s;
5787             }
5788           if (range_is_in_ellipsis_full (label, anchor_index - 1, anchor_index, &s, &e))
5789             {
5790               if (priv->select_info->selection_anchor == e)
5791                 anchor_index = s;
5792               else
5793                 anchor_index = e;
5794             }
5795         }
5796       else
5797         {
5798           if (range_is_in_ellipsis_full (label, anchor_index, anchor_index, &s, &e))
5799             {
5800               if (priv->select_info->selection_anchor == s)
5801                 anchor_index = e;
5802               else if (priv->select_info->selection_anchor == e)
5803                 anchor_index = s;
5804               else if (anchor_index - s < e - anchor_index)
5805                 anchor_index = s;
5806               else
5807                 anchor_index = e;
5808               end_index = anchor_index;
5809             }
5810         }
5811 
5812       if (priv->select_info->selection_anchor == anchor_index &&
5813           priv->select_info->selection_end == end_index)
5814         return;
5815 
5816       g_object_freeze_notify (G_OBJECT (label));
5817 
5818       if (priv->select_info->selection_anchor != anchor_index)
5819         g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_SELECTION_BOUND]);
5820       if (priv->select_info->selection_end != end_index)
5821         g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_CURSOR_POSITION]);
5822 
5823       priv->select_info->selection_anchor = anchor_index;
5824       priv->select_info->selection_end = end_index;
5825 
5826       if (gtk_widget_has_screen (GTK_WIDGET (label)))
5827         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
5828                                               GDK_SELECTION_PRIMARY);
5829       else
5830         clipboard = NULL;
5831 
5832       if (anchor_index != end_index)
5833         {
5834           GtkTargetList *list;
5835           GtkTargetEntry *targets;
5836           gint n_targets;
5837 
5838           list = gtk_target_list_new (NULL, 0);
5839           gtk_target_list_add_text_targets (list, 0);
5840           targets = gtk_target_table_new_from_list (list, &n_targets);
5841 
5842           if (clipboard)
5843             gtk_clipboard_set_with_owner (clipboard,
5844                                           targets, n_targets,
5845                                           get_text_callback,
5846                                           clear_text_callback,
5847                                           G_OBJECT (label));
5848 
5849           gtk_target_table_free (targets, n_targets);
5850           gtk_target_list_unref (list);
5851 
5852           if (!priv->select_info->selection_node)
5853             {
5854               GtkCssNode *widget_node;
5855 
5856               widget_node = gtk_widget_get_css_node (GTK_WIDGET (label));
5857               priv->select_info->selection_node = gtk_css_node_new ();
5858               gtk_css_node_set_name (priv->select_info->selection_node, I_("selection"));
5859               gtk_css_node_set_parent (priv->select_info->selection_node, widget_node);
5860               gtk_css_node_set_state (priv->select_info->selection_node, gtk_css_node_get_state (widget_node));
5861               g_object_unref (priv->select_info->selection_node);
5862             }
5863         }
5864       else
5865         {
5866           if (clipboard &&
5867               gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
5868             gtk_clipboard_clear (clipboard);
5869 
5870           if (priv->select_info->selection_node)
5871             {
5872               gtk_css_node_set_parent (priv->select_info->selection_node, NULL);
5873               priv->select_info->selection_node = NULL;
5874             }
5875         }
5876 
5877       gtk_widget_queue_draw (GTK_WIDGET (label));
5878 
5879       g_object_thaw_notify (G_OBJECT (label));
5880     }
5881 }
5882 
5883 /**
5884  * gtk_label_select_region:
5885  * @label: a #GtkLabel
5886  * @start_offset: start offset (in characters not bytes)
5887  * @end_offset: end offset (in characters not bytes)
5888  *
5889  * Selects a range of characters in the label, if the label is selectable.
5890  * See gtk_label_set_selectable(). If the label is not selectable,
5891  * this function has no effect. If @start_offset or
5892  * @end_offset are -1, then the end of the label will be substituted.
5893  **/
5894 void
gtk_label_select_region(GtkLabel * label,gint start_offset,gint end_offset)5895 gtk_label_select_region  (GtkLabel *label,
5896                           gint      start_offset,
5897                           gint      end_offset)
5898 {
5899   GtkLabelPrivate *priv;
5900 
5901   g_return_if_fail (GTK_IS_LABEL (label));
5902 
5903   priv = label->priv;
5904 
5905   if (priv->text && priv->select_info)
5906     {
5907       if (start_offset < 0)
5908         start_offset = g_utf8_strlen (priv->text, -1);
5909 
5910       if (end_offset < 0)
5911         end_offset = g_utf8_strlen (priv->text, -1);
5912 
5913       gtk_label_select_region_index (label,
5914                                      g_utf8_offset_to_pointer (priv->text, start_offset) - priv->text,
5915                                      g_utf8_offset_to_pointer (priv->text, end_offset) - priv->text);
5916     }
5917 }
5918 
5919 /**
5920  * gtk_label_get_selection_bounds:
5921  * @label: a #GtkLabel
5922  * @start: (out): return location for start of selection, as a character offset
5923  * @end: (out): return location for end of selection, as a character offset
5924  *
5925  * Gets the selected range of characters in the label, returning %TRUE
5926  * if there’s a selection.
5927  *
5928  * Returns: %TRUE if selection is non-empty
5929  **/
5930 gboolean
gtk_label_get_selection_bounds(GtkLabel * label,gint * start,gint * end)5931 gtk_label_get_selection_bounds (GtkLabel  *label,
5932                                 gint      *start,
5933                                 gint      *end)
5934 {
5935   GtkLabelPrivate *priv;
5936 
5937   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
5938 
5939   priv = label->priv;
5940 
5941   if (priv->select_info == NULL)
5942     {
5943       /* not a selectable label */
5944       if (start)
5945         *start = 0;
5946       if (end)
5947         *end = 0;
5948 
5949       return FALSE;
5950     }
5951   else
5952     {
5953       gint start_index, end_index;
5954       gint start_offset, end_offset;
5955       gint len;
5956 
5957       start_index = MIN (priv->select_info->selection_anchor,
5958                    priv->select_info->selection_end);
5959       end_index = MAX (priv->select_info->selection_anchor,
5960                  priv->select_info->selection_end);
5961 
5962       len = strlen (priv->text);
5963 
5964       if (end_index > len)
5965         end_index = len;
5966 
5967       if (start_index > len)
5968         start_index = len;
5969 
5970       start_offset = g_utf8_strlen (priv->text, start_index);
5971       end_offset = g_utf8_strlen (priv->text, end_index);
5972 
5973       if (start_offset > end_offset)
5974         {
5975           gint tmp = start_offset;
5976           start_offset = end_offset;
5977           end_offset = tmp;
5978         }
5979 
5980       if (start)
5981         *start = start_offset;
5982 
5983       if (end)
5984         *end = end_offset;
5985 
5986       return start_offset != end_offset;
5987     }
5988 }
5989 
5990 
5991 /**
5992  * gtk_label_get_layout:
5993  * @label: a #GtkLabel
5994  *
5995  * Gets the #PangoLayout used to display the label.
5996  * The layout is useful to e.g. convert text positions to
5997  * pixel positions, in combination with gtk_label_get_layout_offsets().
5998  * The returned layout is owned by the @label so need not be
5999  * freed by the caller. The @label is free to recreate its layout at
6000  * any time, so it should be considered read-only.
6001  *
6002  * Returns: (transfer none): the #PangoLayout for this label
6003  **/
6004 PangoLayout*
gtk_label_get_layout(GtkLabel * label)6005 gtk_label_get_layout (GtkLabel *label)
6006 {
6007   GtkLabelPrivate *priv;
6008 
6009   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
6010 
6011   priv = label->priv;
6012 
6013   gtk_label_ensure_layout (label);
6014 
6015   return priv->layout;
6016 }
6017 
6018 /**
6019  * gtk_label_get_layout_offsets:
6020  * @label: a #GtkLabel
6021  * @x: (out) (optional): location to store X offset of layout, or %NULL
6022  * @y: (out) (optional): location to store Y offset of layout, or %NULL
6023  *
6024  * Obtains the coordinates where the label will draw the #PangoLayout
6025  * representing the text in the label; useful to convert mouse events
6026  * into coordinates inside the #PangoLayout, e.g. to take some action
6027  * if some part of the label is clicked. Of course you will need to
6028  * create a #GtkEventBox to receive the events, and pack the label
6029  * inside it, since labels are windowless (they return %FALSE from
6030  * gtk_widget_get_has_window()). Remember
6031  * when using the #PangoLayout functions you need to convert to
6032  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
6033  **/
6034 void
gtk_label_get_layout_offsets(GtkLabel * label,gint * x,gint * y)6035 gtk_label_get_layout_offsets (GtkLabel *label,
6036                               gint     *x,
6037                               gint     *y)
6038 {
6039   g_return_if_fail (GTK_IS_LABEL (label));
6040 
6041   gtk_label_ensure_layout (label);
6042 
6043   get_layout_location (label, x, y);
6044 }
6045 
6046 /**
6047  * gtk_label_set_use_markup:
6048  * @label: a #GtkLabel
6049  * @setting: %TRUE if the label’s text should be parsed for markup.
6050  *
6051  * Sets whether the text of the label contains markup in
6052  * [Pango’s text markup language][PangoMarkupFormat].
6053  * See gtk_label_set_markup().
6054  **/
6055 void
gtk_label_set_use_markup(GtkLabel * label,gboolean setting)6056 gtk_label_set_use_markup (GtkLabel *label,
6057 			  gboolean  setting)
6058 {
6059   g_return_if_fail (GTK_IS_LABEL (label));
6060 
6061   g_object_freeze_notify (G_OBJECT (label));
6062 
6063   if (gtk_label_set_use_markup_internal (label, setting))
6064     gtk_label_recalculate (label);
6065 
6066   g_object_thaw_notify (G_OBJECT (label));
6067 }
6068 
6069 /**
6070  * gtk_label_get_use_markup:
6071  * @label: a #GtkLabel
6072  *
6073  * Returns whether the label’s text is interpreted as marked up with
6074  * the [Pango text markup language][PangoMarkupFormat].
6075  * See gtk_label_set_use_markup ().
6076  *
6077  * Returns: %TRUE if the label’s text will be parsed for markup.
6078  **/
6079 gboolean
gtk_label_get_use_markup(GtkLabel * label)6080 gtk_label_get_use_markup (GtkLabel *label)
6081 {
6082   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
6083 
6084   return label->priv->use_markup;
6085 }
6086 
6087 /**
6088  * gtk_label_set_use_underline:
6089  * @label: a #GtkLabel
6090  * @setting: %TRUE if underlines in the text indicate mnemonics
6091  *
6092  * If true, an underline in the text indicates the next character should be
6093  * used for the mnemonic accelerator key.
6094  */
6095 void
gtk_label_set_use_underline(GtkLabel * label,gboolean setting)6096 gtk_label_set_use_underline (GtkLabel *label,
6097 			     gboolean  setting)
6098 {
6099   g_return_if_fail (GTK_IS_LABEL (label));
6100 
6101   g_object_freeze_notify (G_OBJECT (label));
6102 
6103   if (gtk_label_set_use_underline_internal (label, setting))
6104     gtk_label_recalculate (label);
6105 
6106   g_object_thaw_notify (G_OBJECT (label));
6107 }
6108 
6109 /**
6110  * gtk_label_get_use_underline:
6111  * @label: a #GtkLabel
6112  *
6113  * Returns whether an embedded underline in the label indicates a
6114  * mnemonic. See gtk_label_set_use_underline().
6115  *
6116  * Returns: %TRUE whether an embedded underline in the label indicates
6117  *               the mnemonic accelerator keys.
6118  **/
6119 gboolean
gtk_label_get_use_underline(GtkLabel * label)6120 gtk_label_get_use_underline (GtkLabel *label)
6121 {
6122   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
6123 
6124   return label->priv->use_underline;
6125 }
6126 
6127 /**
6128  * gtk_label_set_single_line_mode:
6129  * @label: a #GtkLabel
6130  * @single_line_mode: %TRUE if the label should be in single line mode
6131  *
6132  * Sets whether the label is in single line mode.
6133  *
6134  * Since: 2.6
6135  */
6136 void
gtk_label_set_single_line_mode(GtkLabel * label,gboolean single_line_mode)6137 gtk_label_set_single_line_mode (GtkLabel *label,
6138                                 gboolean single_line_mode)
6139 {
6140   GtkLabelPrivate *priv;
6141 
6142   g_return_if_fail (GTK_IS_LABEL (label));
6143 
6144   priv = label->priv;
6145 
6146   single_line_mode = single_line_mode != FALSE;
6147 
6148   if (priv->single_line_mode != single_line_mode)
6149     {
6150       priv->single_line_mode = single_line_mode;
6151 
6152       gtk_label_clear_layout (label);
6153       gtk_widget_queue_resize (GTK_WIDGET (label));
6154 
6155       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_SINGLE_LINE_MODE]);
6156     }
6157 }
6158 
6159 /**
6160  * gtk_label_get_single_line_mode:
6161  * @label: a #GtkLabel
6162  *
6163  * Returns whether the label is in single line mode.
6164  *
6165  * Returns: %TRUE when the label is in single line mode.
6166  *
6167  * Since: 2.6
6168  **/
6169 gboolean
gtk_label_get_single_line_mode(GtkLabel * label)6170 gtk_label_get_single_line_mode  (GtkLabel *label)
6171 {
6172   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
6173 
6174   return label->priv->single_line_mode;
6175 }
6176 
6177 /* Compute the X position for an offset that corresponds to the "more important
6178  * cursor position for that offset. We use this when trying to guess to which
6179  * end of the selection we should go to when the user hits the left or
6180  * right arrow key.
6181  */
6182 static void
get_better_cursor(GtkLabel * label,gint index,gint * x,gint * y)6183 get_better_cursor (GtkLabel *label,
6184 		   gint      index,
6185 		   gint      *x,
6186 		   gint      *y)
6187 {
6188   GtkLabelPrivate *priv = label->priv;
6189   GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
6190   PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
6191   PangoDirection cursor_direction = get_cursor_direction (label);
6192   gboolean split_cursor;
6193   PangoRectangle strong_pos, weak_pos;
6194 
6195   g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
6196 		"gtk-split-cursor", &split_cursor,
6197 		NULL);
6198 
6199   gtk_label_ensure_layout (label);
6200 
6201   pango_layout_get_cursor_pos (priv->layout, index,
6202 			       &strong_pos, &weak_pos);
6203 
6204   if (split_cursor)
6205     {
6206       *x = strong_pos.x / PANGO_SCALE;
6207       *y = strong_pos.y / PANGO_SCALE;
6208     }
6209   else
6210     {
6211       if (keymap_direction == cursor_direction)
6212 	{
6213 	  *x = strong_pos.x / PANGO_SCALE;
6214 	  *y = strong_pos.y / PANGO_SCALE;
6215 	}
6216       else
6217 	{
6218 	  *x = weak_pos.x / PANGO_SCALE;
6219 	  *y = weak_pos.y / PANGO_SCALE;
6220 	}
6221     }
6222 }
6223 
6224 
6225 static gint
gtk_label_move_logically(GtkLabel * label,gint start,gint count)6226 gtk_label_move_logically (GtkLabel *label,
6227 			  gint      start,
6228 			  gint      count)
6229 {
6230   GtkLabelPrivate *priv = label->priv;
6231   gint offset = g_utf8_pointer_to_offset (priv->text,
6232 					  priv->text + start);
6233 
6234   if (priv->text)
6235     {
6236       PangoLogAttr *log_attrs;
6237       gint n_attrs;
6238       gint length;
6239 
6240       gtk_label_ensure_layout (label);
6241 
6242       length = g_utf8_strlen (priv->text, -1);
6243 
6244       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
6245 
6246       while (count > 0 && offset < length)
6247 	{
6248 	  do
6249 	    offset++;
6250 	  while (offset < length && !log_attrs[offset].is_cursor_position);
6251 
6252 	  count--;
6253 	}
6254       while (count < 0 && offset > 0)
6255 	{
6256 	  do
6257 	    offset--;
6258 	  while (offset > 0 && !log_attrs[offset].is_cursor_position);
6259 
6260 	  count++;
6261 	}
6262 
6263       g_free (log_attrs);
6264     }
6265 
6266   return g_utf8_offset_to_pointer (priv->text, offset) - priv->text;
6267 }
6268 
6269 static gint
gtk_label_move_visually(GtkLabel * label,gint start,gint count)6270 gtk_label_move_visually (GtkLabel *label,
6271 			 gint      start,
6272 			 gint      count)
6273 {
6274   GtkLabelPrivate *priv = label->priv;
6275   gint index;
6276 
6277   index = start;
6278 
6279   while (count != 0)
6280     {
6281       int new_index, new_trailing;
6282       gboolean split_cursor;
6283       gboolean strong;
6284 
6285       gtk_label_ensure_layout (label);
6286 
6287       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
6288 		    "gtk-split-cursor", &split_cursor,
6289 		    NULL);
6290 
6291       if (split_cursor)
6292 	strong = TRUE;
6293       else
6294 	{
6295 	  GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
6296 	  PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
6297 
6298 	  strong = keymap_direction == get_cursor_direction (label);
6299 	}
6300 
6301       if (count > 0)
6302 	{
6303 	  pango_layout_move_cursor_visually (priv->layout, strong, index, 0, 1, &new_index, &new_trailing);
6304 	  count--;
6305 	}
6306       else
6307 	{
6308 	  pango_layout_move_cursor_visually (priv->layout, strong, index, 0, -1, &new_index, &new_trailing);
6309 	  count++;
6310 	}
6311 
6312       if (new_index < 0 || new_index == G_MAXINT)
6313 	break;
6314 
6315       index = new_index;
6316 
6317       while (new_trailing--)
6318 	index = g_utf8_next_char (priv->text + new_index) - priv->text;
6319     }
6320 
6321   return index;
6322 }
6323 
6324 static gint
gtk_label_move_forward_word(GtkLabel * label,gint start)6325 gtk_label_move_forward_word (GtkLabel *label,
6326 			     gint      start)
6327 {
6328   GtkLabelPrivate *priv = label->priv;
6329   gint new_pos = g_utf8_pointer_to_offset (priv->text,
6330 					   priv->text + start);
6331   gint length;
6332 
6333   length = g_utf8_strlen (priv->text, -1);
6334   if (new_pos < length)
6335     {
6336       PangoLogAttr *log_attrs;
6337       gint n_attrs;
6338 
6339       gtk_label_ensure_layout (label);
6340 
6341       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
6342 
6343       /* Find the next word end */
6344       new_pos++;
6345       while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
6346 	new_pos++;
6347 
6348       g_free (log_attrs);
6349     }
6350 
6351   return g_utf8_offset_to_pointer (priv->text, new_pos) - priv->text;
6352 }
6353 
6354 
6355 static gint
gtk_label_move_backward_word(GtkLabel * label,gint start)6356 gtk_label_move_backward_word (GtkLabel *label,
6357 			      gint      start)
6358 {
6359   GtkLabelPrivate *priv = label->priv;
6360   gint new_pos = g_utf8_pointer_to_offset (priv->text,
6361 					   priv->text + start);
6362 
6363   if (new_pos > 0)
6364     {
6365       PangoLogAttr *log_attrs;
6366       gint n_attrs;
6367 
6368       gtk_label_ensure_layout (label);
6369 
6370       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
6371 
6372       new_pos -= 1;
6373 
6374       /* Find the previous word beginning */
6375       while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
6376 	new_pos--;
6377 
6378       g_free (log_attrs);
6379     }
6380 
6381   return g_utf8_offset_to_pointer (priv->text, new_pos) - priv->text;
6382 }
6383 
6384 static void
gtk_label_move_cursor(GtkLabel * label,GtkMovementStep step,gint count,gboolean extend_selection)6385 gtk_label_move_cursor (GtkLabel       *label,
6386                        GtkMovementStep step,
6387                        gint            count,
6388                        gboolean        extend_selection)
6389 {
6390   GtkLabelPrivate *priv = label->priv;
6391   gint old_pos;
6392   gint new_pos;
6393 
6394   if (priv->select_info == NULL)
6395     return;
6396 
6397   old_pos = new_pos = priv->select_info->selection_end;
6398 
6399   if (priv->select_info->selection_end != priv->select_info->selection_anchor &&
6400       !extend_selection)
6401     {
6402       /* If we have a current selection and aren't extending it, move to the
6403        * start/or end of the selection as appropriate
6404        */
6405       switch (step)
6406         {
6407         case GTK_MOVEMENT_VISUAL_POSITIONS:
6408           {
6409             gint end_x, end_y;
6410             gint anchor_x, anchor_y;
6411             gboolean end_is_left;
6412 
6413             get_better_cursor (label, priv->select_info->selection_end, &end_x, &end_y);
6414             get_better_cursor (label, priv->select_info->selection_anchor, &anchor_x, &anchor_y);
6415 
6416             end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
6417 
6418             if (count < 0)
6419               new_pos = end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
6420             else
6421               new_pos = !end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
6422             break;
6423           }
6424         case GTK_MOVEMENT_LOGICAL_POSITIONS:
6425         case GTK_MOVEMENT_WORDS:
6426           if (count < 0)
6427             new_pos = MIN (priv->select_info->selection_end, priv->select_info->selection_anchor);
6428           else
6429             new_pos = MAX (priv->select_info->selection_end, priv->select_info->selection_anchor);
6430           break;
6431         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
6432         case GTK_MOVEMENT_PARAGRAPH_ENDS:
6433         case GTK_MOVEMENT_BUFFER_ENDS:
6434           /* FIXME: Can do better here */
6435           new_pos = count < 0 ? 0 : strlen (priv->text);
6436           break;
6437         case GTK_MOVEMENT_DISPLAY_LINES:
6438         case GTK_MOVEMENT_PARAGRAPHS:
6439         case GTK_MOVEMENT_PAGES:
6440         case GTK_MOVEMENT_HORIZONTAL_PAGES:
6441           break;
6442         }
6443     }
6444   else
6445     {
6446       switch (step)
6447         {
6448         case GTK_MOVEMENT_LOGICAL_POSITIONS:
6449           new_pos = gtk_label_move_logically (label, new_pos, count);
6450           break;
6451         case GTK_MOVEMENT_VISUAL_POSITIONS:
6452           new_pos = gtk_label_move_visually (label, new_pos, count);
6453           if (new_pos == old_pos)
6454             {
6455               if (!extend_selection)
6456                 {
6457                   if (!gtk_widget_keynav_failed (GTK_WIDGET (label),
6458                                                  count > 0 ?
6459                                                  GTK_DIR_RIGHT : GTK_DIR_LEFT))
6460                     {
6461                       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
6462 
6463                       if (toplevel)
6464                         gtk_widget_child_focus (toplevel,
6465                                                 count > 0 ?
6466                                                 GTK_DIR_RIGHT : GTK_DIR_LEFT);
6467                     }
6468                 }
6469               else
6470                 {
6471                   gtk_widget_error_bell (GTK_WIDGET (label));
6472                 }
6473             }
6474           break;
6475         case GTK_MOVEMENT_WORDS:
6476           while (count > 0)
6477             {
6478               new_pos = gtk_label_move_forward_word (label, new_pos);
6479               count--;
6480             }
6481           while (count < 0)
6482             {
6483               new_pos = gtk_label_move_backward_word (label, new_pos);
6484               count++;
6485             }
6486           if (new_pos == old_pos)
6487             gtk_widget_error_bell (GTK_WIDGET (label));
6488           break;
6489         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
6490         case GTK_MOVEMENT_PARAGRAPH_ENDS:
6491         case GTK_MOVEMENT_BUFFER_ENDS:
6492           /* FIXME: Can do better here */
6493           new_pos = count < 0 ? 0 : strlen (priv->text);
6494           if (new_pos == old_pos)
6495             gtk_widget_error_bell (GTK_WIDGET (label));
6496           break;
6497         case GTK_MOVEMENT_DISPLAY_LINES:
6498         case GTK_MOVEMENT_PARAGRAPHS:
6499         case GTK_MOVEMENT_PAGES:
6500         case GTK_MOVEMENT_HORIZONTAL_PAGES:
6501           break;
6502         }
6503     }
6504 
6505   if (extend_selection)
6506     gtk_label_select_region_index (label,
6507                                    priv->select_info->selection_anchor,
6508                                    new_pos);
6509   else
6510     gtk_label_select_region_index (label, new_pos, new_pos);
6511 }
6512 
6513 static void
gtk_label_copy_clipboard(GtkLabel * label)6514 gtk_label_copy_clipboard (GtkLabel *label)
6515 {
6516   GtkLabelPrivate *priv = label->priv;
6517 
6518   if (priv->text && priv->select_info)
6519     {
6520       gint start, end;
6521       gint len;
6522       GtkClipboard *clipboard;
6523 
6524       start = MIN (priv->select_info->selection_anchor,
6525                    priv->select_info->selection_end);
6526       end = MAX (priv->select_info->selection_anchor,
6527                  priv->select_info->selection_end);
6528 
6529       len = strlen (priv->text);
6530 
6531       if (end > len)
6532         end = len;
6533 
6534       if (start > len)
6535         start = len;
6536 
6537       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
6538 
6539       if (start != end)
6540 	gtk_clipboard_set_text (clipboard, priv->text + start, end - start);
6541       else
6542         {
6543           GtkLabelLink *link;
6544 
6545           link = gtk_label_get_focus_link (label);
6546           if (link)
6547             gtk_clipboard_set_text (clipboard, link->uri, -1);
6548         }
6549     }
6550 }
6551 
6552 static void
gtk_label_select_all(GtkLabel * label)6553 gtk_label_select_all (GtkLabel *label)
6554 {
6555   GtkLabelPrivate *priv = label->priv;
6556 
6557   gtk_label_select_region_index (label, 0, strlen (priv->text));
6558 }
6559 
6560 /* Quick hack of a popup menu
6561  */
6562 static void
activate_cb(GtkWidget * menuitem,GtkLabel * label)6563 activate_cb (GtkWidget *menuitem,
6564 	     GtkLabel  *label)
6565 {
6566   const gchar *signal = g_object_get_qdata (G_OBJECT (menuitem), quark_gtk_signal);
6567   g_signal_emit_by_name (label, signal);
6568 }
6569 
6570 static void
append_action_signal(GtkLabel * label,GtkWidget * menu,const gchar * text,const gchar * signal,gboolean sensitive)6571 append_action_signal (GtkLabel     *label,
6572 		      GtkWidget    *menu,
6573 		      const gchar  *text,
6574 		      const gchar  *signal,
6575                       gboolean      sensitive)
6576 {
6577   GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (text);
6578 
6579   g_object_set_qdata (G_OBJECT (menuitem), quark_gtk_signal, (char *)signal);
6580   g_signal_connect (menuitem, "activate",
6581 		    G_CALLBACK (activate_cb), label);
6582 
6583   gtk_widget_set_sensitive (menuitem, sensitive);
6584 
6585   gtk_widget_show (menuitem);
6586   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6587 }
6588 
6589 static void
popup_menu_detach(GtkWidget * attach_widget,GtkMenu * menu)6590 popup_menu_detach (GtkWidget *attach_widget,
6591 		   GtkMenu   *menu)
6592 {
6593   GtkLabel *label = GTK_LABEL (attach_widget);
6594   GtkLabelPrivate *priv = label->priv;
6595 
6596   if (priv->select_info)
6597     priv->select_info->popup_menu = NULL;
6598 }
6599 
6600 static void
open_link_activate_cb(GtkMenuItem * menuitem,GtkLabel * label)6601 open_link_activate_cb (GtkMenuItem *menuitem,
6602                        GtkLabel    *label)
6603 {
6604   GtkLabelLink *link;
6605 
6606   link = g_object_get_qdata (G_OBJECT (menuitem), quark_link);
6607   emit_activate_link (label, link);
6608 }
6609 
6610 static void
copy_link_activate_cb(GtkMenuItem * menuitem,GtkLabel * label)6611 copy_link_activate_cb (GtkMenuItem *menuitem,
6612                        GtkLabel    *label)
6613 {
6614   GtkLabelLink *link;
6615   GtkClipboard *clipboard;
6616 
6617   link = g_object_get_qdata (G_OBJECT (menuitem), quark_link);
6618   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
6619   gtk_clipboard_set_text (clipboard, link->uri, -1);
6620 }
6621 
6622 static gboolean
gtk_label_popup_menu(GtkWidget * widget)6623 gtk_label_popup_menu (GtkWidget *widget)
6624 {
6625   gtk_label_do_popup (GTK_LABEL (widget), NULL);
6626 
6627   return TRUE;
6628 }
6629 
6630 static void
gtk_label_do_popup(GtkLabel * label,const GdkEvent * event)6631 gtk_label_do_popup (GtkLabel       *label,
6632                     const GdkEvent *event)
6633 {
6634   GtkLabelPrivate *priv = label->priv;
6635   GtkWidget *menuitem;
6636   GtkWidget *menu;
6637   gboolean have_selection;
6638   GtkLabelLink *link;
6639 
6640   if (!priv->select_info)
6641     return;
6642 
6643   if (priv->select_info->popup_menu)
6644     gtk_widget_destroy (priv->select_info->popup_menu);
6645 
6646   priv->select_info->popup_menu = menu = gtk_menu_new ();
6647   gtk_style_context_add_class (gtk_widget_get_style_context (menu),
6648                                GTK_STYLE_CLASS_CONTEXT_MENU);
6649 
6650   gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (label), popup_menu_detach);
6651 
6652   have_selection =
6653     priv->select_info->selection_anchor != priv->select_info->selection_end;
6654 
6655   if (event)
6656     {
6657       if (priv->select_info->link_clicked)
6658         link = priv->select_info->active_link;
6659       else
6660         link = NULL;
6661     }
6662   else
6663     link = gtk_label_get_focus_link (label);
6664 
6665   if (!have_selection && link)
6666     {
6667       /* Open Link */
6668       menuitem = gtk_menu_item_new_with_mnemonic (_("_Open Link"));
6669       g_object_set_qdata (G_OBJECT (menuitem), quark_link, link);
6670       gtk_widget_show (menuitem);
6671       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6672 
6673       g_signal_connect (G_OBJECT (menuitem), "activate",
6674                         G_CALLBACK (open_link_activate_cb), label);
6675 
6676       /* Copy Link Address */
6677       menuitem = gtk_menu_item_new_with_mnemonic (_("Copy _Link Address"));
6678       g_object_set_qdata (G_OBJECT (menuitem), quark_link, link);
6679       gtk_widget_show (menuitem);
6680       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6681 
6682       g_signal_connect (G_OBJECT (menuitem), "activate",
6683                         G_CALLBACK (copy_link_activate_cb), label);
6684     }
6685   else
6686     {
6687       append_action_signal (label, menu, _("Cu_t"), "cut-clipboard", FALSE);
6688       append_action_signal (label, menu, _("_Copy"), "copy-clipboard", have_selection);
6689       append_action_signal (label, menu, _("_Paste"), "paste-clipboard", FALSE);
6690 
6691       menuitem = gtk_menu_item_new_with_mnemonic (_("_Delete"));
6692       gtk_widget_set_sensitive (menuitem, FALSE);
6693       gtk_widget_show (menuitem);
6694       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6695 
6696       menuitem = gtk_separator_menu_item_new ();
6697       gtk_widget_show (menuitem);
6698       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6699 
6700       menuitem = gtk_menu_item_new_with_mnemonic (_("Select _All"));
6701       g_signal_connect_swapped (menuitem, "activate",
6702 			        G_CALLBACK (gtk_label_select_all), label);
6703       gtk_widget_show (menuitem);
6704       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6705     }
6706 
6707   g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
6708 
6709   if (event && gdk_event_triggers_context_menu (event))
6710     gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
6711   else
6712     {
6713       gtk_menu_popup_at_widget (GTK_MENU (menu),
6714                                 GTK_WIDGET (label),
6715                                 GDK_GRAVITY_SOUTH,
6716                                 GDK_GRAVITY_NORTH_WEST,
6717                                 event);
6718 
6719       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
6720     }
6721 }
6722 
6723 static void
gtk_label_clear_links(GtkLabel * label)6724 gtk_label_clear_links (GtkLabel *label)
6725 {
6726   GtkLabelPrivate *priv = label->priv;
6727 
6728   if (!priv->select_info)
6729     return;
6730 
6731   g_list_free_full (priv->select_info->links, (GDestroyNotify) link_free);
6732   priv->select_info->links = NULL;
6733   priv->select_info->active_link = NULL;
6734 
6735   _gtk_label_accessible_update_links (label);
6736 }
6737 
6738 static gboolean
gtk_label_activate_link(GtkLabel * label,const gchar * uri)6739 gtk_label_activate_link (GtkLabel    *label,
6740                          const gchar *uri)
6741 {
6742   GtkWidget *widget = GTK_WIDGET (label);
6743   GtkWidget *top_level = gtk_widget_get_toplevel (widget);
6744   guint32 timestamp = gtk_get_current_event_time ();
6745   GError *error = NULL;
6746 
6747   if (!gtk_show_uri_on_window (GTK_WINDOW (top_level), uri, timestamp, &error))
6748     {
6749       g_warning ("Unable to show '%s': %s", uri, error->message);
6750       g_error_free (error);
6751     }
6752 
6753   return TRUE;
6754 }
6755 
6756 static void
emit_activate_link(GtkLabel * label,GtkLabelLink * link)6757 emit_activate_link (GtkLabel     *label,
6758                     GtkLabelLink *link)
6759 {
6760   GtkLabelPrivate *priv = label->priv;
6761   gboolean handled;
6762   GtkStateFlags state;
6763 
6764   g_signal_emit (label, signals[ACTIVATE_LINK], 0, link->uri, &handled);
6765 
6766   /* signal handler might have invalidated the layout */
6767   if (!priv->layout)
6768     return;
6769 
6770   if (handled && priv->track_links && !link->visited &&
6771       priv->select_info && priv->select_info->links)
6772     {
6773       link->visited = TRUE;
6774       state = gtk_css_node_get_state (link->cssnode);
6775       gtk_css_node_set_state (link->cssnode, (state & ~GTK_STATE_FLAG_LINK) | GTK_STATE_FLAG_VISITED);
6776       /* FIXME: shouldn't have to redo everything here */
6777       gtk_label_clear_layout (label);
6778     }
6779 }
6780 
6781 static void
gtk_label_activate_current_link(GtkLabel * label)6782 gtk_label_activate_current_link (GtkLabel *label)
6783 {
6784   GtkLabelLink *link;
6785   GtkWidget *widget = GTK_WIDGET (label);
6786 
6787   link = gtk_label_get_focus_link (label);
6788 
6789   if (link)
6790     {
6791       emit_activate_link (label, link);
6792     }
6793   else
6794     {
6795       GtkWidget *toplevel;
6796       GtkWindow *window;
6797       GtkWidget *default_widget, *focus_widget;
6798 
6799       toplevel = gtk_widget_get_toplevel (widget);
6800       if (GTK_IS_WINDOW (toplevel))
6801         {
6802           window = GTK_WINDOW (toplevel);
6803 
6804           if (window)
6805             {
6806               default_widget = gtk_window_get_default_widget (window);
6807               focus_widget = gtk_window_get_focus (window);
6808 
6809               if (default_widget != widget &&
6810                   !(widget == focus_widget && (!default_widget || !gtk_widget_is_sensitive (default_widget))))
6811                 gtk_window_activate_default (window);
6812             }
6813         }
6814     }
6815 }
6816 
6817 static GtkLabelLink *
gtk_label_get_current_link(GtkLabel * label)6818 gtk_label_get_current_link (GtkLabel *label)
6819 {
6820   GtkLabelPrivate *priv = label->priv;
6821   GtkLabelLink *link;
6822 
6823   if (!priv->select_info)
6824     return NULL;
6825 
6826   if (priv->select_info->link_clicked)
6827     link = priv->select_info->active_link;
6828   else
6829     link = gtk_label_get_focus_link (label);
6830 
6831   return link;
6832 }
6833 
6834 /**
6835  * gtk_label_get_current_uri:
6836  * @label: a #GtkLabel
6837  *
6838  * Returns the URI for the currently active link in the label.
6839  * The active link is the one under the mouse pointer or, in a
6840  * selectable label, the link in which the text cursor is currently
6841  * positioned.
6842  *
6843  * This function is intended for use in a #GtkLabel::activate-link handler
6844  * or for use in a #GtkWidget::query-tooltip handler.
6845  *
6846  * Returns: the currently active URI. The string is owned by GTK+ and must
6847  *   not be freed or modified.
6848  *
6849  * Since: 2.18
6850  */
6851 const gchar *
gtk_label_get_current_uri(GtkLabel * label)6852 gtk_label_get_current_uri (GtkLabel *label)
6853 {
6854   GtkLabelLink *link;
6855 
6856   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
6857 
6858   link = gtk_label_get_current_link (label);
6859 
6860   if (link)
6861     return link->uri;
6862 
6863   return NULL;
6864 }
6865 
6866 /**
6867  * gtk_label_set_track_visited_links:
6868  * @label: a #GtkLabel
6869  * @track_links: %TRUE to track visited links
6870  *
6871  * Sets whether the label should keep track of clicked
6872  * links (and use a different color for them).
6873  *
6874  * Since: 2.18
6875  */
6876 void
gtk_label_set_track_visited_links(GtkLabel * label,gboolean track_links)6877 gtk_label_set_track_visited_links (GtkLabel *label,
6878                                    gboolean  track_links)
6879 {
6880   GtkLabelPrivate *priv;
6881 
6882   g_return_if_fail (GTK_IS_LABEL (label));
6883 
6884   priv = label->priv;
6885 
6886   track_links = track_links != FALSE;
6887 
6888   if (priv->track_links != track_links)
6889     {
6890       priv->track_links = track_links;
6891 
6892       /* FIXME: shouldn't have to redo everything here */
6893       gtk_label_recalculate (label);
6894 
6895       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_TRACK_VISITED_LINKS]);
6896     }
6897 }
6898 
6899 /**
6900  * gtk_label_get_track_visited_links:
6901  * @label: a #GtkLabel
6902  *
6903  * Returns whether the label is currently keeping track
6904  * of clicked links.
6905  *
6906  * Returns: %TRUE if clicked links are remembered
6907  *
6908  * Since: 2.18
6909  */
6910 gboolean
gtk_label_get_track_visited_links(GtkLabel * label)6911 gtk_label_get_track_visited_links (GtkLabel *label)
6912 {
6913   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
6914 
6915   return label->priv->track_links;
6916 }
6917 
6918 static gboolean
gtk_label_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip)6919 gtk_label_query_tooltip (GtkWidget  *widget,
6920                          gint        x,
6921                          gint        y,
6922                          gboolean    keyboard_tip,
6923                          GtkTooltip *tooltip)
6924 {
6925   GtkLabel *label = GTK_LABEL (widget);
6926   GtkLabelPrivate *priv = label->priv;
6927   GtkLabelSelectionInfo *info = priv->select_info;
6928   gint index = -1;
6929   GList *l;
6930 
6931   if (info && info->links)
6932     {
6933       if (keyboard_tip)
6934         {
6935           if (info->selection_anchor == info->selection_end)
6936             index = info->selection_anchor;
6937         }
6938       else
6939         {
6940           if (!get_layout_index (label, x, y, &index))
6941             index = -1;
6942         }
6943 
6944       if (index != -1)
6945         {
6946           for (l = info->links; l != NULL; l = l->next)
6947             {
6948               GtkLabelLink *link = l->data;
6949               if (index >= link->start && index <= link->end)
6950                 {
6951                   if (link->title)
6952                     {
6953                       gtk_tooltip_set_markup (tooltip, link->title);
6954                       return TRUE;
6955                     }
6956                   break;
6957                 }
6958             }
6959         }
6960     }
6961 
6962   return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
6963                                                                    x, y,
6964                                                                    keyboard_tip,
6965                                                                    tooltip);
6966 }
6967 
6968 gint
_gtk_label_get_cursor_position(GtkLabel * label)6969 _gtk_label_get_cursor_position (GtkLabel *label)
6970 {
6971   GtkLabelPrivate *priv = label->priv;
6972 
6973   if (priv->select_info && priv->select_info->selectable)
6974     return g_utf8_pointer_to_offset (priv->text,
6975                                      priv->text + priv->select_info->selection_end);
6976 
6977   return 0;
6978 }
6979 
6980 gint
_gtk_label_get_selection_bound(GtkLabel * label)6981 _gtk_label_get_selection_bound (GtkLabel *label)
6982 {
6983   GtkLabelPrivate *priv = label->priv;
6984 
6985   if (priv->select_info && priv->select_info->selectable)
6986     return g_utf8_pointer_to_offset (priv->text,
6987                                      priv->text + priv->select_info->selection_anchor);
6988 
6989   return 0;
6990 }
6991 
6992 /**
6993  * gtk_label_set_lines:
6994  * @label: a #GtkLabel
6995  * @lines: the desired number of lines, or -1
6996  *
6997  * Sets the number of lines to which an ellipsized, wrapping label
6998  * should be limited. This has no effect if the label is not wrapping
6999  * or ellipsized. Set this to -1 if you don’t want to limit the
7000  * number of lines.
7001  *
7002  * Since: 3.10
7003  */
7004 void
gtk_label_set_lines(GtkLabel * label,gint lines)7005 gtk_label_set_lines (GtkLabel *label,
7006                      gint      lines)
7007 {
7008   GtkLabelPrivate *priv;
7009 
7010   g_return_if_fail (GTK_IS_LABEL (label));
7011 
7012   priv = label->priv;
7013 
7014   if (priv->lines != lines)
7015     {
7016       priv->lines = lines;
7017       gtk_label_clear_layout (label);
7018       g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_LINES]);
7019       gtk_widget_queue_resize (GTK_WIDGET (label));
7020     }
7021 }
7022 
7023 /**
7024  * gtk_label_get_lines:
7025  * @label: a #GtkLabel
7026  *
7027  * Gets the number of lines to which an ellipsized, wrapping
7028  * label should be limited. See gtk_label_set_lines().
7029  *
7030  * Returns: The number of lines
7031  *
7032  * Since: 3.10
7033  */
7034 gint
gtk_label_get_lines(GtkLabel * label)7035 gtk_label_get_lines (GtkLabel *label)
7036 {
7037   g_return_val_if_fail (GTK_IS_LABEL (label), -1);
7038 
7039   return label->priv->lines;
7040 }
7041 
7042 gint
_gtk_label_get_n_links(GtkLabel * label)7043 _gtk_label_get_n_links (GtkLabel *label)
7044 {
7045   GtkLabelPrivate *priv = label->priv;
7046 
7047   if (priv->select_info)
7048     return g_list_length (priv->select_info->links);
7049 
7050   return 0;
7051 }
7052 
7053 const gchar *
_gtk_label_get_link_uri(GtkLabel * label,gint idx)7054 _gtk_label_get_link_uri (GtkLabel *label,
7055                          gint      idx)
7056 {
7057   GtkLabelPrivate *priv = label->priv;
7058 
7059   if (priv->select_info)
7060     {
7061       GtkLabelLink *link = g_list_nth_data (priv->select_info->links, idx);
7062       if (link)
7063         return link->uri;
7064     }
7065 
7066   return NULL;
7067 }
7068 
7069 void
_gtk_label_get_link_extent(GtkLabel * label,gint idx,gint * start,gint * end)7070 _gtk_label_get_link_extent (GtkLabel *label,
7071                             gint      idx,
7072                             gint     *start,
7073                             gint     *end)
7074 {
7075   GtkLabelPrivate *priv = label->priv;
7076   gint i;
7077   GList *l;
7078   GtkLabelLink *link;
7079 
7080   if (priv->select_info)
7081     for (l = priv->select_info->links, i = 0; l; l = l->next, i++)
7082       {
7083         if (i == idx)
7084           {
7085             link = l->data;
7086             *start = link->start;
7087             *end = link->end;
7088             return;
7089           }
7090       }
7091 
7092   *start = -1;
7093   *end = -1;
7094 }
7095 
7096 gint
_gtk_label_get_link_at(GtkLabel * label,gint pos)7097 _gtk_label_get_link_at (GtkLabel *label,
7098                         gint      pos)
7099 {
7100   GtkLabelPrivate *priv = label->priv;
7101   gint i;
7102   GList *l;
7103   GtkLabelLink *link;
7104 
7105   if (priv->select_info)
7106     for (l = priv->select_info->links, i = 0; l; l = l->next, i++)
7107       {
7108         link = l->data;
7109         if (link->start <= pos && pos < link->end)
7110           return i;
7111       }
7112 
7113   return -1;
7114 }
7115 
7116 void
_gtk_label_activate_link(GtkLabel * label,gint idx)7117 _gtk_label_activate_link (GtkLabel *label,
7118                           gint      idx)
7119 {
7120   GtkLabelPrivate *priv = label->priv;
7121 
7122   if (priv->select_info)
7123     {
7124       GtkLabelLink *link = g_list_nth_data (priv->select_info->links, idx);
7125 
7126       if (link)
7127         emit_activate_link (label, link);
7128     }
7129 }
7130 
7131 gboolean
_gtk_label_get_link_visited(GtkLabel * label,gint idx)7132 _gtk_label_get_link_visited (GtkLabel *label,
7133                              gint      idx)
7134 {
7135   GtkLabelPrivate *priv = label->priv;
7136 
7137   if (priv->select_info)
7138     {
7139       GtkLabelLink *link = g_list_nth_data (priv->select_info->links, idx);
7140       return link ? link->visited : FALSE;
7141     }
7142 
7143   return FALSE;
7144 }
7145 
7146 gboolean
_gtk_label_get_link_focused(GtkLabel * label,gint idx)7147 _gtk_label_get_link_focused (GtkLabel *label,
7148                              gint      idx)
7149 {
7150   GtkLabelPrivate *priv = label->priv;
7151   gint i;
7152   GList *l;
7153   GtkLabelLink *link;
7154   GtkLabelSelectionInfo *info = priv->select_info;
7155 
7156   if (!info)
7157     return FALSE;
7158 
7159   if (info->selection_anchor != info->selection_end)
7160     return FALSE;
7161 
7162   for (l = info->links, i = 0; l; l = l->next, i++)
7163     {
7164       if (i == idx)
7165         {
7166           link = l->data;
7167           if (link->start <= info->selection_anchor &&
7168               info->selection_anchor <= link->end)
7169             return TRUE;
7170         }
7171     }
7172 
7173   return FALSE;
7174 }
7175 
7176 /**
7177  * gtk_label_set_xalign:
7178  * @label: a #GtkLabel
7179  * @xalign: the new xalign value, between 0 and 1
7180  *
7181  * Sets the #GtkLabel:xalign property for @label.
7182  *
7183  * Since: 3.16
7184  */
7185 void
gtk_label_set_xalign(GtkLabel * label,gfloat xalign)7186 gtk_label_set_xalign (GtkLabel *label,
7187                       gfloat    xalign)
7188 {
7189   g_return_if_fail (GTK_IS_LABEL (label));
7190 
7191   xalign = CLAMP (xalign, 0.0, 1.0);
7192 
7193   if (label->priv->xalign == xalign)
7194     return;
7195 
7196   label->priv->xalign = xalign;
7197 
7198   gtk_widget_queue_draw (GTK_WIDGET (label));
7199   g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_XALIGN]);
7200 }
7201 
7202 /**
7203  * gtk_label_get_xalign:
7204  * @label: a #GtkLabel
7205  *
7206  * Gets the #GtkLabel:xalign property for @label.
7207  *
7208  * Returns: the xalign property
7209  *
7210  * Since: 3.16
7211  */
7212 gfloat
gtk_label_get_xalign(GtkLabel * label)7213 gtk_label_get_xalign (GtkLabel *label)
7214 {
7215   g_return_val_if_fail (GTK_IS_LABEL (label), 0.5);
7216 
7217   return label->priv->xalign;
7218 }
7219 
7220 /**
7221  * gtk_label_set_yalign:
7222  * @label: a #GtkLabel
7223  * @yalign: the new yalign value, between 0 and 1
7224  *
7225  * Sets the #GtkLabel:yalign property for @label.
7226  *
7227  * Since: 3.16
7228  */
7229 void
gtk_label_set_yalign(GtkLabel * label,gfloat yalign)7230 gtk_label_set_yalign (GtkLabel *label,
7231                       gfloat    yalign)
7232 {
7233   g_return_if_fail (GTK_IS_LABEL (label));
7234 
7235   yalign = CLAMP (yalign, 0.0, 1.0);
7236 
7237   if (label->priv->yalign == yalign)
7238     return;
7239 
7240   label->priv->yalign = yalign;
7241 
7242   gtk_widget_queue_draw (GTK_WIDGET (label));
7243   g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_YALIGN]);
7244 }
7245 
7246 /**
7247  * gtk_label_get_yalign:
7248  * @label: a #GtkLabel
7249  *
7250  * Gets the #GtkLabel:yalign property for @label.
7251  *
7252  * Returns: the yalign property
7253  *
7254  * Since: 3.16
7255  */
7256 gfloat
gtk_label_get_yalign(GtkLabel * label)7257 gtk_label_get_yalign (GtkLabel *label)
7258 {
7259   g_return_val_if_fail (GTK_IS_LABEL (label), 0.5);
7260 
7261   return label->priv->yalign;
7262 }
7263