1/*
2 * Copyright 2002-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Mattias Sundblad
7 *		Andrew Bachmann
8 *		Jonas Sundström
9 */
10
11
12#include "Constants.h"
13#include "StyledEditApp.h"
14#include "StyledEditWindow.h"
15
16#include <Alert.h>
17#include <Autolock.h>
18#include <Catalog.h>
19#include <Locale.h>
20#include <MenuBar.h>
21#include <CharacterSet.h>
22#include <CharacterSetRoster.h>
23#include <FilePanel.h>
24#include <MenuItem.h>
25#include <Message.h>
26#include <Path.h>
27#include <Screen.h>
28
29#include <stdio.h>
30
31
32using namespace BPrivate;
33
34
35BRect gWindowRect(7 - 15, 26 - 15, 507, 426);
36
37
38namespace
39{
40	void
41	cascade()
42	{
43		BScreen screen;
44		BRect screenBorder = screen.Frame();
45		float left = gWindowRect.left + 15;
46		if (left + gWindowRect.Width() > screenBorder.right)
47			left = 7;
48
49		float top = gWindowRect.top + 15;
50		if (top + gWindowRect.Height() > screenBorder.bottom)
51			top = 26;
52
53		gWindowRect.OffsetTo(BPoint(left, top));
54	}
55
56
57	void
58	uncascade()
59	{
60		BScreen screen;
61		BRect screenBorder = screen.Frame();
62
63		float left = gWindowRect.left - 15;
64		if (left < 7) {
65			left = screenBorder.right - gWindowRect.Width() - 7;
66			left = left - ((int)left % 15) + 7;
67		}
68
69		float top = gWindowRect.top - 15;
70		if (top < 26) {
71			top = screenBorder.bottom - gWindowRect.Height() - 26;
72			top = top - ((int)left % 15) + 26;
73		}
74
75		gWindowRect.OffsetTo(BPoint(left, top));
76	}
77}
78
79
80//	#pragma mark -
81
82
83#undef B_TRANSLATION_CONTEXT
84#define B_TRANSLATION_CONTEXT "Open_and_SaveAsPanel"
85
86
87StyledEditApp::StyledEditApp()
88	:
89	BApplication(APP_SIGNATURE),
90	fOpenPanel(NULL)
91{
92	B_TRANSLATE_MARK_SYSTEM_NAME_VOID("StyledEdit");
93
94	fOpenPanel = new BFilePanel();
95	fOpenAsEncoding = 0;
96
97	BMenuBar* menuBar
98		= dynamic_cast<BMenuBar*>(fOpenPanel->Window()->FindView("MenuBar"));
99	if (menuBar != NULL) {
100		fOpenPanelEncodingMenu = new BMenu(B_TRANSLATE("Encoding"));
101		fOpenPanelEncodingMenu->SetRadioMode(true);
102
103		menuBar->AddItem(fOpenPanelEncodingMenu);
104
105		BCharacterSetRoster roster;
106		BCharacterSet charset;
107		while (roster.GetNextCharacterSet(&charset) == B_NO_ERROR) {
108			BString name;
109			if (charset.GetFontID() == B_UNICODE_UTF8)
110				name = B_TRANSLATE("Default");
111			else
112				name = charset.GetPrintName();
113
114			const char* mime = charset.GetMIMEName();
115			if (mime != NULL) {
116				name.Append(" (");
117				name.Append(mime);
118				name.Append(")");
119			}
120			BMenuItem* item
121				= new BMenuItem(name.String(), new BMessage(OPEN_AS_ENCODING));
122			item->SetTarget(this);
123			fOpenPanelEncodingMenu->AddItem(item);
124			if (charset.GetFontID() == fOpenAsEncoding)
125				item->SetMarked(true);
126		}
127	} else
128		fOpenPanelEncodingMenu = NULL;
129
130	fWindowCount = 0;
131	fNextUntitledWindow = 1;
132	fBadArguments = false;
133}
134
135
136StyledEditApp::~StyledEditApp()
137{
138	delete fOpenPanel;
139}
140
141
142void
143StyledEditApp::MessageReceived(BMessage* message)
144{
145	switch (message->what) {
146		case MENU_NEW:
147			OpenDocument();
148			break;
149		case MENU_OPEN:
150			fOpenPanel->Show();
151			break;
152		case B_SILENT_RELAUNCH:
153			OpenDocument();
154			break;
155		case OPEN_AS_ENCODING:
156			void* ptr;
157			if (message->FindPointer("source", &ptr) == B_OK
158				&& fOpenPanelEncodingMenu != NULL) {
159				fOpenAsEncoding = (uint32)fOpenPanelEncodingMenu->IndexOf(
160					(BMenuItem*)ptr);
161			}
162			break;
163
164		default:
165			BApplication::MessageReceived(message);
166			break;
167	}
168}
169
170
171void
172StyledEditApp::OpenDocument()
173{
174	cascade();
175	new StyledEditWindow(gWindowRect, fNextUntitledWindow++, fOpenAsEncoding);
176	fWindowCount++;
177}
178
179
180status_t
181StyledEditApp::OpenDocument(entry_ref* ref, BMessage* message)
182{
183	// traverse eventual symlink
184	BEntry entry(ref, true);
185	entry.GetRef(ref);
186
187	if (entry.IsDirectory()) {
188		BPath path(&entry);
189		fprintf(stderr,
190			"Can't open directory \"%s\" for editing.\n",
191			path.Path());
192		return B_ERROR;
193	}
194
195	BEntry parent;
196	entry.GetParent(&parent);
197
198	if (!entry.Exists() && !parent.Exists()) {
199		fprintf(stderr,
200			"Can't create file. Missing parent directory.\n");
201		return B_ERROR;
202	}
203
204	BWindow* window = NULL;
205	StyledEditWindow* document = NULL;
206
207	for (int32 index = 0; ; index++) {
208		window = WindowAt(index);
209		if (window == NULL)
210			break;
211
212		document = dynamic_cast<StyledEditWindow*>(window);
213		if (document == NULL)
214			continue;
215
216		if (document->IsDocumentEntryRef(ref)) {
217			if (document->Lock()) {
218				document->Activate();
219				document->Unlock();
220				if (message != NULL)
221					document->PostMessage(message);
222				return B_OK;
223			}
224		}
225	}
226
227	cascade();
228	document = new StyledEditWindow(gWindowRect, ref, fOpenAsEncoding);
229
230	if (message != NULL)
231		document->PostMessage(message);
232
233	fWindowCount++;
234
235	return B_OK;
236}
237
238
239void
240StyledEditApp::CloseDocument()
241{
242	uncascade();
243	fWindowCount--;
244	if (fWindowCount == 0) {
245		BAutolock lock(this);
246		Quit();
247	}
248}
249
250
251void
252StyledEditApp::RefsReceived(BMessage* message)
253{
254	int32 index = 0;
255	entry_ref ref;
256
257	while (message->FindRef("refs", index, &ref) == B_OK) {
258		int32 line;
259		if (message->FindInt32("be:line", index, &line) != B_OK)
260			line = -1;
261		int32 start, length;
262		if (message->FindInt32("be:selection_length", index, &length) != B_OK
263			|| message->FindInt32("be:selection_offset", index, &start) != B_OK)
264		{
265			start = -1;
266			length = -1;
267		}
268
269		BMessage* selection = NULL;
270		if (line >= 0 || (start >= 0 && length >= 0)) {
271			selection = new BMessage(UPDATE_LINE_SELECTION);
272			if (line >= 0)
273				selection->AddInt32("be:line", line);
274			if (start >= 0) {
275				selection->AddInt32("be:selection_offset", start);
276				selection->AddInt32("be:selection_length", max_c(0, length));
277			}
278		}
279
280		OpenDocument(&ref, selection);
281		index++;
282	}
283}
284
285
286void
287StyledEditApp::ArgvReceived(int32 argc, char* argv[])
288{
289	// If StyledEdit is already running and gets invoked again
290	// we need to account for a possible mismatch in current
291	// working directory. The paths of the new arguments are
292	// relative to the cwd of the invocation, if they are not
293	// absolute. This cwd we find as a string named "cwd" in
294	// the BLooper's current message.
295
296	const char* cwd = "";
297	BMessage* message = CurrentMessage();
298
299	if (message != NULL) {
300		if (message->FindString("cwd", &cwd) != B_OK)
301			cwd = "";
302	}
303
304	for (int i = 1 ; (i < argc) ; i++) {
305		BPath path;
306		if (argv[i][0] == '/') {
307			path.SetTo(argv[i]);
308		} else {
309			path.SetTo(cwd, argv[i]);
310				// patch relative paths only
311		}
312
313		entry_ref ref;
314		get_ref_for_path(path.Path(), &ref);
315
316		status_t status;
317		status = OpenDocument(&ref);
318
319		if (status != B_OK && IsLaunching())
320			fBadArguments = true;
321	}
322}
323
324
325void
326StyledEditApp::ReadyToRun()
327{
328	if (fWindowCount > 0)
329		return;
330
331	if (fBadArguments)
332		Quit();
333	else
334		OpenDocument();
335}
336
337
338int32
339StyledEditApp::NumberOfWindows()
340{
341	return fWindowCount;
342}
343
344
345//	#pragma mark -
346
347
348int
349main(int argc, char** argv)
350{
351	StyledEditApp styledEdit;
352	styledEdit.Run();
353	return 0;
354}
355
356