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