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