/*
 * Copyright 1999-2009 Jeremy Friesner
 * Copyright 2009-2010 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Jeremy Friesner
 *		Fredrik Modéen
 */
 
 
#include "ShortcutsWindow.h"
 
#include <math.h>
#include <stdio.h>
 
#include <Alert.h>
#include <Application.h>
#include <Button.h>
#include <Catalog.h>
#include <Clipboard.h>
#include <ColumnListView.h>
#include <ColumnTypes.h>
#include <ControlLook.h>
#include <File.h>
#include <FilePanel.h>
#include <FindDirectory.h>
#include <Input.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <Message.h>
#include <Menu.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <MessageFilter.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Screen.h>
#include <ScrollBar.h>
#include <ScrollView.h>
#include <String.h>
#include <SupportDefs.h>
 
#include "EditWindow.h"
#include "KeyInfos.h"
#include "MetaKeyStateMap.h"
#include "ParseCommandLine.h"
#include "PopUpColumn.h"
#include "ShortcutsFilterConstants.h"
#include "ShortcutsSpec.h"
 
 
// Window sizing constraints
#define MAX_WIDTH 10000
#define MAX_HEIGHT 10000
	// SetSizeLimits does not provide a mechanism for specifying an
	// unrestricted maximum. 10,000 seems to be the most common value used
	// in other Haiku system applications.
 
#define WINDOW_SETTINGS_FILE_NAME "Shortcuts_window_settings"
	// Because the "shortcuts_settings" file (SHORTCUTS_SETTING_FILE_NAME) is
	// already used as a communications method between this configurator and
	// the "shortcut_catcher" input_server filter, it should not be overloaded
	// with window position information, instead, a separate file is used.
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ShortcutsWindow"
 
