view ui.c @ 466:796508f828f6

Refactor much of the "windowing" UI code into a new module, ui.[ch]
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 26 May 2012 06:56:18 +0300
parents
children 56689f94e827
line wrap: on
line source

/*
 * NNChat - Custom chat client for NewbieNudes.com chatrooms
 * Written by Matti 'ccr' Hämäläinen
 * (C) Copyright 2008-2012 Tecnic Software productions (TNSP)
 */
#include "util.h"
#include "ui.h"

nn_window_t *chatWindows[SET_MAX_WINDOWS],
            *currWin = NULL;

BOOL    cursesInit = FALSE;
int     cursorVisible = ERR;
WINDOW  *mainWin = NULL,
        *statusWin = NULL,
        *editWin = NULL;


static nn_window_t *nn_window_new(const char *id)
{
    nn_window_t *res = th_calloc(1, sizeof(nn_window_t));

    if (res == NULL) return NULL;

    res->data = th_ringbuf_new(NN_BACKBUF_LEN, th_free);
    if (res->data == NULL)
    {
        th_free(res);
        return NULL;
    }

    res->id = th_strdup(id);

    return res;
}


static void nn_window_free(nn_window_t *win)
{
    if (win != NULL)
    {
        th_ringbuf_free(win->data);
        th_free(win->id);
        th_free(win);
    }
}


nn_window_t *nnwin_main_window()
{
    return chatWindows[0];
}


nn_window_t *nnwin_get(const int index)
{
    if (index >= 1 && index <= SET_MAX_WINDOWS)
        return chatWindows[index - 1];
    else
        return 0;
    
}


BOOL nnwin_init(int delay)
{
    if (LINES < 0 || LINES > 1000) LINES = 24;
    if (COLS < 0 || COLS > 1000) COLS = 80;

    initscr();
    raw();
    keypad(stdscr, TRUE);
    noecho();
    meta(stdscr, TRUE);
    timeout(delay);
    cursorVisible = curs_set(0);

    if (has_colors())
    {
        start_color();

        init_pair( 1, COLOR_RED,     COLOR_BLACK);
        init_pair( 2, COLOR_GREEN,   COLOR_BLACK);
        init_pair( 3, COLOR_YELLOW,  COLOR_BLACK);
        init_pair( 4, COLOR_BLUE,    COLOR_BLACK);
        init_pair( 5, COLOR_MAGENTA, COLOR_BLACK);
        init_pair( 6, COLOR_CYAN,    COLOR_BLACK);
        init_pair( 7, COLOR_WHITE,   COLOR_BLACK);
        init_pair( 8, COLOR_BLACK,   COLOR_BLACK);

        init_pair(10, COLOR_BLACK,   COLOR_RED);
        init_pair(11, COLOR_WHITE,   COLOR_RED);
        init_pair(12, COLOR_GREEN,   COLOR_RED);
        init_pair(13, COLOR_YELLOW,  COLOR_RED);
        init_pair(14, COLOR_BLUE,    COLOR_RED);
        init_pair(15, COLOR_MAGENTA, COLOR_RED);
        init_pair(16, COLOR_CYAN,    COLOR_RED);
    }

    cursesInit = TRUE;

    if (!nnwin_init_windows())
        return FALSE;

#ifdef PDCURSES
        PDC_set_title("NNChat v" NN_VERSION);
#endif

    memset(chatWindows, 0, sizeof(chatWindows));
    chatWindows[0] = nn_window_new(NULL);
    currWin = chatWindows[0];
    
    return TRUE;
}


void nnwin_shutdown()
{
    int i;

    for (i = 0; i < SET_MAX_WINDOWS; i++)
        nn_window_free(chatWindows[i]);

    if (cursesInit)
    {
        if (cursorVisible != ERR)
            curs_set(cursorVisible);

        nnwin_close_windows();

        endwin();
        THMSG(1, "NCurses deinitialized.\n");
    }
}


nn_window_t *nnwin_find(const char *id)
{
    int i;

    for (i = 0; i < SET_MAX_WINDOWS; i++)
    {
        if (chatWindows[i] != NULL &&
            chatWindows[i]->id != NULL &&
            th_strcasecmp(id, chatWindows[i]->id) == 0)
            return chatWindows[i];
    }

    return NULL;
}


