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