1 /* gtkcellrenderertext.c
2  * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include "gtkcellrenderertext.h"
21 
22 #include <stdlib.h>
23 
24 #include "gtkeditable.h"
25 #include "gtkentry.h"
26 #include "gtksizerequest.h"
27 #include "gtkmarshalers.h"
28 #include "gtkintl.h"
29 #include "gtkprivate.h"
30 #include "gtktreeprivate.h"
31 #include "a11y/gtktextcellaccessible.h"
32 
33 
34 /**
35  * SECTION:gtkcellrenderertext
36  * @Short_description: Renders text in a cell
37  * @Title: GtkCellRendererText
38  *
39  * A #GtkCellRendererText renders a given text in its cell, using the font, color and
40  * style information provided by its properties. The text will be ellipsized if it is
41  * too long and the #GtkCellRendererText:ellipsize property allows it.
42  *
43  * If the #GtkCellRenderer:mode is %GTK_CELL_RENDERER_MODE_EDITABLE,
44  * the #GtkCellRendererText allows to edit its text using an entry.
45  */
46 
47 
48 static void gtk_cell_renderer_text_finalize   (GObject                  *object);
49 
50 static void gtk_cell_renderer_text_get_property  (GObject                  *object,
51 						  guint                     param_id,
52 						  GValue                   *value,
53 						  GParamSpec               *pspec);
54 static void gtk_cell_renderer_text_set_property  (GObject                  *object,
55 						  guint                     param_id,
56 						  const GValue             *value,
57 						  GParamSpec               *pspec);
58 static void gtk_cell_renderer_text_render     (GtkCellRenderer          *cell,
59 					       cairo_t                  *cr,
60 					       GtkWidget                *widget,
61 					       const GdkRectangle       *background_area,
62 					       const GdkRectangle       *cell_area,
63 					       GtkCellRendererState      flags);
64 
65 static GtkCellEditable *gtk_cell_renderer_text_start_editing (GtkCellRenderer      *cell,
66 							      GdkEvent             *event,
67 							      GtkWidget            *widget,
68 							      const gchar          *path,
69 							      const GdkRectangle   *background_area,
70 							      const GdkRectangle   *cell_area,
71 							      GtkCellRendererState  flags);
72 
73 static void       gtk_cell_renderer_text_get_preferred_width            (GtkCellRenderer       *cell,
74                                                                          GtkWidget             *widget,
75                                                                          gint                  *minimal_size,
76                                                                          gint                  *natural_size);
77 static void       gtk_cell_renderer_text_get_preferred_height           (GtkCellRenderer       *cell,
78                                                                          GtkWidget             *widget,
79                                                                          gint                  *minimal_size,
80                                                                          gint                  *natural_size);
81 static void       gtk_cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer       *cell,
82                                                                          GtkWidget             *widget,
83                                                                          gint                   width,
84                                                                          gint                  *minimum_height,
85                                                                          gint                  *natural_height);
86 static void       gtk_cell_renderer_text_get_aligned_area               (GtkCellRenderer       *cell,
87 									 GtkWidget             *widget,
88 									 GtkCellRendererState   flags,
89 									 const GdkRectangle    *cell_area,
90 									 GdkRectangle          *aligned_area);
91 
92 
93 
94 enum {
95   EDITED,
96   LAST_SIGNAL
97 };
98 
99 enum {
100   PROP_0,
101 
102   PROP_TEXT,
103   PROP_MARKUP,
104   PROP_ATTRIBUTES,
105   PROP_SINGLE_PARAGRAPH_MODE,
106   PROP_WIDTH_CHARS,
107   PROP_MAX_WIDTH_CHARS,
108   PROP_WRAP_WIDTH,
109   PROP_ALIGN,
110   PROP_PLACEHOLDER_TEXT,
111 
112   /* Style args */
113   PROP_BACKGROUND,
114   PROP_FOREGROUND,
115   PROP_BACKGROUND_GDK,
116   PROP_FOREGROUND_GDK,
117   PROP_BACKGROUND_RGBA,
118   PROP_FOREGROUND_RGBA,
119   PROP_FONT,
120   PROP_FONT_DESC,
121   PROP_FAMILY,
122   PROP_STYLE,
123   PROP_VARIANT,
124   PROP_WEIGHT,
125   PROP_STRETCH,
126   PROP_SIZE,
127   PROP_SIZE_POINTS,
128   PROP_SCALE,
129   PROP_EDITABLE,
130   PROP_STRIKETHROUGH,
131   PROP_UNDERLINE,
132   PROP_RISE,
133   PROP_LANGUAGE,
134   PROP_ELLIPSIZE,
135   PROP_WRAP_MODE,
136 
137   /* Whether-a-style-arg-is-set args */
138   PROP_BACKGROUND_SET,
139   PROP_FOREGROUND_SET,
140   PROP_FAMILY_SET,
141   PROP_STYLE_SET,
142   PROP_VARIANT_SET,
143   PROP_WEIGHT_SET,
144   PROP_STRETCH_SET,
145   PROP_SIZE_SET,
146   PROP_SCALE_SET,
147   PROP_EDITABLE_SET,
148   PROP_STRIKETHROUGH_SET,
149   PROP_UNDERLINE_SET,
150   PROP_RISE_SET,
151   PROP_LANGUAGE_SET,
152   PROP_ELLIPSIZE_SET,
153   PROP_ALIGN_SET,
154 
155   LAST_PROP
156 };
157 
158 static guint text_cell_renderer_signals [LAST_SIGNAL];
159 static GParamSpec *text_cell_renderer_props [LAST_PROP];
160 
161 #define GTK_CELL_RENDERER_TEXT_PATH "gtk-cell-renderer-text-path"
162 
163 struct _GtkCellRendererTextPrivate
164 {
165   GtkWidget *entry;
166 
167   PangoAttrList        *extra_attrs;
168   GdkRGBA               foreground;
169   GdkRGBA               background;
170   PangoAlignment        align;
171   PangoEllipsizeMode    ellipsize;
172   PangoFontDescription *font;
173   PangoLanguage        *language;
174   PangoUnderline        underline_style;
175   PangoWrapMode         wrap_mode;
176 
177   gchar *text;
178   gchar *placeholder_text;
179 
180   gdouble font_scale;
181 
182   gint rise;
183   gint fixed_height_rows;
184   gint width_chars;
185   gint max_width_chars;
186   gint wrap_width;
187 
188   guint in_entry_menu     : 1;
189   guint strikethrough     : 1;
190   guint editable          : 1;
191   guint scale_set         : 1;
192   guint foreground_set    : 1;
193   guint background_set    : 1;
194   guint underline_set     : 1;
195   guint rise_set          : 1;
196   guint strikethrough_set : 1;
197   guint editable_set      : 1;
198   guint calc_fixed_height : 1;
199   guint single_paragraph  : 1;
200   guint language_set      : 1;
201   guint markup_set        : 1;
202   guint ellipsize_set     : 1;
203   guint align_set         : 1;
204 
205   gulong focus_out_id;
206   gulong populate_popup_id;
207   gulong entry_menu_popdown_timeout;
208 };
209 
G_DEFINE_TYPE_WITH_PRIVATE(GtkCellRendererText,gtk_cell_renderer_text,GTK_TYPE_CELL_RENDERER)210 G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererText, gtk_cell_renderer_text, GTK_TYPE_CELL_RENDERER)
211 
212 static void
213 gtk_cell_renderer_text_init (GtkCellRendererText *celltext)
214 {
215   GtkCellRendererTextPrivate *priv;
216   GtkCellRenderer *cell = GTK_CELL_RENDERER (celltext);
217 
218   celltext->priv = gtk_cell_renderer_text_get_instance_private (celltext);
219   priv = celltext->priv;
220 
221   gtk_cell_renderer_set_alignment (cell, 0.0, 0.5);
222   gtk_cell_renderer_set_padding (cell, 2, 2);
223   priv->font_scale = 1.0;
224   priv->fixed_height_rows = -1;
225   priv->font = pango_font_description_new ();
226 
227   priv->width_chars = -1;
228   priv->max_width_chars = -1;
229   priv->wrap_width = -1;
230   priv->wrap_mode = PANGO_WRAP_CHAR;
231   priv->align = PANGO_ALIGN_LEFT;
232   priv->align_set = FALSE;
233 }
234 
235 static void
gtk_cell_renderer_text_class_init(GtkCellRendererTextClass * class)236 gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class)
237 {
238   GObjectClass *object_class = G_OBJECT_CLASS (class);
239   GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
240 
241   object_class->finalize = gtk_cell_renderer_text_finalize;
242 
243   object_class->get_property = gtk_cell_renderer_text_get_property;
244   object_class->set_property = gtk_cell_renderer_text_set_property;
245 
246   cell_class->render = gtk_cell_renderer_text_render;
247   cell_class->start_editing = gtk_cell_renderer_text_start_editing;
248   cell_class->get_preferred_width = gtk_cell_renderer_text_get_preferred_width;
249   cell_class->get_preferred_height = gtk_cell_renderer_text_get_preferred_height;
250   cell_class->get_preferred_height_for_width = gtk_cell_renderer_text_get_preferred_height_for_width;
251   cell_class->get_aligned_area = gtk_cell_renderer_text_get_aligned_area;
252 
253   text_cell_renderer_props[PROP_TEXT] =
254       g_param_spec_string ("text",
255                            P_("Text"),
256                            P_("Text to render"),
257                            NULL,
258                            GTK_PARAM_READWRITE);
259 
260   text_cell_renderer_props[PROP_MARKUP] =
261       g_param_spec_string ("markup",
262                            P_("Markup"),
263                            P_("Marked up text to render"),
264                            NULL,
265                            GTK_PARAM_WRITABLE);
266 
267   text_cell_renderer_props[PROP_ATTRIBUTES] =
268       g_param_spec_boxed ("attributes",
269                           P_("Attributes"),
270                           P_("A list of style attributes to apply to the text of the renderer"),
271                           PANGO_TYPE_ATTR_LIST,
272                           GTK_PARAM_READWRITE);
273 
274   text_cell_renderer_props[PROP_SINGLE_PARAGRAPH_MODE] =
275       g_param_spec_boolean ("single-paragraph-mode",
276                             P_("Single Paragraph Mode"),
277                             P_("Whether to keep all text in a single paragraph"),
278                             FALSE,
279                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
280 
281   text_cell_renderer_props[PROP_BACKGROUND] =
282       g_param_spec_string ("background",
283                            P_("Background color name"),
284                            P_("Background color as a string"),
285                            NULL,
286                            GTK_PARAM_WRITABLE);
287 
288   /**
289    * GtkCellRendererText:background-gdk:
290    *
291    * Background color as a #GdkColor
292    *
293    * Deprecated: 3.4: Use #GtkCellRendererText:background-rgba instead.
294    */
295 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
296   text_cell_renderer_props[PROP_BACKGROUND_GDK] =
297       g_param_spec_boxed ("background-gdk",
298                           P_("Background color"),
299                           P_("Background color as a GdkColor"),
300                           GDK_TYPE_COLOR,
301                           GTK_PARAM_READWRITE | G_PARAM_DEPRECATED);
302 G_GNUC_END_IGNORE_DEPRECATIONS
303 
304   /**
305    * GtkCellRendererText:background-rgba:
306    *
307    * Background color as a #GdkRGBA
308    *
309    * Since: 3.0
310    */
311   text_cell_renderer_props[PROP_BACKGROUND_RGBA] =
312       g_param_spec_boxed ("background-rgba",
313                           P_("Background color as RGBA"),
314                           P_("Background color as a GdkRGBA"),
315                           GDK_TYPE_RGBA,
316                           GTK_PARAM_READWRITE);
317   text_cell_renderer_props[PROP_FOREGROUND] =
318       g_param_spec_string ("foreground",
319                            P_("Foreground color name"),
320                            P_("Foreground color as a string"),
321                            NULL,
322                            GTK_PARAM_WRITABLE);
323 
324   /**
325    * GtkCellRendererText:foreground-gdk:
326    *
327    * Foreground color as a #GdkColor
328    *
329    * Deprecated: 3.4: Use #GtkCellRendererText:foreground-rgba instead.
330    */
331 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
332   text_cell_renderer_props[PROP_FOREGROUND_GDK] =
333       g_param_spec_boxed ("foreground-gdk",
334                           P_("Foreground color"),
335                           P_("Foreground color as a GdkColor"),
336                           GDK_TYPE_COLOR,
337                           GTK_PARAM_READWRITE | G_PARAM_DEPRECATED);
338 G_GNUC_END_IGNORE_DEPRECATIONS
339 
340   /**
341    * GtkCellRendererText:foreground-rgba:
342    *
343    * Foreground color as a #GdkRGBA
344    *
345    * Since: 3.0
346    */
347   text_cell_renderer_props[PROP_FOREGROUND_RGBA] =
348       g_param_spec_boxed ("foreground-rgba",
349                           P_("Foreground color as RGBA"),
350                           P_("Foreground color as a GdkRGBA"),
351                           GDK_TYPE_RGBA,
352                           GTK_PARAM_READWRITE);
353 
354 
355   text_cell_renderer_props[PROP_EDITABLE] =
356       g_param_spec_boolean ("editable",
357                             P_("Editable"),
358                             P_("Whether the text can be modified by the user"),
359                             FALSE,
360                             GTK_PARAM_READWRITE);
361 
362   text_cell_renderer_props[PROP_FONT] =
363       g_param_spec_string ("font",
364                            P_("Font"),
365                            P_("Font description as a string, e.g. \"Sans Italic 12\""),
366                            NULL,
367                            GTK_PARAM_READWRITE);
368 
369   text_cell_renderer_props[PROP_FONT_DESC] =
370       g_param_spec_boxed ("font-desc",
371                           P_("Font"),
372                           P_("Font description as a PangoFontDescription struct"),
373                           PANGO_TYPE_FONT_DESCRIPTION,
374                           GTK_PARAM_READWRITE);
375 
376   text_cell_renderer_props[PROP_FAMILY] =
377       g_param_spec_string ("family",
378                            P_("Font family"),
379                            P_("Name of the font family, e.g. Sans, Helvetica, Times, Monospace"),
380                            NULL,
381                            GTK_PARAM_READWRITE);
382 
383   text_cell_renderer_props[PROP_STYLE] =
384       g_param_spec_enum ("style",
385                          P_("Font style"),
386                          P_("Font style"),
387                          PANGO_TYPE_STYLE,
388                          PANGO_STYLE_NORMAL,
389                          GTK_PARAM_READWRITE);
390 
391   text_cell_renderer_props[PROP_VARIANT] =
392       g_param_spec_enum ("variant",
393                          P_("Font variant"),
394                          P_("Font variant"),
395                          PANGO_TYPE_VARIANT,
396                          PANGO_VARIANT_NORMAL,
397                          GTK_PARAM_READWRITE);
398 
399   text_cell_renderer_props[PROP_WEIGHT] =
400       g_param_spec_int ("weight",
401                         P_("Font weight"),
402                         P_("Font weight"),
403                         0, G_MAXINT,
404                         PANGO_WEIGHT_NORMAL,
405                         GTK_PARAM_READWRITE);
406 
407    text_cell_renderer_props[PROP_STRETCH] =
408        g_param_spec_enum ("stretch",
409                           P_("Font stretch"),
410                           P_("Font stretch"),
411                           PANGO_TYPE_STRETCH,
412                           PANGO_STRETCH_NORMAL,
413                           GTK_PARAM_READWRITE);
414 
415   text_cell_renderer_props[PROP_SIZE] =
416       g_param_spec_int ("size",
417                         P_("Font size"),
418                         P_("Font size"),
419                         0, G_MAXINT,
420                         0,
421                         GTK_PARAM_READWRITE);
422 
423   text_cell_renderer_props[PROP_SIZE_POINTS] =
424       g_param_spec_double ("size-points",
425                            P_("Font points"),
426                            P_("Font size in points"),
427                            0.0, G_MAXDOUBLE,
428                            0.0,
429                            GTK_PARAM_READWRITE);
430 
431   text_cell_renderer_props[PROP_SCALE] =
432       g_param_spec_double ("scale",
433                            P_("Font scale"),
434                            P_("Font scaling factor"),
435                            0.0, G_MAXDOUBLE,
436                            1.0,
437                            GTK_PARAM_READWRITE);
438 
439   text_cell_renderer_props[PROP_RISE] =
440       g_param_spec_int ("rise",
441                         P_("Rise"),
442                         P_("Offset of text above the baseline (below the baseline if rise is negative)"),
443                         -G_MAXINT, G_MAXINT,
444                         0,
445                         GTK_PARAM_READWRITE);
446 
447 
448   text_cell_renderer_props[PROP_STRIKETHROUGH] =
449       g_param_spec_boolean ("strikethrough",
450                             P_("Strikethrough"),
451                             P_("Whether to strike through the text"),
452                             FALSE,
453                             GTK_PARAM_READWRITE);
454 
455   text_cell_renderer_props[PROP_UNDERLINE] =
456       g_param_spec_enum ("underline",
457                          P_("Underline"),
458                          P_("Style of underline for this text"),
459                          PANGO_TYPE_UNDERLINE,
460                          PANGO_UNDERLINE_NONE,
461                          GTK_PARAM_READWRITE);
462 
463   text_cell_renderer_props[PROP_LANGUAGE] =
464       g_param_spec_string ("language",
465                            P_("Language"),
466                            P_("The language this text is in, as an ISO code. "
467                               "Pango can use this as a hint when rendering the text. "
468                               "If you don't understand this parameter, you probably don't need it"),
469                            NULL,
470                            GTK_PARAM_READWRITE);
471 
472   /**
473    * GtkCellRendererText:ellipsize:
474    *
475    * Specifies the preferred place to ellipsize the string, if the cell renderer
476    * does not have enough room to display the entire string. Setting it to
477    * %PANGO_ELLIPSIZE_NONE turns off ellipsizing. See the wrap-width property
478    * for another way of making the text fit into a given width.
479    *
480    * Since: 2.6
481    */
482   text_cell_renderer_props[PROP_ELLIPSIZE] =
483       g_param_spec_enum ("ellipsize",
484                          P_("Ellipsize"),
485                          P_("The preferred place to ellipsize the string, "
486                             "if the cell renderer does not have enough room "
487                             "to display the entire string"),
488                          PANGO_TYPE_ELLIPSIZE_MODE,
489                          PANGO_ELLIPSIZE_NONE,
490                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
491 
492   /**
493    * GtkCellRendererText:width-chars:
494    *
495    * The desired width of the cell, in characters. If this property is set to
496    * -1, the width will be calculated automatically, otherwise the cell will
497    * request either 3 characters or the property value, whichever is greater.
498    *
499    * Since: 2.6
500    **/
501   text_cell_renderer_props[PROP_WIDTH_CHARS] =
502       g_param_spec_int ("width-chars",
503                         P_("Width In Characters"),
504                         P_("The desired width of the label, in characters"),
505                         -1, G_MAXINT,
506                         -1,
507                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
508 
509   /**
510    * GtkCellRendererText:max-width-chars:
511    *
512    * The desired maximum width of the cell, in characters. If this property
513    * is set to -1, the width will be calculated automatically.
514    *
515    * For cell renderers that ellipsize or wrap text; this property
516    * controls the maximum reported width of the cell. The
517    * cell should not receive any greater allocation unless it is
518    * set to expand in its #GtkCellLayout and all of the cell's siblings
519    * have received their natural width.
520    *
521    * Since: 3.0
522    **/
523   text_cell_renderer_props[PROP_MAX_WIDTH_CHARS] =
524       g_param_spec_int ("max-width-chars",
525                         P_("Maximum Width In Characters"),
526                         P_("The maximum width of the cell, in characters"),
527                         -1, G_MAXINT,
528                         -1,
529                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
530 
531   /**
532    * GtkCellRendererText:wrap-mode:
533    *
534    * Specifies how to break the string into multiple lines, if the cell
535    * renderer does not have enough room to display the entire string.
536    * This property has no effect unless the wrap-width property is set.
537    *
538    * Since: 2.8
539    */
540   text_cell_renderer_props[PROP_WRAP_MODE] =
541       g_param_spec_enum ("wrap-mode",
542                          P_("Wrap mode"),
543                          P_("How to break the string into multiple lines, "
544                             "if the cell renderer does not have enough room "
545                             "to display the entire string"),
546                          PANGO_TYPE_WRAP_MODE,
547                          PANGO_WRAP_CHAR,
548                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
549 
550   /**
551    * GtkCellRendererText:wrap-width:
552    *
553    * Specifies the minimum width at which the text is wrapped. The wrap-mode property can
554    * be used to influence at what character positions the line breaks can be placed.
555    * Setting wrap-width to -1 turns wrapping off.
556    *
557    * Since: 2.8
558    */
559   text_cell_renderer_props[PROP_WRAP_WIDTH] =
560       g_param_spec_int ("wrap-width",
561                         P_("Wrap width"),
562                         P_("The width at which the text is wrapped"),
563                         -1, G_MAXINT,
564                         -1,
565                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
566 
567   /**
568    * GtkCellRendererText:alignment:
569    *
570    * Specifies how to align the lines of text with respect to each other.
571    *
572    * Note that this property describes how to align the lines of text in
573    * case there are several of them. The "xalign" property of #GtkCellRenderer,
574    * on the other hand, sets the horizontal alignment of the whole text.
575    *
576    * Since: 2.10
577    */
578   text_cell_renderer_props[PROP_ALIGN] =
579       g_param_spec_enum ("alignment",
580                          P_("Alignment"),
581                          P_("How to align the lines"),
582                          PANGO_TYPE_ALIGNMENT,
583                          PANGO_ALIGN_LEFT,
584                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
585 
586   /**
587    * GtkCellRendererText:placeholder-text:
588    *
589    * The text that will be displayed in the #GtkCellRenderer if
590    * #GtkCellRendererText:editable is %TRUE and the cell is empty.
591    *
592    * Since 3.6
593    */
594   text_cell_renderer_props[PROP_PLACEHOLDER_TEXT] =
595       g_param_spec_string ("placeholder-text",
596                            P_("Placeholder text"),
597                            P_("Text rendered when an editable cell is empty"),
598                            NULL,
599                            GTK_PARAM_READWRITE);
600 
601   /* Style props are set or not */
602 
603 #define ADD_SET_PROP(propname, propval, nick, blurb) text_cell_renderer_props[propval] = g_param_spec_boolean (propname, nick, blurb, FALSE, GTK_PARAM_READWRITE)
604 
605   ADD_SET_PROP ("background-set", PROP_BACKGROUND_SET,
606                 P_("Background set"),
607                 P_("Whether this tag affects the background color"));
608 
609   ADD_SET_PROP ("foreground-set", PROP_FOREGROUND_SET,
610                 P_("Foreground set"),
611                 P_("Whether this tag affects the foreground color"));
612 
613   ADD_SET_PROP ("editable-set", PROP_EDITABLE_SET,
614                 P_("Editability set"),
615                 P_("Whether this tag affects text editability"));
616 
617   ADD_SET_PROP ("family-set", PROP_FAMILY_SET,
618                 P_("Font family set"),
619                 P_("Whether this tag affects the font family"));
620 
621   ADD_SET_PROP ("style-set", PROP_STYLE_SET,
622                 P_("Font style set"),
623                 P_("Whether this tag affects the font style"));
624 
625   ADD_SET_PROP ("variant-set", PROP_VARIANT_SET,
626                 P_("Font variant set"),
627                 P_("Whether this tag affects the font variant"));
628 
629   ADD_SET_PROP ("weight-set", PROP_WEIGHT_SET,
630                 P_("Font weight set"),
631                 P_("Whether this tag affects the font weight"));
632 
633   ADD_SET_PROP ("stretch-set", PROP_STRETCH_SET,
634                 P_("Font stretch set"),
635                 P_("Whether this tag affects the font stretch"));
636 
637   ADD_SET_PROP ("size-set", PROP_SIZE_SET,
638                 P_("Font size set"),
639                 P_("Whether this tag affects the font size"));
640 
641   ADD_SET_PROP ("scale-set", PROP_SCALE_SET,
642                 P_("Font scale set"),
643                 P_("Whether this tag scales the font size by a factor"));
644 
645   ADD_SET_PROP ("rise-set", PROP_RISE_SET,
646                 P_("Rise set"),
647                 P_("Whether this tag affects the rise"));
648 
649   ADD_SET_PROP ("strikethrough-set", PROP_STRIKETHROUGH_SET,
650                 P_("Strikethrough set"),
651                 P_("Whether this tag affects strikethrough"));
652 
653   ADD_SET_PROP ("underline-set", PROP_UNDERLINE_SET,
654                 P_("Underline set"),
655                 P_("Whether this tag affects underlining"));
656 
657   ADD_SET_PROP ("language-set", PROP_LANGUAGE_SET,
658                 P_("Language set"),
659                 P_("Whether this tag affects the language the text is rendered as"));
660 
661   ADD_SET_PROP ("ellipsize-set", PROP_ELLIPSIZE_SET,
662                 P_("Ellipsize set"),
663                 P_("Whether this tag affects the ellipsize mode"));
664 
665   ADD_SET_PROP ("align-set", PROP_ALIGN_SET,
666                 P_("Align set"),
667                 P_("Whether this tag affects the alignment mode"));
668 
669   g_object_class_install_properties (object_class, LAST_PROP, text_cell_renderer_props);
670 
671   /**
672    * GtkCellRendererText::edited:
673    * @renderer: the object which received the signal
674    * @path: the path identifying the edited cell
675    * @new_text: the new text
676    *
677    * This signal is emitted after @renderer has been edited.
678    *
679    * It is the responsibility of the application to update the model
680    * and store @new_text at the position indicated by @path.
681    */
682   text_cell_renderer_signals [EDITED] =
683     g_signal_new (I_("edited"),
684 		  G_OBJECT_CLASS_TYPE (object_class),
685 		  G_SIGNAL_RUN_LAST,
686 		  G_STRUCT_OFFSET (GtkCellRendererTextClass, edited),
687 		  NULL, NULL,
688 		  _gtk_marshal_VOID__STRING_STRING,
689 		  G_TYPE_NONE, 2,
690 		  G_TYPE_STRING,
691 		  G_TYPE_STRING);
692   g_signal_set_va_marshaller (text_cell_renderer_signals [EDITED],
693                               G_OBJECT_CLASS_TYPE (object_class),
694                               _gtk_marshal_VOID__STRING_STRINGv);
695 
696   gtk_cell_renderer_class_set_accessible_type (cell_class, GTK_TYPE_TEXT_CELL_ACCESSIBLE);
697 }
698 
699 static void
gtk_cell_renderer_text_finalize(GObject * object)700 gtk_cell_renderer_text_finalize (GObject *object)
701 {
702   GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object);
703   GtkCellRendererTextPrivate *priv = celltext->priv;
704 
705   pango_font_description_free (priv->font);
706 
707   g_free (priv->text);
708   g_free (priv->placeholder_text);
709 
710   if (priv->extra_attrs)
711     pango_attr_list_unref (priv->extra_attrs);
712 
713   if (priv->language)
714     g_object_unref (priv->language);
715 
716   g_clear_object (&priv->entry);
717 
718   G_OBJECT_CLASS (gtk_cell_renderer_text_parent_class)->finalize (object);
719 }
720 
721 static PangoFontMask
get_property_font_set_mask(guint prop_id)722 get_property_font_set_mask (guint prop_id)
723 {
724   switch (prop_id)
725     {
726     case PROP_FAMILY_SET:
727       return PANGO_FONT_MASK_FAMILY;
728     case PROP_STYLE_SET:
729       return PANGO_FONT_MASK_STYLE;
730     case PROP_VARIANT_SET:
731       return PANGO_FONT_MASK_VARIANT;
732     case PROP_WEIGHT_SET:
733       return PANGO_FONT_MASK_WEIGHT;
734     case PROP_STRETCH_SET:
735       return PANGO_FONT_MASK_STRETCH;
736     case PROP_SIZE_SET:
737       return PANGO_FONT_MASK_SIZE;
738     }
739 
740   return 0;
741 }
742 
743 static void
gtk_cell_renderer_text_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)744 gtk_cell_renderer_text_get_property (GObject        *object,
745 				     guint           param_id,
746 				     GValue         *value,
747 				     GParamSpec     *pspec)
748 {
749   GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object);
750   GtkCellRendererTextPrivate *priv = celltext->priv;
751 
752   switch (param_id)
753     {
754     case PROP_TEXT:
755       g_value_set_string (value, priv->text);
756       break;
757 
758     case PROP_ATTRIBUTES:
759       g_value_set_boxed (value, priv->extra_attrs);
760       break;
761 
762     case PROP_SINGLE_PARAGRAPH_MODE:
763       g_value_set_boolean (value, priv->single_paragraph);
764       break;
765 
766     case PROP_BACKGROUND_GDK:
767       {
768         GdkColor color;
769 
770         color.red = (guint16) (priv->background.red * 65535);
771         color.green = (guint16) (priv->background.green * 65535);
772         color.blue = (guint16) (priv->background.blue * 65535);
773 
774         g_value_set_boxed (value, &color);
775       }
776       break;
777 
778     case PROP_FOREGROUND_GDK:
779       {
780         GdkColor color;
781 
782         color.red = (guint16) (priv->foreground.red * 65535);
783         color.green = (guint16) (priv->foreground.green * 65535);
784         color.blue = (guint16) (priv->foreground.blue * 65535);
785 
786         g_value_set_boxed (value, &color);
787       }
788       break;
789 
790     case PROP_BACKGROUND_RGBA:
791       g_value_set_boxed (value, &priv->background);
792       break;
793 
794     case PROP_FOREGROUND_RGBA:
795       g_value_set_boxed (value, &priv->foreground);
796       break;
797 
798     case PROP_FONT:
799         g_value_take_string (value, pango_font_description_to_string (priv->font));
800       break;
801 
802     case PROP_FONT_DESC:
803       g_value_set_boxed (value, priv->font);
804       break;
805 
806     case PROP_FAMILY:
807       g_value_set_string (value, pango_font_description_get_family (priv->font));
808       break;
809 
810     case PROP_STYLE:
811       g_value_set_enum (value, pango_font_description_get_style (priv->font));
812       break;
813 
814     case PROP_VARIANT:
815       g_value_set_enum (value, pango_font_description_get_variant (priv->font));
816       break;
817 
818     case PROP_WEIGHT:
819       g_value_set_int (value, pango_font_description_get_weight (priv->font));
820       break;
821 
822     case PROP_STRETCH:
823       g_value_set_enum (value, pango_font_description_get_stretch (priv->font));
824       break;
825 
826     case PROP_SIZE:
827       g_value_set_int (value, pango_font_description_get_size (priv->font));
828       break;
829 
830     case PROP_SIZE_POINTS:
831       g_value_set_double (value, ((double)pango_font_description_get_size (priv->font)) / (double)PANGO_SCALE);
832       break;
833 
834     case PROP_SCALE:
835       g_value_set_double (value, priv->font_scale);
836       break;
837 
838     case PROP_EDITABLE:
839       g_value_set_boolean (value, priv->editable);
840       break;
841 
842     case PROP_STRIKETHROUGH:
843       g_value_set_boolean (value, priv->strikethrough);
844       break;
845 
846     case PROP_UNDERLINE:
847       g_value_set_enum (value, priv->underline_style);
848       break;
849 
850     case PROP_RISE:
851       g_value_set_int (value, priv->rise);
852       break;
853 
854     case PROP_LANGUAGE:
855       g_value_set_static_string (value, pango_language_to_string (priv->language));
856       break;
857 
858     case PROP_ELLIPSIZE:
859       g_value_set_enum (value, priv->ellipsize);
860       break;
861 
862     case PROP_WRAP_MODE:
863       g_value_set_enum (value, priv->wrap_mode);
864       break;
865 
866     case PROP_WRAP_WIDTH:
867       g_value_set_int (value, priv->wrap_width);
868       break;
869 
870     case PROP_ALIGN:
871       g_value_set_enum (value, priv->align);
872       break;
873 
874     case PROP_BACKGROUND_SET:
875       g_value_set_boolean (value, priv->background_set);
876       break;
877 
878     case PROP_FOREGROUND_SET:
879       g_value_set_boolean (value, priv->foreground_set);
880       break;
881 
882     case PROP_FAMILY_SET:
883     case PROP_STYLE_SET:
884     case PROP_VARIANT_SET:
885     case PROP_WEIGHT_SET:
886     case PROP_STRETCH_SET:
887     case PROP_SIZE_SET:
888       {
889 	PangoFontMask mask = get_property_font_set_mask (param_id);
890 	g_value_set_boolean (value, (pango_font_description_get_set_fields (priv->font) & mask) != 0);
891 
892 	break;
893       }
894 
895     case PROP_SCALE_SET:
896       g_value_set_boolean (value, priv->scale_set);
897       break;
898 
899     case PROP_EDITABLE_SET:
900       g_value_set_boolean (value, priv->editable_set);
901       break;
902 
903     case PROP_STRIKETHROUGH_SET:
904       g_value_set_boolean (value, priv->strikethrough_set);
905       break;
906 
907     case PROP_UNDERLINE_SET:
908       g_value_set_boolean (value, priv->underline_set);
909       break;
910 
911     case  PROP_RISE_SET:
912       g_value_set_boolean (value, priv->rise_set);
913       break;
914 
915     case PROP_LANGUAGE_SET:
916       g_value_set_boolean (value, priv->language_set);
917       break;
918 
919     case PROP_ELLIPSIZE_SET:
920       g_value_set_boolean (value, priv->ellipsize_set);
921       break;
922 
923     case PROP_ALIGN_SET:
924       g_value_set_boolean (value, priv->align_set);
925       break;
926 
927     case PROP_WIDTH_CHARS:
928       g_value_set_int (value, priv->width_chars);
929       break;
930 
931     case PROP_MAX_WIDTH_CHARS:
932       g_value_set_int (value, priv->max_width_chars);
933       break;
934 
935     case PROP_PLACEHOLDER_TEXT:
936       g_value_set_string (value, priv->placeholder_text);
937       break;
938 
939     case PROP_BACKGROUND:
940     case PROP_FOREGROUND:
941     case PROP_MARKUP:
942     default:
943       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
944       break;
945     }
946 }
947 
948 
949 static void
set_bg_color(GtkCellRendererText * celltext,GdkRGBA * rgba)950 set_bg_color (GtkCellRendererText *celltext,
951               GdkRGBA             *rgba)
952 {
953   GtkCellRendererTextPrivate *priv = celltext->priv;
954 
955   if (rgba)
956     {
957       if (!priv->background_set)
958         {
959           priv->background_set = TRUE;
960           g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_BACKGROUND_SET]);
961         }
962 
963       priv->background = *rgba;
964     }
965   else
966     {
967       if (priv->background_set)
968         {
969           priv->background_set = FALSE;
970           g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_BACKGROUND_SET]);
971         }
972     }
973 }
974 
975 
976 static void
set_fg_color(GtkCellRendererText * celltext,GdkRGBA * rgba)977 set_fg_color (GtkCellRendererText *celltext,
978               GdkRGBA             *rgba)
979 {
980   GtkCellRendererTextPrivate *priv = celltext->priv;
981 
982   if (rgba)
983     {
984       if (!priv->foreground_set)
985         {
986           priv->foreground_set = TRUE;
987           g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_FOREGROUND_SET]);
988         }
989 
990       priv->foreground = *rgba;
991     }
992   else
993     {
994       if (priv->foreground_set)
995         {
996           priv->foreground_set = FALSE;
997           g_object_notify_by_pspec (G_OBJECT (celltext), text_cell_renderer_props[PROP_FOREGROUND_SET]);
998         }
999     }
1000 }
1001 
1002 static PangoFontMask
set_font_desc_fields(PangoFontDescription * desc,PangoFontMask to_set)1003 set_font_desc_fields (PangoFontDescription *desc,
1004 		      PangoFontMask         to_set)
1005 {
1006   PangoFontMask changed_mask = 0;
1007 
1008   if (to_set & PANGO_FONT_MASK_FAMILY)
1009     {
1010       const char *family = pango_font_description_get_family (desc);
1011       if (!family)
1012 	{
1013 	  family = "sans";
1014 	  changed_mask |= PANGO_FONT_MASK_FAMILY;
1015 	}
1016 
1017       pango_font_description_set_family (desc, family);
1018     }
1019   if (to_set & PANGO_FONT_MASK_STYLE)
1020     pango_font_description_set_style (desc, pango_font_description_get_style (desc));
1021   if (to_set & PANGO_FONT_MASK_VARIANT)
1022     pango_font_description_set_variant (desc, pango_font_description_get_variant (desc));
1023   if (to_set & PANGO_FONT_MASK_WEIGHT)
1024     pango_font_description_set_weight (desc, pango_font_description_get_weight (desc));
1025   if (to_set & PANGO_FONT_MASK_STRETCH)
1026     pango_font_description_set_stretch (desc, pango_font_description_get_stretch (desc));
1027   if (to_set & PANGO_FONT_MASK_SIZE)
1028     {
1029       gint size = pango_font_description_get_size (desc);
1030       if (size <= 0)
1031 	{
1032 	  size = 10 * PANGO_SCALE;
1033 	  changed_mask |= PANGO_FONT_MASK_SIZE;
1034 	}
1035 
1036       pango_font_description_set_size (desc, size);
1037     }
1038 
1039   return changed_mask;
1040 }
1041 
1042 static void
notify_set_changed(GObject * object,PangoFontMask changed_mask)1043 notify_set_changed (GObject       *object,
1044 		    PangoFontMask  changed_mask)
1045 {
1046   if (changed_mask & PANGO_FONT_MASK_FAMILY)
1047     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FAMILY_SET]);
1048   if (changed_mask & PANGO_FONT_MASK_STYLE)
1049     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STYLE_SET]);
1050   if (changed_mask & PANGO_FONT_MASK_VARIANT)
1051     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_VARIANT_SET]);
1052   if (changed_mask & PANGO_FONT_MASK_WEIGHT)
1053     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_WEIGHT_SET]);
1054   if (changed_mask & PANGO_FONT_MASK_STRETCH)
1055     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STRETCH_SET]);
1056   if (changed_mask & PANGO_FONT_MASK_SIZE)
1057     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SIZE_SET]);
1058 }
1059 
1060 static void
notify_fields_changed(GObject * object,PangoFontMask changed_mask)1061 notify_fields_changed (GObject       *object,
1062 		       PangoFontMask  changed_mask)
1063 {
1064   if (changed_mask & PANGO_FONT_MASK_FAMILY)
1065     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FAMILY]);
1066   if (changed_mask & PANGO_FONT_MASK_STYLE)
1067     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STYLE]);
1068   if (changed_mask & PANGO_FONT_MASK_VARIANT)
1069     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_VARIANT]);
1070   if (changed_mask & PANGO_FONT_MASK_WEIGHT)
1071     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_WEIGHT]);
1072   if (changed_mask & PANGO_FONT_MASK_STRETCH)
1073     g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STRETCH]);
1074   if (changed_mask & PANGO_FONT_MASK_SIZE)
1075     {
1076       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SIZE]);
1077       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SIZE_POINTS]);
1078     }
1079 }
1080 
1081 static void
set_font_description(GtkCellRendererText * celltext,PangoFontDescription * font_desc)1082 set_font_description (GtkCellRendererText  *celltext,
1083                       PangoFontDescription *font_desc)
1084 {
1085   GtkCellRendererTextPrivate *priv = celltext->priv;
1086   GObject *object = G_OBJECT (celltext);
1087   PangoFontDescription *new_font_desc;
1088   PangoFontMask old_mask, new_mask, changed_mask, set_changed_mask;
1089 
1090   if (font_desc)
1091     new_font_desc = pango_font_description_copy (font_desc);
1092   else
1093     new_font_desc = pango_font_description_new ();
1094 
1095   old_mask = pango_font_description_get_set_fields (priv->font);
1096   new_mask = pango_font_description_get_set_fields (new_font_desc);
1097 
1098   changed_mask = old_mask | new_mask;
1099   set_changed_mask = old_mask ^ new_mask;
1100 
1101   pango_font_description_free (priv->font);
1102   priv->font = new_font_desc;
1103 
1104   g_object_freeze_notify (object);
1105 
1106   g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT_DESC]);
1107   g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT]);
1108 
1109   notify_fields_changed (object, changed_mask);
1110   notify_set_changed (object, set_changed_mask);
1111 
1112   g_object_thaw_notify (object);
1113 }
1114 
1115 static void
gtk_cell_renderer_text_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)1116 gtk_cell_renderer_text_set_property (GObject      *object,
1117 				     guint         param_id,
1118 				     const GValue *value,
1119 				     GParamSpec   *pspec)
1120 {
1121   GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object);
1122   GtkCellRendererTextPrivate *priv = celltext->priv;
1123 
1124   switch (param_id)
1125     {
1126     case PROP_TEXT:
1127       g_free (priv->text);
1128 
1129       if (priv->markup_set)
1130         {
1131           if (priv->extra_attrs)
1132             pango_attr_list_unref (priv->extra_attrs);
1133           priv->extra_attrs = NULL;
1134           priv->markup_set = FALSE;
1135         }
1136 
1137       priv->text = g_value_dup_string (value);
1138       g_object_notify_by_pspec (object, pspec);
1139       break;
1140 
1141     case PROP_ATTRIBUTES:
1142       if (priv->extra_attrs)
1143 	pango_attr_list_unref (priv->extra_attrs);
1144 
1145       priv->extra_attrs = g_value_get_boxed (value);
1146       if (priv->extra_attrs)
1147         pango_attr_list_ref (priv->extra_attrs);
1148       break;
1149     case PROP_MARKUP:
1150       {
1151 	const gchar *str;
1152 	gchar *text = NULL;
1153 	GError *error = NULL;
1154 	PangoAttrList *attrs = NULL;
1155 
1156 	str = g_value_get_string (value);
1157 	if (str && !pango_parse_markup (str, -1, 0, &attrs, &text, NULL, &error))
1158 	  {
1159 	    g_warning ("Failed to set text from markup due to error parsing markup: %s",
1160 		       error->message);
1161 	    g_error_free (error);
1162 	    return;
1163 	  }
1164 
1165 	g_free (priv->text);
1166 
1167 	if (priv->extra_attrs)
1168 	  pango_attr_list_unref (priv->extra_attrs);
1169 
1170 	priv->text = text;
1171 	priv->extra_attrs = attrs;
1172         priv->markup_set = TRUE;
1173       }
1174       break;
1175 
1176     case PROP_SINGLE_PARAGRAPH_MODE:
1177       if (priv->single_paragraph != g_value_get_boolean (value))
1178         {
1179           priv->single_paragraph = g_value_get_boolean (value);
1180           g_object_notify_by_pspec (object, pspec);
1181         }
1182       break;
1183 
1184     case PROP_BACKGROUND:
1185       {
1186         GdkRGBA rgba;
1187 
1188         if (!g_value_get_string (value))
1189           set_bg_color (celltext, NULL);       /* reset to background_set to FALSE */
1190         else if (gdk_rgba_parse (&rgba, g_value_get_string (value)))
1191           set_bg_color (celltext, &rgba);
1192         else
1193           g_warning ("Don't know color '%s'", g_value_get_string (value));
1194 
1195         g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_BACKGROUND_GDK]);
1196       }
1197       break;
1198 
1199     case PROP_FOREGROUND:
1200       {
1201         GdkRGBA rgba;
1202 
1203         if (!g_value_get_string (value))
1204           set_fg_color (celltext, NULL);       /* reset to foreground_set to FALSE */
1205         else if (gdk_rgba_parse (&rgba, g_value_get_string (value)))
1206           set_fg_color (celltext, &rgba);
1207         else
1208           g_warning ("Don't know color '%s'", g_value_get_string (value));
1209 
1210         g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FOREGROUND_GDK]);
1211       }
1212       break;
1213 
1214     case PROP_BACKGROUND_GDK:
1215       {
1216         GdkColor *color;
1217 
1218         color = g_value_get_boxed (value);
1219         if (color)
1220           {
1221             GdkRGBA rgba;
1222 
1223             rgba.red = color->red / 65535.;
1224             rgba.green = color->green / 65535.;
1225             rgba.blue = color->blue / 65535.;
1226             rgba.alpha = 1;
1227 
1228             set_bg_color (celltext, &rgba);
1229           }
1230         else
1231           {
1232             set_bg_color (celltext, NULL);
1233           }
1234       }
1235       break;
1236 
1237     case PROP_FOREGROUND_GDK:
1238       {
1239         GdkColor *color;
1240 
1241         color = g_value_get_boxed (value);
1242         if (color)
1243           {
1244             GdkRGBA rgba;
1245 
1246             rgba.red = color->red / 65535.;
1247             rgba.green = color->green / 65535.;
1248             rgba.blue = color->blue / 65535.;
1249             rgba.alpha = 1;
1250 
1251             set_fg_color (celltext, &rgba);
1252           }
1253         else
1254           {
1255             set_fg_color (celltext, NULL);
1256           }
1257       }
1258       break;
1259 
1260     case PROP_BACKGROUND_RGBA:
1261       set_bg_color (celltext, g_value_get_boxed (value));
1262       break;
1263 
1264     case PROP_FOREGROUND_RGBA:
1265       set_fg_color (celltext, g_value_get_boxed (value));
1266       break;
1267 
1268     case PROP_FONT:
1269       {
1270         PangoFontDescription *font_desc = NULL;
1271         const gchar *name;
1272 
1273         name = g_value_get_string (value);
1274 
1275         if (name)
1276           font_desc = pango_font_description_from_string (name);
1277 
1278         set_font_description (celltext, font_desc);
1279 
1280 	pango_font_description_free (font_desc);
1281 
1282 	if (priv->fixed_height_rows != -1)
1283 	  priv->calc_fixed_height = TRUE;
1284       }
1285       break;
1286 
1287     case PROP_FONT_DESC:
1288       set_font_description (celltext, g_value_get_boxed (value));
1289 
1290       if (priv->fixed_height_rows != -1)
1291 	priv->calc_fixed_height = TRUE;
1292       break;
1293 
1294     case PROP_FAMILY:
1295     case PROP_STYLE:
1296     case PROP_VARIANT:
1297     case PROP_WEIGHT:
1298     case PROP_STRETCH:
1299     case PROP_SIZE:
1300     case PROP_SIZE_POINTS:
1301       {
1302 	PangoFontMask old_set_mask = pango_font_description_get_set_fields (priv->font);
1303 
1304 	switch (param_id)
1305 	  {
1306 	  case PROP_FAMILY:
1307 	    pango_font_description_set_family (priv->font,
1308 					       g_value_get_string (value));
1309 	    break;
1310 	  case PROP_STYLE:
1311 	    pango_font_description_set_style (priv->font,
1312 					      g_value_get_enum (value));
1313 	    break;
1314 	  case PROP_VARIANT:
1315 	    pango_font_description_set_variant (priv->font,
1316 						g_value_get_enum (value));
1317 	    break;
1318 	  case PROP_WEIGHT:
1319 	    pango_font_description_set_weight (priv->font,
1320 					       g_value_get_int (value));
1321 	    break;
1322 	  case PROP_STRETCH:
1323 	    pango_font_description_set_stretch (priv->font,
1324 						g_value_get_enum (value));
1325 	    break;
1326 	  case PROP_SIZE:
1327 	    pango_font_description_set_size (priv->font,
1328 					     g_value_get_int (value));
1329 	    g_object_notify_by_pspec (object, pspec);
1330 	    break;
1331 	  case PROP_SIZE_POINTS:
1332 	    pango_font_description_set_size (priv->font,
1333 					     g_value_get_double (value) * PANGO_SCALE);
1334 	    g_object_notify_by_pspec (object, pspec);
1335 	    break;
1336 	  }
1337 
1338 	if (priv->fixed_height_rows != -1)
1339 	  priv->calc_fixed_height = TRUE;
1340 
1341 	notify_set_changed (object, old_set_mask & pango_font_description_get_set_fields (priv->font));
1342 	g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT_DESC]);
1343 	g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_FONT]);
1344 
1345 	break;
1346       }
1347 
1348     case PROP_SCALE:
1349       priv->font_scale = g_value_get_double (value);
1350       priv->scale_set = TRUE;
1351       if (priv->fixed_height_rows != -1)
1352 	priv->calc_fixed_height = TRUE;
1353       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_SCALE_SET]);
1354       break;
1355 
1356     case PROP_EDITABLE:
1357       priv->editable = g_value_get_boolean (value);
1358       priv->editable_set = TRUE;
1359       if (priv->editable)
1360         g_object_set (celltext, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL);
1361       else
1362         g_object_set (celltext, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
1363       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_EDITABLE_SET]);
1364       break;
1365 
1366     case PROP_STRIKETHROUGH:
1367       priv->strikethrough = g_value_get_boolean (value);
1368       priv->strikethrough_set = TRUE;
1369       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_STRIKETHROUGH_SET]);
1370       break;
1371 
1372     case PROP_UNDERLINE:
1373       priv->underline_style = g_value_get_enum (value);
1374       priv->underline_set = TRUE;
1375       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_UNDERLINE_SET]);
1376 
1377       break;
1378 
1379     case PROP_RISE:
1380       priv->rise = g_value_get_int (value);
1381       priv->rise_set = TRUE;
1382       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_RISE_SET]);
1383       if (priv->fixed_height_rows != -1)
1384 	priv->calc_fixed_height = TRUE;
1385       break;
1386 
1387     case PROP_LANGUAGE:
1388       priv->language_set = TRUE;
1389       if (priv->language)
1390         g_object_unref (priv->language);
1391       priv->language = pango_language_from_string (g_value_get_string (value));
1392       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_LANGUAGE_SET]);
1393       break;
1394 
1395     case PROP_ELLIPSIZE:
1396       priv->ellipsize = g_value_get_enum (value);
1397       priv->ellipsize_set = TRUE;
1398       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_ELLIPSIZE_SET]);
1399       break;
1400 
1401     case PROP_WRAP_MODE:
1402       if (priv->wrap_mode != g_value_get_enum (value))
1403         {
1404           priv->wrap_mode = g_value_get_enum (value);
1405           g_object_notify_by_pspec (object, pspec);
1406         }
1407       break;
1408 
1409     case PROP_WRAP_WIDTH:
1410       if (priv->wrap_width != g_value_get_int (value))
1411         {
1412           priv->wrap_width = g_value_get_int (value);
1413           g_object_notify_by_pspec (object, pspec);
1414         }
1415       break;
1416 
1417     case PROP_WIDTH_CHARS:
1418       if (priv->width_chars != g_value_get_int (value))
1419         {
1420           priv->width_chars  = g_value_get_int (value);
1421           g_object_notify_by_pspec (object, pspec);
1422         }
1423       break;
1424 
1425     case PROP_MAX_WIDTH_CHARS:
1426       if (priv->max_width_chars != g_value_get_int (value))
1427         {
1428           priv->max_width_chars  = g_value_get_int (value);
1429           g_object_notify_by_pspec (object, pspec);
1430         }
1431       break;
1432 
1433     case PROP_ALIGN:
1434       if (priv->align != g_value_get_enum (value))
1435         {
1436           priv->align = g_value_get_enum (value);
1437           g_object_notify_by_pspec (object, pspec);
1438         }
1439       priv->align_set = TRUE;
1440       g_object_notify_by_pspec (object, text_cell_renderer_props[PROP_ALIGN_SET]);
1441       break;
1442 
1443     case PROP_BACKGROUND_SET:
1444       priv->background_set = g_value_get_boolean (value);
1445       break;
1446 
1447     case PROP_FOREGROUND_SET:
1448       priv->foreground_set = g_value_get_boolean (value);
1449       break;
1450 
1451     case PROP_FAMILY_SET:
1452     case PROP_STYLE_SET:
1453     case PROP_VARIANT_SET:
1454     case PROP_WEIGHT_SET:
1455     case PROP_STRETCH_SET:
1456     case PROP_SIZE_SET:
1457       if (!g_value_get_boolean (value))
1458 	{
1459 	  pango_font_description_unset_fields (priv->font,
1460 					       get_property_font_set_mask (param_id));
1461 	}
1462       else
1463 	{
1464 	  PangoFontMask changed_mask;
1465 
1466 	  changed_mask = set_font_desc_fields (priv->font,
1467 					       get_property_font_set_mask (param_id));
1468 	  notify_fields_changed (G_OBJECT (celltext), changed_mask);
1469 	}
1470       break;
1471 
1472     case PROP_SCALE_SET:
1473       priv->scale_set = g_value_get_boolean (value);
1474       break;
1475 
1476     case PROP_EDITABLE_SET:
1477       priv->editable_set = g_value_get_boolean (value);
1478       break;
1479 
1480     case PROP_STRIKETHROUGH_SET:
1481       priv->strikethrough_set = g_value_get_boolean (value);
1482       break;
1483 
1484     case PROP_UNDERLINE_SET:
1485       priv->underline_set = g_value_get_boolean (value);
1486       break;
1487 
1488     case PROP_RISE_SET:
1489       priv->rise_set = g_value_get_boolean (value);
1490       break;
1491 
1492     case PROP_LANGUAGE_SET:
1493       priv->language_set = g_value_get_boolean (value);
1494       break;
1495 
1496     case PROP_ELLIPSIZE_SET:
1497       priv->ellipsize_set = g_value_get_boolean (value);
1498       break;
1499 
1500     case PROP_ALIGN_SET:
1501       priv->align_set = g_value_get_boolean (value);
1502       break;
1503 
1504     case PROP_PLACEHOLDER_TEXT:
1505       g_free (priv->placeholder_text);
1506       priv->placeholder_text = g_value_dup_string (value);
1507       break;
1508 
1509     default:
1510       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1511       break;
1512     }
1513 }
1514 
1515 /**
1516  * gtk_cell_renderer_text_new:
1517  *
1518  * Creates a new #GtkCellRendererText. Adjust how text is drawn using
1519  * object properties. Object properties can be
1520  * set globally (with g_object_set()). Also, with #GtkTreeViewColumn,
1521  * you can bind a property to a value in a #GtkTreeModel. For example,
1522  * you can bind the “text” property on the cell renderer to a string
1523  * value in the model, thus rendering a different string in each row
1524  * of the #GtkTreeView
1525  *
1526  * Returns: the new cell renderer
1527  **/
1528 GtkCellRenderer *
gtk_cell_renderer_text_new(void)1529 gtk_cell_renderer_text_new (void)
1530 {
1531   return g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, NULL);
1532 }
1533 
1534 static inline gboolean
show_placeholder_text(GtkCellRendererText * celltext)1535 show_placeholder_text (GtkCellRendererText *celltext)
1536 {
1537   GtkCellRendererTextPrivate *priv = celltext->priv;
1538 
1539   return priv->editable && priv->placeholder_text &&
1540     (!priv->text || !priv->text[0]);
1541 }
1542 
1543 static void
add_attr(PangoAttrList * attr_list,PangoAttribute * attr)1544 add_attr (PangoAttrList  *attr_list,
1545           PangoAttribute *attr)
1546 {
1547   attr->start_index = 0;
1548   attr->end_index = G_MAXINT;
1549 
1550   pango_attr_list_insert (attr_list, attr);
1551 }
1552 
1553 static PangoLayout*
get_layout(GtkCellRendererText * celltext,GtkWidget * widget,const GdkRectangle * cell_area,GtkCellRendererState flags)1554 get_layout (GtkCellRendererText *celltext,
1555             GtkWidget           *widget,
1556             const GdkRectangle  *cell_area,
1557             GtkCellRendererState flags)
1558 {
1559   GtkCellRendererTextPrivate *priv = celltext->priv;
1560   PangoAttrList *attr_list;
1561   PangoLayout *layout;
1562   PangoUnderline uline;
1563   gint xpad;
1564   gboolean placeholder_layout = show_placeholder_text (celltext);
1565 
1566   layout = gtk_widget_create_pango_layout (widget, placeholder_layout ?
1567                                            priv->placeholder_text : priv->text);
1568 
1569   gtk_cell_renderer_get_padding (GTK_CELL_RENDERER (celltext), &xpad, NULL);
1570 
1571   if (priv->extra_attrs)
1572     attr_list = pango_attr_list_copy (priv->extra_attrs);
1573   else
1574     attr_list = pango_attr_list_new ();
1575 
1576   pango_layout_set_single_paragraph_mode (layout, priv->single_paragraph);
1577 
1578   if (!placeholder_layout && cell_area)
1579     {
1580       /* Add options that affect appearance but not size */
1581 
1582       /* note that background doesn't go here, since it affects
1583        * background_area not the PangoLayout area
1584        */
1585 
1586       if (priv->foreground_set
1587 	  && (flags & GTK_CELL_RENDERER_SELECTED) == 0)
1588         {
1589           PangoColor color;
1590           guint16 alpha;
1591 
1592           color.red = CLAMP (priv->foreground.red * 65535. + 0.5, 0, 65535);
1593           color.green = CLAMP (priv->foreground.green * 65535. + 0.5, 0, 65535);
1594           color.blue = CLAMP (priv->foreground.blue * 65535. + 0.5, 0, 65535);
1595           alpha = CLAMP (priv->foreground.alpha * 65535. + 0.5, 0, 65535);
1596 
1597           add_attr (attr_list,
1598                     pango_attr_foreground_new (color.red, color.green, color.blue));
1599 
1600           add_attr (attr_list, pango_attr_foreground_alpha_new (alpha));
1601         }
1602 
1603       if (priv->strikethrough_set)
1604         add_attr (attr_list, pango_attr_strikethrough_new (priv->strikethrough));
1605     }
1606   else if (placeholder_layout)
1607     {
1608       PangoColor color;
1609       guint16 alpha;
1610       GtkStyleContext *context;
1611       GdkRGBA fg = { 0.5, 0.5, 0.5, 1.0 };
1612 
1613       context = gtk_widget_get_style_context (widget);
1614       gtk_style_context_lookup_color (context, "placeholder_text_color", &fg);
1615 
1616       color.red = CLAMP (fg.red * 65535. + 0.5, 0, 65535);
1617       color.green = CLAMP (fg.green * 65535. + 0.5, 0, 65535);
1618       color.blue = CLAMP (fg.blue * 65535. + 0.5, 0, 65535);
1619       alpha = CLAMP (fg.alpha * 65535. + 0.5, 0, 65535);
1620 
1621       add_attr (attr_list,
1622                 pango_attr_foreground_new (color.red, color.green, color.blue));
1623 
1624       add_attr (attr_list, pango_attr_foreground_alpha_new (alpha));
1625     }
1626 
1627   add_attr (attr_list, pango_attr_font_desc_new (priv->font));
1628 
1629   if (priv->scale_set &&
1630       priv->font_scale != 1.0)
1631     add_attr (attr_list, pango_attr_scale_new (priv->font_scale));
1632 
1633   if (priv->underline_set)
1634     uline = priv->underline_style;
1635   else
1636     uline = PANGO_UNDERLINE_NONE;
1637 
1638   if (priv->language_set)
1639     add_attr (attr_list, pango_attr_language_new (priv->language));
1640 
1641   if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT)
1642     {
1643       switch (uline)
1644         {
1645         case PANGO_UNDERLINE_NONE:
1646           uline = PANGO_UNDERLINE_SINGLE;
1647           break;
1648 
1649         case PANGO_UNDERLINE_SINGLE:
1650           uline = PANGO_UNDERLINE_DOUBLE;
1651           break;
1652 
1653         default:
1654           break;
1655         }
1656     }
1657 
1658   if (uline != PANGO_UNDERLINE_NONE)
1659     add_attr (attr_list, pango_attr_underline_new (priv->underline_style));
1660 
1661   if (priv->rise_set)
1662     add_attr (attr_list, pango_attr_rise_new (priv->rise));
1663 
1664   /* Now apply the attributes as they will effect the outcome
1665    * of pango_layout_get_extents() */
1666   pango_layout_set_attributes (layout, attr_list);
1667   pango_attr_list_unref (attr_list);
1668 
1669   if (priv->ellipsize_set)
1670     pango_layout_set_ellipsize (layout, priv->ellipsize);
1671   else
1672     pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
1673 
1674   if (priv->wrap_width != -1)
1675     {
1676       PangoRectangle rect;
1677       gint           width, text_width;
1678 
1679       pango_layout_get_extents (layout, NULL, &rect);
1680       text_width = rect.width;
1681 
1682       if (cell_area)
1683 	width = (cell_area->width - xpad * 2) * PANGO_SCALE;
1684       else
1685 	width = priv->wrap_width * PANGO_SCALE;
1686 
1687       width = MIN (width, text_width);
1688 
1689       pango_layout_set_width (layout, width);
1690       pango_layout_set_wrap (layout, priv->wrap_mode);
1691     }
1692   else
1693     {
1694       pango_layout_set_width (layout, -1);
1695       pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
1696     }
1697 
1698   if (priv->align_set)
1699     pango_layout_set_alignment (layout, priv->align);
1700   else
1701     {
1702       PangoAlignment align;
1703 
1704       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1705 	align = PANGO_ALIGN_RIGHT;
1706       else
1707 	align = PANGO_ALIGN_LEFT;
1708 
1709       pango_layout_set_alignment (layout, align);
1710     }
1711 
1712   return layout;
1713 }
1714 
1715 
1716 static void
get_size(GtkCellRenderer * cell,GtkWidget * widget,const GdkRectangle * cell_area,PangoLayout * layout,gint * x_offset,gint * y_offset,gint * width,gint * height)1717 get_size (GtkCellRenderer    *cell,
1718 	  GtkWidget          *widget,
1719 	  const GdkRectangle *cell_area,
1720 	  PangoLayout        *layout,
1721 	  gint               *x_offset,
1722 	  gint               *y_offset,
1723 	  gint               *width,
1724 	  gint               *height)
1725 {
1726   GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell);
1727   GtkCellRendererTextPrivate *priv = celltext->priv;
1728   PangoRectangle rect;
1729   gint xpad, ypad;
1730   gint cell_width, cell_height;
1731 
1732   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
1733 
1734   if (priv->calc_fixed_height)
1735     {
1736       GtkStyleContext *style_context;
1737       GtkStateFlags state;
1738       PangoContext *context;
1739       PangoFontMetrics *metrics;
1740       PangoFontDescription *font_desc;
1741       gint row_height;
1742 
1743       style_context = gtk_widget_get_style_context (widget);
1744       state = gtk_widget_get_state_flags (widget);
1745 
1746       gtk_style_context_get (style_context, state, "font", &font_desc, NULL);
1747       pango_font_description_merge_static (font_desc, priv->font, TRUE);
1748 
1749       if (priv->scale_set)
1750 	pango_font_description_set_size (font_desc,
1751 					 priv->font_scale * pango_font_description_get_size (font_desc));
1752 
1753       context = gtk_widget_get_pango_context (widget);
1754 
1755       metrics = pango_context_get_metrics (context,
1756 					   font_desc,
1757 					   pango_context_get_language (context));
1758       row_height = (pango_font_metrics_get_ascent (metrics) +
1759 		    pango_font_metrics_get_descent (metrics));
1760       pango_font_metrics_unref (metrics);
1761 
1762       pango_font_description_free (font_desc);
1763 
1764       gtk_cell_renderer_get_fixed_size (cell, &cell_width, &cell_height);
1765 
1766       gtk_cell_renderer_set_fixed_size (cell,
1767 					cell_width, 2 * ypad +
1768 					priv->fixed_height_rows * PANGO_PIXELS (row_height));
1769 
1770       if (height)
1771 	{
1772 	  *height = cell_height;
1773 	  height = NULL;
1774 	}
1775       priv->calc_fixed_height = FALSE;
1776       if (width == NULL)
1777 	return;
1778     }
1779 
1780   if (layout)
1781     g_object_ref (layout);
1782   else
1783     layout = get_layout (celltext, widget, NULL, 0);
1784 
1785   pango_layout_get_pixel_extents (layout, NULL, &rect);
1786 
1787   if (cell_area)
1788     {
1789       gfloat xalign, yalign;
1790 
1791       gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
1792 
1793       rect.height = MIN (rect.height, cell_area->height - 2 * ypad);
1794       rect.width  = MIN (rect.width, cell_area->width - 2 * xpad);
1795 
1796       if (x_offset)
1797 	{
1798 	  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1799 	    *x_offset = (1.0 - xalign) * (cell_area->width - (rect.width + (2 * xpad)));
1800 	  else
1801 	    *x_offset = xalign * (cell_area->width - (rect.width + (2 * xpad)));
1802 
1803 	  if ((priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) || priv->wrap_width != -1)
1804 	    *x_offset = MAX(*x_offset, 0);
1805 	}
1806       if (y_offset)
1807 	{
1808 	  *y_offset = yalign * (cell_area->height - (rect.height + (2 * ypad)));
1809 	  *y_offset = MAX (*y_offset, 0);
1810 	}
1811     }
1812   else
1813     {
1814       if (x_offset) *x_offset = 0;
1815       if (y_offset) *y_offset = 0;
1816     }
1817 
1818   if (height)
1819     *height = ypad * 2 + rect.height;
1820 
1821   if (width)
1822     *width = xpad * 2 + rect.width;
1823 
1824   g_object_unref (layout);
1825 }
1826 
1827 static void
gtk_cell_renderer_text_render(GtkCellRenderer * cell,cairo_t * cr,GtkWidget * widget,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)1828 gtk_cell_renderer_text_render (GtkCellRenderer      *cell,
1829 			       cairo_t              *cr,
1830 			       GtkWidget            *widget,
1831 			       const GdkRectangle   *background_area,
1832 			       const GdkRectangle   *cell_area,
1833 			       GtkCellRendererState  flags)
1834 
1835 {
1836   GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell);
1837   GtkCellRendererTextPrivate *priv = celltext->priv;
1838   GtkStyleContext *context;
1839   PangoLayout *layout;
1840   gint x_offset = 0;
1841   gint y_offset = 0;
1842   gint xpad, ypad;
1843   PangoRectangle rect;
1844 
1845   layout = get_layout (celltext, widget, cell_area, flags);
1846   get_size (cell, widget, cell_area, layout, &x_offset, &y_offset, NULL, NULL);
1847   context = gtk_widget_get_style_context (widget);
1848 
1849   if (priv->background_set && (flags & GTK_CELL_RENDERER_SELECTED) == 0)
1850     {
1851       gdk_cairo_rectangle (cr, background_area);
1852       gdk_cairo_set_source_rgba (cr, &priv->background);
1853       cairo_fill (cr);
1854     }
1855 
1856   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
1857 
1858   if (priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE)
1859     pango_layout_set_width (layout,
1860 			    (cell_area->width - x_offset - 2 * xpad) * PANGO_SCALE);
1861   else if (priv->wrap_width == -1)
1862     pango_layout_set_width (layout, -1);
1863 
1864   pango_layout_get_pixel_extents (layout, NULL, &rect);
1865   x_offset = x_offset - rect.x;
1866 
1867   cairo_save (cr);
1868 
1869   gdk_cairo_rectangle (cr, cell_area);
1870   cairo_clip (cr);
1871 
1872   gtk_render_layout (context, cr,
1873                      cell_area->x + x_offset + xpad,
1874                      cell_area->y + y_offset + ypad,
1875                      layout);
1876 
1877   cairo_restore (cr);
1878 
1879   g_object_unref (layout);
1880 }
1881 
1882 static void
gtk_cell_renderer_text_editing_done(GtkCellEditable * entry,gpointer data)1883 gtk_cell_renderer_text_editing_done (GtkCellEditable *entry,
1884 				     gpointer         data)
1885 {
1886   GtkCellRendererTextPrivate *priv;
1887   const gchar *path;
1888   const gchar *new_text;
1889   gboolean canceled;
1890 
1891   priv = GTK_CELL_RENDERER_TEXT (data)->priv;
1892 
1893   g_clear_object (&priv->entry);
1894 
1895   if (priv->focus_out_id > 0)
1896     {
1897       g_signal_handler_disconnect (entry, priv->focus_out_id);
1898       priv->focus_out_id = 0;
1899     }
1900 
1901   if (priv->populate_popup_id > 0)
1902     {
1903       g_signal_handler_disconnect (entry, priv->populate_popup_id);
1904       priv->populate_popup_id = 0;
1905     }
1906 
1907   if (priv->entry_menu_popdown_timeout)
1908     {
1909       g_source_remove (priv->entry_menu_popdown_timeout);
1910       priv->entry_menu_popdown_timeout = 0;
1911     }
1912 
1913   g_object_get (entry,
1914                 "editing-canceled", &canceled,
1915                 NULL);
1916   gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled);
1917 
1918   if (canceled)
1919     return;
1920 
1921   path = g_object_get_data (G_OBJECT (entry), GTK_CELL_RENDERER_TEXT_PATH);
1922   new_text = gtk_entry_get_text (GTK_ENTRY (entry));
1923 
1924   g_signal_emit (data, text_cell_renderer_signals[EDITED], 0, path, new_text);
1925 }
1926 
1927 static gboolean
popdown_timeout(gpointer data)1928 popdown_timeout (gpointer data)
1929 {
1930   GtkCellRendererTextPrivate *priv;
1931 
1932   priv = GTK_CELL_RENDERER_TEXT (data)->priv;
1933 
1934   priv->entry_menu_popdown_timeout = 0;
1935 
1936   if (!gtk_widget_has_focus (priv->entry))
1937     gtk_cell_renderer_text_editing_done (GTK_CELL_EDITABLE (priv->entry), data);
1938 
1939   return FALSE;
1940 }
1941 
1942 static void
gtk_cell_renderer_text_popup_unmap(GtkMenu * menu,gpointer data)1943 gtk_cell_renderer_text_popup_unmap (GtkMenu *menu,
1944                                     gpointer data)
1945 {
1946   GtkCellRendererTextPrivate *priv;
1947 
1948   priv = GTK_CELL_RENDERER_TEXT (data)->priv;
1949 
1950   priv->in_entry_menu = FALSE;
1951 
1952   if (priv->entry_menu_popdown_timeout)
1953     return;
1954 
1955   priv->entry_menu_popdown_timeout = gdk_threads_add_timeout (500, popdown_timeout,
1956                                                     data);
1957   g_source_set_name_by_id (priv->entry_menu_popdown_timeout, "[gtk+] popdown_timeout");
1958 }
1959 
1960 static void
gtk_cell_renderer_text_populate_popup(GtkEntry * entry,GtkMenu * menu,gpointer data)1961 gtk_cell_renderer_text_populate_popup (GtkEntry *entry,
1962                                        GtkMenu  *menu,
1963                                        gpointer  data)
1964 {
1965   GtkCellRendererTextPrivate *priv;
1966 
1967   priv = GTK_CELL_RENDERER_TEXT (data)->priv;
1968 
1969   if (priv->entry_menu_popdown_timeout)
1970     {
1971       g_source_remove (priv->entry_menu_popdown_timeout);
1972       priv->entry_menu_popdown_timeout = 0;
1973     }
1974 
1975   priv->in_entry_menu = TRUE;
1976 
1977   g_signal_connect (menu, "unmap",
1978                     G_CALLBACK (gtk_cell_renderer_text_popup_unmap), data);
1979 }
1980 
1981 static gboolean
gtk_cell_renderer_text_focus_out_event(GtkWidget * entry,GdkEvent * event,gpointer data)1982 gtk_cell_renderer_text_focus_out_event (GtkWidget *entry,
1983 		                        GdkEvent  *event,
1984 					gpointer   data)
1985 {
1986   GtkCellRendererTextPrivate *priv;
1987 
1988   priv = GTK_CELL_RENDERER_TEXT (data)->priv;
1989 
1990   if (priv->in_entry_menu)
1991     return FALSE;
1992 
1993   g_object_set (entry,
1994                 "editing-canceled", TRUE,
1995                 NULL);
1996   gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry));
1997   gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry));
1998 
1999   /* entry needs focus-out-event */
2000   return FALSE;
2001 }
2002 
2003 static GtkCellEditable *
gtk_cell_renderer_text_start_editing(GtkCellRenderer * cell,GdkEvent * event,GtkWidget * widget,const gchar * path,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)2004 gtk_cell_renderer_text_start_editing (GtkCellRenderer      *cell,
2005 				      GdkEvent             *event,
2006 				      GtkWidget            *widget,
2007 				      const gchar          *path,
2008 				      const GdkRectangle   *background_area,
2009 				      const GdkRectangle   *cell_area,
2010 				      GtkCellRendererState  flags)
2011 {
2012   GtkCellRendererText *celltext;
2013   GtkCellRendererTextPrivate *priv;
2014   gfloat xalign, yalign;
2015 
2016   celltext = GTK_CELL_RENDERER_TEXT (cell);
2017   priv = celltext->priv;
2018 
2019   /* If the cell isn't editable we return NULL. */
2020   if (priv->editable == FALSE)
2021     return NULL;
2022 
2023   gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
2024 
2025   priv->entry = gtk_entry_new ();
2026   g_object_ref_sink (G_OBJECT (priv->entry));
2027 
2028   gtk_entry_set_has_frame (GTK_ENTRY (priv->entry), FALSE);
2029   gtk_entry_set_alignment (GTK_ENTRY (priv->entry), xalign);
2030   gtk_entry_set_width_chars (GTK_ENTRY (priv->entry), 5);
2031 
2032   if (priv->text)
2033     gtk_entry_set_text (GTK_ENTRY (priv->entry), priv->text);
2034   g_object_set_data_full (G_OBJECT (priv->entry), I_(GTK_CELL_RENDERER_TEXT_PATH), g_strdup (path), g_free);
2035 
2036   gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1);
2037 
2038   priv->in_entry_menu = FALSE;
2039   if (priv->entry_menu_popdown_timeout)
2040     {
2041       g_source_remove (priv->entry_menu_popdown_timeout);
2042       priv->entry_menu_popdown_timeout = 0;
2043     }
2044 
2045   g_signal_connect (priv->entry,
2046 		    "editing-done",
2047 		    G_CALLBACK (gtk_cell_renderer_text_editing_done),
2048 		    celltext);
2049   priv->focus_out_id = g_signal_connect_after (priv->entry, "focus-out-event",
2050 					       G_CALLBACK (gtk_cell_renderer_text_focus_out_event),
2051 					       celltext);
2052   priv->populate_popup_id =
2053     g_signal_connect (priv->entry, "populate-popup",
2054                       G_CALLBACK (gtk_cell_renderer_text_populate_popup),
2055                       celltext);
2056 
2057   gtk_widget_show (priv->entry);
2058 
2059   return GTK_CELL_EDITABLE (priv->entry);
2060 }
2061 
2062 /**
2063  * gtk_cell_renderer_text_set_fixed_height_from_font:
2064  * @renderer: A #GtkCellRendererText
2065  * @number_of_rows: Number of rows of text each cell renderer is allocated, or -1
2066  *
2067  * Sets the height of a renderer to explicitly be determined by the “font” and
2068  * “y_pad” property set on it.  Further changes in these properties do not
2069  * affect the height, so they must be accompanied by a subsequent call to this
2070  * function.  Using this function is unflexible, and should really only be used
2071  * if calculating the size of a cell is too slow (ie, a massive number of cells
2072  * displayed).  If @number_of_rows is -1, then the fixed height is unset, and
2073  * the height is determined by the properties again.
2074  **/
2075 void
gtk_cell_renderer_text_set_fixed_height_from_font(GtkCellRendererText * renderer,gint number_of_rows)2076 gtk_cell_renderer_text_set_fixed_height_from_font (GtkCellRendererText *renderer,
2077 						   gint                 number_of_rows)
2078 {
2079   GtkCellRendererTextPrivate *priv;
2080   GtkCellRenderer *cell;
2081 
2082   g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (renderer));
2083   g_return_if_fail (number_of_rows == -1 || number_of_rows > 0);
2084 
2085   cell = GTK_CELL_RENDERER (renderer);
2086   priv = renderer->priv;
2087 
2088   if (number_of_rows == -1)
2089     {
2090       gint width, height;
2091 
2092       gtk_cell_renderer_get_fixed_size (cell, &width, &height);
2093       gtk_cell_renderer_set_fixed_size (cell, width, -1);
2094     }
2095   else
2096     {
2097       priv->fixed_height_rows = number_of_rows;
2098       priv->calc_fixed_height = TRUE;
2099     }
2100 }
2101 
2102 static void
gtk_cell_renderer_text_get_preferred_width(GtkCellRenderer * cell,GtkWidget * widget,gint * minimum_size,gint * natural_size)2103 gtk_cell_renderer_text_get_preferred_width (GtkCellRenderer *cell,
2104                                             GtkWidget       *widget,
2105                                             gint            *minimum_size,
2106                                             gint            *natural_size)
2107 {
2108   GtkCellRendererTextPrivate *priv;
2109   GtkCellRendererText        *celltext;
2110   PangoLayout                *layout;
2111   PangoContext               *context;
2112   PangoFontMetrics           *metrics;
2113   PangoRectangle              rect;
2114   gint char_width, text_width, ellipsize_chars, xpad;
2115   gint min_width, nat_width;
2116 
2117   /* "width-chars" Hard-coded minimum width:
2118    *    - minimum size should be MAX (width-chars, strlen ("..."));
2119    *    - natural size should be MAX (width-chars, strlen (label->text));
2120    *
2121    * "wrap-width" User specified natural wrap width
2122    *    - minimum size should be MAX (width-chars, 0)
2123    *    - natural size should be MIN (wrap-width, strlen (label->text))
2124    */
2125 
2126   celltext = GTK_CELL_RENDERER_TEXT (cell);
2127   priv = celltext->priv;
2128 
2129   gtk_cell_renderer_get_padding (cell, &xpad, NULL);
2130 
2131   layout = get_layout (celltext, widget, NULL, 0);
2132 
2133   /* Fetch the length of the complete unwrapped text */
2134   pango_layout_set_width (layout, -1);
2135   pango_layout_get_extents (layout, NULL, &rect);
2136   text_width = rect.width;
2137 
2138   /* Fetch the average size of a charachter */
2139   context = pango_layout_get_context (layout);
2140   metrics = pango_context_get_metrics (context,
2141                                        pango_context_get_font_description (context),
2142                                        pango_context_get_language (context));
2143 
2144   char_width = pango_font_metrics_get_approximate_char_width (metrics);
2145 
2146   pango_font_metrics_unref (metrics);
2147   g_object_unref (layout);
2148 
2149   /* enforce minimum width for ellipsized labels at ~3 chars */
2150   if (priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE)
2151     ellipsize_chars = 3;
2152   else
2153     ellipsize_chars = 0;
2154 
2155   if ((priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) || priv->width_chars > 0)
2156     min_width = xpad * 2 +
2157       MIN (PANGO_PIXELS_CEIL (text_width),
2158            (PANGO_PIXELS (char_width) * MAX (priv->width_chars, ellipsize_chars)));
2159   /* If no width-chars set, minimum for wrapping text will be the wrap-width */
2160   else if (priv->wrap_width > -1)
2161     min_width = xpad * 2 + rect.x + MIN (PANGO_PIXELS_CEIL (text_width), priv->wrap_width);
2162   else
2163     min_width = xpad * 2 + rect.x + PANGO_PIXELS_CEIL (text_width);
2164 
2165   if (priv->width_chars > 0)
2166     nat_width = xpad * 2 +
2167       MAX ((PANGO_PIXELS (char_width) * priv->width_chars), PANGO_PIXELS_CEIL (text_width));
2168   else
2169     nat_width = xpad * 2 + PANGO_PIXELS_CEIL (text_width);
2170 
2171   nat_width = MAX (nat_width, min_width);
2172 
2173   if (priv->max_width_chars > 0)
2174     {
2175       gint max_width = xpad * 2 + PANGO_PIXELS (char_width) * priv->max_width_chars;
2176 
2177       min_width = MIN (min_width, max_width);
2178       nat_width = MIN (nat_width, max_width);
2179     }
2180 
2181   if (minimum_size)
2182     *minimum_size = min_width;
2183 
2184   if (natural_size)
2185     *natural_size = nat_width;
2186 }
2187 
2188 static void
gtk_cell_renderer_text_get_preferred_height_for_width(GtkCellRenderer * cell,GtkWidget * widget,gint width,gint * minimum_height,gint * natural_height)2189 gtk_cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer *cell,
2190                                                        GtkWidget       *widget,
2191                                                        gint             width,
2192                                                        gint            *minimum_height,
2193                                                        gint            *natural_height)
2194 {
2195   GtkCellRendererText *celltext;
2196   PangoLayout         *layout;
2197   gint                 text_height, xpad, ypad;
2198 
2199 
2200   celltext = GTK_CELL_RENDERER_TEXT (cell);
2201 
2202   gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
2203 
2204   layout = get_layout (celltext, widget, NULL, 0);
2205 
2206   pango_layout_set_width (layout, (width - xpad * 2) * PANGO_SCALE);
2207   pango_layout_get_pixel_size (layout, NULL, &text_height);
2208 
2209   if (minimum_height)
2210     *minimum_height = text_height + ypad * 2;
2211 
2212   if (natural_height)
2213     *natural_height = text_height + ypad * 2;
2214 
2215   g_object_unref (layout);
2216 }
2217 
2218 static void
gtk_cell_renderer_text_get_preferred_height(GtkCellRenderer * cell,GtkWidget * widget,gint * minimum_size,gint * natural_size)2219 gtk_cell_renderer_text_get_preferred_height (GtkCellRenderer *cell,
2220                                              GtkWidget       *widget,
2221                                              gint            *minimum_size,
2222                                              gint            *natural_size)
2223 {
2224   gint min_width;
2225 
2226   /* Thankfully cell renderers dont rotate, so they only have to do
2227    * height-for-width and not the opposite. Here we have only to return
2228    * the height for the base minimum width of the renderer.
2229    *
2230    * Note this code path wont be followed by GtkTreeView which is
2231    * height-for-width specifically.
2232    */
2233   gtk_cell_renderer_get_preferred_width (cell, widget, &min_width, NULL);
2234   gtk_cell_renderer_text_get_preferred_height_for_width (cell, widget, min_width,
2235                                                          minimum_size, natural_size);
2236 }
2237 
2238 static void
gtk_cell_renderer_text_get_aligned_area(GtkCellRenderer * cell,GtkWidget * widget,GtkCellRendererState flags,const GdkRectangle * cell_area,GdkRectangle * aligned_area)2239 gtk_cell_renderer_text_get_aligned_area (GtkCellRenderer       *cell,
2240 					 GtkWidget             *widget,
2241 					 GtkCellRendererState   flags,
2242 					 const GdkRectangle    *cell_area,
2243 					 GdkRectangle          *aligned_area)
2244 {
2245   GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell);
2246   PangoLayout *layout;
2247   gint x_offset = 0;
2248   gint y_offset = 0;
2249 
2250   layout = get_layout (celltext, widget, cell_area, flags);
2251   get_size (cell, widget, cell_area, layout, &x_offset, &y_offset,
2252 	    &aligned_area->width, &aligned_area->height);
2253 
2254   aligned_area->x = cell_area->x + x_offset;
2255   aligned_area->y = cell_area->y + y_offset;
2256 
2257   g_object_unref (layout);
2258 }
2259