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