10 #include <X11/Xft/Xft.h>
11 #include <fontconfig/fontconfig.h>
14 char *WMFontPanelFontChangedNotification
= "WMFontPanelFontChangedNotification";
16 typedef struct W_FontPanel
{
42 #define MIN_UPPER_HEIGHT 20
43 #define MIN_LOWER_HEIGHT 140
45 #define MAX_FONTS_TO_RETRIEVE 2000
47 #define BUTTON_SPACE_HEIGHT 40
50 #define MIN_HEIGHT (MIN_UPPER_HEIGHT+MIN_LOWER_HEIGHT+BUTTON_SPACE_HEIGHT)
52 #define DEF_UPPER_HEIGHT 60
53 #define DEF_LOWER_HEIGHT 310
56 #define DEF_HEIGHT (DEF_UPPER_HEIGHT+DEF_LOWER_HEIGHT)
58 static int scalableFontSizes
[] = {
73 static void setFontPanelFontName(FontPanel
* panel
, char *family
, char *style
, double size
);
75 static int isXLFD(char *font
, int *length_ret
);
77 static void arrangeLowerFrame(FontPanel
* panel
);
79 static void familyClick(WMWidget
*, void *);
80 static void typefaceClick(WMWidget
*, void *);
81 static void sizeClick(WMWidget
*, void *);
83 static void listFamilies(WMScreen
* scr
, WMFontPanel
* panel
);
85 static void splitViewConstrainCallback(WMSplitView
* sPtr
, int indView
, int *min
, int *max
)
88 *min
= MIN_UPPER_HEIGHT
;
90 *min
= MIN_LOWER_HEIGHT
;
93 static void notificationObserver(void *self
, WMNotification
* notif
)
95 WMFontPanel
*panel
= (WMFontPanel
*) self
;
96 void *object
= WMGetNotificationObject(notif
);
98 if (WMGetNotificationName(notif
) == WMViewSizeDidChangeNotification
) {
99 if (object
== WMWidgetView(panel
->win
)) {
100 int h
= WMWidgetHeight(panel
->win
);
101 int w
= WMWidgetWidth(panel
->win
);
103 WMResizeWidget(panel
->split
, w
, h
- BUTTON_SPACE_HEIGHT
);
105 WMMoveWidget(panel
->setB
, w
- 80, h
- (BUTTON_SPACE_HEIGHT
- 5));
107 WMMoveWidget(panel
->revertB
, w
- 240, h
- (BUTTON_SPACE_HEIGHT
- 5));
109 } else if (object
== WMWidgetView(panel
->upperF
)) {
111 if (WMWidgetHeight(panel
->upperF
) < MIN_UPPER_HEIGHT
) {
112 WMResizeWidget(panel
->upperF
, WMWidgetWidth(panel
->upperF
), MIN_UPPER_HEIGHT
);
114 WMResizeWidget(panel
->sampleT
, WMWidgetWidth(panel
->upperF
) - 20,
115 WMWidgetHeight(panel
->upperF
) - 10);
118 } else if (object
== WMWidgetView(panel
->lowerF
)) {
120 if (WMWidgetHeight(panel
->lowerF
) < MIN_LOWER_HEIGHT
) {
121 WMResizeWidget(panel
->upperF
, WMWidgetWidth(panel
->upperF
), MIN_UPPER_HEIGHT
);
123 WMMoveWidget(panel
->lowerF
, 0, WMWidgetHeight(panel
->upperF
)
124 + WMGetSplitViewDividerThickness(panel
->split
));
126 WMResizeWidget(panel
->lowerF
, WMWidgetWidth(panel
->lowerF
),
127 WMWidgetWidth(panel
->split
) - MIN_UPPER_HEIGHT
128 - WMGetSplitViewDividerThickness(panel
->split
));
130 arrangeLowerFrame(panel
);
136 static void closeWindow(WMWidget
* w
, void *data
)
138 FontPanel
*panel
= (FontPanel
*) data
;
140 WMHideFontPanel(panel
);
143 static void setClickedAction(WMWidget
* w
, void *data
)
145 FontPanel
*panel
= (FontPanel
*) data
;
148 (*panel
->action
) (panel
, panel
->data
);
151 static void revertClickedAction(WMWidget
* w
, void *data
)
153 /*FontPanel *panel = (FontPanel*)data; */
157 WMFontPanel
*WMGetFontPanel(WMScreen
* scr
)
160 WMColor
*dark
, *white
;
164 if (scr
->sharedFontPanel
)
165 return scr
->sharedFontPanel
;
167 panel
= wmalloc(sizeof(FontPanel
));
168 memset(panel
, 0, sizeof(FontPanel
));
170 panel
->win
= WMCreateWindow(scr
, "fontPanel");
171 /* WMSetWidgetBackgroundColor(panel->win, WMWhiteColor(scr)); */
172 WMSetWindowTitle(panel
->win
, _("Font Panel"));
173 WMResizeWidget(panel
->win
, DEF_WIDTH
, DEF_HEIGHT
);
174 WMSetWindowMinSize(panel
->win
, MIN_WIDTH
, MIN_HEIGHT
);
175 WMSetViewNotifySizeChanges(WMWidgetView(panel
->win
), True
);
177 WMSetWindowCloseAction(panel
->win
, closeWindow
, panel
);
179 panel
->split
= WMCreateSplitView(panel
->win
);
180 WMResizeWidget(panel
->split
, DEF_WIDTH
, DEF_HEIGHT
- BUTTON_SPACE_HEIGHT
);
181 WMSetSplitViewConstrainProc(panel
->split
, splitViewConstrainCallback
);
183 divThickness
= WMGetSplitViewDividerThickness(panel
->split
);
185 panel
->upperF
= WMCreateFrame(panel
->win
);
186 WMSetFrameRelief(panel
->upperF
, WRFlat
);
187 WMSetViewNotifySizeChanges(WMWidgetView(panel
->upperF
), True
);
188 panel
->lowerF
= WMCreateFrame(panel
->win
);
189 /* WMSetWidgetBackgroundColor(panel->lowerF, WMBlackColor(scr)); */
190 WMSetFrameRelief(panel
->lowerF
, WRFlat
);
191 WMSetViewNotifySizeChanges(WMWidgetView(panel
->lowerF
), True
);
193 WMAddSplitViewSubview(panel
->split
, W_VIEW(panel
->upperF
));
194 WMAddSplitViewSubview(panel
->split
, W_VIEW(panel
->lowerF
));
196 WMResizeWidget(panel
->upperF
, DEF_WIDTH
, DEF_UPPER_HEIGHT
);
198 WMResizeWidget(panel
->lowerF
, DEF_WIDTH
, DEF_LOWER_HEIGHT
);
200 WMMoveWidget(panel
->lowerF
, 0, 60 + divThickness
);
202 white
= WMWhiteColor(scr
);
203 dark
= WMDarkGrayColor(scr
);
205 panel
->sampleT
= WMCreateTextField(panel
->upperF
);
206 WMResizeWidget(panel
->sampleT
, DEF_WIDTH
- 20, 50);
207 WMMoveWidget(panel
->sampleT
, 10, 10);
208 WMSetTextFieldText(panel
->sampleT
, _("The quick brown fox jumps over the lazy dog"));
210 font
= WMBoldSystemFontOfSize(scr
, 12);
212 panel
->famL
= WMCreateLabel(panel
->lowerF
);
213 WMSetWidgetBackgroundColor(panel
->famL
, dark
);
214 WMSetLabelText(panel
->famL
, _("Family"));
215 WMSetLabelFont(panel
->famL
, font
);
216 WMSetLabelTextColor(panel
->famL
, white
);
217 WMSetLabelRelief(panel
->famL
, WRSunken
);
218 WMSetLabelTextAlignment(panel
->famL
, WACenter
);
220 panel
->famLs
= WMCreateList(panel
->lowerF
);
221 WMSetListAction(panel
->famLs
, familyClick
, panel
);
223 panel
->typL
= WMCreateLabel(panel
->lowerF
);
224 WMSetWidgetBackgroundColor(panel
->typL
, dark
);
225 WMSetLabelText(panel
->typL
, _("Typeface"));
226 WMSetLabelFont(panel
->typL
, font
);
227 WMSetLabelTextColor(panel
->typL
, white
);
228 WMSetLabelRelief(panel
->typL
, WRSunken
);
229 WMSetLabelTextAlignment(panel
->typL
, WACenter
);
231 panel
->typLs
= WMCreateList(panel
->lowerF
);
232 WMSetListAction(panel
->typLs
, typefaceClick
, panel
);
234 panel
->sizL
= WMCreateLabel(panel
->lowerF
);
235 WMSetWidgetBackgroundColor(panel
->sizL
, dark
);
236 WMSetLabelText(panel
->sizL
, _("Size"));
237 WMSetLabelFont(panel
->sizL
, font
);
238 WMSetLabelTextColor(panel
->sizL
, white
);
239 WMSetLabelRelief(panel
->sizL
, WRSunken
);
240 WMSetLabelTextAlignment(panel
->sizL
, WACenter
);
242 panel
->sizT
= WMCreateTextField(panel
->lowerF
);
243 /* WMSetTextFieldAlignment(panel->sizT, WARight); */
245 panel
->sizLs
= WMCreateList(panel
->lowerF
);
246 WMSetListAction(panel
->sizLs
, sizeClick
, panel
);
249 WMReleaseColor(white
);
250 WMReleaseColor(dark
);
252 panel
->setB
= WMCreateCommandButton(panel
->win
);
253 WMResizeWidget(panel
->setB
, 70, 24);
254 WMMoveWidget(panel
->setB
, 240, DEF_HEIGHT
- (BUTTON_SPACE_HEIGHT
- 5));
255 WMSetButtonText(panel
->setB
, _("Set"));
256 WMSetButtonAction(panel
->setB
, setClickedAction
, panel
);
258 panel
->revertB
= WMCreateCommandButton(panel
->win
);
259 WMResizeWidget(panel
->revertB
, 70, 24);
260 WMMoveWidget(panel
->revertB
, 80, DEF_HEIGHT
- (BUTTON_SPACE_HEIGHT
- 5));
261 WMSetButtonText(panel
->revertB
, _("Revert"));
262 WMSetButtonAction(panel
->revertB
, revertClickedAction
, panel
);
264 WMRealizeWidget(panel
->win
);
266 WMMapSubwidgets(panel
->upperF
);
267 WMMapSubwidgets(panel
->lowerF
);
268 WMMapSubwidgets(panel
->split
);
269 WMMapSubwidgets(panel
->win
);
271 WMUnmapWidget(panel
->revertB
);
273 arrangeLowerFrame(panel
);
275 scr
->sharedFontPanel
= panel
;
277 /* register notification observers */
278 WMAddNotificationObserver(notificationObserver
, panel
,
279 WMViewSizeDidChangeNotification
, WMWidgetView(panel
->win
));
280 WMAddNotificationObserver(notificationObserver
, panel
,
281 WMViewSizeDidChangeNotification
, WMWidgetView(panel
->upperF
));
282 WMAddNotificationObserver(notificationObserver
, panel
,
283 WMViewSizeDidChangeNotification
, WMWidgetView(panel
->lowerF
));
285 listFamilies(scr
, panel
);
290 void WMFreeFontPanel(WMFontPanel
* panel
)
292 if (panel
== WMWidgetScreen(panel
->win
)->sharedFontPanel
) {
293 WMWidgetScreen(panel
->win
)->sharedFontPanel
= NULL
;
295 WMRemoveNotificationObserver(panel
);
296 WMUnmapWidget(panel
->win
);
297 WMDestroyWidget(panel
->win
);
301 void WMShowFontPanel(WMFontPanel
* panel
)
303 WMMapWidget(panel
->win
);
306 void WMHideFontPanel(WMFontPanel
* panel
)
308 WMUnmapWidget(panel
->win
);
311 WMFont
*WMGetFontPanelFont(WMFontPanel
* panel
)
313 return WMGetTextFieldFont(panel
->sampleT
);
316 void WMSetFontPanelFont(WMFontPanel
* panel
, char *fontName
)
320 FcChar8
*family
, *style
;
323 if (!isXLFD(fontName
, &fname_len
)) {
324 /* maybe its proper fontconfig and we can parse it */
325 pattern
= FcNameParse((FcChar8
*) fontName
);
327 /* maybe its proper xlfd and we can convert it to an FcPattern */
328 pattern
= XftXlfdParse(fontName
, False
, False
);
329 /*//FcPatternPrint(pattern); */
335 if (FcPatternGetString(pattern
, FC_FAMILY
, 0, &family
) == FcResultMatch
)
336 if (FcPatternGetString(pattern
, FC_STYLE
, 0, &style
) == FcResultMatch
)
337 if (FcPatternGetDouble(pattern
, "pixelsize", 0, &size
) == FcResultMatch
)
338 setFontPanelFontName(panel
, (char *)family
, (char *)style
, size
);
340 FcPatternDestroy(pattern
);
343 void WMSetFontPanelAction(WMFontPanel
* panel
, WMAction2
* action
, void *data
)
345 panel
->action
= action
;
349 static void arrangeLowerFrame(FontPanel
* panel
)
351 int width
= WMWidgetWidth(panel
->lowerF
) - 55 - 30;
352 int height
= WMWidgetHeight(panel
->split
) - WMWidgetHeight(panel
->upperF
);
355 #define LABEL_HEIGHT 20
357 height
-= WMGetSplitViewDividerThickness(panel
->split
);
359 height
-= LABEL_HEIGHT
+ 8;
361 fw
= (125 * width
) / 235;
362 tw
= (110 * width
) / 235;
365 WMMoveWidget(panel
->famL
, 10, 0);
366 WMResizeWidget(panel
->famL
, fw
, LABEL_HEIGHT
);
368 WMMoveWidget(panel
->famLs
, 10, 23);
369 WMResizeWidget(panel
->famLs
, fw
, height
);
371 WMMoveWidget(panel
->typL
, 10 + fw
+ 3, 0);
372 WMResizeWidget(panel
->typL
, tw
, LABEL_HEIGHT
);
374 WMMoveWidget(panel
->typLs
, 10 + fw
+ 3, 23);
375 WMResizeWidget(panel
->typLs
, tw
, height
);
377 WMMoveWidget(panel
->sizL
, 10 + fw
+ 3 + tw
+ 3, 0);
378 WMResizeWidget(panel
->sizL
, sw
+ 4, LABEL_HEIGHT
);
380 WMMoveWidget(panel
->sizT
, 10 + fw
+ 3 + tw
+ 3, 23);
381 WMResizeWidget(panel
->sizT
, sw
+ 4, 20);
383 WMMoveWidget(panel
->sizLs
, 10 + fw
+ 3 + tw
+ 3, 46);
384 WMResizeWidget(panel
->sizLs
, sw
+ 4, height
- 23);
387 #define NUM_FIELDS 14
389 static int isXLFD(char *font
, int *length_ret
)
400 return c
== NUM_FIELDS
;
409 char *name
; /* gotta love simplicity */
413 static int compare_int(const void *a
, const void *b
)
426 static void addSizeToTypeface(Typeface
* face
, int size
)
431 for (j
= 0; j
< sizeof(scalableFontSizes
) / sizeof(int); j
++) {
432 size
= scalableFontSizes
[j
];
434 if (!WMCountInArray(face
->sizes
, (void *)(uintptr_t) size
)) {
435 WMAddToArray(face
->sizes
, (void *)(uintptr_t) size
);
438 WMSortArray(face
->sizes
, compare_int
);
440 if (!WMCountInArray(face
->sizes
, (void *)(uintptr_t) size
)) {
441 WMAddToArray(face
->sizes
, (void *)(uintptr_t) size
);
442 WMSortArray(face
->sizes
, compare_int
);
447 static void addTypefaceToXftFamily(Family
* fam
, char *style
)
452 if (fam
->typefaces
) {
453 WM_ITERATE_ARRAY(fam
->typefaces
, face
, i
) {
454 if (strcmp(face
->typeface
, style
) != 0)
455 continue; /* go to next interation */
456 addSizeToTypeface(face
, 0);
460 fam
->typefaces
= WMCreateArray(4);
463 face
= wmalloc(sizeof(Typeface
));
464 memset(face
, 0, sizeof(Typeface
));
466 face
->typeface
= wstrdup(style
);
467 face
->sizes
= WMCreateArray(4);
468 addSizeToTypeface(face
, 0);
470 WMAddToArray(fam
->typefaces
, face
);
474 * families (same family name) (Hashtable of family -> array)
475 * registries (same family but different registries)
478 static void addFontToXftFamily(WMHashTable
* families
, char *name
, char *style
)
484 array
= WMHashGet(families
, name
);
486 WM_ITERATE_ARRAY(array
, fam
, i
) {
487 if (strcmp(fam
->name
, name
) == 0)
488 addTypefaceToXftFamily(fam
, style
);
493 array
= WMCreateArray(8);
495 fam
= wmalloc(sizeof(Family
));
496 memset(fam
, 0, sizeof(Family
));
498 fam
->name
= wstrdup(name
);
500 addTypefaceToXftFamily(fam
, style
);
502 WMAddToArray(array
, fam
);
504 WMHashInsert(families
, fam
->name
, array
);
507 static void listFamilies(WMScreen
* scr
, WMFontPanel
* panel
)
512 WMHashTable
*families
;
513 WMHashEnumerator enumer
;
517 pat
= FcPatternCreate();
518 os
= FcObjectSetBuild(FC_FAMILY
, FC_STYLE
, NULL
);
519 fs
= FcFontList(0, pat
, os
);
521 WMRunAlertPanel(scr
, panel
->win
, _("Error"),
522 _("Could not init font config library\n"), _("OK"), NULL
, NULL
);
526 FcPatternDestroy(pat
);
528 families
= WMCreateHashTable(WMStringPointerHashCallbacks
);
531 for (i
= 0; i
< fs
->nfont
; i
++) {
535 if (FcPatternGetString(fs
->fonts
[i
], FC_FAMILY
, 0, &family
) == FcResultMatch
)
536 if (FcPatternGetString(fs
->fonts
[i
], FC_STYLE
, 0, &style
) == FcResultMatch
)
537 addFontToXftFamily(families
, (char *)family
, (char *)style
);
539 FcFontSetDestroy(fs
);
542 enumer
= WMEnumerateHashTable(families
);
544 while ((array
= WMNextHashEnumeratorItem(&enumer
))) {
550 WM_ITERATE_ARRAY(array
, fam
, i
) {
551 strcpy(buffer
, fam
->name
);
552 item
= WMAddListItem(panel
->famLs
, buffer
);
554 item
->clientData
= fam
;
560 WMSortListItems(panel
->famLs
);
562 WMFreeHashTable(families
);
565 static void getSelectedFont(FontPanel
* panel
, char buffer
[], int bufsize
)
572 item
= WMGetListSelectedItem(panel
->famLs
);
575 family
= (Family
*) item
->clientData
;
577 item
= WMGetListSelectedItem(panel
->typLs
);
580 face
= (Typeface
*) item
->clientData
;
582 size
= WMGetTextFieldText(panel
->sizT
);
584 snprintf(buffer
, bufsize
, "%s:style=%s:pixelsize=%s", family
->name
, face
->typeface
, size
);
589 static void preview(FontPanel
* panel
)
594 getSelectedFont(panel
, buffer
, sizeof(buffer
));
595 font
= WMCreateFont(WMWidgetScreen(panel
->win
), buffer
);
597 WMSetTextFieldFont(panel
->sampleT
, font
);
602 static void familyClick(WMWidget
* w
, void *data
)
604 WMList
*lPtr
= (WMList
*) w
;
608 FontPanel
*panel
= (FontPanel
*) data
;
610 /* current typeface and size */
616 /* must try to keep the same typeface and size for the new family */
617 item
= WMGetListSelectedItem(panel
->typLs
);
619 oface
= wstrdup(item
->text
);
621 osize
= WMGetTextFieldText(panel
->sizT
);
623 item
= WMGetListSelectedItem(lPtr
);
624 family
= (Family
*) item
->clientData
;
626 WMClearList(panel
->typLs
);
628 WM_ITERATE_ARRAY(family
->typefaces
, face
, i
) {
633 strcpy(buffer
, face
->typeface
);
634 if (strcasecmp(face
->typeface
, "Roman") == 0)
636 if (strcasecmp(face
->typeface
, "Regular") == 0)
639 fitem
= WMInsertListItem(panel
->typLs
, 0, buffer
);
641 fitem
= WMAddListItem(panel
->typLs
, buffer
);
642 fitem
->clientData
= face
;
646 facei
= WMFindRowOfListItemWithTitle(panel
->typLs
, oface
);
652 WMSelectListItem(panel
->typLs
, facei
);
653 typefaceClick(panel
->typLs
, panel
);
656 sizei
= WMFindRowOfListItemWithTitle(panel
->sizLs
, osize
);
659 WMSelectListItem(panel
->sizLs
, sizei
);
660 sizeClick(panel
->sizLs
, panel
);
669 static void typefaceClick(WMWidget
* w
, void *data
)
671 FontPanel
*panel
= (FontPanel
*) data
;
681 osize
= WMGetTextFieldText(panel
->sizT
);
683 item
= WMGetListSelectedItem(panel
->typLs
);
684 face
= (Typeface
*) item
->clientData
;
686 WMClearList(panel
->sizLs
);
688 WM_ITERATE_ARRAY(face
->sizes
, size
, i
) {
689 if ((int)(uintptr_t) size
!= 0) {
690 sprintf(buffer
, "%i", (int)(uintptr_t) size
);
692 WMAddListItem(panel
->sizLs
, buffer
);
697 sizei
= WMFindRowOfListItemWithTitle(panel
->sizLs
, osize
);
700 sizei
= WMFindRowOfListItemWithTitle(panel
->sizLs
, "12");
705 WMSelectListItem(panel
->sizLs
, sizei
);
706 WMSetListPosition(panel
->sizLs
, sizei
);
707 sizeClick(panel
->sizLs
, panel
);
715 static void sizeClick(WMWidget
* w
, void *data
)
717 FontPanel
*panel
= (FontPanel
*) data
;
720 item
= WMGetListSelectedItem(panel
->sizLs
);
721 WMSetTextFieldText(panel
->sizT
, item
->text
);
723 WMSelectTextFieldRange(panel
->sizT
, wmkrange(0, strlen(item
->text
)));
728 static void setFontPanelFontName(FontPanel
* panel
, char *family
, char *style
, double size
)
740 famrow
= WMFindRowOfListItemWithTitle(panel
->famLs
, family
);
745 WMSelectListItem(panel
->famLs
, famrow
);
746 WMSetListPosition(panel
->famLs
, famrow
);
748 WMClearList(panel
->typLs
);
750 item
= WMGetListSelectedItem(panel
->famLs
);
752 fam
= (Family
*) item
->clientData
;
753 WM_ITERATE_ARRAY(fam
->typefaces
, face
, i
) {
758 strcpy(buffer
, face
->typeface
);
759 if (strcasecmp(face
->typeface
, "Roman") == 0)
762 fitem
= WMInsertListItem(panel
->typLs
, 0, buffer
);
764 fitem
= WMAddListItem(panel
->typLs
, buffer
);
765 fitem
->clientData
= face
;
768 stlrow
= WMFindRowOfListItemWithTitle(panel
->typLs
, style
);
775 WMSelectListItem(panel
->typLs
, stlrow
);
777 item
= WMGetListSelectedItem(panel
->typLs
);
779 face
= (Typeface
*) item
->clientData
;
781 WMClearList(panel
->sizLs
);
783 WM_ITERATE_ARRAY(face
->sizes
, vsize
, i
) {
785 if ((int)(uintptr_t) vsize
!= 0) {
786 sprintf(buffer
, "%i", (int)(uintptr_t) vsize
);
788 WMAddListItem(panel
->sizLs
, buffer
);
792 snprintf(asize
, sizeof(asize
) - 1, "%d", (int)(size
+ 0.5));
794 sz
= WMFindRowOfListItemWithTitle(panel
->sizLs
, asize
);
801 WMSelectListItem(panel
->sizLs
, sz
);
802 sizeClick(panel
->sizLs
, panel
);