BOOL nnwin_open(const char *name, BOOL curwin)
{
    int i;
    nn_window_t *res;
    if (name == NULL)
        return FALSE;

    if ((res = nn_window_new(name)) == NULL)
        return FALSE;

    for (i = 1; i < SET_MAX_WINDOWS; i++)
        if (chatWindows[i] == NULL)
        {
            res->num = i;
            chatWindows[i] = res;
            if (curwin)
                currWin = res;
            return TRUE;
        }

    return FALSE;
}


void nnwin_close(nn_window_t *win)
{
    int i;
    if (win == NULL) return;

    for (i = 1; i < SET_MAX_WINDOWS; i++)
        if (chatWindows[i] == win)
        {
            chatWindows[i] = NULL;
            nn_window_free(win);
            return;
        }
}


void nnwin_update_statusline(char *optUserName, int optUserColor)
{
    char tmpStr[128];
    int i;

    if (statusWin == NULL) return;

    str_get_timestamp(tmpStr, sizeof(tmpStr), "%H:%M:%S");

    wbkgdset(statusWin, COLOR_PAIR(10));
    werase(statusWin);

    wattrset(statusWin, A_BOLD | COLOR_PAIR(11));
    mvwaddstr(statusWin, 0, 1, tmpStr);

    wattrset(statusWin, A_BOLD | COLOR_PAIR(13));
    waddstr(statusWin, " | ");
    wattrset(statusWin, A_BOLD | COLOR_PAIR(16));
    waddstr(statusWin, optUserName);
    wattrset(statusWin, A_BOLD | COLOR_PAIR(13));

    wattrset(statusWin, A_BOLD | COLOR_PAIR(13));
    waddstr(statusWin, " | ");
    wattrset(statusWin, A_BOLD | COLOR_PAIR(11));
    snprintf(tmpStr, sizeof(tmpStr), "#%06x", optUserColor);
    waddstr(statusWin, tmpStr);

    wattrset(statusWin, A_BOLD | COLOR_PAIR(13));
    waddstr(statusWin, " | WIN: ");
    snprintf(tmpStr, sizeof(tmpStr), "%d: %s / %d",
        currWin->num + 1,
        currWin->id != NULL ? currWin->id : "MAIN",
        currWin->pos);
    waddstr(statusWin, tmpStr);

    wattrset(statusWin, A_BOLD | COLOR_PAIR(13));
    waddstr(statusWin, " | ");
    wattrset(statusWin, A_BOLD | COLOR_PAIR(11));

    for (i = 0; i < SET_MAX_WINDOWS; i++)
    {
        if (chatWindows[i] != NULL && chatWindows[i]->dirty)
        {
            snprintf(tmpStr, sizeof(tmpStr), "%d ", i + 1);
            waddstr(statusWin, tmpStr);
        }
    }

    wrefresh(statusWin);
}


void nnwin_update_editbuf(nn_editbuf_t *buf)
{
    char *tmp;
    if (editWin == NULL || buf == NULL) return;

    buf->data[buf->len] = 0;
    tmp = nn_username_decode(th_strdup(buf->data));

    werase(editWin);

    wattrset(editWin, A_NORMAL);

    if (buf->pos < buf->len)
    {
        waddnstr(editWin, tmp, buf->pos);
        wattrset(editWin, A_REVERSE);
        waddch(editWin, tmp[buf->pos]);
        wattrset(editWin, A_NORMAL);
        waddnstr(editWin, tmp + buf->pos + 1, buf->len - buf->pos - 1);
    }
    else
    {
        waddnstr(editWin, tmp, buf->len);
        wattrset(editWin, A_REVERSE);
        waddch(editWin, ' ');
        wattrset(editWin, A_NORMAL);
    }
    wrefresh(editWin);
    th_free(tmp);
}


int nnwin_print(WINDOW *win, const char *fmt)
{
    const char *s = fmt;
    int col = 0;

    while (*s)
    {
        if (*s == '½')
        {
            s++;
            if (*s == '½')
            {
                waddch(win, ((unsigned char) *s) | col);
                s++;
            }
            else
            {
                memcpy(&col, s, sizeof(int));
                s += sizeof(int);
            }
        }
        else
        {
            waddch(win, ((unsigned char) *s) | col);
            s++;
        }
    }
    return 0;
}


