/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */

/* AbiSource Application Framework
 * Copyright (C) 2003-2004 Hubert Figuiere
 * Copyright (C) 2004 Francis James Franklin
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * 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.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
 * 02111-1307, USA.
 */


#include "ut_debugmsg.h"

#include "ev_EditMethod.h"
#include "ev_CocoaMenuBar.h"

#include "xap_CocoaApp.h"
#include "xap_CocoaAppController.h"
#include "xap_CocoaFont.h"
#include "xap_CocoaModule.h"
#include "xap_CocoaPlugin.h"
#include "xap_CocoaToolPalette.h"
#include "xap_App.h"
#include "xap_Frame.h"

#include "ie_types.h"

#include "ap_Menu_Id.h"

struct EV_CocoaKeyEquiv
{
	XAP_Menu_Id		menuid;
	char *			equiv;
	unsigned int	modifier;
};

static struct EV_CocoaKeyEquiv KeyEquiv[] = {
	{	AP_MENU_ID_FILE_NEW,				"n",	NSCommandKeyMask									}, // Cmd-N
	{	AP_MENU_ID_FILE_NEW_USING_TEMPLATE,	"N",	NSCommandKeyMask									}, // Cmd-Shift-N
	{	AP_MENU_ID_FILE_OPEN,				"o",	NSCommandKeyMask									}, // Cmd-O
	{	AP_MENU_ID_FILE_SAVE,				"s",	NSCommandKeyMask									}, // Cmd-S
	{	AP_MENU_ID_FILE_SAVEAS,				"S",	NSCommandKeyMask									}, // Cmd-Shift-S
	{	AP_MENU_ID_FILE_CLOSE,				"w",	NSCommandKeyMask									}, // Cmd-W
	{	AP_MENU_ID_FILE_PAGESETUP,			"P",	NSCommandKeyMask									}, // Cmd-Shift-P
	{	AP_MENU_ID_FILE_PRINT,				"p",	NSCommandKeyMask									}, // Cmd-P
	{	AP_MENU_ID_EDIT_UNDO,				"z",	NSCommandKeyMask									}, // Cmd-Z
	{	AP_MENU_ID_EDIT_REDO,				"Z",	NSCommandKeyMask									}, // Cmd-Shift-Z
	{	AP_MENU_ID_EDIT_CUT,				"x",	NSCommandKeyMask									}, // Cmd-X
	{	AP_MENU_ID_EDIT_COPY,				"c",	NSCommandKeyMask									}, // Cmd-C
	{	AP_MENU_ID_EDIT_PASTE,				"v",	NSCommandKeyMask									}, // Cmd-V
	{	AP_MENU_ID_EDIT_PASTE_SPECIAL,		"V",	NSCommandKeyMask|NSAlternateKeyMask					}, // Cmd-Shift-Alt-V
	{	AP_MENU_ID_EDIT_SELECTALL,			"a",	NSCommandKeyMask									}, // Cmd-A
	{	AP_MENU_ID_EDIT_FIND,				"f",	NSCommandKeyMask									}, // Cmd-F
	{	AP_MENU_ID_EDIT_GOTO,				"j",	NSCommandKeyMask									}, // Cmd-J
	{	AP_MENU_ID_VIEW_RULER,				"r",	NSCommandKeyMask									}, // Cmd-R
	{	AP_MENU_ID_FMT_FONT,				"t",	NSCommandKeyMask									}, // Cmd-T
	{	AP_MENU_ID_FMT_BOLD,				"b",	NSCommandKeyMask									}, // Cmd-B
	{	AP_MENU_ID_FMT_ITALIC,				"i",	NSCommandKeyMask									}, // Cmd-I
	{	AP_MENU_ID_FMT_UNDERLINE,			"u",	NSCommandKeyMask									}, // Cmd-U
	{	AP_MENU_ID_TOOLS_SPELL,				":",	NSCommandKeyMask									}, // Cmd-:
	{	AP_MENU_ID_ALIGN_LEFT,				"{",	NSCommandKeyMask									}, // Cmd-{
	{	AP_MENU_ID_ALIGN_CENTER,			"|",	NSCommandKeyMask									}, // Cmd-|
	{	AP_MENU_ID_ALIGN_RIGHT,				"}",	NSCommandKeyMask									}, // Cmd-}
	{	0,									0,		0													}
};

@implementation XAP_CocoaApplication

