xref: /haiku/src/apps/diskprobe/FindWindow.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 "FindWindow.h"
8 #include "DiskProbe.h"
9 
10 #include <Application.h>
11 #include <TextView.h>
12 #include <MenuField.h>
13 #include <PopUpMenu.h>
14 #include <MenuItem.h>
15 #include <Button.h>
16 #include <ScrollView.h>
17 #include <CheckBox.h>
18 #include <Beep.h>
19 
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 
24 
25 enum find_mode {
26 	kAsciiMode,
27 	kHexMode
28 };
29 
30 static const uint32 kMsgFindMode = 'FMde';
31 static const uint32 kMsgStartFind = 'SFnd';
32 
33 
34 class FindTextView : public BTextView {
35 	public:
36 		FindTextView(BRect frame, const char *name, BRect textRect, uint32 resizeMask);
37 
38 		virtual void MakeFocus(bool state);
39 		virtual void TargetedByScrollView(BScrollView *view);
40 
41 		find_mode Mode() const { return fMode; }
42 		status_t SetMode(find_mode mode);
43 
44 		void SetData(BMessage &message);
45 		void GetData(BMessage &message);
46 
47 		virtual void KeyDown(const char *bytes, int32 numBytes);
48 
49 	protected:
50 		virtual	void InsertText(const char *text, int32 length,
51 						int32 offset, const text_run_array *runs);
52 
53 	private:
54 		void HexReformat(int32 oldCursor, int32 &newCursor);
55 		status_t GetHexFromData(const uint8 *in, size_t inSize, char **_hex, size_t *_hexSize);
56 		status_t GetDataFromHex(const char *text, size_t textLength, uint8 **_data, size_t *_dataSize);
57 
58 		BScrollView	*fScrollView;
59 		find_mode	fMode;
60 };
61 
62 
63 //---------------
64 
65 
66 FindTextView::FindTextView(BRect frame, const char *name, BRect textRect,
67 	uint32 resizeMask)
68 	: BTextView(frame, name, textRect, resizeMask),
69 	fScrollView(NULL),
70 	fMode(kAsciiMode)
71 {
72 }
73 
74 
75 void
76 FindTextView::MakeFocus(bool state)
77 {
78 	BTextView::MakeFocus(state);
79 
80 	if (fScrollView != NULL)
81 		fScrollView->SetBorderHighlighted(state);
82 }
83 
84 
85 void
86 FindTextView::TargetedByScrollView(BScrollView *view)
87 {
88 	BTextView::TargetedByScrollView(view);
89 	fScrollView = view;
90 }
91 
92 
93 void
94 FindTextView::HexReformat(int32 oldCursor, int32 &newCursor)
95 {
96 	const char *text = Text();
97 	int32 textLength = TextLength();
98 	char *insert = (char *)malloc(textLength * 2);
99 	if (insert == NULL)
100 		return;
101 
102 	newCursor = TextLength();
103 	int32 out = 0;
104 	for (int32 i = 0; i < textLength; i++) {
105 		if (i == oldCursor) {
106 			// this is the end of the inserted text
107 			newCursor = out;
108 		}
109 
110 		char c = text[i];
111 		if (c >= 'A' && c <= 'F')
112 			c += 'a' - 'A';
113 		if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9'))
114 			insert[out++] = c;
115 
116 		if ((out % 48) == 47)
117 			insert[out++] = '\n';
118 		else if ((out % 3) == 2)
119 			insert[out++] = ' ';
120 	}
121 	insert[out] = '\0';
122 
123 	DeleteText(0, textLength);
124 
125 	// InsertText() does not work here, as we need the text
126 	// to be reformatted as well (newlines, breaks, whatever).
127 	// IOW the BTextView class is not very nicely done.
128 	//	BTextView::InsertText(insert, out, 0, NULL);
129 	fMode = kAsciiMode;
130 	Insert(0, insert, out);
131 	fMode = kHexMode;
132 
133 	free(insert);
134 }
135 
136 
137 void
138 FindTextView::InsertText(const char *text, int32 length, int32 offset,
139 	const text_run_array *runs)
140 {
141 	if (fMode == kHexMode) {
142 		if (offset > TextLength())
143 			offset = TextLength();
144 
145 		BTextView::InsertText(text, length, offset, runs);
146 			// lets add anything, and then start to filter out
147 			// (since we have to reformat the whole text)
148 
149 		int32 start, end;
150 		GetSelection(&start, &end);
151 
152 		int32 cursor;
153 		HexReformat(offset, cursor);
154 
155 		if (length == 1 && start == offset)
156 			Select(cursor + 1, cursor + 1);
157 	} else
158 		BTextView::InsertText(text, length, offset, runs);
159 }
160 
161 
162 void
163 FindTextView::KeyDown(const char *bytes, int32 numBytes)
164 {
165 	if (fMode == kHexMode) {
166 		// filter out invalid (for hex mode) characters
167 		if (numBytes > 1)
168 			return;
169 
170 		switch (bytes[0]) {
171 			case B_RIGHT_ARROW:
172 			case B_LEFT_ARROW:
173 			case B_UP_ARROW:
174 			case B_DOWN_ARROW:
175 			case B_HOME:
176 			case B_END:
177 			case B_PAGE_UP:
178 			case B_PAGE_DOWN:
179 				break;
180 
181 			case B_BACKSPACE:
182 			{
183 				int32 start, end;
184 				GetSelection(&start, &end);
185 
186 				if (bytes[0] == B_BACKSPACE) {
187 					start--;
188 					if (start < 0)
189 						return;
190 				}
191 
192 				if (Text()[start] == ' ')
193 					BTextView::KeyDown(bytes, numBytes);
194 
195 				BTextView::KeyDown(bytes, numBytes);
196 
197 				GetSelection(&start, &end);
198 				HexReformat(start, start);
199 				Select(start, start);
200 				return;
201 			}
202 
203 			case B_DELETE:
204 			{
205 				int32 start, end;
206 				GetSelection(&start, &end);
207 
208 				if (Text()[start] == ' ')
209 					BTextView::KeyDown(bytes, numBytes);
210 
211 				BTextView::KeyDown(bytes, numBytes);
212 
213 				HexReformat(start, start);
214 				Select(start, start);
215 				return;
216 			}
217 
218 			default:
219 			{
220 				if (!strchr("0123456789abcdefABCDEF", bytes[0]))
221 					return;
222 
223 				// the original KeyDown() has severe cursor setting
224 				// problems with our InsertText().
225 
226 				int32 start, end;
227 				GetSelection(&start, &end);
228 				InsertText(bytes, 1, start, NULL);
229 				return;
230 			}
231 		}
232 	}
233 	BTextView::KeyDown(bytes, numBytes);
234 }
235 
236 
237 status_t
238 FindTextView::GetHexFromData(const uint8 *in, size_t inSize, char **_hex, size_t *_hexSize)
239 {
240 	char *hex = (char *)malloc(inSize * 3 + 1);
241 	if (hex == NULL)
242 		return B_NO_MEMORY;
243 
244 	char *out = hex;
245 	for (uint32 i = 0; i < inSize; i++) {
246 		out += sprintf(out, "%02x", *(unsigned char *)(in + i));
247 	}
248 	out[0] = '\0';
249 
250 	*_hex = hex;
251 	*_hexSize = out + 1 - hex;
252 	return B_OK;
253 }
254 
255 
256 status_t
257 FindTextView::GetDataFromHex(const char *text, size_t textLength, uint8 **_data, size_t *_dataSize)
258 {
259 	uint8 *data = (uint8 *)malloc(textLength);
260 	if (data == NULL)
261 		return B_NO_MEMORY;
262 
263 	size_t dataSize = 0;
264 	uint8 hiByte = 0;
265 	bool odd = false;
266 	for (uint32 i = 0; i < textLength; i++) {
267 		char c = text[i];
268 		int32 number;
269 		if (c >= 'A' && c <= 'F')
270 			number = c + 10 - 'A';
271 		else if (c >= 'a' && c <= 'f')
272 			number = c + 10 - 'a';
273 		else if (c >= '0' && c <= '9')
274 			number = c - '0';
275 		else
276 			continue;
277 
278 		if (!odd)
279 			hiByte = (number << 4) & 0xf0;
280 		else
281 			data[dataSize++] = hiByte | (number & 0x0f);
282 
283 		odd = !odd;
284 	}
285 	if (odd)
286 		data[dataSize++] = hiByte;
287 
288 	*_data = data;
289 	*_dataSize = dataSize;
290 	return B_OK;
291 }
292 
293 
294 status_t
295 FindTextView::SetMode(find_mode mode)
296 {
297 	if (fMode == mode)
298 		return B_OK;
299 
300 	if (mode == kHexMode) {
301 		// convert text to hex mode
302 
303 		char *hex;
304 		size_t hexSize;
305 		if (GetHexFromData((const uint8 *)Text(), TextLength(), &hex, &hexSize) < B_OK)
306 			return B_NO_MEMORY;
307 
308 		fMode = mode;
309 
310 		SetText(hex, hexSize);
311 		free(hex);
312 	} else {
313 		// convert hex to ascii
314 
315 		uint8 *data;
316 		size_t dataSize;
317 		if (GetDataFromHex(Text(), TextLength(), &data, &dataSize) < B_OK)
318 			return B_NO_MEMORY;
319 
320 		fMode = mode;
321 
322 		SetText((const char *)data, dataSize);
323 		free(data);
324 	}
325 
326 	return B_OK;
327 }
328 
329 
330 void
331 FindTextView::SetData(BMessage &message)
332 {
333 	const uint8 *data;
334 	ssize_t dataSize;
335 	if (message.FindData("data", B_RAW_TYPE, (const void **)&data, &dataSize) != B_OK)
336 		return;
337 
338 	if (fMode == kHexMode) {
339 		char *hex;
340 		size_t hexSize;
341 		if (GetHexFromData(data, dataSize, &hex, &hexSize) < B_OK)
342 			return;
343 
344 		SetText(hex, hexSize);
345 		free(hex);
346 	} else
347 		SetText((char *)data, dataSize);
348 }
349 
350 
351 void
352 FindTextView::GetData(BMessage &message)
353 {
354 	if (fMode == kHexMode) {
355 		// convert hex-text to real data
356 		uint8 *data;
357 		size_t dataSize;
358 		if (GetDataFromHex(Text(), TextLength(), &data, &dataSize) != B_OK)
359 			return;
360 
361 		message.AddData("data", B_RAW_TYPE, data, dataSize);
362 		free(data);
363 	} else
364 		message.AddData("data", B_RAW_TYPE, Text(), TextLength());
365 }
366 
367 
368 //	#pragma mark -
369 
370 
371 FindWindow::FindWindow(BRect rect, BMessage &previous, BMessenger &target)
372 	: BWindow(rect, "Find", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS),
373 	fTarget(target)
374 {
375 	BView *view = new BView(Bounds(), "main", B_FOLLOW_ALL, 0);
376 	view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
377 	AddChild(view);
378 
379 	int8 mode = kAsciiMode;
380 	previous.FindInt8("find_mode", &mode);
381 
382 	// add the top widgets
383 
384 	fMenu = new BPopUpMenu("mode");
385 	BMessage *message;
386 	BMenuItem *item;
387 	fMenu->AddItem(item = new BMenuItem("Text", message = new BMessage(kMsgFindMode)));
388 	message->AddInt8("mode", kAsciiMode);
389 	if (mode == kAsciiMode)
390 		item->SetMarked(true);
391 	fMenu->AddItem(item = new BMenuItem("Hexadecimal", message = new BMessage(kMsgFindMode)));
392 	message->AddInt8("mode", kHexMode);
393 	if (mode == kHexMode)
394 		item->SetMarked(true);
395 
396 	BRect rect = Bounds().InsetByCopy(5, 5);
397 	BMenuField *menuField = new BMenuField(rect, B_EMPTY_STRING,
398 						"Mode:", fMenu, B_FOLLOW_LEFT | B_FOLLOW_TOP);
399 	menuField->SetDivider(menuField->StringWidth(menuField->Label()) + 8);
400 	menuField->ResizeToPreferred();
401 	view->AddChild(menuField);
402 
403 	// add the bottom widgets
404 
405 	BButton *button = new BButton(rect, B_EMPTY_STRING, "Find", new BMessage(kMsgStartFind),
406 								B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
407 	button->MakeDefault(true);
408 	button->ResizeToPreferred();
409 	button->MoveTo(rect.right - button->Bounds().Width(),
410 				rect.bottom - button->Bounds().Height());
411 	view->AddChild(button);
412 
413 	fCaseCheckBox = new BCheckBox(rect, B_EMPTY_STRING, "Case sensitive",
414 							NULL, B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
415 	fCaseCheckBox->ResizeToPreferred();
416 	fCaseCheckBox->MoveTo(5, button->Frame().top);
417 	bool caseSensitive;
418 	if (previous.FindBool("case_sensitive", &caseSensitive) != B_OK)
419 		caseSensitive = true;
420 	fCaseCheckBox->SetValue(caseSensitive);
421 	view->AddChild(fCaseCheckBox);
422 
423 	// and now those inbetween
424 
425 	rect.top = menuField->Frame().bottom + 5;
426 	rect.bottom = fCaseCheckBox->Frame().top - 8;
427 	rect.InsetBy(2, 2);
428 	fTextView = new FindTextView(rect, B_EMPTY_STRING,
429 						rect.OffsetToCopy(B_ORIGIN).InsetByCopy(3, 3),
430 						B_FOLLOW_ALL);
431 	fTextView->SetWordWrap(true);
432 	fTextView->SetMode((find_mode)mode);
433 	fTextView->SetData(previous);
434 
435 	BScrollView *scrollView = new BScrollView("scroller", fTextView, B_FOLLOW_ALL, B_WILL_DRAW, false, false);
436 	view->AddChild(scrollView);
437 
438 	ResizeTo(290, button->Frame().Height() * 3 + 30);
439 
440 	SetSizeLimits(fCaseCheckBox->Bounds().Width() + button->Bounds().Width() + 20,
441 		32768, button->Frame().Height() * 3 + 10, 32768);
442 }
443 
444 
445 FindWindow::~FindWindow()
446 {
447 }
448 
449 
450 void
451 FindWindow::WindowActivated(bool active)
452 {
453 	fTextView->MakeFocus(active);
454 }
455 
456 
457 void
458 FindWindow::MessageReceived(BMessage *message)
459 {
460 	switch (message->what) {
461 		case kMsgFindMode:
462 		{
463 			int8 mode;
464 			if (message->FindInt8("mode", &mode) != B_OK)
465 				break;
466 
467 			if (fTextView->SetMode((find_mode)mode) != B_OK) {
468 				// activate other item
469 				fMenu->ItemAt(mode == kAsciiMode ? 1 : 0)->SetMarked(true);
470 				beep();
471 			}
472 			fTextView->MakeFocus(true);
473 			break;
474 		}
475 
476 		case kMsgStartFind:
477 		{
478 			BMessage find(kMsgFind);
479 			fTextView->GetData(find);
480 			find.AddBool("case_sensitive", fCaseCheckBox->Value() != 0);
481 			find.AddInt8("find_mode", fTextView->Mode());
482 			fTarget.SendMessage(&find);
483 
484 			PostMessage(B_QUIT_REQUESTED);
485 			break;
486 		}
487 
488 		default:
489 			BWindow::MessageReceived(message);
490 	}
491 }
492 
493 
494 bool
495 FindWindow::QuitRequested()
496 {
497 	be_app_messenger.SendMessage(kMsgFindWindowClosed);
498 	return true;
499 }
500 
501 
502 void
503 FindWindow::SetTarget(BMessenger &target)
504 {
505 	fTarget = target;
506 }
507 
508