#define ERROR "Shortcuts error"
#define WARNING "Shortcuts warning"
 
 
// Creates a pop-up-menu that reflects the possible states of the specified
// meta-key.
static BPopUpMenu*
CreateMetaPopUp(int column)
{
	MetaKeyStateMap& map = GetNthKeyMap(column);
	BPopUpMenu* popup = new BPopUpMenu(NULL, false);
	int stateCount = map.GetNumStates();
 
	for (int i = 0; i < stateCount; i++)
		popup->AddItem(new BMenuItem(map.GetNthStateDesc(i), NULL));
 
	return popup;
}
 
 
// Creates a pop-up that allows the user to choose a key-cap visually
static BPopUpMenu*
CreateKeysPopUp()
{
	BPopUpMenu* popup = new BPopUpMenu(NULL, false);
	int numKeys = GetNumKeyIndices();
	for (int i = 0; i < numKeys; i++) {
		const char* next = GetKeyName(i);
		if (next != NULL)
			popup->AddItem(new BMenuItem(next, NULL));
	}
 
	return popup;
}
 
 
ShortcutsWindow::ShortcutsWindow()
	:
	BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Shortcuts"),
		B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS),
	fSavePanel(NULL),
	fOpenPanel(NULL),
	fSelectPanel(NULL),
	fKeySetModified(false),
	fLastOpenWasAppend(false)
{
	ShortcutsSpec::InitializeMetaMaps();
 
	BMenuBar* menuBar = new BMenuBar("Menu Bar");
 
	BMenu* fileMenu = new BMenu(B_TRANSLATE("File"));
	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Open KeySet" B_UTF8_ELLIPSIS),
		new BMessage(OPEN_KEYSET), 'O'));
	fileMenu->AddItem(new BMenuItem(
		B_TRANSLATE("Append KeySet" B_UTF8_ELLIPSIS),
		new BMessage(APPEND_KEYSET), 'A'));
	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Revert to saved"),
		new BMessage(REVERT_KEYSET), 'R'));
	fileMenu->AddItem(new BSeparatorItem);
	fileMenu->AddItem(new BMenuItem(
		B_TRANSLATE("Save KeySet as" B_UTF8_ELLIPSIS),
		new BMessage(SAVE_KEYSET_AS), 'S'));
	fileMenu->AddItem(new BSeparatorItem);
	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
		new BMessage(B_QUIT_REQUESTED), 'Q'));
	menuBar->AddItem(fileMenu);
 
	fColumnListView = new BColumnListView(NULL,
		B_WILL_DRAW | B_FRAME_EVENTS, B_FANCY_BORDER, false);
 
	float cellWidth = be_plain_font->StringWidth("Either") + 20;
		// ShortcutsSpec does not seem to translate the string "Either".
 
	for (int i = 0; i < ShortcutsSpec::NUM_META_COLUMNS; i++) {
		const char* name = ShortcutsSpec::GetColumnName(i);
		float headerWidth = be_plain_font->StringWidth(name) + 20;
		float width = max_c(headerWidth, cellWidth);
 
		fColumnListView->AddColumn(new PopUpColumn(CreateMetaPopUp(i), name,
				width, width - 1, width * 1.5, B_TRUNCATE_END, false, true, 1),
			fColumnListView->CountColumns());
	}
 
	float keyCellWidth = be_plain_font->StringWidth("Caps Lock") + 20;
	fColumnListView->AddColumn(new PopUpColumn(CreateKeysPopUp(),
			B_TRANSLATE("Key"), keyCellWidth, keyCellWidth - 10,
			keyCellWidth + 30, B_TRUNCATE_END),
		fColumnListView->CountColumns());
	BPopUpMenu* popup = new BPopUpMenu(NULL, false);
	popup->AddItem(new BMenuItem(
		B_TRANSLATE("(Choose application with file requester)"), NULL));
	popup->AddItem(new BMenuItem(
		B_TRANSLATE("*InsertString \"Your Text Here\""), NULL));
	popup->AddItem(new BMenuItem(
		B_TRANSLATE("*MoveMouse +20 +0"), NULL));
	popup->AddItem(new BMenuItem(B_TRANSLATE("*MoveMouseTo 50% 50%"), NULL));
	popup->AddItem(new BMenuItem(B_TRANSLATE("*MouseButton 1"), NULL));
	popup->AddItem(new BMenuItem(
		B_TRANSLATE("*LaunchHandler text/html"), NULL));
	popup->AddItem(new BMenuItem(
		B_TRANSLATE("*Multi \"*MoveMouseTo 100% 0\" \"*MouseButton 1\""),
		NULL));
	popup->AddItem(new BMenuItem(B_TRANSLATE("*MouseDown"), NULL));
	popup->AddItem(new BMenuItem(B_TRANSLATE("*MouseUp"), NULL));
	popup->AddItem(new BMenuItem(
		B_TRANSLATE("*SendMessage application/x-vnd.Be-TRAK 'Tfnd'"), NULL));
	popup->AddItem(new BMenuItem(B_TRANSLATE("*Beep"), NULL));
	fColumnListView->AddColumn(new PopUpColumn(popup, B_TRANSLATE("Application"),
			300.0, 223.0, 324.0, B_TRUNCATE_END, true),
		fColumnListView->CountColumns());
 
	fColumnListView->SetSelectionMessage(new BMessage(HOTKEY_ITEM_SELECTED));
	fColumnListView->SetSelectionMode(B_SINGLE_SELECTION_LIST);
	fColumnListView->SetTarget(this);
 
	fAddButton = new BButton("add", B_TRANSLATE("Add new shortcut"),
		new BMessage(ADD_HOTKEY_ITEM));
 
	fRemoveButton = new BButton("remove",
		B_TRANSLATE("Remove selected shortcut"),
		new BMessage(REMOVE_HOTKEY_ITEM));
	fRemoveButton->SetEnabled(false);
 
	fSaveButton = new BButton("save", B_TRANSLATE("Save & apply"),
		new BMessage(SAVE_KEYSET));
	fSaveButton->SetEnabled(false);
 
	CenterOnScreen();
 
	fColumnListView->ResizeAllColumnsToPreferred();
 
	entry_ref windowSettingsRef;
	if (_GetWindowSettingsFile(&windowSettingsRef)) {
		// The window settings file is not accepted via B_REFS_RECEIVED; this
		// is a behind-the-scenes file that the user will never see or
		// interact with.
		BFile windowSettingsFile(&windowSettingsRef, B_READ_ONLY);
		BMessage loadMessage;
		if (loadMessage.Unflatten(&windowSettingsFile) == B_OK)
			_LoadWindowSettings(loadMessage);
	}
 
	entry_ref keySetRef;
	if (_GetSettingsFile(&keySetRef)) {
		BMessage message(B_REFS_RECEIVED);
		message.AddRef("refs", &keySetRef);
		message.AddString("startupRef", "please");
		PostMessage(&message);
			// tell ourselves to load this file if it exists
	}
 
	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
		.Add(menuBar)
		.AddGroup(B_VERTICAL)
			.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
			.SetInsets(B_USE_WINDOW_SPACING)
			.Add(fColumnListView)
			.AddGroup(B_HORIZONTAL)
				.AddGroup(B_HORIZONTAL)
				.SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP))
				.Add(fAddButton)
				.Add(fRemoveButton)
				.End()
				.AddGroup(B_HORIZONTAL)
					.SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP))
					.Add(fSaveButton)
				.End()
			.End()
		.End();
 
	Show();
}
 
 
ShortcutsWindow::~ShortcutsWindow()
{
	delete fSavePanel;
	delete fOpenPanel;
	delete fSelectPanel;
	be_app->PostMessage(B_QUIT_REQUESTED);
}
 
 
bool
ShortcutsWindow::QuitRequested()
{
	bool result = true;
 
	if (fKeySetModified) {
		BAlert* alert = new BAlert(WARNING,
			B_TRANSLATE("Save changes before closing?"),
			B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"),
			B_TRANSLATE("Save"));
		alert->SetShortcut(0, B_ESCAPE);
		alert->SetShortcut(1, 'd');
		alert->SetShortcut(2, 's');
		switch (alert->Go()) {
			case 0:
				result = false;
				break;
 
			case 1:
				result = true;
				break;
 
			case 2:
				// Save: automatically if possible, otherwise go back and open
				// up the file requester
				if (fLastSaved.InitCheck() == B_OK) {
					if (_SaveKeySet(fLastSaved) == false) {
						BAlert* alert = new BAlert(ERROR,
							B_TRANSLATE("Shortcuts was unable to save your "
								"KeySet file!"),
							B_TRANSLATE("Oh no"));
						alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
						alert->Go();
						result = true; // quit anyway
					}
				} else {
					PostMessage(SAVE_KEYSET);
					result = false;
				}
				break;
		}
	}
 
	if (result) {
		fColumnListView->DeselectAll();
 
		// Save the window position.
		entry_ref ref;
		if (_GetWindowSettingsFile(&ref)) {
			BEntry entry(&ref);
			_SaveWindowSettings(entry);
		}
	}
 
	return result;
}
 
 
bool
ShortcutsWindow::_GetSettingsFile(entry_ref* eref)
{
	BPath path;
	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
		return false;
	else
		path.Append(SHORTCUTS_SETTING_FILE_NAME);
 
	if (BEntry(path.Path(), true).GetRef(eref) == B_OK)
		return true;
	else
		return false;
}
 
 
// Saves a settings file to (saveEntry). Returns true iff successful.
bool
ShortcutsWindow::_SaveKeySet(BEntry& saveEntry)
{
	BFile saveTo(&saveEntry, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
	if (saveTo.InitCheck() != B_OK)
		return false;
 
	BMessage saveMessage;
	for (int i = 0; i < fColumnListView->CountRows(); i++) {
		BMessage next;
		if (((ShortcutsSpec*)fColumnListView->RowAt(i))->Archive(&next)
				== B_OK) {
			saveMessage.AddMessage("spec", &next);
		} else
			printf("Error archiving ShortcutsSpec #%i!\n", i);
	}
 
	bool result = (saveMessage.Flatten(&saveTo) == B_OK);
 
	if (result) {
		fKeySetModified = false;
		fSaveButton->SetEnabled(false);
	}
 
	return result;
}
 
 
// Appends new entries from the file specified in the "spec" entry of
// (loadMessage). Returns true iff successful.
bool
ShortcutsWindow::_LoadKeySet(const BMessage& loadMessage)
{
	int i = 0;
	BMessage message;
	while (loadMessage.FindMessage("spec", i++, &message) == B_OK) {
		ShortcutsSpec* spec
			= (ShortcutsSpec*)ShortcutsSpec::Instantiate(&message);
		if (spec != NULL)
			fColumnListView->AddRow(spec);
		else
			printf("_LoadKeySet: Error parsing spec!\n");
	}
 
	return true;
}
 
 
// Gets the filesystem location of the "Shortcuts_window_settings" file.
bool
ShortcutsWindow::_GetWindowSettingsFile(entry_ref* eref)
{
	BPath path;
	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
		return false;
	else
		path.Append(WINDOW_SETTINGS_FILE_NAME);
 
	return BEntry(path.Path(), true).GetRef(eref) == B_OK;
}
 
 
// Saves the application settings file to (saveEntry).  Because this is a
// non-essential file, errors are ignored when writing the settings.
void
ShortcutsWindow::_SaveWindowSettings(BEntry& saveEntry)
{
	BFile saveTo(&saveEntry, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
	if (saveTo.InitCheck() != B_OK)
		return;
 
	BMessage saveMsg;
	saveMsg.AddRect("window frame", Frame());
 
	BMessage columnsState;
	fColumnListView->SaveState(&columnsState);
	saveMsg.AddMessage ("columns state", &columnsState);
 
	saveMsg.Flatten(&saveTo);
}
 
 
// Loads the application settings file from (loadMessage) and resizes
// the interface to match the previously saved settings. Because this
// is a non-essential file, errors are ignored when loading the settings.
void
ShortcutsWindow::_LoadWindowSettings(const BMessage& loadMessage)
{
	BRect frame;
	if (loadMessage.FindRect("window frame", &frame) == B_OK) {
		// ensure the frame does not resize below the computed minimum.
		float width = max_c(Bounds().right, frame.right - frame.left);
		float height = max_c(Bounds().bottom, frame.bottom - frame.top);
		ResizeTo(width, height);
 
		// ensure the frame is not placed outside of the screen.
		BScreen screen(this);
		float left = min_c(screen.Frame().right - width, frame.left);
		float top = min_c(screen.Frame().bottom - height, frame.top);
		MoveTo(left, top);
	}
 
	BMessage columnsStateMessage;
	if (loadMessage.FindMessage ("columns state", &columnsStateMessage) == B_OK)
		fColumnListView->LoadState(&columnsStateMessage);
}
 
 
// Creates a new entry and adds it to the GUI. (defaultCommand) will be the
// text in the entry, or NULL if no text is desired.
void
ShortcutsWindow::_AddNewSpec(const char* defaultCommand)
{
	_MarkKeySetModified();
 
	ShortcutsSpec* spec;
	BRow* curSel = fColumnListView->CurrentSelection();
	if (curSel)
		spec = new ShortcutsSpec(*((ShortcutsSpec*)curSel));
	else {
		spec = new ShortcutsSpec("");
		for (int i = 0; i < fColumnListView->CountColumns(); i++)
			spec->SetField(new BStringField(""), i);
	}
 
	fColumnListView->AddRow(spec);
	fColumnListView->AddToSelection(spec);
	fColumnListView->ScrollTo(spec);
	if (defaultCommand)
		spec->SetCommand(defaultCommand);
}
 
 
void
ShortcutsWindow::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case OPEN_KEYSET:
		case APPEND_KEYSET:
			fLastOpenWasAppend = (message->what == APPEND_KEYSET);
			if (fOpenPanel)
				fOpenPanel->Show();
			else {
				BMessenger messenger(this);
				fOpenPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL,
					0, false);
				fOpenPanel->Show();
			}
			fOpenPanel->SetButtonLabel(B_DEFAULT_BUTTON, fLastOpenWasAppend ?
				B_TRANSLATE("Append") : B_TRANSLATE("Open"));
			break;
 
		// send a message to myself, to get me to reload the settings file
		case REVERT_KEYSET:
		{
			fLastOpenWasAppend = false;
			BMessage reload(B_REFS_RECEIVED);
			entry_ref eref;
			_GetSettingsFile(&eref);
			reload.AddRef("refs", &eref);
			reload.AddString("startupRef", "yeah");
			PostMessage(&reload);
			break;
		}
 
		// respond to drag-and-drop messages here
		case B_SIMPLE_DATA:
		{
			int i = 0;
 
			entry_ref ref;
			while (message->FindRef("refs", i++, &ref) == B_OK) {
				BEntry entry(&ref);
				if (entry.InitCheck() == B_OK) {
					BPath path(&entry);
 
					if (path.InitCheck() == B_OK) {
						// Add a new item with the given path.
						BString str(path.Path());
						DoStandardEscapes(str);
						_AddNewSpec(str.String());
					}
				}
			}
			break;
		}
 
		// respond to FileRequester's messages here
		case B_REFS_RECEIVED:
		{
			// Find file ref
			entry_ref ref;
			bool isStartMsg = message->HasString("startupRef");
			if (message->FindRef("refs", &ref) == B_OK) {
				// load the file into (fileMsg)
				BMessage fileMsg;
				{
					BFile file(&ref, B_READ_ONLY);
					if ((file.InitCheck() != B_OK)
						|| (fileMsg.Unflatten(&file) != B_OK)) {
						if (isStartMsg) {
							// use this to save to anyway
							fLastSaved = BEntry(&ref);
							break;
						} else {
							BAlert* alert = new BAlert(ERROR,
								B_TRANSLATE("Shortcuts was couldn't open your "
								"KeySet file!"), B_TRANSLATE("OK"));
							alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
							alert->Go(NULL);
							break;
						}
					}
				}
 
				if (fLastOpenWasAppend == false) {
					// Clear the menu...
					while (fColumnListView->CountRows()) {
						ShortcutsSpec* row =
							static_cast<ShortcutsSpec*>(fColumnListView->RowAt(0));
						fColumnListView->RemoveRow(row);
						delete row;
					}
				}
 
				if (_LoadKeySet(fileMsg)) {
					if (isStartMsg) fLastSaved = BEntry(&ref);
					fSaveButton->SetEnabled(isStartMsg == false);
 
					// If we just loaded in the Shortcuts settings file, then
					// no need to tell the user to save on exit.
					entry_ref eref;
					_GetSettingsFile(&eref);
					if (ref == eref) fKeySetModified = false;
				} else {
					BAlert* alert = new BAlert(ERROR,
						B_TRANSLATE("Shortcuts was unable to parse your "
						"KeySet file!"), B_TRANSLATE("OK"));
					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
					alert->Go(NULL);
					break;
				}
			}
			break;
		}
 
		// these messages come from the pop-up menu of the Applications column
		case SELECT_APPLICATION:
		{
			ShortcutsSpec* row =
				static_cast<ShortcutsSpec*>(fColumnListView->CurrentSelection());
			if (row != NULL) {
				entry_ref aref;
				if (message->FindRef("refs", &aref) == B_OK) {
					BEntry ent(&aref);
					if (ent.InitCheck() == B_OK) {
						BPath path;
						if ((ent.GetPath(&path) == B_OK)
							&& (row->
								ProcessColumnTextString(ShortcutsSpec::STRING_COLUMN_INDEX,
									path.Path()))) {
							_MarkKeySetModified();
						}
					}
				}
			}
			break;
		}
 
		case SAVE_KEYSET:
		{
			bool showSaveError = false;
 
			const char* name;
			entry_ref entry;
			if ((message->FindString("name", &name) == B_OK)
				&& (message->FindRef("directory", &entry) == B_OK)) {
				BDirectory dir(&entry);
				BEntry saveTo(&dir, name, true);
				showSaveError = ((saveTo.InitCheck() != B_OK)
					|| (_SaveKeySet(saveTo) == false));
			} else if (fLastSaved.InitCheck() == B_OK) {
				// We've saved this before, save over previous file.
				showSaveError = (_SaveKeySet(fLastSaved) == false);
			} else
				PostMessage(SAVE_KEYSET_AS);
					// open the save requester...
 
			if (showSaveError) {
				BAlert* alert = new BAlert(ERROR,
					B_TRANSLATE("Shortcuts wasn't able to save your keyset."),
					B_TRANSLATE("OK"));
				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
				alert->Go(NULL);
			}
			break;
		}
 
		case SAVE_KEYSET_AS:
		{
			if (fSavePanel)
				fSavePanel->Show();
			else {
				BMessage message(SAVE_KEYSET);
				BMessenger messenger(this);
				fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, NULL, 0,
					false, &message);
				fSavePanel->Show();
			}
			break;
		}
 
		case ADD_HOTKEY_ITEM:
			_AddNewSpec(NULL);
			break;
 
		case REMOVE_HOTKEY_ITEM:
		{
			BRow* item = fColumnListView->CurrentSelection();
			if (item) {
				int index = fColumnListView->IndexOf(item);
				fColumnListView->RemoveRow(item);
				delete item;
				_MarkKeySetModified();
 
				// Rules for new selection: If there is an item at (index),
				// select it. Otherwise, if there is an item at (index-1),
				// select it. Otherwise, select nothing.
				int num = fColumnListView->CountRows();
				if (num > 0) {
					if (index < num)
						fColumnListView->AddToSelection(
							fColumnListView->RowAt(index));
					else {
						if (index > 0)
							index--;
						if (index < num)
							fColumnListView->AddToSelection(
								fColumnListView->RowAt(index));
					}
				}
			}
			break;
		}
 
		// Received when the user clicks on the ColumnListView
		case HOTKEY_ITEM_SELECTED:
		{
			if (fColumnListView->CountRows() > 0)
				fRemoveButton->SetEnabled(true);
			else
				fRemoveButton->SetEnabled(false);
			break;
		}
 
		// Received when an entry is to be modified in response to GUI activity
		case HOTKEY_ITEM_MODIFIED:
		{
			int32 row, column;
 
			if ((message->FindInt32("row", &row) == B_OK)
				&& (message->FindInt32("column", &column) == B_OK)) {
				int32 key;
				const char* bytes;
 
				if (row >= 0) {
					ShortcutsSpec* item = (ShortcutsSpec*)
						fColumnListView->RowAt(row);
					bool repaintNeeded = false; // default
 
					if (message->HasInt32("mouseClick")) {
						repaintNeeded = item->ProcessColumnMouseClick(column);
					} else if ((message->FindString("bytes", &bytes) == B_OK)
						&& (message->FindInt32("key", &key) == B_OK)) {
						repaintNeeded = item->ProcessColumnKeyStroke(column,
							bytes, key);
					} else if (message->FindInt32("unmappedkey", &key) ==
						B_OK) {
						repaintNeeded = ((column == item->KEY_COLUMN_INDEX)
							&& ((key > 0xFF) || (GetKeyName(key) != NULL))
							&& (item->ProcessColumnKeyStroke(column, NULL,
							key)));
					} else if (message->FindString("text", &bytes) == B_OK) {
						if ((bytes[0] == '(')&&(bytes[1] == 'C')) {
							if (fSelectPanel)
								fSelectPanel->Show();
							else {
								BMessage message(SELECT_APPLICATION);
								BMessenger m(this);
								fSelectPanel = new BFilePanel(B_OPEN_PANEL, &m,
									NULL, 0, false, &message);
								fSelectPanel->Show();
							}
							fSelectPanel->SetButtonLabel(B_DEFAULT_BUTTON,
								B_TRANSLATE("Select"));
						} else
							repaintNeeded = item->ProcessColumnTextString(
								column, bytes);
					}
 
					if (repaintNeeded) {
						fColumnListView->Invalidate(row);
						_MarkKeySetModified();
					}
				}
			}
			break;
		}
 
		default:
			BWindow::MessageReceived(message);
			break;
	}
}
 
 
void
ShortcutsWindow::_MarkKeySetModified()
{
	if (fKeySetModified == false) {
		fKeySetModified = true;
		fSaveButton->SetEnabled(true);
	}
}
 
 
void
ShortcutsWindow::Quit()
{
	BWindow::Quit();
}
 
 
void
ShortcutsWindow::DispatchMessage(BMessage* message, BHandler* handler)
{
	switch (message->what) {
		case B_SIMPLE_DATA:
			MessageReceived(message);
			break;
 
		case B_COPY:
		case B_CUT:
			if (be_clipboard->Lock()) {
				ShortcutsSpec* row =
					static_cast<ShortcutsSpec*>(fColumnListView->CurrentSelection());
				if (row) {
					BMessage* data = be_clipboard->Data();
					data->RemoveName("text/plain");
					data->AddData("text/plain", B_MIME_TYPE,
						row->GetCellText(ShortcutsSpec::STRING_COLUMN_INDEX),
						strlen(row->GetCellText(ShortcutsSpec::STRING_COLUMN_INDEX)));
					be_clipboard->Commit();
 
					if (message->what == B_CUT) {
						row->ProcessColumnTextString(
							ShortcutsSpec::STRING_COLUMN_INDEX, "");
						_MarkKeySetModified();
					}
				}
				be_clipboard->Unlock();
			}
			break;
 
		case B_PASTE:
			if (be_clipboard->Lock()) {
				BMessage* data = be_clipboard->Data();
				const char* text;
				ssize_t textLen;
				if (data->FindData("text/plain", B_MIME_TYPE, (const void**)
					&text, &textLen) == B_OK) {
					ShortcutsSpec* row =
					static_cast<ShortcutsSpec*>(fColumnListView->CurrentSelection());
					if (row) {
						for (ssize_t i = 0; i < textLen; i++) {
							char buf[2] = {text[i], 0x00};
							row->ProcessColumnKeyStroke(
								ShortcutsSpec::STRING_COLUMN_INDEX, buf, 0);
						}
					}
					_MarkKeySetModified();
				}
				be_clipboard->Unlock();
			}
			break;
 
		case B_KEY_DOWN:
			ShortcutsSpec* selected;
			if (message->GetInt32("modifiers", 0) != 0)
				BWindow::DispatchMessage(message, handler);
			else if (handler == fColumnListView
				&& (selected =
					static_cast<ShortcutsSpec*>(fColumnListView->CurrentSelection()))) {
				selected->ProcessColumnTextString(
						ShortcutsSpec::KEY_COLUMN_INDEX,
						GetKeyName(message->GetInt32("key", 0)));
				_MarkKeySetModified();
			}
			break;
 
		default:
			BWindow::DispatchMessage(message, handler);
			break;
	}
}

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.