- (void)terminate:(id)sender
{
	UT_UCS4String ucs4_empty;
	ev_EditMethod_invoke("querySaveAndExit", ucs4_empty);
}

- (void)orderFrontStandardAboutPanel:(id)sender
{
	UT_UCS4String ucs4_empty;
	ev_EditMethod_invoke("dlgAbout", ucs4_empty);
}

- (void)orderFrontPreferencesPanel:(id)sender
{
	UT_UCS4String ucs4_empty;
	ev_EditMethod_invoke("dlgOptions", ucs4_empty);
}

- (void)openContextHelp:(id)sender
{
	UT_UCS4String ucs4_empty; // Can we use this to override help-contents location? e.g., to bundle help files? [TODO]
	ev_EditMethod_invoke("helpContents", ucs4_empty); // [TODO: this needs to be redireced to firstResponder]
}

- (void)sendEvent:(NSEvent *)anEvent
{
	NSWindow * keyWindow = [self keyWindow];

	XAP_CocoaAppController * pController = (XAP_CocoaAppController *) [self delegate];

	bool bFrameIsActive = pController ? ([pController currentFrame] ? true : false) : false;

	bool bEventHandled = false;

	if ([anEvent type] == NSKeyDown)
		{
			unsigned int modifierFlags = [anEvent modifierFlags];

			if (modifierFlags & NSCommandKeyMask)
				{
					if ((bFrameIsActive || !keyWindow) && !(modifierFlags & (NSShiftKeyMask|NSControlKeyMask)))
						{
							NSString * str = [anEvent charactersIgnoringModifiers];
							if ([str length] == 1)
								{
									unichar uc;
									[str getCharacters:&uc];

									if ((uc & 0x7f) == uc)
										switch (static_cast<char>(uc))
											{
											case ',': // Cmd-, (preferences)
												if (!(modifierFlags & NSAlternateKeyMask))
													{
														UT_DEBUGMSG(("[XAP_CocoaApplication -sendEvent: (Cmd-,)]\n"));
														[self orderFrontPreferencesPanel:self];
														bEventHandled = true;
													}
												break;

											case '/': // how to do this, since ? = Shift-/
											case '?': // Cmd-? (help)
												if (!(modifierFlags & NSAlternateKeyMask))
													{
														UT_DEBUGMSG(("[XAP_CocoaApplication -sendEvent: (Cmd-?)]\n"));
														[self openContextHelp:self];
														bEventHandled = true;
													}
												break;

											case 'h': // Alt-Cmd-H (hide others)
												if (modifierFlags & NSAlternateKeyMask)
													{
														UT_DEBUGMSG(("[XAP_CocoaApplication -sendEvent: (Alt-Cmd-H)]\n"));
														[self hideOtherApplications:self];
														bEventHandled = true;
													}
												else  // Cmd-H (hide application)
													{
														UT_DEBUGMSG(("[XAP_CocoaApplication -sendEvent: (Cmd-H)]\n"));
														[self hide:self];
														bEventHandled = true;
													}
												break;

											case 'm': // Cmd-M (minimize current frame/window)
												if (bFrameIsActive && keyWindow && !(modifierFlags & NSAlternateKeyMask))
													{
														UT_DEBUGMSG(("[XAP_CocoaApplication -sendEvent: (Cmd-M)]\n"));
														[keyWindow miniaturize:self];
														bEventHandled = true;
													}
												break;

											case 'n': // Cmd-N (open untitled)
												if (pController && !(modifierFlags & NSAlternateKeyMask))
													{
														UT_DEBUGMSG(("[XAP_CocoaApplication -sendEvent: (Cmd-N)]\n"));
														[pController applicationOpenUntitledFile:self];
														bEventHandled = true;
													}
												break;

											case 'o': // Cmd-O (open file)
												if (pController && !(modifierFlags & NSAlternateKeyMask))
													{
														UT_DEBUGMSG(("[XAP_CocoaApplication -sendEvent: (Cmd-O)]\n"));
														[pController applicationOpenFile:self];
														bEventHandled = true;
													}
												break;

											case 'q': // Cmd-Q (quit)
												if (!(modifierFlags & NSAlternateKeyMask))
													{
														UT_DEBUGMSG(("[XAP_CocoaApplication -sendEvent: (Cmd-Q)]\n"));
														[self terminate:self];
														bEventHandled = true;
													}
												break;

											default:
												break;
											}
								}
						}

					if (!bEventHandled && false /* m_MenuDelegate */ && bFrameIsActive)
						{
							id  target;
							SEL action;

							if (false /* [m_MenuDelegate menuHasKeyEquivalent:[self mainMenu] forEvent:anEvent target:&target action:&action] */)
								{
									[self sendAction:action to:target from:self];
									bEventHandled = true;
								}
						}
					else if (!bEventHandled && keyWindow && !bFrameIsActive)
						{
							NSString * str = [anEvent charactersIgnoringModifiers];
							if ([str length] == 1)
								{
									unichar uc;
									[str getCharacters:&uc];

									if ((uc & 0x7f) == uc)
										switch (static_cast<char>(uc))
											{
											case 'a': // In other applications this would normally be handled by the Edit menu
												{
													if (NSResponder * firstResponder = [keyWindow firstResponder])
														{
															[firstResponder selectAll:self];
															bEventHandled = true;
														}
													break;
												}
											case 'c': // In other applications this would normally be handled by the Edit menu
												{
													if (NSResponder * firstResponder = [keyWindow firstResponder])
														if ([firstResponder respondsToSelector:@selector(copy:)])
															{
																NSText * textResponder = (NSText *) firstResponder;
																[textResponder copy:self];
																bEventHandled = true;
															}
													break;
												}
											case 'v': // In other applications this would normally be handled by the Edit menu
												{
													if (NSResponder * firstResponder = [keyWindow firstResponder])
														if ([firstResponder respondsToSelector:@selector(paste:)])
															{
																NSText * textResponder = (NSText *) firstResponder;
																[textResponder paste:self];
																bEventHandled = true;
															}
													break;
												}
											case 'x': // In other applications this would normally be handled by the Edit menu
												{
													if (NSResponder * firstResponder = [keyWindow firstResponder])
														if ([firstResponder respondsToSelector:@selector(cut:)])
															{
																NSText * textResponder = (NSText *) firstResponder;
																[textResponder cut:self];
																bEventHandled = true;
															}
													break;
												}
											default:
												break;
											}
								}
						}
				}
		}
	if (!bEventHandled)
		{
			[super sendEvent:anEvent];
		}
}

