Tobias Boege 1afd7010c6 [GB.NCURSES]
* NEW: Screen.Beep() is a new method which makes an audible beep
* NEW: Screen.Flash() produces a visual flash on the screen
* BUG: Fix Window.ReadLine(): getnstr() doesn't return the input length



git-svn-id: svn://localhost/gambas/trunk@7190 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2015-07-29 19:32:45 +00:00

945 lines
20 KiB
C

/*
* c_window.c - gb.ncurses Window class
*
* Copyright (C) 2012/3 Tobias Boege <tobias@gambas-buch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#define __C_WINDOW_C
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
#include <panel.h>
#include "gambas.h"
#include "main.h"
#include "c_window.h"
#include "c_screen.h"
#include "c_color.h"
#define THIS ((CWINDOW *) _object)
#define MAKE_THING(snip, win, a, b) \
do { \
if ((a) == -1) \
a = get##snip##x(win); \
if ((b) == -1) \
b = get##snip##y(win); \
} while (0)
#define MAKE_CURSOR(win, x, y) MAKE_THING(cur, win, x, y)
#define MAKE_COORDS(win, x, y) MAKE_THING(beg, win, x, y)
#define MAKE_DIM(win, x, y) MAKE_THING(max, win, w, h)
#define CHECK_RAISE_THING(snip, win, a, b) \
if ((a) < 0 || (a) > get##snip##x(win) \
|| (b) < 0 || (b) > get##snip##y(win)) { /* >= ? */ \
GB.Error(GB_ERR_BOUND); \
return; \
}
#define CHECK_RAISE_COORDS(win, x, y) CHECK_RAISE_THING(max, win, x, y)
#define CHECK_RAISE_DIM(win, x, y) CHECK_RAISE_THING(max, win, x, y)
static CWINDOW *_curwin = NULL;
DECLARE_EVENT(EVENT_Read);
/* This macro is mostly called by Gambas implementation functions to request
* output on screen (read: to check if the output is buffered and if not
* produce output by means of REAL_REFRESH()) */
#define REFRESH() CWINDOW_refresh(THIS)
static void CWINDOW_refresh(CWINDOW *win)
{
if (win->buffered)
return;
REAL_REFRESH();
}
void CWINDOW_raise_read(CWINDOW *win)
{
if (!win) {
if (!_curwin)
return;
win = _curwin;
}
if (GB.CanRaise(win, EVENT_Read))
GB.Raise(win, EVENT_Read, 0);
}
static void CWINDOW_from_ncurses(CWINDOW *win, WINDOW *ncwin, bool border)
{
win->main = ncwin;
win->pan = new_panel(ncwin);
keypad(ncwin, TRUE);
win->has_border = border;
win->border = 0;
win->buffered = 0;
win->wrap = 1;
if (border) {
WINDOW *new = derwin(ncwin, getmaxy(ncwin) - 2,
getmaxx(ncwin) - 2,
1, 1);
syncok(new, TRUE);
win->content = new;
} else {
win->content = win->main;
}
win->caption = NULL;
}
BEGIN_METHOD(Window_new, GB_BOOLEAN out_border; GB_INTEGER x; GB_INTEGER y;
GB_INTEGER w; GB_INTEGER h)
WINDOW *new;
int w, h;
w = VARGOPT(w, COLS);
h = VARGOPT(h, LINES);
if (VARGOPT(out_border, 1)) {
w = MIN(w + 2, COLS);
h = MIN(h + 2, LINES);
}
new = newwin(h, w, VARGOPT(y, 0), VARGOPT(x, 0));
CWINDOW_from_ncurses(THIS, new, VARGOPT(out_border, 1));
REFRESH();
END_METHOD
BEGIN_METHOD_VOID(Window_free)
if (_curwin == THIS) {
_curwin = NULL;
INPUT_exit();
}
del_panel(THIS->pan);
if (THIS->has_border)
delwin(THIS->content);
delwin(THIS->main);
if (THIS->caption)
GB.FreeString(&THIS->caption);
REAL_REFRESH();
END_METHOD
BEGIN_METHOD(Window_get, GB_INTEGER y; GB_INTEGER x)
THIS->pos.line = VARG(y);
THIS->pos.col = VARG(x);
RETURN_SELF();
END_METHOD
/**G
* Wait until the user typed one of the characters listed in Opts or until
* they typed wrong Tries times (infinity if not given).
*
* If there is an uppercase letter in the Opts string and the user hits
* Return, the first uppercase letter found in Opts is taken as the default
* value and the function returns. The case of letters does not matter in
* any other regard (this function is case-insensitive).
*
* This function returns the typed character (always in lowercase!).
**/
BEGIN_METHOD(Window_Ask, GB_STRING opts; GB_INTEGER tries)
int t, ch, i;
char *o, c;
t = VARGOPT(tries, -1);
o = STRING(opts);
while (t--) {
ch = INPUT_get(TIMEOUT_NOTIMEOUT);
/* XXX: not meant for ncurses special keys */
if (ch > 127)
continue;
if (ch == '\n')
for (i = 0; i < LENGTH(opts); i++)
if (isupper(o[i]))
goto found;
for (i = 0; i < LENGTH(opts); i++)
if (tolower(o[i]) == (char) ch)
goto found;
}
GB.ReturnNull();
return;
found:
c = tolower(o[i]);
GB.ReturnNewString(&c, 1);
END_METHOD
BEGIN_METHOD_VOID(Window_Lower)
bottom_panel(THIS->pan);
REFRESH();
END_METHOD
static void CWINDOW_clear(CWINDOW *win)
{
werase(win->content);
}
BEGIN_METHOD_VOID(Window_Clear)
CWINDOW_clear(THIS);
REFRESH();
END_METHOD
BEGIN_METHOD_VOID(Window_Drain)
INPUT_drain();
END_METHOD
BEGIN_METHOD(Window_DrawHLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len;
GB_STRING ch)
mvwhline(THIS->content, VARG(y), VARG(x), *STRING(ch), VARG(len));
REFRESH();
END_METHOD
BEGIN_METHOD(Window_DrawVLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len;
GB_STRING ch)
mvwvline(THIS->content, VARG(y), VARG(x), *STRING(ch), VARG(len));
REFRESH();
END_METHOD
static void CWINDOW_get(CWINDOW *win, int x, int y, unsigned int len,
char **ret)
{
char *buf;
MAKE_COORDS(win->content, x, y);
CHECK_RAISE_COORDS(win->content, x, y);
if (len == -1)
len = getmaxx(win->content) - getcurx(win->content);
len = MIN(len, (getmaxy(win->content) - getcury(win->content)) *
getmaxx(win->content) - getcurx(win->content) - 1);
GB.Alloc((void **) &buf, len + 1);
len = mvwinnstr(win->content, y, x, buf, len);
if (len != ERR)
buf[len] = 0;
else
GB.Free((void **) &buf);
*ret = buf;
}
BEGIN_METHOD(Window_Get, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len)
int len;
char *ret;
len = VARGOPT(len, -1);
CWINDOW_get(THIS, VARG(x), VARG(y), len, &ret);
GB.ReturnNewZeroString(ret);
GB.Free((void **) &ret);
END_METHOD
BEGIN_METHOD_VOID(Window_Hide)
hide_panel(THIS->pan);
REFRESH();
END_METHOD
static void CWINDOW_locate(CWINDOW *win, int x, int y)
{
MAKE_CURSOR(win->content, x, y);
CHECK_RAISE_COORDS(win->content, x, y);
wmove(win->content, y, x);
}
BEGIN_METHOD(Window_Locate, GB_INTEGER x; GB_INTEGER y)
CWINDOW_locate(THIS, VARG(x), VARG(y));
REFRESH();
END_METHOD
static void CWINDOW_move(CWINDOW *win, int x, int y)
{
MAKE_COORDS(win->main, x, y);
CHECK_RAISE_COORDS(stdscr, x, y);
move_panel(win->pan, y, x);
#if 0
if (HAS_BORDER) {
mvwin(THIS->content, y + 1, x + 1);
WINDOW_draw_border(THIS, 1);
}
#endif
}
BEGIN_METHOD(Window_Move, GB_INTEGER x; GB_INTEGER y)
CWINDOW_move(THIS, VARGOPT(x, -1), VARGOPT(y, -1));
REFRESH();
END_METHOD
BEGIN_METHOD_VOID(Window_Center)
int x, y;
x = (COLS - getmaxx(THIS->main)) / 2;
y = (LINES - getmaxy(THIS->main)) / 2;
CWINDOW_move(THIS, x, y);
REFRESH();
END_METHOD
static void CWINDOW_print(CWINDOW *win, char *str, int x, int y,
attr_t attr, int pair)
{
int width;
char *p, *q;
attr_t asave; short psave;
wattr_get(win->content, &asave, &psave, NULL);
if (attr == -1)
attr = asave;
if (pair == -1)
pair = psave;
wattr_set(win->content, attr, pair, NULL);
p = str;
do {
CWINDOW_locate(win, x, y);
width = strlen(p);
if (!win->wrap)
width = MIN(width, getmaxx(win->content) - x);
/* waddnstr, being subsequent calls to waddch, rests at the
* end of the current line but we want to go to the next
* one. */
width = MIN(width, getmaxx(win->content) *
(getmaxy(win->content) - y) - x);
if ((q = strchr(p, '\n')))
width = MIN(width, q - p);
waddnstr(win->content, p, width);
p += width;
x = getcurx(win->content);
y = getcury(win->content);
if (y == getmaxy(win->content) - 1)
break;
if (*p == '\n') {
y++;
p++;
}
if (*p)
x = 0;
} while (*p);
CWINDOW_locate(win, x, y);
wattr_set(win->content, asave, psave, NULL);
}
BEGIN_METHOD(Window_Print, GB_STRING text; GB_INTEGER x; GB_INTEGER y;
GB_INTEGER attr; GB_INTEGER pair)
char text[LENGTH(text) + 1];
strncpy(text, STRING(text), LENGTH(text));
text[LENGTH(text)] = 0;
CWINDOW_print(THIS, text, VARGOPT(x, -1), VARGOPT(y, -1),
VARGOPT(attr, -1), VARGOPT(pair, -1));
REFRESH();
END_METHOD
BEGIN_METHOD_VOID(Window_Raise)
top_panel(THIS->pan);
REFRESH();
END_METHOD
BEGIN_METHOD(Window_PrintCenter, GB_STRING text; GB_INTEGER attr;
GB_INTEGER pair)
int lines = 1;
int x, y;
char text[LENGTH(text) + 1];
char *p, *q;
attr_t attr = VARGOPT(attr, -1);
short pair = VARGOPT(pair, -1);
memcpy(text, STRING(text), LENGTH(text));
text[LENGTH(text)] = 0;
p = text;
while ((q = strchr(p, '\n'))) {
lines++;
p = q + 1;
}
p = text;
y = (getmaxy(THIS->content) - lines) / 2;
while (lines--) {
if (!lines) {
x = (getmaxx(THIS->content) - strlen(p)) / 2;
CWINDOW_print(THIS, p, x, y, attr, pair);
} else {
q = strchr(p, '\n');
if (q == p + 1) {
y++;
continue;
}
*q = 0;
x = (getmaxx(THIS->content) - (q - p)) / 2;
CWINDOW_print(THIS, p, x, y, attr, pair);
y++;
p = q + 1;
*q = '\n';
}
}
REFRESH();
END_METHOD
static void CWINDOW_draw_border(CWINDOW *win)
{
switch (win->border) {
case BORDER_NONE:
wborder(win->main, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
break;
case BORDER_ASCII:
wborder(win->main, '|', '|', '-', '-', '+', '+', '+', '+');
break;
case BORDER_ACS:
box(win->main, 0, 0);
break;
}
if (win->border != BORDER_NONE && win->caption) {
int width = getmaxx(win->main) - 2;
width = MIN(width, strlen(win->caption));
mvwaddnstr(win->main, 0, 1, win->caption, width);
}
}
static void CWINDOW_resize(CWINDOW *win, int w, int h)
{
int x, y;
MAKE_DIM(win->main, w, h);
getbegyx(win->main, y, x);
if (win->has_border) {
w += 2;
h += 2;
}
/* XXX: Not rather raise an error? */
w = MIN(w, COLS - x);
h = MIN(h, LINES - y);
/* TODO: With the auto-created Window it does not work properly in
* Screen_Resize() from within xterm (my testcase) */
if (win->border)
wborder(win->main, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
wresize(win->main, h, w);
if (win->has_border)
wresize(win->content, h - 2, w - 2);
replace_panel(win->pan, win->main);
CWINDOW_draw_border(win);
}
BEGIN_METHOD(Window_Resize, GB_INTEGER w; GB_INTEGER h)
CWINDOW_resize(THIS, VARGOPT(w, -1), VARGOPT(h, -1));
REAL_REFRESH();
END_METHOD
BEGIN_METHOD_VOID(Window_SetFullscreen)
CWINDOW_move(THIS, 0, 0);
CWINDOW_resize(THIS, COLS, LINES);
REFRESH();
END_METHOD
BEGIN_METHOD(Window_Read, GB_INTEGER timeout)
int t;
int ret;
t = VARGOPT(timeout, -1);
ret = INPUT_get(t);
GB.ReturnInteger(ret);
END_METHOD
BEGIN_METHOD_VOID(Window_ReadLine)
char line[256]; /* XXX: Must be enough! */
int ret;
bzero(line, sizeof(line));
ret = wgetnstr(THIS->main, line, sizeof(line) - 1);
if (ret == ERR) {
GB.ReturnNull();
return;
}
GB.ReturnNewZeroString(line);
END_METHOD
BEGIN_METHOD_VOID(Window_Show)
show_panel(THIS->pan);
REFRESH();
END_METHOD
static void CWINDOW_setfocus(CWINDOW *win)
{
if (!_curwin)
INPUT_init();
_curwin = win;
}
BEGIN_METHOD_VOID(Window_SetFocus)
CWINDOW_setfocus(THIS);
END_METHOD
BEGIN_PROPERTY(Window_Attributes)
if (READ_PROPERTY) {
attr_t attr;
short pair;
wattr_get(THIS->content, &attr, &pair, NULL);
GB.ReturnInteger(attr);
return;
}
wattrset(THIS->content, VPROP(GB_INTEGER));
END_PROPERTY
/* This is *not* a shortcut to setting Window.{Fore,Back}ground. The latter
* two set the colour of everything in the window. This function only sets
* the colour pair for all subsequent writes! */
BEGIN_PROPERTY(Window_Pair)
chtype bkgd;
bkgd = getbkgd(THIS->content);
if (READ_PROPERTY) {
GB.ReturnInteger(PAIR_NUMBER(bkgd & A_COLOR));
return;
}
bkgd = COLOR_PAIR(VPROP(GB_INTEGER));
wbkgdset(THIS->content, bkgd);
END_PROPERTY
#define COLOR_METHOD(r, f, b) \
short pair, fg, bg; \
attr_t attr; \
\
wattr_get(THIS->content, &attr, &pair, NULL); \
pair_content(pair, &fg, &bg); \
if (READ_PROPERTY) { \
GB.ReturnInteger(r); \
return; \
} \
pair = CPAIR_get(f, b); \
if (pair == -1) { \
GB.Error(GB_ERR_BOUND); \
return; \
} \
wbkgd(THIS->content, COLOR_PAIR(pair) | attr | ' '); \
REFRESH();
/*
* One should be careful when using these two properties. wbkgd() changes
* fore- *and* background of *all* characters in the window. So setting
* the background may result in resetting even the foreground of differently
* coloured characters.
*
* Best is to only set Background/Foreground once at the beginning of the
* program when using colours extensively during the program.
*/
BEGIN_PROPERTY(Window_Background)
COLOR_METHOD(bg, fg, VPROP(GB_INTEGER));
END_PROPERTY
BEGIN_PROPERTY(Window_Foreground)
COLOR_METHOD(fg, VPROP(GB_INTEGER), bg);
END_PROPERTY
BEGIN_PROPERTY(Window_Border)
if (READ_PROPERTY) {
GB.ReturnInteger(THIS->border);
return;
}
THIS->border = VPROP(GB_INTEGER);
CWINDOW_draw_border(THIS);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(Window_Buffered)
if (READ_PROPERTY) {
GB.ReturnBoolean(THIS->buffered);
return;
}
THIS->buffered = VPROP(GB_BOOLEAN);
END_PROPERTY
BEGIN_PROPERTY(Window_Caption)
if (READ_PROPERTY) {
GB.ReturnString(THIS->caption);
return;
}
if (THIS->caption)
GB.FreeString(&THIS->caption);
THIS->caption = GB.NewZeroString(PSTRING());
CWINDOW_draw_border(THIS);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(Window_CursorX)
if (READ_PROPERTY) {
GB.ReturnInteger(getcurx(THIS->content));
return;
}
CWINDOW_locate(THIS, VPROP(GB_INTEGER), -1);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(Window_CursorY)
if (READ_PROPERTY) {
GB.ReturnInteger(getcury(THIS->content));
return;
}
CWINDOW_locate(THIS, -1, VPROP(GB_INTEGER));
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(Window_Height)
if (READ_PROPERTY) {
GB.ReturnInteger(getmaxy(THIS->content));
return;
}
CWINDOW_resize(THIS, -1, VPROP(GB_INTEGER));
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(Window_Wrap)
if (READ_PROPERTY) {
GB.ReturnBoolean(THIS->wrap);
return;
}
THIS->wrap = VPROP(GB_BOOLEAN);
END_PROPERTY
BEGIN_PROPERTY(Window_Width)
if (READ_PROPERTY) {
GB.ReturnInteger(getmaxx(THIS->content));
return;
}
CWINDOW_resize(THIS, VPROP(GB_INTEGER), -1);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(Window_X)
if (READ_PROPERTY) {
GB.ReturnInteger(getbegx(THIS->main));
return;
}
CWINDOW_move(THIS, VPROP(GB_INTEGER), -1);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(Window_Y)
if (READ_PROPERTY) {
GB.ReturnInteger(getbegy(THIS->main));
return;
}
CWINDOW_move(THIS, -1, VPROP(GB_INTEGER));
REFRESH();
END_PROPERTY
GB_DESC CWindowDesc[] = {
GB_DECLARE("Window", sizeof(CWINDOW)),
GB_AUTO_CREATABLE(),
GB_EVENT("Read", NULL, NULL, &EVENT_Read),
/* Methods */
GB_METHOD("_new", NULL, Window_new, "[(BorderFrame)b(X)i(Y)i(W)i(H)i]"),
GB_METHOD("_free", NULL, Window_free, NULL),
GB_METHOD("_get", ".Char.Attrs", Window_get, "(Y)i(X)i"),
GB_METHOD("Ask", "s", Window_Ask, "(Opts)s[(Tries)i]"),
GB_METHOD("Read", "i", Window_Read, "[(Timeout)i]"),
GB_METHOD("ReadLine", "s", Window_ReadLine, NULL),
GB_METHOD("Drain", NULL, Window_Drain, NULL),
GB_METHOD("Get", "s", Window_Get, "[(X)i(Y)i(Len)i]"),
GB_METHOD("Clear", NULL, Window_Clear, NULL),
GB_METHOD("Cls", NULL, Window_Clear, NULL),
GB_METHOD("Lower", NULL, Window_Lower, NULL),
GB_METHOD("Raise", NULL, Window_Raise, NULL),
GB_METHOD("Hide", NULL, Window_Hide, NULL),
GB_METHOD("Show", NULL, Window_Show, NULL),
GB_METHOD("DrawHLine", NULL, Window_DrawHLine,"(X)i(Y)i(Len)i(C)s"),
GB_METHOD("DrawVLine", NULL, Window_DrawVLine,"(X)i(Y)i(Len)i(C)s"),
GB_METHOD("Print", NULL, Window_Print, "(Text)s[(X)i(Y)i(Attr)i(Pair)i]"),
GB_METHOD("PrintCenter", NULL, Window_PrintCenter, "(Text)s[(Attr)i(Pair)i]"),
GB_METHOD("Locate", NULL, Window_Locate, "(X)i(Y)i"),
GB_METHOD("Move", NULL, Window_Move, "[(X)i(Y)i]"),
GB_METHOD("Center", NULL, Window_Center, NULL),
GB_METHOD("Resize", NULL, Window_Resize, "[(W)i(H)i]"),
GB_METHOD("SetFullscreen", NULL, Window_SetFullscreen, NULL),
GB_METHOD("SetFocus", NULL, Window_SetFocus, NULL),
GB_PROPERTY("Attributes", "i", Window_Attributes),
GB_PROPERTY("Background", "i", Window_Background),
GB_PROPERTY("Paper", "i", Window_Background),
GB_PROPERTY("Foreground", "i", Window_Foreground),
GB_PROPERTY("Pen", "i", Window_Foreground),
GB_PROPERTY("Pair", "i", Window_Pair),
GB_PROPERTY("Border", "i", Window_Border),
GB_PROPERTY("Buffered", "b", Window_Buffered),
GB_PROPERTY("Caption", "s", Window_Caption),
GB_PROPERTY("CursorX", "i", Window_CursorX),
GB_PROPERTY("CursorY", "i", Window_CursorY),
/* GB_PROPERTY("ClientHeight", ...), etc */
GB_PROPERTY("Height", "i", Window_Height),
GB_PROPERTY("H", "i", Window_Height),
GB_PROPERTY("Width", "i", Window_Width),
GB_PROPERTY("W", "i", Window_Width),
GB_PROPERTY("Wrap", "b", Window_Wrap),
GB_PROPERTY("X", "i", Window_X),
GB_PROPERTY("Y", "i", Window_Y),
GB_END_DECLARE
};
/*
* Attrs
*/
GB_DESC CWindowAttrsDesc[] = {
GB_DECLARE("Attr", 0),
GB_NOT_CREATABLE(),
GB_CONSTANT("Normal", "i", A_NORMAL),
GB_CONSTANT("Underline", "i", A_UNDERLINE),
GB_CONSTANT("Reverse", "i", A_REVERSE),
GB_CONSTANT("Blink", "i", A_BLINK),
GB_CONSTANT("Bold", "i", A_BOLD),
GB_END_DECLARE
};
/*
* .Char.Attrs
*/
/* Seemingly, chgat() doesn't mark the window dirty. We use wtouchln() and
* wsyncup(). */
#define CHAR_ATTR_METHOD(a) \
int ox, oy; \
chtype ch; \
\
getyx(THIS->content, oy, ox); \
ch = mvwinch(THIS->content, THIS->pos.line, THIS->pos.col); \
if (READ_PROPERTY) { \
GB.ReturnBoolean(ch & a); \
return; \
} \
if (VPROP(GB_BOOLEAN)) \
wchgat(THIS->content, 1, (ch & A_ATTRIBUTES) | a, \
PAIR_NUMBER(ch), NULL); \
else \
wchgat(THIS->content, 1, (ch & A_ATTRIBUTES) & ~a, \
PAIR_NUMBER(ch), NULL); \
wtouchln(THIS->content, THIS->pos.line, 1, 1); \
wsyncup(THIS->content); \
wmove(THIS->content, oy, ox); \
REFRESH();
BEGIN_PROPERTY(CharAttrs_Normal)
int ox, oy;
chtype ch;
getyx(THIS->content, oy, ox);
ch = mvwinch(THIS->content, THIS->pos.line, THIS->pos.col);
if (READ_PROPERTY) {
GB.ReturnBoolean((ch & A_ATTRIBUTES) == A_NORMAL);
return;
}
if (VPROP(GB_BOOLEAN))
wchgat(THIS->content, 1, A_NORMAL, PAIR_NUMBER(ch), NULL);
wtouchln(THIS->content, THIS->pos.line, 1, 1);
wmove(THIS->content, oy, ox);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(CharAttrs_Underline)
CHAR_ATTR_METHOD(A_UNDERLINE);
END_PROPERTY
BEGIN_PROPERTY(CharAttrs_Reverse)
CHAR_ATTR_METHOD(A_REVERSE);
END_PROPERTY
BEGIN_PROPERTY(CharAttrs_Blink)
CHAR_ATTR_METHOD(A_BLINK);
END_PROPERTY
BEGIN_PROPERTY(CharAttrs_Bold)
CHAR_ATTR_METHOD(A_BOLD);
END_PROPERTY
#define CHAR_COLOR_METHOD(r, f, b) \
short pair; \
int ox, oy; \
chtype ch; \
short fg, bg; \
\
getyx(THIS->content, oy, ox); \
ch = mvwinch(THIS->content, THIS->pos.line, THIS->pos.col);\
pair = PAIR_NUMBER(ch & A_COLOR); \
pair_content(pair, &fg, &bg); \
if (READ_PROPERTY) { \
GB.ReturnInteger(r); \
return; \
} \
pair = CPAIR_get(f, b); \
if (pair == -1) { \
GB.Error(GB_ERR_BOUND); \
return; \
} \
wchgat(THIS->content, 1, (ch & A_ATTRIBUTES), pair, NULL);\
wtouchln(THIS->content, THIS->pos.line, 1, 1); \
wsyncup(THIS->content); \
wmove(THIS->content, oy, ox); \
REFRESH();
BEGIN_PROPERTY(CharAttrs_Background)
CHAR_COLOR_METHOD(bg, fg, VPROP(GB_INTEGER));
END_PROPERTY
BEGIN_PROPERTY(CharAttrs_Foreground)
CHAR_COLOR_METHOD(fg, VPROP(GB_INTEGER), bg);
END_PROPERTY
GB_DESC CCharAttrsDesc[] = {
GB_DECLARE(".Char.Attrs", 0),
GB_VIRTUAL_CLASS(),
GB_PROPERTY("Normal", "b", CharAttrs_Normal),
GB_PROPERTY("Underline", "b", CharAttrs_Underline),
GB_PROPERTY("Reverse", "b", CharAttrs_Reverse),
GB_PROPERTY("Blink", "b", CharAttrs_Blink),
GB_PROPERTY("Bold", "b", CharAttrs_Bold),
GB_PROPERTY("Background", "i", CharAttrs_Background),
GB_PROPERTY("Foreground", "i", CharAttrs_Foreground),
GB_END_DECLARE
};
/*
* Border
*/
GB_DESC CBorderDesc[] = {
GB_DECLARE("Border", 0),
GB_NOT_CREATABLE(),
GB_CONSTANT("None", "i", BORDER_NONE),
GB_CONSTANT("Ascii", "i", BORDER_ASCII),
GB_CONSTANT("ACS", "i", BORDER_ACS),
GB_END_DECLARE
};