/*-
* Copyright (c) 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Roy Marples.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Terminals with real soft labels have NOT been tested.
* If you have such a device, please let us know so this comment
* can be adjusted. */
/* POSIX says that each label can be up to 8 columns.
* However, our implementation can allow labels to expand beyond that. */
//#define SLK_SIZE_DYNAMIC
#ifdef SLK_SIZE_DYNAMIC
#define SLK_SIZE MAX_SLK_LABEL
#else
#define SLK_SIZE MAX_SLK_COLS
#endif
static int slk_fmt = SLK_FMT_INVAL; /* fmt of slk_init */
/* Safe variants of public functions. */
static int __slk_attroff(SCREEN *, const chtype);
static int __slk_attron(SCREEN *, const chtype);
static int __slk_attrset(SCREEN *, const chtype);
#ifdef HAVE_WCHAR
static int __slk_attr_off(SCREEN *, const attr_t, void *);
static int __slk_attr_on(SCREEN *, const attr_t, void *);
static int __slk_attr_set(SCREEN *, const attr_t, short, void *opt);
static int __slk_color(SCREEN *, short);
#endif
static int __slk_clear(SCREEN *);
static char *__slk_label(SCREEN *, int);
static int __slk_restore(SCREEN *);
static int __slk_set(SCREEN *, int, const char *, int);
static int __slk_touch(SCREEN *);
#ifdef HAVE_WCHAR
static int __slk_wset(SCREEN *, int, const wchar_t *, int);
#endif
/* Internal engine parts. */
static int __slk_ripoffline(WINDOW *, int);
static int __slk_set_finalise(SCREEN *, int);
static int __slk_draw(SCREEN *, int);
static int __slk_redraw(SCREEN *);
/*
* slk_attrset --
* Set attributes and color pair on ripped off slk window.
*/
int
slk_attrset(const chtype attr)
{
return __slk_attrset(_cursesi_screen, attr);
}
#ifdef HAVE_WCHAR
/*
* slk_attr_set --
* Set wide attributes and color pair on ripped off slk window.
*/
int
slk_attr_set(const attr_t attr, short pair, void *opt)
{
/*
* slk_label --
* Return a pointer to the saved label for key labnum.
*/
char *
slk_label(int labnum)
{
return __slk_label(_cursesi_screen, labnum);
}
/*
* slk_wnoutrefresh --
* Add the contents of the ripped off slk window to the virtual window.
*/
int
slk_noutrefresh(void)
{
return __slk_noutrefresh(_cursesi_screen);
}
/*
* slk_refresh --
* Force a refresh for the ripped off slk window.
*/
int
slk_refresh(void)
{
if (slk_noutrefresh() == ERR)
return ERR;
return doupdate();
}
/*
* slk_restore --
* Retore slk to the screen after a slk_clear.
*/
int
slk_restore(void)
{
return __slk_restore(_cursesi_screen);
}
/*
* slk_set --
* Sets the text of the label specified by labnum
* and how it is displayed.
*/
int
slk_set(int labnum, const char *label, int justify)
{
/*
* slk_touch --
* Sets the ripped off slk window as modified.
*/
int
slk_touch(void)
{
return __slk_touch(_cursesi_screen);
}
#ifdef HAVE_WCHAR
/*
* slk_wset --
* Sets the wide text of the label specified by labnum
* and how it is displayed.
*/
int
slk_wset(int labnum, const wchar_t *label, int justify)
{
#ifdef HAVE_WCHAR
/*
* __slk_attr_on --
* Test and set wide attributes on ripped off slk window.
*/
static int
__slk_attr_on(SCREEN *screen, const attr_t attr, void *opt)
{
#ifdef HAVE_WCHAR
/*
* __slk_attr_set --
* Set wide attributes and color pair on ripped off slk window.
*/
static int
__slk_attr_set(SCREEN *screen, const attr_t attr, short pair, void *opt)
{
/*
* __slk_restore --
* Retore slk to the screen after a slk_clear.
*/
static int
__slk_restore(SCREEN *screen)
{
if (screen == NULL)
return ERR;
screen->slk_hidden = false;
if (screen->is_term_slk) {
if (t_label_on(screen->term) == NULL)
return ERR;
return ti_putp(screen->term,
ti_tiparm(screen->term, t_label_on(screen->term)));
}
if (screen->slk_window == NULL)
return ERR;
if (__slk_redraw(screen) == ERR)
return ERR;
return wrefresh(screen->slk_window);
}
/*
* __slk_set --
* Sets the text of the label specified by labnum
* and how it is displayed.
*/
static int
__slk_set(SCREEN *screen, int labnum, const char *label, int justify)
{
struct __slk_label *l;
const char *end;
size_t len;
char *text;
#ifdef HAVE_WCHAR
wchar_t wc;
size_t wc_len;
#endif
/* Check args. */
if (screen == NULL || labnum < 1 || labnum > screen->slk_nlabels)
return ERR;
switch(justify) {
case SLK_JUSTIFY_LEFT:
case SLK_JUSTIFY_CENTER:
case SLK_JUSTIFY_RIGHT:
break;
default:
return ERR;
}
if (label == NULL)
label = "";
/* Skip leading whitespace. */
while(isspace((unsigned char)*label))
label++;
/* Grab end. */
end = label;
#ifdef HAVE_WCHAR
size_t endlen = strlen(end);
while (*end != '\0') {
wc_len = mbrtowc(&wc, end, endlen, &screen->sp);
if ((ssize_t)wc_len < 0)
return ERR;
if (!iswprint((wint_t)wc))
break;
end += wc_len;
endlen -= wc_len;
}
#else
while(isprint((unsigned char)*end))
end++;
#endif
len = end - label;
/* Take a backup, in-case we can grow the label. */
if ((text = strndup(label, len)) == NULL)
return ERR;
/* All checks out, assign. */
l = &screen->slk_labels[--labnum]; /* internal zero based index */
l->text = text;
l->justify = justify;
__slk_set_finalise(screen, labnum);
return OK;
}
/*
* __slk_touch --
* Sets the ripped off slk window as modified.
*/
static int
__slk_touch(SCREEN *screen)
{
#ifdef HAVE_WCHAR
/*
* __slk_wset --
* Sets the wide text of the label specified by labnum
* and how it is displayed.
*/
static int
__slk_wset(SCREEN *screen, int labnum, const wchar_t *label, int justify)
{
const wchar_t *olabel;
size_t len;
char *str;
int result = ERR;
if (screen->slk_window != NULL)
delwin(screen->slk_window);
for (i = 0; i < screen->slk_nlabels; i++)
free(screen->slk_labels[i].text);
free(screen->slk_labels);
}
/*
* __slk_ripoffline --
* ripoffline callback to accept a WINDOW to create our keys.
*/
static int
__slk_ripoffline(WINDOW *window, int cols)
{
/*
* __slk_resize --
* Size and position the labels in the ripped off slk window.
*/
int
__slk_resize(SCREEN *screen, int cols)
{
int x = 0;
struct __slk_label *l;
if (screen == NULL)
return ERR;
if (screen->is_term_slk || screen->slk_nlabels == 0)
return OK;
half = screen->slk_nlabels / 2;
for (i = 0; i < screen->slk_nlabels; i++) {
(l++)->x = x;
x += screen->slk_label_len;
/* Split labels in half */
if (i == half - 1)
x = cols - (screen->slk_label_len * half) + 1;
}
break;
}
}
/* Write text to the labels. */
for (x = 0; x < screen->slk_nlabels; x++)
__slk_set_finalise(screen, x);
return __slk_redraw(screen);
}
/*
* __slk_set_finalise --
* Does the grunt work of positioning and sizing the text in the label.
*/
static int
__slk_set_finalise(SCREEN *screen, int labnum)
{
struct __slk_label *l;
size_t spc, len, width, x;
char *p;
l = &screen->slk_labels[labnum];
spc = screen->slk_label_len;
#ifdef HAVE_WCHAR
len = 0;
width = 0;
if (l->text != NULL) {
size_t plen;
p = l->text;
plen = strlen(l->text);
while (*p != '\0') {
size_t mblen;
wchar_t wc;
int w;
mblen = mbrtowc(&wc, p, plen, &screen->sp);
if ((ssize_t)mblen < 0)
return ERR;
w = wcwidth(wc);
if (width + w > spc)
break;
width += w;
len += mblen;
p += mblen;
plen -= mblen;
}
}
#else
len = l->text == NULL ? 0 : strlen(l->text);
if (len > spc)
len = spc;
width = len;
#endif
switch(l->justify) {
case SLK_JUSTIFY_LEFT:
x = 0;
break;
case SLK_JUSTIFY_CENTER:
x = (spc - width) / 2;
if (x + width > spc)
x--;
break;
case SLK_JUSTIFY_RIGHT:
x = spc - width;
break;
default:
return ERR; /* impossible */
}
p = l->label;
if (x != 0) {
memset(p, ' ', x);
p += x;
spc -= x;
}
if (len != 0) {
memcpy(p, l->text, len);
p += len;
spc -= width;
}
if (spc != 0) {
memset(p, ' ', spc);
p += spc;
}
*p = '\0'; /* Terminate for plab_norm. */
return __slk_draw(screen, labnum);
}
/*
* __slk_draw --
* Draws the specified key.
*/
static int
__slk_draw(SCREEN *screen, int labnum)
{
const struct __slk_label *l;
int retval, inc, lcnt, tx;
char ts[MB_LEN_MAX];
#ifdef HAVE_WCHAR
cchar_t cc;
wchar_t wc[2];
#endif