]> Pileus Git - ~andy/gtk/blob - gtk/gtkfontsel.c
changed reversed_[12] to reserved_[12] in gtk_*_get_type functions.
[~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 guint
389 gtk_font_selection_get_type()
390 {
391   static guint font_selection_type = 0;
392
393   if(!font_selection_type)
394     {
395       GtkTypeInfo fontsel_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_notebook_get_type(),
408                                              &fontsel_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_notebook_get_type ());
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_font_selection_get_type ());
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_print("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_print("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_print("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_print("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_print("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_print("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_print("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_FONT))
1344     nstandard_sizes = 0;
1345
1346   gtk_clist_freeze (GTK_CLIST(fontsel->size_clist));
1347   gtk_clist_clear (GTK_CLIST(fontsel->size_clist));
1348
1349   /* Interleave the standard sizes with the bitmapped sizes so we get a list
1350      of ascending sizes. If the metric is points, we have to convert the
1351      decipoints to points. */
1352   while (nstandard_sizes || nbitmapped_sizes)
1353     {
1354       can_match = TRUE;
1355       if (fontsel->metric == POINTS_METRIC)
1356         {
1357           if (*bitmapped_sizes % 10 != 0)
1358             can_match = FALSE;
1359           bitmap_size = *bitmapped_sizes / 10;
1360           bitmap_size_float = *bitmapped_sizes / 10;
1361         }
1362       else
1363         {
1364           bitmap_size = *bitmapped_sizes;
1365           bitmap_size_float = *bitmapped_sizes;
1366         }
1367
1368       if (can_match && nstandard_sizes && nbitmapped_sizes
1369           && *standard_sizes == bitmap_size)
1370         {
1371           sprintf(buffer, "%i *", *standard_sizes);
1372           standard_sizes++;
1373           nstandard_sizes--;
1374           bitmapped_sizes++;
1375           nbitmapped_sizes--;
1376         }
1377       else if (nstandard_sizes
1378                && (!nbitmapped_sizes
1379                    || (gfloat)*standard_sizes < bitmap_size_float))
1380         {
1381           sprintf(buffer, "%i", *standard_sizes);
1382           standard_sizes++;
1383           nstandard_sizes--;
1384         }
1385       else
1386         {
1387           if (fontsel->metric == POINTS_METRIC)
1388             {
1389               if (*bitmapped_sizes % 10 == 0)
1390                 sprintf(buffer, "%i *", *bitmapped_sizes / 10);
1391               else
1392                 sprintf(buffer, "%i.%i *", *bitmapped_sizes / 10,
1393                         *bitmapped_sizes % 10);
1394             }
1395           else
1396             {
1397               sprintf(buffer, "%i *", *bitmapped_sizes);
1398             }
1399           bitmapped_sizes++;
1400           nbitmapped_sizes--;
1401         }
1402       size = buffer;
1403       gtk_clist_append(GTK_CLIST(fontsel->size_clist), &size);
1404     }
1405   gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
1406 }
1407
1408
1409 /* If the user hits return in the font size entry, we change to the new font
1410    size. */
1411 static gint
1412 gtk_font_selection_size_key_press (GtkWidget   *w,
1413                                    GdkEventKey *event,
1414                                    gpointer     data)
1415 {
1416   GtkFontSelection *fontsel;
1417   gint new_size;
1418   gfloat new_size_float;
1419   gchar *text;
1420
1421 #ifdef FONTSEL_DEBUG
1422   g_print("In size_key_press\n");
1423 #endif
1424   fontsel = GTK_FONT_SELECTION(data);
1425
1426   if (event->keyval == GDK_Return)
1427     {
1428       text = gtk_entry_get_text (GTK_ENTRY (fontsel->size_entry));
1429       if (fontsel->metric == PIXELS_METRIC)
1430         {
1431           new_size = atoi (text);
1432           if (new_size < 2)
1433             new_size = 2;
1434         }
1435       else
1436         {
1437           new_size_float = atof (text) * 10;
1438           new_size = (gint) new_size_float;
1439           if (new_size < 20)
1440             new_size = 20;
1441         }
1442
1443       /* Remember that this size was set explicitly. */
1444       fontsel->selected_size = new_size;
1445
1446       /* Check if the font size has changed, and return if it hasn't. */
1447       if (fontsel->size == new_size)
1448         return TRUE;
1449
1450       fontsel->size = new_size;
1451       gtk_font_selection_select_best_size (fontsel);
1452       return TRUE;
1453     }
1454
1455   return FALSE;
1456 }
1457
1458
1459 /* This tries to select the closest size to the current size, though it
1460    may have to change the size if only unscaled bitmaps are available.
1461    Note: this will load a font. */
1462 static void
1463 gtk_font_selection_select_best_size(GtkFontSelection *fontsel)
1464 {
1465   FontInfo *font;
1466   FontStyle *styles, *style;
1467   gchar *text;
1468   gint row, best_row = 0, size, size_fraction, best_size = 0, nmatched;
1469   gboolean found = FALSE;
1470   gchar buffer[32];
1471   GList *selection;
1472
1473 #ifdef FONTSEL_DEBUG
1474   g_print("In select_best_size\n");
1475 #endif
1476   font = &fontsel_info->font_info[fontsel->font_index];
1477   styles = &fontsel_info->font_styles[font->style_index];
1478   style = &styles[fontsel->style];
1479
1480   /* Find the closest size available in the size clist. If the exact size is
1481      in the list set found to TRUE. */
1482   for (row = 0; row < GTK_CLIST(fontsel->size_clist)->rows; row++)
1483     {
1484       gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
1485       nmatched = sscanf(text, "%i.%i", &size, &size_fraction);
1486       if (fontsel->metric == POINTS_METRIC)
1487         {
1488           size *= 10;
1489           if (nmatched == 2)
1490             size += size_fraction;
1491         }
1492
1493       if (size == fontsel->selected_size)
1494         {
1495           found = TRUE;
1496           best_size = size;
1497           best_row = row;
1498           break;
1499         }
1500       else if (best_size == 0
1501                || abs(size - fontsel->selected_size)
1502                < (abs(best_size - fontsel->selected_size)))
1503         {
1504           best_size = size;
1505           best_row = row;
1506         }
1507     }
1508
1509   /* If we aren't scaling bitmapped fonts and this is a bitmapped font, we
1510      need to use the closest size found. */
1511   if (!fontsel->scale_bitmapped_fonts && !(style->flags & SCALABLE_FONT))
1512     found = TRUE;
1513
1514   if (found)
1515     {
1516       fontsel->size = best_size;
1517       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
1518       gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), best_row, 0);
1519     }
1520   else
1521     {
1522       fontsel->size = fontsel->selected_size;
1523       selection = GTK_CLIST(fontsel->size_clist)->selection;
1524       if (selection)
1525         gtk_clist_unselect_row(GTK_CLIST(fontsel->size_clist),
1526                                GPOINTER_TO_INT (selection->data), 0);
1527       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
1528
1529       /* Show the size in the size entry. */
1530       if (fontsel->metric == PIXELS_METRIC)
1531         sprintf(buffer, "%i", fontsel->size);
1532       else
1533         {
1534           if (fontsel->size % 10 == 0)
1535             sprintf(buffer, "%i", fontsel->size / 10);
1536           else
1537             sprintf(buffer, "%i.%i", fontsel->size / 10, fontsel->size % 10);
1538         }
1539       gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
1540     }
1541   gtk_font_selection_load_font (fontsel);
1542 }
1543
1544
1545 /* This is called when a size is selected in the list. */
1546 static void
1547 gtk_font_selection_select_size (GtkWidget      *w,
1548                                 gint            row,
1549                                 gint            column,
1550                                 GdkEventButton *bevent,
1551                                 gpointer        data)
1552 {
1553   GtkFontSelection *fontsel;
1554   gint new_size;
1555   gchar *text;
1556   gchar buffer[16];
1557   gint i;
1558
1559 #ifdef FONTSEL_DEBUG
1560   g_print("In select_size\n");
1561 #endif
1562   fontsel = GTK_FONT_SELECTION(data);
1563
1564   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
1565     gtk_widget_grab_focus (w);
1566
1567   /* Copy the size from the clist to the size entry, but without the bitmapped
1568      marker ('*'). */
1569   gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
1570   i = 0;
1571   while (i < 15 && (text[i] == '.' || (text[i] >= '0' && text[i] <= '9')))
1572     {
1573       buffer[i] = text[i];
1574       i++;
1575     }
1576   buffer[i] = '\0';
1577   gtk_entry_set_text(GTK_ENTRY(fontsel->size_entry), buffer);
1578
1579   /* Check if the font size has changed, and return if it hasn't. */
1580   new_size = atoi(text);
1581   if (fontsel->metric == POINTS_METRIC)
1582     new_size *= 10;
1583
1584   if (fontsel->size == new_size)
1585     return;
1586
1587   /* If the size was selected by the user we set the selected_size. */
1588   fontsel->selected_size = new_size;
1589
1590   fontsel->size = new_size;
1591   gtk_font_selection_load_font (fontsel);
1592 }
1593
1594
1595 /* This is called when the pixels or points radio buttons are pressed. */
1596 static void
1597 gtk_font_selection_metric_callback (GtkWidget *w,
1598                                     gpointer   data)
1599 {
1600   GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
1601
1602 #ifdef FONTSEL_DEBUG
1603   g_print("In metric_callback\n");
1604 #endif
1605   if (GTK_TOGGLE_BUTTON(fontsel->pixels_button)->active)
1606     {
1607       if (fontsel->metric == PIXELS_METRIC)
1608         return;
1609       fontsel->metric = PIXELS_METRIC;
1610       fontsel->size = (fontsel->size + 5) / 10;
1611       fontsel->selected_size = (fontsel->selected_size + 5) / 10;
1612     }
1613   else
1614     {
1615       if (fontsel->metric == POINTS_METRIC)
1616         return;
1617       fontsel->metric = POINTS_METRIC;
1618       fontsel->size *= 10;
1619       fontsel->selected_size *= 10;
1620     }
1621   if (fontsel->font_index != -1)
1622     {
1623       gtk_font_selection_show_available_sizes (fontsel);
1624       gtk_font_selection_select_best_size (fontsel);
1625     }
1626 }
1627
1628
1629 /* This searches the given property table and returns the index of the given
1630    string, or 0, which is the wildcard '*' index, if it's not found. */
1631 static guint16
1632 gtk_font_selection_field_to_index (gchar **table,
1633                                    gint    ntable,
1634                                    gchar  *field)
1635 {
1636   gint i;
1637
1638   for (i = 0; i < ntable; i++)
1639     if (strcmp (field, table[i]) == 0)
1640       return i;
1641
1642   return 0;
1643 }
1644
1645
1646
1647 /* This attempts to load the current font, and returns TRUE if it succeeds. */
1648 static gboolean
1649 gtk_font_selection_load_font (GtkFontSelection *fontsel)
1650 {
1651   GdkFont *font;
1652   gchar *fontname, *label_text;
1653
1654   if (fontsel->font)
1655     gdk_font_unref (fontsel->font);
1656   fontsel->font = NULL;
1657
1658   /* If no family has been selected yet, just return FALSE. */
1659   if (fontsel->font_index == -1)
1660     return FALSE;
1661
1662   fontname = gtk_font_selection_get_font_name (fontsel);
1663   if (fontname)
1664     {
1665 #ifdef FONTSEL_DEBUG
1666       g_print("Loading: %s\n", fontname);
1667 #endif
1668       font = gdk_font_load (fontname);
1669       g_free(fontname);
1670
1671       if (font)
1672         {
1673           fontsel->font = font;
1674           /* Make sure the message label is empty, but don't change it unless
1675              it's necessary as it results in a resize of the whole window! */
1676           gtk_label_get(GTK_LABEL(fontsel->message_label), &label_text);
1677           if (strcmp(label_text, ""))
1678             gtk_label_set(GTK_LABEL(fontsel->message_label), "");
1679           gtk_font_selection_update_preview (fontsel);
1680           return TRUE;
1681         }
1682       else 
1683         {
1684           gtk_label_set(GTK_LABEL(fontsel->message_label),
1685                         "The selected font is not available.");
1686         }
1687     }
1688   else
1689     {
1690       gtk_label_set(GTK_LABEL(fontsel->message_label),
1691                     "The selected font is not a valid font.");
1692     }
1693
1694   return FALSE;
1695 }
1696
1697
1698 /* This sets the font in the preview entry to the selected font, and tries to
1699    make sure that the preview entry is a reasonable size, i.e. so that the
1700    text can be seen with a bit of space to spare. But it tries to avoid
1701    resizing the entry every time the font changes.
1702    This also used to shrink the preview if the font size was decreased, but
1703    that made it awkward if the user wanted to resize the window themself. */
1704 static void
1705 gtk_font_selection_update_preview (GtkFontSelection *fontsel)
1706 {
1707   GtkWidget *preview_entry;
1708   GtkStyle *style;
1709   gint text_height, new_height;
1710   gchar *text;
1711   XFontStruct *xfs;
1712
1713 #ifdef FONTSEL_DEBUG
1714   g_print("In update_preview\n");
1715 #endif
1716   style = gtk_style_new ();
1717   gdk_font_unref (style->font);
1718   style->font = fontsel->font;
1719   gdk_font_ref (style->font);
1720
1721   preview_entry = fontsel->preview_entry;
1722   gtk_widget_set_style (preview_entry, style);
1723   gtk_style_unref(style);
1724
1725   text_height = preview_entry->style->font->ascent
1726     + preview_entry->style->font->descent;
1727   /* We don't ever want to be over MAX_PREVIEW_HEIGHT pixels high. */
1728   new_height = text_height + 20;
1729   if (new_height < INITIAL_PREVIEW_HEIGHT)
1730     new_height = INITIAL_PREVIEW_HEIGHT;
1731   if (new_height > MAX_PREVIEW_HEIGHT)
1732     new_height = MAX_PREVIEW_HEIGHT;
1733
1734   if ((preview_entry->requisition.height < text_height + 10)
1735       || (preview_entry->requisition.height > text_height + 40))
1736     gtk_widget_set_usize(preview_entry, -1, new_height);
1737
1738   /* This sets the preview text, if it hasn't been set already. */
1739   text = gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
1740   if (strlen(text) == 0)
1741     gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), PREVIEW_TEXT);
1742   gtk_entry_set_position(GTK_ENTRY(fontsel->preview_entry), 0);
1743
1744   /* If this is a 2-byte font display a message to say it may not be
1745      displayed properly. */
1746   xfs = GDK_FONT_XFONT(fontsel->font);
1747   if (xfs->min_byte1 != 0 || xfs->max_byte1 != 0)
1748     gtk_label_set(GTK_LABEL(fontsel->message_label),
1749                   "This is a 2-byte font and may not be displayed correctly.");
1750 }
1751
1752
1753 static void
1754 gtk_font_selection_switch_page (GtkWidget       *w,
1755                                 GtkNotebookPage *page,
1756                                 gint             page_num,
1757                                 gpointer         data)
1758 {
1759   GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
1760
1761   /* This function strangely gets called when the window is destroyed,
1762      so we check here to see if the notebook is visible. */
1763   if (!GTK_WIDGET_VISIBLE(w))
1764     return;
1765
1766   if (page_num == 0)
1767     gtk_font_selection_filter_fonts(fontsel);
1768   else if (page_num == 1)
1769     gtk_font_selection_show_font_info(fontsel);
1770 }
1771       
1772
1773 static void
1774 gtk_font_selection_show_font_info (GtkFontSelection *fontsel)
1775 {
1776   Atom font_atom, atom;
1777   Bool status;
1778   char *name;
1779   gchar *fontname;
1780   gchar field_buffer[XLFD_MAX_FIELD_LEN];
1781   gchar *field;
1782   gint i;
1783   gboolean shown_actual_fields = FALSE;
1784
1785   fontname = gtk_font_selection_get_font_name(fontsel);
1786   gtk_entry_set_text(GTK_ENTRY(fontsel->requested_font_name),
1787                      fontname ? fontname : "");
1788
1789   gtk_clist_freeze (GTK_CLIST(fontsel->info_clist));
1790   for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1791     {
1792       if (fontname)
1793         field = gtk_font_selection_get_xlfd_field (fontname, i, field_buffer);
1794       else
1795         field = NULL;
1796       if (field)
1797         {
1798           if (i == XLFD_SLANT)
1799             field = gtk_font_selection_expand_slant_code(field);
1800           else if (i == XLFD_SPACING)
1801             field = gtk_font_selection_expand_spacing_code(field);
1802         }
1803       gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 1,
1804                          field ? field : "");
1805     }
1806
1807   if (fontsel->font)
1808     {
1809       font_atom = XInternAtom(GDK_DISPLAY(), "FONT", True);
1810       if (font_atom != None)
1811         {
1812           status = XGetFontProperty(GDK_FONT_XFONT(fontsel->font), font_atom,
1813                                     &atom);
1814           if (status == True)
1815             {
1816               name = XGetAtomName(GDK_DISPLAY(), atom);
1817               gtk_entry_set_text(GTK_ENTRY(fontsel->actual_font_name), name);
1818         
1819               for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1820                 {
1821                   field = gtk_font_selection_get_xlfd_field (name, i,
1822                                                              field_buffer);
1823                   if (i == XLFD_SLANT)
1824                     field = gtk_font_selection_expand_slant_code(field);
1825                   else if (i == XLFD_SPACING)
1826                     field = gtk_font_selection_expand_spacing_code(field);
1827                   gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1828                                      field ? field : "");
1829                 }
1830               shown_actual_fields = TRUE;
1831               XFree(name);
1832             }
1833         }
1834     }
1835   if (!shown_actual_fields)
1836     {
1837       gtk_entry_set_text(GTK_ENTRY(fontsel->actual_font_name), "");
1838       for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1839         {
1840           gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1841                              fontname ? "(unknown)" : "");
1842         }
1843     }
1844   gtk_clist_thaw (GTK_CLIST(fontsel->info_clist));
1845   g_free(fontname);
1846 }
1847
1848
1849 static gchar*
1850 gtk_font_selection_expand_slant_code(gchar *slant)
1851 {
1852   if      (!g_strcasecmp(slant, "r"))   return("roman");
1853   else if (!g_strcasecmp(slant, "i"))   return("italic");
1854   else if (!g_strcasecmp(slant, "o"))   return("oblique");
1855   else if (!g_strcasecmp(slant, "ri"))  return("reverse italic");
1856   else if (!g_strcasecmp(slant, "ro"))  return("reverse oblique");
1857   else if (!g_strcasecmp(slant, "ot"))  return("other");
1858   return slant;
1859 }
1860
1861 static gchar*
1862 gtk_font_selection_expand_spacing_code(gchar *spacing)
1863 {
1864   if      (!g_strcasecmp(spacing, "p")) return("proportional");
1865   else if (!g_strcasecmp(spacing, "m")) return("monospaced");
1866   else if (!g_strcasecmp(spacing, "c")) return("char cell");
1867   return spacing;
1868 }
1869
1870
1871 /*****************************************************************************
1872  * These functions all deal with the Filter page.
1873  *****************************************************************************/
1874
1875 /* This is called when an item is selected in one of the filter clists.
1876    We make sure that the first row of the clist, i.e. the wildcard '*', is
1877    selected if and only if none of the other items are selected.
1878    We may need to be careful about triggering other signals. */
1879 static void
1880 gtk_font_selection_select_filter             (GtkWidget      *w,
1881                                               gint            row,
1882                                               gint            column,
1883                                               GdkEventButton *bevent,
1884                                               gpointer        data)
1885 {
1886   gint i;
1887   
1888   if (row == 0)
1889     for (i = 1; i < GTK_CLIST(w)->rows; i++)
1890       gtk_clist_unselect_row(GTK_CLIST(w), i, 0);
1891   else
1892     gtk_clist_unselect_row(GTK_CLIST(w), 0, 0);
1893 }
1894
1895
1896 /* Here a filter item is being deselected. If there are now no items selected
1897    we select the first '*' item, unless that it is the item being deselected,
1898    in which case we select all of the other items. This makes it easy to
1899    select all items in the list except one or two. */
1900 static void
1901 gtk_font_selection_unselect_filter           (GtkWidget      *w,
1902                                               gint            row,
1903                                               gint            column,
1904                                               GdkEventButton *bevent,
1905                                               gpointer        data)
1906 {
1907   gint i;
1908   if (!GTK_CLIST(w)->selection)
1909     {
1910       if (row == 0)
1911         for (i = 1; i < GTK_CLIST(w)->rows; i++)
1912           gtk_clist_select_row(GTK_CLIST(w), i, 0);
1913       else
1914         gtk_clist_select_row(GTK_CLIST(w), 0, 0);
1915     }
1916 }
1917
1918
1919 /* This is called when the main notebook page is selected. It checks if the
1920    filter has changed, an if so it creates the filter settings, and filters the
1921    fonts shown. If an empty filter (all '*'s) is applied, then filtering is
1922    turned off. */
1923 static void
1924 gtk_font_selection_filter_fonts      (GtkFontSelection *fontsel)
1925 {
1926   GtkWidget *clist;
1927   GList *selection;
1928   gboolean empty_filter = TRUE, filter_changed = FALSE;
1929   gint prop, nselected, i, row, index;
1930
1931 #ifdef FONTSEL_DEBUG
1932   g_print("In filter_fonts\n");
1933 #endif
1934
1935   /* Check if the filter has changed, and also if it is an empty filter,
1936      i.e. all '*'s are selected. */
1937   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
1938     {
1939       clist = fontsel->filter_clists[prop];
1940       selection = GTK_CLIST(clist)->selection;
1941       nselected = g_list_length(selection);
1942       if (nselected != 1 || GPOINTER_TO_INT (selection->data) != 0)
1943         {
1944           empty_filter = FALSE;
1945
1946           if (fontsel->property_nfilters[prop] != nselected)
1947             filter_changed = TRUE;
1948           else
1949             {
1950               for (i = 0; i < nselected; i++)
1951                 {
1952                   row = GPOINTER_TO_INT (selection->data);
1953                   index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
1954                   if (fontsel->property_filters[prop][i] != index)
1955                     filter_changed = TRUE;
1956                   selection = selection->next;
1957                 }
1958             }
1959         }
1960       else
1961         {
1962           if (fontsel->property_nfilters[prop] != 0)
1963             filter_changed = TRUE;
1964         }
1965     }
1966
1967   /* If the filter hasn't changed we just return. */
1968   if (!filter_changed)
1969     return;
1970
1971 #ifdef FONTSEL_DEBUG
1972   g_print("   filter_fonts: filter has changed\n");
1973 #endif
1974
1975   /* Setting the toggle buttons state will trigger the callbacks to clear
1976      or apply the filter, unless the font list is already filtered, in
1977      which case we have to call apply_filter here. */
1978   if (empty_filter)
1979     {
1980       gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
1981       gtk_label_set(GTK_LABEL(fontsel->font_label), "Font:");
1982       gtk_font_selection_apply_filter(fontsel);
1983     }
1984   else
1985     {
1986       gtk_widget_set_sensitive(fontsel->filter_button, TRUE);
1987       gtk_label_set(GTK_LABEL(fontsel->font_label), "Font: [Filtered]");
1988       gtk_font_selection_apply_filter(fontsel);
1989     }
1990 }  
1991
1992
1993 /* This clears the filter, showing all fonts and styles again. */
1994 static void
1995 gtk_font_selection_apply_filter     (GtkFontSelection *fontsel)
1996 {
1997   FontInfo *font_info, *font;
1998   GtkWidget *clist;
1999   GList *selection;
2000   gint nfonts, prop, nselected, i, j, row, style, index;
2001   gchar font_buffer[XLFD_MAX_FIELD_LEN * 2 + 4];
2002   gchar *font_item;
2003   gboolean matched, matched_style;
2004
2005 #ifdef FONTSEL_DEBUG
2006       g_print("In apply_filter\n");
2007 #endif
2008   font_info = fontsel_info->font_info;
2009   nfonts = fontsel_info->nfonts;
2010
2011   /* Free the old filter data and create the new arrays. */
2012   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2013     {
2014       g_free(fontsel->property_filters[prop]);
2015
2016       clist = fontsel->filter_clists[prop];
2017       selection = GTK_CLIST(clist)->selection;
2018       nselected = g_list_length(selection);
2019       if (nselected == 1 && GPOINTER_TO_INT (selection->data) == 0)
2020         {
2021           fontsel->property_filters[prop] = NULL;
2022           fontsel->property_nfilters[prop] = 0;
2023         }
2024       else
2025         {
2026           fontsel->property_filters[prop] = g_new(guint16, nselected);
2027           fontsel->property_nfilters[prop] = nselected;
2028           for (i = 0; i < nselected; i++)
2029             {
2030               row = GPOINTER_TO_INT (selection->data);
2031               index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
2032               fontsel->property_filters[prop][i] = index;
2033               selection = selection->next;
2034             }
2035         }
2036     }
2037
2038   /* Filter the list of fonts. */
2039   gtk_clist_freeze (GTK_CLIST(fontsel->font_clist));
2040   gtk_clist_clear (GTK_CLIST(fontsel->font_clist));
2041   for (i = 0; i < nfonts; i++)
2042     {
2043       font = &font_info[i];
2044
2045       /* Check if the foundry matches. */
2046       if (fontsel->property_nfilters[FOUNDRY] != 0)
2047         {
2048           matched = FALSE;
2049           for (j = 0; j < fontsel->property_nfilters[FOUNDRY]; j++)
2050             {
2051               if (font->foundry == fontsel->property_filters[FOUNDRY][j])
2052                 {
2053                   matched = TRUE;
2054                   break;
2055                 }
2056             }
2057           if (!matched)
2058             continue;
2059         }
2060
2061       /* Now check if the other properties are matched in at least one style.*/
2062       matched_style = FALSE;
2063       for (style = 0; style < font->nstyles; style++)
2064         {
2065           if (gtk_font_selection_style_visible(fontsel, font, style))
2066             {
2067               matched_style = TRUE;
2068               break;
2069             }
2070         }
2071       if (!matched_style)
2072         continue;
2073
2074       /* Insert the font in the clist. */
2075       if ((i > 0 && font->family == font_info[i-1].family)
2076           || (i < nfonts - 1 && font->family == font_info[i+1].family))
2077         {
2078           sprintf(font_buffer, "%s (%s)", font->family,
2079                   fontsel_info->properties[FOUNDRY][font->foundry]);
2080           font_item = font_buffer;
2081           row = gtk_clist_append(GTK_CLIST(fontsel->font_clist), &font_item);
2082         }
2083       else
2084         {
2085           row = gtk_clist_append(GTK_CLIST(fontsel->font_clist),
2086                                  &font->family);
2087         }
2088       gtk_clist_set_row_data(GTK_CLIST(fontsel->font_clist), row,
2089                              GINT_TO_POINTER (i));
2090     }
2091   gtk_clist_thaw (GTK_CLIST(fontsel->font_clist));
2092
2093   /* Clear the current font and the style clist. */
2094   fontsel->font_index = -1;
2095   if (fontsel->font)
2096     gdk_font_unref(fontsel->font);
2097   fontsel->font = NULL;
2098   gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), "");
2099   gtk_clist_clear (GTK_CLIST(fontsel->font_style_clist));
2100   gtk_entry_set_text(GTK_ENTRY(fontsel->font_style_entry), "");
2101 }
2102
2103
2104 /* Returns TRUE if the style is not currently filtered out. */
2105 static gboolean
2106 gtk_font_selection_style_visible(GtkFontSelection *fontsel,
2107                                  FontInfo         *font,
2108                                  gint              style)
2109 {
2110   FontStyle *styles;
2111   guint16 value;
2112   gint prop, j;
2113   gboolean matched;
2114
2115   styles = &fontsel_info->font_styles[font->style_index];
2116
2117   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2118     {
2119       value = styles[style].properties[prop];
2120
2121       if (fontsel->property_nfilters[prop] != 0)
2122         {
2123           matched = FALSE;
2124           for (j = 0; j < fontsel->property_nfilters[prop]; j++)
2125             {
2126               if (value == fontsel->property_filters[prop][j])
2127                 {
2128                   matched = TRUE;
2129                   break;
2130                 }
2131             }
2132           if (!matched)
2133             return FALSE;
2134         }
2135     }
2136   return TRUE;
2137 }
2138
2139
2140 /* This resets all the filter clists to the wildcard '*' options. */
2141 static void
2142 gtk_font_selection_reset_filter      (GtkWidget      *w,
2143                                       gpointer        data)
2144 {
2145   GtkFontSelection *fontsel;
2146   gint prop;
2147
2148   fontsel = GTK_FONT_SELECTION(data);
2149
2150   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2151     gtk_clist_select_row(GTK_CLIST(fontsel->filter_clists[prop]), 0, 0);
2152 }
2153
2154
2155 /* This clears the filter, showing all fonts and styles again. */
2156 static void
2157 gtk_font_selection_on_clear_filter     (GtkWidget      *w,
2158                                         gpointer        data)
2159 {
2160   GtkFontSelection *fontsel;
2161
2162   fontsel = GTK_FONT_SELECTION(data);
2163   gtk_font_selection_clear_filter(fontsel);
2164 }
2165
2166
2167 /* This clears the filter, showing all fonts and styles again. */
2168 static void
2169 gtk_font_selection_clear_filter     (GtkFontSelection *fontsel)
2170 {
2171   gboolean filtered = FALSE, found_style = FALSE;
2172   gint prop, row, style;
2173
2174 #ifdef FONTSEL_DEBUG
2175       g_print("In clear_filter\n");
2176 #endif
2177   /* Clear the filter data. */
2178   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2179     {
2180       if (fontsel->property_filters[prop])
2181         filtered = TRUE;
2182       g_free(fontsel->property_filters[prop]);
2183       fontsel->property_filters[prop] = NULL;
2184       fontsel->property_nfilters[prop] = 0;
2185     }
2186
2187   /* Select all the '*'s on the filter page. */
2188   gtk_font_selection_reset_filter(NULL, fontsel);
2189
2190   /* Update the main notebook page. */
2191   gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
2192   gtk_label_set(GTK_LABEL(fontsel->font_label), "Font:");
2193
2194   /* If there is no filter at present just return. */
2195   if (!filtered)
2196     return;
2197
2198   gtk_font_selection_insert_fonts(fontsel);
2199
2200   /* Now find the current font & style and select them again. */
2201   if (fontsel->font_index != -1)
2202     {
2203       gtk_clist_select_row(GTK_CLIST(fontsel->font_clist),
2204                            fontsel->font_index, 0);
2205       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_clist),
2206                                    fontsel->font_index) != GTK_VISIBILITY_FULL)
2207         gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), fontsel->font_index,
2208                          -1, 0.5, 0);
2209       gtk_font_selection_show_available_styles (fontsel);
2210
2211       for (row = 0; row < GTK_CLIST(fontsel->font_style_clist)->rows; row++)
2212         {
2213           style = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(fontsel->font_style_clist), row));
2214           if (style == fontsel->style)
2215             {
2216               found_style = TRUE;
2217               break;
2218             }
2219         }
2220
2221       if (found_style)
2222         {
2223           gtk_clist_select_row(GTK_CLIST(fontsel->font_style_clist),
2224                                row, 0);
2225           if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist),
2226                                        row) != GTK_VISIBILITY_FULL)
2227             gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), row,
2228                              -1, 0.5, 0);
2229         }
2230     }
2231 }
2232
2233
2234 /*****************************************************************************
2235  * These functions all deal with creating the main class arrays containing
2236  * the data about all available fonts.
2237  *****************************************************************************/
2238 static void
2239 gtk_font_selection_get_fonts (void)
2240 {
2241   gchar **xfontnames;
2242   GSList **fontnames;
2243   gchar *fontname;
2244   GSList * temp_list;
2245   gint num_fonts;
2246   gint i, prop, style, size;
2247   gint npixel_sizes = 0, npoint_sizes = 0;
2248   FontInfo *font;
2249   FontStyle *current_style, *prev_style, *tmp_style;
2250   gboolean matched_style, found_size;
2251   gint pixels, points, res_x, res_y;
2252   gchar field_buffer[XLFD_MAX_FIELD_LEN];
2253   gchar *field;
2254   guint8 flags;
2255   guint16 *pixel_sizes, *point_sizes, *tmp_sizes;
2256
2257   fontsel_info = g_new (GtkFontSelInfo, 1);
2258
2259   /* Get a maximum of MAX_FONTS fontnames from the X server.
2260      Use "-*" as the pattern rather than "-*-*-*-*-*-*-*-*-*-*-*-*-*-*" since
2261      the latter may result in fonts being returned which don't actually exist.
2262      xlsfonts also uses "*" so I think it's OK. "-*" gets rid of aliases. */
2263   xfontnames = XListFonts (GDK_DISPLAY(), "-*", MAX_FONTS, &num_fonts);
2264   /* Output a warning if we actually get MAX_FONTS fonts. */
2265   if (num_fonts == MAX_FONTS)
2266     g_warning("MAX_FONTS exceeded. Some fonts may be missing.");
2267
2268   /* The maximum size of all these tables is the number of font names
2269      returned. We realloc them later when we know exactly how many
2270      unique entries there are. */
2271   fontsel_info->font_info = g_new (FontInfo, num_fonts);
2272   fontsel_info->font_styles = g_new (FontStyle, num_fonts);
2273   fontsel_info->pixel_sizes = g_new (guint16, num_fonts);
2274   fontsel_info->point_sizes = g_new (guint16, num_fonts);
2275
2276   fontnames = g_new (GSList*, num_fonts);
2277
2278   /* Create the initial arrays for the property value strings, though they
2279      may be realloc'ed later. Put the wildcard '*' in the first elements. */
2280   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2281     {
2282       fontsel_info->properties[prop] = g_new(gchar*, PROPERTY_ARRAY_INCREMENT);
2283       fontsel_info->space_allocated[prop] = PROPERTY_ARRAY_INCREMENT;
2284       fontsel_info->nproperties[prop] = 1;
2285       fontsel_info->properties[prop][0] = "*";
2286     }
2287
2288
2289   /* Insert the font families into the main table, sorted by family and
2290      foundry (fonts with different foundries are placed in seaparate FontInfos.
2291      All fontnames in each family + foundry are placed into the fontnames
2292      array of lists. */
2293   fontsel_info->nfonts = 0;
2294   for (i = 0; i < num_fonts; i++)
2295     {
2296 #ifdef FONTSEL_DEBUG
2297       g_print("%s\n", xfontnames[i]);
2298 #endif
2299       if (gtk_font_selection_is_xlfd_font_name (xfontnames[i]))
2300         gtk_font_selection_insert_font (fontnames, &fontsel_info->nfonts, xfontnames[i]);
2301       else
2302         {
2303 #ifdef FONTSEL_DEBUG
2304           g_warning("Skipping invalid font: %s", xfontnames[i]);
2305 #endif
2306         }
2307     }
2308
2309
2310   /* Since many font names will be in the same FontInfo not all of the
2311      allocated FontInfo table will be used, so we will now reallocate it
2312      with the real size. */
2313   fontsel_info->font_info = g_realloc(fontsel_info->font_info,
2314                                sizeof(FontInfo) * fontsel_info->nfonts);
2315
2316
2317   /* Now we work out which choices of weight/slant etc. are valid for each
2318      font. */
2319   fontsel_info->nstyles = 0;
2320   current_style = fontsel_info->font_styles;
2321   for (i = 0; i < fontsel_info->nfonts; i++)
2322     {
2323       font = &fontsel_info->font_info[i];
2324
2325       /* Use the next free position in the styles array. */
2326       font->style_index = fontsel_info->nstyles;
2327
2328       /* Now step through each of the fontnames with this family, and create
2329          a style for each fontname. Each style contains the index into the
2330          weights/slants etc. arrays, and a number of pixel/point sizes. */
2331       style = 0;
2332       temp_list = fontnames[i];
2333       while (temp_list)
2334         {
2335           fontname = temp_list->data;
2336           temp_list = temp_list->next;
2337
2338           for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2339             {
2340               current_style->properties[prop]
2341                 = gtk_font_selection_insert_field (fontname, prop);
2342             }
2343           current_style->pixel_sizes_index = npixel_sizes;
2344           current_style->point_sizes_index = npoint_sizes;
2345           current_style->flags = 0;
2346
2347
2348           field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
2349                                                      field_buffer);
2350           pixels = atoi(field);
2351
2352           field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
2353                                                      field_buffer);
2354           points = atoi(field);
2355
2356           field = gtk_font_selection_get_xlfd_field (fontname,
2357                                                      XLFD_RESOLUTION_X,
2358                                                      field_buffer);
2359           res_x = atoi(field);
2360
2361           field = gtk_font_selection_get_xlfd_field (fontname,
2362                                                      XLFD_RESOLUTION_Y,
2363                                                      field_buffer);
2364           res_y = atoi(field);
2365
2366           if (pixels == 0 && points == 0)
2367             {
2368               if (res_x == 0 && res_y == 0)
2369                 flags = SCALABLE_FONT;
2370               else
2371                 {
2372                   flags = SCALABLE_BITMAP_FONT;
2373                   fontsel_info->scaled_bitmaps_available = TRUE;
2374                 }
2375             }
2376           else
2377             flags = BITMAP_FONT;
2378
2379           /* Now we check to make sure that the style is unique. If it isn't
2380              we forget it. */
2381           prev_style = fontsel_info->font_styles + font->style_index;
2382           matched_style = FALSE;
2383           while (prev_style < current_style)
2384             {
2385               matched_style = TRUE;
2386               for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2387                 {
2388                   if (prev_style->properties[prop]
2389                       != current_style->properties[prop])
2390                     {
2391                       matched_style = FALSE;
2392                       break;
2393                     }
2394                 }
2395               if (matched_style)
2396                 break;
2397               prev_style++;
2398             }
2399
2400           /* If we matched an existing style, we need to add the pixels &
2401              point sizes to the style. If not, we insert the pixel & point
2402              sizes into our new style. Note that we don't add sizes for
2403              scalable fonts. */
2404           if (matched_style)
2405             {
2406               prev_style->flags |= flags;
2407               if (flags == BITMAP_FONT)
2408                 {
2409                   pixel_sizes = fontsel_info->pixel_sizes
2410                     + prev_style->pixel_sizes_index;
2411                   found_size = FALSE;
2412                   for (size = 0; size < prev_style->npixel_sizes; size++)
2413                     {
2414                       if (pixels == *pixel_sizes)
2415                         {
2416                           found_size = TRUE;
2417                           break;
2418                         }
2419                       else if (pixels < *pixel_sizes)
2420                         break;
2421                       pixel_sizes++;
2422                     }
2423                   /* We need to move all the following pixel sizes up, and also
2424                      update the indexes of any following styles. */
2425                   if (!found_size)
2426                     {
2427                       for (tmp_sizes = fontsel_info->pixel_sizes + npixel_sizes;
2428                            tmp_sizes > pixel_sizes; tmp_sizes--)
2429                         *tmp_sizes = *(tmp_sizes - 1);
2430
2431                       *pixel_sizes = pixels;
2432                       npixel_sizes++;
2433                       prev_style->npixel_sizes++;
2434
2435                       tmp_style = prev_style + 1;
2436                       while (tmp_style < current_style)
2437                         {
2438                           tmp_style->pixel_sizes_index++;
2439                           tmp_style++;
2440                         }
2441                     }
2442
2443                   point_sizes = fontsel_info->point_sizes
2444                     + prev_style->point_sizes_index;
2445                   found_size = FALSE;
2446                   for (size = 0; size < prev_style->npoint_sizes; size++)
2447                     {
2448                       if (points == *point_sizes)
2449                         {
2450                           found_size = TRUE;
2451                           break;
2452                         }
2453                       else if (points < *point_sizes)
2454                         break;
2455                       point_sizes++;
2456                     }
2457                   /* We need to move all the following point sizes up, and also
2458                      update the indexes of any following styles. */
2459                   if (!found_size)
2460                     {
2461                       for (tmp_sizes = fontsel_info->point_sizes + npoint_sizes;
2462                            tmp_sizes > point_sizes; tmp_sizes--)
2463                         *tmp_sizes = *(tmp_sizes - 1);
2464
2465                       *point_sizes = points;
2466                       npoint_sizes++;
2467                       prev_style->npoint_sizes++;
2468
2469                       tmp_style = prev_style + 1;
2470                       while (tmp_style < current_style)
2471                         {
2472                           tmp_style->point_sizes_index++;
2473                           tmp_style++;
2474                         }
2475                     }
2476                 }
2477             }
2478           else
2479             {
2480               current_style->flags = flags;
2481               if (flags == BITMAP_FONT)
2482                 {
2483                   fontsel_info->pixel_sizes[npixel_sizes++] = pixels;
2484                   current_style->npixel_sizes = 1;
2485                   fontsel_info->point_sizes[npoint_sizes++] = points;
2486                   current_style->npoint_sizes = 1;
2487                 }
2488               style++;
2489               fontsel_info->nstyles++;
2490               current_style++;
2491             }
2492         }
2493       g_slist_free(fontnames[i]);
2494
2495       /* Set nstyles to the real value, minus duplicated fontnames.
2496          Note that we aren't using all the allocated memory if fontnames are
2497          duplicated. */
2498       font->nstyles = style;
2499     }
2500
2501   /* Since some repeated styles may be skipped we won't have used all the
2502      allocated space, so we will now reallocate it with the real size. */
2503   fontsel_info->font_styles = g_realloc(fontsel_info->font_styles,
2504                                  sizeof(FontStyle) * fontsel_info->nstyles);
2505   fontsel_info->pixel_sizes = g_realloc(fontsel_info->pixel_sizes,
2506                                  sizeof(guint16) * npixel_sizes);
2507   fontsel_info->point_sizes = g_realloc(fontsel_info->point_sizes,
2508                                  sizeof(guint16) * npoint_sizes);
2509   g_free(fontnames);
2510   XFreeFontNames (xfontnames);
2511
2512
2513   /* Debugging Output */
2514   /* This outputs all FontInfos. */
2515 #ifdef FONTSEL_DEBUG
2516   g_print("\n\n Font Family           Weight    Slant     Set Width Spacing   Charset\n\n");
2517   for (i = 0; i < fontsel_info->nfonts; i++)
2518     {
2519       FontInfo *font = &fontsel_info->font_info[i];
2520       FontStyle *styles = fontsel_info->font_styles + font->style_index;
2521       for (style = 0; style < font->nstyles; style++)
2522         {
2523           g_print("%5i %-16.16s ", i, font->family);
2524           for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2525             g_print("%-9.9s ",
2526                     fontsel_info->properties[prop][styles->properties[prop]]);
2527           g_print("\n      ");
2528
2529           if (styles->flags & BITMAP_FONT)
2530             g_print("Bitmapped font  ");
2531           if (styles->flags & SCALABLE_FONT)
2532             g_print("Scalable font  ");
2533           if (styles->flags & SCALABLE_BITMAP_FONT)
2534             g_print("Scalable-Bitmapped font  ");
2535           g_print("\n");
2536
2537           if (styles->npixel_sizes)
2538             {
2539               g_print("      Pixel sizes: ");
2540               tmp_sizes = fontsel_info->pixel_sizes + styles->pixel_sizes_index;
2541               for (size = 0; size < styles->npixel_sizes; size++)
2542                 g_print("%i ", *tmp_sizes++);
2543               g_print("\n");
2544             }
2545
2546           if (styles->npoint_sizes)
2547             {
2548               g_print("      Point sizes: ");
2549               tmp_sizes = fontsel_info->point_sizes + styles->point_sizes_index;
2550               for (size = 0; size < styles->npoint_sizes; size++)
2551                 g_print("%i ", *tmp_sizes++);
2552               g_print("\n");
2553             }
2554
2555           g_print("\n");
2556           styles++;
2557         }
2558     }
2559   /* This outputs all available properties. */
2560   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2561     {
2562       g_print("Property: %s\n", xlfd_field_names[xlfd_index[prop]]);
2563       for (i = 0; i < fontsel_info->nproperties[prop]; i++)
2564         g_print("  %s\n", fontsel_info->properties[prop][i]);
2565     }
2566 #endif
2567 }
2568
2569 /* This inserts the given fontname into the FontInfo table.
2570    If a FontInfo already exists with the same family and foundry, then the
2571    fontname is added to the FontInfos list of fontnames, else a new FontInfo
2572    is created and inserted in alphabetical order in the table. */
2573 static void
2574 gtk_font_selection_insert_font (GSList                *fontnames[],
2575                                 gint                  *ntable,
2576                                 gchar                 *fontname)
2577 {
2578   FontInfo *table;
2579   FontInfo temp_info;
2580   GSList *temp_fontname;
2581   gchar *family;
2582   gboolean family_exists = FALSE;
2583   gint foundry;
2584   gint lower, upper;
2585   gint middle, cmp;
2586   gchar family_buffer[XLFD_MAX_FIELD_LEN];
2587
2588   table = fontsel_info->font_info;
2589
2590   /* insert a fontname into a table */
2591   family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
2592                                               family_buffer);
2593   if (!family)
2594     return;
2595
2596   foundry = gtk_font_selection_insert_field (fontname, FOUNDRY);
2597
2598   lower = 0;
2599   if (*ntable > 0)
2600     {
2601       /* Do a binary search to determine if we have already encountered
2602        *  a font with this family & foundry. */
2603       upper = *ntable;
2604       while (lower < upper)
2605         {
2606           middle = (lower + upper) >> 1;
2607
2608           cmp = strcmp (family, table[middle].family);
2609           /* If the family matches we sort by the foundry. */
2610           if (cmp == 0)
2611             {
2612               family_exists = TRUE;
2613               family = table[middle].family;
2614               cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
2615                            fontsel_info->properties[FOUNDRY][table[middle].foundry]);
2616             }
2617
2618           if (cmp == 0)
2619             {
2620               fontnames[middle] = g_slist_prepend (fontnames[middle],
2621                                                    fontname);
2622               return;
2623             }
2624           else if (cmp < 0)
2625             upper = middle;
2626           else
2627             lower = middle+1;
2628         }
2629     }
2630
2631   /* Add another entry to the table for this new font family */
2632   temp_info.family = family_exists ? family : g_strdup(family);
2633   temp_info.foundry = foundry;
2634   temp_fontname = g_slist_prepend (NULL, fontname);
2635
2636   (*ntable)++;
2637
2638   /* Quickly insert the entry into the table in sorted order
2639    *  using a modification of insertion sort and the knowledge
2640    *  that the entries proper position in the table was determined
2641    *  above in the binary search and is contained in the "lower"
2642    *  variable. */
2643   if (*ntable > 1)
2644     {
2645       upper = *ntable - 1;
2646       while (lower != upper)
2647         {
2648           table[upper] = table[upper-1];
2649           fontnames[upper] = fontnames[upper-1];
2650           upper--;
2651         }
2652     }
2653   table[lower] = temp_info;
2654   fontnames[lower] = temp_fontname;
2655 }
2656
2657
2658 /* This checks that the specified field of the given fontname is in the
2659    appropriate properties array. If not it is added. Thus eventually we get
2660    arrays of all possible weights/slants etc. It returns the array index. */
2661 static gint
2662 gtk_font_selection_insert_field (gchar                 *fontname,
2663                                  gint                   prop)
2664 {
2665   gchar field_buffer[XLFD_MAX_FIELD_LEN];
2666   gchar *field;
2667   guint16 index;
2668
2669   field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
2670                                              field_buffer);
2671   if (!field)
2672     return 0;
2673
2674   /* If the field is already in the array just return its index. */
2675   for (index = 0; index < fontsel_info->nproperties[prop]; index++)
2676     if (!strcmp(field, fontsel_info->properties[prop][index]))
2677       return index;
2678
2679   /* Make sure we have enough space to add the field. */
2680   if (fontsel_info->nproperties[prop] == fontsel_info->space_allocated[prop])
2681     {
2682       fontsel_info->space_allocated[prop] += PROPERTY_ARRAY_INCREMENT;
2683       fontsel_info->properties[prop] = g_realloc(fontsel_info->properties[prop],
2684                                           sizeof(gchar*)
2685                                           * fontsel_info->space_allocated[prop]);
2686     }
2687
2688   /* Add the new field. */
2689   index = fontsel_info->nproperties[prop];
2690   fontsel_info->properties[prop][index] = g_strdup(field);
2691   fontsel_info->nproperties[prop]++;
2692   return index;
2693 }
2694
2695
2696 /*****************************************************************************
2697  * These functions are the main public interface for getting/setting the font.
2698  *****************************************************************************/
2699
2700 GdkFont*
2701 gtk_font_selection_get_font (GtkFontSelection *fontsel)
2702 {
2703   g_return_val_if_fail (fontsel != NULL, NULL);
2704   return fontsel->font;
2705 }
2706
2707
2708 gchar *
2709 gtk_font_selection_get_font_name (GtkFontSelection *fontsel)
2710 {
2711   FontInfo *font;
2712   gchar *family_str, *foundry_str;
2713   gchar *property_str[GTK_NUM_STYLE_PROPERTIES];
2714   gint prop;
2715
2716   g_return_val_if_fail (fontsel != NULL, NULL);
2717   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), NULL);
2718
2719   /* If no family has been selected return NULL. */
2720   if (fontsel->font_index == -1)
2721     return NULL;
2722
2723   font = &fontsel_info->font_info[fontsel->font_index];
2724   family_str = font->family;
2725   foundry_str = fontsel_info->properties[FOUNDRY][font->foundry];
2726
2727   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2728     {
2729       property_str[prop]
2730         = fontsel_info->properties[prop][fontsel->property_values[prop]];
2731       if (strcmp (property_str[prop], "(nil)") == 0)
2732         property_str[prop] = "";
2733     }
2734
2735   return gtk_font_selection_create_xlfd (fontsel->size,
2736                                          fontsel->metric,
2737                                          foundry_str,
2738                                          family_str,
2739                                          property_str[WEIGHT],
2740                                          property_str[SLANT],
2741                                          property_str[SET_WIDTH],
2742                                          property_str[SPACING],
2743                                          property_str[CHARSET]);
2744 }
2745
2746
2747 /* This returns the style with the best match to the current fontsel setting */
2748 static gint
2749 gtk_font_selection_get_best_match(GtkFontSelection *fontsel)
2750 {
2751   FontInfo *font;
2752   FontStyle *styles;
2753   gint prop, style, best_style = 0, matched, best_matched = 0;
2754
2755   font = &fontsel_info->font_info[fontsel->font_index];
2756   styles = &fontsel_info->font_styles[font->style_index];
2757
2758   /* Find the style with the most matches. */
2759   for (style = 0; style < font->nstyles; style++)
2760     {
2761       matched = 0;
2762       for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2763         {
2764           if (fontsel->property_values[prop] == styles[style].properties[prop])
2765             matched++;
2766         }
2767       if (matched > best_matched)
2768         {
2769           best_matched = matched;
2770           best_style = style;
2771         }
2772     }
2773
2774   return best_style;
2775 }
2776
2777
2778 /* This sets the current font, selecting the appropriate clist rows.
2779    First we check the fontname is valid and try to find the font family
2780    - i.e. the name in the main list. If we can't find that, then just return.
2781    Next we try to set each of the properties according to the fontname.
2782    Finally we select the font family & style in the clists. */
2783 gboolean
2784 gtk_font_selection_set_font_name (GtkFontSelection *fontsel,
2785                                   const gchar      *fontname)
2786 {
2787   gchar *family, *field;
2788   gint index, prop, size;
2789   guint16 foundry, value;
2790   gchar family_buffer[XLFD_MAX_FIELD_LEN];
2791   gchar field_buffer[XLFD_MAX_FIELD_LEN];
2792   gchar buffer[16];
2793
2794   g_return_val_if_fail (fontsel != NULL, FALSE);
2795   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), FALSE);
2796   g_return_val_if_fail (fontname != NULL, FALSE);
2797
2798   /* Check it is a valid fontname. */
2799   if (!gtk_font_selection_is_xlfd_font_name(fontname))
2800     return FALSE;
2801
2802   family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
2803                                               family_buffer);
2804   if (!family)
2805     return FALSE;
2806
2807   field = gtk_font_selection_get_xlfd_field (fontname, XLFD_FOUNDRY,
2808                                                field_buffer);
2809   foundry = gtk_font_selection_field_to_index (fontsel_info->properties[FOUNDRY],
2810                                                fontsel_info->nproperties[FOUNDRY],
2811                                                field);
2812
2813   index = gtk_font_selection_find_font(fontsel, family, foundry);
2814   if (index == -1)
2815     return FALSE;
2816
2817   /* Convert the property fields into indices and set them. */
2818   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++) 
2819     {
2820       field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
2821                                                  field_buffer);
2822       value = gtk_font_selection_field_to_index (fontsel_info->properties[prop],
2823                                                  fontsel_info->nproperties[prop],
2824                                                  field);
2825       fontsel->property_values[prop] = value;
2826     }
2827
2828   field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
2829                                              field_buffer);
2830   size = atoi(field);
2831   if (size > 0)
2832     {
2833       if (size < 20)
2834         size = 20;
2835       fontsel->size = fontsel->selected_size = size;
2836       fontsel->metric = POINTS_METRIC;
2837       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(fontsel->points_button),
2838                                   TRUE);
2839       if (size % 10 == 0)
2840         sprintf (buffer, "%i", size / 10);
2841       else
2842         sprintf (buffer, "%i.%i", size / 10, size % 10);
2843     }
2844   else
2845     {
2846       field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
2847                                                  field_buffer);
2848       size = atoi(field);
2849       if (size < 2)
2850         size = 2;
2851       fontsel->size = fontsel->selected_size = size;
2852       fontsel->metric = PIXELS_METRIC;
2853       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(fontsel->pixels_button),
2854                                   TRUE);
2855       sprintf (buffer, "%i", size);
2856     }
2857   gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
2858
2859   /* Clear any current filter. */
2860   gtk_font_selection_clear_filter(fontsel);
2861
2862   /* Now find the best style match. */
2863   fontsel->font_index = index;
2864   gtk_clist_select_row(GTK_CLIST(fontsel->font_clist), index, 0);
2865   if (GTK_WIDGET_MAPPED (fontsel->font_clist))
2866     gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), index, -1, 0.5, 0);
2867   else
2868     fontsel->scroll_on_expose = TRUE;
2869
2870   gtk_font_selection_show_available_styles (fontsel);
2871   /* This will load the font. */
2872   gtk_font_selection_select_best_style (fontsel, FALSE);
2873
2874   return TRUE;
2875 }
2876
2877
2878 /* Returns the index of the given family, or -1 if not found */
2879 static gint
2880 gtk_font_selection_find_font (GtkFontSelection *fontsel,
2881                               gchar            *family,
2882                               guint16           foundry)
2883 {
2884   FontInfo *font_info;
2885   gint lower, upper, middle = -1, cmp, nfonts;
2886
2887   font_info = fontsel_info->font_info;
2888   nfonts = fontsel_info->nfonts;
2889   if (nfonts == 0)
2890     return -1;
2891
2892   /* Do a binary search to find the font family. */
2893   lower = 0;
2894   upper = nfonts;
2895   while (lower < upper)
2896     {
2897       middle = (lower + upper) >> 1;
2898       
2899       cmp = strcmp (family, font_info[middle].family);
2900       if (cmp == 0)
2901         cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
2902                      fontsel_info->properties[FOUNDRY][font_info[middle].foundry]);
2903
2904       if (cmp == 0)
2905         return middle;
2906       else if (cmp < 0)
2907         upper = middle;
2908       else if (cmp > 0)
2909         lower = middle+1;
2910     }
2911
2912   /* If we can't match family & foundry see if just the family matches */
2913   if (!strcmp (family, font_info[middle].family))
2914     return middle;
2915
2916   return -1;
2917 }
2918
2919
2920 /* This returns the text in the preview entry. You should copy the returned
2921    text if you need it. */
2922 gchar*
2923 gtk_font_selection_get_preview_text  (GtkFontSelection *fontsel)
2924 {
2925   return gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
2926 }
2927
2928
2929 /* This sets the text in the preview entry. */
2930 void
2931 gtk_font_selection_set_preview_text  (GtkFontSelection *fontsel,
2932                                       const gchar         *text)
2933 {
2934   gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), text);
2935 }
2936
2937
2938 /*****************************************************************************
2939  * These functions all deal with X Logical Font Description (XLFD) fontnames.
2940  * See the freely available documentation about this.
2941  *****************************************************************************/
2942
2943 /*
2944  * Returns TRUE if the fontname is a valid XLFD.
2945  * (It just checks if the number of dashes is 14, and that each
2946  * field < XLFD_MAX_FIELD_LEN  characters long - that's not in the XLFD but it
2947  * makes it easier for me).
2948  */
2949 static gboolean
2950 gtk_font_selection_is_xlfd_font_name (const gchar *fontname)
2951 {
2952   gint i = 0;
2953   gint field_len = 0;
2954
2955   while (*fontname)
2956     {
2957       if (*fontname++ == '-')
2958         {
2959           if (field_len > XLFD_MAX_FIELD_LEN) return FALSE;
2960           field_len = 0;
2961           i++;
2962         }
2963       else
2964         field_len++;
2965     }
2966
2967   return (i == 14) ? TRUE : FALSE;
2968 }
2969
2970 /*
2971  * This fills the buffer with the specified field from the X Logical Font
2972  * Description name, and returns it. If fontname is NULL or the field is
2973  * longer than XFLD_MAX_FIELD_LEN it returns NULL.
2974  * Note: For the charset field, we also return the encoding, e.g. 'iso8859-1'.
2975  */
2976 static gchar*
2977 gtk_font_selection_get_xlfd_field (const gchar *fontname,
2978                                    FontField    field_num,
2979                                    gchar       *buffer)
2980 {
2981   const gchar *t1, *t2;
2982   gint countdown, len, num_dashes;
2983
2984   if (!fontname)
2985     return NULL;
2986
2987   /* we assume this is a valid fontname...that is, it has 14 fields */
2988
2989   countdown = field_num;
2990   t1 = fontname;
2991   while (*t1 && (countdown >= 0))
2992     if (*t1++ == '-')
2993       countdown--;
2994
2995   num_dashes = (field_num == XLFD_CHARSET) ? 2 : 1;
2996   for (t2 = t1; *t2; t2++)
2997     { 
2998       if (*t2 == '-' && --num_dashes == 0)
2999         break;
3000     }
3001
3002   if (t1 != t2)
3003     {
3004       /* Check we don't overflow the buffer */
3005       len = (long) t2 - (long) t1;
3006       if (len > XLFD_MAX_FIELD_LEN - 1)
3007         return NULL;
3008       strncpy (buffer, t1, len);
3009       buffer[len] = 0;
3010     }
3011   else
3012     strcpy(buffer, "(nil)");
3013
3014   return buffer;
3015 }
3016
3017 /*
3018  * This returns a X Logical Font Description font name, given all the pieces.
3019  * Note: this retval must be freed by the caller.
3020  */
3021 static gchar *
3022 gtk_font_selection_create_xlfd (gint              size,
3023                                 GtkFontMetricType metric,
3024                                 gchar             *foundry,
3025                                 gchar             *family,
3026                                 gchar             *weight,
3027                                 gchar             *slant,
3028                                 gchar             *set_width,
3029                                 gchar             *spacing,
3030                                 gchar             *charset)
3031 {
3032   gchar buffer[16];
3033   gchar *pixel_size = "*", *point_size = "*", *fontname;
3034   gint length;
3035
3036   if (size <= 0)
3037     return NULL;
3038
3039   sprintf (buffer, "%d", (int) size);
3040   if (metric == PIXELS_METRIC)
3041     pixel_size = buffer;
3042   else
3043     point_size = buffer;
3044     
3045   /* Note: be careful here - don't overrun the allocated memory. */
3046   length = strlen(foundry) + strlen(family) + strlen(weight) + strlen(slant)
3047     + strlen(set_width) + strlen(pixel_size) + strlen(point_size)
3048     + strlen(spacing) + strlen(charset)
3049     + 1 + 1 + 1 + 1 + 1 + 3 + 1 + 5 + 3
3050     + 1 /* for the terminating '\0'. */;
3051
3052   fontname = g_new(gchar, length);
3053   /* **NOTE**: If you change this string please change length above! */
3054   sprintf(fontname, "-%s-%s-%s-%s-%s-*-%s-%s-*-*-%s-*-%s",
3055           foundry, family, weight, slant, set_width, pixel_size,
3056           point_size, spacing, charset);
3057   return fontname;
3058 }
3059
3060
3061
3062 /*****************************************************************************
3063  * GtkFontSelectionDialog
3064  *****************************************************************************/
3065
3066 guint
3067 gtk_font_selection_dialog_get_type      (void)
3068 {
3069   static guint font_selection_dialog_type = 0;
3070
3071   if (!font_selection_dialog_type)
3072     {
3073       GtkTypeInfo fontsel_diag_info =
3074       {
3075         "GtkFontSelectionDialog",
3076         sizeof (GtkFontSelectionDialog),
3077         sizeof (GtkFontSelectionDialogClass),
3078         (GtkClassInitFunc) gtk_font_selection_dialog_class_init,
3079         (GtkObjectInitFunc) gtk_font_selection_dialog_init,
3080         /* reserved_1 */ NULL,
3081         /* reserved_2 */ NULL,
3082         (GtkClassInitFunc) NULL,
3083       };
3084
3085       font_selection_dialog_type = gtk_type_unique (gtk_window_get_type (), &fontsel_diag_info);
3086     }
3087
3088   return font_selection_dialog_type;
3089 }
3090
3091 static void
3092 gtk_font_selection_dialog_class_init (GtkFontSelectionDialogClass *klass)
3093 {
3094   GtkObjectClass *object_class;
3095
3096   object_class = (GtkObjectClass*) klass;
3097
3098   font_selection_dialog_parent_class = gtk_type_class (gtk_window_get_type ());
3099 }
3100
3101 static void
3102 gtk_font_selection_dialog_init (GtkFontSelectionDialog *fontseldiag)
3103 {
3104   fontseldiag->dialog_width = -1;
3105   fontseldiag->auto_resize = TRUE;
3106
3107   gtk_widget_set_events(GTK_WIDGET(fontseldiag), GDK_STRUCTURE_MASK);
3108   gtk_signal_connect (GTK_OBJECT (fontseldiag), "configure_event",
3109                       (GtkSignalFunc) gtk_font_selection_dialog_on_configure,
3110                       fontseldiag);
3111
3112   gtk_container_border_width (GTK_CONTAINER (fontseldiag), 4);
3113   gtk_window_set_policy(GTK_WINDOW(fontseldiag), FALSE, TRUE, TRUE);
3114
3115   fontseldiag->main_vbox = gtk_vbox_new (FALSE, 4);
3116   gtk_widget_show (fontseldiag->main_vbox);
3117   gtk_container_add (GTK_CONTAINER (fontseldiag), fontseldiag->main_vbox);
3118
3119   fontseldiag->fontsel = gtk_font_selection_new();
3120   gtk_widget_show (fontseldiag->fontsel);
3121   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
3122                       fontseldiag->fontsel, TRUE, TRUE, 0);
3123
3124   /* Create the action area */
3125   fontseldiag->action_area = gtk_hbutton_box_new ();
3126   gtk_button_box_set_layout(GTK_BUTTON_BOX(fontseldiag->action_area),
3127                             GTK_BUTTONBOX_END);
3128   gtk_button_box_set_spacing(GTK_BUTTON_BOX(fontseldiag->action_area), 5);
3129   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
3130                       fontseldiag->action_area, FALSE, FALSE, 0);
3131   gtk_widget_show (fontseldiag->action_area);
3132
3133   fontseldiag->ok_button = gtk_button_new_with_label("OK");
3134   GTK_WIDGET_SET_FLAGS (fontseldiag->ok_button, GTK_CAN_DEFAULT);
3135   gtk_widget_show(fontseldiag->ok_button);
3136   gtk_box_pack_start (GTK_BOX (fontseldiag->action_area),
3137                       fontseldiag->ok_button, TRUE, TRUE, 0);
3138   gtk_widget_grab_default (fontseldiag->ok_button);
3139
3140   fontseldiag->apply_button = gtk_button_new_with_label("Apply");
3141   GTK_WIDGET_SET_FLAGS (fontseldiag->apply_button, GTK_CAN_DEFAULT);
3142   /*gtk_widget_show(fontseldiag->apply_button);*/
3143   gtk_box_pack_start (GTK_BOX(fontseldiag->action_area),
3144                       fontseldiag->apply_button, TRUE, TRUE, 0);
3145
3146   fontseldiag->cancel_button = gtk_button_new_with_label("Cancel");
3147   GTK_WIDGET_SET_FLAGS (fontseldiag->cancel_button, GTK_CAN_DEFAULT);
3148   gtk_widget_show(fontseldiag->cancel_button);
3149   gtk_box_pack_start (GTK_BOX(fontseldiag->action_area),
3150                       fontseldiag->cancel_button, TRUE, TRUE, 0);
3151
3152
3153 }
3154
3155 GtkWidget*
3156 gtk_font_selection_dialog_new   (const gchar      *title)
3157 {
3158   GtkFontSelectionDialog *fontseldiag;
3159
3160   fontseldiag = gtk_type_new (gtk_font_selection_dialog_get_type ());
3161   gtk_window_set_title (GTK_WINDOW (fontseldiag),
3162                         title ? title : "Font Selection");
3163
3164   return GTK_WIDGET (fontseldiag);
3165 }
3166
3167 gchar*
3168 gtk_font_selection_dialog_get_font_name (GtkFontSelectionDialog *fsd)
3169 {
3170   return gtk_font_selection_get_font_name(GTK_FONT_SELECTION(fsd->fontsel));
3171 }
3172
3173 GdkFont*
3174 gtk_font_selection_dialog_get_font      (GtkFontSelectionDialog *fsd)
3175 {
3176   return gtk_font_selection_get_font(GTK_FONT_SELECTION(fsd->fontsel));
3177 }
3178
3179 gboolean
3180 gtk_font_selection_dialog_set_font_name (GtkFontSelectionDialog *fsd,
3181                                          const gchar      *fontname)
3182 {
3183   return gtk_font_selection_set_font_name(GTK_FONT_SELECTION(fsd->fontsel),
3184                                           fontname);
3185
3186 }
3187
3188 gchar*
3189 gtk_font_selection_dialog_get_preview_text (GtkFontSelectionDialog *fsd)
3190 {
3191   return gtk_font_selection_get_preview_text(GTK_FONT_SELECTION(fsd->fontsel));
3192 }
3193
3194 void
3195 gtk_font_selection_dialog_set_preview_text (GtkFontSelectionDialog *fsd,
3196                                             const gchar   *text)
3197 {
3198   gtk_font_selection_set_preview_text(GTK_FONT_SELECTION(fsd->fontsel), text);
3199 }
3200
3201
3202 /* This turns auto-shrink off if the user resizes the width of the dialog.
3203    It also turns it back on again if the user resizes it back to its normal
3204    width. */
3205 static gint
3206 gtk_font_selection_dialog_on_configure (GtkWidget         *widget,
3207                                         GdkEventConfigure *event,
3208                                         GtkFontSelectionDialog *fsd)
3209 {
3210   /* This sets the initial width. */
3211   if (fsd->dialog_width == -1)
3212     fsd->dialog_width = event->width;
3213   else if (fsd->auto_resize && fsd->dialog_width != event->width)
3214     {
3215       fsd->auto_resize = FALSE;
3216       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, FALSE);
3217     }
3218   else if (!fsd->auto_resize && fsd->dialog_width == event->width)
3219     {
3220       fsd->auto_resize = TRUE;
3221       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, TRUE);
3222     }
3223
3224   return FALSE;
3225 }