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