#define QPUTCH(ch) th_vputch(&(win->buf), &(win->bufsize), &(win->len), ch)

int nnwin_print_buf(nn_window_t *win, const char *fmt)
{
    const char *s = fmt;
    int col = 0;
    while (*s)
    {
        if (*s == '½')
        {
            s++;
            if (*s == '½')
            {
                QPUTCH(*s);
                QPUTCH(*s);
                win->chlen++;
            }
            else
            {
                int val = 0;
                while (*s >= '0' && *s <= '9')
                {
                    val *= 10;
                    val += (*s - '0');
                    s++;
                }
                if (*s != '½') return -1;

                if (val < 9)
                    col = A_DIM | COLOR_PAIR(val);
                else if (val < 30)
                    col = A_BOLD | COLOR_PAIR(val - 9);

                QPUTCH('½');

                if (!th_growbuf(&(win->buf), &(win->bufsize), &(win->len), sizeof(int)))
                    return -2;

                memcpy(win->buf + win->len, &col, sizeof(int));
                win->len += sizeof(int);
            }
        }
        else if (*s == '\n')
        {
            QPUTCH('\n');
            QPUTCH(0);
            th_ringbuf_add(win->data, win->buf);
            win->buf = NULL;
            win->chlen = 0;
            win->dirty = TRUE;
        }
        else if (*s != '\r')
        {
            QPUTCH((unsigned char) *s == 255 ? ' ' : *s);
            win->chlen++;
        }

        s++;
    }

    return 0;
}


void nnwin_update_all(void)
{
    if (mainWin) redrawwin(mainWin);
    if (statusWin) redrawwin(statusWin);
    if (editWin) redrawwin(editWin);
}


char *nnwin_prompt_requester(WINDOW *win, const char *info, BOOL allowEmpty)
{
    char tmpBuf[512], *ptr;
    size_t pos;
    int curSave = curs_set(1);

    echo();
    waddstr(win, info);
    wgetnstr(win, tmpBuf, sizeof(tmpBuf) - 1);
    noecho();

    if (curSave != ERR)
        curs_set(curSave);

    for (pos = strlen(tmpBuf) - 1; pos > 0 && th_isspace(tmpBuf[pos]); pos--)
        tmpBuf[pos] = 0;

    ptr = str_trim_left(tmpBuf);

    if (allowEmpty || strlen(ptr) > 0)
        return th_strdup(ptr);
    else
        return NULL;
}


BOOL nnwin_update_main(BOOL force)
{
    int h, offs;
    qringbuf_t *buf;

    // Check pointers
    if (mainWin == NULL || currWin == NULL)
        return FALSE;

    // Check if update is forced or if the window is dirty
    if (!force && !currWin->dirty)
        return FALSE;

    // Compute how many lines from backbuffer fit on the screen
    buf = currWin->data;
    h = getmaxy(mainWin);

    // Clear and redraw window
    werase(mainWin);
    scrollok(mainWin, 1);
    for (offs = buf->size - h - currWin->pos; offs >= 0 && offs < buf->size - currWin->pos && offs < buf->size; offs++)
    {
        if (buf->data[offs] != NULL)
            nnwin_print(mainWin, buf->data[offs]);
    }

    currWin->dirty = FALSE;
    wrefresh(mainWin);
    return TRUE;
}


void nnwin_close_windows(void)
{
    if (mainWin) delwin(mainWin);
    if (statusWin) delwin(statusWin);
    if (editWin) delwin(editWin);
}


BOOL nnwin_init_windows(void)
{
    int w, h;

    getmaxyx(stdscr, h, w);

    nnwin_close_windows();

    mainWin = subwin(stdscr, h - 4, w, 0, 0);
    statusWin = subwin(stdscr, 1, w, h - 4, 0);
    editWin = subwin(stdscr, 3, w, h - 3, 0);

    if (mainWin == NULL || statusWin == NULL || editWin == NULL)
        return FALSE;

    return TRUE;
}