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