]> Pileus Git - ~andy/gtk/blob - gtk/gtkfontchooser.c
Simplify a few things
[~andy/gtk] / gtk / gtkfontchooser.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2011 Alberto Ruiz <aruiz@gnome.org>
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include <stdlib.h>
23 #include <glib/gprintf.h>
24 #include <string.h>
25
26 #include <atk/atk.h>
27
28 #include "gtkfontchooser.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkentry.h"
31 #include "gtkframe.h"
32 #include "gtkhbbox.h"
33 #include "gtkhbox.h"
34 #include "gtklabel.h"
35 #include "gtkliststore.h"
36 #include "gtkstock.h"
37 #include "gtktextview.h"
38 #include "gtktreeselection.h"
39 #include "gtktreeview.h"
40 #include "gtkbox.h"
41 #include "gtkscrolledwindow.h"
42 #include "gtkintl.h"
43 #include "gtkaccessible.h"
44 #include "gtkbuildable.h"
45 #include "gtkprivate.h"
46 #include "gtkalignment.h"
47 #include "gtkscale.h"
48 #include "gtkbox.h"
49 #include "gtkspinbutton.h"
50 #include "gtkwidget.h"
51 #include "gtkgrid.h"
52
53 /**
54  * SECTION:gtkfontchooser
55  * @Short_description: A widget for selecting fonts
56  * @Title: GtkFontChooser
57  * @See_also: #GtkFontChooserDialog
58  *
59  * The #GtkFontChooser widget lists the available fonts, styles and sizes,
60  * allowing the user to select a font.
61  * It is used in the #GtkFontChooserDialog widget to provide a dialog box for
62  * selecting fonts.
63  *
64  * To set the font which is initially selected, use
65  * gtk_font_chooser_set_font_name().
66  *
67  * To get the selected font use gtk_font_chooser_get_font_name().
68  *
69  * To change the text which is shown in the preview area, use
70  * gtk_font_chooser_set_preview_text().
71  *
72  * Since: 3.2
73  */
74
75
76 struct _GtkFontChooserPrivate
77 {
78   GtkWidget    *search_entry;
79   GtkWidget    *family_face_list;
80   GtkWidget    *list_scrolled_window;
81   GtkWidget    *empty_list;
82   GtkListStore *model;
83   GtkTreeModel *filter;
84
85   GtkWidget       *preview;
86   GtkWidget       *preview_scrolled_window;
87   gchar           *preview_text;
88   gboolean         show_preview_entry;
89
90   GtkWidget *size_spin;
91   GtkWidget *size_slider;
92
93   gint             size;
94   PangoFontFace   *face;
95   PangoFontFamily *family;
96 };
97
98 #define DEFAULT_FONT_NAME "Sans 10"
99 #define MAX_FONT_SIZE 999
100
101 /* This is the initial fixed height and the top padding of the preview entry */
102 #define PREVIEW_HEIGHT 72
103 #define PREVIEW_TOP_PADDING 6
104
105 /* These are the sizes of the font, style & size lists. */
106 #define FONT_LIST_HEIGHT  136
107 #define FONT_LIST_WIDTH   190
108 #define FONT_STYLE_LIST_WIDTH 170
109 #define FONT_SIZE_LIST_WIDTH  60
110
111 #define ROW_FORMAT_STRING "<span weight=\"bold\" size=\"small\">%s</span>\n<span size=\"x-large\" font_desc=\"%s\">%s</span>"
112
113 #define NO_FONT_MATCHED_SEARCH "No fonts matched your search. You can revise your search and try again."
114
115 /* These are what we use as the standard font sizes, for the size list.
116  */
117 static const gint font_sizes[] = {
118   6, 8, 9, 10, 11, 12, 13, 14, 16, 20, 24, 36, 48, 72
119 };
120
121 enum {
122    PROP_0,
123    PROP_FONT_NAME,
124    PROP_PREVIEW_TEXT,
125    PROP_SHOW_PREVIEW_ENTRY
126 };
127
128
129 enum {
130   FAMILY_COLUMN,
131   FACE_COLUMN,
132   PREVIEW_TEXT_COLUMN,
133   PREVIEW_TITLE_COLUMN
134 };
135
136 static void  gtk_font_chooser_set_property       (GObject         *object,
137                                                   guint            prop_id,
138                                                   const GValue    *value,
139                                                   GParamSpec      *pspec);
140 static void  gtk_font_chooser_get_property       (GObject         *object,
141                                                   guint            prop_id,
142                                                   GValue          *value,
143                                                   GParamSpec      *pspec);
144 static void  gtk_font_chooser_finalize           (GObject         *object);
145
146 static void  gtk_font_chooser_screen_changed     (GtkWidget       *widget,
147                                                   GdkScreen       *previous_screen);
148 static void  gtk_font_chooser_style_updated      (GtkWidget      *widget);
149
150 static void  gtk_font_chooser_ref_family         (GtkFontChooser *fontchooser,
151                                                   PangoFontFamily  *family);
152 static void  gtk_font_chooser_ref_face           (GtkFontChooser *fontchooser,
153                                                   PangoFontFace    *face);
154
155 static void gtk_font_chooser_bootstrap_fontlist (GtkFontChooser *fontchooser);
156
157 G_DEFINE_TYPE (GtkFontChooser, gtk_font_chooser, GTK_TYPE_BOX)
158
159 static void
160 gtk_font_chooser_class_init (GtkFontChooserClass *klass)
161 {
162   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
163   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
164
165   widget_class->screen_changed = gtk_font_chooser_screen_changed;
166   widget_class->style_updated = gtk_font_chooser_style_updated;
167
168   gobject_class->finalize = gtk_font_chooser_finalize;
169   gobject_class->set_property = gtk_font_chooser_set_property;
170   gobject_class->get_property = gtk_font_chooser_get_property;
171
172   g_object_class_install_property (gobject_class,
173                                    PROP_FONT_NAME,
174                                    g_param_spec_string ("font-name",
175                                                         P_("Font name"),
176                                                         P_("The string that represents this font"),
177                                                         DEFAULT_FONT_NAME,
178                                                         GTK_PARAM_READWRITE));
179   g_object_class_install_property (gobject_class,
180                                    PROP_PREVIEW_TEXT,
181                                    g_param_spec_string ("preview-text",
182                                                         P_("Preview text"),
183                                                         P_("The text to display in order to demonstrate the selected font"),
184                                                         pango_language_get_sample_string (NULL),
185                                                         GTK_PARAM_READWRITE));
186
187   g_object_class_install_property (gobject_class,
188                                    PROP_SHOW_PREVIEW_ENTRY,
189                                    g_param_spec_boolean ("show-preview-entry",
190                                                         P_("Show preview text entry"),
191                                                         P_("Whether the preview text entry is shown or not"),
192                                                         TRUE,
193                                                         GTK_PARAM_READWRITE));
194
195   g_type_class_add_private (klass, sizeof (GtkFontChooserPrivate));
196 }
197
198 static void
199 gtk_font_chooser_set_property (GObject         *object,
200                                guint            prop_id,
201                                const GValue    *value,
202                                GParamSpec      *pspec)
203 {
204   GtkFontChooser *fontchooser;
205
206   fontchooser = GTK_FONT_CHOOSER (object);
207
208   switch (prop_id)
209     {
210     case PROP_FONT_NAME:
211       gtk_font_chooser_set_font_name (fontchooser, g_value_get_string (value));
212       break;
213     case PROP_PREVIEW_TEXT:
214       gtk_font_chooser_set_preview_text (fontchooser, g_value_get_string (value));
215       break;
216     case PROP_SHOW_PREVIEW_ENTRY:
217       gtk_font_chooser_set_show_preview_entry (fontchooser, g_value_get_boolean (value));
218       break;
219     default:
220       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
221       break;
222     }
223 }
224
225 static void
226 gtk_font_chooser_get_property (GObject         *object,
227                                guint            prop_id,
228                                GValue          *value,
229                                GParamSpec      *pspec)
230 {
231   GtkFontChooser *fontchooser;
232
233   fontchooser = GTK_FONT_CHOOSER (object);
234
235   switch (prop_id)
236     {
237     case PROP_FONT_NAME:
238       g_value_take_string (value, gtk_font_chooser_get_font_name (fontchooser));
239       break;
240     case PROP_PREVIEW_TEXT:
241       g_value_set_string (value, gtk_font_chooser_get_preview_text (fontchooser));
242       break;
243     case PROP_SHOW_PREVIEW_ENTRY:
244       g_value_set_boolean (value, gtk_font_chooser_get_show_preview_entry (fontchooser));
245       break;
246     default:
247       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
248       break;
249     }
250 }
251
252 static void
253 text_changed_cb (GtkEntry       *entry,
254                  GParamSpec     *pspec,
255                  GtkFontChooser *fc)
256 {
257   GtkFontChooserPrivate *priv = fc->priv;
258   const gchar *text;
259
260   text = gtk_entry_get_text (entry);
261
262   if (text == NULL || text[0] == '\0')
263     {
264       GIcon *icon;
265
266       icon = g_themed_icon_new_with_default_fallbacks ("edit-find-symbolic");
267       g_object_set (G_OBJECT (priv->search_entry),
268                     "secondary-icon-gicon", icon,
269                     "secondary-icon-activatable", FALSE,
270                     "secondary-icon-sensitive", FALSE,
271                     NULL);
272       g_object_unref (icon);
273     }
274   else
275     {
276       if (!gtk_entry_get_icon_activatable (GTK_ENTRY (priv->search_entry), GTK_ENTRY_ICON_SECONDARY))
277         {
278           GIcon *icon;
279
280           icon = g_themed_icon_new_with_default_fallbacks ("edit-clear-symbolic");
281           g_object_set (G_OBJECT (priv->search_entry),
282                         "secondary-icon-gicon", icon,
283                         "secondary-icon-activatable", TRUE,
284                         "secondary-icon-sensitive", TRUE,
285                         NULL);
286           g_object_unref (icon);
287         }
288     }
289
290   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
291 }
292
293 static void
294 icon_press_cb (GtkEntry             *entry,
295                GtkEntryIconPosition  pos,
296                GdkEvent             *event,
297                gpointer              user_data)
298 {
299   gtk_entry_set_text (entry, "");
300 }
301
302 static void
303 slider_change_cb (GtkAdjustment *adjustment,
304                   gpointer       user_data)
305 {
306   GtkFontChooser        *fc    = (GtkFontChooser*)user_data;
307   GtkFontChooserPrivate *priv  = fc->priv;
308   GtkAdjustment         *spin_adj     = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON (priv->size_spin));
309   gdouble                slider_value = gtk_adjustment_get_value (adjustment);
310   gdouble                spin_value   = gtk_adjustment_get_value (spin_adj);
311
312   if (slider_value != spin_value)
313     gtk_adjustment_set_value (spin_adj,
314                               gtk_adjustment_get_value (adjustment));
315 }
316
317 static void
318 spin_change_cb (GtkAdjustment *adjustment,
319                 gpointer       user_data)
320 {
321   PangoFontDescription    *desc;
322   GtkFontChooser          *fontchooser = (GtkFontChooser*)user_data;
323   GtkFontChooserPrivate   *priv        = fontchooser->priv;
324   GtkAdjustment           *slider_adj  = gtk_range_get_adjustment (GTK_RANGE (priv->size_slider));
325
326   gdouble size = gtk_adjustment_get_value (adjustment);
327   priv->size = ((gint)size) * PANGO_SCALE;
328
329   desc = pango_context_get_font_description (gtk_widget_get_pango_context (priv->preview));
330   pango_font_description_set_size (desc, priv->size);
331   gtk_widget_override_font (priv->preview, desc);
332
333   g_object_notify (G_OBJECT (fontchooser), "font-name");
334
335   /* If the new value is lower than the lower bound of the slider, we set
336    * the slider adjustment to the lower bound value if it is not already set
337    */
338   if (size < gtk_adjustment_get_lower (slider_adj) &&
339       gtk_adjustment_get_value (slider_adj) != gtk_adjustment_get_lower (slider_adj))
340     gtk_adjustment_set_value (slider_adj, gtk_adjustment_get_lower (slider_adj));
341
342   /* If the new value is upper than the upper bound of the slider, we set
343    * the slider adjustment to the upper bound value if it is not already set
344    */
345   else if (size > gtk_adjustment_get_upper (slider_adj) &&
346            gtk_adjustment_get_value (slider_adj) != gtk_adjustment_get_upper (slider_adj))
347     gtk_adjustment_set_value (slider_adj, gtk_adjustment_get_upper (slider_adj));
348
349   /* If the new value is not already set on the slider we set it */
350   else if (size != gtk_adjustment_get_value (slider_adj))
351     gtk_adjustment_set_value (slider_adj, size);
352
353   gtk_widget_queue_draw (priv->preview);
354 }
355
356 static void
357 set_range_marks (GtkFontChooserPrivate *priv,
358                  GtkWidget             *size_slider,
359                  gint                  *sizes,
360                  gint                   length)
361 {
362   GtkAdjustment *adj;
363   gint i;
364   gdouble value;
365
366   if (length < 2)
367     {
368       sizes = (gint*)font_sizes;
369       length = G_N_ELEMENTS (font_sizes);
370     }
371
372   gtk_scale_clear_marks (GTK_SCALE (size_slider));
373
374   adj = gtk_range_get_adjustment(GTK_RANGE (size_slider));
375
376   gtk_adjustment_set_lower (adj, (gdouble) sizes[0]);
377   gtk_adjustment_set_upper (adj, (gdouble) sizes[length-1]);
378
379   value = gtk_adjustment_get_value (adj);
380   if (value > (gdouble) sizes[length-1])
381     gtk_adjustment_set_value (adj, (gdouble) sizes[length-1]);
382   else if (value < (gdouble) sizes[0])
383     gtk_adjustment_set_value (adj, (gdouble) sizes[0]);
384
385   for (i = 0; i < length; i++)
386     gtk_scale_add_mark (GTK_SCALE (size_slider),
387                         (gdouble) sizes[i],
388                         GTK_POS_BOTTOM, NULL);
389 }
390
391 static void
392 cursor_changed_cb (GtkTreeView *treeview,
393                    gpointer     user_data)
394 {
395   PangoFontFamily      *family;
396   PangoFontFace        *face;
397   PangoFontDescription *desc;
398
399   gint *sizes;
400   gint  i, n_sizes;
401
402   GtkTreeIter  iter;
403   GtkTreePath *path = gtk_tree_path_new ();
404
405   GtkFontChooser *fontchooser = (GtkFontChooser*)user_data;
406
407   gtk_tree_view_get_cursor (treeview, &path, NULL);
408
409   if (!path)
410     return;
411
412   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (fontchooser->priv->filter), &iter, path))
413     {
414       gtk_tree_path_free (path);
415       return;
416     }
417
418
419   gtk_tree_model_get (GTK_TREE_MODEL (fontchooser->priv->filter), &iter,
420                       FACE_COLUMN, &face,
421                       FAMILY_COLUMN, &family,
422                       -1);
423
424   gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0.5, 0.5);
425
426   gtk_tree_path_free (path);
427   path = NULL;
428
429   if (!face || !family)
430     {
431       g_object_unref (face);
432       g_object_unref (family);
433       return;
434     }
435
436   desc = pango_font_face_describe (face);
437   pango_font_description_set_size (desc, fontchooser->priv->size);
438   gtk_widget_override_font (fontchooser->priv->preview, desc);
439
440   pango_font_face_list_sizes (face, &sizes, &n_sizes);
441   /* It seems not many fonts actually have a sane set of sizes */
442   for (i = 0; i < n_sizes; i++)
443     sizes[i] = sizes[i] / PANGO_SCALE;
444
445   set_range_marks (fontchooser->priv, fontchooser->priv->size_slider, sizes, n_sizes);
446
447   gtk_font_chooser_ref_family (fontchooser, family);
448   gtk_font_chooser_ref_face   (fontchooser, face);
449
450   /* Free resources */
451   g_object_unref ((gpointer)family);
452   g_object_unref ((gpointer)face);
453   pango_font_description_free(desc);
454
455   g_object_notify (G_OBJECT (fontchooser), "font-name");
456 }
457
458 static gboolean
459 zoom_preview_cb (GtkWidget      *scrolled_window,
460                  GdkEventScroll *event,
461                  gpointer        user_data)
462 {
463   GtkFontChooser        *fc    = (GtkFontChooser*)user_data;
464   GtkFontChooserPrivate *priv  = fc->priv;
465
466   GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->size_spin));
467
468   if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT)
469     gtk_adjustment_set_value (adj,
470                               gtk_adjustment_get_value (adj) +
471                               gtk_adjustment_get_step_increment (adj));
472   else if (event->direction == GDK_SCROLL_DOWN || event->direction == GDK_SCROLL_LEFT)
473     gtk_adjustment_set_value (adj,
474                               gtk_adjustment_get_value (adj) -
475                               gtk_adjustment_get_step_increment (adj));
476   return TRUE;
477 }
478
479 #if 0
480 static void
481 row_inserted_cb (GtkTreeModel *model,
482                  GtkTreePath  *path,
483                  GtkTreeIter  *iter,
484                  gpointer      user_data)
485 {
486   GtkFontChooser        *fontchooser = (GtkFontChooser*)user_data;
487   GtkFontChooserPrivate *priv        = fontchooser->priv;
488
489   if (gtk_bin_get_child (GTK_BIN (priv->list_scrolled_window)) ==
490       priv->empty_list)
491     {
492       g_object_ref (priv->empty_list);
493       gtk_container_remove (GTK_CONTAINER (priv->list_scrolled_window),
494                             priv->empty_list);
495       gtk_container_add (GTK_CONTAINER (priv->list_scrolled_window),
496                          priv->family_face_list);
497     }
498 }
499
500 static void
501 row_deleted_cb  (GtkTreeModel *model,
502                  GtkTreePath  *path,
503                  gpointer      user_data)
504 {
505   GtkFontChooser        *fontchooser = (GtkFontChooser*)user_data;
506   GtkFontChooserPrivate *priv        = fontchooser->priv;
507
508   if (gtk_tree_model_iter_n_children (model, NULL) == 0)
509     {
510       if (gtk_bin_get_child (GTK_BIN (priv->list_scrolled_window)) ==
511           priv->family_face_list)
512         {
513           g_object_ref (priv->family_face_list);
514           gtk_container_remove (GTK_CONTAINER (priv->list_scrolled_window),
515                                 priv->family_face_list);
516           gtk_container_add (GTK_CONTAINER (priv->list_scrolled_window),
517                              priv->empty_list);
518         }
519     }
520 }
521 #endif
522
523 static void
524 gtk_font_chooser_init (GtkFontChooser *fontchooser)
525 {
526   GIcon                   *icon;
527   GtkFontChooserPrivate   *priv;
528   PangoFontDescription    *font_desc;
529   GtkWidget               *scrolled_win;
530   GtkWidget               *grid;
531   GtkWidget               *sub_grid;
532
533   fontchooser->priv = G_TYPE_INSTANCE_GET_PRIVATE (fontchooser,
534                                                    GTK_TYPE_FONT_CHOOSER,
535                                                    GtkFontChooserPrivate);
536
537   priv = fontchooser->priv;
538
539   /* Default preview string  */
540   priv->preview_text = g_strdup (pango_language_get_sample_string (NULL));
541   priv->show_preview_entry = TRUE;
542
543   /* Getting the default size */
544   font_desc  = pango_context_get_font_description (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)));
545   priv->size = pango_font_description_get_size (font_desc);
546   priv->face = NULL;
547   priv->family = NULL;
548
549   gtk_widget_push_composite_child ();
550
551   /* Creating fundamental widgets for the private struct */
552   priv->search_entry = gtk_entry_new ();
553   priv->family_face_list = gtk_tree_view_new ();
554   priv->preview = gtk_entry_new ();
555   priv->size_slider = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL,
556                                                 (gdouble) font_sizes[0],
557                                                 (gdouble) font_sizes[G_N_ELEMENTS (font_sizes) - 1],
558                                                 1.0);
559
560   priv->size_spin = gtk_spin_button_new_with_range (0.0, (gdouble)(G_MAXINT / PANGO_SCALE), 1.0);
561
562   /** Bootstrapping widget layout **/
563   gtk_box_set_spacing (GTK_BOX (fontchooser), 6);
564
565   /* Main font family/face view */
566   priv->list_scrolled_window = gtk_scrolled_window_new (NULL, NULL);
567   scrolled_win = priv->list_scrolled_window;
568   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
569                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
570   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
571                                        GTK_SHADOW_ETCHED_IN);
572   gtk_container_add (GTK_CONTAINER (scrolled_win),
573                      priv->family_face_list);
574
575   /* Text to display when list is empty */
576   priv->empty_list = gtk_text_view_new ();
577   gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->empty_list)),
578                             _(NO_FONT_MATCHED_SEARCH),
579                             -1);
580
581   /* Basic layout */
582   grid = gtk_grid_new ();
583   sub_grid = gtk_grid_new ();
584
585   gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
586   gtk_grid_set_row_spacing (GTK_GRID (sub_grid), 6);
587
588   gtk_grid_attach (GTK_GRID (grid), priv->search_entry, 0, 0, 3, 1);
589   gtk_grid_attach (GTK_GRID (grid), scrolled_win,       0, 1, 3, 1);
590   gtk_grid_attach (GTK_GRID (grid), priv->preview,      0, 2, 3, 1);
591   gtk_grid_attach (GTK_GRID (grid), sub_grid,           0, 4, 3, 1);
592
593   gtk_widget_set_hexpand  (GTK_WIDGET (sub_grid),          TRUE);
594   gtk_grid_attach (GTK_GRID (sub_grid), priv->size_slider,  0, 3, 2, 1);
595   gtk_grid_attach (GTK_GRID (sub_grid), priv->size_spin,    2, 3, 1, 1);
596
597   gtk_widget_set_hexpand  (GTK_WIDGET (scrolled_win),      TRUE);
598   gtk_widget_set_vexpand  (GTK_WIDGET (scrolled_win),      TRUE);
599   gtk_widget_set_hexpand  (GTK_WIDGET (priv->search_entry), TRUE);
600
601   gtk_widget_set_hexpand  (GTK_WIDGET (priv->size_slider), TRUE);
602   gtk_widget_set_hexpand  (GTK_WIDGET (priv->size_spin),   FALSE);
603
604   gtk_grid_set_column_homogeneous (GTK_GRID (sub_grid), FALSE);
605   gtk_box_pack_start (GTK_BOX (fontchooser), grid, TRUE, TRUE, 0);
606
607   /* Setting the adjustment values for the size slider */
608   gtk_adjustment_set_value (gtk_range_get_adjustment (GTK_RANGE (priv->size_slider)),
609                             (gdouble)(priv->size / PANGO_SCALE));
610   gtk_adjustment_set_value (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->size_spin)),
611                             (gdouble)(priv->size / PANGO_SCALE));
612
613   gtk_widget_show_all (GTK_WIDGET (fontchooser));
614   gtk_widget_hide     (GTK_WIDGET (fontchooser));
615
616   /* Treeview column and model bootstrapping */
617   gtk_font_chooser_bootstrap_fontlist (fontchooser);
618
619   /* Set default preview text */
620   gtk_entry_set_text (GTK_ENTRY (priv->preview),
621                       pango_language_get_sample_string (NULL));
622
623   /* Set search icon and place holder text */
624   icon = g_themed_icon_new_with_default_fallbacks ("edit-find-symbolic");
625   g_object_set (G_OBJECT (priv->search_entry),
626                 "secondary-icon-gicon", icon,
627                 "secondary-icon-activatable", FALSE,
628                 "secondary-icon-sensitive", FALSE,
629                 NULL);
630   g_object_unref (icon);
631
632   gtk_entry_set_placeholder_text (GTK_ENTRY (priv->search_entry), _("Search font name"));
633
634   /** Callback connections **/
635   g_signal_connect (priv->search_entry, "notify::text",
636                     G_CALLBACK (text_changed_cb), fontchooser);
637   g_signal_connect (priv->search_entry,
638                     "icon-press", G_CALLBACK (icon_press_cb), NULL);
639
640   g_signal_connect (gtk_range_get_adjustment (GTK_RANGE (priv->size_slider)),
641                     "value-changed", G_CALLBACK (slider_change_cb), fontchooser);
642   g_signal_connect (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->size_spin)),
643                     "value-changed", G_CALLBACK (spin_change_cb), fontchooser);
644
645   g_signal_connect (priv->family_face_list, "cursor-changed",
646                     G_CALLBACK (cursor_changed_cb), fontchooser);
647
648   /* Zoom on preview scroll*/
649   g_signal_connect (priv->preview, "scroll-event",
650                     G_CALLBACK (zoom_preview_cb), fontchooser);
651
652   g_signal_connect (priv->size_slider, "scroll-event",
653                     G_CALLBACK (zoom_preview_cb), fontchooser);
654
655   set_range_marks (priv, priv->size_slider, (gint*)font_sizes, G_N_ELEMENTS (font_sizes));
656
657   /* Font list empty hides the scrolledwindow */
658   /*
659   g_signal_connect (G_OBJECT (priv->filter), "row-deleted",
660                     G_CALLBACK (row_deleted_cb), fontchooser);
661   g_signal_connect (G_OBJECT (priv->filter), "row-inserted",
662                     G_CALLBACK (row_inserted_cb), fontchooser);
663   */
664
665   /* Set default focus */
666   gtk_widget_pop_composite_child ();
667 }
668
669 /**
670  * gtk_font_chooser_new:
671  *
672  * Creates a new #GtkFontChooser.
673  *
674  * Return value: a new #GtkFontChooser
675  *
676  * Since: 3.2
677  */
678 GtkWidget *
679 gtk_font_chooser_new (void)
680 {
681   GtkFontChooser *fontchooser;
682
683   fontchooser = g_object_new (GTK_TYPE_FONT_CHOOSER, NULL);
684
685   return GTK_WIDGET (fontchooser);
686 }
687
688 static int
689 cmp_families (const void *a,
690               const void *b)
691 {
692   const char *a_name = pango_font_family_get_name (*(PangoFontFamily **)a);
693   const char *b_name = pango_font_family_get_name (*(PangoFontFamily **)b);
694
695   return g_utf8_collate (a_name, b_name);
696 }
697
698 static void
699 populate_list (GtkFontChooser *fontchooser,
700                GtkTreeView    *treeview,
701                GtkListStore   *model)
702 {
703   GtkStyleContext      *style_context;
704   PangoFontDescription *default_font;
705
706   GtkTreeIter   match_row;
707   GtkTreePath  *path;
708
709   gint n_families, i;
710   PangoFontFamily **families;
711
712   GString     *tmp = g_string_new (NULL);
713   GString     *family_and_face = g_string_new (NULL);
714
715   pango_context_list_families (gtk_widget_get_pango_context (GTK_WIDGET (treeview)),
716                                &families,
717                                &n_families);
718
719   qsort (families, n_families, sizeof (PangoFontFamily *), cmp_families);
720
721   gtk_list_store_clear (model);
722
723   /* Get row header font color */
724   style_context = gtk_widget_get_style_context (GTK_WIDGET (treeview));
725
726   /* Get theme font */
727   default_font  = (PangoFontDescription*) gtk_style_context_get_font (style_context,
728                                                                       GTK_STATE_NORMAL);
729
730   /* Iterate over families and faces */
731   for (i = 0; i < n_families; i++)
732     {
733       GtkTreeIter     iter;
734       PangoFontFace **faces;
735
736       int             j, n_faces;
737       const gchar    *fam_name = pango_font_family_get_name (families[i]);
738
739       pango_font_family_list_faces (families[i], &faces, &n_faces);
740
741       for (j = 0; j < n_faces; j++)
742         {
743           PangoFontDescription *pango_desc = pango_font_face_describe (faces[j]);
744           const gchar *face_name = pango_font_face_get_face_name (faces[j]);
745           gchar       *font_desc = pango_font_description_to_string (pango_desc);
746
747           /* foreground_color, family_name, face_name, desc, sample string */
748           g_string_printf (family_and_face, "%s %s", fam_name, face_name);
749
750           g_string_printf (tmp, ROW_FORMAT_STRING,
751                            family_and_face->str,
752                            font_desc,
753                            fontchooser->priv->preview_text);
754
755           gtk_list_store_append (model, &iter);
756           gtk_list_store_set (model, &iter,
757                               FAMILY_COLUMN, families[i],
758                               FACE_COLUMN, faces[j],
759                               PREVIEW_TITLE_COLUMN, family_and_face->str,
760                               PREVIEW_TEXT_COLUMN, tmp->str,
761                               -1);
762
763           /* Select the first font or the default font/face from the style context */
764           if ((i == 0 && j == 0) ||
765               (!strcmp (fam_name, pango_font_description_get_family (default_font)) && j == 0))
766             match_row = iter;
767
768           pango_font_description_free(pango_desc);
769           g_free (font_desc);
770         }
771
772       g_free (faces);
773     }
774
775   path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &match_row);
776
777   if (path)
778     {
779       gtk_tree_view_set_cursor (treeview, path, NULL, FALSE);
780       gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0.5, 0.5);
781       gtk_tree_path_free(path);
782     }
783
784   g_string_free (family_and_face, TRUE);
785   g_string_free (tmp, TRUE);
786   g_free (families);
787 }
788
789 static gboolean
790 visible_func (GtkTreeModel *model,
791               GtkTreeIter  *iter,
792               gpointer      user_data)
793 {
794   gboolean result = TRUE;
795   GtkFontChooserPrivate *priv = (GtkFontChooserPrivate*)user_data;
796
797   const gchar *search_text = (const gchar*)gtk_entry_get_text (GTK_ENTRY (priv->search_entry));
798   gchar       *font_name;
799   gchar       *term;
800   gchar      **split_terms;
801   gint         n_terms = 0;
802
803   /* If there's no filter string we show the item */
804   if (strlen (search_text) == 0)
805     return TRUE;
806
807   gtk_tree_model_get (model, iter,
808                       PREVIEW_TITLE_COLUMN, &font_name,
809                       -1);
810
811   if (font_name == NULL)
812     return FALSE;
813
814   split_terms = g_strsplit (search_text, " ", 0);
815   term = split_terms[0];
816
817   while (term && result)
818   {
819     gchar* font_name_casefold = g_utf8_casefold (font_name, -1);
820     gchar* term_casefold = g_utf8_casefold (term, -1);
821
822     if (g_strrstr (font_name_casefold, term_casefold))
823       result = result && TRUE;
824     else
825       result = FALSE;
826
827     n_terms++;
828     term = split_terms[n_terms];
829
830     g_free (term_casefold);
831     g_free (font_name_casefold);
832   }
833
834   g_free (font_name);
835   g_strfreev (split_terms);
836
837   return result;
838 }
839
840 static void
841 gtk_font_chooser_bootstrap_fontlist (GtkFontChooser *fontchooser)
842 {
843   GtkTreeView       *treeview = GTK_TREE_VIEW (fontchooser->priv->family_face_list);
844   GtkCellRenderer   *cell;
845   GtkTreeViewColumn *col;
846
847   fontchooser->priv->model = gtk_list_store_new (4,
848                                                  PANGO_TYPE_FONT_FAMILY,
849                                                  PANGO_TYPE_FONT_FACE,
850                                                  G_TYPE_STRING,
851                                                  G_TYPE_STRING);
852
853   fontchooser->priv->filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fontchooser->priv->model),
854                                                          NULL);
855   g_object_unref (fontchooser->priv->model);
856
857   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (fontchooser->priv->filter),
858                                           visible_func,
859                                           (gpointer)fontchooser->priv,
860                                           NULL);
861
862   gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (fontchooser->priv->filter));
863   g_object_unref (fontchooser->priv->filter);
864
865   gtk_tree_view_set_rules_hint      (treeview, TRUE);
866   gtk_tree_view_set_headers_visible (treeview, FALSE);
867
868   cell = gtk_cell_renderer_text_new ();
869   col = gtk_tree_view_column_new_with_attributes ("Family",
870                                                   cell,
871                                                   "markup", PREVIEW_TEXT_COLUMN,
872                                                   NULL);
873
874   g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
875
876   gtk_tree_view_append_column (treeview, col);
877
878   populate_list (fontchooser, treeview, fontchooser->priv->model);
879 }
880
881
882 static void
883 gtk_font_chooser_finalize (GObject *object)
884 {
885   GtkFontChooser *fontchooser = GTK_FONT_CHOOSER (object);
886
887   gtk_font_chooser_ref_family (fontchooser, NULL);
888   gtk_font_chooser_ref_face (fontchooser, NULL);
889
890   G_OBJECT_CLASS (gtk_font_chooser_parent_class)->finalize (object);
891 }
892
893
894 static void
895 gtk_font_chooser_screen_changed (GtkWidget *widget,
896                                  GdkScreen *previous_screen)
897 {
898   GtkFontChooser *fontchooser = GTK_FONT_CHOOSER (widget);
899
900   populate_list (fontchooser,
901                  GTK_TREE_VIEW (fontchooser->priv->family_face_list),
902                  fontchooser->priv->model);
903 }
904
905 static void
906 gtk_font_chooser_style_updated (GtkWidget *widget)
907 {
908   GtkFontChooser *fontchooser = GTK_FONT_CHOOSER (widget);
909
910   GTK_WIDGET_CLASS (gtk_font_chooser_parent_class)->style_updated (widget);
911
912   populate_list (fontchooser,
913                  GTK_TREE_VIEW (fontchooser->priv->family_face_list),
914                  fontchooser->priv->model);
915 }
916
917 static void
918 gtk_font_chooser_ref_family (GtkFontChooser  *fontchooser,
919                              PangoFontFamily *family)
920 {
921   GtkFontChooserPrivate *priv = fontchooser->priv;
922
923   if (family)
924     family = g_object_ref (family);
925   if (priv->family)
926     g_object_unref (priv->family);
927   priv->family = family;
928 }
929
930 static void
931 gtk_font_chooser_ref_face (GtkFontChooser *fontchooser,
932                            PangoFontFace  *face)
933 {
934   GtkFontChooserPrivate *priv = fontchooser->priv;
935
936   if (face)
937     face = g_object_ref (face);
938   if (priv->face)
939     g_object_unref (priv->face);
940   priv->face = face;
941 }
942
943
944 /*
945  * These functions are the main public interface for getting/setting the font.
946  */
947
948 /**
949  * gtk_font_chooser_get_family:
950  * @fontchooser: a #GtkFontChooser
951  *
952  * Gets the #PangoFontFamily representing the selected font family.
953  * Font families are a collection of font faces.
954  *
955  * Return value: (transfer none): A #PangoFontFamily representing the
956  *     selected font family. The returned object is owned by @fontchooser
957  *     and must not be modified or freed.
958  *
959  * Since: 3.2
960  */
961 PangoFontFamily *
962 gtk_font_chooser_get_family (GtkFontChooser *fontchooser)
963 {
964   g_return_val_if_fail (GTK_IS_FONT_CHOOSER (fontchooser), NULL);
965
966   return fontchooser->priv->family;
967 }
968
969 /**
970  * gtk_font_chooser_get_face:
971  * @fontchooser: a #GtkFontChooser
972  *
973  * Gets the #PangoFontFace representing the selected font group
974  * details (i.e. family, slant, weight, width, etc).
975  *
976  * Return value: (transfer none): A #PangoFontFace representing the
977  *     selected font group details. The returned object is owned by
978  *     @fontchooser and must not be modified or freed.
979  *
980  * Since: 3.2
981  */
982 PangoFontFace *
983 gtk_font_chooser_get_face (GtkFontChooser *fontchooser)
984 {
985   g_return_val_if_fail (GTK_IS_FONT_CHOOSER (fontchooser), NULL);
986
987   return fontchooser->priv->face;
988 }
989
990 /**
991  * gtk_font_chooser_get_size:
992  * @fontchooser: a #GtkFontChooser
993  *
994  * The selected font size.
995  *
996  * Return value: A n integer representing the selected font size,
997  *     or -1 if no font size is selected.
998  *
999  * Since: 3.2
1000  **/
1001 gint
1002 gtk_font_chooser_get_size (GtkFontChooser *fontchooser)
1003 {
1004   g_return_val_if_fail (GTK_IS_FONT_CHOOSER (fontchooser), -1);
1005
1006   return fontchooser->priv->size;
1007 }
1008
1009 /**
1010  * gtk_font_chooser_get_font_name:
1011  * @fontchooser: a #GtkFontChooser
1012  *
1013  * Gets the currently-selected font name.
1014  *
1015  * Note that this can be a different string than what you set with
1016  * gtk_font_chooser_set_font_name(), as the font chooser widget may
1017  * normalize font names and thus return a string with a different
1018  * structure. For example, "Helvetica Italic Bold 12" could be
1019  * normalized to "Helvetica Bold Italic 12".
1020  *
1021  * Use pango_font_description_equal() if you want to compare two
1022  * font descriptions.
1023  *
1024  * Return value: (transfer full) (allow-none): A string with the name
1025  *     of the current font, or %NULL if  no font is selected. You must
1026  *     free this string with g_free().
1027  *
1028  * Since: 3.2
1029  */
1030 gchar *
1031 gtk_font_chooser_get_font_name (GtkFontChooser *fontchooser)
1032 {
1033   gchar                *font_name;
1034   gchar                *font_desc_name;
1035   PangoFontDescription *desc;
1036
1037   if (!fontchooser->priv->face)
1038     return NULL;
1039
1040   desc = pango_font_face_describe (fontchooser->priv->face);
1041   font_desc_name = pango_font_description_to_string (desc);
1042   pango_font_description_free (desc);
1043
1044   font_name = g_strdup_printf ("%s %d", font_desc_name, fontchooser->priv->size / PANGO_SCALE);
1045   g_free (font_desc_name);
1046   return font_name;
1047 }
1048
1049 /**
1050  * gtk_font_chooser_set_font_name:
1051  * @fontchooser: a #GtkFontChooser
1052  * @fontname: a font name like "Helvetica 12" or "Times Bold 18"
1053  *
1054  * Sets the currently-selected font.
1055  *
1056  * Note that the @fontchooser needs to know the screen in which
1057  * it will appear for this to work; this can be guaranteed by simply
1058  * making sure that the @fontchooser is inserted in a toplevel window
1059  * before you call this function.
1060  *
1061  * Return value: %TRUE if the font could be set successfully; %FALSE
1062  *     if no such font exists or if the @fontchooser doesn't belong
1063  *     to a particular screen yet.
1064  *
1065  * Since: 3.2
1066  */
1067 gboolean
1068 gtk_font_chooser_set_font_name (GtkFontChooser *fontchooser,
1069                                 const gchar    *fontname)
1070 {
1071   GtkFontChooserPrivate *priv = fontchooser->priv;
1072   GtkTreeIter           iter;
1073   gboolean              valid;
1074   gchar                *family_name;
1075   PangoFontDescription *desc;
1076   gboolean              found = FALSE;
1077
1078   g_return_val_if_fail (GTK_IS_FONT_CHOOSER (fontchooser), FALSE);
1079   g_return_val_if_fail (fontname != NULL, FALSE);
1080
1081   if (!gtk_widget_has_screen (GTK_WIDGET (fontchooser)))
1082     return FALSE;
1083
1084   desc = pango_font_description_from_string (fontname);
1085   family_name = (gchar*)pango_font_description_get_family (desc);
1086
1087   if (!family_name)
1088     {
1089       pango_font_description_free (desc);
1090       return FALSE;
1091     }
1092
1093   /* We make sure the filter is clear */
1094   gtk_entry_set_text (GTK_ENTRY (priv->search_entry), "");
1095
1096   /* We find the matching family/face */
1097   for (valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->filter), &iter);
1098        valid;
1099        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->filter), &iter))
1100     {
1101       PangoFontFace        *face;
1102       PangoFontDescription *tmp_desc;
1103
1104       gtk_tree_model_get (GTK_TREE_MODEL (priv->filter), &iter,
1105                           FACE_COLUMN,   &face,
1106                           -1);
1107
1108       tmp_desc = pango_font_face_describe (face);
1109       if (pango_font_description_get_size_is_absolute (desc))
1110         pango_font_description_set_absolute_size (tmp_desc,
1111                                                   pango_font_description_get_size (desc));
1112       else
1113         pango_font_description_set_size (tmp_desc,
1114                                          pango_font_description_get_size (desc));
1115
1116       if (pango_font_description_equal (desc, tmp_desc))
1117         {
1118           GtkTreePath *path;
1119           gint size = pango_font_description_get_size (desc);
1120
1121           if (size)
1122             {
1123               if (pango_font_description_get_size_is_absolute (desc))
1124                 size = size * PANGO_SCALE;
1125               gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->size_spin),
1126                                          size / PANGO_SCALE);
1127             }
1128
1129           path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->filter),
1130                                           &iter);
1131
1132           if (path)
1133             {
1134               gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->family_face_list),
1135                                         path,
1136                                         NULL,
1137                                         FALSE);
1138               gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->family_face_list),
1139                                             path,
1140                                             NULL,
1141                                             FALSE,
1142                                             0.5,
1143                                             0.5);
1144               gtk_tree_path_free (path);
1145             }
1146
1147           found = TRUE;
1148         }
1149
1150       g_object_unref (face);
1151       pango_font_description_free (tmp_desc);
1152
1153       if (found)
1154         break;
1155     }
1156
1157   pango_font_description_free (desc);
1158   g_object_notify (G_OBJECT (fontchooser), "font-name");
1159
1160   return found;
1161 }
1162
1163 /**
1164  * gtk_font_chooser_get_preview_text:
1165  * @fontchooser: a #GtkFontChooser
1166  *
1167  * Gets the text displayed in the preview area.
1168  *
1169  * Return value: (transfer none): the text displayed in the
1170  *     preview area. This string is owned by the widget and
1171  *     should not be modified or freed
1172  *
1173  * Since: 3.2
1174  */
1175 const gchar*
1176 gtk_font_chooser_get_preview_text (GtkFontChooser *fontchooser)
1177 {
1178   g_return_val_if_fail (GTK_IS_FONT_CHOOSER (fontchooser), NULL);
1179
1180   return (const gchar*)fontchooser->priv->preview_text;
1181 }
1182
1183
1184 /**
1185  * gtk_font_chooser_set_preview_text:
1186  * @fontchooser: a #GtkFontChooser
1187  * @text: (transfer none): the text to display in the preview area
1188  *
1189  * Sets the text displayed in the preview area.
1190  * The @text is used to show how the selected font looks.
1191  *
1192  * Since: 3.2
1193  */
1194 void
1195 gtk_font_chooser_set_preview_text (GtkFontChooser *fontchooser,
1196                                    const gchar    *text)
1197 {
1198   g_return_if_fail (GTK_IS_FONT_CHOOSER (fontchooser));
1199   g_return_if_fail (text != NULL);
1200
1201   g_free (fontchooser->priv->preview_text);
1202   fontchooser->priv->preview_text = g_strdup (text);
1203
1204   populate_list (fontchooser,
1205                  GTK_TREE_VIEW (fontchooser->priv->family_face_list),
1206                  fontchooser->priv->model);
1207
1208   gtk_entry_set_text (GTK_ENTRY (fontchooser->priv->preview), text);
1209
1210   g_object_notify (G_OBJECT (fontchooser), "preview-text");
1211 }
1212
1213 /**
1214  * gtk_font_chooser_get_show_preview_entry:
1215  * @fontchooser: a #GtkFontChooser
1216  *
1217  * Returns whether the preview entry is shown or not.
1218  *
1219  * Return value: %TRUE if the preview entry is shown
1220  *     or %FALSE if it is hidden.
1221  *
1222  * Since: 3.2
1223  */
1224 gboolean
1225 gtk_font_chooser_get_show_preview_entry (GtkFontChooser *fontchooser)
1226 {
1227   g_return_val_if_fail (GTK_IS_FONT_CHOOSER (fontchooser), FALSE);
1228
1229   return fontchooser->priv->show_preview_entry;
1230 }
1231
1232 /**
1233  * gtk_font_chooser_set_show_preview_entry:
1234  * @fontchooser: a #GtkFontChooser
1235  * @show_preview_entry: whether to show the editable preview entry or not
1236  *
1237  * Shows or hides the editable preview entry.
1238  *
1239  * Since: 3.2
1240  */
1241 void
1242 gtk_font_chooser_set_show_preview_entry (GtkFontChooser *fontchooser,
1243                                          gboolean        show_preview_entry)
1244 {
1245   g_return_if_fail (GTK_IS_FONT_CHOOSER (fontchooser));
1246
1247   if (show_preview_entry)
1248     gtk_widget_show (fontchooser->priv->preview_scrolled_window);
1249   else
1250     gtk_widget_hide (fontchooser->priv->preview_scrolled_window);
1251
1252   fontchooser->priv->show_preview_entry = show_preview_entry;
1253   g_object_notify (G_OBJECT (fontchooser), "show-preview-entry");
1254 }