]> Pileus Git - ~andy/gtk/blob - gtk/gtkfontsel.c
1ddbd2eb8858a56c1862cf1788ac51211bd856f3
[~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 static void    gtk_font_selection_class_init         (GtkFontSelectionClass *klass);
80 static void    gtk_font_selection_init               (GtkFontSelection      *fontsel);
81 static void    gtk_font_selection_finalize           (GObject               *object);
82
83 /* These are the callbacks & related functions. */
84 static void     gtk_font_selection_select_font           (GtkWidget        *w,
85                                                           gint              row,
86                                                           gint              column,
87                                                           GdkEventButton   *bevent,
88                                                           gpointer          data);
89 static void     gtk_font_selection_show_available_fonts  (GtkFontSelection *fs);
90
91 static void     gtk_font_selection_show_available_styles (GtkFontSelection *fs);
92 static void     gtk_font_selection_select_best_style     (GtkFontSelection *fs,
93                                                           gboolean          use_first);
94 static void     gtk_font_selection_select_style          (GtkWidget        *w,
95                                                           gint              row,
96                                                           gint              column,
97                                                           GdkEventButton   *bevent,
98                                                           gpointer          data);
99
100 static void     gtk_font_selection_select_best_size      (GtkFontSelection *fs);
101 static void     gtk_font_selection_show_available_sizes  (GtkFontSelection *fs);
102 static void     gtk_font_selection_size_activate         (GtkWidget        *w,
103                                                           gpointer          data);
104 static void     gtk_font_selection_select_size           (GtkWidget        *w,
105                                                           gint              row,
106                                                           gint              column,
107                                                           GdkEventButton   *bevent,
108                                                           gpointer          data);
109
110 static void     gtk_font_selection_expose_list           (GtkWidget        *w,
111                                                           GdkEventExpose   *event,
112                                                           gpointer          data);
113
114
115 /* Misc. utility functions. */
116 static void     gtk_font_selection_load_font         (GtkFontSelection *fs);
117 static void    gtk_font_selection_update_preview     (GtkFontSelection *fs);
118
119 /* FontSelectionDialog */
120 static void    gtk_font_selection_dialog_class_init  (GtkFontSelectionDialogClass *klass);
121 static void    gtk_font_selection_dialog_init        (GtkFontSelectionDialog *fontseldiag);
122
123 static gint    gtk_font_selection_dialog_on_configure(GtkWidget      *widget,
124                                                       GdkEventConfigure *event,
125                                                       GtkFontSelectionDialog *fsd);
126
127 static GtkWindowClass *font_selection_parent_class = NULL;
128 static GtkVBoxClass *font_selection_dialog_parent_class = NULL;
129
130 GtkType
131 gtk_font_selection_get_type()
132 {
133   static GtkType font_selection_type = 0;
134   
135   if(!font_selection_type)
136     {
137       static const GtkTypeInfo fontsel_type_info =
138       {
139         "GtkFontSelection",
140         sizeof (GtkFontSelection),
141         sizeof (GtkFontSelectionClass),
142         (GtkClassInitFunc) gtk_font_selection_class_init,
143         (GtkObjectInitFunc) gtk_font_selection_init,
144         /* reserved_1 */ NULL,
145         /* reserved_2 */ NULL,
146         (GtkClassInitFunc) NULL,
147       };
148       
149       font_selection_type = gtk_type_unique (GTK_TYPE_VBOX,
150                                              &fontsel_type_info);
151     }
152   
153   return font_selection_type;
154 }
155
156 static void
157 gtk_font_selection_class_init(GtkFontSelectionClass *klass)
158 {
159   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
160   
161   font_selection_parent_class = gtk_type_class (GTK_TYPE_VBOX);
162   
163   gobject_class->finalize = gtk_font_selection_finalize;
164 }
165
166 static void
167 gtk_font_selection_init(GtkFontSelection *fontsel)
168 {
169   GtkWidget *scrolled_win;
170   GtkWidget *text_frame;
171   GtkWidget *text_box;
172   GtkWidget *table, *label;
173
174   fontsel->font_desc = pango_font_description_from_string ("sans 12");
175   
176   /* Create the table of font, style & size. */
177   table = gtk_table_new (3, 3, FALSE);
178   gtk_widget_show (table);
179   gtk_table_set_col_spacings(GTK_TABLE(table), 8);
180   gtk_box_pack_start (GTK_BOX (fontsel), table, TRUE, TRUE, 0);
181
182   fontsel->font_entry = gtk_entry_new ();
183   gtk_entry_set_editable(GTK_ENTRY(fontsel->font_entry), FALSE);
184   gtk_widget_set_usize (fontsel->font_entry, 20, -1);
185   gtk_widget_show (fontsel->font_entry);
186   gtk_table_attach (GTK_TABLE (table), fontsel->font_entry, 0, 1, 1, 2,
187                     GTK_FILL, 0, 0, 0);
188   
189   fontsel->font_style_entry = gtk_entry_new ();
190   gtk_entry_set_editable (GTK_ENTRY(fontsel->font_style_entry), FALSE);
191   gtk_widget_set_usize (fontsel->font_style_entry, 20, -1);
192   gtk_widget_show (fontsel->font_style_entry);
193   gtk_table_attach (GTK_TABLE (table), fontsel->font_style_entry, 1, 2, 1, 2,
194                     GTK_FILL, 0, 0, 0);
195   
196   fontsel->size_entry = gtk_entry_new ();
197   gtk_widget_set_usize (fontsel->size_entry, 20, -1);
198   gtk_widget_show (fontsel->size_entry);
199   gtk_table_attach (GTK_TABLE (table), fontsel->size_entry, 2, 3, 1, 2,
200                     GTK_FILL, 0, 0, 0);
201   gtk_signal_connect (GTK_OBJECT (fontsel->size_entry), "activate",
202                       (GtkSignalFunc) gtk_font_selection_size_activate,
203                       fontsel);
204   
205   fontsel->font_label = gtk_label_new_with_mnemonic (_("_Family:"));
206   gtk_label_set_mnemonic_widget (GTK_LABEL (fontsel->font_label),
207                                  fontsel->font_entry);
208   gtk_misc_set_alignment (GTK_MISC (fontsel->font_label), 0.0, 0.5);
209   gtk_widget_show (fontsel->font_label);
210   gtk_table_attach (GTK_TABLE (table), fontsel->font_label, 0, 1, 0, 1,
211                     GTK_FILL, 0, 0, 0);  
212   label = gtk_label_new_with_mnemonic (_("_Style:"));
213   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
214                                  fontsel->font_style_entry);
215   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
216   gtk_widget_show (label);
217   gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1,
218                     GTK_FILL, 0, 0, 0);
219   label = gtk_label_new_with_mnemonic (_("Si_ze:"));
220   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
221                                  fontsel->size_entry);
222   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
223   gtk_widget_show (label);
224   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
225                     GTK_FILL, 0, 0, 0);
226   
227   
228   /* Create the clists  */
229   fontsel->font_clist = gtk_clist_new (1);
230   gtk_clist_column_titles_hide (GTK_CLIST (fontsel->font_clist));
231   gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_clist), 0, TRUE);
232   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
233   gtk_widget_set_usize (scrolled_win, FONT_LIST_WIDTH, FONT_LIST_HEIGHT);
234   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->font_clist);
235   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
236                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
237   gtk_widget_show(fontsel->font_clist);
238   gtk_widget_show(scrolled_win);
239
240   gtk_table_attach (GTK_TABLE (table), scrolled_win, 0, 1, 2, 3,
241                     GTK_EXPAND | GTK_FILL,
242                     GTK_EXPAND | GTK_FILL, 0, 0);
243   
244   fontsel->font_style_clist = gtk_clist_new (1);
245   gtk_clist_column_titles_hide (GTK_CLIST (fontsel->font_style_clist));
246   gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_style_clist),
247                                     0, TRUE);
248   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
249   gtk_widget_set_usize (scrolled_win, FONT_STYLE_LIST_WIDTH, -1);
250   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->font_style_clist);
251   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
252                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
253   gtk_widget_show(fontsel->font_style_clist);
254   gtk_widget_show(scrolled_win);
255   gtk_table_attach (GTK_TABLE (table), scrolled_win, 1, 2, 2, 3,
256                     GTK_EXPAND | GTK_FILL,
257                     GTK_EXPAND | GTK_FILL, 0, 0);
258   
259   fontsel->size_clist = gtk_clist_new (1);
260   gtk_clist_column_titles_hide (GTK_CLIST(fontsel->size_clist));
261   gtk_clist_set_column_width (GTK_CLIST(fontsel->size_clist), 0, 20);
262   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
263   gtk_widget_set_usize (scrolled_win, FONT_SIZE_LIST_WIDTH, -1);
264   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->size_clist);
265   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
266                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
267   gtk_widget_show (fontsel->size_clist);
268   gtk_widget_show (scrolled_win);
269   gtk_table_attach (GTK_TABLE (table), scrolled_win, 2, 3, 2, 3,
270                     GTK_FILL, GTK_FILL, 0, 0);
271   
272   /* Insert the fonts. If there exist fonts with the same family but
273      different foundries, then the foundry name is appended in brackets. */
274   gtk_font_selection_show_available_fonts(fontsel);
275   
276   gtk_signal_connect (GTK_OBJECT (fontsel->font_clist), "select_row",
277                       GTK_SIGNAL_FUNC(gtk_font_selection_select_font),
278                       fontsel);
279   GTK_WIDGET_SET_FLAGS (fontsel->font_clist, GTK_CAN_FOCUS);
280
281   gtk_signal_connect_after (GTK_OBJECT (fontsel->font_clist), "expose_event",
282                             GTK_SIGNAL_FUNC(gtk_font_selection_expose_list),
283                             fontsel);
284   
285   gtk_font_selection_show_available_styles (fontsel);
286   
287   gtk_signal_connect (GTK_OBJECT (fontsel->font_style_clist), "select_row",
288                       GTK_SIGNAL_FUNC(gtk_font_selection_select_style),
289                       fontsel);
290   GTK_WIDGET_SET_FLAGS (fontsel->font_style_clist, GTK_CAN_FOCUS);
291
292   gtk_font_selection_show_available_sizes (fontsel);
293   
294   gtk_signal_connect (GTK_OBJECT (fontsel->size_clist), "select_row",
295                       GTK_SIGNAL_FUNC(gtk_font_selection_select_size),
296                       fontsel);
297   GTK_WIDGET_SET_FLAGS (fontsel->size_clist, GTK_CAN_FOCUS);
298   
299   /* create the text entry widget */
300   text_frame = gtk_frame_new (_("Preview:"));
301   gtk_widget_show (text_frame);
302   gtk_frame_set_shadow_type(GTK_FRAME(text_frame), GTK_SHADOW_ETCHED_IN);
303   gtk_box_pack_start (GTK_BOX (fontsel), text_frame,
304                       FALSE, TRUE, 0);
305   
306   /* This is just used to get a 4-pixel space around the preview entry. */
307   text_box = gtk_hbox_new (FALSE, 0);
308   gtk_widget_show (text_box);
309   gtk_container_add (GTK_CONTAINER (text_frame), text_box);
310   gtk_container_set_border_width (GTK_CONTAINER (text_box), 4);
311   
312   fontsel->preview_entry = gtk_entry_new ();
313   gtk_widget_show (fontsel->preview_entry);
314   gtk_widget_set_usize (fontsel->preview_entry, -1, INITIAL_PREVIEW_HEIGHT);
315   gtk_box_pack_start (GTK_BOX (text_box), fontsel->preview_entry,
316                       TRUE, TRUE, 0);
317
318   gtk_font_selection_update_preview (fontsel);
319 }
320
321 GtkWidget *
322 gtk_font_selection_new()
323 {
324   GtkFontSelection *fontsel;
325   
326   fontsel = gtk_type_new (GTK_TYPE_FONT_SELECTION);
327   
328   return GTK_WIDGET (fontsel);
329 }
330
331 static void
332 gtk_font_selection_finalize (GObject *object)
333 {
334   GtkFontSelection *fontsel;
335   
336   g_return_if_fail (object != NULL);
337   g_return_if_fail (GTK_IS_FONT_SELECTION (object));
338   
339   fontsel = GTK_FONT_SELECTION (object);
340
341   pango_font_description_free (fontsel->font_desc);
342
343   if (fontsel->font)
344     gdk_font_unref (fontsel->font);
345   
346   if (G_OBJECT_CLASS (font_selection_parent_class)->finalize)
347     (* G_OBJECT_CLASS (font_selection_parent_class)->finalize) (object);
348 }
349
350
351 /* This is called when the clist is exposed. Here we scroll to the current
352    font if necessary. */
353 static void
354 gtk_font_selection_expose_list (GtkWidget               *widget,
355                                 GdkEventExpose          *event,
356                                 gpointer                 data)
357 {
358   GtkFontSelection *fontsel;
359   GList *selection;
360   gint index;
361   
362 #ifdef FONTSEL_DEBUG
363   g_message("In expose_list\n");
364 #endif
365   fontsel = GTK_FONT_SELECTION(data);
366   
367   /* Try to scroll the font family clist to the selected item */
368   selection = GTK_CLIST(fontsel->font_clist)->selection;
369   if (selection)
370     {
371       index = GPOINTER_TO_INT (selection->data);
372       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_clist), index)
373           != GTK_VISIBILITY_FULL)
374         gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), index, -1, 0.5, 0);
375     }
376       
377   /* Try to scroll the font style clist to the selected item */
378   selection = GTK_CLIST(fontsel->font_style_clist)->selection;
379   if (selection)
380     {
381       index = GPOINTER_TO_INT (selection->data);
382       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist), index)
383           != GTK_VISIBILITY_FULL)
384         gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), index, -1,
385                          0.5, 0);
386     }
387       
388   /* Try to scroll the font size clist to the selected item */
389   selection = GTK_CLIST(fontsel->size_clist)->selection;
390   if (selection)
391     {
392       index = GPOINTER_TO_INT (selection->data);
393       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->size_clist), index)
394           != GTK_VISIBILITY_FULL)
395       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), index, -1, 0.5, 0);
396     }
397 }
398
399 /* This is called when a family is selected in the list. */
400 static void
401 gtk_font_selection_select_font (GtkWidget      *w,
402                                 gint            row,
403                                 gint            column,
404                                 GdkEventButton *bevent,
405                                 gpointer        data)
406 {
407   GtkFontSelection *fontsel;
408   gchar *family_name;
409   gint index;
410   
411   fontsel = GTK_FONT_SELECTION (data);
412
413   if (GTK_CLIST (fontsel->font_clist)->selection)
414     {
415       index = GPOINTER_TO_INT (GTK_CLIST (fontsel->font_clist)->selection->data);
416
417       if (gtk_clist_get_text (GTK_CLIST (fontsel->font_clist), index, 0, &family_name) &&
418           strcasecmp (fontsel->font_desc->family_name, family_name) != 0)
419         {
420           g_free (fontsel->font_desc->family_name);
421           fontsel->font_desc->family_name  = g_strdup (family_name);
422
423           gtk_entry_set_text (GTK_ENTRY (fontsel->font_entry), family_name);
424           
425           gtk_font_selection_show_available_styles (fontsel);
426           gtk_font_selection_select_best_style (fontsel, TRUE);
427         }
428     }
429 }
430
431 static int
432 cmp_strings (const void *a, const void *b)
433 {
434   return strcasecmp (*(const char **)a, *(const char **)b);
435 }
436
437 static void
438 gtk_font_selection_show_available_fonts (GtkFontSelection *fontsel)
439 {
440   gchar **families;
441   int n_families, i;
442
443   pango_context_list_families (gtk_widget_get_pango_context (GTK_WIDGET (fontsel)),
444                                &families, &n_families);
445   qsort (families, n_families, sizeof(char *), cmp_strings);
446
447   gtk_clist_freeze (GTK_CLIST (fontsel->font_clist));
448   gtk_clist_clear (GTK_CLIST (fontsel->font_clist));
449
450   for (i=0; i<n_families; i++)
451     {
452       gtk_clist_append (GTK_CLIST (fontsel->font_clist), &families[i]);
453
454       if (!strcasecmp (families[i], fontsel->font_desc->family_name))
455         {
456           gtk_clist_select_row (GTK_CLIST(fontsel->font_clist), i, 0);
457           gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), families[i]);
458         }
459     }
460   
461   gtk_clist_thaw (GTK_CLIST(fontsel->font_clist));
462
463   pango_font_map_free_families (families, n_families);
464 }
465
466 static int
467 compare_font_descriptions (const PangoFontDescription *a, const PangoFontDescription *b)
468 {
469   int val = strcasecmp (a->family_name, b->family_name);
470   if (val != 0)
471     return val;
472
473   if (a->weight != b->weight)
474     return a->weight - b->weight;
475
476   if (a->style != b->style)
477     return a->style - b->style;
478   
479   if (a->stretch != b->stretch)
480     return a->stretch - b->stretch;
481
482   if (a->variant != b->variant)
483     return a->variant - b->variant;
484
485   return 0;
486 }
487
488 static int
489 font_description_sort_func (const void *a, const void *b)
490 {
491   return compare_font_descriptions (*(PangoFontDescription **)a, *(PangoFontDescription **)b);
492 }
493
494 /* This fills the font style clist with all the possible style combinations
495    for the current font family. */
496 static void
497 gtk_font_selection_show_available_styles (GtkFontSelection *fontsel)
498 {
499   PangoFontDescription **descs;
500   int n_descs, i;
501   gint match_row = -1;
502   gchar *str;
503   
504   pango_context_list_fonts (gtk_widget_get_pango_context (GTK_WIDGET (fontsel)),
505                             fontsel->font_desc->family_name, &descs, &n_descs);
506   qsort (descs, n_descs, sizeof(PangoFontDescription *), font_description_sort_func);
507
508   gtk_clist_freeze (GTK_CLIST (fontsel->font_style_clist));
509   gtk_clist_clear (GTK_CLIST (fontsel->font_style_clist));
510
511   for (i=0; i<n_descs; i++)
512     {
513       PangoFontDescription tmp_desc;
514
515       tmp_desc = *descs[i];
516       tmp_desc.family_name = NULL;
517       tmp_desc.size = 0;
518
519       str = pango_font_description_to_string (&tmp_desc);
520       gtk_clist_append (GTK_CLIST (fontsel->font_style_clist), &str);
521
522       if (descs[i]->weight == fontsel->font_desc->weight &&
523           descs[i]->style == fontsel->font_desc->style &&
524           descs[i]->stretch == fontsel->font_desc->stretch &&
525           descs[i]->variant == fontsel->font_desc->variant)
526         match_row = i;
527       
528       g_free (str);
529     }
530
531   gtk_clist_select_row (GTK_CLIST (fontsel->font_style_clist), match_row, 0);
532   if (match_row >= 0)
533     {
534       gtk_clist_get_text (GTK_CLIST (fontsel->font_style_clist), match_row, 0, &str);
535       gtk_entry_set_text (GTK_ENTRY (fontsel->font_style_entry), str);
536     }
537   
538   gtk_clist_thaw (GTK_CLIST(fontsel->font_style_clist));
539
540   pango_font_descriptions_free (descs, n_descs);
541 }
542
543
544 /* This selects a style when the user selects a font. It just uses the first
545    available style at present. I was thinking of trying to maintain the
546    selected style, e.g. bold italic, when the user selects different fonts.
547    However, the interface is so easy to use now I'm not sure it's worth it.
548    Note: This will load a font. */
549 static void
550 gtk_font_selection_select_best_style(GtkFontSelection *fontsel,
551                                      gboolean          use_first)
552 {
553   gint best_row = 0;
554   
555   gtk_clist_select_row(GTK_CLIST(fontsel->font_style_clist), best_row, 0);
556   if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist), best_row)
557       != GTK_VISIBILITY_FULL)
558     gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), best_row, -1,
559                      0.5, 0);
560   gtk_font_selection_show_available_sizes (fontsel);
561   gtk_font_selection_select_best_size (fontsel);
562 }
563
564
565 /* This is called when a style is selected in the list. */
566 static void
567 gtk_font_selection_select_style (GtkWidget      *w,
568                                  gint           row,
569                                  gint           column,
570                                  GdkEventButton *bevent,
571                                  gpointer        data)
572 {
573   GtkFontSelection *fontsel = GTK_FONT_SELECTION (data);
574   PangoFontDescription *tmp_desc;
575   gchar *text;
576   gint index;
577   
578   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
579     gtk_widget_grab_focus (w);
580   
581   if (GTK_CLIST (fontsel->font_style_clist)->selection)
582     {
583       index = GPOINTER_TO_INT (GTK_CLIST (fontsel->font_style_clist)->selection->data);
584
585       if (gtk_clist_get_text (GTK_CLIST (fontsel->font_style_clist), index, 0, &text))
586         {
587           tmp_desc = pango_font_description_from_string (text);
588           
589           fontsel->font_desc->style = tmp_desc->style;
590           fontsel->font_desc->variant = tmp_desc->variant;
591           fontsel->font_desc->weight = tmp_desc->weight;
592           fontsel->font_desc->stretch = tmp_desc->stretch;
593           
594           pango_font_description_free (tmp_desc);
595         }
596     }
597
598   gtk_font_selection_show_available_sizes (fontsel);
599   gtk_font_selection_select_best_size (fontsel);
600 }
601
602 static void
603 gtk_font_selection_show_available_sizes (GtkFontSelection *fontsel)
604 {
605   gint i;
606   gchar buffer[128];
607   gchar *size;
608
609   /* Insert the standard font sizes */
610   gtk_clist_freeze (GTK_CLIST (fontsel->size_clist));
611   gtk_clist_clear (GTK_CLIST (fontsel->size_clist));
612
613   for (i = 0; i < G_N_ELEMENTS (font_sizes); i++)
614     {
615       sprintf(buffer, "%i", font_sizes[i]);
616       size = buffer;
617       gtk_clist_append (GTK_CLIST(fontsel->size_clist), &size);
618       if (font_sizes[i] * PANGO_SCALE == fontsel->font_desc->size)
619         gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), i, 0);
620     }
621   gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
622
623   sprintf (buffer, "%i", fontsel->font_desc->size / PANGO_SCALE);
624   gtk_entry_set_text (GTK_ENTRY(fontsel->size_entry), buffer);
625 }
626
627 static void
628 gtk_font_selection_select_best_size (GtkFontSelection *fontsel)
629 {
630   gtk_font_selection_load_font (fontsel);  
631 }
632
633 /* If the user hits return in the font size entry, we change to the new font
634    size. */
635 static void
636 gtk_font_selection_size_activate (GtkWidget   *w,
637                                   gpointer     data)
638 {
639   GtkFontSelection *fontsel;
640   gint new_size;
641   gchar *text;
642   
643   fontsel = GTK_FONT_SELECTION (data);
644
645   text = gtk_entry_get_text (GTK_ENTRY (fontsel->size_entry));
646   new_size = atoi (text) * PANGO_SCALE;
647   
648   if (fontsel->font_desc->size != new_size)
649     {
650       fontsel->font_desc->size = new_size;
651       gtk_font_selection_load_font (fontsel);
652     }
653 }
654
655 /* This is called when a size is selected in the list. */
656 static void
657 gtk_font_selection_select_size (GtkWidget      *w,
658                                 gint            row,
659                                 gint            column,
660                                 GdkEventButton *bevent,
661                                 gpointer        data)
662 {
663   GtkFontSelection *fontsel;
664   gint new_size;
665   gchar *text;
666   
667   fontsel = GTK_FONT_SELECTION (data);
668   
669   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
670     gtk_widget_grab_focus (w);
671   
672   gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
673   new_size = atoi (text) * PANGO_SCALE;
674   
675   if (fontsel->font_desc->size != new_size)
676     {
677       /* If the size was selected by the user we set the selected_size. */
678       fontsel->font_desc->size = new_size;
679
680       gtk_font_selection_load_font (fontsel);
681     }
682 }
683
684 static void
685 gtk_font_selection_load_font (GtkFontSelection *fontsel)
686 {
687   if (fontsel->font)
688     gdk_font_unref (fontsel->font);
689   fontsel->font = NULL;
690
691   gtk_font_selection_update_preview (fontsel);
692 }
693
694 /* This sets the font in the preview entry to the selected font, and tries to
695    make sure that the preview entry is a reasonable size, i.e. so that the
696    text can be seen with a bit of space to spare. But it tries to avoid
697    resizing the entry every time the font changes.
698    This also used to shrink the preview if the font size was decreased, but
699    that made it awkward if the user wanted to resize the window themself. */
700 static void
701 gtk_font_selection_update_preview (GtkFontSelection *fontsel)
702 {
703   GtkRcStyle *rc_style;
704   gint new_height;
705   GtkRequisition old_requisition;
706   GtkWidget *preview_entry = fontsel->preview_entry;
707   gchar *text;
708
709   gtk_widget_get_child_requisition (preview_entry, &old_requisition);
710   
711   rc_style = gtk_rc_style_new ();
712   rc_style->font_desc = pango_font_description_copy (fontsel->font_desc);
713   gtk_widget_modify_style (preview_entry, rc_style);
714   gtk_rc_style_unref (rc_style);
715
716   gtk_widget_size_request (preview_entry, NULL);
717   
718   /* We don't ever want to be over MAX_PREVIEW_HEIGHT pixels high. */
719   new_height = CLAMP (preview_entry->requisition.height, INITIAL_PREVIEW_HEIGHT, MAX_PREVIEW_HEIGHT);
720
721   if (new_height > old_requisition.height || new_height < old_requisition.height - 30)
722     gtk_widget_set_usize(preview_entry, -1, new_height);
723   
724   /* This sets the preview text, if it hasn't been set already. */
725   text = gtk_entry_get_text(GTK_ENTRY(preview_entry));
726   if (strlen(text) == 0)
727     gtk_entry_set_text(GTK_ENTRY(preview_entry), PREVIEW_TEXT);
728   gtk_entry_set_position(GTK_ENTRY(preview_entry), 0);
729 }
730
731 /*****************************************************************************
732  * These functions are the main public interface for getting/setting the font.
733  *****************************************************************************/
734
735 GdkFont*
736 gtk_font_selection_get_font (GtkFontSelection *fontsel)
737 {
738   if (!fontsel->font)
739     fontsel->font = gdk_font_from_description (fontsel->font_desc);
740   
741   return fontsel->font;
742 }
743
744
745 gchar *
746 gtk_font_selection_get_font_name (GtkFontSelection *fontsel)
747 {
748   return pango_font_description_to_string (fontsel->font_desc);
749 }
750
751
752 /* This sets the current font, selecting the appropriate clist rows.
753    First we check the fontname is valid and try to find the font family
754    - i.e. the name in the main list. If we can't find that, then just return.
755    Next we try to set each of the properties according to the fontname.
756    Finally we select the font family & style in the clists. */
757 gboolean
758 gtk_font_selection_set_font_name (GtkFontSelection *fontsel,
759                                   const gchar      *fontname)
760 {
761   PangoFontDescription *new_desc;
762   PangoFontDescription **descs;
763   int n_descs, i;
764   gboolean found = FALSE;
765
766   g_return_val_if_fail (fontsel != NULL, FALSE);
767   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), FALSE);
768   
769   new_desc = pango_font_description_from_string (fontname);
770
771   /* Check to make sure that this is in the list of allowed fonts */
772
773   pango_context_list_fonts (gtk_widget_get_pango_context (GTK_WIDGET (fontsel)),
774                             new_desc->family_name, &descs, &n_descs);
775
776   for (i=0; i<n_descs; i++)
777     {
778       if (descs[i]->weight == new_desc->weight &&
779           descs[i]->style == new_desc->style &&
780           descs[i]->stretch == new_desc->stretch &&
781           descs[i]->variant == new_desc->variant)
782         {
783           found = TRUE;
784           break;
785         }
786     }
787
788   pango_font_descriptions_free (descs, n_descs);
789
790   if (!found)
791     return FALSE;
792
793   pango_font_description_free (fontsel->font_desc);
794   fontsel->font_desc = new_desc;
795
796   return TRUE;
797 }
798
799
800 /* This returns the text in the preview entry. You should copy the returned
801    text if you need it. */
802 gchar*
803 gtk_font_selection_get_preview_text  (GtkFontSelection *fontsel)
804 {
805   return gtk_entry_get_text (GTK_ENTRY (fontsel->preview_entry));
806 }
807
808
809 /* This sets the text in the preview entry. */
810 void
811 gtk_font_selection_set_preview_text  (GtkFontSelection *fontsel,
812                                       const gchar         *text)
813 {
814   gtk_entry_set_text (GTK_ENTRY (fontsel->preview_entry), text);
815 }
816
817 /*****************************************************************************
818  * GtkFontSelectionDialog
819  *****************************************************************************/
820
821 GtkType
822 gtk_font_selection_dialog_get_type (void)
823 {
824   static guint font_selection_dialog_type = 0;
825   
826   if (!font_selection_dialog_type)
827     {
828       GtkTypeInfo fontsel_diag_info =
829       {
830         "GtkFontSelectionDialog",
831         sizeof (GtkFontSelectionDialog),
832         sizeof (GtkFontSelectionDialogClass),
833         (GtkClassInitFunc) gtk_font_selection_dialog_class_init,
834         (GtkObjectInitFunc) gtk_font_selection_dialog_init,
835         /* reserved_1 */ NULL,
836         /* reserved_2 */ NULL,
837         (GtkClassInitFunc) NULL,
838       };
839       
840       font_selection_dialog_type = gtk_type_unique (GTK_TYPE_DIALOG,
841                                                     &fontsel_diag_info);
842     }
843   
844   return font_selection_dialog_type;
845 }
846
847 static void
848 gtk_font_selection_dialog_class_init (GtkFontSelectionDialogClass *klass)
849 {
850   GtkObjectClass *object_class;
851   
852   object_class = (GtkObjectClass*) klass;
853   
854   font_selection_dialog_parent_class = gtk_type_class (GTK_TYPE_DIALOG);
855 }
856
857 static void
858 gtk_font_selection_dialog_init (GtkFontSelectionDialog *fontseldiag)
859 {
860   GtkDialog *dialog;
861
862   dialog = GTK_DIALOG (fontseldiag);
863   
864   fontseldiag->dialog_width = -1;
865   fontseldiag->auto_resize = TRUE;
866   
867   gtk_widget_set_events(GTK_WIDGET(fontseldiag), GDK_STRUCTURE_MASK);
868   gtk_signal_connect (GTK_OBJECT (fontseldiag), "configure_event",
869                       (GtkSignalFunc) gtk_font_selection_dialog_on_configure,
870                       fontseldiag);
871   
872   gtk_container_set_border_width (GTK_CONTAINER (fontseldiag), 4);
873   gtk_window_set_policy(GTK_WINDOW(fontseldiag), FALSE, TRUE, TRUE);
874   
875   fontseldiag->main_vbox = dialog->vbox;
876   
877   fontseldiag->fontsel = gtk_font_selection_new();
878   gtk_container_set_border_width (GTK_CONTAINER (fontseldiag->fontsel), 4);
879   gtk_widget_show (fontseldiag->fontsel);
880   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
881                       fontseldiag->fontsel, TRUE, TRUE, 0);
882   
883   /* Create the action area */
884   fontseldiag->action_area = dialog->action_area;
885   
886   fontseldiag->ok_button = gtk_dialog_add_button (dialog,
887                                                   GTK_STOCK_BUTTON_OK,
888                                                   GTK_RESPONSE_OK);
889   gtk_widget_grab_default (fontseldiag->ok_button);
890   
891   fontseldiag->apply_button = gtk_dialog_add_button (dialog,
892                                                      GTK_STOCK_BUTTON_APPLY,
893                                                      GTK_RESPONSE_APPLY);
894   gtk_widget_hide (fontseldiag->apply_button);
895
896   
897   fontseldiag->cancel_button = gtk_dialog_add_button (dialog,
898                                                       GTK_STOCK_BUTTON_CANCEL,
899                                                       GTK_RESPONSE_CANCEL);
900
901   gtk_window_set_title (GTK_WINDOW (fontseldiag),
902                         _("Font Selection"));
903
904 }
905
906 GtkWidget*
907 gtk_font_selection_dialog_new   (const gchar      *title)
908 {
909   GtkFontSelectionDialog *fontseldiag;
910   
911   fontseldiag = gtk_type_new (GTK_TYPE_FONT_SELECTION_DIALOG);
912
913   if (title)
914     gtk_window_set_title (GTK_WINDOW (fontseldiag), title);
915   
916   return GTK_WIDGET (fontseldiag);
917 }
918
919 gchar*
920 gtk_font_selection_dialog_get_font_name (GtkFontSelectionDialog *fsd)
921 {
922   return gtk_font_selection_get_font_name(GTK_FONT_SELECTION(fsd->fontsel));
923 }
924
925 GdkFont*
926 gtk_font_selection_dialog_get_font      (GtkFontSelectionDialog *fsd)
927 {
928   return gtk_font_selection_get_font(GTK_FONT_SELECTION(fsd->fontsel));
929 }
930
931 gboolean
932 gtk_font_selection_dialog_set_font_name (GtkFontSelectionDialog *fsd,
933                                          const gchar      *fontname)
934 {
935   return gtk_font_selection_set_font_name(GTK_FONT_SELECTION(fsd->fontsel),
936                                           fontname);
937 }
938
939 gchar*
940 gtk_font_selection_dialog_get_preview_text (GtkFontSelectionDialog *fsd)
941 {
942   return gtk_font_selection_get_preview_text(GTK_FONT_SELECTION(fsd->fontsel));
943 }
944
945 void
946 gtk_font_selection_dialog_set_preview_text (GtkFontSelectionDialog *fsd,
947                                             const gchar   *text)
948 {
949   gtk_font_selection_set_preview_text(GTK_FONT_SELECTION(fsd->fontsel), text);
950 }
951
952
953 /* This turns auto-shrink off if the user resizes the width of the dialog.
954    It also turns it back on again if the user resizes it back to its normal
955    width. */
956 static gint
957 gtk_font_selection_dialog_on_configure (GtkWidget         *widget,
958                                         GdkEventConfigure *event,
959                                         GtkFontSelectionDialog *fsd)
960 {
961   /* This sets the initial width. */
962   if (fsd->dialog_width == -1)
963     fsd->dialog_width = event->width;
964   else if (fsd->auto_resize && fsd->dialog_width != event->width)
965     {
966       fsd->auto_resize = FALSE;
967       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, FALSE);
968     }
969   else if (!fsd->auto_resize && fsd->dialog_width == event->width)
970     {
971       fsd->auto_resize = TRUE;
972       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, TRUE);
973     }
974   
975   return FALSE;
976 }