xref: /haiku/src/apps/diskprobe/DiskProbe.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
1 /*
2  * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "DiskProbe.h"
8 #include "DataEditor.h"
9 #include "DataView.h"
10 #include "FileWindow.h"
11 #include "AttributeWindow.h"
12 #include "OpenWindow.h"
13 #include "FindWindow.h"
14 
15 #include <AboutWindow.h>
16 #include <Alert.h>
17 #include <Application.h>
18 #include <Autolock.h>
19 #include <Catalog.h>
20 #include <Directory.h>
21 #include <Entry.h>
22 #include <FilePanel.h>
23 #include <FindDirectory.h>
24 #include <Locale.h>
25 #include <Path.h>
26 #include <Screen.h>
27 #include <TextView.h>
28 
29 #include <stdio.h>
30 #include <string.h>
31 
32 #undef B_TRANSLATION_CONTEXT
33 #define B_TRANSLATION_CONTEXT "DiskProbe"
34 
35 const char *kSignature = "application/x-vnd.Haiku-DiskProbe";
36 
37 static const uint32 kMsgDiskProbeSettings = 'DPst';
38 static const uint32 kCascadeOffset = 20;
39 
40 
41 struct disk_probe_settings {
42 	BRect	window_frame;
43 	int32	base_type;
44 	int32	font_size;
45 	int32	flags;
46 };
47 
48 enum disk_probe_flags {
49 	kCaseSensitive	= 0x01,	// this flag alone is R5 DiskProbe settings compatible
50 	kHexFindMode	= 0x02,
51 };
52 
53 class Settings {
54 	public:
55 		Settings();
56 		~Settings();
57 
58 		const BMessage &Message() const { return fMessage; }
59 		void UpdateFrom(BMessage *message);
60 
61 	private:
62 		status_t Open(BFile *file, int32 mode);
63 
64 		BMessage	fMessage;
65 		bool		fUpdated;
66 };
67 
68 
69 class DiskProbe : public BApplication {
70 	public:
71 		DiskProbe();
72 		virtual ~DiskProbe();
73 
74 		virtual void ReadyToRun();
75 
76 		virtual void RefsReceived(BMessage *message);
77 		virtual void ArgvReceived(int32 argc, char **argv);
78 		virtual void MessageReceived(BMessage *message);
79 
80 		virtual bool QuitRequested();
81 
82 	private:
83 		status_t Probe(BEntry &entry, const char *attribute = NULL);
84 
85 		Settings	fSettings;
86 		BFilePanel	*fFilePanel;
87 		BWindow		*fOpenWindow;
88 		FindWindow	*fFindWindow;
89 		uint32		fWindowCount;
90 		BRect		fWindowFrame;
91 		BMessenger	fFindTarget;
92 };
93 
94 
95 //-----------------
96 
97 
98 Settings::Settings()
99 	:
100 	fMessage(kMsgDiskProbeSettings),
101 	fUpdated(false)
102 {
103 	BScreen screen;
104 	fMessage.AddRect("window_frame", BRect(50, 50, screen.Frame().Width() - 50,
105 		screen.Frame().Height() - 50));
106 	fMessage.AddInt32("base_type", kHexBase);
107 	fMessage.AddFloat("font_size", 12.0f);
108 	fMessage.AddBool("case_sensitive", true);
109 	fMessage.AddInt8("find_mode", kAsciiMode);
110 
111 	BFile file;
112 	if (Open(&file, B_READ_ONLY) != B_OK)
113 		return;
114 
115 	// ToDo: load/save settings as flattened BMessage - but not yet,
116 	//		since that will break compatibility with R5's DiskProbe
117 
118 	disk_probe_settings settings;
119 	if (file.Read(&settings, sizeof(settings)) == sizeof(settings)) {
120 #if B_HOST_IS_BENDIAN
121 		// settings are saved in little endian
122 		settings.window_frame.left = B_LENDIAN_TO_HOST_FLOAT(
123 			settings.window_frame.left);
124 		settings.window_frame.top = B_LENDIAN_TO_HOST_FLOAT(
125 			settings.window_frame.top);
126 		settings.window_frame.right = B_LENDIAN_TO_HOST_FLOAT(
127 			settings.window_frame.right);
128 		settings.window_frame.bottom = B_LENDIAN_TO_HOST_FLOAT(
129 			settings.window_frame.bottom);
130 #endif
131 		// check if the window frame is on screen at all
132 		BScreen screen;
133 		if (screen.Frame().Contains(settings.window_frame.LeftTop())
134 			&& settings.window_frame.Width() < screen.Frame().Width()
135 			&& settings.window_frame.Height() < screen.Frame().Height())
136 			fMessage.ReplaceRect("window_frame", settings.window_frame);
137 
138 		if (settings.base_type == kHexBase
139 			|| settings.base_type == kDecimalBase)
140 			fMessage.ReplaceInt32("base_type",
141 				B_LENDIAN_TO_HOST_INT32(settings.base_type));
142 		if (settings.font_size >= 0 && settings.font_size <= 72)
143 			fMessage.ReplaceFloat("font_size",
144 				float(B_LENDIAN_TO_HOST_INT32(settings.font_size)));
145 
146 		fMessage.ReplaceBool("case_sensitive",
147 			settings.flags & kCaseSensitive);
148 		fMessage.ReplaceInt8("find_mode",
149 			settings.flags & kHexFindMode ? kHexMode : kAsciiMode);
150 	}
151 }
152 
153 
154 Settings::~Settings()
155 {
156 	// only save the settings if something has changed
157 	if (!fUpdated)
158 		return;
159 
160 	BFile file;
161 	if (Open(&file, B_CREATE_FILE | B_WRITE_ONLY) != B_OK)
162 		return;
163 
164 	disk_probe_settings settings;
165 
166 	settings.window_frame = fMessage.FindRect("window_frame");
167 #if B_HOST_IS_BENDIAN
168 	// settings are saved in little endian
169 	settings.window_frame.left = B_HOST_TO_LENDIAN_FLOAT(
170 		settings.window_frame.left);
171 	settings.window_frame.top = B_HOST_TO_LENDIAN_FLOAT(
172 		settings.window_frame.top);
173 	settings.window_frame.right = B_HOST_TO_LENDIAN_FLOAT(
174 		settings.window_frame.right);
175 	settings.window_frame.bottom = B_HOST_TO_LENDIAN_FLOAT(
176 		settings.window_frame.bottom);
177 #endif
178 
179 	settings.base_type = B_HOST_TO_LENDIAN_INT32(
180 		fMessage.FindInt32("base_type"));
181 	settings.font_size = B_HOST_TO_LENDIAN_INT32(
182 		int32(fMessage.FindFloat("font_size") + 0.5f));
183 	settings.flags = B_HOST_TO_LENDIAN_INT32(
184 		(fMessage.FindBool("case_sensitive") ? kCaseSensitive : 0)
185 		| (fMessage.FindInt8("find_mode") == kHexMode ? kHexFindMode : 0));
186 
187 	file.Write(&settings, sizeof(settings));
188 }
189 
190 
191 status_t
192 Settings::Open(BFile *file, int32 mode)
193 {
194 	BPath path;
195 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
196 		return B_ERROR;
197 
198 	path.Append("DiskProbe_data");
199 
200 	return file->SetTo(path.Path(), mode);
201 }
202 
203 
204 void
205 Settings::UpdateFrom(BMessage *message)
206 {
207 	BRect frame;
208 	if (message->FindRect("window_frame", &frame) == B_OK)
209 		fMessage.ReplaceRect("window_frame", frame);
210 
211 	int32 baseType;
212 	if (message->FindInt32("base_type", &baseType) == B_OK)
213 		fMessage.ReplaceInt32("base_type", baseType);
214 
215 	float fontSize;
216 	if (message->FindFloat("font_size", &fontSize) == B_OK)
217 		fMessage.ReplaceFloat("font_size", fontSize);
218 
219 	bool caseSensitive;
220 	if (message->FindBool("case_sensitive", &caseSensitive) == B_OK)
221 		fMessage.ReplaceBool("case_sensitive", caseSensitive);
222 
223 	int8 findMode;
224 	if (message->FindInt8("find_mode", &findMode) == B_OK)
225 		fMessage.ReplaceInt8("find_mode", findMode);
226 
227 	fUpdated = true;
228 }
229 
230 
231 //	#pragma mark -
232 
233 
234 DiskProbe::DiskProbe()
235 	: BApplication(kSignature),
236 	fOpenWindow(NULL),
237 	fFindWindow(NULL),
238 	fWindowCount(0)
239 {
240 	fFilePanel = new BFilePanel();
241 	fWindowFrame = fSettings.Message().FindRect("window_frame");
242 }
243 
244 
245 DiskProbe::~DiskProbe()
246 {
247 	delete fFilePanel;
248 }
249 
250 
251 void
252 DiskProbe::ReadyToRun()
253 {
254 	// are there already windows open?
255 	if (CountWindows() != 1)
256 		return;
257 
258 	// if not, ask the user to open a file
259 	PostMessage(kMsgOpenOpenWindow);
260 }
261 
262 
263 /** Opens a window containing the file pointed to by the entry_ref.
264  *	This function will fail if that file doesn't exist or could not
265  *	be opened.
266  *	It will check if there already is a window that probes the
267  *	file in question and will activate it in that case.
268  *	This function must be called with the application looper locked.
269  */
270 
271 status_t
272 DiskProbe::Probe(BEntry &entry, const char *attribute)
273 {
274 	entry_ref ref;
275 	status_t status = entry.GetRef(&ref);
276 	if (status < B_OK)
277 		return status;
278 
279 	ProbeWindow *lastWindow(NULL);
280 
281 	// Do we already have that window open?
282 	for (int32 i = CountWindows(); i-- > 0; ) {
283 		ProbeWindow *window = dynamic_cast<ProbeWindow *>(WindowAt(i));
284 		if (window == NULL)
285 			continue;
286 
287 		if (window->Contains(ref, attribute)) {
288 			window->Activate(true);
289 			return B_OK;
290 		}
291 		if (lastWindow == NULL)
292 			lastWindow = window;
293 	}
294 
295 	// Does the file really exist?
296 	if (!entry.Exists())
297 		return B_ENTRY_NOT_FOUND;
298 
299 	entry.GetRef(&ref);
300 
301 	// cascade window
302 	BRect rect;
303 	if (lastWindow != NULL)
304 		rect = lastWindow->Frame();
305 	else
306 		rect = fWindowFrame;
307 
308 	rect.OffsetBy(kCascadeOffset, kCascadeOffset);
309 
310 	BWindow *window;
311 	if (attribute != NULL)
312 		window = new AttributeWindow(rect, &ref, attribute, &fSettings.Message());
313 	else
314 		window = new FileWindow(rect, &ref, &fSettings.Message());
315 
316 	window->Show();
317 
318 	/* adjust the cascading... we can only do this after the window was created
319 	 * to adjust to the real size */
320 	rect.right = window->Frame().right;
321 	rect.bottom = window->Frame().bottom;
322 
323 	BScreen screen;
324 	BRect screenBorder = screen.Frame();
325 
326 	float left = rect.left;
327 	if (left + rect.Width() > screenBorder.right)
328 		left = 7;
329 
330 	float top = rect.top;
331 	if (top + rect.Height() > screenBorder.bottom)
332 		top = 26;
333 
334 	rect.OffsetTo(BPoint(left, top));
335 	window->MoveTo(BPoint(left, top));
336 
337 	fWindowCount++;
338 
339 	return B_OK;
340 }
341 
342 
343 void
344 DiskProbe::RefsReceived(BMessage *message)
345 {
346 	bool traverseLinks = (modifiers() & B_SHIFT_KEY) == 0;
347 
348 	int32 index = 0;
349 	entry_ref ref;
350 	while (message->FindRef("refs", index++, &ref) == B_OK) {
351 		const char *attribute = NULL;
352 		if (message->FindString("attributes", index - 1, &attribute) == B_OK)
353 			traverseLinks = false;
354 
355 		BEntry entry;
356 		status_t status = entry.SetTo(&ref, traverseLinks);
357 
358 		if (status == B_OK)
359 			status = Probe(entry, attribute);
360 
361 		if (status != B_OK) {
362 			char buffer[1024];
363 			snprintf(buffer, sizeof(buffer),
364 				B_TRANSLATE_COMMENT("Could not open \"%s\":\n"
365 				"%s", "Opening of entry reference buffer for a DiskProbe "
366 				"request Alert message. The name of entry reference and "
367 				"error message is shown."),
368 				ref.name, strerror(status));
369 
370 			BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"),
371 				buffer, B_TRANSLATE("OK"), NULL, NULL,
372 				B_WIDTH_AS_USUAL, B_STOP_ALERT);
373 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
374 			alert->Go();
375 		}
376 	}
377 }
378 
379 
380 void
381 DiskProbe::ArgvReceived(int32 argc, char **argv)
382 {
383 	BMessage *message = CurrentMessage();
384 
385 	BDirectory currentDirectory;
386 	if (message)
387 		currentDirectory.SetTo(message->FindString("cwd"));
388 
389 	BMessage refs;
390 
391 	for (int i = 1 ; i < argc ; i++) {
392 		BPath path;
393 		if (argv[i][0] == '/')
394 			path.SetTo(argv[i]);
395 		else
396 			path.SetTo(&currentDirectory, argv[i]);
397 
398 		status_t status;
399 		entry_ref ref;
400 		BEntry entry;
401 
402 		if ((status = entry.SetTo(path.Path(), false)) != B_OK
403 			|| (status = entry.GetRef(&ref)) != B_OK) {
404 			fprintf(stderr, B_TRANSLATE("Could not open file \"%s\": %s\n"),
405 				path.Path(), strerror(status));
406 			continue;
407 		}
408 
409 		refs.AddRef("refs", &ref);
410 	}
411 
412 	RefsReceived(&refs);
413 }
414 
415 
416 void
417 DiskProbe::MessageReceived(BMessage *message)
418 {
419 	switch (message->what) {
420 		case kMsgOpenOpenWindow:
421 			if (fOpenWindow == NULL) {
422 				fOpenWindow = new OpenWindow();
423 				fOpenWindow->Show();
424 				fWindowCount++;
425 			} else
426 				fOpenWindow->Activate(true);
427 			break;
428 
429 		case kMsgOpenWindowClosed:
430 			fOpenWindow = NULL;
431 			// supposed to fall through
432 		case kMsgWindowClosed:
433 			if (--fWindowCount == 0 && !fFilePanel->IsShowing())
434 				PostMessage(B_QUIT_REQUESTED);
435 			break;
436 
437 		case kMsgSettingsChanged:
438 			fSettings.UpdateFrom(message);
439 			break;
440 
441 		case kMsgFindWindowClosed:
442 			fFindWindow = NULL;
443 			break;
444 		case kMsgFindTarget:
445 		{
446 			BMessenger target;
447 			if (message->FindMessenger("target", &target) != B_OK)
448 				break;
449 
450 			if (fFindWindow != NULL && fFindWindow->Lock()) {
451 				fFindWindow->SetTarget(target);
452 				fFindWindow->Unlock();
453 			}
454 			break;
455 		}
456 		case kMsgOpenFindWindow:
457 		{
458 			BMessenger target;
459 			if (message->FindMessenger("target", &target) != B_OK)
460 				break;
461 
462 			if (fFindWindow == NULL) {
463 				// open it!
464 				fFindWindow = new FindWindow(fWindowFrame.OffsetByCopy(80, 80), *message,
465 										target, &fSettings.Message());
466 				fFindWindow->Show();
467 			} else
468 				fFindWindow->Activate();
469 			break;
470 		}
471 
472 		case kMsgOpenFilePanel:
473 			fFilePanel->Show();
474 			break;
475 		case B_CANCEL:
476 			if (fWindowCount == 0)
477 				PostMessage(B_QUIT_REQUESTED);
478 			break;
479 
480 		default:
481 			BApplication::MessageReceived(message);
482 			break;
483 	}
484 }
485 
486 
487 bool
488 DiskProbe::QuitRequested()
489 {
490 	return true;
491 }
492 
493 
494 //	#pragma mark -
495 
496 
497 int
498 main(int argc, char **argv)
499 {
500 	DiskProbe probe;
501 
502 	probe.Run();
503 	return 0;
504 }
505