11 #include <X11/Xft/Xft.h>
12 #include <fontconfig/fontconfig.h>
15 char *WMFontPanelFontChangedNotification
= "WMFontPanelFontChangedNotification";
17 typedef struct W_FontPanel
{
43 #define MIN_UPPER_HEIGHT 20
44 #define MIN_LOWER_HEIGHT 140
46 #define BUTTON_SPACE_HEIGHT 40
49 #define MIN_HEIGHT (MIN_UPPER_HEIGHT+MIN_LOWER_HEIGHT+BUTTON_SPACE_HEIGHT)
51 #define DEF_UPPER_HEIGHT 60
52 #define DEF_LOWER_HEIGHT 310
55 #define DEF_HEIGHT (DEF_UPPER_HEIGHT+DEF_LOWER_HEIGHT)
57 static const int scalableFontSizes
[] = {
72 static void setFontPanelFontName(FontPanel
* panel
, const char *family
, const char *style
, double size
);
74 static int isXLFD(const char *font
, int *length_ret
);
76 static void arrangeLowerFrame(FontPanel
* panel
);
78 static void familyClick(WMWidget
*, void *);
79 static void typefaceClick(WMWidget
*, void *);
80 static void sizeClick(WMWidget
*, void *);
82 static void listFamilies(WMScreen
* scr
, WMFontPanel
* panel
);
84 static void splitViewConstrainCallback(WMSplitView
* sPtr
, int indView
, int *min
, int *max
)
86 /* Parameter not used, but tell the compiler that it is ok */
91 *min
= MIN_UPPER_HEIGHT
;
93 *min
= MIN_LOWER_HEIGHT
;
96 static void notificationObserver(void *self
, WMNotification
* notif
)
98 WMFontPanel
*panel
= (WMFontPanel
*) self
;
99 void *object
= WMGetNotificationObject(notif
);
101 if (WMGetNotificationName(notif
) == WMViewSizeDidChangeNotification
) {
102 if (object
== WMWidgetView(panel
->win
)) {
103 int h
= WMWidgetHeight(panel
->win
);
104 int w
= WMWidgetWidth(panel
->win
);
106 WMResizeWidget(panel
->split
, w
, h
- BUTTON_SPACE_HEIGHT
);
108 WMMoveWidget(panel
->setB
, w
- 80, h
- (BUTTON_SPACE_HEIGHT
- 5));
110 WMMoveWidget(panel
->revertB
, w
- 240, h
- (BUTTON_SPACE_HEIGHT
- 5));
112 } else if (object
== WMWidgetView(panel
->upperF
)) {
114 if (WMWidgetHeight(panel
->upperF
) < MIN_UPPER_HEIGHT
) {
115 WMResizeWidget(panel
->upperF
, WMWidgetWidth(panel
->upperF
), MIN_UPPER_HEIGHT
);
117 WMResizeWidget(panel
->sampleT
, WMWidgetWidth(panel
->upperF
) - 20,
118 WMWidgetHeight(panel
->upperF
) - 10);
121 } else if (object
== WMWidgetView(panel
->lowerF
)) {
123 if (WMWidgetHeight(panel
->lowerF
) < MIN_LOWER_HEIGHT
) {
124 WMResizeWidget(panel
->upperF
, WMWidgetWidth(panel
->upperF
), MIN_UPPER_HEIGHT
);
126 WMMoveWidget(panel
->lowerF
, 0, WMWidgetHeight(panel
->upperF
)
127 + WMGetSplitViewDividerThickness(panel
->split
));
129 WMResizeWidget(panel
->lowerF
, WMWidgetWidth(panel
->lowerF
),
130 WMWidgetWidth(panel
->split
) - MIN_UPPER_HEIGHT
131 - WMGetSplitViewDividerThickness(panel
->split
));
133 arrangeLowerFrame(panel
);
139 static void closeWindow(WMWidget
* w
, void *data
)
141 FontPanel
*panel
= (FontPanel
*) data
;
143 /* Parameter not used, but tell the compiler that it is ok */
146 WMHideFontPanel(panel
);
149 static void setClickedAction(WMWidget
* w
, void *data
)
151 FontPanel
*panel
= (FontPanel
*) data
;
153 /* Parameter not used, but tell the compiler that it is ok */
157 (*panel
->action
) (panel
, panel
->data
);
160 static void revertClickedAction(WMWidget
* w
, void *data
)
162 /* Parameter not used, but tell the compiler that it is ok */
166 /*FontPanel *panel = (FontPanel*)data; */
170 WMFontPanel
*WMGetFontPanel(WMScreen
* scr
)
173 WMColor
*dark
, *white
;
177 if (scr
->sharedFontPanel
)
178 return scr
->sharedFontPanel
;
180 panel
= wmalloc(sizeof(FontPanel
));
182 panel
->win
= WMCreateWindow(scr
, "fontPanel");
183 /* WMSetWidgetBackgroundColor(panel->win, WMWhiteColor(scr)); */
184 WMSetWindowTitle(panel
->win
, _("Font Panel"));
185 WMResizeWidget(panel
->win
, DEF_WIDTH
, DEF_HEIGHT
);
186 WMSetWindowMinSize(panel
->win
, MIN_WIDTH
, MIN_HEIGHT
);
187 WMSetViewNotifySizeChanges(WMWidgetView(panel
->win
), True
);
189 WMSetWindowCloseAction(panel
->win
, closeWindow
, panel
);
191 panel
->split
= WMCreateSplitView(panel
->win
);
192 WMResizeWidget(panel
->split
, DEF_WIDTH
, DEF_HEIGHT
- BUTTON_SPACE_HEIGHT
);
193 WMSetSplitViewConstrainProc(panel
->split
, splitViewConstrainCallback
);
195 divThickness
= WMGetSplitViewDividerThickness(panel
->split
);
197 panel
->upperF
= WMCreateFrame(panel
->win
);
198 WMSetFrameRelief(panel
->upperF
, WRFlat
);
199 WMSetViewNotifySizeChanges(WMWidgetView(panel
->upperF
), True
);
200 panel
->lowerF
= WMCreateFrame(panel
->win
);
201 /* WMSetWidgetBackgroundColor(panel->lowerF, WMBlackColor(scr)); */
202 WMSetFrameRelief(panel
->lowerF
, WRFlat
);
203 WMSetViewNotifySizeChanges(WMWidgetView(panel
->lowerF
), True
);
205 WMAddSplitViewSubview(panel
->split
, W_VIEW(panel
->upperF
));
206 WMAddSplitViewSubview(panel
->split
, W_VIEW(panel
->lowerF
));
208 WMResizeWidget(panel
->upperF
, DEF_WIDTH
, DEF_UPPER_HEIGHT
);
210 WMResizeWidget(panel
->lowerF
, DEF_WIDTH
, DEF_LOWER_HEIGHT
);
212 WMMoveWidget(panel
->lowerF
, 0, 60 + divThickness
);
214 white
= WMWhiteColor(scr
);
215 dark
= WMDarkGrayColor(scr
);
217 panel
->sampleT
= WMCreateTextField(panel
->upperF
);
218 WMResizeWidget(panel
->sampleT
, DEF_WIDTH
- 20, 50);
219 WMMoveWidget(panel
->sampleT
, 10, 10);
220 WMSetTextFieldText(panel
->sampleT
, _("The quick brown fox jumps over the lazy dog"));
222 font
= WMBoldSystemFontOfSize(scr
, 12);
224 panel
->famL
= WMCreateLabel(panel
->lowerF
);
225 WMSetWidgetBackgroundColor(panel
->famL
, dark
);
226 WMSetLabelText(panel
->famL
, _("Family"));
227 WMSetLabelFont(panel
->famL
, font
);
228 WMSetLabelTextColor(panel
->famL
, white
);
229 WMSetLabelRelief(panel
->famL
, WRSunken
);
230 WMSetLabelTextAlignment(panel
->famL
, WACenter
);
232 panel
->famLs
= WMCreateList(panel
->lowerF
);
233 WMSetListAction(panel
->famLs
, familyClick
, panel
);
235 panel
->typL
= WMCreateLabel(panel
->lowerF
);
236 WMSetWidgetBackgroundColor(panel
->typL
, dark
);
237 WMSetLabelText(panel
->typL
, _("Typeface"));
238 WMSetLabelFont(panel
->typL
, font
);
239 WMSetLabelTextColor(panel
->typL
, white
);
240 WMSetLabelRelief(panel
->typL
, WRSunken
);
241 WMSetLabelTextAlignment(panel
->typL
, WACenter
);
243 panel
->typLs
= WMCreateList(panel
->lowerF
);
244 WMSetListAction(panel
->typLs
, typefaceClick
, panel
);
246 panel
->sizL
= WMCreateLabel(panel
->lowerF
);
247 WMSetWidgetBackgroundColor(panel
->sizL
, dark
);
248 WMSetLabelText(panel
->sizL
, _("Size"));
249 WMSetLabelFont(panel
->sizL
, font
);
250 WMSetLabelTextColor(panel
->sizL
, white
);
251 WMSetLabelRelief(panel
->sizL
, WRSunken
);
252 WMSetLabelTextAlignment(panel
->sizL
, WACenter
);
254 panel
->sizT
= WMCreateTextField(panel
->lowerF
);
255 /* WMSetTextFieldAlignment(panel->sizT, WARight); */
257 panel
->sizLs
= WMCreateList(panel
->lowerF
);
258 WMSetListAction(panel
->sizLs
, sizeClick
, panel
);
261 WMReleaseColor(white
);
262 WMReleaseColor(dark
);
264 panel
->setB
= WMCreateCommandButton(panel
->win
);
265 WMResizeWidget(panel
->setB
, 70, 24);
266 WMMoveWidget(panel
->setB
, 240, DEF_HEIGHT
- (BUTTON_SPACE_HEIGHT
- 5));
267 WMSetButtonText(panel
->setB
, _("Set"));
268 WMSetButtonAction(panel
->setB
, setClickedAction
, panel
);
270 panel
->revertB
= WMCreateCommandButton(panel
->win
);
271 WMResizeWidget(panel
->revertB
, 70, 24);
272 WMMoveWidget(panel
->revertB
, 80, DEF_HEIGHT
- (BUTTON_SPACE_HEIGHT
- 5));
273 WMSetButtonText(panel
->revertB
, _("Revert"));
274 WMSetButtonAction(panel
->revertB
, revertClickedAction
, panel
);
276 WMRealizeWidget(panel
->win
);
278 WMMapSubwidgets(panel
->upperF
);
279 WMMapSubwidgets(panel
->lowerF
);
280 WMMapSubwidgets(panel
->split
);
281 WMMapSubwidgets(panel
->win
);
283 WMUnmapWidget(panel
->revertB
);
285 arrangeLowerFrame(panel
);
287 scr
->sharedFontPanel
= panel
;
289 /* register notification observers */
290 WMAddNotificationObserver(notificationObserver
, panel
,
291 WMViewSizeDidChangeNotification
, WMWidgetView(panel
->win
));
292 WMAddNotificationObserver(notificationObserver
, panel
,
293 WMViewSizeDidChangeNotification
, WMWidgetView(panel
->upperF
));
294 WMAddNotificationObserver(notificationObserver
, panel
,
295 WMViewSizeDidChangeNotification
, WMWidgetView(panel
->lowerF
));
297 listFamilies(scr
, panel
);
302 void WMFreeFontPanel(WMFontPanel
* panel
)
304 if (panel
== WMWidgetScreen(panel
->win
)->sharedFontPanel
) {
305 WMWidgetScreen(panel
->win
)->sharedFontPanel
= NULL
;
307 WMRemoveNotificationObserver(panel
);
308 WMUnmapWidget(panel
->win
);
309 WMDestroyWidget(panel
->win
);
313 void WMShowFontPanel(WMFontPanel
* panel
)
315 WMMapWidget(panel
->win
);
318 void WMHideFontPanel(WMFontPanel
* panel
)
320 WMUnmapWidget(panel
->win
);
323 WMFont
*WMGetFontPanelFont(WMFontPanel
* panel
)
325 return WMGetTextFieldFont(panel
->sampleT
);
328 void WMSetFontPanelFont(WMFontPanel
* panel
, const char *fontName
)
332 FcChar8
*family
, *style
;
335 if (!isXLFD(fontName
, &fname_len
)) {
336 /* maybe its proper fontconfig and we can parse it */
337 pattern
= FcNameParse((const FcChar8
*) fontName
);
339 /* maybe its proper xlfd and we can convert it to an FcPattern */
340 pattern
= XftXlfdParse(fontName
, False
, False
);
341 /*//FcPatternPrint(pattern); */
347 if (FcPatternGetString(pattern
, FC_FAMILY
, 0, &family
) == FcResultMatch
)
348 if (FcPatternGetString(pattern
, FC_STYLE
, 0, &style
) == FcResultMatch
)
349 if (FcPatternGetDouble(pattern
, "pixelsize", 0, &size
) == FcResultMatch
)
350 setFontPanelFontName(panel
, (char *)family
, (char *)style
, size
);
352 FcPatternDestroy(pattern
);
355 void WMSetFontPanelAction(WMFontPanel
* panel
, WMAction2
* action
, void *data
)
357 panel
->action
= action
;
361 static void arrangeLowerFrame(FontPanel
* panel
)
363 int width
= WMWidgetWidth(panel
->lowerF
) - 55 - 30;
364 int height
= WMWidgetHeight(panel
->split
) - WMWidgetHeight(panel
->upperF
);
367 #define LABEL_HEIGHT 20
369 height
-= WMGetSplitViewDividerThickness(panel
->split
);
371 height
-= LABEL_HEIGHT
+ 8;
373 fw
= (125 * width
) / 235;
374 tw
= (110 * width
) / 235;
377 WMMoveWidget(panel
->famL
, 10, 0);
378 WMResizeWidget(panel
->famL
, fw
, LABEL_HEIGHT
);
380 WMMoveWidget(panel
->famLs
, 10, 23);
381 WMResizeWidget(panel
->famLs
, fw
, height
);
383 WMMoveWidget(panel
->typL
, 10 + fw
+ 3, 0);
384 WMResizeWidget(panel
->typL
, tw
, LABEL_HEIGHT
);
386 WMMoveWidget(panel
->typLs
, 10 + fw
+ 3, 23);
387 WMResizeWidget(panel
->typLs
, tw
, height
);
389 WMMoveWidget(panel
->sizL
, 10 + fw
+ 3 + tw
+ 3, 0);
390 WMResizeWidget(panel
->sizL
, sw
+ 4, LABEL_HEIGHT
);
392 WMMoveWidget(panel
->sizT
, 10 + fw
+ 3 + tw
+ 3, 23);
393 WMResizeWidget(panel
->sizT
, sw
+ 4, 20);
395 WMMoveWidget(panel
->sizLs
, 10 + fw
+ 3 + tw
+ 3, 46);
396 WMResizeWidget(panel
->sizLs
, sw
+ 4, height
- 23);
399 #define NUM_FIELDS 14
401 static int isXLFD(const char *font
, int *length_ret
)
412 return c
== NUM_FIELDS
;
421 char *name
; /* gotta love simplicity */
425 static int compare_int(const void *a
, const void *b
)
438 static void addSizeToTypeface(Typeface
* face
, int size
)
443 for (j
= 0; j
< wlengthof(scalableFontSizes
); j
++) {
444 size
= scalableFontSizes
[j
];
446 if (!WMCountInArray(face
->sizes
, (void *)(uintptr_t) size
)) {
447 WMAddToArray(face
->sizes
, (void *)(uintptr_t) size
);
450 WMSortArray(face
->sizes
, compare_int
);
452 if (!WMCountInArray(face
->sizes
, (void *)(uintptr_t) size
)) {
453 WMAddToArray(face
->sizes
, (void *)(uintptr_t) size
);
454 WMSortArray(face
->sizes
, compare_int
);
459 static void addTypefaceToXftFamily(Family
* fam
, const char *style
)
464 if (fam
->typefaces
) {
465 WM_ITERATE_ARRAY(fam
->typefaces
, face
, i
) {
466 if (strcmp(face
->typeface
, style
) != 0)
467 continue; /* go to next interation */
468 addSizeToTypeface(face
, 0);
472 fam
->typefaces
= WMCreateArray(4);
475 face
= wmalloc(sizeof(Typeface
));
477 face
->typeface
= wstrdup(style
);
478 face
->sizes
= WMCreateArray(4);
479 addSizeToTypeface(face
, 0);
481 WMAddToArray(fam
->typefaces
, face
);
485 * families (same family name) (Hashtable of family -> array)
486 * registries (same family but different registries)
489 static void addFontToXftFamily(WMHashTable
* families
, const char *name
, const char *style
)
495 array
= WMHashGet(families
, name
);
497 WM_ITERATE_ARRAY(array
, fam
, i
) {
498 if (strcmp(fam
->name
, name
) == 0)
499 addTypefaceToXftFamily(fam
, style
);
504 array
= WMCreateArray(8);
506 fam
= wmalloc(sizeof(Family
));
508 fam
->name
= wstrdup(name
);
510 addTypefaceToXftFamily(fam
, style
);
512 WMAddToArray(array
, fam
);
514 WMHashInsert(families
, fam
->name
, array
);
517 static void listFamilies(WMScreen
* scr
, WMFontPanel
* panel
)
522 WMHashTable
*families
;
523 WMHashEnumerator enumer
;
527 pat
= FcPatternCreate();
528 os
= FcObjectSetBuild(FC_FAMILY
, FC_STYLE
, NULL
);
529 fs
= FcFontList(0, pat
, os
);
531 WMRunAlertPanel(scr
, panel
->win
, _("Error"),
532 _("Could not init font config library\n"), _("OK"), NULL
, NULL
);
536 FcPatternDestroy(pat
);
538 families
= WMCreateHashTable(WMStringPointerHashCallbacks
);
541 for (i
= 0; i
< fs
->nfont
; i
++) {
545 if (FcPatternGetString(fs
->fonts
[i
], FC_FAMILY
, 0, &family
) == FcResultMatch
)
546 if (FcPatternGetString(fs
->fonts
[i
], FC_STYLE
, 0, &style
) == FcResultMatch
)
547 addFontToXftFamily(families
, (char *)family
, (char *)style
);
549 FcFontSetDestroy(fs
);
552 enumer
= WMEnumerateHashTable(families
);
554 while ((array
= WMNextHashEnumeratorItem(&enumer
))) {
560 WM_ITERATE_ARRAY(array
, fam
, i
) {
561 wstrlcpy(buffer
, fam
->name
, sizeof(buffer
));
562 item
= WMAddListItem(panel
->famLs
, buffer
);
564 item
->clientData
= fam
;
570 WMSortListItems(panel
->famLs
);
572 WMFreeHashTable(families
);
575 static void getSelectedFont(FontPanel
* panel
, char buffer
[], int bufsize
)
582 item
= WMGetListSelectedItem(panel
->famLs
);
585 family
= (Family
*) item
->clientData
;
587 item
= WMGetListSelectedItem(panel
->typLs
);
590 face
= (Typeface
*) item
->clientData
;
592 size
= WMGetTextFieldText(panel
->sizT
);
594 snprintf(buffer
, bufsize
, "%s:style=%s:pixelsize=%s", family
->name
, face
->typeface
, size
);
599 static void preview(FontPanel
* panel
)
604 getSelectedFont(panel
, buffer
, sizeof(buffer
));
605 font
= WMCreateFont(WMWidgetScreen(panel
->win
), buffer
);
607 WMSetTextFieldFont(panel
->sampleT
, font
);
612 static void familyClick(WMWidget
* w
, void *data
)
614 WMList
*lPtr
= (WMList
*) w
;
618 FontPanel
*panel
= (FontPanel
*) data
;
620 /* current typeface and size */
626 /* must try to keep the same typeface and size for the new family */
627 item
= WMGetListSelectedItem(panel
->typLs
);
629 oface
= wstrdup(item
->text
);
631 osize
= WMGetTextFieldText(panel
->sizT
);
633 item
= WMGetListSelectedItem(lPtr
);
634 family
= (Family
*) item
->clientData
;
636 WMClearList(panel
->typLs
);
638 WM_ITERATE_ARRAY(family
->typefaces
, face
, i
) {
643 wstrlcpy(buffer
, face
->typeface
, sizeof(buffer
));
644 if (strcasecmp(face
->typeface
, "Roman") == 0)
646 if (strcasecmp(face
->typeface
, "Regular") == 0)
649 fitem
= WMInsertListItem(panel
->typLs
, 0, buffer
);
651 fitem
= WMAddListItem(panel
->typLs
, buffer
);
652 fitem
->clientData
= face
;
656 facei
= WMFindRowOfListItemWithTitle(panel
->typLs
, oface
);
662 WMSelectListItem(panel
->typLs
, facei
);
663 typefaceClick(panel
->typLs
, panel
);
666 sizei
= WMFindRowOfListItemWithTitle(panel
->sizLs
, osize
);
669 WMSelectListItem(panel
->sizLs
, sizei
);
670 sizeClick(panel
->sizLs
, panel
);
679 static void typefaceClick(WMWidget
* w
, void *data
)
681 FontPanel
*panel
= (FontPanel
*) data
;
691 /* Parameter not used, but tell the compiler that it is ok */
694 osize
= WMGetTextFieldText(panel
->sizT
);
696 item
= WMGetListSelectedItem(panel
->typLs
);
697 face
= (Typeface
*) item
->clientData
;
699 WMClearList(panel
->sizLs
);
701 WM_ITERATE_ARRAY(face
->sizes
, size
, i
) {
703 int size_int
= (intptr_t) size
;
705 sprintf(buffer
, "%i", size_int
);
707 WMAddListItem(panel
->sizLs
, buffer
);
712 sizei
= WMFindRowOfListItemWithTitle(panel
->sizLs
, osize
);
715 sizei
= WMFindRowOfListItemWithTitle(panel
->sizLs
, "12");
720 WMSelectListItem(panel
->sizLs
, sizei
);
721 WMSetListPosition(panel
->sizLs
, sizei
);
722 sizeClick(panel
->sizLs
, panel
);
730 static void sizeClick(WMWidget
* w
, void *data
)
732 FontPanel
*panel
= (FontPanel
*) data
;
735 /* Parameter not used, but tell the compiler that it is ok */
738 item
= WMGetListSelectedItem(panel
->sizLs
);
739 WMSetTextFieldText(panel
->sizT
, item
->text
);
741 WMSelectTextFieldRange(panel
->sizT
, wmkrange(0, strlen(item
->text
)));
746 static void setFontPanelFontName(FontPanel
* panel
, const char *family
, const char *style
, double size
)
758 famrow
= WMFindRowOfListItemWithTitle(panel
->famLs
, family
);
763 WMSelectListItem(panel
->famLs
, famrow
);
764 WMSetListPosition(panel
->famLs
, famrow
);
766 WMClearList(panel
->typLs
);
768 item
= WMGetListSelectedItem(panel
->famLs
);
770 fam
= (Family
*) item
->clientData
;
771 WM_ITERATE_ARRAY(fam
->typefaces
, face
, i
) {
776 wstrlcpy(buffer
, face
->typeface
, sizeof(buffer
));
777 if (strcasecmp(face
->typeface
, "Roman") == 0)
780 fitem
= WMInsertListItem(panel
->typLs
, 0, buffer
);
782 fitem
= WMAddListItem(panel
->typLs
, buffer
);
783 fitem
->clientData
= face
;
786 stlrow
= WMFindRowOfListItemWithTitle(panel
->typLs
, style
);
793 WMSelectListItem(panel
->typLs
, stlrow
);
795 item
= WMGetListSelectedItem(panel
->typLs
);
797 face
= (Typeface
*) item
->clientData
;
799 WMClearList(panel
->sizLs
);
801 WM_ITERATE_ARRAY(face
->sizes
, vsize
, i
) {
805 int size_int
= (intptr_t) vsize
;
807 sprintf(buffer
, "%i", size_int
);
809 WMAddListItem(panel
->sizLs
, buffer
);
813 snprintf(asize
, sizeof(asize
) - 1, "%d", (int)(size
+ 0.5));
815 sz
= WMFindRowOfListItemWithTitle(panel
->sizLs
, asize
);
821 WMSelectListItem(panel
->sizLs
, sz
);
822 sizeClick(panel
->sizLs
, panel
);