#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <gtcaca/win_compat.h>
#else
#include <unistd.h>
#endif
#include <caca.h>
#include <gtcaca/window.h>
#include <gtcaca/main.h>
#define MAX_FOCUSABLE_CHILDREN 64
static int _gtcaca_widget_is_focusable(gtcaca_widget_t *widget)
{
switch (widget->type) {
case GTCACA_WIDGET_BUTTON:
case GTCACA_WIDGET_TEXTLIST:
case GTCACA_WIDGET_ENTRY:
case GTCACA_WIDGET_CHECKBOX:
case GTCACA_WIDGET_RADIOBUTTON:
case GTCACA_WIDGET_COMBOBOX:
case GTCACA_WIDGET_TEXTVIEW:
case GTCACA_WIDGET_SCALE:
case GTCACA_WIDGET_SPINBUTTON:
case GTCACA_WIDGET_SWITCH:
case GTCACA_WIDGET_EXPANDER:
case GTCACA_WIDGET_EDITOR:
case GTCACA_WIDGET_TREE:
case GTCACA_WIDGET_TABLE:
case GTCACA_WIDGET_MAP:
return 1;
default:
return 0;
}
}
gtcaca_window_widget_t *gtcaca_window_new(gtcaca_widget_t *parent, const char *window_title, int x, int y, int width, int height)
{
gtcaca_window_widget_t *win;
win = malloc(sizeof(gtcaca_window_widget_t));
if (!win) {
fprintf(stderr, "Error: Cannot allocate new window\n");
return NULL;
}
win->id = gtcaca_get_newid();
win->has_focus = 1;
win->is_visible = 1;
win->window_title = window_title ? strdup(window_title) : NULL;
win->x = x;
win->y = y;
win->width = width;
win->height = height;
win->type = GTCACA_WIDGET_WINDOW;
win->parent = parent;
win->children = NULL;
win->focused_child = NULL;
win->default_widget = NULL;
win->close_anim = GTCACA_WINDOW_ANIM_NONE;
win->close_anim_target = NULL;
win->color_focus_bg = gmo.theme.windowfocus.bg;
win->color_focus_fg = gmo.theme.windowfocus.fg;
win->color_nonfocus_bg = gmo.theme.window.bg;
win->color_nonfocus_fg = gmo.theme.window.fg;
gtcaca_window_set_focus(win);
CDL_APPEND(gmo.widgets_list, (gtcaca_widget_t *)win);
gtcaca_window_draw(win);
return win;
}
gtcaca_window_widget_t *gtcaca_window_new_centered(gtcaca_widget_t *parent, const char *window_title, int width, int height)
{
int cw = caca_get_canvas_width(gmo.cv);
int ch = caca_get_canvas_height(gmo.cv);
int x = (cw - width) / 2;
int y = (ch - height) / 2;
if (x < 0) x = 0;
if (y < 0) y = 0;
return gtcaca_window_new(parent, window_title, x, y, width, height);
}
void gtcaca_window_draw(gtcaca_window_widget_t *win)
{
gtcaca_widget_colorize(GTCACA_WIDGET(win));
caca_fill_box(gmo.cv, win->x, win->y, win->width, win->height, ' ');
caca_draw_cp437_box(gmo.cv, win->x, win->y, win->width, win->height);
if (win->window_title) {
caca_printf(gmo.cv, win->x + 2, win->y, "| %s |", win->window_title);
}
}
void gtcaca_window_set_focus(gtcaca_window_widget_t *win)
{
gtcaca_widget_t *widget;
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget->type == GTCACA_WIDGET_WINDOW ||
(widget->parent && widget->parent->type == GTCACA_WIDGET_WINDOW)) {
widget->has_focus = 0;
}
}
win->has_focus = 1;
if (win->focused_child) {
win->focused_child->has_focus = 1;
} else {
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget->parent == (gtcaca_widget_t *)win && _gtcaca_widget_is_focusable(widget)) {
widget->has_focus = 1;
win->focused_child = widget;
break;
}
}
}
}
gtcaca_window_widget_t *gtcaca_window_get_current_focus(void)
{
gtcaca_widget_t *widget;
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget->type == GTCACA_WIDGET_WINDOW) {
if (widget->has_focus) return (gtcaca_window_widget_t *)widget;
}
}
return NULL;
}
gtcaca_window_widget_t *gtcaca_window_get_next(gtcaca_window_widget_t *win)
{
gtcaca_widget_t *widget;
int found = 0;
CDL_FOREACH(gmo.widgets_list, widget) {
if (found && widget->type == GTCACA_WIDGET_WINDOW)
return (gtcaca_window_widget_t *)widget;
if (widget == (gtcaca_widget_t *)win)
found = 1;
}
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget->type == GTCACA_WIDGET_WINDOW)
return (gtcaca_window_widget_t *)widget;
}
return NULL;
}
gtcaca_window_widget_t *gtcaca_window_get_prev(gtcaca_window_widget_t *win)
{
gtcaca_widget_t *widget;
gtcaca_window_widget_t *prev = NULL, *last = NULL;
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget->type == GTCACA_WIDGET_WINDOW)
last = (gtcaca_window_widget_t *)widget;
}
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget->type != GTCACA_WIDGET_WINDOW) continue;
if (widget == (gtcaca_widget_t *)win)
return prev ? prev : last;
prev = (gtcaca_window_widget_t *)widget;
}
return NULL;
}
void gtcaca_window_set_focused_child(gtcaca_window_widget_t *win, gtcaca_widget_t *child)
{
gtcaca_widget_t *widget;
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget->parent == (gtcaca_widget_t *)win) {
widget->has_focus = 0;
}
}
if (child) {
child->has_focus = 1;
win->focused_child = child;
}
}
void gtcaca_window_set_default(gtcaca_window_widget_t *win, gtcaca_widget_t *widget)
{
win->default_widget = widget;
}
void gtcaca_window_focus_next_child(gtcaca_window_widget_t *win)
{
gtcaca_widget_t *focusable[MAX_FOCUSABLE_CHILDREN];
int n = 0, current = -1, i;
gtcaca_widget_t *widget;
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget->parent == (gtcaca_widget_t *)win &&
_gtcaca_widget_is_focusable(widget) &&
n < MAX_FOCUSABLE_CHILDREN) {
if (widget->has_focus) current = n;
focusable[n++] = widget;
}
}
if (n == 0) return;
if (current >= 0) focusable[current]->has_focus = 0;
i = (current + 1) % n;
focusable[i]->has_focus = 1;
win->focused_child = focusable[i];
}
void gtcaca_window_focus_prev_child(gtcaca_window_widget_t *win)
{
gtcaca_widget_t *focusable[MAX_FOCUSABLE_CHILDREN];
int n = 0, current = -1, i;
gtcaca_widget_t *widget;
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget->parent == (gtcaca_widget_t *)win &&
_gtcaca_widget_is_focusable(widget) &&
n < MAX_FOCUSABLE_CHILDREN) {
if (widget->has_focus) current = n;
focusable[n++] = widget;
}
}
if (n == 0) return;
if (current >= 0) focusable[current]->has_focus = 0;
i = (current - 1 + n) % n;
focusable[i]->has_focus = 1;
win->focused_child = focusable[i];
}
void gtcaca_window_set_close_animation(gtcaca_window_widget_t *win,
gtcaca_window_close_anim_t anim,
gtcaca_widget_t *target)
{
win->close_anim = anim;
win->close_anim_target = target;
}
void gtcaca_window_close(gtcaca_window_widget_t *win)
{
gtcaca_widget_t *widget;
int sx = win->x, sy = win->y, sw = win->width, sh = win->height;
CDL_FOREACH(gmo.widgets_list, widget) {
if (widget == GTCACA_WIDGET(win) || widget->parent == GTCACA_WIDGET(win)) {
widget->is_visible = 0;
widget->has_focus = 0;
}
}
if (win->close_anim == GTCACA_WINDOW_ANIM_SHRINK && win->close_anim_target) {
gtcaca_widget_t *tgt = win->close_anim_target;
int tx = tgt->x + tgt->width / 2;
int ty = tgt->y + 1;
int steps = 16, i;
for (i = 0; i <= steps; i++) {
float t = (float)i / (float)steps;
int cw = (int)(sw * (1.0f - t));
int ch = (int)(sh * (1.0f - t));
int cx = (int)((sx + sw / 2) + (tx - (sx + sw / 2)) * t);
int cy = (int)((sy + sh / 2) + (ty - (sy + sh / 2)) * t);
int bx = cx - cw / 2;
int by = cy - ch / 2;
gtcaca_redraw();
if (cw >= 2 && ch >= 2) {
caca_set_color_ansi(gmo.cv, CACA_YELLOW, CACA_RED);
caca_fill_box(gmo.cv, bx, by, cw, ch, ' ');
caca_draw_cp437_box(gmo.cv, bx, by, cw, ch);
} else if (cw >= 1 && ch >= 1) {
caca_set_color_ansi(gmo.cv, CACA_YELLOW, CACA_RED);
caca_put_char(gmo.cv, bx, by, '*');
}
caca_refresh_display(gmo.dp);
usleep(35000);
}
}
gtcaca_redraw();
caca_refresh_display(gmo.dp);
}