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