]> Pileus Git - ~andy/gtk/blob - gtk/gtkfontsel.c
restricted the --g*fatal-* arguments to --g-fatal-warnings again. this
[~andy/gtk] / gtk / gtkfontsel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkFontSelection widget for Gtk+, by Damon Chaplin, May 1998.
5  * Based on the GnomeFontSelector widget, by Elliot Lee, but major changes.
6  * The GnomeFontSelector was derived from app/text_tool.c in the GIMP.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /*
25  * Limits:
26  *
27  *  Fontnames    - A maximum of MAX_FONTS (32767) fontnames will be retrieved
28  *                 from X Windows with XListFonts(). Any more are ignored.
29  *                 I think this limit may have been set because of a limit in
30  *                 GtkList. It could possibly be increased since we are using
31  *                 GtkClists now, but I'd be surprised if it was reached.
32  *  Field length - XLFD_MAX_FIELD_LEN is the maximum length that any field of a
33  *                 fontname can be for it to be considered valid. Others are
34  *                 ignored.
35  *  Properties   - Maximum of 65535 choices for each font property - guint16's
36  *                 are used as indices, e.g. in the FontInfo struct.
37  *  Combinations - Maximum of 65535 combinations of properties for each font
38  *                 family - a guint16 is used in the FontInfo struct.
39  *  Font size    - Minimum font size of 2 pixels/points, since trying to load
40  *                 some fonts with a size of 1 can cause X to hang.
41  *                 (e.g. the Misc Fixed fonts).
42  */
43
44 /*
45  * Possible Improvements:
46  *
47  *  Font Styles  - could sort the styles into a reasonable order - regular
48  *                 first, then bold, bold italic etc. Could also try to keep
49  *                 a similar style as the user selects different fonts.
50  *
51  *  I18N         - the default preview text is not useful for international
52  *                 fonts. Maybe the first few characters of the font could be
53  *                 displayed instead.
54  *               - fontsets? should these be handled by the font dialog?
55  */
56
57 /*
58  * Debugging: compile with -DFONTSEL_DEBUG for lots of debugging output.
59  */
60
61
62 #include <stdlib.h>
63 #include <stdio.h>
64 #include <string.h>
65 #include <ctype.h>
66 #include <X11/Xlib.h>
67
68 #include "gdk/gdkx.h"
69 #include "gdk/gdkkeysyms.h"
70
71 #include "gtkbutton.h"
72 #include "gtkclist.h"
73 #include "gtkentry.h"
74 #include "gtkfontsel.h"
75 #include "gtkframe.h"
76 #include "gtkhbbox.h"
77 #include "gtkhbox.h"
78 #include "gtklabel.h"
79 #include "gtknotebook.h"
80 #include "gtkradiobutton.h"
81 #include "gtksignal.h"
82 #include "gtktable.h"
83 #include "gtkvbox.h"
84
85 /* The maximum number of fontnames requested with XListFonts(). */
86 #define MAX_FONTS 32767
87
88 /* This is the largest field length we will accept. If a fontname has a field
89    larger than this we will skip it. */
90 #define XLFD_MAX_FIELD_LEN 64
91
92 /* These are what we use as the standard font sizes, for the size clist.
93    Note that when using points we still show these integer point values but
94    we work internally in decipoints (and decipoint values can be typed in). */
95 static guint16 font_sizes[] = {
96   8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28,
97   32, 36, 40, 48, 56, 64, 72
98 };
99
100 /* Initial font metric & size (Remember point sizes are in decipoints).
101    The font size should match one of those in the font_sizes array. */
102 #define INITIAL_METRIC            POINTS_METRIC
103 #define INITIAL_FONT_SIZE         140
104
105 /* This is the default text shown in the preview entry, though the user
106    can set it. Remember that some fonts only have capital letters. */
107 #define PREVIEW_TEXT "abcdefghijk ABCDEFGHIJK"
108
109 /* This is the initial and maximum height of the preview entry (it expands
110    when large font sizes are selected). Initial height is also the minimum. */
111 #define INITIAL_PREVIEW_HEIGHT 44
112 #define MAX_PREVIEW_HEIGHT 300
113
114 /* These are the sizes of the font, style & size clists. */
115 #define FONT_LIST_HEIGHT        136
116 #define FONT_LIST_WIDTH         180
117 #define FONT_STYLE_LIST_WIDTH   160
118 #define FONT_SIZE_LIST_WIDTH    60
119
120 /* This is the number of fields in an X Logical Font Description font name.
121    Note that we count the registry & encoding as 1. */
122 #define GTK_XLFD_NUM_FIELDS 13
123
124 /* Used for the flags field in FontStyle. Note that they can be combined,
125    e.g. a style can have multiple bitmaps and a true scalable version.
126    The displayed flag is used when displaying styles to remember which
127    styles have already been displayed. */
128 enum
129 {
130   BITMAP_FONT           = 1 << 0,
131   SCALABLE_FONT         = 1 << 1,
132   SCALABLE_BITMAP_FONT  = 1 << 2,
133   DISPLAYED             = 1 << 3
134 };
135
136 typedef struct _GtkFontSelInfo GtkFontSelInfo;
137 typedef struct _FontInfo FontInfo;
138 typedef struct _FontStyle FontStyle;
139
140 /* This struct represents one family of fonts (with one foundry), e.g. adobe
141    courier or sony fixed. It stores the family name, the index of the foundry
142    name, and the index of and number of available styles. */
143 struct _FontInfo
144 {
145   gchar   *family;
146   guint16  foundry;
147   gint     style_index;
148   guint16  nstyles;
149 };
150 /* This represents one style, as displayed in the Font Style clist. It can
151    have a number of available pixel sizes and point sizes. The indexes point
152    into the two big fontsel_info->pixel_sizes & fontsel_info->point_sizes arrays. */
153 struct _FontStyle
154 {
155   guint16  properties[GTK_NUM_STYLE_PROPERTIES];
156   gint     pixel_sizes_index;
157   guint16  npixel_sizes;
158   gint     point_sizes_index;
159   guint16  npoint_sizes;
160   guint8   flags;
161 };
162
163 struct _GtkFontSelInfo {
164   
165   /* This is a table with each FontInfo representing one font family+foundry */
166   FontInfo *font_info;
167   gint nfonts;
168   
169   /* This stores all the valid combinations of properties for every family.
170      Each FontInfo holds an index into its own space in this one big array. */
171   FontStyle *font_styles;
172   gint nstyles;
173   
174   /* This stores all the font sizes available for every style.
175      Each style holds an index into these arrays. */
176   guint16 *pixel_sizes;
177   guint16 *point_sizes;
178   
179   /* These are the arrays of strings of all possible weights, slants, 
180      set widths, spacings, charsets & foundries, and the amount of space
181      allocated for each array. */
182   gchar **properties[GTK_NUM_FONT_PROPERTIES];
183   guint16 nproperties[GTK_NUM_FONT_PROPERTIES];
184   guint16 space_allocated[GTK_NUM_FONT_PROPERTIES];
185   
186   /* Whether any scalable bitmap fonts are available. If not, the 'Allow
187      scaled bitmap fonts' toggle button is made insensitive. */
188   gboolean scaled_bitmaps_available;
189 };
190
191 /* These are the field numbers in the X Logical Font Description fontnames,
192    e.g. -adobe-courier-bold-o-normal--25-180-100-100-m-150-iso8859-1 */
193 typedef enum
194 {
195   XLFD_FOUNDRY          = 0,
196   XLFD_FAMILY           = 1,
197   XLFD_WEIGHT           = 2,
198   XLFD_SLANT            = 3,
199   XLFD_SET_WIDTH        = 4,
200   XLFD_ADD_STYLE        = 5,
201   XLFD_PIXELS           = 6,
202   XLFD_POINTS           = 7,
203   XLFD_RESOLUTION_X     = 8,
204   XLFD_RESOLUTION_Y     = 9,
205   XLFD_SPACING          = 10,
206   XLFD_AVERAGE_WIDTH    = 11,
207   XLFD_CHARSET          = 12
208 } FontField;
209
210 /* These are the names of the fields, used on the info & filter page. */
211 static gchar* xlfd_field_names[GTK_XLFD_NUM_FIELDS] = {
212   "Foundry:",
213   "Family:",
214   "Weight:",
215   "Slant:",
216   "Set Width:",
217   "Add Style:",
218   "Pixel Size:",
219   "Point Size:",
220   "Resolution X:",
221   "Resolution Y:",
222   "Spacing:",
223   "Average Width:",
224   "Charset:",
225 };
226
227 /* These are the array indices of the font properties used in several arrays,
228    and should match the xlfd_index array below. */
229 typedef enum
230 {
231   WEIGHT        = 0,
232   SLANT         = 1,
233   SET_WIDTH     = 2,
234   SPACING       = 3,
235   CHARSET       = 4,
236   FOUNDRY       = 5
237 } PropertyIndexType;
238
239 /* This is used to look up a field in a fontname given one of the above
240    property indices. */
241 static FontField xlfd_index[GTK_NUM_FONT_PROPERTIES] = {
242   XLFD_WEIGHT,
243   XLFD_SLANT,
244   XLFD_SET_WIDTH,
245   XLFD_SPACING,
246   XLFD_CHARSET,
247   XLFD_FOUNDRY
248 };
249
250 /* These are the positions of the properties in the filter table - x, y. */
251 static gint filter_positions[GTK_NUM_FONT_PROPERTIES][2] = {
252   { 1, 0 }, { 0, 2 }, { 1, 2 }, { 2, 2 }, { 2, 0 }, { 0, 0 }
253 };
254 static gint filter_heights[GTK_NUM_FONT_PROPERTIES] = {
255   100, 80, 80, 50, 100, 100
256 };
257
258 static GtkFontSelInfo *fontsel_info = NULL;
259
260 /* The initial size and increment of each of the arrays of property values. */
261 #define PROPERTY_ARRAY_INCREMENT        16
262
263 static void    gtk_font_selection_class_init         (GtkFontSelectionClass *klass);
264 static void    gtk_font_selection_init               (GtkFontSelection *fontsel);
265 static void    gtk_font_selection_destroy            (GtkObject      *object);
266
267 /* These are all used for class initialization - loading in the fonts etc. */
268 static void    gtk_font_selection_get_fonts          (void);
269 static void    gtk_font_selection_insert_font        (GSList         *fontnames[],
270                                                       gint           *ntable,
271                                                       gchar          *fontname);
272 static gint    gtk_font_selection_insert_field       (gchar          *fontname,
273                                                       gint            prop);
274
275 /* These are the callbacks & related functions. */
276 static void    gtk_font_selection_select_font        (GtkWidget      *w,
277                                                       gint            row,
278                                                       gint            column,
279                                                       GdkEventButton *bevent,
280                                                       gpointer        data);
281 static gint    gtk_font_selection_on_clist_key_press (GtkWidget      *clist,
282                                                       GdkEventKey    *event,
283                                                       GtkFontSelection *fs);
284 static gboolean gtk_font_selection_select_next       (GtkFontSelection *fs,
285                                                       GtkWidget        *clist,
286                                                       gint              step);
287 static void    gtk_font_selection_show_available_styles
288 (GtkFontSelection *fs);
289 static void    gtk_font_selection_select_best_style  (GtkFontSelection *fs,
290                                                       gboolean         use_first);
291 static gint    gtk_font_selection_get_best_match     (GtkFontSelection *fs);
292
293 static void    gtk_font_selection_select_style       (GtkWidget      *w,
294                                                       gint            row,
295                                                       gint            column,
296                                                       GdkEventButton *bevent,
297                                                       gpointer        data);
298 static void    gtk_font_selection_show_available_sizes
299 (GtkFontSelection *fs);
300 static gint    gtk_font_selection_size_key_press     (GtkWidget      *w,
301                                                       GdkEventKey    *event,
302                                                       gpointer        data);
303 static void    gtk_font_selection_select_best_size   (GtkFontSelection *fs);
304 static void    gtk_font_selection_select_size        (GtkWidget      *w,
305                                                       gint            row,
306                                                       gint            column,
307                                                       GdkEventButton *bevent,
308                                                       gpointer        data);
309
310 static void    gtk_font_selection_metric_callback    (GtkWidget      *w,
311                                                       gpointer        data);
312 static void    gtk_font_selection_expose_list        (GtkWidget      *w,
313                                                       GdkEventExpose *event,
314                                                       gpointer        data);
315
316 static void    gtk_font_selection_switch_page        (GtkWidget      *w,
317                                                       GtkNotebookPage *page,
318                                                       gint             page_num,
319                                                       gpointer         data);
320 static void    gtk_font_selection_show_font_info     (GtkFontSelection *fs);
321
322 static void    gtk_font_selection_select_filter      (GtkWidget      *w,
323                                                       gint            row,
324                                                       gint            column,
325                                                       GdkEventButton *bevent,
326                                                       gpointer        data);
327 static void    gtk_font_selection_unselect_filter    (GtkWidget      *w,
328                                                       gint            row,
329                                                       gint            column,
330                                                       GdkEventButton *bevent,
331                                                       gpointer        data);
332 static void    gtk_font_selection_filter_fonts       (GtkFontSelection *fs);
333 static gboolean gtk_font_selection_style_visible     (GtkFontSelection *fs,
334                                                       FontInfo       *font,
335                                                       gint            style);
336 static void    gtk_font_selection_reset_filter       (GtkWidget      *w,
337                                                       gpointer        data);
338 static void    gtk_font_selection_on_clear_filter    (GtkWidget      *w,
339                                                       gpointer        data);
340 static void    gtk_font_selection_apply_filter       (GtkFontSelection *fs);
341 static void    gtk_font_selection_clear_filter       (GtkFontSelection *fs);
342 static void    gtk_font_selection_toggle_scaled_bitmaps
343 (GtkWidget      *w,
344  gpointer        data);
345
346 /* Misc. utility functions. */
347 static void    gtk_font_selection_insert_fonts       (GtkFontSelection *fs);
348 static gboolean gtk_font_selection_load_font         (GtkFontSelection *fs);
349 static void    gtk_font_selection_update_preview     (GtkFontSelection *fs);
350
351 static gint    gtk_font_selection_find_font          (GtkFontSelection *fs,
352                                                       gchar          *family,
353                                                       guint16         foundry);
354 static guint16 gtk_font_selection_field_to_index     (gchar         **table,
355                                                       gint            ntable,
356                                                       gchar          *field);
357
358 static gchar*  gtk_font_selection_expand_slant_code  (gchar          *slant);
359 static gchar*  gtk_font_selection_expand_spacing_code(gchar          *spacing);
360
361 /* Functions for handling X Logical Font Description fontnames. */
362 static gboolean gtk_font_selection_is_xlfd_font_name (const gchar    *fontname);
363 static char*   gtk_font_selection_get_xlfd_field     (const gchar    *fontname,
364                                                       FontField       field_num,
365                                                       gchar          *buffer);
366 static gchar * gtk_font_selection_create_xlfd        (gint            size,
367                                                       GtkFontMetricType metric,
368                                                       gchar          *foundry,
369                                                       gchar          *family,
370                                                       gchar          *weight,
371                                                       gchar          *slant,
372                                                       gchar          *set_width,
373                                                       gchar          *spacing,
374                                                       gchar          *charset);
375
376
377 /* FontSelectionDialog */
378 static void    gtk_font_selection_dialog_class_init  (GtkFontSelectionDialogClass *klass);
379 static void    gtk_font_selection_dialog_init        (GtkFontSelectionDialog *fontseldiag);
380
381 static gint    gtk_font_selection_dialog_on_configure(GtkWidget      *widget,
382                                                       GdkEventConfigure *event,
383                                                       GtkFontSelectionDialog *fsd);
384
385 static GtkWindowClass *font_selection_parent_class = NULL;
386 static GtkNotebookClass *font_selection_dialog_parent_class = NULL;
387
388 GtkType
389 gtk_font_selection_get_type()
390 {
391   static GtkType font_selection_type = 0;
392   
393   if(!font_selection_type)
394     {
395       GtkTypeInfo fontsel_type_info =
396       {
397         "GtkFontSelection",
398         sizeof (GtkFontSelection),
399         sizeof (GtkFontSelectionClass),
400         (GtkClassInitFunc) gtk_font_selection_class_init,
401         (GtkObjectInitFunc) gtk_font_selection_init,
402         /* reserved_1 */ NULL,
403         /* reserved_2 */ NULL,
404         (GtkClassInitFunc) NULL,
405       };
406       
407       font_selection_type = gtk_type_unique (GTK_TYPE_NOTEBOOK,
408                                              &fontsel_type_info);
409     }
410   
411   return font_selection_type;
412 }
413
414 static void
415 gtk_font_selection_class_init(GtkFontSelectionClass *klass)
416 {
417   GtkObjectClass *object_class;
418   
419   object_class = (GtkObjectClass *) klass;
420   
421   font_selection_parent_class = gtk_type_class (GTK_TYPE_NOTEBOOK);
422   
423   object_class->destroy = gtk_font_selection_destroy;
424   
425   gtk_font_selection_get_fonts ();
426 }
427
428 static void
429 gtk_font_selection_init(GtkFontSelection *fontsel)
430 {
431   GtkWidget *text_frame;
432   GtkWidget *text_box;
433   GtkWidget *table, *label, *hbox, *hbox2, *clist, *button, *vbox, *alignment;
434   gint i, prop, row;
435   gchar *titles[] = { "Font Property", "Requested Value", "Actual Value" };
436   gchar buffer[128];
437   gchar *size;
438   gint size_to_match;
439   gchar *row_text[3];
440   gchar *property, *text;
441   gboolean inserted;
442   
443   /* Initialize the GtkFontSelection struct. We do this here in case any
444      callbacks are triggered while creating the interface. */
445   fontsel->font = NULL;
446   fontsel->font_index = -1;
447   fontsel->style = -1;
448   fontsel->metric = INITIAL_METRIC;
449   fontsel->size = INITIAL_FONT_SIZE;
450   fontsel->selected_size = INITIAL_FONT_SIZE;
451   fontsel->scale_bitmapped_fonts = FALSE;
452   
453   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
454     {
455       fontsel->property_filters[prop] = NULL;
456       fontsel->property_nfilters[prop] = 0;
457     }
458   
459   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
460     fontsel->property_values[prop] = 0;
461   
462   fontsel->scroll_on_expose = TRUE;
463   
464   /* Create the main notebook page. */
465   fontsel->main_vbox = gtk_vbox_new (FALSE, 4);
466   gtk_widget_show (fontsel->main_vbox);
467   gtk_container_border_width (GTK_CONTAINER (fontsel->main_vbox), 6);
468   label = gtk_label_new("Font");
469   gtk_widget_set_usize (label, 120, -1);
470   gtk_notebook_append_page (GTK_NOTEBOOK (fontsel),
471                             fontsel->main_vbox, label);
472   
473   /* Create the table of font, style & size. */
474   table = gtk_table_new (3, 3, FALSE);
475   gtk_widget_show (table);
476   gtk_table_set_col_spacings(GTK_TABLE(table), 8);
477   gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), table, TRUE, TRUE, 0);
478   
479   fontsel->font_label = gtk_label_new("Font:");
480   gtk_misc_set_alignment (GTK_MISC (fontsel->font_label), 0.0, 0.5);
481   gtk_widget_show (fontsel->font_label);
482   gtk_table_attach (GTK_TABLE (table), fontsel->font_label, 0, 1, 0, 1,
483                     GTK_FILL, 0, 0, 0);
484   label = gtk_label_new("Font Style:");
485   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
486   gtk_widget_show (label);
487   gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1,
488                     GTK_FILL, 0, 0, 0);
489   label = gtk_label_new("Size:");
490   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
491   gtk_widget_show (label);
492   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
493                     GTK_FILL, 0, 0, 0);
494   
495   fontsel->font_entry = gtk_entry_new();
496   gtk_entry_set_editable(GTK_ENTRY(fontsel->font_entry), FALSE);
497   gtk_widget_set_usize (fontsel->font_entry, 20, -1);
498   gtk_widget_show (fontsel->font_entry);
499   gtk_table_attach (GTK_TABLE (table), fontsel->font_entry, 0, 1, 1, 2,
500                     GTK_FILL, 0, 0, 0);
501   fontsel->font_style_entry = gtk_entry_new();
502   gtk_entry_set_editable(GTK_ENTRY(fontsel->font_style_entry), FALSE);
503   gtk_widget_set_usize (fontsel->font_style_entry, 20, -1);
504   gtk_widget_show (fontsel->font_style_entry);
505   gtk_table_attach (GTK_TABLE (table), fontsel->font_style_entry, 1, 2, 1, 2,
506                     GTK_FILL, 0, 0, 0);
507   fontsel->size_entry = gtk_entry_new();
508   gtk_widget_set_usize (fontsel->size_entry, 20, -1);
509   gtk_widget_show (fontsel->size_entry);
510   gtk_table_attach (GTK_TABLE (table), fontsel->size_entry, 2, 3, 1, 2,
511                     GTK_FILL, 0, 0, 0);
512   gtk_signal_connect (GTK_OBJECT (fontsel->size_entry), "key_press_event",
513                       (GtkSignalFunc) gtk_font_selection_size_key_press,
514                       fontsel);
515   
516   /* Create the clists  */
517   fontsel->font_clist = gtk_clist_new(1);
518   gtk_clist_column_titles_hide (GTK_CLIST(fontsel->font_clist));
519   gtk_clist_set_column_width (GTK_CLIST(fontsel->font_clist), 0, 300);
520   gtk_clist_set_policy(GTK_CLIST(fontsel->font_clist), GTK_POLICY_ALWAYS,
521                        GTK_POLICY_AUTOMATIC);
522   gtk_widget_set_usize (fontsel->font_clist, FONT_LIST_WIDTH,
523                         FONT_LIST_HEIGHT);
524   gtk_widget_show(fontsel->font_clist);
525   gtk_table_attach (GTK_TABLE (table), fontsel->font_clist, 0, 1, 2, 3,
526                     GTK_EXPAND | GTK_FILL,
527                     GTK_EXPAND | GTK_FILL, 0, 0);
528   
529   fontsel->font_style_clist = gtk_clist_new(1);
530   gtk_clist_column_titles_hide (GTK_CLIST(fontsel->font_style_clist));
531   gtk_clist_set_column_width (GTK_CLIST(fontsel->font_style_clist), 0, 300);
532   gtk_clist_set_policy(GTK_CLIST(fontsel->font_style_clist), GTK_POLICY_ALWAYS,
533                        GTK_POLICY_AUTOMATIC);
534   gtk_widget_set_usize (fontsel->font_style_clist, FONT_STYLE_LIST_WIDTH, -1);
535   gtk_widget_show(fontsel->font_style_clist);
536   gtk_table_attach (GTK_TABLE (table), fontsel->font_style_clist, 1, 2, 2, 3,
537                     GTK_EXPAND | GTK_FILL,
538                     GTK_EXPAND | GTK_FILL, 0, 0);
539   
540   fontsel->size_clist = gtk_clist_new(1);
541   gtk_clist_column_titles_hide (GTK_CLIST(fontsel->size_clist));
542   gtk_clist_set_policy(GTK_CLIST(fontsel->size_clist), GTK_POLICY_ALWAYS,
543                        GTK_POLICY_AUTOMATIC);
544   gtk_widget_set_usize (fontsel->size_clist, FONT_SIZE_LIST_WIDTH, -1);
545   gtk_widget_show(fontsel->size_clist);
546   gtk_table_attach (GTK_TABLE (table), fontsel->size_clist, 2, 3, 2, 3,
547                     GTK_FILL, GTK_FILL, 0, 0);
548   
549   
550   /* Insert the fonts. If there exist fonts with the same family but
551      different foundries, then the foundry name is appended in brackets. */
552   gtk_font_selection_insert_fonts(fontsel);
553   
554   gtk_signal_connect (GTK_OBJECT (fontsel->font_clist), "select_row",
555                       GTK_SIGNAL_FUNC(gtk_font_selection_select_font),
556                       fontsel);
557   GTK_WIDGET_SET_FLAGS (fontsel->font_clist, GTK_CAN_FOCUS);
558   gtk_signal_connect (GTK_OBJECT (fontsel->font_clist), "key_press_event",
559                       GTK_SIGNAL_FUNC(gtk_font_selection_on_clist_key_press),
560                       fontsel);
561   gtk_signal_connect_after (GTK_OBJECT (fontsel->font_clist), "expose_event",
562                             GTK_SIGNAL_FUNC(gtk_font_selection_expose_list),
563                             fontsel);
564   
565   gtk_signal_connect (GTK_OBJECT (fontsel->font_style_clist), "select_row",
566                       GTK_SIGNAL_FUNC(gtk_font_selection_select_style),
567                       fontsel);
568   GTK_WIDGET_SET_FLAGS (fontsel->font_style_clist, GTK_CAN_FOCUS);
569   gtk_signal_connect (GTK_OBJECT (fontsel->font_style_clist),
570                       "key_press_event",
571                       GTK_SIGNAL_FUNC(gtk_font_selection_on_clist_key_press),
572                       fontsel);
573   
574   /* Insert the standard font sizes */
575   gtk_clist_freeze (GTK_CLIST(fontsel->size_clist));
576   size_to_match = INITIAL_FONT_SIZE;
577   if (INITIAL_METRIC == POINTS_METRIC)
578     size_to_match = size_to_match / 10;
579   for (i = 0; i < sizeof(font_sizes) / sizeof(font_sizes[0]); i++)
580     {
581       sprintf(buffer, "%i", font_sizes[i]);
582       size = buffer;
583       gtk_clist_append(GTK_CLIST(fontsel->size_clist), &size);
584       if (font_sizes[i] == size_to_match)
585         {
586           gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), i, 0);
587           gtk_entry_set_text(GTK_ENTRY(fontsel->size_entry), buffer);
588         }
589     }
590   gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
591   
592   gtk_signal_connect (GTK_OBJECT (fontsel->size_clist), "select_row",
593                       GTK_SIGNAL_FUNC(gtk_font_selection_select_size),
594                       fontsel);
595   GTK_WIDGET_SET_FLAGS (fontsel->size_clist, GTK_CAN_FOCUS);
596   gtk_signal_connect (GTK_OBJECT (fontsel->size_clist), "key_press_event",
597                       GTK_SIGNAL_FUNC(gtk_font_selection_on_clist_key_press),
598                       fontsel);
599   
600   
601   /* create the Filter, Scale Bitmaps & Metric buttons */
602   hbox = gtk_hbox_new(FALSE, 8);
603   gtk_widget_show (hbox);
604   gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), hbox, FALSE, TRUE, 0);
605   
606   fontsel->filter_button = gtk_button_new_with_label("  Clear Filter  ");
607   gtk_widget_show(fontsel->filter_button);
608   gtk_box_pack_start (GTK_BOX (hbox), fontsel->filter_button, FALSE, FALSE, 0);
609   gtk_widget_set_sensitive (fontsel->filter_button, FALSE);
610   gtk_signal_connect (GTK_OBJECT (fontsel->filter_button), "clicked",
611                       GTK_SIGNAL_FUNC(gtk_font_selection_on_clear_filter),
612                       fontsel);
613   
614   fontsel->scaled_bitmaps_button
615     = gtk_check_button_new_with_label("Allow scaled bitmap fonts");
616   gtk_widget_show(fontsel->scaled_bitmaps_button);
617   gtk_box_pack_start (GTK_BOX (hbox), fontsel->scaled_bitmaps_button,
618                       FALSE, FALSE, 0);
619   if (!fontsel_info->scaled_bitmaps_available)
620     gtk_widget_set_sensitive (fontsel->scaled_bitmaps_button, FALSE);
621   gtk_signal_connect (GTK_OBJECT (fontsel->scaled_bitmaps_button), "clicked",
622                       GTK_SIGNAL_FUNC(gtk_font_selection_toggle_scaled_bitmaps),
623                       fontsel);
624   
625   
626   hbox2 = gtk_hbox_new(FALSE, 0);
627   gtk_widget_show (hbox2);
628   gtk_box_pack_end (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0);
629   
630   label = gtk_label_new("Metric:");
631   gtk_widget_show (label);
632   gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, TRUE, 8);
633   
634   fontsel->points_button = gtk_radio_button_new_with_label(NULL, "Points");
635   gtk_widget_show (fontsel->points_button);
636   gtk_box_pack_start (GTK_BOX (hbox2), fontsel->points_button, FALSE, TRUE, 0);
637   if (INITIAL_METRIC == POINTS_METRIC)
638     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(fontsel->points_button),
639                                 TRUE);
640   
641   fontsel->pixels_button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(fontsel->points_button), "Pixels");
642   gtk_widget_show (fontsel->pixels_button);
643   gtk_box_pack_start (GTK_BOX (hbox2), fontsel->pixels_button, FALSE, TRUE, 0);
644   if (INITIAL_METRIC == PIXELS_METRIC)
645     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(fontsel->pixels_button),
646                                 TRUE);
647   
648   gtk_signal_connect(GTK_OBJECT(fontsel->points_button), "toggled",
649                      (GtkSignalFunc) gtk_font_selection_metric_callback,
650                      fontsel);
651   gtk_signal_connect(GTK_OBJECT(fontsel->pixels_button), "toggled",
652                      (GtkSignalFunc) gtk_font_selection_metric_callback,
653                      fontsel);
654   
655   
656   /* create the text entry widget */
657   text_frame = gtk_frame_new ("Preview:");
658   gtk_widget_show (text_frame);
659   gtk_frame_set_shadow_type(GTK_FRAME(text_frame), GTK_SHADOW_ETCHED_IN);
660   gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), text_frame,
661                       FALSE, TRUE, 0);
662   
663   /* This is just used to get a 4-pixel space around the preview entry. */
664   text_box = gtk_hbox_new (FALSE, 0);
665   gtk_widget_show (text_box);
666   gtk_container_add (GTK_CONTAINER (text_frame), text_box);
667   gtk_container_border_width (GTK_CONTAINER (text_box), 4);
668   
669   fontsel->preview_entry = gtk_entry_new ();
670   gtk_widget_show (fontsel->preview_entry);
671   gtk_widget_set_usize (fontsel->preview_entry, -1, INITIAL_PREVIEW_HEIGHT);
672   gtk_box_pack_start (GTK_BOX (text_box), fontsel->preview_entry,
673                       TRUE, TRUE, 0);
674   
675   /* Create the message area */
676   fontsel->message_label = gtk_label_new("");
677   gtk_widget_show (fontsel->message_label);
678   gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), fontsel->message_label, 
679                       FALSE, FALSE, 0);
680   
681   
682   /* Create the font info page */
683   fontsel->info_vbox = gtk_vbox_new (FALSE, 4);
684   gtk_widget_show (fontsel->info_vbox);
685   gtk_container_border_width (GTK_CONTAINER (fontsel->info_vbox), 2);
686   label = gtk_label_new("Font Information");
687   gtk_widget_set_usize (label, 120, -1);
688   gtk_notebook_append_page (GTK_NOTEBOOK (fontsel),
689                             fontsel->info_vbox, label);
690   
691   fontsel->info_clist = gtk_clist_new_with_titles(3, titles);
692   gtk_widget_set_usize (fontsel->info_clist, 390, 150);
693   gtk_clist_set_column_width(GTK_CLIST(fontsel->info_clist), 0, 130);
694   gtk_clist_set_column_width(GTK_CLIST(fontsel->info_clist), 1, 130);
695   gtk_clist_set_column_width(GTK_CLIST(fontsel->info_clist), 2, 130);
696   gtk_clist_column_titles_passive(GTK_CLIST(fontsel->info_clist));
697   gtk_clist_set_policy(GTK_CLIST(fontsel->info_clist), GTK_POLICY_AUTOMATIC,
698                        GTK_POLICY_AUTOMATIC);
699   gtk_widget_show(fontsel->info_clist);
700   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), fontsel->info_clist,
701                       TRUE, TRUE, 0);
702   
703   /* Insert the property names */
704   gtk_clist_freeze (GTK_CLIST(fontsel->info_clist));
705   row_text[1] = "";
706   row_text[2] = "";
707   for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
708     {
709       row_text[0] = xlfd_field_names[i];
710       gtk_clist_append(GTK_CLIST(fontsel->info_clist), row_text);
711       gtk_clist_set_shift(GTK_CLIST(fontsel->info_clist), i, 0, 0, 4);
712       gtk_clist_set_shift(GTK_CLIST(fontsel->info_clist), i, 1, 0, 4);
713       gtk_clist_set_shift(GTK_CLIST(fontsel->info_clist), i, 2, 0, 4);
714     }
715   gtk_clist_thaw (GTK_CLIST(fontsel->info_clist));
716   
717   label = gtk_label_new("Requested Font Name:");
718   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
719   gtk_widget_show (label);
720   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), label, FALSE, TRUE, 0);
721   
722   fontsel->requested_font_name = gtk_entry_new();
723   gtk_entry_set_editable(GTK_ENTRY(fontsel->requested_font_name), FALSE);
724   gtk_widget_show (fontsel->requested_font_name);
725   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox),
726                       fontsel->requested_font_name, FALSE, TRUE, 0);
727   
728   label = gtk_label_new("Actual Font Name:");
729   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
730   gtk_widget_show (label);
731   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), label, FALSE, TRUE, 0);
732   
733   fontsel->actual_font_name = gtk_entry_new();
734   gtk_entry_set_editable(GTK_ENTRY(fontsel->actual_font_name), FALSE);
735   gtk_widget_show (fontsel->actual_font_name);
736   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox),
737                       fontsel->actual_font_name, FALSE, TRUE, 0);
738   
739   sprintf(buffer, "%i fonts available with a total of %i styles.",
740           fontsel_info->nfonts, fontsel_info->nstyles);
741   label = gtk_label_new(buffer);
742   gtk_widget_show (label);
743   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), label, FALSE, FALSE, 0);
744   
745   gtk_signal_connect (GTK_OBJECT (fontsel), "switch_page",
746                       GTK_SIGNAL_FUNC(gtk_font_selection_switch_page),
747                       fontsel);
748   
749   
750   /* Create the Filter page. */
751   fontsel->filter_vbox = gtk_vbox_new (FALSE, 4);
752   gtk_widget_show (fontsel->filter_vbox);
753   gtk_container_border_width (GTK_CONTAINER (fontsel->filter_vbox), 2);
754   label = gtk_label_new("Filter");
755   gtk_widget_set_usize (label, 120, -1);
756   gtk_notebook_append_page (GTK_NOTEBOOK (fontsel),
757                             fontsel->filter_vbox, label);
758   
759   table = gtk_table_new (4, 3, FALSE);
760   gtk_table_set_col_spacings(GTK_TABLE(table), 2);
761   gtk_widget_show (table);
762   gtk_box_pack_start (GTK_BOX (fontsel->filter_vbox), table, TRUE, TRUE, 0);
763   
764   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
765     {
766       gint left = filter_positions[prop][0];
767       gint top = filter_positions[prop][1];
768       
769       label = gtk_label_new(xlfd_field_names[xlfd_index[prop]]);
770       gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
771       gtk_misc_set_padding (GTK_MISC (label), 0, 2);
772       gtk_widget_show(label);
773       gtk_table_attach (GTK_TABLE (table), label, left, left + 1,
774                         top, top + 1, GTK_FILL, GTK_FILL, 0, 0);
775       
776       clist = gtk_clist_new(1);
777       gtk_widget_set_usize (clist, 100, filter_heights[prop]);
778       gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_MULTIPLE);
779       gtk_clist_column_titles_hide(GTK_CLIST(clist));
780       gtk_clist_set_policy(GTK_CLIST(clist), GTK_POLICY_AUTOMATIC,
781                            GTK_POLICY_AUTOMATIC);
782       gtk_widget_show(clist);
783       
784       /* For the bottom-right cell we add the 'Clear Filter' button. */
785       if (top == 2 && left == 2)
786         {
787           vbox = gtk_vbox_new(FALSE, 0);
788           gtk_widget_show(vbox);
789           gtk_table_attach (GTK_TABLE (table), vbox, left, left + 1,
790                             top + 1, top + 2, GTK_FILL, GTK_FILL, 0, 0);
791           
792           gtk_box_pack_start (GTK_BOX (vbox), clist, TRUE, TRUE, 0);
793           
794           alignment = gtk_alignment_new(0.5, 0.0, 0.8, 0.0);
795           gtk_widget_show(alignment);
796           gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, TRUE, 4);
797           
798           button = gtk_button_new_with_label("Clear Filter");
799           gtk_widget_show(button);
800           gtk_container_add(GTK_CONTAINER(alignment), button);
801           gtk_signal_connect (GTK_OBJECT (button), "clicked",
802                               GTK_SIGNAL_FUNC(gtk_font_selection_reset_filter),
803                               fontsel);
804         }
805       else
806         gtk_table_attach (GTK_TABLE (table), clist,
807                           left, left + 1, top + 1, top + 2,
808                           GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
809       
810       gtk_signal_connect (GTK_OBJECT (clist), "select_row",
811                           GTK_SIGNAL_FUNC(gtk_font_selection_select_filter),
812                           fontsel);
813       gtk_signal_connect (GTK_OBJECT (clist), "unselect_row",
814                           GTK_SIGNAL_FUNC(gtk_font_selection_unselect_filter),
815                           fontsel);
816       
817       /* Insert the property names, expanded, and in sorted order.
818          But we make sure that the wildcard '*' is first. */
819       gtk_clist_freeze (GTK_CLIST(clist));
820       property = "*";
821       gtk_clist_append(GTK_CLIST(clist), &property);
822       
823       for (i = 1; i < fontsel_info->nproperties[prop]; i++) {
824         property = fontsel_info->properties[prop][i];
825         if (prop == SLANT)
826           property = gtk_font_selection_expand_slant_code(property);
827         else if (prop == SPACING)
828           property = gtk_font_selection_expand_spacing_code(property);
829         
830         inserted = FALSE;
831         for (row = 1; row < GTK_CLIST(clist)->rows; row++)
832           {
833             gtk_clist_get_text(GTK_CLIST(clist), row, 0, &text);
834             if (strcmp(property, text) < 0)
835               {
836                 inserted = TRUE;
837                 gtk_clist_insert(GTK_CLIST(clist), row, &property);
838                 break;
839               }
840           }
841         if (!inserted)
842           row = gtk_clist_append(GTK_CLIST(clist), &property);
843         gtk_clist_set_row_data(GTK_CLIST(clist), row, GINT_TO_POINTER (i));
844       }
845       gtk_clist_select_row(GTK_CLIST(clist), 0, 0);
846       gtk_clist_thaw (GTK_CLIST(clist));
847       fontsel->filter_clists[prop] = clist;
848     }
849 }
850
851 GtkWidget *
852 gtk_font_selection_new()
853 {
854   GtkFontSelection *fontsel;
855   
856   fontsel = gtk_type_new (GTK_TYPE_FONT_SELECTION);
857   
858   return GTK_WIDGET (fontsel);
859 }
860
861 static void
862 gtk_font_selection_destroy (GtkObject *object)
863 {
864   GtkFontSelection *fontsel;
865   
866   g_return_if_fail (object != NULL);
867   g_return_if_fail (GTK_IS_FONT_SELECTION (object));
868   
869   fontsel = GTK_FONT_SELECTION (object);
870   
871   /* All we have to do is unref the font, if we have one. */
872   if (fontsel->font)
873     gdk_font_unref (fontsel->font);
874   
875   if (GTK_OBJECT_CLASS (font_selection_parent_class)->destroy)
876     (* GTK_OBJECT_CLASS (font_selection_parent_class)->destroy) (object);
877 }
878
879
880 /* This inserts all the fonts into the family clist. */
881 static void
882 gtk_font_selection_insert_fonts (GtkFontSelection *fontsel)
883 {
884   FontInfo *font_info, *font;
885   gchar font_buffer[XLFD_MAX_FIELD_LEN * 2 + 4];
886   gchar *font_item;
887   gint nfonts, i, row;
888   
889   font_info = fontsel_info->font_info;
890   nfonts = fontsel_info->nfonts;
891   
892   gtk_clist_freeze (GTK_CLIST(fontsel->font_clist));
893   gtk_clist_clear (GTK_CLIST(fontsel->font_clist));
894   for (i = 0; i < nfonts; i++)
895     {
896       font = &font_info[i];
897       if ((i > 0 && font->family == font_info[i-1].family)
898           || (i < nfonts - 1 && font->family == font_info[i+1].family))
899         {
900           sprintf(font_buffer, "%s (%s)", font->family,
901                   fontsel_info->properties[FOUNDRY][font->foundry]);
902           font_item = font_buffer;
903           row = gtk_clist_append(GTK_CLIST(fontsel->font_clist), &font_item);
904         }
905       else
906         row = gtk_clist_append(GTK_CLIST(fontsel->font_clist), &font->family);
907       gtk_clist_set_row_data(GTK_CLIST(fontsel->font_clist), row, GINT_TO_POINTER (i));
908     }
909   gtk_clist_thaw (GTK_CLIST(fontsel->font_clist));
910 }
911
912
913 /* This is called when the clist is exposed. Here we scroll to the current
914    font if necessary. */
915 static void
916 gtk_font_selection_expose_list (GtkWidget               *widget,
917                                 GdkEventExpose          *event,
918                                 gpointer                 data)
919 {
920   GtkFontSelection *fontsel;
921   FontInfo *font_info;
922   GList *selection;
923   gint index;
924   
925 #ifdef FONTSEL_DEBUG
926   g_message("In expose_list\n");
927 #endif
928   fontsel = GTK_FONT_SELECTION(data);
929   
930   if (fontsel->scroll_on_expose)
931     {
932       fontsel->scroll_on_expose = FALSE;
933       
934       font_info = fontsel_info->font_info;
935       
936       /* Try to scroll the font family clist to the selected item */
937       selection = GTK_CLIST(fontsel->font_clist)->selection;
938       if (selection)
939         {
940           index = GPOINTER_TO_INT (selection->data);
941           gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), index, -1, 0.5, 0);
942         }
943       
944       /* Try to scroll the font style clist to the selected item */
945       selection = GTK_CLIST(fontsel->font_style_clist)->selection;
946       if (selection)
947         {
948           index = GPOINTER_TO_INT (selection->data);
949           gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), index, -1,
950                            0.5, 0);
951         }
952       
953       /* Try to scroll the font size clist to the selected item */
954       selection = GTK_CLIST(fontsel->size_clist)->selection;
955       if (selection)
956         {
957           index = GPOINTER_TO_INT (selection->data);
958           gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), index, -1, 0.5, 0);
959         }
960     }
961 }
962
963
964 /* This is called when a family is selected in the list. */
965 static void
966 gtk_font_selection_select_font (GtkWidget      *w,
967                                 gint            row,
968                                 gint            column,
969                                 GdkEventButton *bevent,
970                                 gpointer        data)
971 {
972   GtkFontSelection *fontsel;
973   FontInfo *font_info;
974   FontInfo *font;
975   
976 #ifdef FONTSEL_DEBUG
977   g_message("In select_font\n");
978 #endif
979   fontsel = GTK_FONT_SELECTION(data);
980   font_info = fontsel_info->font_info;
981   
982   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
983     gtk_widget_grab_focus (w);
984   
985   row = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (fontsel->font_clist), row));
986   font = &font_info[row];
987   gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), font->family);
988   
989   /* If it is already the current font, just return. */
990   if (fontsel->font_index == row)
991     return;
992   
993   fontsel->font_index = row;
994   gtk_font_selection_show_available_styles (fontsel);
995   gtk_font_selection_select_best_style (fontsel, TRUE);
996 }
997
998
999 static gint
1000 gtk_font_selection_on_clist_key_press (GtkWidget        *clist,
1001                                        GdkEventKey      *event,
1002                                        GtkFontSelection *fontsel)
1003 {
1004 #ifdef FONTSEL_DEBUG
1005   g_message("In on_clist_key_press\n");
1006 #endif
1007   if (event->keyval == GDK_Up)
1008     return gtk_font_selection_select_next (fontsel, clist, -1);
1009   else if (event->keyval == GDK_Down)
1010     return gtk_font_selection_select_next (fontsel, clist, 1);
1011   else
1012     return FALSE;
1013 }
1014
1015
1016 static gboolean
1017 gtk_font_selection_select_next (GtkFontSelection *fontsel,
1018                                 GtkWidget        *clist,
1019                                 gint              step)
1020 {
1021   GList *selection;
1022   gint current_row, row;
1023   
1024   selection = GTK_CLIST(clist)->selection;
1025   if (!selection)
1026     return FALSE;
1027   current_row = GPOINTER_TO_INT (selection->data);
1028   
1029   for (row = current_row + step;
1030        row >= 0 && row < GTK_CLIST(clist)->rows;
1031        row += step)
1032     {
1033       /* If this is the style clist, make sure that the item is not a charset
1034          entry. */
1035       if (clist == fontsel->font_style_clist)
1036         if (GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(clist), row)) == -1)
1037           continue;
1038       
1039       /* Now we've found the row to select. */
1040       if (gtk_clist_row_is_visible(GTK_CLIST(clist), row)
1041           != GTK_VISIBILITY_FULL)
1042         gtk_clist_moveto(GTK_CLIST(clist), row, -1, (step < 0) ? 0 : 1, 0);
1043       gtk_clist_select_row(GTK_CLIST(clist), row, 0);
1044       return TRUE;
1045     }
1046   return FALSE;
1047 }
1048
1049
1050 /* This fills the font style clist with all the possible style combinations
1051    for the current font family. */
1052 static void
1053 gtk_font_selection_show_available_styles (GtkFontSelection *fontsel)
1054 {
1055   FontInfo *font;
1056   FontStyle *styles;
1057   gint style, tmpstyle, row;
1058   gint weight_index, slant_index, set_width_index, spacing_index;
1059   gint charset_index;
1060   gchar *weight, *slant, *set_width, *spacing;
1061   gchar *charset = NULL;
1062   gchar *new_item;
1063   gchar buffer[XLFD_MAX_FIELD_LEN * 6 + 2];
1064   GdkColor *inactive_fg, *inactive_bg;
1065   gboolean show_charset;
1066   
1067 #ifdef FONTSEL_DEBUG
1068   g_message("In show_available_styles\n");
1069 #endif
1070   font = &fontsel_info->font_info[fontsel->font_index];
1071   styles = &fontsel_info->font_styles[font->style_index];
1072   
1073   gtk_clist_freeze (GTK_CLIST(fontsel->font_style_clist));
1074   gtk_clist_clear (GTK_CLIST(fontsel->font_style_clist));
1075   
1076   /* First we mark all visible styles as not having been displayed yet,
1077      and check if every style has the same charset. If not then we will
1078      display the charset in the list before the styles. */
1079   show_charset = FALSE;
1080   charset_index = -1;
1081   for (style = 0; style < font->nstyles; style++)
1082     {
1083       if (gtk_font_selection_style_visible(fontsel, font, style))
1084         {
1085           styles[style].flags &= ~DISPLAYED;
1086           
1087           if (charset_index == -1)
1088             charset_index  = styles[style].properties[CHARSET];
1089           else if (charset_index != styles[style].properties[CHARSET])
1090             show_charset = TRUE;
1091         }
1092       else
1093         styles[style].flags |= DISPLAYED;
1094     }
1095   
1096   /* Step through the undisplayed styles, finding the next charset which
1097      hasn't been displayed yet. Then display the charset on one line, if
1098      necessary, and the visible styles indented beneath it. */
1099   inactive_fg = &fontsel->font_style_clist->style->fg[GTK_STATE_INSENSITIVE];
1100   inactive_bg = &fontsel->font_style_clist->style->bg[GTK_STATE_INSENSITIVE];
1101   
1102   for (style = 0; style < font->nstyles; style++)
1103     {
1104       if (styles[style].flags & DISPLAYED) continue;
1105       
1106       if (show_charset)
1107         {
1108           charset_index  = styles[style].properties[CHARSET];
1109           charset  = fontsel_info->properties[CHARSET] [charset_index];
1110           row = gtk_clist_append(GTK_CLIST(fontsel->font_style_clist),
1111                                  &charset);
1112           gtk_clist_set_row_data(GTK_CLIST(fontsel->font_style_clist), row,
1113                                  (gpointer) -1);
1114           gtk_clist_set_foreground(GTK_CLIST(fontsel->font_style_clist),
1115                                    row, inactive_fg);
1116           gtk_clist_set_background(GTK_CLIST(fontsel->font_style_clist),
1117                                    row, inactive_bg);
1118         }
1119       
1120       for (tmpstyle = style; tmpstyle < font->nstyles; tmpstyle++)
1121         {
1122           if (styles[tmpstyle].flags & DISPLAYED
1123               || charset_index != styles[tmpstyle].properties[CHARSET])
1124             continue;
1125           
1126           styles[tmpstyle].flags |= DISPLAYED;
1127           
1128           weight_index    = styles[tmpstyle].properties[WEIGHT];
1129           slant_index     = styles[tmpstyle].properties[SLANT];
1130           set_width_index = styles[tmpstyle].properties[SET_WIDTH];
1131           spacing_index   = styles[tmpstyle].properties[SPACING];
1132           weight    = fontsel_info->properties[WEIGHT]   [weight_index];
1133           slant     = fontsel_info->properties[SLANT]    [slant_index];
1134           set_width = fontsel_info->properties[SET_WIDTH][set_width_index];
1135           spacing   = fontsel_info->properties[SPACING]  [spacing_index];
1136           
1137           /* Convert '(nil)' weights to 'regular', since it looks nicer. */
1138           if      (!g_strcasecmp(weight, "(nil)"))      weight = "regular";
1139           
1140           /* We don't show default values or (nil) in the other properties. */
1141           if      (!g_strcasecmp(slant, "r"))        slant = NULL;
1142           else if (!g_strcasecmp(slant, "(nil)"))    slant = NULL;
1143           else if (!g_strcasecmp(slant, "i"))        slant = "italic";
1144           else if (!g_strcasecmp(slant, "o"))        slant = "oblique";
1145           else if (!g_strcasecmp(slant, "ri"))       slant = "reverse italic";
1146           else if (!g_strcasecmp(slant, "ro"))       slant = "reverse oblique";
1147           else if (!g_strcasecmp(slant, "ot"))       slant = "other";
1148           
1149           if      (!g_strcasecmp(set_width, "normal")) set_width = NULL;
1150           else if (!g_strcasecmp(set_width, "(nil)"))  set_width = NULL;
1151           
1152           if      (!g_strcasecmp(spacing, "p"))        spacing = NULL;
1153           else if (!g_strcasecmp(spacing, "(nil)"))    spacing = NULL;
1154           else if (!g_strcasecmp(spacing, "m"))        spacing = "[M]";
1155           else if (!g_strcasecmp(spacing, "c"))        spacing = "[C]";
1156           
1157           /* Add the strings together, making sure there is 1 space between
1158              them */
1159           strcpy(buffer, weight);
1160           if (slant)
1161             {
1162               strcat(buffer, " ");
1163               strcat(buffer, slant);
1164             }
1165           if (set_width)
1166             {
1167               strcat(buffer, " ");
1168               strcat(buffer, set_width);
1169             }
1170           if (spacing)
1171             {
1172               strcat(buffer, " ");
1173               strcat(buffer, spacing);
1174             }
1175           
1176           new_item = buffer;
1177           row = gtk_clist_append(GTK_CLIST(fontsel->font_style_clist),
1178                                  &new_item);
1179           if (show_charset)
1180             gtk_clist_set_shift(GTK_CLIST(fontsel->font_style_clist), row, 0,
1181                                 0, 4);
1182           gtk_clist_set_row_data(GTK_CLIST(fontsel->font_style_clist), row,
1183                                  GINT_TO_POINTER (tmpstyle));
1184         }
1185     }
1186   
1187   gtk_clist_thaw (GTK_CLIST(fontsel->font_style_clist));
1188 }
1189
1190
1191 /* This selects a style when the user selects a font. It just uses the first
1192    available style at present. I was thinking of trying to maintain the
1193    selected style, e.g. bold italic, when the user selects different fonts.
1194    However, the interface is so easy to use now I'm not sure it's worth it.
1195    Note: This will load a font. */
1196 static void
1197 gtk_font_selection_select_best_style(GtkFontSelection *fontsel,
1198                                      gboolean          use_first)
1199 {
1200   FontInfo *font;
1201   FontStyle *styles;
1202   gint row, prop, style = -1, style_to_find;
1203   gboolean found = FALSE;
1204   
1205 #ifdef FONTSEL_DEBUG
1206   g_message("In select_best_style\n");
1207 #endif
1208   font = &fontsel_info->font_info[fontsel->font_index];
1209   styles = &fontsel_info->font_styles[font->style_index];
1210   
1211   /* If use_first is set, we just find the first style in the list, not
1212      including charset items. */
1213   style_to_find = use_first ? -1 : gtk_font_selection_get_best_match (fontsel);
1214   
1215   for (row = 0; row < GTK_CLIST(fontsel->font_style_clist)->rows; row++)
1216     {
1217       style = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (fontsel->font_style_clist), row));
1218       if (style != -1 && (style_to_find == -1 || style_to_find == style))
1219         {
1220           found = TRUE;
1221           break;
1222         }
1223     }
1224   g_return_if_fail (found);
1225   
1226   fontsel->style = style;
1227   
1228   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
1229     fontsel->property_values[prop] = styles[fontsel->style].properties[prop];
1230   
1231   gtk_clist_select_row(GTK_CLIST(fontsel->font_style_clist), row, 0);
1232   if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist), row)
1233       != GTK_VISIBILITY_FULL)
1234     gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), row, -1, 0.5, 0);
1235   gtk_font_selection_show_available_sizes (fontsel);
1236   gtk_font_selection_select_best_size (fontsel);
1237 }
1238
1239
1240 /* This is called when a style is selected in the list. */
1241 static void
1242 gtk_font_selection_select_style (GtkWidget      *w,
1243                                  gint           row,
1244                                  gint           column,
1245                                  GdkEventButton *bevent,
1246                                  gpointer        data)
1247 {
1248   GtkFontSelection *fontsel;
1249   FontInfo *font_info;
1250   FontInfo *font;
1251   FontStyle *styles;
1252   gint style, prop;
1253   gchar *text;
1254   
1255 #ifdef FONTSEL_DEBUG
1256   g_message("In select_style\n");
1257 #endif
1258   fontsel = GTK_FONT_SELECTION(data);
1259   font_info = fontsel_info->font_info;
1260   font = &font_info[fontsel->font_index];
1261   styles = &fontsel_info->font_styles[font->style_index];
1262   
1263   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
1264     gtk_widget_grab_focus (w);
1265   
1266   /* The style index is stored in the row data, so we just need to copy
1267      the style values into the fontsel and reload the font. */
1268   style = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(fontsel->font_style_clist), row));
1269   
1270   /* Don't allow selection of charset rows. */
1271   if (style == -1)
1272     {
1273       gtk_clist_unselect_row(GTK_CLIST(fontsel->font_style_clist), row, 0);
1274       return;
1275     }
1276   
1277   gtk_clist_get_text(GTK_CLIST(fontsel->font_style_clist), row, 0, &text);
1278   gtk_entry_set_text(GTK_ENTRY(fontsel->font_style_entry), text);
1279   
1280   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
1281     fontsel->property_values[prop] = styles[style].properties[prop];
1282   
1283   if (fontsel->style == style)
1284     return;
1285   
1286   fontsel->style = style;
1287   gtk_font_selection_show_available_sizes (fontsel);
1288   gtk_font_selection_select_best_size (fontsel);
1289 }
1290
1291
1292 static void
1293 gtk_font_selection_toggle_scaled_bitmaps (GtkWidget      *w,
1294                                           gpointer        data)
1295 {
1296   GtkFontSelection *fontsel;
1297   
1298   fontsel = GTK_FONT_SELECTION(data);
1299   
1300   fontsel->scale_bitmapped_fonts
1301     = GTK_TOGGLE_BUTTON(w)->active ? TRUE : FALSE;
1302   if (fontsel->font_index != -1)
1303     {
1304       gtk_font_selection_show_available_sizes (fontsel);
1305       gtk_font_selection_select_best_size (fontsel);
1306     }
1307 }
1308
1309
1310 /* This shows all the available sizes in the size clist, according to the
1311    current metric and the current font & style. */
1312 static void
1313 gtk_font_selection_show_available_sizes (GtkFontSelection *fontsel)
1314 {
1315   FontInfo *font;
1316   FontStyle *styles, *style;
1317   guint16 *standard_sizes, *bitmapped_sizes, bitmap_size;
1318   gint nstandard_sizes, nbitmapped_sizes;
1319   gchar buffer[16], *size;
1320   gfloat bitmap_size_float;
1321   gboolean can_match;
1322   
1323 #ifdef FONTSEL_DEBUG
1324   g_message("In show_available_sizes\n");
1325 #endif
1326   font = &fontsel_info->font_info[fontsel->font_index];
1327   styles = &fontsel_info->font_styles[font->style_index];
1328   style = &styles[fontsel->style];
1329   
1330   standard_sizes = font_sizes;
1331   nstandard_sizes = sizeof(font_sizes) / sizeof(font_sizes[0]);
1332   if (fontsel->metric == POINTS_METRIC)
1333     {
1334       bitmapped_sizes = &fontsel_info->point_sizes[style->point_sizes_index];
1335       nbitmapped_sizes = style->npoint_sizes;
1336     }
1337   else
1338     {
1339       bitmapped_sizes = &fontsel_info->pixel_sizes[style->pixel_sizes_index];
1340       nbitmapped_sizes = style->npixel_sizes;
1341     }
1342   
1343   if (!(fontsel->scale_bitmapped_fonts && style->flags & SCALABLE_BITMAP_FONT)
1344       && !(style->flags & SCALABLE_FONT))
1345     nstandard_sizes = 0;
1346   
1347   gtk_clist_freeze (GTK_CLIST(fontsel->size_clist));
1348   gtk_clist_clear (GTK_CLIST(fontsel->size_clist));
1349   
1350   /* Interleave the standard sizes with the bitmapped sizes so we get a list
1351      of ascending sizes. If the metric is points, we have to convert the
1352      decipoints to points. */
1353   while (nstandard_sizes || nbitmapped_sizes)
1354     {
1355       can_match = TRUE;
1356       if (fontsel->metric == POINTS_METRIC)
1357         {
1358           if (*bitmapped_sizes % 10 != 0)
1359             can_match = FALSE;
1360           bitmap_size = *bitmapped_sizes / 10;
1361           bitmap_size_float = *bitmapped_sizes / 10;
1362         }
1363       else
1364         {
1365           bitmap_size = *bitmapped_sizes;
1366           bitmap_size_float = *bitmapped_sizes;
1367         }
1368       
1369       if (can_match && nstandard_sizes && nbitmapped_sizes
1370           && *standard_sizes == bitmap_size)
1371         {
1372           sprintf(buffer, "%i *", *standard_sizes);
1373           standard_sizes++;
1374           nstandard_sizes--;
1375           bitmapped_sizes++;
1376           nbitmapped_sizes--;
1377         }
1378       else if (nstandard_sizes
1379                && (!nbitmapped_sizes
1380                    || (gfloat)*standard_sizes < bitmap_size_float))
1381         {
1382           sprintf(buffer, "%i", *standard_sizes);
1383           standard_sizes++;
1384           nstandard_sizes--;
1385         }
1386       else
1387         {
1388           if (fontsel->metric == POINTS_METRIC)
1389             {
1390               if (*bitmapped_sizes % 10 == 0)
1391                 sprintf(buffer, "%i *", *bitmapped_sizes / 10);
1392               else
1393                 sprintf(buffer, "%i.%i *", *bitmapped_sizes / 10,
1394                         *bitmapped_sizes % 10);
1395             }
1396           else
1397             {
1398               sprintf(buffer, "%i *", *bitmapped_sizes);
1399             }
1400           bitmapped_sizes++;
1401           nbitmapped_sizes--;
1402         }
1403       size = buffer;
1404       gtk_clist_append(GTK_CLIST(fontsel->size_clist), &size);
1405     }
1406   gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
1407 }
1408
1409
1410 /* If the user hits return in the font size entry, we change to the new font
1411    size. */
1412 static gint
1413 gtk_font_selection_size_key_press (GtkWidget   *w,
1414                                    GdkEventKey *event,
1415                                    gpointer     data)
1416 {
1417   GtkFontSelection *fontsel;
1418   gint new_size;
1419   gfloat new_size_float;
1420   gchar *text;
1421   
1422 #ifdef FONTSEL_DEBUG
1423   g_message("In size_key_press\n");
1424 #endif
1425   fontsel = GTK_FONT_SELECTION(data);
1426   
1427   if (event->keyval == GDK_Return)
1428     {
1429       text = gtk_entry_get_text (GTK_ENTRY (fontsel->size_entry));
1430       if (fontsel->metric == PIXELS_METRIC)
1431         {
1432           new_size = atoi (text);
1433           if (new_size < 2)
1434             new_size = 2;
1435         }
1436       else
1437         {
1438           new_size_float = atof (text) * 10;
1439           new_size = (gint) new_size_float;
1440           if (new_size < 20)
1441             new_size = 20;
1442         }
1443       
1444       /* Remember that this size was set explicitly. */
1445       fontsel->selected_size = new_size;
1446       
1447       /* Check if the font size has changed, and return if it hasn't. */
1448       if (fontsel->size == new_size)
1449         return TRUE;
1450       
1451       fontsel->size = new_size;
1452       gtk_font_selection_select_best_size (fontsel);
1453       return TRUE;
1454     }
1455   
1456   return FALSE;
1457 }
1458
1459
1460 /* This tries to select the closest size to the current size, though it
1461    may have to change the size if only unscaled bitmaps are available.
1462    Note: this will load a font. */
1463 static void
1464 gtk_font_selection_select_best_size(GtkFontSelection *fontsel)
1465 {
1466   FontInfo *font;
1467   FontStyle *styles, *style;
1468   gchar *text;
1469   gint row, best_row = 0, size, size_fraction, best_size = 0, nmatched;
1470   gboolean found = FALSE;
1471   gchar buffer[32];
1472   GList *selection;
1473   
1474 #ifdef FONTSEL_DEBUG
1475   g_message("In select_best_size\n");
1476 #endif
1477   font = &fontsel_info->font_info[fontsel->font_index];
1478   styles = &fontsel_info->font_styles[font->style_index];
1479   style = &styles[fontsel->style];
1480   
1481   /* Find the closest size available in the size clist. If the exact size is
1482      in the list set found to TRUE. */
1483   for (row = 0; row < GTK_CLIST(fontsel->size_clist)->rows; row++)
1484     {
1485       gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
1486       nmatched = sscanf(text, "%i.%i", &size, &size_fraction);
1487       if (fontsel->metric == POINTS_METRIC)
1488         {
1489           size *= 10;
1490           if (nmatched == 2)
1491             size += size_fraction;
1492         }
1493       
1494       if (size == fontsel->selected_size)
1495         {
1496           found = TRUE;
1497           best_size = size;
1498           best_row = row;
1499           break;
1500         }
1501       else if (best_size == 0
1502                || abs(size - fontsel->selected_size)
1503                < (abs(best_size - fontsel->selected_size)))
1504         {
1505           best_size = size;
1506           best_row = row;
1507         }
1508     }
1509   
1510   /* If we aren't scaling bitmapped fonts and this is a bitmapped font, we
1511      need to use the closest size found. */
1512   if (!(fontsel->scale_bitmapped_fonts && style->flags & SCALABLE_BITMAP_FONT)
1513       && !(style->flags & SCALABLE_FONT))
1514     found = TRUE;
1515   
1516   if (found)
1517     {
1518       fontsel->size = best_size;
1519       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
1520       gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), best_row, 0);
1521     }
1522   else
1523     {
1524       fontsel->size = fontsel->selected_size;
1525       selection = GTK_CLIST(fontsel->size_clist)->selection;
1526       if (selection)
1527         gtk_clist_unselect_row(GTK_CLIST(fontsel->size_clist),
1528                                GPOINTER_TO_INT (selection->data), 0);
1529       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
1530       
1531       /* Show the size in the size entry. */
1532       if (fontsel->metric == PIXELS_METRIC)
1533         sprintf(buffer, "%i", fontsel->size);
1534       else
1535         {
1536           if (fontsel->size % 10 == 0)
1537             sprintf(buffer, "%i", fontsel->size / 10);
1538           else
1539             sprintf(buffer, "%i.%i", fontsel->size / 10, fontsel->size % 10);
1540         }
1541       gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
1542     }
1543   gtk_font_selection_load_font (fontsel);
1544 }
1545
1546
1547 /* This is called when a size is selected in the list. */
1548 static void
1549 gtk_font_selection_select_size (GtkWidget      *w,
1550                                 gint            row,
1551                                 gint            column,
1552                                 GdkEventButton *bevent,
1553                                 gpointer        data)
1554 {
1555   GtkFontSelection *fontsel;
1556   gint new_size;
1557   gchar *text;
1558   gchar buffer[16];
1559   gint i;
1560   
1561 #ifdef FONTSEL_DEBUG
1562   g_message("In select_size\n");
1563 #endif
1564   fontsel = GTK_FONT_SELECTION(data);
1565   
1566   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
1567     gtk_widget_grab_focus (w);
1568   
1569   /* Copy the size from the clist to the size entry, but without the bitmapped
1570      marker ('*'). */
1571   gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
1572   i = 0;
1573   while (i < 15 && (text[i] == '.' || (text[i] >= '0' && text[i] <= '9')))
1574     {
1575       buffer[i] = text[i];
1576       i++;
1577     }
1578   buffer[i] = '\0';
1579   gtk_entry_set_text(GTK_ENTRY(fontsel->size_entry), buffer);
1580   
1581   /* Check if the font size has changed, and return if it hasn't. */
1582   new_size = atoi(text);
1583   if (fontsel->metric == POINTS_METRIC)
1584     new_size *= 10;
1585   
1586   if (fontsel->size == new_size)
1587     return;
1588   
1589   /* If the size was selected by the user we set the selected_size. */
1590   fontsel->selected_size = new_size;
1591   
1592   fontsel->size = new_size;
1593   gtk_font_selection_load_font (fontsel);
1594 }
1595
1596
1597 /* This is called when the pixels or points radio buttons are pressed. */
1598 static void
1599 gtk_font_selection_metric_callback (GtkWidget *w,
1600                                     gpointer   data)
1601 {
1602   GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
1603   
1604 #ifdef FONTSEL_DEBUG
1605   g_message("In metric_callback\n");
1606 #endif
1607   if (GTK_TOGGLE_BUTTON(fontsel->pixels_button)->active)
1608     {
1609       if (fontsel->metric == PIXELS_METRIC)
1610         return;
1611       fontsel->metric = PIXELS_METRIC;
1612       fontsel->size = (fontsel->size + 5) / 10;
1613       fontsel->selected_size = (fontsel->selected_size + 5) / 10;
1614     }
1615   else
1616     {
1617       if (fontsel->metric == POINTS_METRIC)
1618         return;
1619       fontsel->metric = POINTS_METRIC;
1620       fontsel->size *= 10;
1621       fontsel->selected_size *= 10;
1622     }
1623   if (fontsel->font_index != -1)
1624     {
1625       gtk_font_selection_show_available_sizes (fontsel);
1626       gtk_font_selection_select_best_size (fontsel);
1627     }
1628 }
1629
1630
1631 /* This searches the given property table and returns the index of the given
1632    string, or 0, which is the wildcard '*' index, if it's not found. */
1633 static guint16
1634 gtk_font_selection_field_to_index (gchar **table,
1635                                    gint    ntable,
1636                                    gchar  *field)
1637 {
1638   gint i;
1639   
1640   for (i = 0; i < ntable; i++)
1641     if (strcmp (field, table[i]) == 0)
1642       return i;
1643   
1644   return 0;
1645 }
1646
1647
1648
1649 /* This attempts to load the current font, and returns TRUE if it succeeds. */
1650 static gboolean
1651 gtk_font_selection_load_font (GtkFontSelection *fontsel)
1652 {
1653   GdkFont *font;
1654   gchar *fontname, *label_text;
1655   
1656   if (fontsel->font)
1657     gdk_font_unref (fontsel->font);
1658   fontsel->font = NULL;
1659   
1660   /* If no family has been selected yet, just return FALSE. */
1661   if (fontsel->font_index == -1)
1662     return FALSE;
1663   
1664   fontname = gtk_font_selection_get_font_name (fontsel);
1665   if (fontname)
1666     {
1667 #ifdef FONTSEL_DEBUG
1668       g_message("Loading: %s\n", fontname);
1669 #endif
1670       font = gdk_font_load (fontname);
1671       g_free(fontname);
1672       
1673       if (font)
1674         {
1675           fontsel->font = font;
1676           /* Make sure the message label is empty, but don't change it unless
1677              it's necessary as it results in a resize of the whole window! */
1678           gtk_label_get(GTK_LABEL(fontsel->message_label), &label_text);
1679           if (strcmp(label_text, ""))
1680             gtk_label_set(GTK_LABEL(fontsel->message_label), "");
1681           gtk_font_selection_update_preview (fontsel);
1682           return TRUE;
1683         }
1684       else 
1685         {
1686           gtk_label_set(GTK_LABEL(fontsel->message_label),
1687                         "The selected font is not available.");
1688         }
1689     }
1690   else
1691     {
1692       gtk_label_set(GTK_LABEL(fontsel->message_label),
1693                     "The selected font is not a valid font.");
1694     }
1695   
1696   return FALSE;
1697 }
1698
1699
1700 /* This sets the font in the preview entry to the selected font, and tries to
1701    make sure that the preview entry is a reasonable size, i.e. so that the
1702    text can be seen with a bit of space to spare. But it tries to avoid
1703    resizing the entry every time the font changes.
1704    This also used to shrink the preview if the font size was decreased, but
1705    that made it awkward if the user wanted to resize the window themself. */
1706 static void
1707 gtk_font_selection_update_preview (GtkFontSelection *fontsel)
1708 {
1709   GtkWidget *preview_entry;
1710   GtkStyle *style;
1711   gint text_height, new_height;
1712   gchar *text;
1713   XFontStruct *xfs;
1714   
1715 #ifdef FONTSEL_DEBUG
1716   g_message("In update_preview\n");
1717 #endif
1718   style = gtk_style_new ();
1719   gdk_font_unref (style->font);
1720   style->font = fontsel->font;
1721   gdk_font_ref (style->font);
1722   
1723   preview_entry = fontsel->preview_entry;
1724   gtk_widget_set_style (preview_entry, style);
1725   gtk_style_unref(style);
1726   
1727   text_height = preview_entry->style->font->ascent
1728     + preview_entry->style->font->descent;
1729   /* We don't ever want to be over MAX_PREVIEW_HEIGHT pixels high. */
1730   new_height = text_height + 20;
1731   if (new_height < INITIAL_PREVIEW_HEIGHT)
1732     new_height = INITIAL_PREVIEW_HEIGHT;
1733   if (new_height > MAX_PREVIEW_HEIGHT)
1734     new_height = MAX_PREVIEW_HEIGHT;
1735   
1736   if ((preview_entry->requisition.height < text_height + 10)
1737       || (preview_entry->requisition.height > text_height + 40))
1738     gtk_widget_set_usize(preview_entry, -1, new_height);
1739   
1740   /* This sets the preview text, if it hasn't been set already. */
1741   text = gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
1742   if (strlen(text) == 0)
1743     gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), PREVIEW_TEXT);
1744   gtk_entry_set_position(GTK_ENTRY(fontsel->preview_entry), 0);
1745   
1746   /* If this is a 2-byte font display a message to say it may not be
1747      displayed properly. */
1748   xfs = GDK_FONT_XFONT(fontsel->font);
1749   if (xfs->min_byte1 != 0 || xfs->max_byte1 != 0)
1750     gtk_label_set(GTK_LABEL(fontsel->message_label),
1751                   "This is a 2-byte font and may not be displayed correctly.");
1752 }
1753
1754
1755 static void
1756 gtk_font_selection_switch_page (GtkWidget       *w,
1757                                 GtkNotebookPage *page,
1758                                 gint             page_num,
1759                                 gpointer         data)
1760 {
1761   GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
1762   
1763   /* This function strangely gets called when the window is destroyed,
1764      so we check here to see if the notebook is visible. */
1765   if (!GTK_WIDGET_VISIBLE(w))
1766     return;
1767   
1768   if (page_num == 0)
1769     gtk_font_selection_filter_fonts(fontsel);
1770   else if (page_num == 1)
1771     gtk_font_selection_show_font_info(fontsel);
1772 }
1773
1774
1775 static void
1776 gtk_font_selection_show_font_info (GtkFontSelection *fontsel)
1777 {
1778   Atom font_atom, atom;
1779   Bool status;
1780   char *name;
1781   gchar *fontname;
1782   gchar field_buffer[XLFD_MAX_FIELD_LEN];
1783   gchar *field;
1784   gint i;
1785   gboolean shown_actual_fields = FALSE;
1786   
1787   fontname = gtk_font_selection_get_font_name(fontsel);
1788   gtk_entry_set_text(GTK_ENTRY(fontsel->requested_font_name),
1789                      fontname ? fontname : "");
1790   
1791   gtk_clist_freeze (GTK_CLIST(fontsel->info_clist));
1792   for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1793     {
1794       if (fontname)
1795         field = gtk_font_selection_get_xlfd_field (fontname, i, field_buffer);
1796       else
1797         field = NULL;
1798       if (field)
1799         {
1800           if (i == XLFD_SLANT)
1801             field = gtk_font_selection_expand_slant_code(field);
1802           else if (i == XLFD_SPACING)
1803             field = gtk_font_selection_expand_spacing_code(field);
1804         }
1805       gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 1,
1806                          field ? field : "");
1807     }
1808   
1809   if (fontsel->font)
1810     {
1811       font_atom = XInternAtom(GDK_DISPLAY(), "FONT", True);
1812       if (font_atom != None)
1813         {
1814           status = XGetFontProperty(GDK_FONT_XFONT(fontsel->font), font_atom,
1815                                     &atom);
1816           if (status == True)
1817             {
1818               name = XGetAtomName(GDK_DISPLAY(), atom);
1819               gtk_entry_set_text(GTK_ENTRY(fontsel->actual_font_name), name);
1820               
1821               for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1822                 {
1823                   field = gtk_font_selection_get_xlfd_field (name, i,
1824                                                              field_buffer);
1825                   if (i == XLFD_SLANT)
1826                     field = gtk_font_selection_expand_slant_code(field);
1827                   else if (i == XLFD_SPACING)
1828                     field = gtk_font_selection_expand_spacing_code(field);
1829                   gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1830                                      field ? field : "");
1831                 }
1832               shown_actual_fields = TRUE;
1833               XFree(name);
1834             }
1835         }
1836     }
1837   if (!shown_actual_fields)
1838     {
1839       gtk_entry_set_text(GTK_ENTRY(fontsel->actual_font_name), "");
1840       for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1841         {
1842           gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1843                              fontname ? "(unknown)" : "");
1844         }
1845     }
1846   gtk_clist_thaw (GTK_CLIST(fontsel->info_clist));
1847   g_free(fontname);
1848 }
1849
1850
1851 static gchar*
1852 gtk_font_selection_expand_slant_code(gchar *slant)
1853 {
1854   if      (!g_strcasecmp(slant, "r"))   return("roman");
1855   else if (!g_strcasecmp(slant, "i"))   return("italic");
1856   else if (!g_strcasecmp(slant, "o"))   return("oblique");
1857   else if (!g_strcasecmp(slant, "ri"))  return("reverse italic");
1858   else if (!g_strcasecmp(slant, "ro"))  return("reverse oblique");
1859   else if (!g_strcasecmp(slant, "ot"))  return("other");
1860   return slant;
1861 }
1862
1863 static gchar*
1864 gtk_font_selection_expand_spacing_code(gchar *spacing)
1865 {
1866   if      (!g_strcasecmp(spacing, "p")) return("proportional");
1867   else if (!g_strcasecmp(spacing, "m")) return("monospaced");
1868   else if (!g_strcasecmp(spacing, "c")) return("char cell");
1869   return spacing;
1870 }
1871
1872
1873 /*****************************************************************************
1874  * These functions all deal with the Filter page.
1875  *****************************************************************************/
1876
1877 /* This is called when an item is selected in one of the filter clists.
1878    We make sure that the first row of the clist, i.e. the wildcard '*', is
1879    selected if and only if none of the other items are selected.
1880    We may need to be careful about triggering other signals. */
1881 static void
1882 gtk_font_selection_select_filter             (GtkWidget      *w,
1883                                               gint            row,
1884                                               gint            column,
1885                                               GdkEventButton *bevent,
1886                                               gpointer        data)
1887 {
1888   gint i;
1889   
1890   if (row == 0)
1891     for (i = 1; i < GTK_CLIST(w)->rows; i++)
1892       gtk_clist_unselect_row(GTK_CLIST(w), i, 0);
1893   else
1894     gtk_clist_unselect_row(GTK_CLIST(w), 0, 0);
1895 }
1896
1897
1898 /* Here a filter item is being deselected. If there are now no items selected
1899    we select the first '*' item, unless that it is the item being deselected,
1900    in which case we select all of the other items. This makes it easy to
1901    select all items in the list except one or two. */
1902 static void
1903 gtk_font_selection_unselect_filter           (GtkWidget      *w,
1904                                               gint            row,
1905                                               gint            column,
1906                                               GdkEventButton *bevent,
1907                                               gpointer        data)
1908 {
1909   gint i;
1910   if (!GTK_CLIST(w)->selection)
1911     {
1912       if (row == 0)
1913         for (i = 1; i < GTK_CLIST(w)->rows; i++)
1914           gtk_clist_select_row(GTK_CLIST(w), i, 0);
1915       else
1916         gtk_clist_select_row(GTK_CLIST(w), 0, 0);
1917     }
1918 }
1919
1920
1921 /* This is called when the main notebook page is selected. It checks if the
1922    filter has changed, an if so it creates the filter settings, and filters the
1923    fonts shown. If an empty filter (all '*'s) is applied, then filtering is
1924    turned off. */
1925 static void
1926 gtk_font_selection_filter_fonts      (GtkFontSelection *fontsel)
1927 {
1928   GtkWidget *clist;
1929   GList *selection;
1930   gboolean empty_filter = TRUE, filter_changed = FALSE;
1931   gint prop, nselected, i, row, index;
1932   
1933 #ifdef FONTSEL_DEBUG
1934   g_message("In filter_fonts\n");
1935 #endif
1936   
1937   /* Check if the filter has changed, and also if it is an empty filter,
1938      i.e. all '*'s are selected. */
1939   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
1940     {
1941       clist = fontsel->filter_clists[prop];
1942       selection = GTK_CLIST(clist)->selection;
1943       nselected = g_list_length(selection);
1944       if (nselected != 1 || GPOINTER_TO_INT (selection->data) != 0)
1945         {
1946           empty_filter = FALSE;
1947           
1948           if (fontsel->property_nfilters[prop] != nselected)
1949             filter_changed = TRUE;
1950           else
1951             {
1952               for (i = 0; i < nselected; i++)
1953                 {
1954                   row = GPOINTER_TO_INT (selection->data);
1955                   index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
1956                   if (fontsel->property_filters[prop][i] != index)
1957                     filter_changed = TRUE;
1958                   selection = selection->next;
1959                 }
1960             }
1961         }
1962       else
1963         {
1964           if (fontsel->property_nfilters[prop] != 0)
1965             filter_changed = TRUE;
1966         }
1967     }
1968   
1969   /* If the filter hasn't changed we just return. */
1970   if (!filter_changed)
1971     return;
1972   
1973 #ifdef FONTSEL_DEBUG
1974   g_message("   filter_fonts: filter has changed\n");
1975 #endif
1976   
1977   /* Setting the toggle buttons state will trigger the callbacks to clear
1978      or apply the filter, unless the font list is already filtered, in
1979      which case we have to call apply_filter here. */
1980   if (empty_filter)
1981     {
1982       gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
1983       gtk_label_set(GTK_LABEL(fontsel->font_label), "Font:");
1984       gtk_font_selection_apply_filter(fontsel);
1985     }
1986   else
1987     {
1988       gtk_widget_set_sensitive(fontsel->filter_button, TRUE);
1989       gtk_label_set(GTK_LABEL(fontsel->font_label), "Font: [Filtered]");
1990       gtk_font_selection_apply_filter(fontsel);
1991     }
1992 }  
1993
1994
1995 /* This clears the filter, showing all fonts and styles again. */
1996 static void
1997 gtk_font_selection_apply_filter     (GtkFontSelection *fontsel)
1998 {
1999   FontInfo *font_info, *font;
2000   GtkWidget *clist;
2001   GList *selection;
2002   gint nfonts, prop, nselected, i, j, row, style, index;
2003   gchar font_buffer[XLFD_MAX_FIELD_LEN * 2 + 4];
2004   gchar *font_item;
2005   gboolean matched, matched_style;
2006   
2007 #ifdef FONTSEL_DEBUG
2008   g_message("In apply_filter\n");
2009 #endif
2010   font_info = fontsel_info->font_info;
2011   nfonts = fontsel_info->nfonts;
2012   
2013   /* Free the old filter data and create the new arrays. */
2014   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2015     {
2016       g_free(fontsel->property_filters[prop]);
2017       
2018       clist = fontsel->filter_clists[prop];
2019       selection = GTK_CLIST(clist)->selection;
2020       nselected = g_list_length(selection);
2021       if (nselected == 1 && GPOINTER_TO_INT (selection->data) == 0)
2022         {
2023           fontsel->property_filters[prop] = NULL;
2024           fontsel->property_nfilters[prop] = 0;
2025         }
2026       else
2027         {
2028           fontsel->property_filters[prop] = g_new(guint16, nselected);
2029           fontsel->property_nfilters[prop] = nselected;
2030           for (i = 0; i < nselected; i++)
2031             {
2032               row = GPOINTER_TO_INT (selection->data);
2033               index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
2034               fontsel->property_filters[prop][i] = index;
2035               selection = selection->next;
2036             }
2037         }
2038     }
2039   
2040   /* Filter the list of fonts. */
2041   gtk_clist_freeze (GTK_CLIST(fontsel->font_clist));
2042   gtk_clist_clear (GTK_CLIST(fontsel->font_clist));
2043   for (i = 0; i < nfonts; i++)
2044     {
2045       font = &font_info[i];
2046       
2047       /* Check if the foundry matches. */
2048       if (fontsel->property_nfilters[FOUNDRY] != 0)
2049         {
2050           matched = FALSE;
2051           for (j = 0; j < fontsel->property_nfilters[FOUNDRY]; j++)
2052             {
2053               if (font->foundry == fontsel->property_filters[FOUNDRY][j])
2054                 {
2055                   matched = TRUE;
2056                   break;
2057                 }
2058             }
2059           if (!matched)
2060             continue;
2061         }
2062       
2063       /* Now check if the other properties are matched in at least one style.*/
2064       matched_style = FALSE;
2065       for (style = 0; style < font->nstyles; style++)
2066         {
2067           if (gtk_font_selection_style_visible(fontsel, font, style))
2068             {
2069               matched_style = TRUE;
2070               break;
2071             }
2072         }
2073       if (!matched_style)
2074         continue;
2075       
2076       /* Insert the font in the clist. */
2077       if ((i > 0 && font->family == font_info[i-1].family)
2078           || (i < nfonts - 1 && font->family == font_info[i+1].family))
2079         {
2080           sprintf(font_buffer, "%s (%s)", font->family,
2081                   fontsel_info->properties[FOUNDRY][font->foundry]);
2082           font_item = font_buffer;
2083           row = gtk_clist_append(GTK_CLIST(fontsel->font_clist), &font_item);
2084         }
2085       else
2086         {
2087           row = gtk_clist_append(GTK_CLIST(fontsel->font_clist),
2088                                  &font->family);
2089         }
2090       gtk_clist_set_row_data(GTK_CLIST(fontsel->font_clist), row,
2091                              GINT_TO_POINTER (i));
2092     }
2093   gtk_clist_thaw (GTK_CLIST(fontsel->font_clist));
2094   
2095   /* Clear the current font and the style clist. */
2096   fontsel->font_index = -1;
2097   if (fontsel->font)
2098     gdk_font_unref(fontsel->font);
2099   fontsel->font = NULL;
2100   gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), "");
2101   gtk_clist_clear (GTK_CLIST(fontsel->font_style_clist));
2102   gtk_entry_set_text(GTK_ENTRY(fontsel->font_style_entry), "");
2103 }
2104
2105
2106 /* Returns TRUE if the style is not currently filtered out. */
2107 static gboolean
2108 gtk_font_selection_style_visible(GtkFontSelection *fontsel,
2109                                  FontInfo         *font,
2110                                  gint              style)
2111 {
2112   FontStyle *styles;
2113   guint16 value;
2114   gint prop, j;
2115   gboolean matched;
2116   
2117   styles = &fontsel_info->font_styles[font->style_index];
2118   
2119   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2120     {
2121       value = styles[style].properties[prop];
2122       
2123       if (fontsel->property_nfilters[prop] != 0)
2124         {
2125           matched = FALSE;
2126           for (j = 0; j < fontsel->property_nfilters[prop]; j++)
2127             {
2128               if (value == fontsel->property_filters[prop][j])
2129                 {
2130                   matched = TRUE;
2131                   break;
2132                 }
2133             }
2134           if (!matched)
2135             return FALSE;
2136         }
2137     }
2138   return TRUE;
2139 }
2140
2141
2142 /* This resets all the filter clists to the wildcard '*' options. */
2143 static void
2144 gtk_font_selection_reset_filter      (GtkWidget      *w,
2145                                       gpointer        data)
2146 {
2147   GtkFontSelection *fontsel;
2148   gint prop;
2149   
2150   fontsel = GTK_FONT_SELECTION(data);
2151   
2152   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2153     gtk_clist_select_row(GTK_CLIST(fontsel->filter_clists[prop]), 0, 0);
2154 }
2155
2156
2157 /* This clears the filter, showing all fonts and styles again. */
2158 static void
2159 gtk_font_selection_on_clear_filter     (GtkWidget      *w,
2160                                         gpointer        data)
2161 {
2162   GtkFontSelection *fontsel;
2163   
2164   fontsel = GTK_FONT_SELECTION(data);
2165   gtk_font_selection_clear_filter(fontsel);
2166 }
2167
2168
2169 /* This clears the filter, showing all fonts and styles again. */
2170 static void
2171 gtk_font_selection_clear_filter     (GtkFontSelection *fontsel)
2172 {
2173   gboolean filtered = FALSE, found_style = FALSE;
2174   gint prop, row, style;
2175   
2176 #ifdef FONTSEL_DEBUG
2177   g_message("In clear_filter\n");
2178 #endif
2179   /* Clear the filter data. */
2180   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2181     {
2182       if (fontsel->property_filters[prop])
2183         filtered = TRUE;
2184       g_free(fontsel->property_filters[prop]);
2185       fontsel->property_filters[prop] = NULL;
2186       fontsel->property_nfilters[prop] = 0;
2187     }
2188   
2189   /* Select all the '*'s on the filter page. */
2190   gtk_font_selection_reset_filter(NULL, fontsel);
2191   
2192   /* Update the main notebook page. */
2193   gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
2194   gtk_label_set(GTK_LABEL(fontsel->font_label), "Font:");
2195   
2196   /* If there is no filter at present just return. */
2197   if (!filtered)
2198     return;
2199   
2200   gtk_font_selection_insert_fonts(fontsel);
2201   
2202   /* Now find the current font & style and select them again. */
2203   if (fontsel->font_index != -1)
2204     {
2205       gtk_clist_select_row(GTK_CLIST(fontsel->font_clist),
2206                            fontsel->font_index, 0);
2207       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_clist),
2208                                    fontsel->font_index) != GTK_VISIBILITY_FULL)
2209         gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), fontsel->font_index,
2210                          -1, 0.5, 0);
2211       gtk_font_selection_show_available_styles (fontsel);
2212       
2213       for (row = 0; row < GTK_CLIST(fontsel->font_style_clist)->rows; row++)
2214         {
2215           style = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(fontsel->font_style_clist), row));
2216           if (style == fontsel->style)
2217             {
2218               found_style = TRUE;
2219               break;
2220             }
2221         }
2222       
2223       if (found_style)
2224         {
2225           gtk_clist_select_row(GTK_CLIST(fontsel->font_style_clist),
2226                                row, 0);
2227           if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist),
2228                                        row) != GTK_VISIBILITY_FULL)
2229             gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), row,
2230                              -1, 0.5, 0);
2231         }
2232     }
2233 }
2234
2235
2236 /*****************************************************************************
2237  * These functions all deal with creating the main class arrays containing
2238  * the data about all available fonts.
2239  *****************************************************************************/
2240 static void
2241 gtk_font_selection_get_fonts (void)
2242 {
2243   gchar **xfontnames;
2244   GSList **fontnames;
2245   gchar *fontname;
2246   GSList * temp_list;
2247   gint num_fonts;
2248   gint i, prop, style, size;
2249   gint npixel_sizes = 0, npoint_sizes = 0;
2250   FontInfo *font;
2251   FontStyle *current_style, *prev_style, *tmp_style;
2252   gboolean matched_style, found_size;
2253   gint pixels, points, res_x, res_y;
2254   gchar field_buffer[XLFD_MAX_FIELD_LEN];
2255   gchar *field;
2256   guint8 flags;
2257   guint16 *pixel_sizes, *point_sizes, *tmp_sizes;
2258   
2259   fontsel_info = g_new (GtkFontSelInfo, 1);
2260   
2261   /* Get a maximum of MAX_FONTS fontnames from the X server.
2262      Use "-*" as the pattern rather than "-*-*-*-*-*-*-*-*-*-*-*-*-*-*" since
2263      the latter may result in fonts being returned which don't actually exist.
2264      xlsfonts also uses "*" so I think it's OK. "-*" gets rid of aliases. */
2265   xfontnames = XListFonts (GDK_DISPLAY(), "-*", MAX_FONTS, &num_fonts);
2266   /* Output a warning if we actually get MAX_FONTS fonts. */
2267   if (num_fonts == MAX_FONTS)
2268     g_warning("MAX_FONTS exceeded. Some fonts may be missing.");
2269   
2270   /* The maximum size of all these tables is the number of font names
2271      returned. We realloc them later when we know exactly how many
2272      unique entries there are. */
2273   fontsel_info->font_info = g_new (FontInfo, num_fonts);
2274   fontsel_info->font_styles = g_new (FontStyle, num_fonts);
2275   fontsel_info->pixel_sizes = g_new (guint16, num_fonts);
2276   fontsel_info->point_sizes = g_new (guint16, num_fonts);
2277   
2278   fontnames = g_new (GSList*, num_fonts);
2279   
2280   /* Create the initial arrays for the property value strings, though they
2281      may be realloc'ed later. Put the wildcard '*' in the first elements. */
2282   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2283     {
2284       fontsel_info->properties[prop] = g_new(gchar*, PROPERTY_ARRAY_INCREMENT);
2285       fontsel_info->space_allocated[prop] = PROPERTY_ARRAY_INCREMENT;
2286       fontsel_info->nproperties[prop] = 1;
2287       fontsel_info->properties[prop][0] = "*";
2288     }
2289   
2290   
2291   /* Insert the font families into the main table, sorted by family and
2292      foundry (fonts with different foundries are placed in seaparate FontInfos.
2293      All fontnames in each family + foundry are placed into the fontnames
2294      array of lists. */
2295   fontsel_info->nfonts = 0;
2296   for (i = 0; i < num_fonts; i++)
2297     {
2298 #ifdef FONTSEL_DEBUG
2299       g_message("%s\n", xfontnames[i]);
2300 #endif
2301       if (gtk_font_selection_is_xlfd_font_name (xfontnames[i]))
2302         gtk_font_selection_insert_font (fontnames, &fontsel_info->nfonts, xfontnames[i]);
2303       else
2304         {
2305 #ifdef FONTSEL_DEBUG
2306           g_warning("Skipping invalid font: %s", xfontnames[i]);
2307 #endif
2308         }
2309     }
2310   
2311   
2312   /* Since many font names will be in the same FontInfo not all of the
2313      allocated FontInfo table will be used, so we will now reallocate it
2314      with the real size. */
2315   fontsel_info->font_info = g_realloc(fontsel_info->font_info,
2316                                       sizeof(FontInfo) * fontsel_info->nfonts);
2317   
2318   
2319   /* Now we work out which choices of weight/slant etc. are valid for each
2320      font. */
2321   fontsel_info->nstyles = 0;
2322   current_style = fontsel_info->font_styles;
2323   for (i = 0; i < fontsel_info->nfonts; i++)
2324     {
2325       font = &fontsel_info->font_info[i];
2326       
2327       /* Use the next free position in the styles array. */
2328       font->style_index = fontsel_info->nstyles;
2329       
2330       /* Now step through each of the fontnames with this family, and create
2331          a style for each fontname. Each style contains the index into the
2332          weights/slants etc. arrays, and a number of pixel/point sizes. */
2333       style = 0;
2334       temp_list = fontnames[i];
2335       while (temp_list)
2336         {
2337           fontname = temp_list->data;
2338           temp_list = temp_list->next;
2339           
2340           for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2341             {
2342               current_style->properties[prop]
2343                 = gtk_font_selection_insert_field (fontname, prop);
2344             }
2345           current_style->pixel_sizes_index = npixel_sizes;
2346           current_style->npixel_sizes = 0;
2347           current_style->point_sizes_index = npoint_sizes;
2348           current_style->npoint_sizes = 0;
2349           current_style->flags = 0;
2350           
2351           
2352           field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
2353                                                      field_buffer);
2354           pixels = atoi(field);
2355           
2356           field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
2357                                                      field_buffer);
2358           points = atoi(field);
2359           
2360           field = gtk_font_selection_get_xlfd_field (fontname,
2361                                                      XLFD_RESOLUTION_X,
2362                                                      field_buffer);
2363           res_x = atoi(field);
2364           
2365           field = gtk_font_selection_get_xlfd_field (fontname,
2366                                                      XLFD_RESOLUTION_Y,
2367                                                      field_buffer);
2368           res_y = atoi(field);
2369           
2370           if (pixels == 0 && points == 0)
2371             {
2372               if (res_x == 0 && res_y == 0)
2373                 flags = SCALABLE_FONT;
2374               else
2375                 {
2376                   flags = SCALABLE_BITMAP_FONT;
2377                   fontsel_info->scaled_bitmaps_available = TRUE;
2378                 }
2379             }
2380           else
2381             flags = BITMAP_FONT;
2382           
2383           /* Now we check to make sure that the style is unique. If it isn't
2384              we forget it. */
2385           prev_style = fontsel_info->font_styles + font->style_index;
2386           matched_style = FALSE;
2387           while (prev_style < current_style)
2388             {
2389               matched_style = TRUE;
2390               for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2391                 {
2392                   if (prev_style->properties[prop]
2393                       != current_style->properties[prop])
2394                     {
2395                       matched_style = FALSE;
2396                       break;
2397                     }
2398                 }
2399               if (matched_style)
2400                 break;
2401               prev_style++;
2402             }
2403           
2404           /* If we matched an existing style, we need to add the pixels &
2405              point sizes to the style. If not, we insert the pixel & point
2406              sizes into our new style. Note that we don't add sizes for
2407              scalable fonts. */
2408           if (matched_style)
2409             {
2410               prev_style->flags |= flags;
2411               if (flags == BITMAP_FONT)
2412                 {
2413                   pixel_sizes = fontsel_info->pixel_sizes
2414                     + prev_style->pixel_sizes_index;
2415                   found_size = FALSE;
2416                   for (size = 0; size < prev_style->npixel_sizes; size++)
2417                     {
2418                       if (pixels == *pixel_sizes)
2419                         {
2420                           found_size = TRUE;
2421                           break;
2422                         }
2423                       else if (pixels < *pixel_sizes)
2424                         break;
2425                       pixel_sizes++;
2426                     }
2427                   /* We need to move all the following pixel sizes up, and also
2428                      update the indexes of any following styles. */
2429                   if (!found_size)
2430                     {
2431                       for (tmp_sizes = fontsel_info->pixel_sizes + npixel_sizes;
2432                            tmp_sizes > pixel_sizes; tmp_sizes--)
2433                         *tmp_sizes = *(tmp_sizes - 1);
2434                       
2435                       *pixel_sizes = pixels;
2436                       npixel_sizes++;
2437                       prev_style->npixel_sizes++;
2438                       
2439                       tmp_style = prev_style + 1;
2440                       while (tmp_style < current_style)
2441                         {
2442                           tmp_style->pixel_sizes_index++;
2443                           tmp_style++;
2444                         }
2445                     }
2446                   
2447                   point_sizes = fontsel_info->point_sizes
2448                     + prev_style->point_sizes_index;
2449                   found_size = FALSE;
2450                   for (size = 0; size < prev_style->npoint_sizes; size++)
2451                     {
2452                       if (points == *point_sizes)
2453                         {
2454                           found_size = TRUE;
2455                           break;
2456                         }
2457                       else if (points < *point_sizes)
2458                         break;
2459                       point_sizes++;
2460                     }
2461                   /* We need to move all the following point sizes up, and also
2462                      update the indexes of any following styles. */
2463                   if (!found_size)
2464                     {
2465                       for (tmp_sizes = fontsel_info->point_sizes + npoint_sizes;
2466                            tmp_sizes > point_sizes; tmp_sizes--)
2467                         *tmp_sizes = *(tmp_sizes - 1);
2468                       
2469                       *point_sizes = points;
2470                       npoint_sizes++;
2471                       prev_style->npoint_sizes++;
2472                       
2473                       tmp_style = prev_style + 1;
2474                       while (tmp_style < current_style)
2475                         {
2476                           tmp_style->point_sizes_index++;
2477                           tmp_style++;
2478                         }
2479                     }
2480                 }
2481             }
2482           else
2483             {
2484               current_style->flags = flags;
2485               if (flags == BITMAP_FONT)
2486                 {
2487                   fontsel_info->pixel_sizes[npixel_sizes++] = pixels;
2488                   current_style->npixel_sizes = 1;
2489                   fontsel_info->point_sizes[npoint_sizes++] = points;
2490                   current_style->npoint_sizes = 1;
2491                 }
2492               style++;
2493               fontsel_info->nstyles++;
2494               current_style++;
2495             }
2496         }
2497       g_slist_free(fontnames[i]);
2498       
2499       /* Set nstyles to the real value, minus duplicated fontnames.
2500          Note that we aren't using all the allocated memory if fontnames are
2501          duplicated. */
2502       font->nstyles = style;
2503     }
2504   
2505   /* Since some repeated styles may be skipped we won't have used all the
2506      allocated space, so we will now reallocate it with the real size. */
2507   fontsel_info->font_styles = g_realloc(fontsel_info->font_styles,
2508                                         sizeof(FontStyle) * fontsel_info->nstyles);
2509   fontsel_info->pixel_sizes = g_realloc(fontsel_info->pixel_sizes,
2510                                         sizeof(guint16) * npixel_sizes);
2511   fontsel_info->point_sizes = g_realloc(fontsel_info->point_sizes,
2512                                         sizeof(guint16) * npoint_sizes);
2513   g_free(fontnames);
2514   XFreeFontNames (xfontnames);
2515   
2516   
2517   /* Debugging Output */
2518   /* This outputs all FontInfos. */
2519 #ifdef FONTSEL_DEBUG
2520   g_message("\n\n Font Family           Weight    Slant     Set Width Spacing   Charset\n\n");
2521   for (i = 0; i < fontsel_info->nfonts; i++)
2522     {
2523       FontInfo *font = &fontsel_info->font_info[i];
2524       FontStyle *styles = fontsel_info->font_styles + font->style_index;
2525       for (style = 0; style < font->nstyles; style++)
2526         {
2527           g_message("%5i %-16.16s ", i, font->family);
2528           for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2529             g_message("%-9.9s ",
2530                       fontsel_info->properties[prop][styles->properties[prop]]);
2531           g_message("\n      ");
2532           
2533           if (styles->flags & BITMAP_FONT)
2534             g_message("Bitmapped font  ");
2535           if (styles->flags & SCALABLE_FONT)
2536             g_message("Scalable font  ");
2537           if (styles->flags & SCALABLE_BITMAP_FONT)
2538             g_message("Scalable-Bitmapped font  ");
2539           g_message("\n");
2540           
2541           if (styles->npixel_sizes)
2542             {
2543               g_message("      Pixel sizes: ");
2544               tmp_sizes = fontsel_info->pixel_sizes + styles->pixel_sizes_index;
2545               for (size = 0; size < styles->npixel_sizes; size++)
2546                 g_message("%i ", *tmp_sizes++);
2547               g_message("\n");
2548             }
2549           
2550           if (styles->npoint_sizes)
2551             {
2552               g_message("      Point sizes: ");
2553               tmp_sizes = fontsel_info->point_sizes + styles->point_sizes_index;
2554               for (size = 0; size < styles->npoint_sizes; size++)
2555                 g_message("%i ", *tmp_sizes++);
2556               g_message("\n");
2557             }
2558           
2559           g_message("\n");
2560           styles++;
2561         }
2562     }
2563   /* This outputs all available properties. */
2564   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2565     {
2566       g_message("Property: %s\n", xlfd_field_names[xlfd_index[prop]]);
2567       for (i = 0; i < fontsel_info->nproperties[prop]; i++)
2568         g_message("  %s\n", fontsel_info->properties[prop][i]);
2569     }
2570 #endif
2571 }
2572
2573 /* This inserts the given fontname into the FontInfo table.
2574    If a FontInfo already exists with the same family and foundry, then the
2575    fontname is added to the FontInfos list of fontnames, else a new FontInfo
2576    is created and inserted in alphabetical order in the table. */
2577 static void
2578 gtk_font_selection_insert_font (GSList                *fontnames[],
2579                                 gint                  *ntable,
2580                                 gchar                 *fontname)
2581 {
2582   FontInfo *table;
2583   FontInfo temp_info;
2584   GSList *temp_fontname;
2585   gchar *family;
2586   gboolean family_exists = FALSE;
2587   gint foundry;
2588   gint lower, upper;
2589   gint middle, cmp;
2590   gchar family_buffer[XLFD_MAX_FIELD_LEN];
2591   
2592   table = fontsel_info->font_info;
2593   
2594   /* insert a fontname into a table */
2595   family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
2596                                               family_buffer);
2597   if (!family)
2598     return;
2599   
2600   foundry = gtk_font_selection_insert_field (fontname, FOUNDRY);
2601   
2602   lower = 0;
2603   if (*ntable > 0)
2604     {
2605       /* Do a binary search to determine if we have already encountered
2606        *  a font with this family & foundry. */
2607       upper = *ntable;
2608       while (lower < upper)
2609         {
2610           middle = (lower + upper) >> 1;
2611           
2612           cmp = strcmp (family, table[middle].family);
2613           /* If the family matches we sort by the foundry. */
2614           if (cmp == 0)
2615             {
2616               family_exists = TRUE;
2617               family = table[middle].family;
2618               cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
2619                            fontsel_info->properties[FOUNDRY][table[middle].foundry]);
2620             }
2621           
2622           if (cmp == 0)
2623             {
2624               fontnames[middle] = g_slist_prepend (fontnames[middle],
2625                                                    fontname);
2626               return;
2627             }
2628           else if (cmp < 0)
2629             upper = middle;
2630           else
2631             lower = middle+1;
2632         }
2633     }
2634   
2635   /* Add another entry to the table for this new font family */
2636   temp_info.family = family_exists ? family : g_strdup(family);
2637   temp_info.foundry = foundry;
2638   temp_fontname = g_slist_prepend (NULL, fontname);
2639   
2640   (*ntable)++;
2641   
2642   /* Quickly insert the entry into the table in sorted order
2643    *  using a modification of insertion sort and the knowledge
2644    *  that the entries proper position in the table was determined
2645    *  above in the binary search and is contained in the "lower"
2646    *  variable. */
2647   if (*ntable > 1)
2648     {
2649       upper = *ntable - 1;
2650       while (lower != upper)
2651         {
2652           table[upper] = table[upper-1];
2653           fontnames[upper] = fontnames[upper-1];
2654           upper--;
2655         }
2656     }
2657   table[lower] = temp_info;
2658   fontnames[lower] = temp_fontname;
2659 }
2660
2661
2662 /* This checks that the specified field of the given fontname is in the
2663    appropriate properties array. If not it is added. Thus eventually we get
2664    arrays of all possible weights/slants etc. It returns the array index. */
2665 static gint
2666 gtk_font_selection_insert_field (gchar                 *fontname,
2667                                  gint                   prop)
2668 {
2669   gchar field_buffer[XLFD_MAX_FIELD_LEN];
2670   gchar *field;
2671   guint16 index;
2672   
2673   field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
2674                                              field_buffer);
2675   if (!field)
2676     return 0;
2677   
2678   /* If the field is already in the array just return its index. */
2679   for (index = 0; index < fontsel_info->nproperties[prop]; index++)
2680     if (!strcmp(field, fontsel_info->properties[prop][index]))
2681       return index;
2682   
2683   /* Make sure we have enough space to add the field. */
2684   if (fontsel_info->nproperties[prop] == fontsel_info->space_allocated[prop])
2685     {
2686       fontsel_info->space_allocated[prop] += PROPERTY_ARRAY_INCREMENT;
2687       fontsel_info->properties[prop] = g_realloc(fontsel_info->properties[prop],
2688                                                  sizeof(gchar*)
2689                                                  * fontsel_info->space_allocated[prop]);
2690     }
2691   
2692   /* Add the new field. */
2693   index = fontsel_info->nproperties[prop];
2694   fontsel_info->properties[prop][index] = g_strdup(field);
2695   fontsel_info->nproperties[prop]++;
2696   return index;
2697 }
2698
2699
2700 /*****************************************************************************
2701  * These functions are the main public interface for getting/setting the font.
2702  *****************************************************************************/
2703
2704 GdkFont*
2705 gtk_font_selection_get_font (GtkFontSelection *fontsel)
2706 {
2707   g_return_val_if_fail (fontsel != NULL, NULL);
2708   return fontsel->font;
2709 }
2710
2711
2712 gchar *
2713 gtk_font_selection_get_font_name (GtkFontSelection *fontsel)
2714 {
2715   FontInfo *font;
2716   gchar *family_str, *foundry_str;
2717   gchar *property_str[GTK_NUM_STYLE_PROPERTIES];
2718   gint prop;
2719   
2720   g_return_val_if_fail (fontsel != NULL, NULL);
2721   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), NULL);
2722   
2723   /* If no family has been selected return NULL. */
2724   if (fontsel->font_index == -1)
2725     return NULL;
2726   
2727   font = &fontsel_info->font_info[fontsel->font_index];
2728   family_str = font->family;
2729   foundry_str = fontsel_info->properties[FOUNDRY][font->foundry];
2730   
2731   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2732     {
2733       property_str[prop]
2734         = fontsel_info->properties[prop][fontsel->property_values[prop]];
2735       if (strcmp (property_str[prop], "(nil)") == 0)
2736         property_str[prop] = "";
2737     }
2738   
2739   return gtk_font_selection_create_xlfd (fontsel->size,
2740                                          fontsel->metric,
2741                                          foundry_str,
2742                                          family_str,
2743                                          property_str[WEIGHT],
2744                                          property_str[SLANT],
2745                                          property_str[SET_WIDTH],
2746                                          property_str[SPACING],
2747                                          property_str[CHARSET]);
2748 }
2749
2750
2751 /* This returns the style with the best match to the current fontsel setting */
2752 static gint
2753 gtk_font_selection_get_best_match(GtkFontSelection *fontsel)
2754 {
2755   FontInfo *font;
2756   FontStyle *styles;
2757   gint prop, style, best_style = 0, matched, best_matched = 0;
2758   
2759   font = &fontsel_info->font_info[fontsel->font_index];
2760   styles = &fontsel_info->font_styles[font->style_index];
2761   
2762   /* Find the style with the most matches. */
2763   for (style = 0; style < font->nstyles; style++)
2764     {
2765       matched = 0;
2766       for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2767         {
2768           if (fontsel->property_values[prop] == styles[style].properties[prop])
2769             matched++;
2770         }
2771       if (matched > best_matched)
2772         {
2773           best_matched = matched;
2774           best_style = style;
2775         }
2776     }
2777   
2778   return best_style;
2779 }
2780
2781
2782 /* This sets the current font, selecting the appropriate clist rows.
2783    First we check the fontname is valid and try to find the font family
2784    - i.e. the name in the main list. If we can't find that, then just return.
2785    Next we try to set each of the properties according to the fontname.
2786    Finally we select the font family & style in the clists. */
2787 gboolean
2788 gtk_font_selection_set_font_name (GtkFontSelection *fontsel,
2789                                   const gchar      *fontname)
2790 {
2791   gchar *family, *field;
2792   gint index, prop, size;
2793   guint16 foundry, value;
2794   gchar family_buffer[XLFD_MAX_FIELD_LEN];
2795   gchar field_buffer[XLFD_MAX_FIELD_LEN];
2796   gchar buffer[16];
2797   
2798   g_return_val_if_fail (fontsel != NULL, FALSE);
2799   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), FALSE);
2800   g_return_val_if_fail (fontname != NULL, FALSE);
2801   
2802   /* Check it is a valid fontname. */
2803   if (!gtk_font_selection_is_xlfd_font_name(fontname))
2804     return FALSE;
2805   
2806   family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
2807                                               family_buffer);
2808   if (!family)
2809     return FALSE;
2810   
2811   field = gtk_font_selection_get_xlfd_field (fontname, XLFD_FOUNDRY,
2812                                              field_buffer);
2813   foundry = gtk_font_selection_field_to_index (fontsel_info->properties[FOUNDRY],
2814                                                fontsel_info->nproperties[FOUNDRY],
2815                                                field);
2816   
2817   index = gtk_font_selection_find_font(fontsel, family, foundry);
2818   if (index == -1)
2819     return FALSE;
2820   
2821   /* Convert the property fields into indices and set them. */
2822   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++) 
2823     {
2824       field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
2825                                                  field_buffer);
2826       value = gtk_font_selection_field_to_index (fontsel_info->properties[prop],
2827                                                  fontsel_info->nproperties[prop],
2828                                                  field);
2829       fontsel->property_values[prop] = value;
2830     }
2831   
2832   field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
2833                                              field_buffer);
2834   size = atoi(field);
2835   if (size > 0)
2836     {
2837       if (size < 20)
2838         size = 20;
2839       fontsel->size = fontsel->selected_size = size;
2840       fontsel->metric = POINTS_METRIC;
2841       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(fontsel->points_button),
2842                                   TRUE);
2843       if (size % 10 == 0)
2844         sprintf (buffer, "%i", size / 10);
2845       else
2846         sprintf (buffer, "%i.%i", size / 10, size % 10);
2847     }
2848   else
2849     {
2850       field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
2851                                                  field_buffer);
2852       size = atoi(field);
2853       if (size < 2)
2854         size = 2;
2855       fontsel->size = fontsel->selected_size = size;
2856       fontsel->metric = PIXELS_METRIC;
2857       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(fontsel->pixels_button),
2858                                   TRUE);
2859       sprintf (buffer, "%i", size);
2860     }
2861   gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
2862   
2863   /* Clear any current filter. */
2864   gtk_font_selection_clear_filter(fontsel);
2865   
2866   /* Now find the best style match. */
2867   fontsel->font_index = index;
2868   gtk_clist_select_row(GTK_CLIST(fontsel->font_clist), index, 0);
2869   if (GTK_WIDGET_MAPPED (fontsel->font_clist))
2870     gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), index, -1, 0.5, 0);
2871   else
2872     fontsel->scroll_on_expose = TRUE;
2873   
2874   gtk_font_selection_show_available_styles (fontsel);
2875   /* This will load the font. */
2876   gtk_font_selection_select_best_style (fontsel, FALSE);
2877   
2878   return TRUE;
2879 }
2880
2881
2882 /* Returns the index of the given family, or -1 if not found */
2883 static gint
2884 gtk_font_selection_find_font (GtkFontSelection *fontsel,
2885                               gchar            *family,
2886                               guint16           foundry)
2887 {
2888   FontInfo *font_info;
2889   gint lower, upper, middle = -1, cmp, nfonts;
2890   
2891   font_info = fontsel_info->font_info;
2892   nfonts = fontsel_info->nfonts;
2893   if (nfonts == 0)
2894     return -1;
2895   
2896   /* Do a binary search to find the font family. */
2897   lower = 0;
2898   upper = nfonts;
2899   while (lower < upper)
2900     {
2901       middle = (lower + upper) >> 1;
2902       
2903       cmp = strcmp (family, font_info[middle].family);
2904       if (cmp == 0)
2905         cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
2906                      fontsel_info->properties[FOUNDRY][font_info[middle].foundry]);
2907       
2908       if (cmp == 0)
2909         return middle;
2910       else if (cmp < 0)
2911         upper = middle;
2912       else if (cmp > 0)
2913         lower = middle+1;
2914     }
2915   
2916   /* If we can't match family & foundry see if just the family matches */
2917   if (!strcmp (family, font_info[middle].family))
2918     return middle;
2919   
2920   return -1;
2921 }
2922
2923
2924 /* This returns the text in the preview entry. You should copy the returned
2925    text if you need it. */
2926 gchar*
2927 gtk_font_selection_get_preview_text  (GtkFontSelection *fontsel)
2928 {
2929   return gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
2930 }
2931
2932
2933 /* This sets the text in the preview entry. */
2934 void
2935 gtk_font_selection_set_preview_text  (GtkFontSelection *fontsel,
2936                                       const gchar         *text)
2937 {
2938   gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), text);
2939 }
2940
2941
2942 /*****************************************************************************
2943  * These functions all deal with X Logical Font Description (XLFD) fontnames.
2944  * See the freely available documentation about this.
2945  *****************************************************************************/
2946
2947 /*
2948  * Returns TRUE if the fontname is a valid XLFD.
2949  * (It just checks if the number of dashes is 14, and that each
2950  * field < XLFD_MAX_FIELD_LEN  characters long - that's not in the XLFD but it
2951  * makes it easier for me).
2952  */
2953 static gboolean
2954 gtk_font_selection_is_xlfd_font_name (const gchar *fontname)
2955 {
2956   gint i = 0;
2957   gint field_len = 0;
2958   
2959   while (*fontname)
2960     {
2961       if (*fontname++ == '-')
2962         {
2963           if (field_len > XLFD_MAX_FIELD_LEN) return FALSE;
2964           field_len = 0;
2965           i++;
2966         }
2967       else
2968         field_len++;
2969     }
2970   
2971   return (i == 14) ? TRUE : FALSE;
2972 }
2973
2974 /*
2975  * This fills the buffer with the specified field from the X Logical Font
2976  * Description name, and returns it. If fontname is NULL or the field is
2977  * longer than XFLD_MAX_FIELD_LEN it returns NULL.
2978  * Note: For the charset field, we also return the encoding, e.g. 'iso8859-1'.
2979  */
2980 static gchar*
2981 gtk_font_selection_get_xlfd_field (const gchar *fontname,
2982                                    FontField    field_num,
2983                                    gchar       *buffer)
2984 {
2985   const gchar *t1, *t2;
2986   gint countdown, len, num_dashes;
2987   
2988   if (!fontname)
2989     return NULL;
2990   
2991   /* we assume this is a valid fontname...that is, it has 14 fields */
2992   
2993   countdown = field_num;
2994   t1 = fontname;
2995   while (*t1 && (countdown >= 0))
2996     if (*t1++ == '-')
2997       countdown--;
2998   
2999   num_dashes = (field_num == XLFD_CHARSET) ? 2 : 1;
3000   for (t2 = t1; *t2; t2++)
3001     { 
3002       if (*t2 == '-' && --num_dashes == 0)
3003         break;
3004     }
3005   
3006   if (t1 != t2)
3007     {
3008       /* Check we don't overflow the buffer */
3009       len = (long) t2 - (long) t1;
3010       if (len > XLFD_MAX_FIELD_LEN - 1)
3011         return NULL;
3012       strncpy (buffer, t1, len);
3013       buffer[len] = 0;
3014     }
3015   else
3016     strcpy(buffer, "(nil)");
3017   
3018   return buffer;
3019 }
3020
3021 /*
3022  * This returns a X Logical Font Description font name, given all the pieces.
3023  * Note: this retval must be freed by the caller.
3024  */
3025 static gchar *
3026 gtk_font_selection_create_xlfd (gint              size,
3027                                 GtkFontMetricType metric,
3028                                 gchar             *foundry,
3029                                 gchar             *family,
3030                                 gchar             *weight,
3031                                 gchar             *slant,
3032                                 gchar             *set_width,
3033                                 gchar             *spacing,
3034                                 gchar             *charset)
3035 {
3036   gchar buffer[16];
3037   gchar *pixel_size = "*", *point_size = "*", *fontname;
3038   gint length;
3039   
3040   if (size <= 0)
3041     return NULL;
3042   
3043   sprintf (buffer, "%d", (int) size);
3044   if (metric == PIXELS_METRIC)
3045     pixel_size = buffer;
3046   else
3047     point_size = buffer;
3048   
3049   /* Note: be careful here - don't overrun the allocated memory. */
3050   length = strlen(foundry) + strlen(family) + strlen(weight) + strlen(slant)
3051     + strlen(set_width) + strlen(pixel_size) + strlen(point_size)
3052     + strlen(spacing) + strlen(charset)
3053     + 1 + 1 + 1 + 1 + 1 + 3 + 1 + 5 + 3
3054     + 1 /* for the terminating '\0'. */;
3055   
3056   fontname = g_new(gchar, length);
3057   /* **NOTE**: If you change this string please change length above! */
3058   sprintf(fontname, "-%s-%s-%s-%s-%s-*-%s-%s-*-*-%s-*-%s",
3059           foundry, family, weight, slant, set_width, pixel_size,
3060           point_size, spacing, charset);
3061   return fontname;
3062 }
3063
3064
3065
3066 /*****************************************************************************
3067  * GtkFontSelectionDialog
3068  *****************************************************************************/
3069
3070 guint
3071 gtk_font_selection_dialog_get_type (void)
3072 {
3073   static guint font_selection_dialog_type = 0;
3074   
3075   if (!font_selection_dialog_type)
3076     {
3077       GtkTypeInfo fontsel_diag_info =
3078       {
3079         "GtkFontSelectionDialog",
3080         sizeof (GtkFontSelectionDialog),
3081         sizeof (GtkFontSelectionDialogClass),
3082         (GtkClassInitFunc) gtk_font_selection_dialog_class_init,
3083         (GtkObjectInitFunc) gtk_font_selection_dialog_init,
3084         /* reserved_1 */ NULL,
3085         /* reserved_2 */ NULL,
3086         (GtkClassInitFunc) NULL,
3087       };
3088       
3089       font_selection_dialog_type = gtk_type_unique (GTK_TYPE_WINDOW, &fontsel_diag_info);
3090     }
3091   
3092   return font_selection_dialog_type;
3093 }
3094
3095 static void
3096 gtk_font_selection_dialog_class_init (GtkFontSelectionDialogClass *klass)
3097 {
3098   GtkObjectClass *object_class;
3099   
3100   object_class = (GtkObjectClass*) klass;
3101   
3102   font_selection_dialog_parent_class = gtk_type_class (GTK_TYPE_WINDOW);
3103 }
3104
3105 static void
3106 gtk_font_selection_dialog_init (GtkFontSelectionDialog *fontseldiag)
3107 {
3108   fontseldiag->dialog_width = -1;
3109   fontseldiag->auto_resize = TRUE;
3110   
3111   gtk_widget_set_events(GTK_WIDGET(fontseldiag), GDK_STRUCTURE_MASK);
3112   gtk_signal_connect (GTK_OBJECT (fontseldiag), "configure_event",
3113                       (GtkSignalFunc) gtk_font_selection_dialog_on_configure,
3114                       fontseldiag);
3115   
3116   gtk_container_border_width (GTK_CONTAINER (fontseldiag), 4);
3117   gtk_window_set_policy(GTK_WINDOW(fontseldiag), FALSE, TRUE, TRUE);
3118   
3119   fontseldiag->main_vbox = gtk_vbox_new (FALSE, 4);
3120   gtk_widget_show (fontseldiag->main_vbox);
3121   gtk_container_add (GTK_CONTAINER (fontseldiag), fontseldiag->main_vbox);
3122   
3123   fontseldiag->fontsel = gtk_font_selection_new();
3124   gtk_widget_show (fontseldiag->fontsel);
3125   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
3126                       fontseldiag->fontsel, TRUE, TRUE, 0);
3127   
3128   /* Create the action area */
3129   fontseldiag->action_area = gtk_hbutton_box_new ();
3130   gtk_button_box_set_layout(GTK_BUTTON_BOX(fontseldiag->action_area),
3131                             GTK_BUTTONBOX_END);
3132   gtk_button_box_set_spacing(GTK_BUTTON_BOX(fontseldiag->action_area), 5);
3133   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
3134                       fontseldiag->action_area, FALSE, FALSE, 0);
3135   gtk_widget_show (fontseldiag->action_area);
3136   
3137   fontseldiag->ok_button = gtk_button_new_with_label("OK");
3138   GTK_WIDGET_SET_FLAGS (fontseldiag->ok_button, GTK_CAN_DEFAULT);
3139   gtk_widget_show(fontseldiag->ok_button);
3140   gtk_box_pack_start (GTK_BOX (fontseldiag->action_area),
3141                       fontseldiag->ok_button, TRUE, TRUE, 0);
3142   gtk_widget_grab_default (fontseldiag->ok_button);
3143   
3144   fontseldiag->apply_button = gtk_button_new_with_label("Apply");
3145   GTK_WIDGET_SET_FLAGS (fontseldiag->apply_button, GTK_CAN_DEFAULT);
3146   /*gtk_widget_show(fontseldiag->apply_button);*/
3147   gtk_box_pack_start (GTK_BOX(fontseldiag->action_area),
3148                       fontseldiag->apply_button, TRUE, TRUE, 0);
3149   
3150   fontseldiag->cancel_button = gtk_button_new_with_label("Cancel");
3151   GTK_WIDGET_SET_FLAGS (fontseldiag->cancel_button, GTK_CAN_DEFAULT);
3152   gtk_widget_show(fontseldiag->cancel_button);
3153   gtk_box_pack_start (GTK_BOX(fontseldiag->action_area),
3154                       fontseldiag->cancel_button, TRUE, TRUE, 0);
3155   
3156   
3157 }
3158
3159 GtkWidget*
3160 gtk_font_selection_dialog_new   (const gchar      *title)
3161 {
3162   GtkFontSelectionDialog *fontseldiag;
3163   
3164   fontseldiag = gtk_type_new (GTK_TYPE_FONT_SELECTION_DIALOG);
3165   gtk_window_set_title (GTK_WINDOW (fontseldiag),
3166                         title ? title : "Font Selection");
3167   
3168   return GTK_WIDGET (fontseldiag);
3169 }
3170
3171 gchar*
3172 gtk_font_selection_dialog_get_font_name (GtkFontSelectionDialog *fsd)
3173 {
3174   return gtk_font_selection_get_font_name(GTK_FONT_SELECTION(fsd->fontsel));
3175 }
3176
3177 GdkFont*
3178 gtk_font_selection_dialog_get_font      (GtkFontSelectionDialog *fsd)
3179 {
3180   return gtk_font_selection_get_font(GTK_FONT_SELECTION(fsd->fontsel));
3181 }
3182
3183 gboolean
3184 gtk_font_selection_dialog_set_font_name (GtkFontSelectionDialog *fsd,
3185                                          const gchar      *fontname)
3186 {
3187   return gtk_font_selection_set_font_name(GTK_FONT_SELECTION(fsd->fontsel),
3188                                           fontname);
3189   
3190 }
3191
3192 gchar*
3193 gtk_font_selection_dialog_get_preview_text (GtkFontSelectionDialog *fsd)
3194 {
3195   return gtk_font_selection_get_preview_text(GTK_FONT_SELECTION(fsd->fontsel));
3196 }
3197
3198 void
3199 gtk_font_selection_dialog_set_preview_text (GtkFontSelectionDialog *fsd,
3200                                             const gchar   *text)
3201 {
3202   gtk_font_selection_set_preview_text(GTK_FONT_SELECTION(fsd->fontsel), text);
3203 }
3204
3205
3206 /* This turns auto-shrink off if the user resizes the width of the dialog.
3207    It also turns it back on again if the user resizes it back to its normal
3208    width. */
3209 static gint
3210 gtk_font_selection_dialog_on_configure (GtkWidget         *widget,
3211                                         GdkEventConfigure *event,
3212                                         GtkFontSelectionDialog *fsd)
3213 {
3214   /* This sets the initial width. */
3215   if (fsd->dialog_width == -1)
3216     fsd->dialog_width = event->width;
3217   else if (fsd->auto_resize && fsd->dialog_width != event->width)
3218     {
3219       fsd->auto_resize = FALSE;
3220       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, FALSE);
3221     }
3222   else if (!fsd->auto_resize && fsd->dialog_width == event->width)
3223     {
3224       fsd->auto_resize = TRUE;
3225       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, TRUE);
3226     }
3227   
3228   return FALSE;
3229 }