653 lines
16 KiB
C
653 lines
16 KiB
C
/*
|
|
* Copyright (C) 2000-2005 Carsten Haitzler, Geoff Harrison and various contributors
|
|
* Copyright (C) 2004-2005 Kim Woelders
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies of the Software, its documentation and marketing & publicity
|
|
* materials, and acknowledgment shall be given in the documentation, materials
|
|
* and software packages that this Software was used.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
#include "E.h"
|
|
#include "conf.h"
|
|
|
|
static char *
|
|
TextstateFontLookup(const char *name)
|
|
{
|
|
const char *font;
|
|
|
|
if (*name == '*')
|
|
{
|
|
font = FontLookup(name + 1);
|
|
if (font)
|
|
name = font;
|
|
}
|
|
return Estrdup(name);
|
|
}
|
|
|
|
static TextState *
|
|
TextstateCreate(void)
|
|
{
|
|
TextState *ts;
|
|
|
|
ts = Ecalloc(1, sizeof(TextState));
|
|
if (!ts)
|
|
return NULL;
|
|
|
|
ts->fontname = NULL;
|
|
ts->style.mode = MODE_WRAP_CHAR;
|
|
ts->style.orientation = FONT_TO_RIGHT;
|
|
ts->efont = NULL;
|
|
ts->xfont = NULL;
|
|
ts->xfontset = 0;
|
|
|
|
return ts;
|
|
}
|
|
|
|
static void
|
|
TextStateDestroy(TextState * ts)
|
|
{
|
|
if (ts->fontname)
|
|
Efree(ts->fontname);
|
|
if (ts->xfont)
|
|
XFreeFont(disp, ts->xfont);
|
|
if (ts->efont)
|
|
Efont_free(ts->efont);
|
|
Efree(ts);
|
|
}
|
|
|
|
static TextClass *
|
|
TextclassCreate(const char *name)
|
|
{
|
|
TextClass *tc;
|
|
|
|
tc = Ecalloc(1, sizeof(TextClass));
|
|
if (!tc)
|
|
return NULL;
|
|
|
|
tc->name = Estrdup(name);
|
|
tc->justification = 512;
|
|
|
|
return tc;
|
|
}
|
|
|
|
static void
|
|
TextclassDestroy(TextClass * tc)
|
|
{
|
|
if (tc->ref_count > 0)
|
|
{
|
|
DialogOK(_("Textclass Error!"), _("%u references remain\n"),
|
|
tc->ref_count);
|
|
return;
|
|
}
|
|
if (tc->name)
|
|
Efree(tc->name);
|
|
if (tc->norm.normal)
|
|
TextStateDestroy(tc->norm.normal);
|
|
if (tc->norm.hilited)
|
|
TextStateDestroy(tc->norm.hilited);
|
|
if (tc->norm.clicked)
|
|
TextStateDestroy(tc->norm.clicked);
|
|
if (tc->norm.disabled)
|
|
TextStateDestroy(tc->norm.disabled);
|
|
if (tc->active.normal)
|
|
TextStateDestroy(tc->active.normal);
|
|
if (tc->active.hilited)
|
|
TextStateDestroy(tc->active.hilited);
|
|
if (tc->active.clicked)
|
|
TextStateDestroy(tc->active.clicked);
|
|
if (tc->active.disabled)
|
|
TextStateDestroy(tc->active.disabled);
|
|
if (tc->sticky.normal)
|
|
TextStateDestroy(tc->sticky.normal);
|
|
if (tc->sticky.hilited)
|
|
TextStateDestroy(tc->sticky.hilited);
|
|
if (tc->sticky.clicked)
|
|
TextStateDestroy(tc->sticky.clicked);
|
|
if (tc->sticky.disabled)
|
|
TextStateDestroy(tc->sticky.disabled);
|
|
if (tc->sticky_active.normal)
|
|
TextStateDestroy(tc->sticky_active.normal);
|
|
if (tc->sticky_active.hilited)
|
|
TextStateDestroy(tc->sticky_active.hilited);
|
|
if (tc->sticky_active.clicked)
|
|
TextStateDestroy(tc->sticky_active.clicked);
|
|
if (tc->sticky_active.disabled)
|
|
TextStateDestroy(tc->sticky_active.disabled);
|
|
Efree(tc);
|
|
}
|
|
|
|
static void
|
|
TextclassPopulate(TextClass * tclass)
|
|
{
|
|
|
|
if (!tclass)
|
|
return;
|
|
|
|
if (!tclass->norm.normal)
|
|
return;
|
|
|
|
if (!tclass->norm.hilited)
|
|
tclass->norm.hilited = tclass->norm.normal;
|
|
if (!tclass->norm.clicked)
|
|
tclass->norm.clicked = tclass->norm.normal;
|
|
if (!tclass->norm.disabled)
|
|
tclass->norm.disabled = tclass->norm.normal;
|
|
|
|
if (!tclass->active.normal)
|
|
tclass->active.normal = tclass->norm.normal;
|
|
if (!tclass->active.hilited)
|
|
tclass->active.hilited = tclass->active.normal;
|
|
if (!tclass->active.clicked)
|
|
tclass->active.clicked = tclass->active.normal;
|
|
if (!tclass->active.disabled)
|
|
tclass->active.disabled = tclass->active.normal;
|
|
|
|
if (!tclass->sticky.normal)
|
|
tclass->sticky.normal = tclass->norm.normal;
|
|
if (!tclass->sticky.hilited)
|
|
tclass->sticky.hilited = tclass->sticky.normal;
|
|
if (!tclass->sticky.clicked)
|
|
tclass->sticky.clicked = tclass->sticky.normal;
|
|
if (!tclass->sticky.disabled)
|
|
tclass->sticky.disabled = tclass->sticky.normal;
|
|
|
|
if (!tclass->sticky_active.normal)
|
|
tclass->sticky_active.normal = tclass->norm.normal;
|
|
if (!tclass->sticky_active.hilited)
|
|
tclass->sticky_active.hilited = tclass->sticky_active.normal;
|
|
if (!tclass->sticky_active.clicked)
|
|
tclass->sticky_active.clicked = tclass->sticky_active.normal;
|
|
if (!tclass->sticky_active.disabled)
|
|
tclass->sticky_active.disabled = tclass->sticky_active.normal;
|
|
}
|
|
|
|
TextClass *
|
|
TextclassFind(const char *name, int fallback)
|
|
{
|
|
TextClass *tc;
|
|
|
|
if (name)
|
|
{
|
|
tc = FindItem(name, 0, LIST_FINDBY_NAME, LIST_TYPE_TCLASS);
|
|
if (tc || !fallback)
|
|
return tc;
|
|
}
|
|
|
|
tc = FindItem("__FALLBACK_TCLASS", 0, LIST_FINDBY_NAME, LIST_TYPE_TCLASS);
|
|
|
|
return tc;
|
|
}
|
|
|
|
int
|
|
TextclassConfigLoad(FILE * fs)
|
|
{
|
|
int err = 0;
|
|
char s[FILEPATH_LEN_MAX];
|
|
char s2[FILEPATH_LEN_MAX];
|
|
int i1, r, g, b;
|
|
TextClass *tc = NULL;
|
|
TextState *ts = NULL;
|
|
int fields;
|
|
|
|
while (GetLine(s, sizeof(s), fs))
|
|
{
|
|
s2[0] = 0;
|
|
i1 = CONFIG_INVALID;
|
|
fields = sscanf(s, "%i %4000[^=]", &i1, s2);
|
|
|
|
if (fields < 1)
|
|
{
|
|
i1 = CONFIG_INVALID;
|
|
}
|
|
else if (i1 == CONFIG_CLOSE)
|
|
{
|
|
if (fields != 1)
|
|
Alert(_("CONFIG: ignoring extra data in \"%s\"\n"), s);
|
|
}
|
|
else if (i1 != CONFIG_INVALID)
|
|
{
|
|
if (fields != 2)
|
|
{
|
|
Alert(_("CONFIG: missing required data in \"%s\"\n"), s);
|
|
i1 = CONFIG_INVALID;
|
|
}
|
|
}
|
|
switch (i1)
|
|
{
|
|
case CONFIG_CLOSE:
|
|
if (tc)
|
|
{
|
|
TextclassPopulate(tc);
|
|
AddItem(tc, tc->name, 0, LIST_TYPE_TCLASS);
|
|
}
|
|
goto done;
|
|
case CONFIG_CLASSNAME:
|
|
if (ConfigSkipIfExists(fs, s2, LIST_TYPE_TCLASS))
|
|
goto done;
|
|
tc = TextclassCreate(s2);
|
|
break;
|
|
case TEXT_ORIENTATION:
|
|
if (ts)
|
|
ts->style.orientation = atoi(s2);
|
|
break;
|
|
case TEXT_JUSTIFICATION:
|
|
if (tc)
|
|
tc->justification = atoi(s2);
|
|
break;
|
|
case CONFIG_DESKTOP:
|
|
case ICLASS_NORMAL:
|
|
if (tc)
|
|
tc->norm.normal = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_CLICKED:
|
|
if (tc)
|
|
tc->norm.clicked = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_HILITED:
|
|
if (tc)
|
|
tc->norm.hilited = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_DISABLED:
|
|
if (tc)
|
|
tc->norm.disabled = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_STICKY_NORMAL:
|
|
if (tc)
|
|
tc->sticky.normal = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_STICKY_CLICKED:
|
|
if (tc)
|
|
tc->sticky.clicked = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_STICKY_HILITED:
|
|
if (tc)
|
|
tc->sticky.hilited = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_STICKY_DISABLED:
|
|
if (tc)
|
|
tc->sticky.disabled = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_ACTIVE_NORMAL:
|
|
if (tc)
|
|
tc->active.normal = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_ACTIVE_CLICKED:
|
|
if (tc)
|
|
tc->active.clicked = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_ACTIVE_HILITED:
|
|
if (tc)
|
|
tc->active.hilited = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_ACTIVE_DISABLED:
|
|
if (tc)
|
|
tc->active.disabled = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_STICKY_ACTIVE_NORMAL:
|
|
if (tc)
|
|
tc->sticky_active.normal = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_STICKY_ACTIVE_CLICKED:
|
|
if (tc)
|
|
tc->sticky_active.clicked = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_STICKY_ACTIVE_HILITED:
|
|
if (tc)
|
|
tc->sticky_active.hilited = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case ICLASS_STICKY_ACTIVE_DISABLED:
|
|
if (tc)
|
|
tc->sticky_active.disabled = ts = TextstateCreate();
|
|
if (ts)
|
|
{
|
|
ts->fontname = TextstateFontLookup(s2);
|
|
ts->style.mode = MODE_VERBATIM;
|
|
}
|
|
break;
|
|
case TEXT_MODE:
|
|
if (ts)
|
|
ts->style.mode = atoi(s2);
|
|
break;
|
|
case TEXT_EFFECT:
|
|
if (ts)
|
|
ts->effect = atoi(s2);
|
|
break;
|
|
case TEXT_FG_COL:
|
|
if (ts)
|
|
{
|
|
r = g = b = 0;
|
|
sscanf(s, "%*s %i %i %i", &r, &g, &b);
|
|
ESetColor(&ts->fg_col, r, g, b);
|
|
}
|
|
break;
|
|
case TEXT_BG_COL:
|
|
if (ts)
|
|
{
|
|
r = g = b = 0;
|
|
sscanf(s, "%*s %i %i %i", &r, &g, &b);
|
|
ESetColor(&ts->bg_col, r, g, b);
|
|
}
|
|
break;
|
|
default:
|
|
Alert(_("Warning: unable to determine what to do with\n"
|
|
"the following text in the middle of current Text"
|
|
" definition:\n" "%s\nWill ignore and continue...\n"), s);
|
|
}
|
|
|
|
}
|
|
err = -1;
|
|
|
|
done:
|
|
return err;
|
|
}
|
|
|
|
void
|
|
TextclassApply(ImageClass * iclass, Window win, int w, int h, int active,
|
|
int sticky, int state, char expose __UNUSED__,
|
|
TextClass * tclass, const char *text)
|
|
{
|
|
|
|
if ((!iclass) || (!tclass) || (!text) || (!win) || (w < 1) || (h < 1))
|
|
return;
|
|
|
|
#if USE_DQ_TCLASS /* Try not using the draw queue here. */
|
|
if (Mode.queue_up)
|
|
{
|
|
DrawQueue *dq;
|
|
|
|
dq = Emalloc(sizeof(DrawQueue));
|
|
dq->win = win;
|
|
dq->iclass = iclass;
|
|
if (dq->iclass)
|
|
dq->iclass->ref_count++;
|
|
dq->w = w;
|
|
dq->h = h;
|
|
dq->active = active;
|
|
dq->sticky = sticky;
|
|
dq->state = state;
|
|
dq->expose = expose;
|
|
dq->tclass = tclass;
|
|
if (dq->tclass)
|
|
dq->tclass->ref_count++;
|
|
if (text)
|
|
dq->text = Estrdup(text);
|
|
else
|
|
dq->text = NULL;
|
|
dq->w = w;
|
|
dq->shape_propagate = 0;
|
|
dq->pager = NULL;
|
|
dq->redraw_pager = NULL;
|
|
dq->d = NULL;
|
|
dq->di = NULL;
|
|
dq->x = 0;
|
|
dq->y = 0;
|
|
AddItem(dq, "DRAW", dq->win, LIST_TYPE_DRAW);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
XClearWindow(disp, win);
|
|
|
|
TextDraw(tclass, win, active, sticky, state, text, iclass->padding.left,
|
|
iclass->padding.top,
|
|
w - (iclass->padding.left + iclass->padding.right),
|
|
h - (iclass->padding.top + iclass->padding.bottom),
|
|
h - (iclass->padding.top + iclass->padding.bottom),
|
|
tclass->justification);
|
|
}
|
|
|
|
/*
|
|
* Textclass Module
|
|
*/
|
|
|
|
static void
|
|
TextclassSighan(int sig, void *prm __UNUSED__)
|
|
{
|
|
TextClass *tc;
|
|
|
|
switch (sig)
|
|
{
|
|
case ESIGNAL_INIT:
|
|
/* create a fallback textclass in case no textclass is found */
|
|
tc = TextclassCreate("__FALLBACK_TCLASS");
|
|
tc->norm.normal = TextstateCreate();
|
|
tc->norm.normal->fontname =
|
|
Estrdup("-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*");
|
|
ESetColor(&(tc->norm.normal->fg_col), 0, 0, 0);
|
|
AddItem(tc, tc->name, 0, LIST_TYPE_TCLASS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
TextclassIpc(const char *params, Client * c __UNUSED__)
|
|
{
|
|
char param1[FILEPATH_LEN_MAX];
|
|
char param2[FILEPATH_LEN_MAX];
|
|
char param3[FILEPATH_LEN_MAX];
|
|
char pq;
|
|
TextClass *tc;
|
|
|
|
if (!params)
|
|
{
|
|
IpcPrintf("Please specify...\n");
|
|
return;
|
|
}
|
|
|
|
param1[0] = 0;
|
|
param2[0] = 0;
|
|
param3[0] = 0;
|
|
|
|
word(params, 1, param1);
|
|
word(params, 2, param2);
|
|
|
|
if (!strncmp(param1, "list", 2))
|
|
{
|
|
TextClass **lst;
|
|
int num, i;
|
|
|
|
lst = (TextClass **) ListItemType(&num, LIST_TYPE_TCLASS);
|
|
for (i = 0; i < num; i++)
|
|
IpcPrintf("%s\n", lst[i]->name);
|
|
if (lst)
|
|
Efree(lst);
|
|
return;
|
|
}
|
|
|
|
if (!param1[0])
|
|
{
|
|
IpcPrintf("TextClass not specified\n");
|
|
return;
|
|
}
|
|
|
|
tc = TextclassFind(param1, 0);
|
|
if (!tc)
|
|
{
|
|
IpcPrintf("TextClass not found: %s\n", param1);
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(param2, "create"))
|
|
{
|
|
}
|
|
else if (!strcmp(param2, "delete"))
|
|
{
|
|
if (tc)
|
|
TextclassDestroy(tc);
|
|
}
|
|
else if (!strcmp(param2, "modify"))
|
|
{
|
|
}
|
|
else if (!strcmp(param2, "apply"))
|
|
{
|
|
if (tc)
|
|
{
|
|
int state;
|
|
int x, y;
|
|
const char *txt;
|
|
Window win;
|
|
|
|
word(params, 3, param3);
|
|
win = (Window) strtol(param3, (char **)NULL, 0);
|
|
word(params, 4, param3);
|
|
x = atoi(param3);
|
|
word(params, 5, param3);
|
|
y = atoi(param3);
|
|
word(params, 6, param3);
|
|
state = STATE_NORMAL;
|
|
if (!strcmp(param3, "normal"))
|
|
state = STATE_NORMAL;
|
|
else if (!strcmp(param3, "hilited"))
|
|
state = STATE_HILITED;
|
|
else if (!strcmp(param3, "clicked"))
|
|
state = STATE_CLICKED;
|
|
else if (!strcmp(param3, "disabled"))
|
|
state = STATE_DISABLED;
|
|
txt = atword(params, 7);
|
|
pq = Mode.queue_up;
|
|
Mode.queue_up = 0;
|
|
if (txt)
|
|
TextDraw(tc, win, 0, 0, state, txt, x, y, 99999, 99999, 17, 0);
|
|
Mode.queue_up = pq;
|
|
}
|
|
}
|
|
else if (!strcmp(param2, "query_size"))
|
|
{
|
|
if (tc)
|
|
{
|
|
int w, h;
|
|
const char *txt;
|
|
|
|
w = h = 0;
|
|
txt = atword(params, 3);
|
|
if (txt)
|
|
TextSize(tc, 0, 0, STATE_NORMAL, txt, &w, &h, 17);
|
|
IpcPrintf("%i %i\n", w, h);
|
|
}
|
|
}
|
|
else if (!strcmp(param2, "query"))
|
|
{
|
|
if (tc)
|
|
IpcPrintf("TextClass %s found\n", tc->name);
|
|
}
|
|
else if (!strcmp(param2, "ref_count"))
|
|
{
|
|
if (tc)
|
|
IpcPrintf("%u references remain.\n", tc->ref_count);
|
|
}
|
|
else
|
|
{
|
|
IpcPrintf("Error: Unknown operation specified\n");
|
|
}
|
|
}
|
|
|
|
IpcItem TextclassIpcArray[] = {
|
|
{
|
|
TextclassIpc,
|
|
"textclass", NULL,
|
|
"List textclasses, create/delete/modify/apply a textclass",
|
|
NULL}
|
|
,
|
|
};
|
|
#define N_IPC_FUNCS (sizeof(TextclassIpcArray)/sizeof(IpcItem))
|
|
|
|
/*
|
|
* Module descriptor
|
|
*/
|
|
EModule ModTextclass = {
|
|
"textclass", "tc",
|
|
TextclassSighan,
|
|
{N_IPC_FUNCS, TextclassIpcArray}
|
|
,
|
|
{0, NULL}
|
|
};
|