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