- Fixed all // comments
[wmaker-crm.git] / WPrefs.app / FontSimple.c
blob3cf80100f13dcf9838aeb52767d2f40d767cc282
1 /* FontSimple.c- simplified font configuration panel
2 *
3 * WPrefs - Window Maker Preferences Program
4 *
5 * Copyright (c) 1998-2004 Alfredo K. Kojima
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
24 #include "WPrefs.h"
25 #include <unistd.h>
26 #include <fontconfig/fontconfig.h>
28 #define SAMPLE_TEXT "The Lazy Fox Jumped Ipsum Foobar 1234 - 56789"
31 typedef struct {
32 int weight;
33 int width;
34 int slant;
35 } FontStyle;
37 typedef struct {
38 char *name;
39 int stylen;
40 FontStyle *styles;
41 } FontFamily;
44 typedef struct {
45 int familyn;
46 FontFamily *families;
47 } FontList;
50 typedef struct _Panel {
51 WMBox *box;
52 char *sectionName;
54 char *description;
56 CallbackRec callbacks;
58 WMWidget *parent;
60 WMPopUpButton *optionP;
62 WMList *familyL;
63 WMList *styleL;
65 WMList *sizeL;
67 WMTextField *sampleT;
69 FontList *fonts;
70 } _Panel;
73 #define ICON_FILE "fonts"
76 static struct {
77 char *option;
78 char *label;
79 } fontOptions[]= {
80 {"WindowTitleFont", N_("Window Title")},
81 {"MenuTitleFont", N_("Menu Title")},
82 {"MenuTextFont", N_("Menu Text")},
83 {"IconTitleFont", N_("Icon Title")},
84 {"ClipTitleFont", N_("Clip Title")},
85 {"LargeDisplayFont", N_("Desktop Caption")},
86 {NULL, NULL},
90 static char *standardSizes[]= {
91 "6",
92 "8",
93 "9",
94 "10",
95 "11",
96 "12",
97 "13",
98 "14",
99 "15",
100 "16",
101 "18",
102 "20",
103 "22",
104 "24",
105 "28",
106 "32",
107 "36",
108 "48",
109 "64",
110 "72",
111 NULL
115 static struct {
116 int weight;
117 char *name;
118 } fontWeights[]= {
119 {FC_WEIGHT_THIN, "Thin"},
120 {FC_WEIGHT_EXTRALIGHT, "ExtraLight"},
121 {FC_WEIGHT_LIGHT, "Light"},
122 {FC_WEIGHT_NORMAL, "Normal"},
123 {FC_WEIGHT_MEDIUM, ""}, /*"medium"},*/
124 {FC_WEIGHT_DEMIBOLD, "DemiBold"},
125 {FC_WEIGHT_BOLD, "Bold"},
126 {FC_WEIGHT_EXTRABOLD, "ExtraBold"},
127 {FC_WEIGHT_BLACK, "Black"},
128 {0, NULL}
131 static struct {
132 int slant;
133 char *name;
134 } fontSlants[]= {
135 {FC_SLANT_ROMAN, ""}, /*"Roman"},*/
136 {FC_SLANT_ITALIC, "Italic"},
137 {FC_SLANT_OBLIQUE, "Oblique"},
138 {0, NULL}
141 static struct {
142 int width;
143 char *name;
144 } fontWidths[]= {
145 {FC_WIDTH_ULTRACONDENSED, "UltraCondensed"},
146 {FC_WIDTH_EXTRACONDENSED, "ExtraCondensed"},
147 {FC_WIDTH_CONDENSED, "Condensed"},
148 {FC_WIDTH_SEMICONDENSED, "SemiCondensed"},
149 {FC_WIDTH_NORMAL, ""}, /*"normal"},*/
150 {FC_WIDTH_SEMIEXPANDED, "SemiExpanded"},
151 {FC_WIDTH_EXPANDED, "Expanded"},
152 {FC_WIDTH_EXTRAEXPANDED, "ExtraExpanded"},
153 {FC_WIDTH_ULTRAEXPANDED, "UltraExpanded"},
154 {0, NULL}
161 static int compare_family(const void *a, const void *b)
163 FontFamily *fa= (FontFamily*)a;
164 FontFamily *fb= (FontFamily*)b;
165 return strcmp(fa->name, fb->name);
169 static int compare_styles(const void *a, const void *b)
171 FontStyle *sa= (FontStyle*)a;
172 FontStyle *sb= (FontStyle*)b;
173 int compare;
175 compare = sa->weight - sb->weight;
176 if (compare != 0)
177 return compare;
178 compare = sa->slant - sb->slant;
179 if (compare != 0)
180 return compare;
181 return (sa->width - sb->width);
185 static void
186 lookup_available_fonts(_Panel *panel)
188 FcPattern *pat = FcPatternCreate();
189 FcObjectSet *os;
190 FcFontSet *fonts;
191 FontFamily *family;
193 os = FcObjectSetBuild(FC_FAMILY, FC_WEIGHT, FC_WIDTH, FC_SLANT, NULL);
195 fonts = FcFontList(0, pat, os);
197 if (fonts)
199 int i;
201 panel->fonts= wmalloc(sizeof(FontList));
202 panel->fonts->familyn= 0;
203 panel->fonts->families= wmalloc(sizeof(FontFamily)*fonts->nfont);
205 for (i= 0; i < fonts->nfont; i++)
207 FcChar8 *name;
208 int weight, slant, width;
209 int j, found;
211 if (FcPatternGetString(fonts->fonts[i], FC_FAMILY, 0, &name) != FcResultMatch)
212 continue;
214 if (FcPatternGetInteger(fonts->fonts[i], FC_WEIGHT, 0, &weight) != FcResultMatch)
215 weight= FC_WEIGHT_MEDIUM;
217 if (FcPatternGetInteger(fonts->fonts[i], FC_WIDTH, 0, &width) != FcResultMatch)
218 width= FC_WIDTH_NORMAL;
220 if (FcPatternGetInteger(fonts->fonts[i], FC_SLANT, 0, &slant) != FcResultMatch)
221 slant= FC_SLANT_ROMAN;
223 found = -1;
224 for (j = 0; j < panel->fonts->familyn && found<0; j++)
225 if (strcasecmp(panel->fonts->families[j].name, name)==0)
226 found= j;
228 if (found < 0)
230 panel->fonts->families[panel->fonts->familyn++].name= wstrdup(name);
231 family= panel->fonts->families + panel->fonts->familyn-1;
232 family->stylen= 0;
233 family->styles= NULL;
235 else
236 family= panel->fonts->families+found;
238 family->stylen++;
239 family->styles= wrealloc(family->styles, sizeof(FontStyle)*family->stylen);
240 family->styles[family->stylen-1].weight= weight;
241 family->styles[family->stylen-1].slant= slant;
242 family->styles[family->stylen-1].width= width;
244 qsort(panel->fonts->families, panel->fonts->familyn, sizeof(FontFamily),
245 compare_family);
247 for (i=0; i < panel->fonts->familyn; i++)
249 qsort(panel->fonts->families[i].styles, panel->fonts->families[i].stylen,
250 sizeof(FontStyle), compare_styles);
253 FcFontSetDestroy(fonts);
255 if (os)
256 FcObjectSetDestroy(os);
257 if (pat)
258 FcPatternDestroy(pat);
260 panel->fonts->families[panel->fonts->familyn++].name= wstrdup("sans serif");
261 family= panel->fonts->families + panel->fonts->familyn-1;
262 family->styles= wmalloc(sizeof(FontStyle)*2);
263 family->stylen= 2;
264 family->styles[0].weight= FC_WEIGHT_MEDIUM;
265 family->styles[0].slant= FC_SLANT_ROMAN;
266 family->styles[0].width= FC_WIDTH_NORMAL;
267 family->styles[1].weight= FC_WEIGHT_BOLD;
268 family->styles[1].slant= FC_SLANT_ROMAN;
269 family->styles[1].width= FC_WIDTH_NORMAL;
271 panel->fonts->families[panel->fonts->familyn++].name= wstrdup("serif");
272 family= panel->fonts->families + panel->fonts->familyn-1;
273 family->styles= wmalloc(sizeof(FontStyle)*2);
274 family->stylen= 2;
275 family->styles[0].weight= FC_WEIGHT_MEDIUM;
276 family->styles[0].slant= FC_SLANT_ROMAN;
277 family->styles[0].width= FC_WIDTH_NORMAL;
278 family->styles[1].weight= FC_WEIGHT_BOLD;
279 family->styles[1].slant= FC_SLANT_ROMAN;
280 family->styles[1].width= FC_WIDTH_NORMAL;
284 static char*
285 getSelectedFont(_Panel *panel, char *curfont)
287 WMListItem *item;
288 FcPattern *pat= FcNameParse(curfont);
289 char *name;
291 item= WMGetListSelectedItem(panel->familyL);
292 if (item)
294 FcPatternDel(pat, FC_FAMILY);
295 FcPatternAddString(pat, FC_FAMILY, item->text);
298 item= WMGetListSelectedItem(panel->styleL);
299 if (item)
301 FontStyle *style= (FontStyle*)item->clientData;
303 FcPatternDel(pat, FC_WEIGHT);
304 FcPatternAddInteger(pat, FC_WEIGHT, style->weight);
306 FcPatternDel(pat, FC_WIDTH);
307 FcPatternAddInteger(pat, FC_WIDTH, style->width);
309 FcPatternDel(pat, FC_SLANT);
310 FcPatternAddInteger(pat, FC_SLANT, style->slant);
313 item= WMGetListSelectedItem(panel->sizeL);
314 if (item)
316 FcPatternDel(pat, FC_PIXEL_SIZE);
317 FcPatternAddDouble(pat, FC_PIXEL_SIZE, atoi(item->text));
320 name= FcNameUnparse(pat);
321 FcPatternDestroy(pat);
323 return name;
328 static void
329 updateSampleFont(_Panel *panel)
331 WMMenuItem *item= WMGetPopUpButtonMenuItem(panel->optionP,
332 WMGetPopUpButtonSelectedItem(panel->optionP));
333 char *fn= WMGetMenuItemRepresentedObject(item);
334 WMFont *font= WMCreateFont(WMWidgetScreen(panel->box), fn);
336 if (font)
338 WMSetTextFieldFont(panel->sampleT, font);
339 WMReleaseFont(font);
346 static void
347 selectedFamily(WMWidget *w, void *data)
349 _Panel *panel= (_Panel*)data;
350 WMListItem *item;
351 FontStyle *oldStyle= NULL;
352 char buffer[1024];
354 item= WMGetListSelectedItem(panel->styleL);
355 if (item)
356 oldStyle= (FontStyle*)item->clientData;
358 item= WMGetListSelectedItem(panel->familyL);
360 if (item)
362 FontFamily *family= (FontFamily*)item->clientData;
363 int i, oldi= 0, oldscore= 0;
365 WMClearList(panel->styleL);
366 for (i = 0; i < family->stylen; i++)
368 int j;
369 char *weight= "", *slant= "", *width= "";
370 WMListItem *item;
372 for (j= 0; fontWeights[j].name; j++)
373 if (fontWeights[j].weight == family->styles[i].weight)
375 weight= fontWeights[j].name;
376 break;
378 for (j= 0; fontWidths[j].name; j++)
379 if (fontWidths[j].width == family->styles[i].width)
381 width= fontWidths[j].name;
382 break;
384 for (j= 0; fontSlants[j].name; j++)
385 if (fontSlants[j].slant == family->styles[i].slant)
387 slant= fontSlants[j].name;
388 break;
390 sprintf(buffer, "%s%s%s%s%s",
391 weight, *weight?" ":"",
392 slant, (*slant || *weight)?" ":"",
393 width);
394 if (!buffer[0])
395 strcpy(buffer, "Roman");
397 item= WMAddListItem(panel->styleL, buffer);
398 item->clientData= family->styles+i;
400 if (oldStyle) {
401 int score= 0;
403 if (oldStyle->width == family->styles[i].width)
404 score |= 1;
405 if (oldStyle->weight == family->styles[i].weight)
406 score |= 2;
407 if (oldStyle->slant == family->styles[i].slant)
408 score |= 4;
410 if (score > oldscore)
412 oldi= i;
413 oldscore= score;
417 WMSelectListItem(panel->styleL, oldi);
420 int index= WMGetPopUpButtonSelectedItem(panel->optionP);
421 WMMenuItem *item= WMGetPopUpButtonMenuItem(panel->optionP, index);
422 char *ofont, *nfont;
424 ofont= (char*)WMGetMenuItemRepresentedObject(item);
426 nfont= getSelectedFont(panel, ofont);
427 free(ofont);
428 WMSetMenuItemRepresentedObject(item, nfont);
430 updateSampleFont(panel);
435 static void
436 selected(WMWidget *w, void *data)
438 _Panel *panel= (_Panel*)data;
439 int index= WMGetPopUpButtonSelectedItem(panel->optionP);
440 WMMenuItem *item= WMGetPopUpButtonMenuItem(panel->optionP, index);
441 char *ofont, *nfont;
443 ofont= (char*)WMGetMenuItemRepresentedObject(item);
445 nfont= getSelectedFont(panel, ofont);
446 free(ofont);
447 WMSetMenuItemRepresentedObject(item, nfont);
449 updateSampleFont(panel);
453 static void
454 selectedOption(WMWidget *w, void *data)
456 _Panel *panel= (_Panel*)data;
457 int index = WMGetPopUpButtonSelectedItem(panel->optionP);
458 WMMenuItem *item= WMGetPopUpButtonMenuItem(panel->optionP, index);
459 char *font;
461 font= (char*)WMGetMenuItemRepresentedObject(item);
462 if (font)
464 FcPattern *pat;
466 pat= FcNameParse(font);
467 if (pat)
469 FcChar8 *name;
470 int weight, slant, width;
471 double size;
472 int distance, closest, found;
473 int i;
475 FcDefaultSubstitute(pat);
477 if (FcPatternGetString(pat, FC_FAMILY, 0, &name) != FcResultMatch)
478 name= "sans serif";
480 found= 0;
481 /* select family */
482 for (i= 0; i < WMGetListNumberOfRows(panel->familyL); i++)
484 WMListItem *item= WMGetListItem(panel->familyL, i);
485 FontFamily *family= (FontFamily*)item->clientData;
487 if (strcasecmp(family->name, name)==0)
489 found= 1;
490 WMSelectListItem(panel->familyL, i);
491 WMSetListPosition(panel->familyL, i);
492 break;
495 if (!found)
496 WMSelectListItem(panel->familyL, -1);
497 selectedFamily(panel->familyL, panel);
499 /* select style */
500 if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &weight) != FcResultMatch)
501 weight= FC_WEIGHT_NORMAL;
502 if (FcPatternGetInteger(pat, FC_WIDTH, 0, &width) != FcResultMatch)
503 width= FC_WIDTH_NORMAL;
504 if (FcPatternGetInteger(pat, FC_SLANT, 0, &slant) != FcResultMatch)
505 slant= FC_SLANT_ROMAN;
507 if (FcPatternGetDouble(pat, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
508 size= 10.0;
510 for (i=0, found=0, closest=0; i < WMGetListNumberOfRows(panel->styleL); i++)
512 WMListItem *item= WMGetListItem(panel->styleL, i);
513 FontStyle *style= (FontStyle*)item->clientData;
515 distance = ((abs(style->weight - weight) << 16) +
516 (abs(style->slant - slant) << 8) +
517 (abs(style->width - width)));
519 if (i==0 || distance < closest) {
520 closest = distance;
521 found = i;
522 if (distance == 0) {
523 break; /* perfect match */
527 WMSelectListItem(panel->styleL, found);
528 WMSetListPosition(panel->styleL, found);
530 for (i=0, found=0, closest=0; i < WMGetListNumberOfRows(panel->sizeL); i++)
532 WMListItem *item= WMGetListItem(panel->sizeL, i);
533 int distance;
535 distance = abs(size - atoi(item->text));
537 if (i==0 || distance < closest) {
538 closest= distance;
539 found= i;
540 if (distance == 0) {
541 break; /* perfect match */
545 WMSelectListItem(panel->sizeL, found);
546 WMSetListPosition(panel->sizeL, found);
548 selected(NULL, panel);
550 else
551 wwarning("Can't parse font '%s'", font);
554 updateSampleFont(panel);
558 static WMLabel *
559 createListLabel(WMScreen *scr, WMWidget *parent, char *text)
561 WMLabel *label;
562 WMColor *color;
563 WMFont *boldFont= WMBoldSystemFontOfSize(scr, 12);
565 label = WMCreateLabel(parent);
566 WMSetLabelFont(label, boldFont);
567 WMSetLabelText(label, text);
568 WMSetLabelRelief(label, WRSunken);
569 WMSetLabelTextAlignment(label, WACenter);
570 color = WMDarkGrayColor(scr);
571 WMSetWidgetBackgroundColor(label, color);
572 WMReleaseColor(color);
573 color = WMWhiteColor(scr);
574 WMSetLabelTextColor(label, color);
575 WMReleaseColor(color);
577 WMReleaseFont(boldFont);
579 return label;
583 static void
584 showData(_Panel *panel)
586 int i;
587 WMMenuItem *item;
589 for (i= 0; i < WMGetPopUpButtonNumberOfItems(panel->optionP); i++)
591 char *ofont, *font;
593 item= WMGetPopUpButtonMenuItem(panel->optionP, i);
595 ofont= WMGetMenuItemRepresentedObject(item);
596 if (ofont)
597 wfree(ofont);
599 font= GetStringForKey(fontOptions[i].option);
600 if (font)
601 font= wstrdup(font);
602 WMSetMenuItemRepresentedObject(item, font);
605 WMSetPopUpButtonSelectedItem(panel->optionP, 0);
606 selectedOption(panel->optionP, panel);
610 static void
611 storeData(_Panel *panel)
613 int i;
614 WMMenuItem *item;
615 for (i= 0; i < WMGetPopUpButtonNumberOfItems(panel->optionP); i++)
617 char *font;
619 item= WMGetPopUpButtonMenuItem(panel->optionP, i);
621 font= WMGetMenuItemRepresentedObject(item);
622 if (font && *font)
624 SetStringForKey(font, fontOptions[i].option);
630 static void
631 createPanel(Panel *p)
633 _Panel *panel = (_Panel*)p;
634 WMScreen *scr = WMWidgetScreen(panel->parent);
635 WMLabel *label;
636 WMBox *hbox, *vbox;
637 int i;
639 lookup_available_fonts(panel);
641 panel->box = WMCreateBox(panel->parent);
642 WMSetViewExpandsToParent(WMWidgetView(panel->box), 5, 2, 5, 5);
643 WMSetBoxHorizontal(panel->box, False);
644 WMSetBoxBorderWidth(panel->box, 8);
645 WMMapWidget(panel->box);
647 hbox = WMCreateBox(panel->box);
648 WMSetBoxHorizontal(hbox, True);
649 WMAddBoxSubview(panel->box, WMWidgetView(hbox), False, True, 40, 22, 8);
651 vbox= WMCreateBox(hbox);
652 WMAddBoxSubview(hbox, WMWidgetView(vbox), False, True, 130, 0, 10);
653 WMSetBoxHorizontal(vbox, False);
654 panel->optionP = WMCreatePopUpButton(vbox);
655 WMAddBoxSubviewAtEnd(vbox, WMWidgetView(panel->optionP), False, True, 20, 0, 8);
656 for (i= 0; fontOptions[i].option; i++)
658 WMAddPopUpButtonItem(panel->optionP, _(fontOptions[i].label));
660 WMSetPopUpButtonAction(panel->optionP, selectedOption, panel);
662 label = WMCreateLabel(hbox);
663 WMSetLabelText(label, _("Sample Text"));
664 WMSetLabelTextAlignment(label, WARight);
665 WMAddBoxSubview(hbox, WMWidgetView(label), False, True, 80, 0, 2);
667 panel->sampleT= WMCreateTextField(hbox);
668 WMSetViewExpandsToParent(WMWidgetView(panel->sampleT), 10, 18, 10, 10);
669 WMSetTextFieldText(panel->sampleT, SAMPLE_TEXT);
670 WMAddBoxSubview(hbox, WMWidgetView(panel->sampleT), True, True, 60, 0, 0);
673 hbox = WMCreateBox(panel->box);
674 WMSetBoxHorizontal(hbox, True);
675 WMAddBoxSubview(panel->box, WMWidgetView(hbox), True, True, 100, 0, 2);
677 vbox = WMCreateBox(hbox);
678 WMSetBoxHorizontal(vbox, False);
679 WMAddBoxSubview(hbox, WMWidgetView(vbox), False, True, 240, 20, 4);
681 label = createListLabel(scr, vbox, _("Family"));
682 WMAddBoxSubview(vbox, WMWidgetView(label), False, True, 20, 0, 2);
684 /* family */
685 panel->familyL = WMCreateList(vbox);
686 WMAddBoxSubview(vbox, WMWidgetView(panel->familyL), True, True, 0, 0, 0);
687 if (panel->fonts)
689 WMListItem *item;
690 for (i= 0; i < panel->fonts->familyn; i++)
692 item = WMAddListItem(panel->familyL, panel->fonts->families[i].name);
693 item->clientData= panel->fonts->families+i;
696 else
697 WMAddListItem(panel->familyL, "sans serif");
699 WMSetListAction(panel->familyL, selectedFamily, panel);
701 vbox = WMCreateBox(hbox);
702 WMSetBoxHorizontal(vbox, False);
703 WMAddBoxSubview(hbox, WMWidgetView(vbox), True, True, 10, 0, 0);
706 WMBox *box = WMCreateBox(vbox);
707 WMSetBoxHorizontal(box, True);
708 WMAddBoxSubview(vbox, WMWidgetView(box), False, True, 20, 0, 2);
710 label = createListLabel(scr, box, _("Style"));
711 WMAddBoxSubview(box, WMWidgetView(label), True, True, 20, 0, 4);
713 label = createListLabel(scr, box, _("Size"));
714 WMAddBoxSubview(box, WMWidgetView(label), False, True, 60, 0, 0);
716 box = WMCreateBox(vbox);
717 WMSetBoxHorizontal(box, True);
718 WMAddBoxSubview(vbox, WMWidgetView(box), True, True, 20, 0, 0);
720 panel->styleL = WMCreateList(box);
721 WMAddBoxSubview(box, WMWidgetView(panel->styleL), True, True, 0, 0, 4);
722 WMSetListAction(panel->styleL, selected, panel);
724 panel->sizeL = WMCreateList(box);
725 WMAddBoxSubview(box, WMWidgetView(panel->sizeL), False, True, 60, 0, 0);
726 for (i= 0; standardSizes[i]; i++)
728 WMAddListItem(panel->sizeL, standardSizes[i]);
730 WMSetListAction(panel->sizeL, selected, panel);
734 WMMapSubwidgets(panel->box);
735 WMMapWidget(panel->box);
736 WMRealizeWidget(panel->box);
738 showData(panel);
743 Panel*
744 InitFontSimple(WMScreen *scr, WMWidget *parent)
746 _Panel *panel;
748 panel = wmalloc(sizeof(_Panel));
749 memset(panel, 0, sizeof(_Panel));
751 panel->sectionName = _("Font Configuration");
753 panel->description = _("Configure fonts for Window Maker titlebars, menus etc.");
755 panel->parent = parent;
757 panel->callbacks.createWidgets = createPanel;
758 panel->callbacks.updateDomain = storeData;
760 AddSection(panel, ICON_FILE);
762 return panel;