500 lines
12 KiB
C
500 lines
12 KiB
C
/*
|
|
* Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
|
|
* Copyright (C) 2004-2023 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"
|
|
#include "emodule.h"
|
|
#include "iclass.h"
|
|
#include "list.h"
|
|
#include "tclass.h"
|
|
#include "xwin.h"
|
|
|
|
#define ENABLE_DESTROY 0 /* Broken */
|
|
|
|
static LIST_HEAD(tclass_list);
|
|
|
|
static TextClass *TextclassGetFallback(void);
|
|
|
|
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(const char *font)
|
|
{
|
|
TextState *ts;
|
|
|
|
ts = ECALLOC(TextState, 1);
|
|
if (!ts)
|
|
return NULL;
|
|
|
|
ts->style.orientation = FONT_TO_RIGHT;
|
|
|
|
if (font)
|
|
ts->fontname = TextstateFontLookup(font);
|
|
|
|
return ts;
|
|
}
|
|
|
|
static void
|
|
TextstateDestroy(TextState *ts)
|
|
{
|
|
if (!ts)
|
|
return;
|
|
|
|
Efree(ts->fontname);
|
|
if (ts->ops)
|
|
ts->ops->Destroy(ts);
|
|
|
|
Efree(ts);
|
|
}
|
|
|
|
static TextState *
|
|
TextstateSet(TextState **tsp, const char *name)
|
|
{
|
|
TextState *ts;
|
|
|
|
ts = TextstateCreate(name);
|
|
|
|
if (*tsp)
|
|
TextstateDestroy(*tsp);
|
|
*tsp = ts;
|
|
|
|
return ts;
|
|
}
|
|
|
|
static TextClass *
|
|
TextclassCreate(const char *name)
|
|
{
|
|
TextClass *tc;
|
|
|
|
tc = ECALLOC(TextClass, 1);
|
|
if (!tc)
|
|
return NULL;
|
|
|
|
LIST_PREPEND(TextClass, &tclass_list, tc);
|
|
|
|
tc->name = Estrdup(name);
|
|
tc->justification = 512;
|
|
|
|
return tc;
|
|
}
|
|
|
|
#if ENABLE_DESTROY
|
|
static void
|
|
TextclassDestroy(TextClass *tc)
|
|
{
|
|
if (tc->ref_count > 0)
|
|
{
|
|
DialogOK("TextClass Error!", _("%u references remain"), tc->ref_count);
|
|
return;
|
|
}
|
|
Efree(tc->name);
|
|
TextStateDestroy(tc->norm.normal);
|
|
TextStateDestroy(tc->norm.hilited);
|
|
TextStateDestroy(tc->norm.clicked);
|
|
TextStateDestroy(tc->norm.disabled);
|
|
TextStateDestroy(tc->active.normal);
|
|
TextStateDestroy(tc->active.hilited);
|
|
TextStateDestroy(tc->active.clicked);
|
|
TextStateDestroy(tc->active.disabled);
|
|
TextStateDestroy(tc->sticky.normal);
|
|
TextStateDestroy(tc->sticky.hilited);
|
|
TextStateDestroy(tc->sticky.clicked);
|
|
TextStateDestroy(tc->sticky.disabled);
|
|
TextStateDestroy(tc->sticky_active.normal);
|
|
TextStateDestroy(tc->sticky_active.hilited);
|
|
TextStateDestroy(tc->sticky_active.clicked);
|
|
TextStateDestroy(tc->sticky_active.disabled);
|
|
|
|
Efree(tc);
|
|
}
|
|
#endif /* ENABLE_DESTROY */
|
|
|
|
TextClass *
|
|
TextclassAlloc(const char *name, int fallback)
|
|
{
|
|
TextClass *tc;
|
|
|
|
if (!name || !name[0])
|
|
return NULL;
|
|
|
|
tc = TextclassFind(name, fallback);
|
|
if (tc)
|
|
tc->ref_count++;
|
|
|
|
return tc;
|
|
}
|
|
|
|
void
|
|
TextclassFree(TextClass *tc)
|
|
{
|
|
if (tc)
|
|
tc->ref_count--;
|
|
}
|
|
|
|
int
|
|
TextclassGetJustification(TextClass *tc)
|
|
{
|
|
return tc->justification;
|
|
}
|
|
|
|
void
|
|
TextclassSetJustification(TextClass *tc, int just)
|
|
{
|
|
tc->justification = just;
|
|
}
|
|
|
|
#define TSTATE_SET_STATE(which, fallback) \
|
|
if (!tc->which) tc->which = tc->fallback;
|
|
|
|
static void
|
|
TextclassPopulate(TextClass *tc)
|
|
{
|
|
if (!tc || !tc->norm.normal)
|
|
return;
|
|
|
|
TSTATE_SET_STATE(norm.hilited, norm.normal);
|
|
TSTATE_SET_STATE(norm.clicked, norm.normal);
|
|
TSTATE_SET_STATE(norm.disabled, norm.normal);
|
|
|
|
TSTATE_SET_STATE(active.normal, norm.normal);
|
|
TSTATE_SET_STATE(active.hilited, active.normal);
|
|
TSTATE_SET_STATE(active.clicked, active.normal);
|
|
TSTATE_SET_STATE(active.disabled, active.normal);
|
|
|
|
TSTATE_SET_STATE(sticky.normal, norm.normal);
|
|
TSTATE_SET_STATE(sticky.hilited, sticky.normal);
|
|
TSTATE_SET_STATE(sticky.clicked, sticky.normal);
|
|
TSTATE_SET_STATE(sticky.disabled, sticky.normal);
|
|
|
|
TSTATE_SET_STATE(sticky_active.normal, norm.normal);
|
|
TSTATE_SET_STATE(sticky_active.hilited, sticky_active.normal);
|
|
TSTATE_SET_STATE(sticky_active.clicked, sticky_active.normal);
|
|
TSTATE_SET_STATE(sticky_active.disabled, sticky_active.normal);
|
|
}
|
|
|
|
static int
|
|
_TextclassMatchName(const void *data, const void *match)
|
|
{
|
|
return strcmp(((const TextClass *)data)->name, (const char *)match);
|
|
}
|
|
|
|
TextClass *
|
|
TextclassFind(const char *name, int fallback)
|
|
{
|
|
TextClass *tc = NULL;
|
|
|
|
if (name)
|
|
tc = LIST_FIND(TextClass, &tclass_list, _TextclassMatchName, name);
|
|
if (tc || !fallback)
|
|
return tc;
|
|
|
|
#if 0
|
|
Eprintf("%s: Get fallback (%s)\n", __func__, name);
|
|
#endif
|
|
return TextclassGetFallback();
|
|
}
|
|
|
|
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;
|
|
|
|
while (GetLine(s, sizeof(s), fs))
|
|
{
|
|
i1 = ConfigParseline1(s, s2, NULL, NULL);
|
|
|
|
/* tc not needed */
|
|
switch (i1)
|
|
{
|
|
case CONFIG_CLOSE:
|
|
TextclassPopulate(tc);
|
|
goto done;
|
|
case CONFIG_CLASSNAME:
|
|
if (TextclassFind(s2, 0))
|
|
{
|
|
SkipTillEnd(fs);
|
|
goto done;
|
|
}
|
|
tc = TextclassCreate(s2);
|
|
continue;
|
|
}
|
|
|
|
/* tc needed */
|
|
if (!tc)
|
|
break;
|
|
|
|
switch (i1)
|
|
{
|
|
case TEXT_JUSTIFICATION:
|
|
tc->justification = atoi(s2);
|
|
continue;
|
|
case ICLASS_NORMAL:
|
|
ts = TextstateSet(&tc->norm.normal, s2);
|
|
continue;
|
|
case ICLASS_CLICKED:
|
|
ts = TextstateSet(&tc->norm.clicked, s2);
|
|
continue;
|
|
case ICLASS_HILITED:
|
|
ts = TextstateSet(&tc->norm.hilited, s2);
|
|
continue;
|
|
case ICLASS_DISABLED:
|
|
ts = TextstateSet(&tc->norm.disabled, s2);
|
|
continue;
|
|
case ICLASS_STICKY_NORMAL:
|
|
ts = TextstateSet(&tc->sticky.normal, s2);
|
|
continue;
|
|
case ICLASS_STICKY_CLICKED:
|
|
ts = TextstateSet(&tc->sticky.clicked, s2);
|
|
continue;
|
|
case ICLASS_STICKY_HILITED:
|
|
ts = TextstateSet(&tc->sticky.hilited, s2);
|
|
continue;
|
|
case ICLASS_STICKY_DISABLED:
|
|
ts = TextstateSet(&tc->sticky.disabled, s2);
|
|
continue;
|
|
case ICLASS_ACTIVE_NORMAL:
|
|
ts = TextstateSet(&tc->active.normal, s2);
|
|
continue;
|
|
case ICLASS_ACTIVE_CLICKED:
|
|
ts = TextstateSet(&tc->active.clicked, s2);
|
|
continue;
|
|
case ICLASS_ACTIVE_HILITED:
|
|
ts = TextstateSet(&tc->active.hilited, s2);
|
|
continue;
|
|
case ICLASS_ACTIVE_DISABLED:
|
|
ts = TextstateSet(&tc->active.disabled, s2);
|
|
continue;
|
|
case ICLASS_STICKY_ACTIVE_NORMAL:
|
|
ts = TextstateSet(&tc->sticky_active.normal, s2);
|
|
continue;
|
|
case ICLASS_STICKY_ACTIVE_CLICKED:
|
|
ts = TextstateSet(&tc->sticky_active.clicked, s2);
|
|
continue;
|
|
case ICLASS_STICKY_ACTIVE_HILITED:
|
|
ts = TextstateSet(&tc->sticky_active.hilited, s2);
|
|
continue;
|
|
case ICLASS_STICKY_ACTIVE_DISABLED:
|
|
ts = TextstateSet(&tc->sticky_active.disabled, s2);
|
|
continue;
|
|
}
|
|
|
|
/* ts needed */
|
|
if (!ts)
|
|
break;
|
|
|
|
switch (i1)
|
|
{
|
|
case TEXT_ORIENTATION:
|
|
ts->style.orientation = atoi(s2);
|
|
continue;
|
|
case TEXT_EFFECT:
|
|
ts->style.effect = atoi(s2);
|
|
continue;
|
|
case TEXT_FG_COL:
|
|
r = g = b = 0;
|
|
sscanf(s, "%*s %i %i %i", &r, &g, &b);
|
|
COLOR32_FROM_RGB(ts->fg_col, r, g, b);
|
|
continue;
|
|
case TEXT_BG_COL:
|
|
r = g = b = 0;
|
|
sscanf(s, "%*s %i %i %i", &r, &g, &b);
|
|
COLOR32_FROM_RGB(ts->bg_col, r, g, b);
|
|
continue;
|
|
default:
|
|
ConfigParseError("TextClass", s);
|
|
continue;
|
|
}
|
|
}
|
|
err = -1;
|
|
|
|
done:
|
|
return err;
|
|
}
|
|
|
|
static TextClass *
|
|
TextclassGetFallback(void)
|
|
{
|
|
TextClass *tc;
|
|
|
|
tc = TextclassFind("__fb_tc", 0);
|
|
if (tc)
|
|
return tc;
|
|
|
|
/* Create fallback textclass */
|
|
tc = TextclassCreate("__fb_tc");
|
|
if (!tc)
|
|
return tc;
|
|
|
|
tc->norm.normal =
|
|
TextstateCreate("-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*");
|
|
COLOR32_FROM_RGB(tc->norm.normal->fg_col, 0, 0, 0);
|
|
TextclassPopulate(tc);
|
|
|
|
return tc;
|
|
}
|
|
|
|
/*
|
|
* Textclass Module
|
|
*/
|
|
|
|
static void
|
|
TextclassIpc(const char *params)
|
|
{
|
|
char param1[1024];
|
|
char param2[1024];
|
|
int l;
|
|
const char *p;
|
|
TextClass *tc;
|
|
|
|
if (!params)
|
|
{
|
|
IpcPrintf("Please specify...\n");
|
|
return;
|
|
}
|
|
|
|
p = params;
|
|
l = 0;
|
|
param1[0] = param2[0] = '\0';
|
|
sscanf(p, "%1000s %1000s %n", param1, param2, &l);
|
|
p += l;
|
|
|
|
if (!strncmp(param1, "list", 2))
|
|
{
|
|
LIST_FOR_EACH(TextClass, &tclass_list, tc) IpcPrintf("%s\n", tc->name);
|
|
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, "apply"))
|
|
{
|
|
EX_Window xwin;
|
|
Win win;
|
|
char state[20];
|
|
int x, y, st;
|
|
|
|
/* 3:xwin 4:x 5:y 6:state 7-:txt */
|
|
xwin = NoXID;
|
|
x = y = 0;
|
|
state[0] = '\0';
|
|
l = 0;
|
|
sscanf(p, "%x %d %d %16s %n", &xwin, &x, &y, state, &l);
|
|
p += l;
|
|
|
|
if (!strcmp(state, "normal"))
|
|
st = STATE_NORMAL;
|
|
else if (!strcmp(state, "hilited"))
|
|
st = STATE_HILITED;
|
|
else if (!strcmp(state, "clicked"))
|
|
st = STATE_CLICKED;
|
|
else if (!strcmp(state, "disabled"))
|
|
st = STATE_DISABLED;
|
|
else
|
|
st = STATE_NORMAL;
|
|
|
|
if (l == 0)
|
|
return;
|
|
|
|
win = ECreateWinFromXwin(xwin);
|
|
if (!win)
|
|
return;
|
|
|
|
TextDraw(tc, win, NoXID, 0, 0, st, p, x, y, 99999, 99999, 0);
|
|
EDestroyWin(win);
|
|
}
|
|
else if (!strcmp(param2, "query_size"))
|
|
{
|
|
int w, h;
|
|
|
|
/* 3-:txt */
|
|
|
|
if (l == 0)
|
|
return;
|
|
|
|
w = h = 0;
|
|
TextSize(tc, 0, 0, STATE_NORMAL, p, &w, &h);
|
|
IpcPrintf("%i %i\n", w, h);
|
|
}
|
|
else if (!strcmp(param2, "query"))
|
|
{
|
|
IpcPrintf("TextClass %s found\n", tc->name);
|
|
}
|
|
else if (!strcmp(param2, "ref_count"))
|
|
{
|
|
IpcPrintf("%u references remain\n", tc->ref_count);
|
|
}
|
|
else
|
|
{
|
|
IpcPrintf("Error: unknown operation specified\n");
|
|
}
|
|
}
|
|
|
|
static const IpcItem TextclassIpcArray[] = {
|
|
{
|
|
TextclassIpc,
|
|
"textclass", "tc",
|
|
"List textclasses, apply a textclass",
|
|
NULL }
|
|
,
|
|
};
|
|
|
|
/*
|
|
* Module descriptor
|
|
*/
|
|
extern const EModule ModTextclass;
|
|
|
|
const EModule ModTextclass = {
|
|
"textclass", "tc",
|
|
NULL,
|
|
MOD_ITEMS(TextclassIpcArray),
|
|
{ 0, NULL }
|
|
};
|