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