view console/libgnt/gntmain.c @ 13907:cc60d0861337

[gaim-migrate @ 16402] This commit has 1234 lines of diff :) Windows can now be moved (alt+m, then the arrow keys, then escape/enter). Add a window to enable/disable accounts. But the 'add' etc. buttons don't have any callbacks yet. I am going to need to do some more widgets (checkbox, combobox) before I do anything else. I have also updated the test programs to work with the changes in libgnt. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sun, 02 Jul 2006 22:13:06 +0000
parents eaaf73de9188
children b210409cdc56
line wrap: on
line source

#include "gnt.h"
#include "gntbox.h"
#include "gntkeys.h"
#include "gntcolors.h"

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <unistd.h>
#include <string.h>

static GList *focus_list;
static int X_MIN;
static int X_MAX;
static int Y_MIN;
static int Y_MAX;

static GMainLoop *loop;

typedef struct
{
	GntWidget *me;
	GList *below;		/* List of widgets below me */
	GList *above;		/* List of widgets above me */
} GntNode;

typedef enum
{
	GNT_KP_MODE_NORMAL,
	GNT_KP_MODE_RESIZE,
	GNT_KP_MODE_MOVE,
	GNT_KP_MODE_MENU,
} GntKeyPressMode;

static GHashTable *nodes;

static void free_node(gpointer data);
static void draw_taskbar();

void gnt_screen_take_focus(GntWidget *widget)
{
	GntWidget *w = NULL;

	if (focus_list)
		w = focus_list->data;

	/* XXX: ew */
	focus_list = g_list_first(focus_list);
	focus_list = g_list_append(focus_list, widget);
	focus_list = g_list_find(focus_list, widget);

	gnt_widget_set_focus(widget, TRUE);
	if (w)
		gnt_widget_set_focus(w, FALSE);
	draw_taskbar();
}

void gnt_screen_remove_widget(GntWidget *widget)
{
	int pos = g_list_index(g_list_first(focus_list), widget);
	GList *next;

	if (pos == -1)
		return;

	focus_list = g_list_first(focus_list);
	focus_list = g_list_remove(focus_list, widget);
	next = g_list_nth(focus_list, pos - 1);
	if (next)
		focus_list = next;

	if (focus_list)
	{
		gnt_widget_set_focus(focus_list->data, TRUE);
		gnt_widget_draw(focus_list->data);
	}
	draw_taskbar();
}

static void
bring_on_top(GntWidget *widget)
{
	GntNode *node = g_hash_table_lookup(nodes, widget);
	GList *iter;

	if (!node)
		return;

	for (iter = node->above; iter;)
	{
		GntNode *n = iter->data;
		iter = iter->next;
		n->below = g_list_remove(n->below, node);
		n->above = g_list_prepend(n->above, node);

		node->above = g_list_remove(node->above, n);
		node->below = g_list_prepend(node->below, n);
	}
}

static void
draw_taskbar()
{
	static WINDOW *taskbar = NULL;
	GList *iter;
	int n, width;
	int i;

	if (taskbar == NULL)
	{
		taskbar = newwin(1, getmaxx(stdscr), getmaxy(stdscr) - 1, 0);
	}

	wbkgdset(taskbar, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
	werase(taskbar);

	n = g_list_length(g_list_first(focus_list));
	if (n)
		width = getmaxx(stdscr) / n;

	for (i = 0, iter = g_list_first(focus_list); iter; iter = iter->next, i++)
	{
		GntWidget *w = iter->data;
		int color;

		if (w == focus_list->data)
		{
			/* This is the current window in focus */
			color = GNT_COLOR_TITLE;
			GNT_WIDGET_UNSET_FLAGS(w, GNT_WIDGET_URGENT);
		}
		else if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_URGENT))
		{
			/* This is a window with the URGENT hint set */
			color = GNT_COLOR_TITLE_D;
		}
		else
		{
			color = GNT_COLOR_NORMAL;
		}
		wbkgdset(taskbar, '\0' | COLOR_PAIR(color));
		mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), width);
		mvwprintw(taskbar, 0, width * i, "%s", GNT_BOX(w)->title);
	}

	wrefresh(taskbar);
}

static void
switch_window(int direction)
{
	GntWidget *w = NULL;
	if (focus_list)
		w = focus_list->data;

	if (direction == 1)
	{
		if (focus_list && focus_list->next)
			focus_list = focus_list->next;
		else
			focus_list = g_list_first(focus_list);
	}
	else if (direction == -1)
	{
		if (focus_list && focus_list->prev)
			focus_list = focus_list->prev;
		else
			focus_list = g_list_last(focus_list);
	}
	
	if (focus_list)
	{
		gnt_widget_set_focus(focus_list->data, TRUE);
		bring_on_top(focus_list->data);
		gnt_widget_draw(focus_list->data);
	}

	if (w && (!focus_list || w != focus_list->data))
		gnt_widget_set_focus(w, FALSE);
}		

