changeset 14613:62bb53609a36

[gaim-migrate @ 17341] Menus and windows. I have added a test-app test/menu.c to show how to use it. Pressing Ctrl+o brings up the menu for the window (if it has one). It should now be possible to add menus for account-actions and all that stuff. Patches are very welcome. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sun, 24 Sep 2006 07:14:26 +0000
parents 1f46715c08d9
children 45cdf2cf456c
files console/libgnt/Makefile.am console/libgnt/gnt.h console/libgnt/gntbox.c console/libgnt/gntmain.c console/libgnt/gntmenu.c console/libgnt/gntmenu.h console/libgnt/gntmenuitem.c console/libgnt/gntmenuitem.h console/libgnt/gnttree.c console/libgnt/gnttree.h console/libgnt/gntwidget.c console/libgnt/gntwidget.h console/libgnt/gntwindow.c console/libgnt/gntwindow.h console/libgnt/test/Makefile console/libgnt/test/focus.c console/libgnt/test/menu.c console/libgnt/wms/s.c
diffstat 18 files changed, 859 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/console/libgnt/Makefile.am	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/Makefile.am	Sun Sep 24 07:14:26 2006 +0000
@@ -16,10 +16,13 @@
 	gntlabel.c \
 	gntline.c \
 	gntmarshal.c \
+	gntmenu.c \
+	gntmenuitem.c \
 	gntstyle.c \
 	gnttextview.c \
 	gnttree.c \
 	gntutils.c \
+	gntwindow.c \
 	gntmain.c
 
 libgnt_la_headers = \
@@ -34,10 +37,13 @@
 	gntlabel.h \
 	gntline.h \
 	gntmarshal.h \
+	gntmenu.h \
+	gntmenuitem.h \
 	gntstyle.h \
 	gnttextview.h \
 	gnttree.h \
 	gntutils.h \
+	gntwindow.h \
 	gntwm.h \
 	gnt.h
 
--- a/console/libgnt/gnt.h	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/gnt.h	Sun Sep 24 07:14:26 2006 +0000
@@ -29,5 +29,7 @@
 
 void gnt_register_action(const char *label, void (*callback)());
 
+gboolean gnt_screen_menu_show(gpointer menu);
+
 void gnt_quit();
 
--- a/console/libgnt/gntbox.c	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/gntbox.c	Sun Sep 24 07:14:26 2006 +0000
@@ -504,9 +504,15 @@
 static void
 gnt_box_init(GTypeInstance *instance, gpointer class)
 {
+	GntWidget *widget = GNT_WIDGET(instance);
+	GntBox *box = GNT_BOX(widget);
 	/* Initially make both the height and width resizable.
 	 * Update the flags as necessary when widgets are added to it. */
-	GNT_WIDGET_SET_FLAGS(GNT_WIDGET(instance), GNT_WIDGET_GROW_X | GNT_WIDGET_GROW_Y);
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X | GNT_WIDGET_GROW_Y);
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS);
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW);
+	box->pad = 1;
+	box->fill = TRUE;
 	GNTDEBUG;
 }
 
@@ -547,14 +553,7 @@
 
 	box->homogeneous = homo;
 	box->vertical = vert;
-	box->pad = 1;
-	box->fill = TRUE;
-	gnt_widget_set_take_focus(widget, TRUE);
-	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW);
-	if (vert)
-		box->alignment = GNT_ALIGN_LEFT;
-	else
-		box->alignment = GNT_ALIGN_MID;
+	box->alignment = vert ? GNT_ALIGN_LEFT : GNT_ALIGN_MID;
 
 	return widget;
 }
--- a/console/libgnt/gntmain.c	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/gntmain.c	Sun Sep 24 07:14:26 2006 +0000
@@ -8,6 +8,7 @@
 #include "gntbox.h"
 #include "gntcolors.h"
 #include "gntkeys.h"
+#include "gntmenu.h"
 #include "gntstyle.h"
 #include "gnttree.h"
 #include "gntutils.h"
@@ -34,6 +35,14 @@
  * 	Need to wattrset for colors to use with PDCurses.
  */
 
+/**
+ * There can be at most one menu at a time on the screen.
+ * If there is a menu being displayed, then all the keystrokes will be sent to
+ * the menu until it is closed, either when the user activates a menuitem, or
+ * presses Escape to cancel the menu.
+ */
+static GntMenu *menu;
+
 static int lock_focus_list;
 static GList *focus_list;
 static GList *ordered;
