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