@end

static XAP_CocoaAppController * XAP_AppController_Instance = nil;

@implementation XAP_CocoaAppController

+ (XAP_CocoaAppController*)sharedAppController // do we really need/want this?? the app controller is instantiated within the application's nib
{
	if (!XAP_AppController_Instance)
		{
			[[XAP_CocoaAppController alloc] init];
		}
	return XAP_AppController_Instance;
}

- (id)init 
{
	if (XAP_AppController_Instance)
		{
			NSLog (@"Attempt to allocate more that one XAP_CocoaAppController");
			return nil;
		}

	if (self = [super init])
		{
			XAP_AppController_Instance = self;

			m_FilesRequestedDuringLaunch = [[NSMutableArray alloc] initWithCapacity:8];

			m_bApplicationLaunching   = YES;

			m_bAutoLoadPluginsAfterLaunch = NO;

			m_PanelMenu   = [[NSMenu alloc] initWithTitle:@"Panels"];
			m_ContextMenu = [[NSMenu alloc] initWithTitle:@"Context Menu"];

			m_FontReferenceDictionary = [[NSMutableDictionary alloc] initWithCapacity:128];
			m_FontFamilyDictionary    = [XAP_CocoaFontFamilyHelper fontFamilyHelperDictionary:m_FontReferenceDictionary];

			[m_FontFamilyDictionary retain];

			m_MenuIDRefDictionary = [[NSMutableDictionary alloc] initWithCapacity:16];

			m_Plugins      = [[NSMutableArray alloc] initWithCapacity:16];
			m_PluginsTools = [[NSMutableArray alloc] initWithCapacity:16];

			m_PluginsToolsSeparator = [NSMenuItem separatorItem];
			[m_PluginsToolsSeparator retain];
		}
	return self;
}

