16c333678SFredrik Modeen/*
2b42302c5SJohn Scipione * Copyright 1999-2009 Jeremy Friesner
3b42302c5SJohn Scipione * Copyright 2009-2010 Haiku, Inc. All rights reserved.
46c333678SFredrik Modeen * Distributed under the terms of the MIT License.
56c333678SFredrik Modeen *
66c333678SFredrik Modeen * Authors:
76c333678SFredrik Modeen *		Jeremy Friesner
86c333678SFredrik Modeen *		Fredrik Mod��en
96c333678SFredrik Modeen */
106c333678SFredrik Modeen
116c333678SFredrik Modeen
12fa1ee6d4SJonas Sundström#include "ShortcutsWindow.h"
136c333678SFredrik Modeen
146c333678SFredrik Modeen#include <math.h>
156c333678SFredrik Modeen#include <stdio.h>
166c333678SFredrik Modeen
176c333678SFredrik Modeen#include <Alert.h>
186c333678SFredrik Modeen#include <Application.h>
19ce59b51cSRene Gollent#include <Button.h>
203672a07eSStephan Aßmus#include <Catalog.h>
216c333678SFredrik Modeen#include <Clipboard.h>
22149ce46dSAdrien Destugues#include <ColumnListView.h>
23149ce46dSAdrien Destugues#include <ColumnTypes.h>
24ce59b51cSRene Gollent#include <ControlLook.h>
25fa1ee6d4SJonas Sundström#include <File.h>
26ce59b51cSRene Gollent#include <FilePanel.h>
27fa1ee6d4SJonas Sundström#include <FindDirectory.h>
28fa1ee6d4SJonas Sundström#include <Input.h>
29149ce46dSAdrien Destugues#include <LayoutBuilder.h>
303672a07eSStephan Aßmus#include <Locale.h>
31ce59b51cSRene Gollent#include <Message.h>
326c333678SFredrik Modeen#include <Menu.h>
336c333678SFredrik Modeen#include <MenuBar.h>
34fa1ee6d4SJonas Sundström#include <MenuItem.h>
35fa1ee6d4SJonas Sundström#include <MessageFilter.h>
36fa1ee6d4SJonas Sundström#include <Path.h>
37fa1ee6d4SJonas Sundström#include <PopUpMenu.h>
38ce59b51cSRene Gollent#include <Screen.h>
396c333678SFredrik Modeen#include <ScrollBar.h>
406c333678SFredrik Modeen#include <ScrollView.h>
416c333678SFredrik Modeen#include <String.h>
42ce59b51cSRene Gollent#include <SupportDefs.h>
436c333678SFredrik Modeen
44149ce46dSAdrien Destugues#include "EditWindow.h"
456c333678SFredrik Modeen#include "KeyInfos.h"
466c333678SFredrik Modeen#include "MetaKeyStateMap.h"
47fa1ee6d4SJonas Sundström#include "ParseCommandLine.h"
48149ce46dSAdrien Destugues#include "PopUpColumn.h"
496c333678SFredrik Modeen#include "ShortcutsFilterConstants.h"
50fa1ee6d4SJonas Sundström#include "ShortcutsSpec.h"
51fa1ee6d4SJonas Sundström
526c333678SFredrik Modeen
536c333678SFredrik Modeen// Window sizing constraints
54ce59b51cSRene Gollent#define MAX_WIDTH 10000
55ce59b51cSRene Gollent#define MAX_HEIGHT 10000
56ce59b51cSRene Gollent	// SetSizeLimits does not provide a mechanism for specifying an
57b42302c5SJohn Scipione	// unrestricted maximum. 10,000 seems to be the most common value used
58ce59b51cSRene Gollent	// in other Haiku system applications.
59ce59b51cSRene Gollent
60ce59b51cSRene Gollent#define WINDOW_SETTINGS_FILE_NAME "Shortcuts_window_settings"
61ce59b51cSRene Gollent	// Because the "shortcuts_settings" file (SHORTCUTS_SETTING_FILE_NAME) is
62ce59b51cSRene Gollent	// already used as a communications method between this configurator and
63ce59b51cSRene Gollent	// the "shortcut_catcher" input_server filter, it should not be overloaded
64b42302c5SJohn Scipione	// with window position information, instead, a separate file is used.
656c333678SFredrik Modeen
66546208a5SOliver Tappe#undef B_TRANSLATION_CONTEXT
67546208a5SOliver Tappe#define B_TRANSLATION_CONTEXT "ShortcutsWindow"
683672a07eSStephan Aßmus
699c1a9b92SAdrien Destugues#define ERROR "Shortcuts error"
706c333678SFredrik Modeen#define WARNING "Shortcuts warning"
716c333678SFredrik Modeen
726c333678SFredrik Modeen
73b42302c5SJohn Scipione// Creates a pop-up-menu that reflects the possible states of the specified
746c333678SFredrik Modeen// meta-key.
753672a07eSStephan Aßmusstatic BPopUpMenu*
76b42302c5SJohn ScipioneCreateMetaPopUp(int column)
776c333678SFredrik Modeen{
78b42302c5SJohn Scipione	MetaKeyStateMap& map = GetNthKeyMap(column);
79b42302c5SJohn Scipione	BPopUpMenu* popup = new BPopUpMenu(NULL, false);
80b42302c5SJohn Scipione	int stateCount = map.GetNumStates();
81b42302c5SJohn Scipione
82b42302c5SJohn Scipione	for (int i = 0; i < stateCount; i++)
836c333678SFredrik Modeen		popup->AddItem(new BMenuItem(map.GetNthStateDesc(i), NULL));
84b42302c5SJohn Scipione
856c333678SFredrik Modeen	return popup;
866c333678SFredrik Modeen}
876c333678SFredrik Modeen
88fa1ee6d4SJonas Sundström
896c333678SFredrik Modeen// Creates a pop-up that allows the user to choose a key-cap visually
903672a07eSStephan Aßmusstatic BPopUpMenu*
913672a07eSStephan AßmusCreateKeysPopUp()
926c333678SFredrik Modeen{
936c333678SFredrik Modeen	BPopUpMenu* popup = new BPopUpMenu(NULL, false);
946c333678SFredrik Modeen	int numKeys = GetNumKeyIndices();
956c333678SFredrik Modeen	for (int i = 0; i < numKeys; i++) {
966c333678SFredrik Modeen		const char* next = GetKeyName(i);
97b42302c5SJohn Scipione		if (next != NULL)
986c333678SFredrik Modeen			popup->AddItem(new BMenuItem(next, NULL));
996c333678SFredrik Modeen	}
100b42302c5SJohn Scipione
1016c333678SFredrik Modeen	return popup;
1026c333678SFredrik Modeen}
1036c333678SFredrik Modeen
1046c333678SFredrik Modeen
1056c333678SFredrik ModeenShortcutsWindow::ShortcutsWindow()
1066c333678SFredrik Modeen	:
107ce59b51cSRene Gollent	BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Shortcuts"),
108d13b9014SRene Gollent		B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS),
109b42302c5SJohn Scipione	fSavePanel(NULL),
110b42302c5SJohn Scipione	fOpenPanel(NULL),
111b42302c5SJohn Scipione	fSelectPanel(NULL),
112b42302c5SJohn Scipione	fKeySetModified(false),
1133672a07eSStephan Aßmus	fLastOpenWasAppend(false)
1146c333678SFredrik Modeen{
1153672a07eSStephan Aßmus	ShortcutsSpec::InitializeMetaMaps();
116b42302c5SJohn Scipione
117eff2d235SHumdinger	BMenuBar* menuBar = new BMenuBar("Menu Bar");
1186c333678SFredrik Modeen
1193672a07eSStephan Aßmus	BMenu* fileMenu = new BMenu(B_TRANSLATE("File"));
120ce59b51cSRene Gollent	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Open KeySet" B_UTF8_ELLIPSIS),
1216c333678SFredrik Modeen		new BMessage(OPEN_KEYSET), 'O'));
1223672a07eSStephan Aßmus	fileMenu->AddItem(new BMenuItem(
123ce59b51cSRene Gollent		B_TRANSLATE("Append KeySet" B_UTF8_ELLIPSIS),
1246c333678SFredrik Modeen		new BMessage(APPEND_KEYSET), 'A'));
125ce59b51cSRene Gollent	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Revert to saved"),
1268d6561d6SJanus		new BMessage(REVERT_KEYSET), 'R'));
1276c333678SFredrik Modeen	fileMenu->AddItem(new BSeparatorItem);
1283672a07eSStephan Aßmus	fileMenu->AddItem(new BMenuItem(
129ce59b51cSRene Gollent		B_TRANSLATE("Save KeySet as" B_UTF8_ELLIPSIS),
1306c333678SFredrik Modeen		new BMessage(SAVE_KEYSET_AS), 'S'));
1316c333678SFredrik Modeen	fileMenu->AddItem(new BSeparatorItem);
1323672a07eSStephan Aßmus	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
1333672a07eSStephan Aßmus		new BMessage(B_QUIT_REQUESTED), 'Q'));
1346c333678SFredrik Modeen	menuBar->AddItem(fileMenu);
1356c333678SFredrik Modeen
136149ce46dSAdrien Destugues	fColumnListView = new BColumnListView(NULL,
137a5167229SJanus		B_WILL_DRAW | B_FRAME_EVENTS, B_FANCY_BORDER, false);
138ce59b51cSRene Gollent
139ce59b51cSRene Gollent	float cellWidth = be_plain_font->StringWidth("Either") + 20;
140ce59b51cSRene Gollent		// ShortcutsSpec does not seem to translate the string "Either".
1416c333678SFredrik Modeen
1423672a07eSStephan Aßmus	for (int i = 0; i < ShortcutsSpec::NUM_META_COLUMNS; i++) {
143ce59b51cSRene Gollent		const char* name = ShortcutsSpec::GetColumnName(i);
144ce59b51cSRene Gollent		float headerWidth = be_plain_font->StringWidth(name) + 20;
145ce59b51cSRene Gollent		float width = max_c(headerWidth, cellWidth);
146ce59b51cSRene Gollent
147149ce46dSAdrien Destugues		fColumnListView->AddColumn(new PopUpColumn(CreateMetaPopUp(i), name,
148149ce46dSAdrien Destugues				width, width - 1, width * 1.5, B_TRUNCATE_END, false, true, 1),
149149ce46dSAdrien Destugues			fColumnListView->CountColumns());
1503672a07eSStephan Aßmus	}
1516c333678SFredrik Modeen
152ce59b51cSRene Gollent	float keyCellWidth = be_plain_font->StringWidth("Caps Lock") + 20;
153149ce46dSAdrien Destugues	fColumnListView->AddColumn(new PopUpColumn(CreateKeysPopUp(),
154149ce46dSAdrien Destugues			B_TRANSLATE("Key"), keyCellWidth, keyCellWidth - 10,
155149ce46dSAdrien Destugues			keyCellWidth + 30, B_TRUNCATE_END),
156149ce46dSAdrien Destugues		fColumnListView->CountColumns());
1576c333678SFredrik Modeen	BPopUpMenu* popup = new BPopUpMenu(NULL, false);
1586c333678SFredrik Modeen	popup->AddItem(new BMenuItem(
1593672a07eSStephan Aßmus		B_TRANSLATE("(Choose application with file requester)"), NULL));
1603672a07eSStephan Aßmus	popup->AddItem(new BMenuItem(
1613672a07eSStephan Aßmus		B_TRANSLATE("*InsertString \"Your Text Here\""), NULL));
1626c333678SFredrik Modeen	popup->AddItem(new BMenuItem(
1633672a07eSStephan Aßmus		B_TRANSLATE("*MoveMouse +20 +0"), NULL));
1643672a07eSStephan Aßmus	popup->AddItem(new BMenuItem(B_TRANSLATE("*MoveMouseTo 50% 50%"), NULL));
1653672a07eSStephan Aßmus	popup->AddItem(new BMenuItem(B_TRANSLATE("*MouseButton 1"), NULL));
1663672a07eSStephan Aßmus	popup->AddItem(new BMenuItem(
1673672a07eSStephan Aßmus		B_TRANSLATE("*LaunchHandler text/html"), NULL));
1683672a07eSStephan Aßmus	popup->AddItem(new BMenuItem(
1693672a07eSStephan Aßmus		B_TRANSLATE("*Multi \"*MoveMouseTo 100% 0\" \"*MouseButton 1\""),
1703672a07eSStephan Aßmus		NULL));
1713672a07eSStephan Aßmus	popup->AddItem(new BMenuItem(B_TRANSLATE("*MouseDown"), NULL));
1723672a07eSStephan Aßmus	popup->AddItem(new BMenuItem(B_TRANSLATE("*MouseUp"), NULL));
1733672a07eSStephan Aßmus	popup->AddItem(new BMenuItem(
1743672a07eSStephan Aßmus		B_TRANSLATE("*SendMessage application/x-vnd.Be-TRAK 'Tfnd'"), NULL));
1753672a07eSStephan Aßmus	popup->AddItem(new BMenuItem(B_TRANSLATE("*Beep"), NULL));
176149ce46dSAdrien Destugues	fColumnListView->AddColumn(new PopUpColumn(popup, B_TRANSLATE("Application"),
177149ce46dSAdrien Destugues			300.0, 223.0, 324.0, B_TRUNCATE_END, true),
178149ce46dSAdrien Destugues		fColumnListView->CountColumns());
1796c333678SFredrik Modeen
1806c333678SFredrik Modeen	fColumnListView->SetSelectionMessage(new BMessage(HOTKEY_ITEM_SELECTED));
181149ce46dSAdrien Destugues	fColumnListView->SetSelectionMode(B_SINGLE_SELECTION_LIST);
1826c333678SFredrik Modeen	fColumnListView->SetTarget(this);
1836c333678SFredrik Modeen
184eff2d235SHumdinger	fAddButton = new BButton("add", B_TRANSLATE("Add new shortcut"),
185a5167229SJanus		new BMessage(ADD_HOTKEY_ITEM));
186ce59b51cSRene Gollent
187eff2d235SHumdinger	fRemoveButton = new BButton("remove",
188ce59b51cSRene Gollent		B_TRANSLATE("Remove selected shortcut"),
189a5167229SJanus		new BMessage(REMOVE_HOTKEY_ITEM));
1906c333678SFredrik Modeen	fRemoveButton->SetEnabled(false);
1916c333678SFredrik Modeen
192eff2d235SHumdinger	fSaveButton = new BButton("save", B_TRANSLATE("Save & apply"),
193a5167229SJanus		new BMessage(SAVE_KEYSET));
1946c333678SFredrik Modeen	fSaveButton->SetEnabled(false);
1956c333678SFredrik Modeen
196ce59b51cSRene Gollent	CenterOnScreen();
197ce59b51cSRene Gollent
198a5167229SJanus	fColumnListView->ResizeAllColumnsToPreferred();
199a5167229SJanus
200ce59b51cSRene Gollent	entry_ref windowSettingsRef;
201ce59b51cSRene Gollent	if (_GetWindowSettingsFile(&windowSettingsRef)) {
202ce59b51cSRene Gollent		// The window settings file is not accepted via B_REFS_RECEIVED; this
203ce59b51cSRene Gollent		// is a behind-the-scenes file that the user will never see or
204ce59b51cSRene Gollent		// interact with.
205ce59b51cSRene Gollent		BFile windowSettingsFile(&windowSettingsRef, B_READ_ONLY);
206b42302c5SJohn Scipione		BMessage loadMessage;
207b42302c5SJohn Scipione		if (loadMessage.Unflatten(&windowSettingsFile) == B_OK)
208b42302c5SJohn Scipione			_LoadWindowSettings(loadMessage);
209ce59b51cSRene Gollent	}
210ce59b51cSRene Gollent
211ce59b51cSRene Gollent	entry_ref keySetRef;
212ce59b51cSRene Gollent	if (_GetSettingsFile(&keySetRef)) {
213b42302c5SJohn Scipione		BMessage message(B_REFS_RECEIVED);
214b42302c5SJohn Scipione		message.AddRef("refs", &keySetRef);
215b42302c5SJohn Scipione		message.AddString("startupRef", "please");
216b42302c5SJohn Scipione		PostMessage(&message);
217b42302c5SJohn Scipione			// tell ourselves to load this file if it exists
2186c333678SFredrik Modeen	}
219b42302c5SJohn Scipione
220149ce46dSAdrien Destugues	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
221149ce46dSAdrien Destugues		.Add(menuBar)
222149ce46dSAdrien Destugues		.AddGroup(B_VERTICAL)
223eff2d235SHumdinger			.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
224d0ac6099SHumdinger			.SetInsets(B_USE_WINDOW_SPACING)
225149ce46dSAdrien Destugues			.Add(fColumnListView)
226149ce46dSAdrien Destugues			.AddGroup(B_HORIZONTAL)
227149ce46dSAdrien Destugues				.AddGroup(B_HORIZONTAL)
228149ce46dSAdrien Destugues				.SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP))
229149ce46dSAdrien Destugues				.Add(fAddButton)
230149ce46dSAdrien Destugues				.Add(fRemoveButton)
231149ce46dSAdrien Destugues				.End()
232149ce46dSAdrien Destugues				.AddGroup(B_HORIZONTAL)
233149ce46dSAdrien Destugues					.SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP))
234149ce46dSAdrien Destugues					.Add(fSaveButton)
235149ce46dSAdrien Destugues				.End()
236149ce46dSAdrien Destugues			.End()
237149ce46dSAdrien Destugues		.End();
238149ce46dSAdrien Destugues
2396c333678SFredrik Modeen	Show();
2406c333678SFredrik Modeen}
2416c333678SFredrik Modeen
2426c333678SFredrik Modeen
2436c333678SFredrik ModeenShortcutsWindow::~ShortcutsWindow()
2446c333678SFredrik Modeen{
2456c333678SFredrik Modeen	delete fSavePanel;
2466c333678SFredrik Modeen	delete fOpenPanel;
2476c333678SFredrik Modeen	delete fSelectPanel;
2486c333678SFredrik Modeen	be_app->PostMessage(B_QUIT_REQUESTED);
2496c333678SFredrik Modeen}
2506c333678SFredrik Modeen
2516c333678SFredrik Modeen
2526c333678SFredrik Modeenbool
2536c333678SFredrik ModeenShortcutsWindow::QuitRequested()
2546c333678SFredrik Modeen{
255b42302c5SJohn Scipione	bool result = true;
2566c333678SFredrik Modeen
2576c333678SFredrik Modeen	if (fKeySetModified) {
258b42302c5SJohn Scipione		BAlert* alert = new BAlert(WARNING,
259776c58b2SHumdinger			B_TRANSLATE("Save changes before closing?"),
260776c58b2SHumdinger			B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"),
2613672a07eSStephan Aßmus			B_TRANSLATE("Save"));
262776c58b2SHumdinger		alert->SetShortcut(0, B_ESCAPE);
263776c58b2SHumdinger		alert->SetShortcut(1, 'd');
264776c58b2SHumdinger		alert->SetShortcut(2, 's');
265149ce46dSAdrien Destugues		switch (alert->Go()) {
266776c58b2SHumdinger			case 0:
267b42302c5SJohn Scipione				result = false;
2683672a07eSStephan Aßmus				break;
2696c333678SFredrik Modeen
270776c58b2SHumdinger			case 1:
271b42302c5SJohn Scipione				result = true;
272776c58b2SHumdinger				break;
273776c58b2SHumdinger
2746c333678SFredrik Modeen			case 2:
2756c333678SFredrik Modeen				// Save: automatically if possible, otherwise go back and open
2766c333678SFredrik Modeen				// up the file requester
2773672a07eSStephan Aßmus				if (fLastSaved.InitCheck() == B_OK) {
2786c333678SFredrik Modeen					if (_SaveKeySet(fLastSaved) == false) {
279d2297737SRene Gollent						BAlert* alert = new BAlert(ERROR,
2803672a07eSStephan Aßmus							B_TRANSLATE("Shortcuts was unable to save your "
281d2297737SRene Gollent								"KeySet file!"),
282aed35104SHumdinger							B_TRANSLATE("Oh no"));
283aed35104SHumdinger						alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
284aed35104SHumdinger						alert->Go();
285149ce46dSAdrien Destugues						result = true; // quit anyway
2866c333678SFredrik Modeen					}
2876c333678SFredrik Modeen				} else {
2886c333678SFredrik Modeen					PostMessage(SAVE_KEYSET);
289b42302c5SJohn Scipione					result = false;
2906c333678SFredrik Modeen				}
2913672a07eSStephan Aßmus				break;
2926c333678SFredrik Modeen		}
2936c333678SFredrik Modeen	}
2946c333678SFredrik Modeen
295b42302c5SJohn Scipione	if (result) {
296fa1ee6d4SJonas Sundström		fColumnListView->DeselectAll();
297ce59b51cSRene Gollent
298ce59b51cSRene Gollent		// Save the window position.
299ce59b51cSRene Gollent		entry_ref ref;
300ce59b51cSRene Gollent		if (_GetWindowSettingsFile(&ref)) {
301ce59b51cSRene Gollent			BEntry entry(&ref);
302ce59b51cSRene Gollent			_SaveWindowSettings(entry);
303ce59b51cSRene Gollent		}
304ce59b51cSRene Gollent	}
305b42302c5SJohn Scipione
306b42302c5SJohn Scipione	return result;
3076c333678SFredrik Modeen}
3086c333678SFredrik Modeen
3096c333678SFredrik Modeen
3106c333678SFredrik Modeenbool
3116c333678SFredrik ModeenShortcutsWindow::_GetSettingsFile(entry_ref* eref)
3126c333678SFredrik Modeen{
3136c333678SFredrik Modeen	BPath path;
3146c333678SFredrik Modeen	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
3156c333678SFredrik Modeen		return false;
316d2297737SRene Gollent	else
3176c333678SFredrik Modeen		path.Append(SHORTCUTS_SETTING_FILE_NAME);
3186c333678SFredrik Modeen
3193672a07eSStephan Aßmus	if (BEntry(path.Path(), true).GetRef(eref) == B_OK)
3206c333678SFredrik Modeen		return true;
3216c333678SFredrik Modeen	else
3226c333678SFredrik Modeen		return false;
3236c333678SFredrik Modeen}
3246c333678SFredrik Modeen
3256c333678SFredrik Modeen
3266c333678SFredrik Modeen// Saves a settings file to (saveEntry). Returns true iff successful.
3276c333678SFredrik Modeenbool
3286c333678SFredrik ModeenShortcutsWindow::_SaveKeySet(BEntry& saveEntry)
3296c333678SFredrik Modeen{
3306c333678SFredrik Modeen	BFile saveTo(&saveEntry, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
3313672a07eSStephan Aßmus	if (saveTo.InitCheck() != B_OK)
3326c333678SFredrik Modeen		return false;
3336c333678SFredrik Modeen
334b42302c5SJohn Scipione	BMessage saveMessage;
335149ce46dSAdrien Destugues	for (int i = 0; i < fColumnListView->CountRows(); i++) {
3366c333678SFredrik Modeen		BMessage next;
337149ce46dSAdrien Destugues		if (((ShortcutsSpec*)fColumnListView->RowAt(i))->Archive(&next)
338b42302c5SJohn Scipione				== B_OK) {
339b42302c5SJohn Scipione			saveMessage.AddMessage("spec", &next);
340b42302c5SJohn Scipione		} else
3413672a07eSStephan Aßmus			printf("Error archiving ShortcutsSpec #%i!\n", i);
3426c333678SFredrik Modeen	}
343b42302c5SJohn Scipione
344b42302c5SJohn Scipione	bool result = (saveMessage.Flatten(&saveTo) == B_OK);
345b42302c5SJohn Scipione
346b42302c5SJohn Scipione	if (result) {
3476c333678SFredrik Modeen		fKeySetModified = false;
3486c333678SFredrik Modeen		fSaveButton->SetEnabled(false);
3496c333678SFredrik Modeen	}
3506c333678SFredrik Modeen
351b42302c5SJohn Scipione	return result;
3526c333678SFredrik Modeen}
3536c333678SFredrik Modeen
3546c333678SFredrik Modeen
355d2297737SRene Gollent// Appends new entries from the file specified in the "spec" entry of
356b42302c5SJohn Scipione// (loadMessage). Returns true iff successful.
3576c333678SFredrik Modeenbool
358b42302c5SJohn ScipioneShortcutsWindow::_LoadKeySet(const BMessage& loadMessage)
3596c333678SFredrik Modeen{
3606c333678SFredrik Modeen	int i = 0;
361b42302c5SJohn Scipione	BMessage message;
362b42302c5SJohn Scipione	while (loadMessage.FindMessage("spec", i++, &message) == B_OK) {
363b42302c5SJohn Scipione		ShortcutsSpec* spec
364b42302c5SJohn Scipione			= (ShortcutsSpec*)ShortcutsSpec::Instantiate(&message);
365b42302c5SJohn Scipione		if (spec != NULL)
366149ce46dSAdrien Destugues			fColumnListView->AddRow(spec);
367b42302c5SJohn Scipione		else
3686c333678SFredrik Modeen			printf("_LoadKeySet: Error parsing spec!\n");
3696c333678SFredrik Modeen	}
370b42302c5SJohn Scipione
3716c333678SFredrik Modeen	return true;
3726c333678SFredrik Modeen}
3736c333678SFredrik Modeen
3746c333678SFredrik Modeen
375ce59b51cSRene Gollent// Gets the filesystem location of the "Shortcuts_window_settings" file.
376ce59b51cSRene Gollentbool
377ce59b51cSRene GollentShortcutsWindow::_GetWindowSettingsFile(entry_ref* eref)
378ce59b51cSRene Gollent{
379ce59b51cSRene Gollent	BPath path;
380ce59b51cSRene Gollent	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
381ce59b51cSRene Gollent		return false;
382ce59b51cSRene Gollent	else
383ce59b51cSRene Gollent		path.Append(WINDOW_SETTINGS_FILE_NAME);
384ce59b51cSRene Gollent
385ce59b51cSRene Gollent	return BEntry(path.Path(), true).GetRef(eref) == B_OK;
386ce59b51cSRene Gollent}
387ce59b51cSRene Gollent
388ce59b51cSRene Gollent
389ce59b51cSRene Gollent// Saves the application settings file to (saveEntry).  Because this is a
390ce59b51cSRene Gollent// non-essential file, errors are ignored when writing the settings.
391ce59b51cSRene Gollentvoid
392ce59b51cSRene GollentShortcutsWindow::_SaveWindowSettings(BEntry& saveEntry)
393ce59b51cSRene Gollent{
394ce59b51cSRene Gollent	BFile saveTo(&saveEntry, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
395ce59b51cSRene Gollent	if (saveTo.InitCheck() != B_OK)
396ce59b51cSRene Gollent		return;
397ce59b51cSRene Gollent
398ce59b51cSRene Gollent	BMessage saveMsg;
399ce59b51cSRene Gollent	saveMsg.AddRect("window frame", Frame());
400ce59b51cSRene Gollent
401a5167229SJanus	BMessage columnsState;
402a5167229SJanus	fColumnListView->SaveState(&columnsState);
403a5167229SJanus	saveMsg.AddMessage ("columns state", &columnsState);
404ce59b51cSRene Gollent
405ce59b51cSRene Gollent	saveMsg.Flatten(&saveTo);
406ce59b51cSRene Gollent}
407ce59b51cSRene Gollent
408ce59b51cSRene Gollent
409b42302c5SJohn Scipione// Loads the application settings file from (loadMessage) and resizes
410b42302c5SJohn Scipione// the interface to match the previously saved settings. Because this
411b42302c5SJohn Scipione// is a non-essential file, errors are ignored when loading the settings.
412ce59b51cSRene Gollentvoid
413b42302c5SJohn ScipioneShortcutsWindow::_LoadWindowSettings(const BMessage& loadMessage)
414ce59b51cSRene Gollent{
415ce59b51cSRene Gollent	BRect frame;
416b42302c5SJohn Scipione	if (loadMessage.FindRect("window frame", &frame) == B_OK) {
417b42302c5SJohn Scipione		// ensure the frame does not resize below the computed minimum.
418ce59b51cSRene Gollent		float width = max_c(Bounds().right, frame.right - frame.left);
419ce59b51cSRene Gollent		float height = max_c(Bounds().bottom, frame.bottom - frame.top);
420ce59b51cSRene Gollent		ResizeTo(width, height);
421ce59b51cSRene Gollent
422b42302c5SJohn Scipione		// ensure the frame is not placed outside of the screen.
423ce59b51cSRene Gollent		BScreen screen(this);
424ce59b51cSRene Gollent		float left = min_c(screen.Frame().right - width, frame.left);
425ce59b51cSRene Gollent		float top = min_c(screen.Frame().bottom - height, frame.top);
426ce59b51cSRene Gollent		MoveTo(left, top);
427ce59b51cSRene Gollent	}
428ce59b51cSRene Gollent
429a5167229SJanus	BMessage columnsStateMessage;
430a5167229SJanus	if (loadMessage.FindMessage ("columns state", &columnsStateMessage) == B_OK)
431a5167229SJanus		fColumnListView->LoadState(&columnsStateMessage);
432ce59b51cSRene Gollent}
433ce59b51cSRene Gollent
434ce59b51cSRene Gollent
435b42302c5SJohn Scipione// Creates a new entry and adds it to the GUI. (defaultCommand) will be the
4366c333678SFredrik Modeen// text in the entry, or NULL if no text is desired.
4376c333678SFredrik Modeenvoid
4386c333678SFredrik ModeenShortcutsWindow::_AddNewSpec(const char* defaultCommand)
4396c333678SFredrik Modeen{
4406c333678SFredrik Modeen	_MarkKeySetModified();
4416c333678SFredrik Modeen
4426c333678SFredrik Modeen	ShortcutsSpec* spec;
443149ce46dSAdrien Destugues	BRow* curSel = fColumnListView->CurrentSelection();
444149ce46dSAdrien Destugues	if (curSel)
445149ce46dSAdrien Destugues		spec = new ShortcutsSpec(*((ShortcutsSpec*)curSel));
446149ce46dSAdrien Destugues	else {
447149ce46dSAdrien Destugues		spec = new ShortcutsSpec("");
448149ce46dSAdrien Destugues		for (int i = 0; i < fColumnListView->CountColumns(); i++)
449149ce46dSAdrien Destugues			spec->SetField(new BStringField(""), i);
450149ce46dSAdrien Destugues	}
451149ce46dSAdrien Destugues
452149ce46dSAdrien Destugues	fColumnListView->AddRow(spec);
453149ce46dSAdrien Destugues	fColumnListView->AddToSelection(spec);
454149ce46dSAdrien Destugues	fColumnListView->ScrollTo(spec);
455149ce46dSAdrien Destugues	if (defaultCommand)
456149ce46dSAdrien Destugues		spec->SetCommand(defaultCommand);
4576c333678SFredrik Modeen}
4586c333678SFredrik Modeen
4596c333678SFredrik Modeen
4606c333678SFredrik Modeenvoid
461b42302c5SJohn ScipioneShortcutsWindow::MessageReceived(BMessage* message)
4626c333678SFredrik Modeen{
463149ce46dSAdrien Destugues	switch (message->what) {
464b42302c5SJohn Scipione		case OPEN_KEYSET:
465b42302c5SJohn Scipione		case APPEND_KEYSET:
466b42302c5SJohn Scipione			fLastOpenWasAppend = (message->what == APPEND_KEYSET);
467b42302c5SJohn Scipione			if (fOpenPanel)
4686c333678SFredrik Modeen				fOpenPanel->Show();
4696c333678SFredrik Modeen			else {
470b42302c5SJohn Scipione				BMessenger messenger(this);
471b42302c5SJohn Scipione				fOpenPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL,
472b42302c5SJohn Scipione					0, false);
4736c333678SFredrik Modeen				fOpenPanel->Show();
4746c333678SFredrik Modeen			}
475d2297737SRene Gollent			fOpenPanel->SetButtonLabel(B_DEFAULT_BUTTON, fLastOpenWasAppend ?
4763672a07eSStephan Aßmus				B_TRANSLATE("Append") : B_TRANSLATE("Open"));
4773672a07eSStephan Aßmus			break;
4786c333678SFredrik Modeen
479b42302c5SJohn Scipione		// send a message to myself, to get me to reload the settings file
4806c333678SFredrik Modeen		case REVERT_KEYSET:
4816c333678SFredrik Modeen		{
4826c333678SFredrik Modeen			fLastOpenWasAppend = false;
4836c333678SFredrik Modeen			BMessage reload(B_REFS_RECEIVED);
4846c333678SFredrik Modeen			entry_ref eref;
4856c333678SFredrik Modeen			_GetSettingsFile(&eref);
4866c333678SFredrik Modeen			reload.AddRef("refs", &eref);
4876c333678SFredrik Modeen			reload.AddString("startupRef", "yeah");
4886c333678SFredrik Modeen			PostMessage(&reload);
4893672a07eSStephan Aßmus			break;
4906c333678SFredrik Modeen		}
4916c333678SFredrik Modeen
492b42302c5SJohn Scipione		// respond to drag-and-drop messages here
4936c333678SFredrik Modeen		case B_SIMPLE_DATA:
4946c333678SFredrik Modeen		{
4956c333678SFredrik Modeen			int i = 0;
4966c333678SFredrik Modeen
4976c333678SFredrik Modeen			entry_ref ref;
498b42302c5SJohn Scipione			while (message->FindRef("refs", i++, &ref) == B_OK) {
4996c333678SFredrik Modeen				BEntry entry(&ref);
5003672a07eSStephan Aßmus				if (entry.InitCheck() == B_OK) {
5016c333678SFredrik Modeen					BPath path(&entry);
502149ce46dSAdrien Destugues
5033672a07eSStephan Aßmus					if (path.InitCheck() == B_OK) {
5046c333678SFredrik Modeen						// Add a new item with the given path.
5056c333678SFredrik Modeen						BString str(path.Path());
5066c333678SFredrik Modeen						DoStandardEscapes(str);
5076c333678SFredrik Modeen						_AddNewSpec(str.String());
5086c333678SFredrik Modeen					}
5096c333678SFredrik Modeen				}
5106c333678SFredrik Modeen			}
5113672a07eSStephan Aßmus			break;
5126c333678SFredrik Modeen		}
5136c333678SFredrik Modeen
514b42302c5SJohn Scipione		// respond to FileRequester's messages here
5156c333678SFredrik Modeen		case B_REFS_RECEIVED:
5166c333678SFredrik Modeen		{
5176c333678SFredrik Modeen			// Find file ref
5186c333678SFredrik Modeen			entry_ref ref;
519b42302c5SJohn Scipione			bool isStartMsg = message->HasString("startupRef");
520b42302c5SJohn Scipione			if (message->FindRef("refs", &ref) == B_OK) {
5216c333678SFredrik Modeen				// load the file into (fileMsg)
5226c333678SFredrik Modeen				BMessage fileMsg;
5236c333678SFredrik Modeen				{
5246c333678SFredrik Modeen					BFile file(&ref, B_READ_ONLY);
525d2297737SRene Gollent					if ((file.InitCheck() != B_OK)
5263672a07eSStephan Aßmus						|| (fileMsg.Unflatten(&file) != B_OK)) {
5276c333678SFredrik Modeen						if (isStartMsg) {
5286c333678SFredrik Modeen							// use this to save to anyway
5296c333678SFredrik Modeen							fLastSaved = BEntry(&ref);
5306c333678SFredrik Modeen							break;
5316c333678SFredrik Modeen						} else {
532d2297737SRene Gollent							BAlert* alert = new BAlert(ERROR,
5333672a07eSStephan Aßmus								B_TRANSLATE("Shortcuts was couldn't open your "
534aed35104SHumdinger								"KeySet file!"), B_TRANSLATE("OK"));
535aed35104SHumdinger							alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
536aed35104SHumdinger							alert->Go(NULL);
5376c333678SFredrik Modeen							break;
5386c333678SFredrik Modeen						}
5396c333678SFredrik Modeen					}
5406c333678SFredrik Modeen				}
541149ce46dSAdrien Destugues
5426c333678SFredrik Modeen				if (fLastOpenWasAppend == false) {
5436c333678SFredrik Modeen					// Clear the menu...
544149ce46dSAdrien Destugues					while (fColumnListView->CountRows()) {
545149ce46dSAdrien Destugues						ShortcutsSpec* row =
546149ce46dSAdrien Destugues							static_cast<ShortcutsSpec*>(fColumnListView->RowAt(0));
547149ce46dSAdrien Destugues						fColumnListView->RemoveRow(row);
548149ce46dSAdrien Destugues						delete row;
5493672a07eSStephan Aßmus					}
5506c333678SFredrik Modeen				}
5516c333678SFredrik Modeen
5526c333678SFredrik Modeen				if (_LoadKeySet(fileMsg)) {
5536c333678SFredrik Modeen					if (isStartMsg) fLastSaved = BEntry(&ref);
5546c333678SFredrik Modeen					fSaveButton->SetEnabled(isStartMsg == false);
5556c333678SFredrik Modeen
556b42302c5SJohn Scipione					// If we just loaded in the Shortcuts settings file, then
5576c333678SFredrik Modeen					// no need to tell the user to save on exit.
5586c333678SFredrik Modeen					entry_ref eref;
5596c333678SFredrik Modeen					_GetSettingsFile(&eref);
5606c333678SFredrik Modeen					if (ref == eref) fKeySetModified = false;
5616c333678SFredrik Modeen				} else {
562d2297737SRene Gollent					BAlert* alert = new BAlert(ERROR,
5633672a07eSStephan Aßmus						B_TRANSLATE("Shortcuts was unable to parse your "
564aed35104SHumdinger						"KeySet file!"), B_TRANSLATE("OK"));
565aed35104SHumdinger					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
566aed35104SHumdinger					alert->Go(NULL);
5676c333678SFredrik Modeen					break;
5686c333678SFredrik Modeen				}
5696c333678SFredrik Modeen			}
5703672a07eSStephan Aßmus			break;
5716c333678SFredrik Modeen		}
5726c333678SFredrik Modeen
573b42302c5SJohn Scipione		// these messages come from the pop-up menu of the Applications column
5746c333678SFredrik Modeen		case SELECT_APPLICATION:
5756c333678SFredrik Modeen		{
576149ce46dSAdrien Destugues			ShortcutsSpec* row =
577149ce46dSAdrien Destugues				static_cast<ShortcutsSpec*>(fColumnListView->CurrentSelection());
578149ce46dSAdrien Destugues			if (row != NULL) {
5796c333678SFredrik Modeen				entry_ref aref;
580b42302c5SJohn Scipione				if (message->FindRef("refs", &aref) == B_OK) {
5816c333678SFredrik Modeen					BEntry ent(&aref);
5823672a07eSStephan Aßmus					if (ent.InitCheck() == B_OK) {
5836c333678SFredrik Modeen						BPath path;
584b42302c5SJohn Scipione						if ((ent.GetPath(&path) == B_OK)
585149ce46dSAdrien Destugues							&& (row->
586b42302c5SJohn Scipione								ProcessColumnTextString(ShortcutsSpec::STRING_COLUMN_INDEX,
587b42302c5SJohn Scipione									path.Path()))) {
5886c333678SFredrik Modeen							_MarkKeySetModified();
5896c333678SFredrik Modeen						}
5906c333678SFredrik Modeen					}
5916c333678SFredrik Modeen				}
5926c333678SFredrik Modeen			}
5933672a07eSStephan Aßmus			break;
5946c333678SFredrik Modeen		}
5956c333678SFredrik Modeen
5966c333678SFredrik Modeen		case SAVE_KEYSET:
5976c333678SFredrik Modeen		{
5986c333678SFredrik Modeen			bool showSaveError = false;
5996c333678SFredrik Modeen
600b42302c5SJohn Scipione			const char* name;
6016c333678SFredrik Modeen			entry_ref entry;
602b42302c5SJohn Scipione			if ((message->FindString("name", &name) == B_OK)
603b42302c5SJohn Scipione				&& (message->FindRef("directory", &entry) == B_OK)) {
6046c333678SFredrik Modeen				BDirectory dir(&entry);
6056c333678SFredrik Modeen				BEntry saveTo(&dir, name, true);
606b42302c5SJohn Scipione				showSaveError = ((saveTo.InitCheck() != B_OK)
607b42302c5SJohn Scipione					|| (_SaveKeySet(saveTo) == false));
6083672a07eSStephan Aßmus			} else if (fLastSaved.InitCheck() == B_OK) {
6096c333678SFredrik Modeen				// We've saved this before, save over previous file.
6106c333678SFredrik Modeen				showSaveError = (_SaveKeySet(fLastSaved) == false);
611b42302c5SJohn Scipione			} else
612b42302c5SJohn Scipione				PostMessage(SAVE_KEYSET_AS);
613b42302c5SJohn Scipione					// open the save requester...
6146c333678SFredrik Modeen
6156c333678SFredrik Modeen			if (showSaveError) {
616