]> Pileus Git - ~andy/gtk/blob - gtk/gtkfontsel.c
don't cast a possible NULL pointer.
[~andy/gtk] / gtk / gtkfontsel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * Massively updated for Pango by Owen Taylor, May 2000
5  * GtkFontSelection widget for Gtk+, by Damon Chaplin, May 1998.
6  * Based on the GnomeFontSelector widget, by Elliot Lee, but major changes.
7  * The GnomeFontSelector was derived from app/text_tool.c in the GIMP.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /*
26  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
27  * file for a list of people on the GTK+ Team.  See the ChangeLog
28  * files for a list of changes.  These files are distributed with
29  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
30  */
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
36
37 #include "gdk/gdk.h"
38 #include "gdk/gdkkeysyms.h"
39
40 #include "gtkfontsel.h"
41
42 #include "gtkbutton.h"
43 #include "gtkclist.h"
44 #include "gtkentry.h"
45 #include "gtkframe.h"
46 #include "gtkhbbox.h"
47 #include "gtkhbox.h"
48 #include "gtklabel.h"
49 #include "gtkrc.h"
50 #include "gtksignal.h"
51 #include "gtkstock.h"
52 #include "gtktable.h"
53 #include "gtkvbox.h"
54 #include "gtkscrolledwindow.h"
55 #include "gtkintl.h"
56
57 /* This is the default text shown in the preview entry, though the user
58    can set it. Remember that some fonts only have capital letters. */
59 #define PREVIEW_TEXT "abcdefghijk ABCDEFGHIJK"
60
61 /* This is the initial and maximum height of the preview entry (it expands
62    when large font sizes are selected). Initial height is also the minimum. */
63 #define INITIAL_PREVIEW_HEIGHT 44
64 #define MAX_PREVIEW_HEIGHT 300
65
66 /* These are the sizes of the font, style & size clists. */
67 #define FONT_LIST_HEIGHT        136
68 #define FONT_LIST_WIDTH         190
69 #define FONT_STYLE_LIST_WIDTH   170
70 #define FONT_SIZE_LIST_WIDTH    60
71
72 /* These are what we use as the standard font sizes, for the size clist.
73  */
74 static const guint16 font_sizes[] = {
75   8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28,
76   32, 36, 40, 48, 56, 64, 72
77 };
78
79 enum {
80    PROP_0,
81    PROP_FONT_NAME,
82    PROP_FONT,
83    PROP_PREVIEW_TEXT
84 };
85
86 static void    gtk_font_selection_class_init         (GtkFontSelectionClass *klass);
87 static void    gtk_font_selection_set_property       (GObject         *object,
88                                                       guint            prop_id,
89                                                       const GValue    *value,
90                                                       GParamSpec      *pspec);
91 static void    gtk_font_selection_get_property       (GObject         *object,
92                                                       guint            prop_id,
93                                                       GValue          *value,
94                                                       GParamSpec      *pspec);
95 static void    gtk_font_selection_init               (GtkFontSelection      *fontsel);
96 static void    gtk_font_selection_finalize           (GObject               *object);
97
98 /* These are the callbacks & related functions. */
99 static void     gtk_font_selection_select_font           (GtkWidget        *w,
100                                                           gint              row,
101                                                           gint              column,
102                                                           GdkEventButton   *bevent,
103                                                           gpointer          data);
104 static void     gtk_font_selection_show_available_fonts  (GtkFontSelection *fs);
105
106 static void     gtk_font_selection_show_available_styles (GtkFontSelection *fs);
107 static void     gtk_font_selection_select_best_style     (GtkFontSelection *fs,
108                                                           gboolean          use_first);
109 static void     gtk_font_selection_select_style          (GtkWidget        *w,
110                                                           gint              row,
111                                                           gint              column,
112                                                           GdkEventButton   *bevent,
113                                                           gpointer          data);
114
115 static void     gtk_font_selection_select_best_size      (GtkFontSelection *fs);
116 static void     gtk_font_selection_show_available_sizes  (GtkFontSelection *fs);
117 static void     gtk_font_selection_size_activate         (GtkWidget        *w,
118                                                           gpointer          data);
119 static void     gtk_font_selection_select_size           (GtkWidget        *w,
120                                                           gint              row,
121                                                           gint              column,
122                                                           GdkEventButton   *bevent,
123                                                           gpointer          data);
124
125 static void     gtk_font_selection_scroll_on_map         (GtkWidget        *w,
126                                                           gpointer          data);
127
128 static void     gtk_font_selection_preview_changed       (GtkWidget        *entry,
129                                                           GtkFontSelection *fontsel);
130
131 /* Misc. utility functions. */
132 static void     gtk_font_selection_load_font         (GtkFontSelection *fs);
133 static void    gtk_font_selection_update_preview     (GtkFontSelection *fs);
134
135 /* FontSelectionDialog */
136 static void    gtk_font_selection_dialog_class_init  (GtkFontSelectionDialogClass *klass);
137 static void    gtk_font_selection_dialog_init        (GtkFontSelectionDialog *fontseldiag);
138
139 static gint    gtk_font_selection_dialog_on_configure (GtkWidget      *widget,
140                                                        GdkEventConfigure *event,
141                                                        GtkFontSelectionDialog *fsd);
142
143 static GtkWindowClass *font_selection_parent_class = NULL;
144 static GtkVBoxClass *font_selection_dialog_parent_class = NULL;
145
146 GtkType
147 gtk_font_selection_get_type ()
148 {
149   static GtkType font_selection_type = 0;
150   
151   if (!font_selection_type)
152     {
153       static const GtkTypeInfo fontsel_type_info =
154       {
155         "GtkFontSelection",
156         sizeof (GtkFontSelection),
157         sizeof (GtkFontSelectionClass),
158         (GtkClassInitFunc) gtk_font_selection_class_init,
159         (GtkObjectInitFunc) gtk_font_selection_init,
160         /* reserved_1 */ NULL,
161         /* reserved_2 */ NULL,
162         (GtkClassInitFunc) NULL,
163       };
164       
165       font_selection_type = gtk_type_unique (GTK_TYPE_VBOX,
166                                              &fontsel_type_info);
167     }
168   
169   return font_selection_type;
170 }
171
172 static void
173 gtk_font_selection_class_init (GtkFontSelectionClass *klass)
174 {
175   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
176   
177   font_selection_parent_class = gtk_type_class (GTK_TYPE_VBOX);
178   
179   gobject_class->set_property = gtk_font_selection_set_property;
180   gobject_class->get_property = gtk_font_selection_get_property;
181    
182   g_object_class_install_property (gobject_class,
183                                    PROP_FONT_NAME,
184                                    g_param_spec_string ("font_name",
185                                                         _("Font name"),
186                                                         _("The X string that represents this font."),
187                                                         NULL,
188                                                         G_PARAM_READWRITE));
189   g_object_class_install_property (gobject_class,
190                                    PROP_FONT,
191                                    g_param_spec_boxed ("font",
192                                                        _("Font"),
193                                                        _("The GdkFont that is currently selected."),
194                                                        GDK_TYPE_FONT,
195                                                        G_PARAM_READABLE));
196   g_object_class_install_property (gobject_class,
197                                    PROP_PREVIEW_TEXT,
198                                    g_param_spec_string ("preview_text",
199                                                         _("Preview text"),
200                                                         _("The text to display in order to demonstrate the selected font."),
201                                                         PREVIEW_TEXT,
202                                                         G_PARAM_READWRITE));
203   gobject_class->finalize = gtk_font_selection_finalize;
204 }
205
206 static void 
207 gtk_font_selection_set_property (GObject         *object,
208                                  guint            prop_id,
209                                  const GValue    *value,
210                                  GParamSpec      *pspec)
211 {
212   GtkFontSelection *fontsel;
213
214   fontsel = GTK_FONT_SELECTION (object);
215
216   switch (prop_id)
217     {
218     case PROP_FONT_NAME:
219       gtk_font_selection_set_font_name (fontsel, g_value_get_string (value));
220       break;
221     case PROP_PREVIEW_TEXT:
222       gtk_font_selection_set_preview_text (fontsel, g_value_get_string (value));
223       break;
224     default:
225       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226       break;
227     }
228 }
229
230 static void gtk_font_selection_get_property (GObject         *object,
231                                              guint            prop_id,
232                                              GValue          *value,
233                                              GParamSpec      *pspec)
234 {
235   GtkFontSelection *fontsel;
236
237   fontsel = GTK_FONT_SELECTION (object);
238
239   switch (prop_id)
240     {
241     case PROP_FONT_NAME:
242       g_value_set_string (value, gtk_font_selection_get_font_name (fontsel));
243       break;
244     case PROP_FONT:
245       g_value_set_object (value, gtk_font_selection_get_font (fontsel));
246       break;
247     case PROP_PREVIEW_TEXT:
248       g_value_set_string (value, gtk_font_selection_get_preview_text (fontsel));
249       break;
250     default:
251       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
252       break;
253     }
254 }
255
256
257 static void
258 gtk_font_selection_init (GtkFontSelection *fontsel)
259 {
260   GtkWidget *scrolled_win;
261   GtkWidget *text_frame;
262   GtkWidget *text_box;
263   GtkWidget *table, *label;
264
265   fontsel->size = 12 * PANGO_SCALE;
266   
267   /* Create the table of font, style & size. */
268   table = gtk_table_new (3, 3, FALSE);
269   gtk_widget_show (table);
270   gtk_table_set_col_spacings (GTK_TABLE (table), 8);
271   gtk_box_pack_start (GTK_BOX (fontsel), table, TRUE, TRUE, 0);
272
273   fontsel->font_entry = gtk_entry_new ();
274   gtk_entry_set_editable (GTK_ENTRY (fontsel->font_entry), FALSE);
275   gtk_widget_set_usize (fontsel->font_entry, 20, -1);
276   gtk_widget_show (fontsel->font_entry);
277   gtk_table_attach (GTK_TABLE (table), fontsel->font_entry, 0, 1, 1, 2,
278                     GTK_FILL, 0, 0, 0);
279   
280   fontsel->font_style_entry = gtk_entry_new ();
281   gtk_entry_set_editable (GTK_ENTRY (fontsel->font_style_entry), FALSE);
282   gtk_widget_set_usize (fontsel->font_style_entry, 20, -1);
283   gtk_widget_show (fontsel->font_style_entry);
284   gtk_table_attach (GTK_TABLE (table), fontsel->font_style_entry, 1, 2, 1, 2,
285                     GTK_FILL, 0, 0, 0);
286   
287   fontsel->size_entry = gtk_entry_new ();
288   gtk_widget_set_usize (fontsel->size_entry, 20, -1);
289   gtk_widget_show (fontsel->size_entry);
290   gtk_table_attach (GTK_TABLE (table), fontsel->size_entry, 2, 3, 1, 2,
291                     GTK_FILL, 0, 0, 0);
292   gtk_signal_connect (GTK_OBJECT (fontsel->size_entry), "activate",
293                       (GtkSignalFunc) gtk_font_selection_size_activate,
294                       fontsel);
295   
296   fontsel->font_label = gtk_label_new_with_mnemonic (_("_Family:"));
297   gtk_label_set_mnemonic_widget (GTK_LABEL (fontsel->font_label),
298                                  fontsel->font_entry);
299   gtk_misc_set_alignment (GTK_MISC (fontsel->font_label), 0.0, 0.5);
300   gtk_widget_show (fontsel->font_label);
301   gtk_table_attach (GTK_TABLE (table), fontsel->font_label, 0, 1, 0, 1,
302                     GTK_FILL, 0, 0, 0);  
303   label = gtk_label_new_with_mnemonic (_("_Style:"));
304   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
305                                  fontsel->font_style_entry);
306   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
307   gtk_widget_show (label);
308   gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1,
309                     GTK_FILL, 0, 0, 0);
310   label = gtk_label_new_with_mnemonic (_("Si_ze:"));
311   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
312                                  fontsel->size_entry);
313   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
314   gtk_widget_show (label);
315   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
316                     GTK_FILL, 0, 0, 0);
317   
318   
319   /* Create the clists  */
320   fontsel->font_clist = gtk_clist_new (1);
321   gtk_clist_column_titles_hide (GTK_CLIST (fontsel->font_clist));
322   gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_clist), 0, TRUE);
323   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
324   gtk_widget_set_usize (scrolled_win, FONT_LIST_WIDTH, FONT_LIST_HEIGHT);
325   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->font_clist);
326   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
327                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
328   gtk_widget_show (fontsel->font_clist);
329   gtk_widget_show (scrolled_win);
330
331   gtk_table_attach (GTK_TABLE (table), scrolled_win, 0, 1, 2, 3,
332                     GTK_EXPAND | GTK_FILL,
333                     GTK_EXPAND | GTK_FILL, 0, 0);
334   
335   fontsel->font_style_clist = gtk_clist_new (1);
336   gtk_clist_column_titles_hide (GTK_CLIST (fontsel->font_style_clist));
337   gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_style_clist),
338                                     0, TRUE);
339   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
340   gtk_widget_set_usize (scrolled_win, FONT_STYLE_LIST_WIDTH, -1);
341   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->font_style_clist);
342   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
343                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
344   gtk_widget_show (fontsel->font_style_clist);
345   gtk_widget_show (scrolled_win);
346   gtk_table_attach (GTK_TABLE (table), scrolled_win, 1, 2, 2, 3,
347                     GTK_EXPAND | GTK_FILL,
348                     GTK_EXPAND | GTK_FILL, 0, 0);
349   
350   fontsel->size_clist = gtk_clist_new (1);
351   gtk_clist_column_titles_hide (GTK_CLIST (fontsel->size_clist));
352   gtk_clist_set_column_width (GTK_CLIST (fontsel->size_clist), 0, 20);
353   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
354   gtk_widget_set_usize (scrolled_win, FONT_SIZE_LIST_WIDTH, -1);
355   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->size_clist);
356   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
357                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
358   gtk_widget_show (fontsel->size_clist);
359   gtk_widget_show (scrolled_win);
360   gtk_table_attach (GTK_TABLE (table), scrolled_win, 2, 3, 2, 3,
361                     GTK_FILL, GTK_FILL, 0, 0);
362   
363   /* Insert the fonts. If there exist fonts with the same family but
364      different foundries, then the foundry name is appended in brackets. */
365   gtk_font_selection_show_available_fonts (fontsel);
366   
367   gtk_signal_connect (GTK_OBJECT (fontsel->font_clist), "select_row",
368                       GTK_SIGNAL_FUNC (gtk_font_selection_select_font),
369                       fontsel);
370   GTK_WIDGET_SET_FLAGS (fontsel->font_clist, GTK_CAN_FOCUS);
371
372   gtk_signal_connect_after (GTK_OBJECT (fontsel->font_clist), "map",
373                             GTK_SIGNAL_FUNC (gtk_font_selection_scroll_on_map),
374                             fontsel);
375   
376   gtk_font_selection_show_available_styles (fontsel);
377   
378   gtk_signal_connect (GTK_OBJECT (fontsel->font_style_clist), "select_row",
379                       GTK_SIGNAL_FUNC (gtk_font_selection_select_style),
380                       fontsel);
381   GTK_WIDGET_SET_FLAGS (fontsel->font_style_clist, GTK_CAN_FOCUS);
382
383   gtk_font_selection_show_available_sizes (fontsel);
384   
385   gtk_signal_connect (GTK_OBJECT (fontsel->size_clist), "select_row",
386                       GTK_SIGNAL_FUNC (gtk_font_selection_select_size),
387                       fontsel);
388   GTK_WIDGET_SET_FLAGS (fontsel->size_clist, GTK_CAN_FOCUS);
389   
390   /* create the text entry widget */
391   text_frame = gtk_frame_new (_("Preview:"));
392   gtk_widget_show (text_frame);
393   gtk_frame_set_shadow_type (GTK_FRAME (text_frame), GTK_SHADOW_ETCHED_IN);
394   gtk_box_pack_start (GTK_BOX (fontsel), text_frame,
395                       FALSE, TRUE, 0);
396   
397   /* This is just used to get a 4-pixel space around the preview entry. */
398   text_box = gtk_hbox_new (FALSE, 0);
399   gtk_widget_show (text_box);
400   gtk_container_add (GTK_CONTAINER (text_frame), text_box);
401   gtk_container_set_border_width (GTK_CONTAINER (text_box), 4);
402   
403   fontsel->preview_entry = gtk_entry_new ();
404   gtk_widget_show (fontsel->preview_entry);
405   gtk_signal_connect (GTK_OBJECT (fontsel->preview_entry), "changed",
406                       (GtkSignalFunc) gtk_font_selection_preview_changed,
407                       fontsel);
408   gtk_widget_set_usize (fontsel->preview_entry, -1, INITIAL_PREVIEW_HEIGHT);
409   gtk_box_pack_start (GTK_BOX (text_box), fontsel->preview_entry,
410                       TRUE, TRUE, 0);
411
412   gtk_font_selection_update_preview (fontsel);
413 }
414
415 GtkWidget *
416 gtk_font_selection_new ()
417 {
418   GtkFontSelection *fontsel;
419   
420   fontsel = gtk_type_new (GTK_TYPE_FONT_SELECTION);
421   
422   return GTK_WIDGET (fontsel);
423 }
424
425 static void
426 gtk_font_selection_finalize (GObject *object)
427 {
428   GtkFontSelection *fontsel;
429   
430   g_return_if_fail (GTK_IS_FONT_SELECTION (object));
431   
432   fontsel = GTK_FONT_SELECTION (object);
433
434   g_free (fontsel->families);
435   g_free (fontsel->faces);
436
437   if (fontsel->font)
438     gdk_font_unref (fontsel->font);
439   
440   if (G_OBJECT_CLASS (font_selection_parent_class)->finalize)
441     (* G_OBJECT_CLASS (font_selection_parent_class)->finalize) (object);
442 }
443
444 static void
445 gtk_font_selection_preview_changed (GtkWidget        *entry,
446                                     GtkFontSelection *fontsel)
447 {
448   g_object_notify (G_OBJECT (fontsel), "preview_text");
449 }
450
451 /* This is called when the clist is mapped. Here we scroll to the current
452    font if necessary. */
453 static void
454 gtk_font_selection_scroll_on_map (GtkWidget             *widget,
455                                   gpointer               data)
456 {
457   GtkFontSelection *fontsel;
458   GList *selection;
459   gint index;
460   
461 #ifdef FONTSEL_DEBUG
462   g_message ("In expose_list\n");
463 #endif
464   fontsel = GTK_FONT_SELECTION (data);
465   
466   /* Try to scroll the font family clist to the selected item */
467   selection = GTK_CLIST (fontsel->font_clist)->selection;
468   if (selection)
469     {
470       index = GPOINTER_TO_INT (selection->data);
471       if (gtk_clist_row_is_visible (GTK_CLIST (fontsel->font_clist), index)
472           != GTK_VISIBILITY_FULL)
473         gtk_clist_moveto (GTK_CLIST (fontsel->font_clist), index, -1, 0.5, 0);
474     }
475       
476   /* Try to scroll the font style clist to the selected item */
477   selection = GTK_CLIST (fontsel->font_style_clist)->selection;
478   if (selection)
479     {
480       index = GPOINTER_TO_INT (selection->data);
481       if (gtk_clist_row_is_visible (GTK_CLIST (fontsel->font_style_clist), index)
482           != GTK_VISIBILITY_FULL)
483         gtk_clist_moveto (GTK_CLIST (fontsel->font_style_clist), index, -1,
484                          0.5, 0);
485     }
486       
487   /* Try to scroll the font size clist to the selected item */
488   selection = GTK_CLIST (fontsel->size_clist)->selection;
489   if (selection)
490     {
491       index = GPOINTER_TO_INT (selection->data);
492       if (gtk_clist_row_is_visible (GTK_CLIST (fontsel->size_clist), index)
493           != GTK_VISIBILITY_FULL)
494       gtk_clist_moveto (GTK_CLIST (fontsel->size_clist), index, -1, 0.5, 0);
495     }
496 }
497
498 /* This is called when a family is selected in the list. */
499 static void
500 gtk_font_selection_select_font (GtkWidget      *w,
501                                 gint            row,
502                                 gint            column,
503                                 GdkEventButton *bevent,
504                                 gpointer        data)
505 {
506   GtkFontSelection *fontsel;
507   const gchar *family_name;
508   gint index;
509   
510   fontsel = GTK_FONT_SELECTION (data);
511
512   if (GTK_CLIST (fontsel->font_clist)->selection)
513     {
514       index = GPOINTER_TO_INT (GTK_CLIST (fontsel->font_clist)->selection->data);
515
516       if (fontsel->family != fontsel->families[index])
517         {
518           fontsel->family = fontsel->families[index];
519           
520           family_name = pango_font_family_get_name (fontsel->family);
521           
522           gtk_entry_set_text (GTK_ENTRY (fontsel->font_entry), family_name);
523           
524           gtk_font_selection_show_available_styles (fontsel);
525           gtk_font_selection_select_best_style (fontsel, TRUE);
526         }
527     }
528 }
529
530 static int
531 cmp_families (const void *a, const void *b)
532 {
533   const char *a_name = pango_font_family_get_name (*(PangoFontFamily **)a);
534   const char *b_name = pango_font_family_get_name (*(PangoFontFamily **)b);
535   
536   return strcmp (a_name, b_name);
537 }
538
539 static void
540 gtk_font_selection_show_available_fonts (GtkFontSelection *fontsel)
541 {
542   gint n_families, i;
543   gint match_row = -1;
544
545   pango_context_list_families (gtk_widget_get_pango_context (GTK_WIDGET (fontsel)),
546                                &fontsel->families, &n_families);
547   qsort (fontsel->families, n_families, sizeof (PangoFontFamily *), cmp_families);
548
549   gtk_clist_freeze (GTK_CLIST (fontsel->font_clist));
550   gtk_clist_clear (GTK_CLIST (fontsel->font_clist));
551
552   for (i=0; i<n_families; i++)
553     {
554       const gchar *name = pango_font_family_get_name (fontsel->families[i]);
555       
556       gtk_clist_append (GTK_CLIST (fontsel->font_clist), (char **)&name);
557
558       if (!g_strcasecmp (name, "sans"))
559         match_row = i;
560     }
561
562   if (match_row < 0)
563     match_row = 0;
564
565   gtk_clist_select_row (GTK_CLIST (fontsel->font_clist), match_row, 0);
566   gtk_entry_set_text (GTK_ENTRY (fontsel->font_entry), 
567                       pango_font_family_get_name (fontsel->families[match_row]));
568   fontsel->family = fontsel->families[match_row];
569   
570   gtk_clist_thaw (GTK_CLIST (fontsel->font_clist));
571 }
572
573 static int
574 compare_font_descriptions (const PangoFontDescription *a, const PangoFontDescription *b)
575 {
576   int val = strcmp (pango_font_description_get_family (a), pango_font_description_get_family (b));
577   if (val != 0)
578     return val;
579
580   if (pango_font_description_get_weight (a) != pango_font_description_get_weight (b))
581     return pango_font_description_get_weight (a) - pango_font_description_get_weight (b);
582
583   if (pango_font_description_get_style (a) != pango_font_description_get_style (b))
584     return pango_font_description_get_style (a) - pango_font_description_get_style (b);
585   
586   if (pango_font_description_get_stretch (a) != pango_font_description_get_stretch (b))
587     return pango_font_description_get_stretch (a) - pango_font_description_get_stretch (b);
588
589   if (pango_font_description_get_variant (a) != pango_font_description_get_variant (b))
590     return pango_font_description_get_variant (a) - pango_font_description_get_variant (b);
591
592   return 0;
593 }
594
595 static int
596 faces_sort_func (const void *a, const void *b)
597 {
598   PangoFontDescription *desc_a = pango_font_face_describe (*(PangoFontFace **)a);
599   PangoFontDescription *desc_b = pango_font_face_describe (*(PangoFontFace **)b);
600   
601   int ord = compare_font_descriptions (desc_a, desc_b);
602
603   pango_font_description_free (desc_a);
604   pango_font_description_free (desc_b);
605
606   return ord;
607 }
608
609 static gboolean
610 font_description_style_equal (const PangoFontDescription *a,
611                               const PangoFontDescription *b)
612 {
613   return (pango_font_description_get_weight (a) == pango_font_description_get_weight (b) &&
614           pango_font_description_get_style (a) == pango_font_description_get_style (b) &&
615           pango_font_description_get_stretch (a) == pango_font_description_get_stretch (b) &&
616           pango_font_description_get_variant (a) == pango_font_description_get_variant (b));
617 }
618
619 /* This fills the font style clist with all the possible style combinations
620    for the current font family. */
621 static void
622 gtk_font_selection_show_available_styles (GtkFontSelection *fontsel)
623 {
624   gint match_row = -1;
625   gint n_faces, i;
626   const gchar *str;
627   PangoFontDescription *old_desc;
628
629   if (fontsel->face)
630     old_desc = pango_font_face_describe (fontsel->face);
631   else
632     old_desc= NULL;
633
634   if (fontsel->faces)
635     g_free (fontsel->faces);
636   
637   pango_font_family_list_faces (fontsel->family, &fontsel->faces, &n_faces);
638   qsort (fontsel->faces, n_faces, sizeof (PangoFontFace *), faces_sort_func);
639
640   gtk_clist_freeze (GTK_CLIST (fontsel->font_style_clist));
641   gtk_clist_clear (GTK_CLIST (fontsel->font_style_clist));
642
643   for (i=0; i < n_faces; i++)
644     {
645       str = pango_font_face_get_face_name (fontsel->faces[i]);
646       gtk_clist_append (GTK_CLIST (fontsel->font_style_clist), (char **)&str);
647
648       if (old_desc)
649         {
650           PangoFontDescription *tmp_desc = pango_font_face_describe (fontsel->faces[i]);
651           
652           if (font_description_style_equal (tmp_desc, old_desc))
653             match_row = i;
654       
655           pango_font_description_free (tmp_desc);
656         }
657     }
658
659   if (old_desc)
660     pango_font_description_free (old_desc);
661
662   if (match_row < 0 && n_faces)
663     match_row = 0;
664   
665   if (match_row >= 0)
666     {
667       fontsel->face = fontsel->faces[match_row];
668       gtk_clist_select_row (GTK_CLIST (fontsel->font_style_clist), match_row, 0);
669
670       str = pango_font_face_get_face_name (fontsel->face);
671       gtk_entry_set_text (GTK_ENTRY (fontsel->font_style_entry), str);
672     }
673   else
674     fontsel->face = NULL;
675   
676   gtk_clist_thaw (GTK_CLIST (fontsel->font_style_clist));
677 }
678
679
680 /* This selects a style when the user selects a font. It just uses the first
681    available style at present. I was thinking of trying to maintain the
682    selected style, e.g. bold italic, when the user selects different fonts.
683    However, the interface is so easy to use now I'm not sure it's worth it.
684    Note: This will load a font. */
685 static void
686 gtk_font_selection_select_best_style (GtkFontSelection *fontsel,
687                                       gboolean          use_first)
688 {
689   gint best_row = 0;
690   
691   gtk_clist_select_row (GTK_CLIST (fontsel->font_style_clist), best_row, 0);
692   if (gtk_clist_row_is_visible (GTK_CLIST (fontsel->font_style_clist), best_row)
693       != GTK_VISIBILITY_FULL)
694     gtk_clist_moveto (GTK_CLIST (fontsel->font_style_clist), best_row, -1,
695                       0.5, 0);
696   gtk_font_selection_show_available_sizes (fontsel);
697   gtk_font_selection_select_best_size (fontsel);
698 }
699
700
701 /* This is called when a style is selected in the list. */
702 static void
703 gtk_font_selection_select_style (GtkWidget      *w,
704                                  gint           row,
705                                  gint           column,
706                                  GdkEventButton *bevent,
707                                  gpointer        data)
708 {
709   GtkFontSelection *fontsel = GTK_FONT_SELECTION (data);
710   gint index;
711   
712   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
713     gtk_widget_grab_focus (w);
714   
715   if (GTK_CLIST (fontsel->font_style_clist)->selection)
716     {
717       index = GPOINTER_TO_INT (GTK_CLIST (fontsel->font_style_clist)->selection->data);
718       fontsel->face = fontsel->faces[index];
719     }
720
721   gtk_font_selection_show_available_sizes (fontsel);
722   gtk_font_selection_select_best_size (fontsel);
723 }
724
725 static void
726 gtk_font_selection_show_available_sizes (GtkFontSelection *fontsel)
727 {
728   gint i;
729   gint current_size = fontsel->size;
730   gchar buffer[128];
731   gchar *size;
732
733   /* Insert the standard font sizes */
734   gtk_clist_freeze (GTK_CLIST (fontsel->size_clist));
735   gtk_clist_clear (GTK_CLIST (fontsel->size_clist));
736
737   for (i = 0; i < G_N_ELEMENTS (font_sizes); i++)
738     {
739       sprintf (buffer, "%i", font_sizes[i]);
740       size = buffer;
741       gtk_clist_append (GTK_CLIST (fontsel->size_clist), &size);
742       if (font_sizes[i] * PANGO_SCALE == current_size)
743         gtk_clist_select_row (GTK_CLIST (fontsel->size_clist), i, 0);
744     }
745   gtk_clist_thaw (GTK_CLIST (fontsel->size_clist));
746
747   sprintf (buffer, "%i", current_size / PANGO_SCALE);
748   gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
749 }
750
751 static void
752 gtk_font_selection_select_best_size (GtkFontSelection *fontsel)
753 {
754   gtk_font_selection_load_font (fontsel);  
755 }
756
757 /* If the user hits return in the font size entry, we change to the new font
758    size. */
759 static void
760 gtk_font_selection_size_activate (GtkWidget   *w,
761                                   gpointer     data)
762 {
763   GtkFontSelection *fontsel;
764   gint new_size;
765   const gchar *text;
766   
767   fontsel = GTK_FONT_SELECTION (data);
768
769   text = gtk_entry_get_text (GTK_ENTRY (fontsel->size_entry));
770   new_size = atoi (text) * PANGO_SCALE;
771   
772   if (fontsel->size != new_size)
773     {
774       fontsel->size = new_size;
775       gtk_font_selection_load_font (fontsel);
776     }
777 }
778
779 /* This is called when a size is selected in the list. */
780 static void
781 gtk_font_selection_select_size (GtkWidget      *w,
782                                 gint            row,
783                                 gint            column,
784                                 GdkEventButton *bevent,
785                                 gpointer        data)
786 {
787   GtkFontSelection *fontsel;
788   gint new_size;
789   gchar *text;
790   
791   fontsel = GTK_FONT_SELECTION (data);
792   
793   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
794     gtk_widget_grab_focus (w);
795   
796   gtk_clist_get_text (GTK_CLIST (fontsel->size_clist), row, 0, &text);
797   new_size = atoi (text) * PANGO_SCALE;
798   
799   if (fontsel->size != new_size)
800     {
801       /* If the size was selected by the user we set the selected_size. */
802       fontsel->size = new_size;
803       gtk_font_selection_load_font (fontsel);
804     }
805 }
806
807 static void
808 gtk_font_selection_load_font (GtkFontSelection *fontsel)
809 {
810   if (fontsel->font)
811     gdk_font_unref (fontsel->font);
812   fontsel->font = NULL;
813
814   gtk_font_selection_update_preview (fontsel);
815 }
816
817 static PangoFontDescription *
818 gtk_font_selection_get_font_description (GtkFontSelection *fontsel)
819 {
820   PangoFontDescription *font_desc = pango_font_face_describe (fontsel->face);
821   pango_font_description_set_size (font_desc, fontsel->size);
822
823   return font_desc;
824 }
825
826 /* This sets the font in the preview entry to the selected font, and tries to
827    make sure that the preview entry is a reasonable size, i.e. so that the
828    text can be seen with a bit of space to spare. But it tries to avoid
829    resizing the entry every time the font changes.
830    This also used to shrink the preview if the font size was decreased, but
831    that made it awkward if the user wanted to resize the window themself. */
832 static void
833 gtk_font_selection_update_preview (GtkFontSelection *fontsel)
834 {
835   GtkRcStyle *rc_style;
836   gint new_height;
837   GtkRequisition old_requisition;
838   GtkWidget *preview_entry = fontsel->preview_entry;
839   const gchar *text;
840
841   gtk_widget_get_child_requisition (preview_entry, &old_requisition);
842   
843   rc_style = gtk_rc_style_new ();
844   rc_style->font_desc = gtk_font_selection_get_font_description (fontsel);
845   
846   gtk_widget_modify_style (preview_entry, rc_style);
847   gtk_rc_style_unref (rc_style);
848
849   gtk_widget_size_request (preview_entry, NULL);
850   
851   /* We don't ever want to be over MAX_PREVIEW_HEIGHT pixels high. */
852   new_height = CLAMP (preview_entry->requisition.height, INITIAL_PREVIEW_HEIGHT, MAX_PREVIEW_HEIGHT);
853
854   if (new_height > old_requisition.height || new_height < old_requisition.height - 30)
855     gtk_widget_set_usize (preview_entry, -1, new_height);
856   
857   /* This sets the preview text, if it hasn't been set already. */
858   text = gtk_entry_get_text (GTK_ENTRY (preview_entry));
859   if (strlen (text) == 0)
860     gtk_entry_set_text (GTK_ENTRY (preview_entry), PREVIEW_TEXT);
861   gtk_entry_set_position (GTK_ENTRY (preview_entry), 0);
862 }
863
864 /*****************************************************************************
865  * These functions are the main public interface for getting/setting the font.
866  *****************************************************************************/
867
868 GdkFont*
869 gtk_font_selection_get_font (GtkFontSelection *fontsel)
870 {
871   if (!fontsel->font)
872     {
873       PangoFontDescription *font_desc = gtk_font_selection_get_font_description (fontsel);
874       fontsel->font = gdk_font_from_description (font_desc);
875       pango_font_description_free (font_desc);
876     }
877   
878   return fontsel->font;
879 }
880
881
882 gchar *
883 gtk_font_selection_get_font_name (GtkFontSelection *fontsel)
884 {
885   gchar *result;
886   
887   PangoFontDescription *font_desc = gtk_font_selection_get_font_description (fontsel);
888   result = pango_font_description_to_string (font_desc);
889   pango_font_description_free (font_desc);
890
891   return result;
892 }
893
894
895 /* This sets the current font, selecting the appropriate clist rows.
896    First we check the fontname is valid and try to find the font family
897    - i.e. the name in the main list. If we can't find that, then just return.
898    Next we try to set each of the properties according to the fontname.
899    Finally we select the font family & style in the clists. */
900 gboolean
901 gtk_font_selection_set_font_name (GtkFontSelection *fontsel,
902                                   const gchar      *fontname)
903 {
904   PangoFontFamily *new_family = NULL;
905   PangoFontFace *new_face = NULL;
906   PangoFontDescription *new_desc;
907   gint n_families, n_faces, i;
908   
909   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), FALSE);
910   
911   new_desc = pango_font_description_from_string (fontname);
912
913   /* Check to make sure that this is in the list of allowed fonts */
914
915   n_families = GTK_CLIST (fontsel->font_clist)->rows;
916   for (i = 0; i < n_families; i++)
917     {
918       if (g_strcasecmp (pango_font_family_get_name (fontsel->families[i]),
919                       pango_font_description_get_family (new_desc)) == 0)
920         new_family = fontsel->families[i];
921     }
922
923   if (!new_family)
924     return FALSE;
925
926   fontsel->family = new_family;
927   gtk_font_selection_show_available_styles (fontsel);
928
929   n_faces = GTK_CLIST (fontsel->font_style_clist)->rows;
930   for (i=0; i < n_faces; i++)
931     {
932       PangoFontDescription *tmp_desc = pango_font_face_describe (fontsel->faces[i]);
933       
934       if (font_description_style_equal (tmp_desc, new_desc))
935         new_face = fontsel->faces[i];
936
937       pango_font_description_free (tmp_desc);
938
939       if (new_face)
940         break;
941     }
942
943   if (!new_face)
944     new_face = fontsel->faces[0];
945
946   fontsel->face = new_face;
947   gtk_font_selection_select_best_size (fontsel);
948
949   g_object_freeze_notify (G_OBJECT (fontsel));
950   g_object_notify (G_OBJECT (fontsel), "font_name");
951   g_object_notify (G_OBJECT (fontsel), "font");
952   g_object_thaw_notify (G_OBJECT (fontsel));
953
954   return TRUE;
955 }
956
957
958 /* This returns the text in the preview entry. You should copy the returned
959    text if you need it. */
960 G_CONST_RETURN gchar*
961 gtk_font_selection_get_preview_text  (GtkFontSelection *fontsel)
962 {
963   return gtk_entry_get_text (GTK_ENTRY (fontsel->preview_entry));
964 }
965
966
967 /* This sets the text in the preview entry. */
968 void
969 gtk_font_selection_set_preview_text  (GtkFontSelection *fontsel,
970                                       const gchar         *text)
971 {
972   gtk_entry_set_text (GTK_ENTRY (fontsel->preview_entry), text);
973 }
974
975 /*****************************************************************************
976  * GtkFontSelectionDialog
977  *****************************************************************************/
978
979 GtkType
980 gtk_font_selection_dialog_get_type (void)
981 {
982   static GtkType font_selection_dialog_type = 0;
983   
984   if (!font_selection_dialog_type)
985     {
986       GtkTypeInfo fontsel_diag_info =
987       {
988         "GtkFontSelectionDialog",
989         sizeof (GtkFontSelectionDialog),
990         sizeof (GtkFontSelectionDialogClass),
991         (GtkClassInitFunc) gtk_font_selection_dialog_class_init,
992         (GtkObjectInitFunc) gtk_font_selection_dialog_init,
993         /* reserved_1 */ NULL,
994         /* reserved_2 */ NULL,
995         (GtkClassInitFunc) NULL,
996       };
997       
998       font_selection_dialog_type = gtk_type_unique (GTK_TYPE_DIALOG,
999                                                     &fontsel_diag_info);
1000     }
1001   
1002   return font_selection_dialog_type;
1003 }
1004
1005 static void
1006 gtk_font_selection_dialog_class_init (GtkFontSelectionDialogClass *klass)
1007 {
1008   GtkObjectClass *object_class;
1009   
1010   object_class = (GtkObjectClass*) klass;
1011   
1012   font_selection_dialog_parent_class = gtk_type_class (GTK_TYPE_DIALOG);
1013 }
1014
1015 static void
1016 gtk_font_selection_dialog_init (GtkFontSelectionDialog *fontseldiag)
1017 {
1018   GtkDialog *dialog;
1019
1020   dialog = GTK_DIALOG (fontseldiag);
1021   
1022   fontseldiag->dialog_width = -1;
1023   fontseldiag->auto_resize = TRUE;
1024   
1025   gtk_widget_set_events (GTK_WIDGET (fontseldiag), GDK_STRUCTURE_MASK);
1026   gtk_signal_connect (GTK_OBJECT (fontseldiag), "configure_event",
1027                       (GtkSignalFunc) gtk_font_selection_dialog_on_configure,
1028                       fontseldiag);
1029   
1030   gtk_container_set_border_width (GTK_CONTAINER (fontseldiag), 4);
1031   gtk_window_set_policy (GTK_WINDOW (fontseldiag), FALSE, TRUE, TRUE);
1032   
1033   fontseldiag->main_vbox = dialog->vbox;
1034   
1035   fontseldiag->fontsel = gtk_font_selection_new ();
1036   gtk_container_set_border_width (GTK_CONTAINER (fontseldiag->fontsel), 4);
1037   gtk_widget_show (fontseldiag->fontsel);
1038   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
1039                       fontseldiag->fontsel, TRUE, TRUE, 0);
1040   
1041   /* Create the action area */
1042   fontseldiag->action_area = dialog->action_area;
1043
1044   fontseldiag->cancel_button = gtk_dialog_add_button (dialog,
1045                                                       GTK_STOCK_CANCEL,
1046                                                       GTK_RESPONSE_CANCEL);
1047
1048   fontseldiag->apply_button = gtk_dialog_add_button (dialog,
1049                                                      GTK_STOCK_APPLY,
1050                                                      GTK_RESPONSE_APPLY);
1051   gtk_widget_hide (fontseldiag->apply_button);
1052
1053   fontseldiag->ok_button = gtk_dialog_add_button (dialog,
1054                                                   GTK_STOCK_OK,
1055                                                   GTK_RESPONSE_OK);
1056   gtk_widget_grab_default (fontseldiag->ok_button);
1057   
1058   gtk_window_set_title (GTK_WINDOW (fontseldiag),
1059                         _("Font Selection"));
1060
1061 }
1062
1063 GtkWidget*
1064 gtk_font_selection_dialog_new (const gchar *title)
1065 {
1066   GtkFontSelectionDialog *fontseldiag;
1067   
1068   fontseldiag = gtk_type_new (GTK_TYPE_FONT_SELECTION_DIALOG);
1069
1070   if (title)
1071     gtk_window_set_title (GTK_WINDOW (fontseldiag), title);
1072   
1073   return GTK_WIDGET (fontseldiag);
1074 }
1075
1076 gchar*
1077 gtk_font_selection_dialog_get_font_name (GtkFontSelectionDialog *fsd)
1078 {
1079   return gtk_font_selection_get_font_name (GTK_FONT_SELECTION (fsd->fontsel));
1080 }
1081
1082 GdkFont*
1083 gtk_font_selection_dialog_get_font (GtkFontSelectionDialog *fsd)
1084 {
1085   return gtk_font_selection_get_font (GTK_FONT_SELECTION (fsd->fontsel));
1086 }
1087
1088 gboolean
1089 gtk_font_selection_dialog_set_font_name (GtkFontSelectionDialog *fsd,
1090                                          const gchar      *fontname)
1091 {
1092   return gtk_font_selection_set_font_name (GTK_FONT_SELECTION (fsd->fontsel), fontname);
1093 }
1094
1095 G_CONST_RETURN gchar*
1096 gtk_font_selection_dialog_get_preview_text (GtkFontSelectionDialog *fsd)
1097 {
1098   return gtk_font_selection_get_preview_text (GTK_FONT_SELECTION (fsd->fontsel));
1099 }
1100
1101 void
1102 gtk_font_selection_dialog_set_preview_text (GtkFontSelectionDialog *fsd,
1103                                             const gchar            *text)
1104 {
1105   gtk_font_selection_set_preview_text (GTK_FONT_SELECTION (fsd->fontsel), text);
1106 }
1107
1108
1109 /* This turns auto-shrink off if the user resizes the width of the dialog.
1110    It also turns it back on again if the user resizes it back to its normal
1111    width. */
1112 static gint
1113 gtk_font_selection_dialog_on_configure (GtkWidget              *widget,
1114                                         GdkEventConfigure      *event,
1115                                         GtkFontSelectionDialog *fsd)
1116 {
1117   /* This sets the initial width. */
1118   if (fsd->dialog_width == -1)
1119     fsd->dialog_width = event->width;
1120   else if (fsd->auto_resize && fsd->dialog_width != event->width)
1121     {
1122       fsd->auto_resize = FALSE;
1123       gtk_window_set_policy (GTK_WINDOW (fsd), FALSE, TRUE, FALSE);
1124     }
1125   else if (!fsd->auto_resize && fsd->dialog_width == event->width)
1126     {
1127       fsd->auto_resize = TRUE;
1128       gtk_window_set_policy (GTK_WINDOW (fsd), FALSE, TRUE, TRUE);
1129     }
1130   
1131   return FALSE;
1132 }