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