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