@@ -120,6 +129,15 @@
 static gboolean
 update_screen(gpointer null)
 {
+	if (menu) {
+		GntMenu *top = menu;
+		while (top) {
+			GntNode *node = g_hash_table_lookup(nodes, top);
+			if (node)
+				top_panel(node->panel);
+			top = top->submenu;
+		}
+	}
 	update_panels();
 	doupdate();
 	return TRUE;
@@ -685,7 +703,7 @@
 static int
 reverse_char(WINDOW *d, int y, int x, gboolean set)
 {
-#define DECIDE(ch) (set ? ((ch) | WA_REVERSE) : ((ch) & ~WA_REVERSE))
+#define DECIDE(ch) (set ? ((ch) | A_REVERSE) : ((ch) & ~A_REVERSE))
 
 #ifdef NO_WIDECHAR
 	chtype ch;
@@ -777,8 +795,9 @@
 
 	if (mode == GNT_KP_MODE_NORMAL)
 	{
-		if (ordered)
-		{
+		if (menu) {
+			ret = gnt_widget_key_pressed(GNT_WIDGET(menu), buffer);
+		} else if (ordered) {
 			ret = gnt_widget_key_pressed(ordered->data, buffer);
 		}
 
@@ -1209,8 +1228,8 @@
 	
 	while (widget->parent)
 		widget = widget->parent;
-	
-	gnt_box_sync_children(GNT_BOX(widget));
+	if (!GNT_IS_MENU(widget))
+		gnt_box_sync_children(GNT_BOX(widget));
 	node = g_hash_table_lookup(nodes, widget);
 	if (node && !node->panel)
 	{
@@ -1244,6 +1263,9 @@
 	GntWidget *w;
 	if (!widget)
 		return FALSE;
+	
+	if (GNT_IS_MENU(widget))
+		return TRUE;
 
 	w = widget;
 
@@ -1416,3 +1438,25 @@
 	lock_focus_list = 0;
 }
 
+static void
+reset_menu(GntWidget *widget, gpointer null)
+{
+	menu = NULL;
+}
+
+gboolean gnt_screen_menu_show(gpointer newmenu)
+{
+	if (menu) {
+		/* For now, if a menu is being displayed, then another menu
+		 * can NOT take over. */
+		return FALSE;
+	}
+
+	menu = newmenu;
+	gnt_widget_draw(GNT_WIDGET(menu));
+
+	g_signal_connect(G_OBJECT(menu), "hide", G_CALLBACK(reset_menu), NULL);
+
+	return TRUE;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console/libgnt/gntmenu.c	Sun Sep 24 07:14:26 2006 +0000
@@ -0,0 +1,290 @@
+#include "gntmenu.h"
+
+#include <string.h>
+
+enum
+{
+	SIGS = 1,
+};
+
+static GntTreeClass *parent_class = NULL;
+static guint signals[SIGS] = { 0 };
+
+void (*org_draw)(GntWidget *wid);
+void (*org_destroy)(GntWidget *wid);
+void (*org_map)(GntWidget *wid);
+gboolean (*org_key_pressed)(GntWidget *w, const char *t);
+
+static void
+gnt_menu_draw(GntWidget *widget)
+{
+	GntMenu *menu = GNT_MENU(widget);
+	GList *iter;
+	chtype type;
+	int i;
+
+	if (menu->type == GNT_MENU_TOPLEVEL) {
+		wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT));
+		werase(widget->window);
+
+		for (i = 0, iter = menu->list; iter; iter = iter->next, i++) {
+			GntMenuItem *item = GNT_MENUITEM(iter->data);
+			type = ' ' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT);
+			if (i == menu->selected)
+				type |= A_REVERSE;
+			item->priv.x = getcurx(widget->window) + widget->priv.x;
+			item->priv.y = getcury(widget->window) + widget->priv.y + 1;
+			wbkgdset(widget->window, type);
+			wprintw(widget->window, " %s   ", item->text);
+		}
+	} else {
+		org_draw(widget);
+	}
+
+	GNTDEBUG;
+}
+
+static void
+gnt_menu_size_request(GntWidget *widget)
+{
+	GntMenu *menu = GNT_MENU(widget);
+
+	if (menu->type == GNT_MENU_TOPLEVEL) {
+		widget->priv.height = 1;
+		widget->priv.width = getmaxx(stdscr);
+	} else {
+		widget->priv.height = g_list_length(menu->list) + 2;
+		widget->priv.width = 25;  /* XXX: */
+	}
+}
+
+static void
+menu_tree_add(GntMenu *menu, GntMenuItem *item, GntMenuItem *parent)
+{
+	gnt_tree_add_row_last(GNT_TREE(menu), item,
+		gnt_tree_create_row(GNT_TREE(menu), item->text, item->submenu ? ">" : " "), parent);
+	if (0 && item->submenu) {
+		GntMenu *sub = GNT_MENU(item->submenu);
+		GList *iter;
+		for (iter = sub->list; iter; iter = iter->next) {
+			GntMenuItem *it = GNT_MENUITEM(iter->data);
+			menu_tree_add(menu, it, item);
+		}
+	}
+}
+
+static void
+gnt_menu_map(GntWidget *widget)
+{
+	GntMenu *menu = GNT_MENU(widget);
+
+	if (menu->type == GNT_MENU_TOPLEVEL) {
+		gnt_widget_size_request(widget);
+	} else {
+		/* Populate the tree */
+		GList *iter;
+		gnt_tree_remove_all(GNT_TREE(widget));
+		for (iter = menu->list; iter; iter = iter->next) {
+			GntMenuItem *item = GNT_MENUITEM(iter->data);
+			menu_tree_add(menu, item, NULL);
+		}
+		org_map(widget);
+	}
+	GNTDEBUG;
+}
+
+static void
+menuitem_activate(GntMenu *menu, GntMenuItem *item)
+{
+	if (item) {
+		if (item->submenu) {
+			GntMenu *sub = GNT_MENU(item->submenu);
+			menu->submenu = sub;
+			sub->type = GNT_MENU_POPUP;	/* Submenus are *never* toplevel */
+			sub->parentmenu = menu;
+			if (menu->type != GNT_MENU_TOPLEVEL) {
+				GntWidget *widget = GNT_WIDGET(menu);
+				item->priv.x = widget->priv.x + widget->priv.width - 1;
+				item->priv.y = widget->priv.y + gnt_tree_get_selection_visible_line(GNT_TREE(menu));
+			}
+			gnt_widget_set_position(GNT_WIDGET(sub), item->priv.x, item->priv.y);
+			gnt_widget_draw(GNT_WIDGET(sub));
+		} else if (item->callback) {
+			item->callback(item, item->callbackdata);
+			while (menu) {
+				gnt_widget_hide(GNT_WIDGET(menu));
+				menu = menu->parentmenu;
+			}
+		}
+	}
+}
+
+static gboolean
+gnt_menu_key_pressed(GntWidget *widget, const char *text)
+{
+	GntMenu *menu = GNT_MENU(widget);
+	int current = menu->selected;
+
+	if (menu->submenu) {
+		/*if (gnt_widget_key_pressed(GNT_WIDGET(menu->submenu), text))*/
+			/*return TRUE;*/
+		return (gnt_widget_key_pressed(GNT_WIDGET(menu->submenu), text));
+	}
+
+	if (text[0] == 27 && text[1] == 0) {
+		/* Escape closes menu */
+		GntMenu *par = menu->parentmenu;
+		if (par != NULL) {
+			par->submenu = NULL;
+			gnt_widget_hide(widget);
+		} else
+			gnt_widget_hide(widget);
+		return TRUE;
+	}
+
+	if (menu->type == GNT_MENU_TOPLEVEL) {
+		if (text[0] == 27) {
+			if (strcmp(text + 1, GNT_KEY_LEFT) == 0) {
+				menu->selected--;
+				if (menu->selected < 0)
+					menu->selected = g_list_length(menu->list) - 1;
+			} else if (strcmp(text + 1, GNT_KEY_RIGHT) == 0) {
+				menu->selected++;
+				if (menu->selected >= g_list_length(menu->list))
+					menu->selected = 0;
+			}
+		} else if (text[0] == '\r' && text[1] == 0) {
+			gnt_widget_activate(widget);
+		}
+
+		if (current != menu->selected) {
+			gnt_widget_draw(widget);
+			return TRUE;
+		}
+	} else {
+		return org_key_pressed(widget, text);
+	}
+
+	return FALSE;
+}
+
+static void
+gnt_menu_destroy(GntWidget *widget)
+{
+	GntMenu *menu = GNT_MENU(widget);
+	g_list_foreach(menu->list, (GFunc)g_object_run_dispose, NULL);
+	g_list_free(menu->list);
+	org_destroy(widget);
+}
+
+static void
+gnt_menu_activate(GntWidget *widget)
+{
+	GntMenu *menu = GNT_MENU(widget);
+	GntMenuItem *item;
+
+	if (menu->type == GNT_MENU_TOPLEVEL) {
+		item = g_list_nth_data(menu->list, menu->selected);
+	} else {
+		item = gnt_tree_get_selection_data(GNT_TREE(menu));
+	}
+
+	if (item)
+		menuitem_activate(menu, item);
+}
+
+static void
+gnt_menu_hide(GntWidget *widget)
+{
+	GntMenu *menu = GNT_MENU(widget);
+	if (menu->parentmenu)
+		menu->parentmenu->submenu = NULL;
+}
+
+static void
+gnt_menu_class_init(GntMenuClass *klass)
+{
+	GntWidgetClass *wid_class = GNT_WIDGET_CLASS(klass);
+	parent_class = GNT_TREE_CLASS(klass);
+
+	org_destroy = wid_class->destroy;
+	org_map = wid_class->map;
+	org_draw = wid_class->draw;
+	org_key_pressed = wid_class->key_pressed;
+
+	wid_class->destroy = gnt_menu_destroy;
+	wid_class->draw = gnt_menu_draw;
+	wid_class->map = gnt_menu_map;
+	wid_class->size_request = gnt_menu_size_request;
+	wid_class->key_pressed = gnt_menu_key_pressed;
+	wid_class->activate = gnt_menu_activate;
+	wid_class->hide = gnt_menu_hide;
+
+	GNTDEBUG;
+}
+
+static void
+gnt_menu_init(GTypeInstance *instance, gpointer class)
+{
+	GntWidget *widget = GNT_WIDGET(instance);
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW | GNT_WIDGET_NO_BORDER |
+			GNT_WIDGET_CAN_TAKE_FOCUS);
+	GNTDEBUG;
+}
+
+/******************************************************************************
+ * GntMenu API
+ *****************************************************************************/
+GType
+gnt_menu_get_gtype(void)
+{
+	static GType type = 0;
+
+	if(type == 0)
+	{
+		static const GTypeInfo info = {
+			sizeof(GntMenuClass),
+			NULL,					/* base_init		*/
+			NULL,					/* base_finalize	*/
+			(GClassInitFunc)gnt_menu_class_init,
+			NULL,					/* class_finalize	*/
+			NULL,					/* class_data		*/
+			sizeof(GntMenu),
+			0,						/* n_preallocs		*/
+			gnt_menu_init,			/* instance_init	*/
+		};
+
+		type = g_type_register_static(GNT_TYPE_TREE,
+									  "GntMenu",
+									  &info, 0);
+	}
+
+	return type;
+}
+
+GntWidget *gnt_menu_new(GntMenuType type)
+{
+	GntWidget *widget = g_object_new(GNT_TYPE_MENU, NULL);
+	GntMenu *menu = GNT_MENU(widget);
+	menu->list = NULL;
+	menu->selected = -1;
+	menu->type = type;
+
+	if (type == GNT_MENU_TOPLEVEL) {
+		widget->priv.x = 0;
+		widget->priv.y = 0;
+	} else {
+		GNT_TREE(widget)->show_separator = FALSE;
+		_gnt_tree_init_internals(GNT_TREE(widget), 2);
+		gnt_tree_set_col_width(GNT_TREE(widget), 1, 1);  /* The second column is to indicate that it has a submenu */
+		GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER);
+	}
+
+	return widget;
+}
+
+void gnt_menu_add_item(GntMenu *menu, GntMenuItem *item)
+{
+	menu->list = g_list_append(menu->list, item);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console/libgnt/gntmenu.h	Sun Sep 24 07:14:26 2006 +0000
@@ -0,0 +1,70 @@
+#ifndef GNT_MENU_H
+#define GNT_MENU_H
+
+#include "gnttree.h"
+#include "gntcolors.h"
+#include "gntkeys.h"
+
+#define GNT_TYPE_MENU				(gnt_menu_get_gtype())
+#define GNT_MENU(obj)				(G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_MENU, GntMenu))
+#define GNT_MENU_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_MENU, GntMenuClass))
+#define GNT_IS_MENU(obj)			(G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_MENU))
+#define GNT_IS_MENU_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_MENU))
+#define GNT_MENU_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_MENU, GntMenuClass))
+
+#define GNT_MENU_FLAGS(obj)				(GNT_MENU(obj)->priv.flags)
+#define GNT_MENU_SET_FLAGS(obj, flags)		(GNT_MENU_FLAGS(obj) |= flags)
+#define GNT_MENU_UNSET_FLAGS(obj, flags)	(GNT_MENU_FLAGS(obj) &= ~(flags))
+
+typedef struct _GnMenu			GntMenu;
+typedef struct _GnMenuPriv		GntMenuPriv;
+typedef struct _GnMenuClass		GntMenuClass;
+
+#include "gntmenuitem.h"
+
+/**
+ * A toplevel-menu is displayed at the top of the screen, and it spans accross
+ * the entire width of the screen.
+ * A popup-menu could be displayed, for example, as a context menu for widgets.
+ */
+typedef enum
+{
+	GNT_MENU_TOPLEVEL = 1,  /* Menu for a toplevel window */
+	GNT_MENU_POPUP,         /* A popup menu */
+} GntMenuType;
+
+struct _GnMenu
+{
+	GntTree parent;
+	GntMenuType type;
+	
+	GList *list;
+	int selected;
+
+	/* This will keep track of its immediate submenu which is visible so that
+	 * keystrokes can be passed to it. */
+	GntMenu *submenu;
+	GntMenu *parentmenu;
+};
+
+struct _GnMenuClass
+{
+	GntTreeClass parent;
+
+	void (*gnt_reserved1)(void);
+	void (*gnt_reserved2)(void);
+	void (*gnt_reserved3)(void);
+	void (*gnt_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType gnt_menu_get_gtype(void);
+
+GntWidget *gnt_menu_new(GntMenuType type);
+
+void gnt_menu_add_item(GntMenu *menu, GntMenuItem *item);
+
+G_END_DECLS
+
+#endif /* GNT_MENU_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console/libgnt/gntmenuitem.c	Sun Sep 24 07:14:26 2006 +0000
@@ -0,0 +1,79 @@
+#include "gntmenu.h"
+#include "gntmenuitem.h"
+
+static GObjectClass *parent_class = NULL;
+
+static void
+gnt_menuitem_destroy(GObject *obj)
+{
+	GntMenuItem *item = GNT_MENUITEM(obj);
+	g_free(item->text);
+	item->text = NULL;
+	if (item->submenu)
+		gnt_widget_destroy(GNT_WIDGET(item->submenu));
+}
+
+static void
+gnt_menuitem_class_init(GntMenuItemClass *klass)
+{
+	parent_class = G_OBJECT_CLASS(klass);
+
+	parent_class->dispose = gnt_menuitem_destroy;
+}
+
+static void
+gnt_menuitem_init(GTypeInstance *instance, gpointer class)
+{
+}
+
+/******************************************************************************
+ * GntMenuItem API
+ *****************************************************************************/
+GType
+gnt_menuitem_get_gtype(void)
+{
+	static GType type = 0;
+
+	if(type == 0)
+	{
+		static const GTypeInfo info = {
+			sizeof(GntMenuItemClass),
+			NULL,					/* base_init		*/
+			NULL,					/* base_finalize	*/
+			(GClassInitFunc)gnt_menuitem_class_init,
+			NULL,					/* class_finalize	*/
+			NULL,					/* class_data		*/
+			sizeof(GntMenuItem),
+			0,						/* n_preallocs		*/
+			gnt_menuitem_init,			/* instance_init	*/
+		};
+
+		type = g_type_register_static(G_TYPE_OBJECT,
+									  "GntMenuItem",
+									  &info, 0);
+	}
+
+	return type;
+}
+
+GObject *gnt_menuitem_new(const char *text)
+{
+	GObject *item = g_object_new(GNT_TYPE_MENUITEM, NULL);
+	GntMenuItem *menuitem = GNT_MENUITEM(item);
+
+	menuitem->text = g_strdup(text);
+
+	return item;
+}
+
+void gnt_menuitem_set_callback(GntMenuItem *item, GntMenuItemCallback callback, gpointer data)
+{
+	item->callback = callback;
+	item->callbackdata = data;
+}
+
+void gnt_menuitem_set_submenu(GntMenuItem *item, GntMenu *menu)
+{
+	item->submenu = menu;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console/libgnt/gntmenuitem.h	Sun Sep 24 07:14:26 2006 +0000
@@ -0,0 +1,69 @@
+#ifndef GNT_MENUITEM_H
+#define GNT_MENUITEM_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define GNT_TYPE_MENUITEM				(gnt_menuitem_get_gtype())
+#define GNT_MENUITEM(obj)				(G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_MENUITEM, GntMenuItem))
+#define GNT_MENUITEM_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_MENUITEM, GntMenuItemClass))
+#define GNT_IS_MENUITEM(obj)			(G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_MENUITEM))
+#define GNT_IS_MENUITEM_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_MENUITEM))
+#define GNT_MENUITEM_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_MENUITEM, GntMenuItemClass))
+
+#define GNT_MENUITEM_FLAGS(obj)				(GNT_MENUITEM(obj)->priv.flags)
+#define GNT_MENUITEM_SET_FLAGS(obj, flags)		(GNT_MENUITEM_FLAGS(obj) |= flags)
+#define GNT_MENUITEM_UNSET_FLAGS(obj, flags)	(GNT_MENUITEM_FLAGS(obj) &= ~(flags))
+
+typedef struct _GnMenuItem			GntMenuItem;
+typedef struct _GnMenuItemPriv		GntMenuItemPriv;
+typedef struct _GnMenuItemClass		GntMenuItemClass;
+
+struct _GnMenuItemPriv
+{
+	/* These will be used to determine the position of the submenu */
+	int x;
+	int y;
+};
+
+typedef void (*GntMenuItemCallback)(GntMenuItem *item, gpointer data);
+
+struct _GnMenuItem
+{
+	GObject parent;
+	GntMenuItemPriv priv;
+
+	char *text;
+
+	/* A GntMenuItem can have a callback associated with it.
+	 * The callback will be activated whenever the suer selects it and presses enter (or clicks).
+	 * However, if the GntMenuItem has some child, then the callback and callbackdata will be ignored. */
+	gpointer callbackdata;
+	GntMenuItemCallback callback;
+
+	GntMenu *submenu;
+};
+
+struct _GnMenuItemClass
+{
+	GObjectClass parent;
+
+	void (*gnt_reserved1)(void);
+	void (*gnt_reserved2)(void);
+	void (*gnt_reserved3)(void);
+	void (*gnt_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType gnt_menuitem_get_gtype(void);
+
+GObject *gnt_menuitem_new(const char *text);
+
+void gnt_menuitem_set_callback(GntMenuItem *item, GntMenuItemCallback callback, gpointer data);
+
+void gnt_menuitem_set_submenu(GntMenuItem *item, GntMenu *menu);
+
+G_END_DECLS
+
+#endif /* GNT_MENUITEM_H */
--- a/console/libgnt/gnttree.c	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/gnttree.c	Sun Sep 24 07:14:26 2006 +0000
@@ -288,8 +288,10 @@
 			tree_mark_columns(tree, pos, widget->priv.height - pos,
 					ACS_BTEE | COLOR_PAIR(GNT_COLOR_NORMAL));
 		}
-		tree_mark_columns(tree, pos, pos + 1, ACS_PLUS | COLOR_PAIR(GNT_COLOR_NORMAL));
-		tree_mark_columns(tree, pos, pos, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL));
+		tree_mark_columns(tree, pos, pos + 1,
+			(tree->show_separator ? ACS_PLUS : ACS_HLINE) | COLOR_PAIR(GNT_COLOR_NORMAL));
+		tree_mark_columns(tree, pos, pos,
+			(tree->show_separator ? ACS_VLINE : ' ') | COLOR_PAIR(GNT_COLOR_NORMAL));
 		start = 2;
 	}
 
@@ -356,7 +358,8 @@
 		whline(widget->window, ' ', widget->priv.width - pos * 2 - g_utf8_strlen(str, -1) - 1);
 		tree->bottom = row;
 		g_free(str);
-		tree_mark_columns(tree, pos, i, ACS_VLINE | attr);
+		tree_mark_columns(tree, pos, i,
+			(tree->show_separator ? ACS_VLINE : ' ') | attr);
 	}
 
 	wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
@@ -364,7 +367,8 @@
 	{
 		mvwhline(widget->window, i, pos, ' ',
 				widget->priv.width - pos * 2 - 1);
-		tree_mark_columns(tree, pos, i, ACS_VLINE);
+		tree_mark_columns(tree, pos, i,
+			(tree->show_separator ? ACS_VLINE : ' '));
 		i++;
 	}
 
@@ -696,6 +700,8 @@
 gnt_tree_init(GTypeInstance *instance, gpointer class)
 {
 	GntWidget *widget = GNT_WIDGET(instance);
+	GntTree *tree = GNT_TREE(widget);
+	tree->show_separator = TRUE;
 	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X | GNT_WIDGET_GROW_Y);
 	widget->priv.minw = 4;
 	widget->priv.minh = 4;
@@ -1125,19 +1131,24 @@
 		redraw_tree(tree);
 }
 
-GntWidget *gnt_tree_new_with_columns(int col)
+void _gnt_tree_init_internals(GntTree *tree, int col)
 {
-	GntWidget *widget = g_object_new(GNT_TYPE_TREE, NULL);
-	GntTree *tree = GNT_TREE(widget);
-
+	tree->ncol = col;
 	tree->hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_tree_row);
-	tree->ncol = col;
 	tree->columns = g_new0(struct _GntTreeColInfo, col);
 	while (col--)
 	{
 		tree->columns[col].width = 15;
 	}
 	tree->show_title = FALSE;
+}
+
+GntWidget *gnt_tree_new_with_columns(int col)
+{
+	GntWidget *widget = g_object_new(GNT_TYPE_TREE, NULL);
+	GntTree *tree = GNT_TREE(widget);
+
+	_gnt_tree_init_internals(tree, col);
 	
 	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW);
 	gnt_widget_set_take_focus(widget, TRUE);
