/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomoki SEKIYAMA <sekiyama@yahoo.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/



#include <ctype.h>

#include "v/VMenu.h"
#include "v/VWindow.h"
extern "C" {
#include "memory_debug.h"
bool vobject_quit();
}

// ===================================================================
//    Implementation of Machine-Common Menu Routine
// ===================================================================

VMenuItem::VMenuItem(
	short type,
	short flag,
	char *name,
	LC_WRITING_STYLE *ws,
	short modifier,
	char shortcut_key,
	char access_key,
	V_CALLBACK(func))
{
	static int next_id = 1000;
	int id;
	switch ( type ) {
	  case VMT_OTHER:
		id = next_id++;
		break;
	  default:
		id = type;
	}
	
	set_member_vars(id, type, flag, name, ws, 
			modifier, shortcut_key, access_key, func, 0);
}

void
VMenuBar::init()
{
}


void
VCustomizedMenuBar::make_customized_menu(VMenuBar *bar)
{
	items = new VMenuItem(*bar->get_items(), this);
}



// ===================================================================
//    GTK Menu Bar Management
// ===================================================================

#include <gtk/gtk.h>

GdkModifierType
modifiers_to_gdk_state(short modifier)
{
	gint ret = 0;
	if ( modifier & V_MODKEY_SHIFT )
		ret |= GDK_SHIFT_MASK;
	if ( modifier & V_MODKEY_CAPS )
		ret |= GDK_LOCK_MASK;
	if ( modifier & V_MODKEY_ALT )
		ret |= GDK_MOD1_MASK;
	if ( modifier & V_MODKEY_CTRL )
		ret |= GDK_CONTROL_MASK;
	if ( modifier & V_MODKEY_META )
		ret |= GDK_MOD2_MASK;
	return (GdkModifierType)ret;
}

void
v_set_menu_flag_do(GtkMenuItem *widget, short flag)
{
	gtk_widget_set_sensitive(GTK_WIDGET(widget), flag&VMF_ENABLED);
	if ( GTK_IS_CHECK_MENU_ITEM(widget) )
		gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget),
			flag&(VMF_CHECKED|VMF_FLAGGED));
}

void
VCustomizedMenuBar::set_menu_flag_do(VMenuItem *item, short flag)
{
	v_serialized_exec_sub(v_set_menu_flag_do, item->info.widget, flag);
}

void
v_set_menu_name_do(GtkMenuItem *widget, const L_CHAR *name)
{
	gtk_label_set_text(
		GTK_LABEL(gtk_bin_get_child(GTK_BIN(widget))),
		ucd_n_string(name));
}

void
VCustomizedMenuBar::set_menu_name_do(VMenuItem *item, const L_CHAR *name)
{
	v_serialized_exec_sub(v_set_menu_name_do, item->info.widget, name);
}


// --------------- Edit Menu Update Implementaion ---------------

extern "C" void
VCustomizedMenuBarImp::setup_edit_menu_contextual(
	GtkMenu *menu, VMenuItem *item)
{
	((VCustomizedMenuBar*)(item->menu_bar))->update(item->submenu);
}

extern "C" void
VCustomizedMenuBarImp::setup_edit_menu_contextual_finish(
	GtkMenu *menu, VMenuItem *item)
{
	for ( VMenuItem *sub = item->submenu ; sub ; sub = sub->next ) {
		if ( sub->type >= VMT_EDIT_START && sub->type <= VMT_EDIT_END )
			gtk_widget_set_sensitive(GTK_WIDGET(sub->info.widget), true);
		if ( sub->submenu )
			setup_edit_menu_contextual_finish(menu, sub->submenu);
	}
}


// --------------- Window Menu Implementaion ---------------

struct WindowMenuList {
	GtkWidget *item;
	WindowMenuList *next;
} *window_menu_item_list = 0;

struct AppendWindowParam {
	GtkMenuShell	*shell;
	GtkWindow	*window;
};

extern "C" void
gtk_window_present_glue(GtkMenuItem *menu_item, GtkWindow *window)
{
	gtk_window_present(window);
}

