xref: /haiku/src/apps/stylededit/StyledEditApp.cpp (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
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 
32 using namespace BPrivate;
33 
34 
35 static BRect sWindowRect(7, 26, 507, 426);
36 static float sCascadeOffset = 15;
37 static BPoint sTopLeft = BPoint(7, 26);
38 
39 
40 namespace
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 
89 StyledEditApp::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 	sWindowRect.PrintToStream();
145 }
146 
147 
148 StyledEditApp::~StyledEditApp()
149 {
150 	delete fOpenPanel;
151 }
152 
153 
154 void
155 StyledEditApp::MessageReceived(BMessage* message)
156 {
157 	switch (message->what) {
158 		case MENU_NEW:
159 			OpenDocument();
160 			break;
161 		case MENU_OPEN:
162 			fOpenPanel->Show();
163 			break;
164 		case B_SILENT_RELAUNCH:
165 			OpenDocument();
166 			break;
167 		case OPEN_AS_ENCODING:
168 			void* ptr;
169 			if (message->FindPointer("source", &ptr) == B_OK
170 				&& fOpenPanelEncodingMenu != NULL) {
171 				fOpenAsEncoding = (uint32)fOpenPanelEncodingMenu->IndexOf(
172 					(BMenuItem*)ptr);
173 			}
174 			break;
175 
176 		default:
177 			BApplication::MessageReceived(message);
178 			break;
179 	}
180 }
181 
182 
183 void
184 StyledEditApp::OpenDocument()
185 {
186 	new StyledEditWindow(sWindowRect, fNextUntitledWindow++, fOpenAsEncoding);
187 	cascade();
188 	fWindowCount++;
189 }
190 
191 
192 status_t
193 StyledEditApp::OpenDocument(entry_ref* ref, BMessage* message)
194 {
195 	// traverse eventual symlink
196 	BEntry entry(ref, true);
197 	entry.GetRef(ref);
198 
199 	if (entry.IsDirectory()) {
200 		BPath path(&entry);
201 		fprintf(stderr,
202 			"Can't open directory \"%s\" for editing.\n",
203 			path.Path());
204 		return B_ERROR;
205 	}
206 
207 	BEntry parent;
208 	entry.GetParent(&parent);
209 
210 	if (!entry.Exists() && !parent.Exists()) {
211 		fprintf(stderr,
212 			"Can't create file. Missing parent directory.\n");
213 		return B_ERROR;
214 	}
215 
216 	BWindow* window = NULL;
217 	StyledEditWindow* document = NULL;
218 
219 	for (int32 index = 0; ; index++) {
220 		window = WindowAt(index);
221 		if (window == NULL)
222 			break;
223 
224 		document = dynamic_cast<StyledEditWindow*>(window);
225 		if (document == NULL)
226 			continue;
227 
228 		if (document->IsDocumentEntryRef(ref)) {
229 			if (document->Lock()) {
230 				document->Activate();
231 				document->Unlock();
232 				if (message != NULL)
233 					document->PostMessage(message);
234 				return B_OK;
235 			}
236 		}
237 	}
238 
239 	document = new StyledEditWindow(sWindowRect, ref, fOpenAsEncoding);
240 	cascade();
241 
242 	if (message != NULL)
243 		document->PostMessage(message);
244 
245 	fWindowCount++;
246 
247 	return B_OK;
248 }
249 
250 
251 void
252 StyledEditApp::CloseDocument()
253 {
254 	uncascade();
255 	fWindowCount--;
256 	if (fWindowCount == 0) {
257 		BAutolock lock(this);
258 		Quit();
259 	}
260 }
261 
262 
263 void
264 StyledEditApp::RefsReceived(BMessage* message)
265 {
266 	int32 index = 0;
267 	entry_ref ref;
268 
269 	while (message->FindRef("refs", index, &ref) == B_OK) {
270 		int32 line;
271 		if (message->FindInt32("be:line", index, &line) != B_OK)
272 			line = -1;
273 		int32 start, length;
274 		if (message->FindInt32("be:selection_length", index, &length) != B_OK
275 			|| message->FindInt32("be:selection_offset", index, &start) != B_OK)
276 		{
277 			start = -1;
278 			length = -1;
279 		}
280 
281 		BMessage* selection = NULL;
282 		if (line >= 0 || (start >= 0 && length >= 0)) {
283 			selection = new BMessage(UPDATE_LINE_SELECTION);
284 			if (line >= 0)
285 				selection->AddInt32("be:line", line);
286 			if (start >= 0) {
287 				selection->AddInt32("be:selection_offset", start);
288 				selection->AddInt32("be:selection_length", max_c(0, length));
289 			}
290 		}
291 
292 		OpenDocument(&ref, selection);
293 		index++;
294 	}
295 }
296 
297 
298 void
299 StyledEditApp::ArgvReceived(int32 argc, char* argv[])
300 {
301 	// If StyledEdit is already running and gets invoked again
302 	// we need to account for a possible mismatch in current
303 	// working directory. The paths of the new arguments are
304 	// relative to the cwd of the invocation, if they are not
305 	// absolute. This cwd we find as a string named "cwd" in
306 	// the BLooper's current message.
307 
308 	const char* cwd = "";
309 	BMessage* message = CurrentMessage();
310 
311 	if (message != NULL) {
312 		if (message->FindString("cwd", &cwd) != B_OK)
313 			cwd = "";
314 	}
315 
316 	for (int i = 1 ; (i < argc) ; i++) {
317 		BPath path;
318 		if (argv[i][0] == '/') {
319 			path.SetTo(argv[i]);
320 		} else {
321 			path.SetTo(cwd, argv[i]);
322 				// patch relative paths only
323 		}
324 
325 		entry_ref ref;
326 		get_ref_for_path(path.Path(), &ref);
327 
328 		status_t status;
329 		status = OpenDocument(&ref);
330 
331 		if (status != B_OK && IsLaunching())
332 			fBadArguments = true;
333 	}
334 }
335 
336 
337 void
338 StyledEditApp::ReadyToRun()
339 {
340 	if (fWindowCount > 0)
341 		return;
342 
343 	if (fBadArguments)
344 		Quit();
345 	else
346 		OpenDocument();
347 }
348 
349 
350 int32
351 StyledEditApp::NumberOfWindows()
352 {
353 	return fWindowCount;
354 }
355 
356 
357 //	#pragma mark -
358 
359 
360 int
361 main(int argc, char** argv)
362 {
363 	StyledEditApp styledEdit;
364 	styledEdit.Run();
365 	return 0;
366 }
367 
368