- (void)dealloc
{
	if (m_FilesRequestedDuringLaunch)
		{
			[m_FilesRequestedDuringLaunch release];
			m_FilesRequestedDuringLaunch = 0;
		}
	if (m_PanelMenu)
		{
			[m_PanelMenu release];
			m_PanelMenu = 0;
		}
	if (m_ContextMenu)
		{
			[m_ContextMenu release];
			m_ContextMenu = 0;
		}
	if (m_FontReferenceDictionary)
		{
			[m_FontReferenceDictionary release];
			m_FontReferenceDictionary = 0;
		}
	if (m_FontFamilyDictionary)
		{
			[m_FontFamilyDictionary release];
			m_FontFamilyDictionary = 0;
		}
	if (m_MenuIDRefDictionary)
		{
			[m_MenuIDRefDictionary release];
			m_MenuIDRefDictionary = 0;
		}
	if (m_Plugins)
		{
			[m_Plugins release];
			m_Plugins = 0;
		}
	if (m_PluginsTools)
		{
			[m_PluginsTools release];
			m_PluginsTools = 0;
		}
	if (m_PluginsToolsSeparator)
		{
			[m_PluginsToolsSeparator release];
			m_PluginsToolsSeparator = 0;
		}
	[super dealloc];
}

- (void)setAutoLoadPluginsAfterLaunch:(BOOL)autoLoadPluginsAfterLaunch
{
	m_bAutoLoadPluginsAfterLaunch = autoLoadPluginsAfterLaunch;
}

- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key
{
	return [key isEqualToString:@"orderedDocuments"];
}

- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
	if (const char * home = getenv("HOME"))
		{
			NSString * desktop = [[NSString stringWithUTF8String:home] stringByAppendingPathComponent:@"Desktop"];

			[[NSFileManager defaultManager] changeCurrentDirectoryPath:desktop];
		}
	if (NSMenu * menu = [NSApp windowsMenu])
		if (NSMenuItem * item = [[NSMenuItem alloc] initWithTitle:@"Panels" action:nil keyEquivalent:@""])
			{
				[menu addItem:item];
				[item setSubmenu:m_PanelMenu];
				[item release];
			}
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
	UT_DEBUGMSG(("[XAP_CocoaAppController -applicationDidFinishLaunching:]\n"));
	m_bApplicationLaunching = NO;

	XAP_CocoaApp * pApp = static_cast<XAP_CocoaApp *>(XAP_App::getApp());

	if (EV_CocoaMenuBar * pCocoaMenuBar = pApp->getCocoaMenuBar())
		{
			m_AppItem[XAP_CocoaAppMenu_File  ] = oMenuItem_File;
			m_AppItem[XAP_CocoaAppMenu_Edit  ] = oMenuItem_Edit;
			m_AppItem[XAP_CocoaAppMenu_View  ] = oMenuItem_View;
			m_AppItem[XAP_CocoaAppMenu_Insert] = oMenuItem_Insert;
			m_AppItem[XAP_CocoaAppMenu_Format] = oMenuItem_Format;
			m_AppItem[XAP_CocoaAppMenu_Tools ] = oMenuItem_Tools;
			m_AppItem[XAP_CocoaAppMenu_Table ] = oMenuItem_Table;
			m_AppItem[XAP_CocoaAppMenu_Window] = oMenuItem_Window;
			m_AppItem[XAP_CocoaAppMenu_Help  ] = oMenuItem_Help;

			m_AppMenu[XAP_CocoaAppMenu_File  ] = oMenu_File;
			m_AppMenu[XAP_CocoaAppMenu_Edit  ] = oMenu_Edit;
			m_AppMenu[XAP_CocoaAppMenu_View  ] = oMenu_View;
			m_AppMenu[XAP_CocoaAppMenu_Insert] = oMenu_Insert;
			m_AppMenu[XAP_CocoaAppMenu_Format] = oMenu_Format;
			m_AppMenu[XAP_CocoaAppMenu_Tools ] = oMenu_Tools;
			m_AppMenu[XAP_CocoaAppMenu_Table ] = oMenu_Table;
			m_AppMenu[XAP_CocoaAppMenu_Window] = oMenu_Window;
			m_AppMenu[XAP_CocoaAppMenu_Help  ] = oMenu_Help;

			UT_DEBUGMSG(("[...FinishLaunching] Building application menu:\n"));
			pCocoaMenuBar->buildAppMenu();
		}
	[XAP_CocoaToolPalette instance:self];

	if (m_bAutoLoadPluginsAfterLaunch)
		{
			UT_DEBUGMSG(("[...FinishLaunching] Auto-Loading plug-ins:\n"));
			XAP_CocoaModule::loadAllPlugins();
		}

	BOOL bFileOpenedDuringLaunch = NO;

	unsigned count = [m_FilesRequestedDuringLaunch count];

	for (unsigned i = 0; i < count; i++) // filter out plugins from list and open those first
		{
			NSString * filename = (NSString *) [m_FilesRequestedDuringLaunch objectAtIndex:i];

			UT_UTF8String path([filename UTF8String]);

			if (XAP_CocoaModule::hasPluginExtension(path))
				{
					[self application:NSApp openFile:filename];
				}
		}
	for (unsigned i = 0; i < count; i++) // open the rest
		{
			NSString * filename = (NSString *) [m_FilesRequestedDuringLaunch objectAtIndex:i];

			UT_UTF8String path([filename UTF8String]);

			if (!XAP_CocoaModule::hasPluginExtension(path))
				if ([self application:NSApp openFile:filename])
					{
						bFileOpenedDuringLaunch = YES;
					}
		}
	if (bFileOpenedDuringLaunch == NO)
		{
			UT_DEBUGMSG(("[...FinishLaunching] No file opened during launch, so opening untitled document:\n"));
			[self applicationOpenUntitledFile:NSApp];
		}
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender // probably unused now that NSApp's terminate is overridden
{
	UT_DEBUGMSG(("- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender\n"));
	UT_UCS4String ucs4_empty;
	bool bQuit = ev_EditMethod_invoke("querySaveAndExit", ucs4_empty);
	return (bQuit ? NSTerminateNow : NSTerminateCancel);
}