@@ -1224,3 +1235,9 @@
 			gnt_widget_draw(GNT_WIDGET(tree));
 	}
 }
+
+void gnt_tree_set_show_separator(GntTree *tree, gboolean set)
+{
+	tree->show_separator = set;
+}
+
--- a/console/libgnt/gnttree.h	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/gnttree.h	Sun Sep 24 07:14:26 2006 +0000
@@ -46,6 +46,7 @@
 		char *title;
 	} *columns;             /* Would a GList be better? */
 	gboolean show_title;
+	gboolean show_separator; /* Whether to show column separators */
 
 	GCompareFunc compare;
 };
@@ -54,7 +55,7 @@
 {
 	GntWidgetClass parent;
 
-	void (*selection_changed)(int old, int current);
+	void (*selection_changed)(GntTreeRow *old, GntTreeRow * current);
 
 	void (*gnt_reserved1)(void);
 	void (*gnt_reserved2)(void);
@@ -120,6 +121,13 @@
 
 void gnt_tree_set_expanded(GntTree *tree, void *key, gboolean expanded);
 
+void gnt_tree_set_show_separator(GntTree *tree, gboolean set);
+
 G_END_DECLS
 
+/* The following functions should NOT be used by applications. */
+
+/* This should be called by the subclasses of GntTree's in their _new function */
+void _gnt_tree_init_internals(GntTree *tree, int col);
+
 #endif /* GNT_TREE_H */
--- a/console/libgnt/gntwidget.c	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/gntwidget.c	Sun Sep 24 07:14:26 2006 +0000
@@ -9,6 +9,7 @@
 {
 	SIG_DESTROY,
 	SIG_DRAW,
+	SIG_HIDE,
 	SIG_GIVE_FOCUS,
 	SIG_LOST_FOCUS,
 	SIG_KEY_PRESSED,
@@ -167,6 +168,14 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
+	signals[SIG_HIDE] = 
+		g_signal_new("hide",
+					 G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST,
+					 G_STRUCT_OFFSET(GntWidgetClass, hide),
+					 NULL, NULL,
+					 g_cclosure_marshal_VOID__VOID,
+					 G_TYPE_NONE, 0);
 	signals[SIG_EXPOSE] = 
 		g_signal_new("expose",
 					 G_TYPE_FROM_CLASS(klass),
@@ -403,6 +412,7 @@
 void
 gnt_widget_hide(GntWidget *widget)
 {
+	g_signal_emit(widget, signals[SIG_HIDE], 0);
 	wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
 #if 0
 	/* XXX: I have no clue why, but this seemed to be necessary. */
--- a/console/libgnt/gntwidget.h	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/gntwidget.h	Sun Sep 24 07:14:26 2006 +0000
@@ -93,6 +93,7 @@
 	void (*show)(GntWidget *obj);		/* This will call draw() and take focus (if it can take focus) */
 	void (*destroy)(GntWidget *obj);
 	void (*draw)(GntWidget *obj);		/* This will draw the widget */
+	void (*hide)(GntWidget *obj);
 	void (*expose)(GntWidget *widget, int x, int y, int width, int height);
 	void (*gained_focus)(GntWidget *widget);
 	void (*lost_focus)(GntWidget *widget);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console/libgnt/gntwindow.c	Sun Sep 24 07:14:26 2006 +0000
@@ -0,0 +1,113 @@
+#include "gntwindow.h"
+
+#include <string.h>
+
+enum
+{
+	SIGS = 1,
+};
+
+static GntBoxClass *parent_class = NULL;
+static guint signals[SIGS] = { 0 };
+
+gboolean (*org_keypress)(GntWidget *widget, const char *text);
+void (*org_destroy)(GntWidget *widget);
+
+static gboolean
+gnt_window_key_pressed(GntWidget *widget, const char *text)
+{
+	if (strcmp(text, GNT_KEY_CTRL_O) == 0 && GNT_WINDOW(widget)->menu) {
+		gnt_screen_menu_show(GNT_WINDOW(widget)->menu);
+		return TRUE;
+	} else
+		return org_keypress(widget, text);
+		
+}
+
+static void
+gnt_window_destroy(GntWidget *widget)
+{
+	GntWindow *window = GNT_WINDOW(widget);
+	if (window->menu)
+		gnt_widget_destroy(GNT_WIDGET(window->menu));
+	org_destroy(widget);
+}
+
+static void
+gnt_window_class_init(GntWindowClass *klass)
+{
+	GntWidgetClass *wid_class = GNT_WIDGET_CLASS(klass);
+	parent_class = GNT_BOX_CLASS(klass);
+
+	org_keypress = wid_class->key_pressed;
+	wid_class->key_pressed = gnt_window_key_pressed;
+
+	org_destroy = wid_class->destroy;
+	wid_class->destroy = gnt_window_destroy;
+
+	GNTDEBUG;
+}
+
+static void
+gnt_window_init(GTypeInstance *instance, gpointer class)
+{
+	GntWidget *widget = GNT_WIDGET(instance);
+	GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW);
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS);
+	GNTDEBUG;
+}
+
+/******************************************************************************
+ * GntWindow API
+ *****************************************************************************/
+GType
+gnt_window_get_gtype(void)
+{
+	static GType type = 0;
+
+	if(type == 0)
+	{
+		static const GTypeInfo info = {
+			sizeof(GntWindowClass),
+			NULL,					/* base_init		*/
+			NULL,					/* base_finalize	*/
+			(GClassInitFunc)gnt_window_class_init,
+			NULL,					/* class_finalize	*/
+			NULL,					/* class_data		*/
+			sizeof(GntWindow),
+			0,						/* n_preallocs		*/
+			gnt_window_init,			/* instance_init	*/
+		};
+
+		type = g_type_register_static(GNT_TYPE_BOX,
+									  "GntWindow",
+									  &info, 0);
+	}
+
+	return type;
+}
+
+GntWidget *gnt_window_new()
+{
+	GntWidget *widget = g_object_new(GNT_TYPE_WINDOW, NULL);
+
+	return widget;
+}
+
+GntWidget *gnt_window_box_new(gboolean homo, gboolean vert)
+{
+	GntWidget *wid = gnt_window_new();
+	GntBox *box = GNT_BOX(wid);
+
+	box->homogeneous = homo;
+	box->vertical = vert;
+	box->alignment = vert ? GNT_ALIGN_LEFT : GNT_ALIGN_MID;
+
+	return wid;
+}
+
+void gnt_window_set_menu(GntWindow *window, GntMenu *menu)
+{
+	window->menu = menu;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console/libgnt/gntwindow.h	Sun Sep 24 07:14:26 2006 +0000
@@ -0,0 +1,56 @@
+#ifndef GNT_WINDOW_H
+#define GNT_WINDOW_H
+
+#include "gnt.h"
+#include "gntbox.h"
+#include "gntcolors.h"
+#include "gntkeys.h"
+#include "gntmenu.h"
+
+#define GNT_TYPE_WINDOW				(gnt_window_get_gtype())
+#define GNT_WINDOW(obj)				(G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_WINDOW, GntWindow))
+#define GNT_WINDOW_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_WINDOW, GntWindowClass))
+#define GNT_IS_WINDOW(obj)			(G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_WINDOW))
+#define GNT_IS_WINDOW_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_WINDOW))
+#define GNT_WINDOW_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_WINDOW, GntWindowClass))
+
+#define GNT_WINDOW_FLAGS(obj)				(GNT_WINDOW(obj)->priv.flags)
+#define GNT_WINDOW_SET_FLAGS(obj, flags)		(GNT_WINDOW_FLAGS(obj) |= flags)
+#define GNT_WINDOW_UNSET_FLAGS(obj, flags)	(GNT_WINDOW_FLAGS(obj) &= ~(flags))
+
+typedef struct _GnWindow			GntWindow;
+typedef struct _GnWindowPriv		GntWindowPriv;
+typedef struct _GnWindowClass		GntWindowClass;
+
+struct _GnWindow
+{
+	GntBox parent;
+	GntMenu *menu;
+};
+
+struct _GnWindowClass
+{
+	GntBoxClass parent;
+
+	void (*gnt_reserved1)(void);
+	void (*gnt_reserved2)(void);
+	void (*gnt_reserved3)(void);
+	void (*gnt_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType gnt_window_get_gtype(void);
+
+#define gnt_vwindow_new(homo) gnt_window_box_new(homo, TRUE)
+#define gnt_hwindow_new(homo) gnt_window_box_new(homo, FALSE)
+
+GntWidget *gnt_window_new();
+
+GntWidget *gnt_window_box_new(gboolean homo, gboolean vert);
+
+void gnt_window_set_menu(GntWindow *window, GntMenu *menu);
+
+G_END_DECLS
+
+#endif /* GNT_WINDOW_H */
--- a/console/libgnt/test/Makefile	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/test/Makefile	Sun Sep 24 07:14:26 2006 +0000
@@ -2,7 +2,7 @@
 CFLAGS=`pkg-config --cflags gobject-2.0 gmodule-2.0` -g -I../ -DSTANDALONE
 LDFLAGS=`pkg-config --libs gobject-2.0 gmodule-2.0 gnt` -pg
 
-EXAMPLES=combo focus tv multiwin keys
+EXAMPLES=combo focus tv multiwin keys menu
 
 all:
 	make examples
--- a/console/libgnt/test/focus.c	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/test/focus.c	Sun Sep 24 07:14:26 2006 +0000
@@ -61,7 +61,7 @@
 	gnt_tree_add_choice(GNT_TREE(tree), "b", gnt_tree_create_row(GNT_TREE(tree), "b"), "d", NULL);
 
 	GNT_WIDGET_UNSET_FLAGS(hbox, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW);
-	gnt_box_set_title(GNT_BOX(hbox), "\u4e0a\u6d77\u6700\u4f4e\u6708\u5de5 …");
+	gnt_box_set_title(GNT_BOX(hbox), "\u4e0a\u6d77\u6700\u4f4e\u6708\u5de5 \u4e0a\u6d77\u6700\u4f4e\u6708\u5de5 ……\u4e0a\u6d77\u6700\u4f4e\u6708\u5de5 …");
 
 	g_signal_connect(G_OBJECT(tree), "toggled", G_CALLBACK(toggled), NULL);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console/libgnt/test/menu.c	Sun Sep 24 07:14:26 2006 +0000
@@ -0,0 +1,65 @@
+#include "gnt.h"
+#include "gntbox.h"
+#include "gntlabel.h"
+#include "gntmenu.h"
+#include "gntmenuitem.h"
+#include "gntwindow.h"
+
+void dothis(GntMenuItem *item, gpointer null)
+{
+	GntWidget *w = gnt_vbox_new(FALSE);
+	gnt_box_set_toplevel(GNT_BOX(w), TRUE);
+	gnt_box_add_widget(GNT_BOX(w),
+			gnt_label_new("Callback to a menuitem"));
+	gnt_widget_show(w);
+}
+
+int main()
+{
+	freopen(".error", "w", stderr);
+	gnt_init();
+
+	GntWidget *menu = gnt_menu_new(GNT_MENU_TOPLEVEL);
+	GObject *item = gnt_menuitem_new("File");
+
+	gnt_menu_add_item(GNT_MENU(menu), GNT_MENUITEM(item));
+
+	item = gnt_menuitem_new("Edit");
+	gnt_menu_add_item(GNT_MENU(menu), GNT_MENUITEM(item));
+
+	item = gnt_menuitem_new("Help");
+	gnt_menu_add_item(GNT_MENU(menu), GNT_MENUITEM(item));
+
+	GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
+	gnt_menuitem_set_submenu(GNT_MENUITEM(item), GNT_MENU(sub));
+
+	item = gnt_menuitem_new("Online Help");
+	gnt_menu_add_item(GNT_MENU(sub), GNT_MENUITEM(item));
+
+	item = gnt_menuitem_new("About");
+	gnt_menu_add_item(GNT_MENU(sub), GNT_MENUITEM(item));
+
+	sub = gnt_menu_new(GNT_MENU_POPUP);
+	gnt_menuitem_set_submenu(GNT_MENUITEM(item), GNT_MENU(sub));
+
+	item = gnt_menuitem_new("Online Help");
+	gnt_menu_add_item(GNT_MENU(sub), GNT_MENUITEM(item));
+	gnt_menuitem_set_callback(GNT_MENUITEM(item), dothis, NULL);
+
+	gnt_screen_menu_show(menu);
+
+
+	GntWidget *win = gnt_window_new();
+	gnt_box_add_widget(GNT_BOX(win),
+		gnt_label_new("..."));
+	gnt_box_set_title(GNT_BOX(win), "Title");
+	gnt_window_set_menu(GNT_WINDOW(win), GNT_MENU(menu));
+	gnt_widget_show(win);
+
+	gnt_main();
+
+	gnt_quit();
+
+	return  0;
+}
+
--- a/console/libgnt/wms/s.c	Sat Sep 23 19:04:03 2006 +0000
+++ b/console/libgnt/wms/s.c	Sun Sep 24 07:14:26 2006 +0000
@@ -1,4 +1,6 @@
+#include "gnt.h"
 #include "gntbox.h"
+#include "gntmenu.h"
 #include "gntwm.h"
 
 #include "gntblist.h"
@@ -52,6 +54,8 @@
 	int maxx, maxy;
 	const char *name;
 
+	if (GNT_IS_MENU(win))
+		return new_panel(win->window);
 	getmaxyx(stdscr, maxy, maxx);
 
 	gnt_widget_get_position(win, &x, &y);