static gboolean
io_invoke(GIOChannel *source, GIOCondition cond, gpointer null)
{
	char buffer[256];
	static GntKeyPressMode mode = GNT_KP_MODE_NORMAL;
	gboolean ret = FALSE;

	int rd = read(0, buffer, sizeof(buffer) - 1);
	if (rd < 0)
	{
		endwin();
		printf("ERROR!\n");
		exit(1);
	}
	else if (rd == 0)
	{
		endwin();
		printf("EOF\n");
		exit(1);
	}

	buffer[rd] = 0;

	if (mode == GNT_KP_MODE_NORMAL)
	{
		if (focus_list)
		{
			ret = gnt_widget_key_pressed(focus_list->data, buffer);
		}

		if (!ret)
		{
			if (buffer[0] == 27)
			{
				/* Some special key has been pressed */
				if (strcmp(buffer+1, GNT_KEY_POPUP) == 0)
				{}
				else if (strcmp(buffer + 1, "c") == 0)
				{
					/* Alt + c was pressed. I am going to use it to close a window. */
					if (focus_list)
					{
						gnt_widget_destroy(focus_list->data);
					}
				}
				else if (strcmp(buffer + 1, "q") == 0)
				{
					/* I am going to use Alt + q to quit. */
					g_main_loop_quit(loop);
				}
				else if (strcmp(buffer + 1, "n") == 0)
				{
					/* Alt + n to go to the next window */
					switch_window(1);
				}
				else if (strcmp(buffer + 1, "p") == 0)
				{
					/* Alt + p to go to the previous window */
					switch_window(-1);
				}
				else if (strcmp(buffer + 1, "m") == 0 && focus_list)
				{
					mode = GNT_KP_MODE_MOVE;
				}
			}
		}
	}
	else if (mode == GNT_KP_MODE_MOVE && focus_list)
	{
		if (buffer[0] == 27)
		{
			gboolean changed = FALSE;
			int x, y, w, h;
			GntWidget *widget = GNT_WIDGET(focus_list->data);

			gnt_widget_get_position(widget, &x, &y);
			gnt_widget_get_size(widget, &w, &h);

			if (strcmp(buffer + 1, GNT_KEY_LEFT) == 0)
			{
				if (x > X_MIN)
				{
					x--;
					changed = TRUE;
				}
			}
			else if (strcmp(buffer + 1, GNT_KEY_RIGHT) == 0)
			{
				if (x + w < X_MAX)
				{
					x++;
					changed = TRUE;
				}
			}
			else if (strcmp(buffer + 1, GNT_KEY_UP) == 0)
			{
				if (y > Y_MIN)
				{
					y--;
					changed = TRUE;
				}						
			}
			else if (strcmp(buffer + 1, GNT_KEY_DOWN) == 0)
			{
				if (y + h < Y_MAX)
				{
					y++;
					changed = TRUE;
				}
			}
			else if (buffer[1] == 0)
			{
				mode = GNT_KP_MODE_NORMAL;
				changed = TRUE;
			}

			if (changed)
			{
				gnt_widget_hide(widget);
				gnt_widget_set_position(widget, x, y);
				gnt_widget_show(widget);
			}
		}
		else if (*buffer == '\r')
		{
			mode = GNT_KP_MODE_NORMAL;
		}
	}

	draw_taskbar();
	refresh();

	return TRUE;
}

