1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-19 12:28:22 +01:00
Files
wmaker/WINGs/wfontpanel.c
Tobias Stoeckmann cc30444dda No need to call memset after wmalloc
memset is the last function call in wmalloc, just before it returns the
newly allocated memory.  Therefore it is not needed to call it again
after wmalloc call.  Although I would prefer to switch wmalloc to a
calloc-based wcalloc function, the compatibility of WINGs for old apps
should be kept.
2012-05-04 18:41:01 -03:00

804 lines
19 KiB
C

#include "WINGsP.h"
#include "WUtil.h"
#include "wconfig.h"
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <stdint.h>
#include <X11/Xft/Xft.h>
#include <fontconfig/fontconfig.h>
/* XXX TODO */
char *WMFontPanelFontChangedNotification = "WMFontPanelFontChangedNotification";
typedef struct W_FontPanel {
WMWindow *win;
WMFrame *upperF;
WMTextField *sampleT;
WMSplitView *split;
WMFrame *lowerF;
WMLabel *famL;
WMList *famLs;
WMLabel *typL;
WMList *typLs;
WMLabel *sizL;
WMTextField *sizT;
WMList *sizLs;
WMAction2 *action;
void *data;
WMButton *revertB;
WMButton *setB;
WMPropList *fdb;
} FontPanel;
#define MIN_UPPER_HEIGHT 20
#define MIN_LOWER_HEIGHT 140
#define MAX_FONTS_TO_RETRIEVE 2000
#define BUTTON_SPACE_HEIGHT 40
#define MIN_WIDTH 250
#define MIN_HEIGHT (MIN_UPPER_HEIGHT+MIN_LOWER_HEIGHT+BUTTON_SPACE_HEIGHT)
#define DEF_UPPER_HEIGHT 60
#define DEF_LOWER_HEIGHT 310
#define DEF_WIDTH 320
#define DEF_HEIGHT (DEF_UPPER_HEIGHT+DEF_LOWER_HEIGHT)
static int scalableFontSizes[] = {
8,
10,
11,
12,
14,
16,
18,
20,
24,
36,
48,
64
};
static void setFontPanelFontName(FontPanel * panel, char *family, char *style, double size);
static int isXLFD(char *font, int *length_ret);
static void arrangeLowerFrame(FontPanel * panel);
static void familyClick(WMWidget *, void *);
static void typefaceClick(WMWidget *, void *);
static void sizeClick(WMWidget *, void *);
static void listFamilies(WMScreen * scr, WMFontPanel * panel);
static void splitViewConstrainCallback(WMSplitView * sPtr, int indView, int *min, int *max)
{
if (indView == 0)
*min = MIN_UPPER_HEIGHT;
else
*min = MIN_LOWER_HEIGHT;
}
static void notificationObserver(void *self, WMNotification * notif)
{
WMFontPanel *panel = (WMFontPanel *) self;
void *object = WMGetNotificationObject(notif);
if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification) {
if (object == WMWidgetView(panel->win)) {
int h = WMWidgetHeight(panel->win);
int w = WMWidgetWidth(panel->win);
WMResizeWidget(panel->split, w, h - BUTTON_SPACE_HEIGHT);
WMMoveWidget(panel->setB, w - 80, h - (BUTTON_SPACE_HEIGHT - 5));
WMMoveWidget(panel->revertB, w - 240, h - (BUTTON_SPACE_HEIGHT - 5));
} else if (object == WMWidgetView(panel->upperF)) {
if (WMWidgetHeight(panel->upperF) < MIN_UPPER_HEIGHT) {
WMResizeWidget(panel->upperF, WMWidgetWidth(panel->upperF), MIN_UPPER_HEIGHT);
} else {
WMResizeWidget(panel->sampleT, WMWidgetWidth(panel->upperF) - 20,
WMWidgetHeight(panel->upperF) - 10);
}
} else if (object == WMWidgetView(panel->lowerF)) {
if (WMWidgetHeight(panel->lowerF) < MIN_LOWER_HEIGHT) {
WMResizeWidget(panel->upperF, WMWidgetWidth(panel->upperF), MIN_UPPER_HEIGHT);
WMMoveWidget(panel->lowerF, 0, WMWidgetHeight(panel->upperF)
+ WMGetSplitViewDividerThickness(panel->split));
WMResizeWidget(panel->lowerF, WMWidgetWidth(panel->lowerF),
WMWidgetWidth(panel->split) - MIN_UPPER_HEIGHT
- WMGetSplitViewDividerThickness(panel->split));
} else {
arrangeLowerFrame(panel);
}
}
}
}
static void closeWindow(WMWidget * w, void *data)
{
FontPanel *panel = (FontPanel *) data;
WMHideFontPanel(panel);
}
static void setClickedAction(WMWidget * w, void *data)
{
FontPanel *panel = (FontPanel *) data;
if (panel->action)
(*panel->action) (panel, panel->data);
}
static void revertClickedAction(WMWidget * w, void *data)
{
/*FontPanel *panel = (FontPanel*)data; */
/* XXX TODO */
}
WMFontPanel *WMGetFontPanel(WMScreen * scr)
{
FontPanel *panel;
WMColor *dark, *white;
WMFont *font;
int divThickness;
if (scr->sharedFontPanel)
return scr->sharedFontPanel;
panel = wmalloc(sizeof(FontPanel));
panel->win = WMCreateWindow(scr, "fontPanel");
/* WMSetWidgetBackgroundColor(panel->win, WMWhiteColor(scr)); */
WMSetWindowTitle(panel->win, _("Font Panel"));
WMResizeWidget(panel->win, DEF_WIDTH, DEF_HEIGHT);
WMSetWindowMinSize(panel->win, MIN_WIDTH, MIN_HEIGHT);
WMSetViewNotifySizeChanges(WMWidgetView(panel->win), True);
WMSetWindowCloseAction(panel->win, closeWindow, panel);
panel->split = WMCreateSplitView(panel->win);
WMResizeWidget(panel->split, DEF_WIDTH, DEF_HEIGHT - BUTTON_SPACE_HEIGHT);
WMSetSplitViewConstrainProc(panel->split, splitViewConstrainCallback);
divThickness = WMGetSplitViewDividerThickness(panel->split);
panel->upperF = WMCreateFrame(panel->win);
WMSetFrameRelief(panel->upperF, WRFlat);
WMSetViewNotifySizeChanges(WMWidgetView(panel->upperF), True);
panel->lowerF = WMCreateFrame(panel->win);
/* WMSetWidgetBackgroundColor(panel->lowerF, WMBlackColor(scr)); */
WMSetFrameRelief(panel->lowerF, WRFlat);
WMSetViewNotifySizeChanges(WMWidgetView(panel->lowerF), True);
WMAddSplitViewSubview(panel->split, W_VIEW(panel->upperF));
WMAddSplitViewSubview(panel->split, W_VIEW(panel->lowerF));
WMResizeWidget(panel->upperF, DEF_WIDTH, DEF_UPPER_HEIGHT);
WMResizeWidget(panel->lowerF, DEF_WIDTH, DEF_LOWER_HEIGHT);
WMMoveWidget(panel->lowerF, 0, 60 + divThickness);
white = WMWhiteColor(scr);
dark = WMDarkGrayColor(scr);
panel->sampleT = WMCreateTextField(panel->upperF);
WMResizeWidget(panel->sampleT, DEF_WIDTH - 20, 50);
WMMoveWidget(panel->sampleT, 10, 10);
WMSetTextFieldText(panel->sampleT, _("The quick brown fox jumps over the lazy dog"));
font = WMBoldSystemFontOfSize(scr, 12);
panel->famL = WMCreateLabel(panel->lowerF);
WMSetWidgetBackgroundColor(panel->famL, dark);
WMSetLabelText(panel->famL, _("Family"));
WMSetLabelFont(panel->famL, font);
WMSetLabelTextColor(panel->famL, white);
WMSetLabelRelief(panel->famL, WRSunken);
WMSetLabelTextAlignment(panel->famL, WACenter);
panel->famLs = WMCreateList(panel->lowerF);
WMSetListAction(panel->famLs, familyClick, panel);
panel->typL = WMCreateLabel(panel->lowerF);
WMSetWidgetBackgroundColor(panel->typL, dark);
WMSetLabelText(panel->typL, _("Typeface"));
WMSetLabelFont(panel->typL, font);
WMSetLabelTextColor(panel->typL, white);
WMSetLabelRelief(panel->typL, WRSunken);
WMSetLabelTextAlignment(panel->typL, WACenter);
panel->typLs = WMCreateList(panel->lowerF);
WMSetListAction(panel->typLs, typefaceClick, panel);
panel->sizL = WMCreateLabel(panel->lowerF);
WMSetWidgetBackgroundColor(panel->sizL, dark);
WMSetLabelText(panel->sizL, _("Size"));
WMSetLabelFont(panel->sizL, font);
WMSetLabelTextColor(panel->sizL, white);
WMSetLabelRelief(panel->sizL, WRSunken);
WMSetLabelTextAlignment(panel->sizL, WACenter);
panel->sizT = WMCreateTextField(panel->lowerF);
/* WMSetTextFieldAlignment(panel->sizT, WARight); */
panel->sizLs = WMCreateList(panel->lowerF);
WMSetListAction(panel->sizLs, sizeClick, panel);
WMReleaseFont(font);
WMReleaseColor(white);
WMReleaseColor(dark);
panel->setB = WMCreateCommandButton(panel->win);
WMResizeWidget(panel->setB, 70, 24);
WMMoveWidget(panel->setB, 240, DEF_HEIGHT - (BUTTON_SPACE_HEIGHT - 5));
WMSetButtonText(panel->setB, _("Set"));
WMSetButtonAction(panel->setB, setClickedAction, panel);
panel->revertB = WMCreateCommandButton(panel->win);
WMResizeWidget(panel->revertB, 70, 24);
WMMoveWidget(panel->revertB, 80, DEF_HEIGHT - (BUTTON_SPACE_HEIGHT - 5));
WMSetButtonText(panel->revertB, _("Revert"));
WMSetButtonAction(panel->revertB, revertClickedAction, panel);
WMRealizeWidget(panel->win);
WMMapSubwidgets(panel->upperF);
WMMapSubwidgets(panel->lowerF);
WMMapSubwidgets(panel->split);
WMMapSubwidgets(panel->win);
WMUnmapWidget(panel->revertB);
arrangeLowerFrame(panel);
scr->sharedFontPanel = panel;
/* register notification observers */
WMAddNotificationObserver(notificationObserver, panel,
WMViewSizeDidChangeNotification, WMWidgetView(panel->win));
WMAddNotificationObserver(notificationObserver, panel,
WMViewSizeDidChangeNotification, WMWidgetView(panel->upperF));
WMAddNotificationObserver(notificationObserver, panel,
WMViewSizeDidChangeNotification, WMWidgetView(panel->lowerF));
listFamilies(scr, panel);
return panel;
}
void WMFreeFontPanel(WMFontPanel * panel)
{
if (panel == WMWidgetScreen(panel->win)->sharedFontPanel) {
WMWidgetScreen(panel->win)->sharedFontPanel = NULL;
}
WMRemoveNotificationObserver(panel);
WMUnmapWidget(panel->win);
WMDestroyWidget(panel->win);
wfree(panel);
}
void WMShowFontPanel(WMFontPanel * panel)
{
WMMapWidget(panel->win);
}
void WMHideFontPanel(WMFontPanel * panel)
{
WMUnmapWidget(panel->win);
}
WMFont *WMGetFontPanelFont(WMFontPanel * panel)
{
return WMGetTextFieldFont(panel->sampleT);
}
void WMSetFontPanelFont(WMFontPanel * panel, char *fontName)
{
int fname_len;
FcPattern *pattern;
FcChar8 *family, *style;
double size;
if (!isXLFD(fontName, &fname_len)) {
/* maybe its proper fontconfig and we can parse it */
pattern = FcNameParse((FcChar8 *) fontName);
} else {
/* maybe its proper xlfd and we can convert it to an FcPattern */
pattern = XftXlfdParse(fontName, False, False);
/*//FcPatternPrint(pattern); */
}
if (!pattern)
return;
if (FcPatternGetString(pattern, FC_FAMILY, 0, &family) == FcResultMatch)
if (FcPatternGetString(pattern, FC_STYLE, 0, &style) == FcResultMatch)
if (FcPatternGetDouble(pattern, "pixelsize", 0, &size) == FcResultMatch)
setFontPanelFontName(panel, (char *)family, (char *)style, size);
FcPatternDestroy(pattern);
}
void WMSetFontPanelAction(WMFontPanel * panel, WMAction2 * action, void *data)
{
panel->action = action;
panel->data = data;
}
static void arrangeLowerFrame(FontPanel * panel)
{
int width = WMWidgetWidth(panel->lowerF) - 55 - 30;
int height = WMWidgetHeight(panel->split) - WMWidgetHeight(panel->upperF);
int fw, tw, sw;
#define LABEL_HEIGHT 20
height -= WMGetSplitViewDividerThickness(panel->split);
height -= LABEL_HEIGHT + 8;
fw = (125 * width) / 235;
tw = (110 * width) / 235;
sw = 55;
WMMoveWidget(panel->famL, 10, 0);
WMResizeWidget(panel->famL, fw, LABEL_HEIGHT);
WMMoveWidget(panel->famLs, 10, 23);
WMResizeWidget(panel->famLs, fw, height);
WMMoveWidget(panel->typL, 10 + fw + 3, 0);
WMResizeWidget(panel->typL, tw, LABEL_HEIGHT);
WMMoveWidget(panel->typLs, 10 + fw + 3, 23);
WMResizeWidget(panel->typLs, tw, height);
WMMoveWidget(panel->sizL, 10 + fw + 3 + tw + 3, 0);
WMResizeWidget(panel->sizL, sw + 4, LABEL_HEIGHT);
WMMoveWidget(panel->sizT, 10 + fw + 3 + tw + 3, 23);
WMResizeWidget(panel->sizT, sw + 4, 20);
WMMoveWidget(panel->sizLs, 10 + fw + 3 + tw + 3, 46);
WMResizeWidget(panel->sizLs, sw + 4, height - 23);
}
#define NUM_FIELDS 14
static int isXLFD(char *font, int *length_ret)
{
int c = 0;
*length_ret = 0;
while (*font) {
(*length_ret)++;
if (*font++ == '-')
c++;
}
return c == NUM_FIELDS;
}
typedef struct {
char *typeface;
WMArray *sizes;
} Typeface;
typedef struct {
char *name; /* gotta love simplicity */
WMArray *typefaces;
} Family;
static int compare_int(const void *a, const void *b)
{
int i1 = *(int *)a;
int i2 = *(int *)b;
if (i1 < i2)
return -1;
else if (i1 > i2)
return 1;
else
return 0;
}
static void addSizeToTypeface(Typeface * face, int size)
{
if (size == 0) {
int j;
for (j = 0; j < sizeof(scalableFontSizes) / sizeof(int); j++) {
size = scalableFontSizes[j];
if (!WMCountInArray(face->sizes, (void *)(uintptr_t) size)) {
WMAddToArray(face->sizes, (void *)(uintptr_t) size);
}
}
WMSortArray(face->sizes, compare_int);
} else {
if (!WMCountInArray(face->sizes, (void *)(uintptr_t) size)) {
WMAddToArray(face->sizes, (void *)(uintptr_t) size);
WMSortArray(face->sizes, compare_int);
}
}
}
static void addTypefaceToXftFamily(Family * fam, char *style)
{
Typeface *face;
WMArrayIterator i;
if (fam->typefaces) {
WM_ITERATE_ARRAY(fam->typefaces, face, i) {
if (strcmp(face->typeface, style) != 0)
continue; /* go to next interation */
addSizeToTypeface(face, 0);
return;
}
} else {
fam->typefaces = WMCreateArray(4);
}
face = wmalloc(sizeof(Typeface));
face->typeface = wstrdup(style);
face->sizes = WMCreateArray(4);
addSizeToTypeface(face, 0);
WMAddToArray(fam->typefaces, face);
}
/*
* families (same family name) (Hashtable of family -> array)
* registries (same family but different registries)
*
*/
static void addFontToXftFamily(WMHashTable * families, char *name, char *style)
{
WMArrayIterator i;
WMArray *array;
Family *fam;
array = WMHashGet(families, name);
if (array) {
WM_ITERATE_ARRAY(array, fam, i) {
if (strcmp(fam->name, name) == 0)
addTypefaceToXftFamily(fam, style);
return;
}
}
array = WMCreateArray(8);
fam = wmalloc(sizeof(Family));
fam->name = wstrdup(name);
addTypefaceToXftFamily(fam, style);
WMAddToArray(array, fam);
WMHashInsert(families, fam->name, array);
}
static void listFamilies(WMScreen * scr, WMFontPanel * panel)
{
FcObjectSet *os = 0;
FcFontSet *fs;
FcPattern *pat;
WMHashTable *families;
WMHashEnumerator enumer;
WMArray *array;
int i;
pat = FcPatternCreate();
os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, NULL);
fs = FcFontList(0, pat, os);
if (!fs) {
WMRunAlertPanel(scr, panel->win, _("Error"),
_("Could not init font config library\n"), _("OK"), NULL, NULL);
return;
}
if (pat)
FcPatternDestroy(pat);
families = WMCreateHashTable(WMStringPointerHashCallbacks);
if (fs) {
for (i = 0; i < fs->nfont; i++) {
FcChar8 *family;
FcChar8 *style;
if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch)
if (FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch)
addFontToXftFamily(families, (char *)family, (char *)style);
}
FcFontSetDestroy(fs);
}
enumer = WMEnumerateHashTable(families);
while ((array = WMNextHashEnumeratorItem(&enumer))) {
WMArrayIterator i;
Family *fam;
char buffer[256];
WMListItem *item;
WM_ITERATE_ARRAY(array, fam, i) {
wstrlcpy(buffer, fam->name, sizeof(buffer));
item = WMAddListItem(panel->famLs, buffer);
item->clientData = fam;
}
WMFreeArray(array);
}
WMSortListItems(panel->famLs);
WMFreeHashTable(families);
}
static void getSelectedFont(FontPanel * panel, char buffer[], int bufsize)
{
WMListItem *item;
Family *family;
Typeface *face;
char *size;
item = WMGetListSelectedItem(panel->famLs);
if (!item)
return;
family = (Family *) item->clientData;
item = WMGetListSelectedItem(panel->typLs);
if (!item)
return;
face = (Typeface *) item->clientData;
size = WMGetTextFieldText(panel->sizT);
snprintf(buffer, bufsize, "%s:style=%s:pixelsize=%s", family->name, face->typeface, size);
wfree(size);
}
static void preview(FontPanel * panel)
{
char buffer[512];
WMFont *font;
getSelectedFont(panel, buffer, sizeof(buffer));
font = WMCreateFont(WMWidgetScreen(panel->win), buffer);
if (font) {
WMSetTextFieldFont(panel->sampleT, font);
WMReleaseFont(font);
}
}
static void familyClick(WMWidget * w, void *data)
{
WMList *lPtr = (WMList *) w;
WMListItem *item;
Family *family;
Typeface *face;
FontPanel *panel = (FontPanel *) data;
WMArrayIterator i;
/* current typeface and size */
char *oface = NULL;
char *osize = NULL;
int facei = -1;
int sizei = -1;
/* must try to keep the same typeface and size for the new family */
item = WMGetListSelectedItem(panel->typLs);
if (item)
oface = wstrdup(item->text);
osize = WMGetTextFieldText(panel->sizT);
item = WMGetListSelectedItem(lPtr);
family = (Family *) item->clientData;
WMClearList(panel->typLs);
WM_ITERATE_ARRAY(family->typefaces, face, i) {
char buffer[256];
int top = 0;
WMListItem *fitem;
wstrlcpy(buffer, face->typeface, sizeof(buffer));
if (strcasecmp(face->typeface, "Roman") == 0)
top = 1;
if (strcasecmp(face->typeface, "Regular") == 0)
top = 1;
if (top)
fitem = WMInsertListItem(panel->typLs, 0, buffer);
else
fitem = WMAddListItem(panel->typLs, buffer);
fitem->clientData = face;
}
if (oface) {
facei = WMFindRowOfListItemWithTitle(panel->typLs, oface);
wfree(oface);
}
if (facei < 0) {
facei = 0;
}
WMSelectListItem(panel->typLs, facei);
typefaceClick(panel->typLs, panel);
if (osize) {
sizei = WMFindRowOfListItemWithTitle(panel->sizLs, osize);
}
if (sizei >= 0) {
WMSelectListItem(panel->sizLs, sizei);
sizeClick(panel->sizLs, panel);
}
if (osize)
wfree(osize);
preview(panel);
}
static void typefaceClick(WMWidget * w, void *data)
{
FontPanel *panel = (FontPanel *) data;
WMListItem *item;
Typeface *face;
WMArrayIterator i;
char buffer[32];
char *osize = NULL;
int sizei = -1;
void *size;
osize = WMGetTextFieldText(panel->sizT);
item = WMGetListSelectedItem(panel->typLs);
face = (Typeface *) item->clientData;
WMClearList(panel->sizLs);
WM_ITERATE_ARRAY(face->sizes, size, i) {
if ((uintptr_t)size != 0) {
sprintf(buffer, "%li", (uintptr_t)size);
WMAddListItem(panel->sizLs, buffer);
}
}
if (osize) {
sizei = WMFindRowOfListItemWithTitle(panel->sizLs, osize);
}
if (sizei < 0) {
sizei = WMFindRowOfListItemWithTitle(panel->sizLs, "12");
}
if (sizei < 0) {
sizei = 0;
}
WMSelectListItem(panel->sizLs, sizei);
WMSetListPosition(panel->sizLs, sizei);
sizeClick(panel->sizLs, panel);
if (osize)
wfree(osize);
preview(panel);
}
static void sizeClick(WMWidget * w, void *data)
{
FontPanel *panel = (FontPanel *) data;
WMListItem *item;
item = WMGetListSelectedItem(panel->sizLs);
WMSetTextFieldText(panel->sizT, item->text);
WMSelectTextFieldRange(panel->sizT, wmkrange(0, strlen(item->text)));
preview(panel);
}
static void setFontPanelFontName(FontPanel * panel, char *family, char *style, double size)
{
int famrow;
int stlrow;
int sz;
char asize[64];
void *vsize;
WMListItem *item;
Family *fam;
Typeface *face;
WMArrayIterator i;
famrow = WMFindRowOfListItemWithTitle(panel->famLs, family);
if (famrow < 0) {
famrow = 0;
return;
}
WMSelectListItem(panel->famLs, famrow);
WMSetListPosition(panel->famLs, famrow);
WMClearList(panel->typLs);
item = WMGetListSelectedItem(panel->famLs);
fam = (Family *) item->clientData;
WM_ITERATE_ARRAY(fam->typefaces, face, i) {
char buffer[256];
int top = 0;
WMListItem *fitem;
wstrlcpy(buffer, face->typeface, sizeof(buffer));
if (strcasecmp(face->typeface, "Roman") == 0)
top = 1;
if (top)
fitem = WMInsertListItem(panel->typLs, 0, buffer);
else
fitem = WMAddListItem(panel->typLs, buffer);
fitem->clientData = face;
}
stlrow = WMFindRowOfListItemWithTitle(panel->typLs, style);
if (stlrow < 0) {
stlrow = 0;
return;
}
WMSelectListItem(panel->typLs, stlrow);
item = WMGetListSelectedItem(panel->typLs);
face = (Typeface *) item->clientData;
WMClearList(panel->sizLs);
WM_ITERATE_ARRAY(face->sizes, vsize, i) {
char buffer[32];
if ((uintptr_t)vsize != 0) {
sprintf(buffer, "%li", (uintptr_t)vsize);
WMAddListItem(panel->sizLs, buffer);
}
}
snprintf(asize, sizeof(asize) - 1, "%d", (int)(size + 0.5));
sz = WMFindRowOfListItemWithTitle(panel->sizLs, asize);
if (sz < 0) {
sz = 4;
return;
}
WMSelectListItem(panel->sizLs, sz);
sizeClick(panel->sizLs, panel);
return;
}