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