- (void)applicationWillTerminate:(NSNotification *)aNotification // probably unused now that NSApp's terminate is overridden
{
	if ([XAP_CocoaToolPalette instantiated])
		{
			[[XAP_CocoaToolPalette instance:self] close];
		}
}

- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
	if (m_bApplicationLaunching == YES)
		{
			[m_FilesRequestedDuringLaunch addObject:filename];
			return YES;
		}

	UT_DEBUGMSG(("Requested to open %s\n", [filename UTF8String]));

	UT_UTF8String path([filename UTF8String]);

	if (XAP_CocoaModule::hasPluginExtension(path))
		{
			return (XAP_CocoaModule::loadPlugin(path) ? YES : NO);
		}

	XAP_App * pApp = XAP_App::getApp();
	XAP_Frame * pNewFrame = pApp->newFrame();

	bool result = (UT_OK == pNewFrame->loadDocument([filename UTF8String], IEFT_Unknown));
	if (result)
	{
		/*
		 * TODO: check what we should really do now
		 */
	}
	if (result)
	{
		pNewFrame->show();
	}
	return (result ? YES : NO);
}

- (BOOL)application:(NSApplication *)theApplication openTempFile:(NSString *)filename
{
	/*
		TODO: really open temp file. ie, delete it when done
	 */
	UT_DEBUGMSG(("Requested to open temp file %s\n", [filename UTF8String]));
	return [self application:theApplication openFile:filename];
}

- (BOOL)application:(NSApplication *)theApplication printFile:(NSString *)filename
{
	/*
		TODO: really print the file.
	 */
	UT_DEBUGMSG(("Requested to print %s\n", [filename UTF8String]));
	return [self application:theApplication openFile:filename];
}

- (BOOL)applicationOpenUntitledFile:(NSApplication *)theApplication
{
	if (m_bApplicationLaunching == YES)
		{
			return YES;
		}

	UT_DEBUGMSG(("Requested to open untitled file...\n"));

	EV_EditMethodContainer * pEMC = XAP_App::getApp()->getEditMethodContainer();
	if (!pEMC)
		return NO;

	EV_EditMethod * pEM = pEMC->findEditMethodByName("fileNew");
	if (!pEM)
		return NO;

	return (pEM->Fn(0,0) ? YES : NO);
}

- (BOOL)applicationOpenFile:(NSApplication *)theApplication
{
	EV_EditMethodContainer * pEMC = XAP_App::getApp()->getEditMethodContainer();
	if (!pEMC)
		return NO;

	EV_EditMethod * pEM = pEMC->findEditMethodByName("fileOpen");
	if (!pEM)
		return NO;

	return (pEM->Fn(0,0) ? YES : NO);
}

- (id)dockFileNew:(id)sender
{
	[self applicationOpenUntitledFile:NSApp];
	return self;
}

- (id)dockFileOpen:(id)sender
{
	[self applicationOpenFile:NSApp];
	return self;
}
/*
- (NSMenu *)applicationDockMenu:(NSApplication *)sender
{
	XAP_CocoaApp * pCocoaApp = static_cast<XAP_CocoaApp *>(XAP_App::getApp());
	return (NSMenu *) pCocoaApp->getDockNSMenu ();
}
 */