extern "C" void
append_window_menu(GtkWindow *window, AppendWindowParam* param)
{
	const gchar *title = gtk_window_get_title(GTK_WINDOW(window));
	if ( title ) {
		GtkWidget *menu_item = gtk_check_menu_item_new_with_label(title);
		if ( param->window == window )
			gtk_check_menu_item_set_active(
				GTK_CHECK_MENU_ITEM(menu_item), true);
		gtk_widget_show(menu_item);
		gtk_menu_shell_append(param->shell, menu_item);
		gtk_signal_connect(GTK_OBJECT(menu_item), "activate", 
			GTK_SIGNAL_FUNC(gtk_window_present_glue), window);
		window_menu_item_list = new WindowMenuList
			((WindowMenuList){menu_item, window_menu_item_list});
	}
}

void
setup_window_menu(GtkMenu *menu, GtkWindow	*window)
{
	while ( window_menu_item_list ) {
		WindowMenuList *next = window_menu_item_list->next;
		gtk_widget_destroy(window_menu_item_list->item);
		delete window_menu_item_list;
		window_menu_item_list = next;
	}
	AppendWindowParam p = {GTK_MENU_SHELL(menu), window};
	g_list_foreach(gtk_window_list_toplevels(), GFunc(append_window_menu), &p);
}

// --------------- Menu Interface Implementaion ---------------


extern "C" {

V_CALLBACK_D(call_vobject_quit)
{
	vobject_quit();
}

void
VCustomizedMenuBarImp::menu_choosed(GtkMenuItem *menu_item, VMenuItem *item)
{
	if ( item->type == VMT_CLOSE )
		((VWindow*)(item->info.work))->attempt_close();
	else if ( item->type == VMT_QUIT )
		vq_insert_callback(0, call_vobject_quit, 0, 0, 0);
	else
		((VCustomizedMenuBar*)(item->menu_bar))->menu_choosed(item);
}

void
VCustomizedMenuBarImp::set_check_status(GtkCheckMenuItem *menu_item, VMenuItem *item)
{
	gtk_signal_handler_block_by_func(GTK_OBJECT(menu_item),
		GTK_SIGNAL_FUNC(VCustomizedMenuBarImp::menu_choosed), item);
	VCustomizedMenuBar::set_menu_flag_do(item, item->flag);
	gtk_signal_handler_unblock_by_func(GTK_OBJECT(menu_item),
		GTK_SIGNAL_FUNC(VCustomizedMenuBarImp::menu_choosed), item);
}

GtkWidget *
VCustomizedMenuBarImp::set_menu_item_checkable(GtkWidget *check_menu, gpointer sh_arg)
{
	gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(check_menu), TRUE);
	gtk_signal_connect(GTK_OBJECT(check_menu), "toggled",
		GTK_SIGNAL_FUNC(set_check_status), sh_arg);
	return check_menu;
}

gchar *
menu_type_to_gtk_stock_id(int type)
{
	switch( type ) {
		case VMT_OTHER:	return 0;
		case VMT_SETUP:	return GTK_STOCK_PREFERENCES;
		case VMT_ABOUT:	return 0;
		case VMT_CLOSE:	return GTK_STOCK_CLOSE;
		case VMT_QUIT:	return GTK_STOCK_QUIT;
		case VMT_UNDO:	return GTK_STOCK_UNDO;
		case VMT_REDO:	return GTK_STOCK_REDO;
		case VMT_CUT:	return GTK_STOCK_CUT;
		case VMT_COPY:	return GTK_STOCK_COPY;
		case VMT_PASTE:	return GTK_STOCK_PASTE;
		case VMT_CLEAR:	return GTK_STOCK_CLEAR;
		case VMT_SEL_ALL:	return 0;
	  default:		return 0;
	}
}

const char *
make_menu_mnemonic(L_CHAR *name, char access_key)
{
	if ( name == 0 )
		return "";
	
	char *ret;
	L_CHAR *ucd = code_convert_with_combine(
		name, l_strlen(name), utf8_cm.main_code,
		CBF_SRC_PLANE|CBF_DST_PLANE);
	
	if ( access_key == 0 ) {
		ret = n_string(&utf8_cm, ucd);
		d_f_ree(ucd);
		return ret;
	}
	
	L_CHAR *mname = (L_CHAR*)d_alloc(4*(l_strlen(ucd)+6));
	int mnemonic_found = 0, len;
	for ( len = 0 ; ucd[len] ; len++ ) {
		if ( ucd[len] == (L_CHAR)access_key ) {
			mnemonic_found = 1;
			mname[len] = '_';
			break;
		}
		mname[len] = ucd[len];
	}
	for ( ; ucd[len] ; len++ )
		mname[len+mnemonic_found] = ucd[len];
	if ( mnemonic_found == 0 ) {
		mname[len++] = ' ';
		mname[len++] = '(';
		mname[len++] = '_';
		mname[len++] = tolower(access_key);
		mname[len++] = ')';
	}
	mname[len+mnemonic_found] = 0;
	ret = n_string(&utf8_cm, mname);
	d_f_ree(mname);
	d_f_ree(ucd);
	return ret;
}

