xref: /haiku/src/apps/stylededit/StyledEditApp.cpp (revision f638c82a22f6c759ae2027a668ce854a5a3e04cb)
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
cascade()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
uncascade()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 
StyledEditApp()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 }
145 
146 
~StyledEditApp()147 StyledEditApp::~StyledEditApp()
148 {
149 	delete fOpenPanel;
150 }
151 
152 
153 void
MessageReceived(BMessage * message)154 StyledEditApp::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 
182 void
OpenDocument()183 StyledEditApp::OpenDocument()
184 {
185 	new StyledEditWindow(sWindowRect, fNextUntitledWindow++, fOpenAsEncoding);
186 	cascade();
187 	fWindowCount++;
188 }
189 
190 
191 status_t
OpenDocument(entry_ref * ref,BMessage * message)192 StyledEditApp::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 
250 void
CloseDocument()251 StyledEditApp::CloseDocument()
252 {
253 	uncascade();
254 	fWindowCount--;
255 	if (fWindowCount == 0) {
256 		BAutolock lock(this);
257 		Quit();
258 	}
259 }
260 
261 
262 void
RefsReceived(BMessage * message)263 StyledEditApp::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 
297 void
ArgvReceived(int32 argc,char * argv[])298 StyledEditApp::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 
336 void
ReadyToRun()337 StyledEditApp::ReadyToRun()
338 {
339 	if (fWindowCount > 0)
340 		return;
341 
342 	if (fBadArguments)
343 		Quit();
344 	else
345 		OpenDocument();
346 }
347 
348 
349 int32
NumberOfWindows()350 StyledEditApp::NumberOfWindows()
351 {
352 	return fWindowCount;
353 }
354 
355 
356 //	#pragma mark -
357 
358 
359 int
main(int argc,char ** argv)360 main(int argc, char** argv)
361 {
362 	StyledEditApp styledEdit;
363 	styledEdit.Run();
364 	return 0;
365 }
366 
367