void gnt_init()
{
	GIOChannel *channel = g_io_channel_unix_new(0);

	g_io_channel_set_encoding(channel, NULL, NULL);
	g_io_channel_set_buffered(channel, FALSE);
	g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL );

	int result = g_io_add_watch(channel, 
					(G_IO_IN | G_IO_HUP | G_IO_ERR),
					io_invoke, NULL);

	setlocale(LC_ALL, "");
	initscr();
	start_color();
	gnt_init_colors();

	X_MIN = 0;
	Y_MIN = 0;
	X_MAX = getmaxx(stdscr);
	Y_MAX = getmaxy(stdscr) - 1;

	nodes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_node);

	wbkgdset(stdscr, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
	noecho();
	refresh();
	mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
	wbkgdset(stdscr, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
	werase(stdscr);
	wrefresh(stdscr);

	g_type_init();
}

void gnt_main()
{
	loop = g_main_loop_new(NULL, FALSE);
	g_main_loop_run(loop);
}

/*********************************
 * Stuff for 'window management' *
 *********************************/

static void
free_node(gpointer data)
{
	GntNode *node = data;
	g_list_free(node->below);
	g_list_free(node->above);
	g_free(node);
}

static void
check_intersection(gpointer key, gpointer value, gpointer data)
{
	GntNode *n = value;
	GntNode *nu = data;

	if (value == NULL)
		return;
	if (n->me == nu->me)
		return;

	if (n->me->priv.x + n->me->priv.width < nu->me->priv.x)
		return;
	if (nu->me->priv.x + nu->me->priv.width < n->me->priv.x)
		return;

	if (n->me->priv.y + n->me->priv.height < nu->me->priv.y)
		return;
	if (nu->me->priv.y + nu->me->priv.height < n->me->priv.y)
		return;

	n->above = g_list_prepend(n->above, nu);
	nu->below = g_list_prepend(nu->below, n);
}

void gnt_screen_occupy(GntWidget *widget)
{
	GntNode *node;

	if (widget->parent)
	{
		while (widget->parent)
			widget = widget->parent;
	}
	
	if (g_hash_table_lookup(nodes, widget))
		return;		/* XXX: perhaps _update instead? */

	node = g_new0(GntNode, 1);
	node->me = widget;

	g_hash_table_foreach(nodes, check_intersection, node);
	g_hash_table_replace(nodes, widget, node);
}

void gnt_screen_release(GntWidget *widget)
{
	WINDOW *win;
	GList *iter;
	GntNode *node = g_hash_table_lookup(nodes, widget);

	gnt_screen_remove_widget(widget);

	if (node == NULL)	/* Yay! Nothing to do. */
		return;

	win = dupwin(widget->window);
	werase(win);

	/* XXX: This is not going to work.
	 *      It will be necessary to build a topology and go from there. */
	for (iter = node->below; iter; iter = iter->next)
	{
		GntNode *n = iter->data;
		GntWidget *w = n->me;
		int left, right, top, bottom;

		left = MAX(widget->priv.x, w->priv.x) - w->priv.x;
		right = MIN(widget->priv.x + widget->priv.width, w->priv.x + w->priv.width) - w->priv.x;
		
		top = MAX(widget->priv.y, w->priv.y) - w->priv.y;
		bottom = MIN(widget->priv.y + widget->priv.height, w->priv.y + w->priv.height) - w->priv.y;

		copywin(w->window, win, top, left,
					w->priv.y - widget->priv.y + top,
					w->priv.x - widget->priv.x + left,
					w->priv.y - widget->priv.y + bottom - 1,
					w->priv.x - widget->priv.x + right - 1, FALSE);
		n->above = g_list_remove(n->above, node);
	}

	for (iter = node->above; iter; iter = iter->next)
	{
		GntNode *n = iter->data;
		n->below = g_list_remove(n->below, node);
	}

	wrefresh(win);
	delwin(win);

	g_hash_table_remove(nodes, widget);
}

void gnt_screen_update(GntWidget *widget)
{
	GList *iter;
	WINDOW *win;
	GntNode *node;
	
	if (widget->parent)
	{
		while (widget->parent)
			widget = widget->parent;
	}
	
	gnt_box_sync_children(GNT_BOX(widget));
	node = g_hash_table_lookup(nodes, widget);

	win = dupwin(widget->window);
	
	if (node && node->above)
	{
		/* XXX: Same here: need to build a topology first. */
		for (iter = node->above; iter; iter = iter->next)
		{
			GntNode *n = iter->data;
			GntWidget *w = n->me;
			int left, right, top, bottom;

			left = MAX(widget->priv.x, w->priv.x) - w->priv.x;
			right = MIN(widget->priv.x + widget->priv.width, w->priv.x + w->priv.width) - w->priv.x;
			
			top = MAX(widget->priv.y, w->priv.y) - w->priv.y;
			bottom = MIN(widget->priv.y + widget->priv.height, w->priv.y + w->priv.height) - w->priv.y;

			copywin(w->window, win, top, left,
					w->priv.y - widget->priv.y + top,
					w->priv.x - widget->priv.x + left,
					w->priv.y - widget->priv.y + bottom - 1,
					w->priv.x - widget->priv.x + right - 1, FALSE);
		}
	}

	wrefresh(win);
	delwin(win);
}

gboolean gnt_widget_has_focus(GntWidget *widget)
{
	GntWidget *w;
	if (!widget)
		return FALSE;

	w = widget;

	while (widget->parent)
	{
		widget = widget->parent;
	}

	if (focus_list && focus_list->data == widget)
	{
		if (GNT_IS_BOX(widget) &&
				(GNT_BOX(widget)->active == w || widget == w))
			return TRUE;
	}

	return FALSE;
}

void gnt_widget_set_urgent(GntWidget *widget)
{
	while (widget->parent)
		widget = widget->parent;

	if (focus_list && focus_list->data == widget)
		return;

	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_URGENT);
	draw_taskbar();
}

void gnt_quit()
{
	endwin();
}