GtkWidget *
VCustomizedMenuBarImp::make_menu_items_sub(
	VMenuItem *items,
	GtkAccelGroup *accel_group,
	GtkWidget *menu_bar,
	VWindow *vwindow,
	VInfo *window)
{
	GtkWidget *ret;
	GtkWidget *menu_item, *image;
	const gchar *stock_id;
	
	if ( menu_bar )
		ret = 0;
	else
		ret = gtk_menu_new();
	
	
	for ( ; items ; items = items->next ) {
		
		if ( items->type == VMT_SEPARATOR ) {
			menu_item = gtk_menu_item_new();
		}
		else if ( (stock_id = menu_type_to_gtk_stock_id(items->type)) != 0 ) {
			menu_item = gtk_image_menu_item_new_with_mnemonic(
				make_menu_mnemonic(items->name, items->access_key));
			image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_MENU);
			gtk_widget_show(image);
			gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
		}
		else {
			const char *mname = 
				make_menu_mnemonic(items->name, items->access_key); // auto free
			if ( menu_bar || items->type >= VMT_EDIT_START && items->type <= VMT_EDIT_END )
				menu_item = gtk_menu_item_new_with_mnemonic(mname);
			else
				menu_item = set_menu_item_checkable(
					gtk_check_menu_item_new_with_mnemonic(mname), items);
		}
		
		gtk_signal_connect(GTK_OBJECT(menu_item), "activate", 
			GTK_SIGNAL_FUNC(menu_choosed), items);
		items->info.widget = GTK_MENU_ITEM(menu_item);
		items->info.window = GTK_WINDOW(window);
		items->info.work = 0;
		if ( items->submenu )
			gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
				make_menu_items_sub(items->submenu, accel_group, 0, vwindow, window));
		if ( items->shortcut_key ) {
			gtk_widget_add_accelerator(menu_item,
				"activate", accel_group, items->shortcut_key,
				modifiers_to_gdk_state(items->modifier), GTK_ACCEL_VISIBLE);
		}
		gtk_widget_show(menu_item);
		
		GtkWidget *edit_menu, *window_menu;
		switch ( items->type ) {
		  case VMT_OTHER:
		  case VMT_SETUP:
		  case VMT_ABOUT:
		  case VMT_QUIT:
			gtk_widget_set_sensitive(menu_item, (items->flag & VMF_ENABLED));
			break;
		  case VMT_CLOSE:
			items->info.work = vwindow;
			break;
		  case VMT_EDIT:
			edit_menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
			if ( edit_menu ) {
				gtk_signal_connect(GTK_OBJECT(edit_menu), "show",
					GTK_SIGNAL_FUNC(VCustomizedMenuBarImp::
						setup_edit_menu_contextual), items);
				gtk_signal_connect(GTK_OBJECT(edit_menu), "hide",
					GTK_SIGNAL_FUNC(VCustomizedMenuBarImp::
						setup_edit_menu_contextual_finish), items);
			}
			break;
		  case VMT_WINDOW:
			window_menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
			if ( window_menu == 0 ) {
				window_menu = gtk_menu_new();
				gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), window_menu);
			}
			gtk_signal_connect(GTK_OBJECT(window_menu), "show",
				GTK_SIGNAL_FUNC(setup_window_menu), window);
			break;
		  case VMT_HELP:
			gtk_menu_item_set_right_justified(GTK_MENU_ITEM(menu_item), true);
			break;
		}
		
		if ( menu_bar )
			gtk_menu_bar_append(menu_bar, menu_item);
		else
			gtk_menu_append(ret, menu_item);
	}
	return ret;
}

GtkMenuBar *
VCustomizedMenuBarImp::make_menu_items(
		VMenuItem *items,
		GtkAccelGroup *accel_group,
		VWindow *vwindow,
		VInfo *window)
{
	GtkWidget *ret = gtk_menu_bar_new();
	gtk_widget_show(ret);
	make_menu_items_sub(items, accel_group, ret, vwindow, window);
	return GTK_MENU_BAR(ret);
}

}