/* For building the Application Menu
 */
- (void)setAboutTitle:(NSString *)title
{
	[oMenuItem_AboutAbiWord setTitle:title];
}

- (void)setPrefsTitle:(NSString *)title
{
	[oMenuItem_Preferences setTitle:title];
}

- (void)setCHelpTitle:(NSString *)title // context-help
{
	[oMenuItem_AbiWordHelp setTitle:title];
}

- (void)setTitle:(NSString *)title forMenu:(XAP_CocoaAppMenu_Id)appMenu
{
	switch (appMenu)
		{
		case XAP_CocoaAppMenu_File:
		case XAP_CocoaAppMenu_Edit:
		case XAP_CocoaAppMenu_View:
		case XAP_CocoaAppMenu_Insert:
		case XAP_CocoaAppMenu_Format:
		case XAP_CocoaAppMenu_Tools:
		case XAP_CocoaAppMenu_Table:
		case XAP_CocoaAppMenu_Window: // ??
		case XAP_CocoaAppMenu_Help:
			[m_AppItem[appMenu] setTitle:title];
			break;

		case XAP_CocoaAppMenu_AbiWord: // disallow

		case XAP_CocoaAppMenu_count__:
		default:
			// shouldn't really happen, but...
			break;
		}
}

- (const char *)keyEquivalentForMenuID:(int /* XAP_Menu_Id */)menuid modifierMask:(unsigned int *)mask
{
	const char * equiv = 0;

	struct EV_CocoaKeyEquiv * pKE = KeyEquiv;
	while (pKE->equiv)
		{
			if (menuid == (int) pKE->menuid)
				{
					equiv = pKE->equiv;
					if ( mask)
						*mask = pKE->modifier;
					break;
				}
			++pKE;
		}
	return equiv;
}

- (NSMenu *)panelMenu
{
	return m_PanelMenu;
}

- (NSMenu *)contextMenu
{
	return m_ContextMenu;
}

- (void)appendPanelItem:(NSMenuItem *)item
{
	[m_PanelMenu addItem:item];
}

- (void)appendContextItem:(NSMenuItem *)item
{
	[m_ContextMenu addItem:item];
}

- (void)appendItem:(NSMenuItem *)item toMenu:(XAP_CocoaAppMenu_Id)appMenu
{
	switch (appMenu)
		{
		case XAP_CocoaAppMenu_File:
		case XAP_CocoaAppMenu_Edit:
		case XAP_CocoaAppMenu_View:
		case XAP_CocoaAppMenu_Insert:
		case XAP_CocoaAppMenu_Format:
		case XAP_CocoaAppMenu_Tools:
		case XAP_CocoaAppMenu_Table:
		case XAP_CocoaAppMenu_Help:
			{
				// UT_DEBUGMSG(("appendItem: toMenu:\"%s\"\n", [[m_AppItem[appMenu] title] UTF8String]));
				[m_AppMenu[appMenu] addItem:item];
			}
			break;

		case XAP_CocoaAppMenu_AbiWord:
		case XAP_CocoaAppMenu_Window:
			// these menus treated separately

		case XAP_CocoaAppMenu_count__:
		default:
			// shouldn't really happen, but...
			break;
		}
}

- (void)clearContextMenu
{
	while (int count = [m_ContextMenu numberOfItems])
		{
			[m_ContextMenu removeItemAtIndex:(count - 1)];
		}
}

- (void)clearMenu:(XAP_CocoaAppMenu_Id)appMenu
{
	switch (appMenu)
		{
		case XAP_CocoaAppMenu_File:
		case XAP_CocoaAppMenu_Edit:
		case XAP_CocoaAppMenu_View:
		case XAP_CocoaAppMenu_Insert:
		case XAP_CocoaAppMenu_Format:
		case XAP_CocoaAppMenu_Tools:
		case XAP_CocoaAppMenu_Table:
			while (int count = [m_AppMenu[appMenu] numberOfItems])
				{
					[m_AppMenu[appMenu] removeItemAtIndex:(count - 1)];
				}
			break;

		case XAP_CocoaAppMenu_Help:
			while (int count = [m_AppMenu[appMenu] numberOfItems])
				{
					if (count == 1) // the first Help item (context-help) is treated separately
						break;
					[m_AppMenu[appMenu] removeItemAtIndex:(count - 1)];
				}
			break;

		case XAP_CocoaAppMenu_AbiWord:
		case XAP_CocoaAppMenu_Window:
			// these menus treated separately

		case XAP_CocoaAppMenu_count__:
		default:
			// shouldn't really happen, but...
			break;
		}
}

- (void)clearAllMenus
{
	// !AbiWord
	[self clearMenu:XAP_CocoaAppMenu_File  ];
	[self clearMenu:XAP_CocoaAppMenu_Edit  ];
	[self clearMenu:XAP_CocoaAppMenu_View  ];
	[self clearMenu:XAP_CocoaAppMenu_Insert];
	[self clearMenu:XAP_CocoaAppMenu_Format];
	[self clearMenu:XAP_CocoaAppMenu_Tools ];
	[self clearMenu:XAP_CocoaAppMenu_Table ];
	// !Windows
	[self clearMenu:XAP_CocoaAppMenu_Help  ];
}

- (NSString *)familyNameForFont:(NSString *)fontName
{
	NSString * familyName = nil;

	if (XAP_CocoaFontReference * fontRef = [m_FontReferenceDictionary objectForKey:fontName])
		{
			familyName = [fontRef fontFamily];
		}
	return familyName;
}

- (XAP_CocoaFontReference *)helperReferenceForFont:(NSString *)fontName
{
	return [m_FontReferenceDictionary objectForKey:fontName];
}

- (XAP_CocoaFontFamilyHelper *)helperForFontFamily:(NSString *)fontFamilyName
{
	return [m_FontFamilyDictionary objectForKey:fontFamilyName];
}

- (XAP_CocoaFontFamilyHelper *)helperForUnknownFontFamily:(NSString *)fontFamilyName
{
	XAP_CocoaFontFamilyHelper * helper = [[XAP_CocoaFontFamilyHelper alloc] initWithFontFamilyName:fontFamilyName known:NO];

	[m_FontFamilyDictionary setObject:helper forKey:fontFamilyName];

	[helper addFontReferences:m_FontReferenceDictionary];
	[helper release];

	return helper;
}

- (void)appendPluginMenuItem:(NSMenuItem *)menuItem
{
	if (![m_PluginsTools containsObject:menuItem])
		{
			if ([m_PluginsTools count] == 0)
				{
					[m_AppMenu[XAP_CocoaAppMenu_Tools] addItem:m_PluginsToolsSeparator];
				}
			[m_AppMenu[XAP_CocoaAppMenu_Tools] addItem:menuItem];

			[m_PluginsTools addObject:menuItem];
		}
}

- (void)removePluginMenuItem:(NSMenuItem *)menuItem
{
	if ([m_PluginsTools containsObject:menuItem])
		{
			[m_AppMenu[XAP_CocoaAppMenu_Tools] removeItem:menuItem];

			if ([m_PluginsTools count] == 1)
				{
					[m_AppMenu[XAP_CocoaAppMenu_Tools] removeItem:m_PluginsToolsSeparator];
				}
			[m_PluginsTools removeObject:menuItem];
		}
}

/* Do we need this? getLastFocussedFrame() should be tracking this now... [TODO!!]
 */
- (void)setCurrentView:(AV_View *)view inFrame:(XAP_Frame *)frame
{
	if (frame)
		{
			XAP_App * pApp = XAP_App::getApp();

			pApp->clearLastFocussedFrame();
			pApp->rememberFocussedFrame(static_cast<void *>(frame));
		}

	m_pViewPrevious  = m_pViewCurrent;
	m_pFramePrevious = m_pFrameCurrent;

	m_pViewCurrent  = view;
	m_pFrameCurrent = frame;

	[self notifyFrameViewChange];
}

- (void)resetCurrentView:(AV_View *)view inFrame:(XAP_Frame *)frame
{
	// UT_DEBUGMSG(("XAP_CocoaAppController - (void)resetCurrentView:(AV_View *)view inFrame:(XAP_Frame *)frame\n"));
	if (m_pFrameCurrent == frame)
		{
			m_pViewCurrent = view;
			[self notifyFrameViewChange];
		}
}

- (void)unsetCurrentView:(AV_View *)view inFrame:(XAP_Frame *)frame
{
	XAP_App * pApp = XAP_App::getApp();

	if (pApp->getLastFocussedFrame() == frame)
		{
			pApp->clearLastFocussedFrame();
		}
	if ((m_pViewCurrent == view) && (m_pFrameCurrent == frame))
		{
			m_pViewCurrent = 0;
			m_pFrameCurrent = 0;
		}
	if ((m_pViewPrevious == view) && (m_pFramePrevious == frame))
		{
			m_pViewPrevious = 0;
			m_pFramePrevious = 0;
		}
	[self notifyFrameViewChange];
}

- (AV_View *)currentView
{
	return m_pViewCurrent;
}

- (XAP_Frame *)currentFrame
{
	return m_pFrameCurrent;
}

- (AV_View *)previousView
{
	return m_pViewPrevious;
}

- (XAP_Frame *)previousFrame
{
	return m_pFramePrevious;
}

- (void)notifyFrameViewChange
{
	if ([XAP_CocoaToolPalette instantiated])
		{
			[[XAP_CocoaToolPalette instance:self] setCurrentView:m_pViewCurrent inFrame:m_pFrameCurrent];
		}

	unsigned count = [m_Plugins count];

	for (unsigned i = 0; i < count; i++)
		{
			XAP_CocoaPlugin * plugin = (XAP_CocoaPlugin *) [m_Plugins objectAtIndex:i];
			[[plugin delegate] pluginCurrentDocumentHasChanged];
		}
}

/* load .Abi bundle plugin at path, returns nil on failure
 */
- (XAP_CocoaPlugin *)loadPlugin:(NSString *)path
{
	if (!path)
		return nil;

	XAP_CocoaPlugin * cocoa_plugin = [[XAP_CocoaPlugin alloc] init];
	if (!cocoa_plugin)
		return nil;

	if ([cocoa_plugin loadBundleWithPath:path])
		{
			[m_Plugins addObject:cocoa_plugin];
		}
	else
		{
			[cocoa_plugin release];
			cocoa_plugin = nil;
		}
	return cocoa_plugin;
}

/* list of currently loaded plugins
 */
- (NSArray *)plugins
{
	return m_Plugins;
}

/* checks to see whether the plugins can deactivate, and, if they can, deactivates them;
 * returns false if any of the plugins object
 */
- (BOOL)deactivateAllPlugins
{
	unsigned count = [m_Plugins count];

	BOOL bCanDeactivate = YES;

	for (unsigned i = 0; i < count; i++)
		{
			XAP_CocoaPlugin * plugin = (XAP_CocoaPlugin *) [m_Plugins objectAtIndex:i];

			bCanDeactivate = [[plugin delegate] pluginCanDeactivate];
			if (!bCanDeactivate)
				break;
		}
	if (!bCanDeactivate)
		return NO;

	for (unsigned i = 0; i < count; i++)
		{
			XAP_CocoaPlugin * plugin = (XAP_CocoaPlugin *) [m_Plugins objectAtIndex:i];

			[[plugin delegate] pluginDeactivate];
		}
	return YES;
}

/* checks to see whether the plugins can deactivate, and, if they can, deactivates them;
 * returns false if the plugin objects, unless override is YES.
 */
- (BOOL)deactivatePlugin:(XAP_CocoaPlugin *)plugin overridePlugin:(BOOL)override
{
	if (!override)
		{
			if (![[plugin delegate] pluginCanDeactivate])
				return NO;
		}
	[[plugin delegate] pluginDeactivate];

	return YES;
}

/* This provides a mechanism for associating XAP_CocoaPlugin_MenuItem objects
 * with a given menu ID.
 */
- (void)addRef:(AP_CocoaPlugin_MenuIDRef *)ref forMenuID:(NSNumber *)menuid
{
	[m_MenuIDRefDictionary setObject:ref forKey:menuid];
}

/* This provides a mechanism for finding XAP_CocoaPlugin_MenuItem objects associated
 * with a given menu ID.
 */
- (AP_CocoaPlugin_MenuIDRef *)refForMenuID:(NSNumber *)menuid
{
	return (AP_CocoaPlugin_MenuIDRef *) [m_MenuIDRefDictionary objectForKey:menuid];
}

/* This provides a mechanism for removing XAP_CocoaPlugin_MenuItem objects associated
 * with a given menu ID.
 */
- (void)removeRefForMenuID:(NSNumber *)menuid
{
	[m_MenuIDRefDictionary removeObjectForKey:menuid];
}

@end
