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 <AboutWindow.h> 16 #include <Alert.h> 17 #include <Application.h> 18 #include <Autolock.h> 19 #include <Catalog.h> 20 #include <Directory.h> 21 #include <Entry.h> 22 #include <FilePanel.h> 23 #include <FindDirectory.h> 24 #include <Locale.h> 25 #include <Path.h> 26 #include <Screen.h> 27 #include <TextView.h> 28 29 #include <stdio.h> 30 #include <string.h> 31 32 #undef B_TRANSLATE_CONTEXT 33 #define B_TRANSLATE_CONTEXT "DiskProbe" 34 35 const char *kSignature = "application/x-vnd.Haiku-DiskProbe"; 36 37 static const uint32 kMsgDiskProbeSettings = 'DPst'; 38 static const uint32 kCascadeOffset = 20; 39 40 41 struct disk_probe_settings { 42 BRect window_frame; 43 int32 base_type; 44 int32 font_size; 45 int32 flags; 46 }; 47 48 enum disk_probe_flags { 49 kCaseSensitive = 0x01, // this flag alone is R5 DiskProbe settings compatible 50 kHexFindMode = 0x02, 51 }; 52 53 class Settings { 54 public: 55 Settings(); 56 ~Settings(); 57 58 const BMessage &Message() const { return fMessage; } 59 void UpdateFrom(BMessage *message); 60 61 private: 62 status_t Open(BFile *file, int32 mode); 63 64 BMessage fMessage; 65 bool fUpdated; 66 }; 67 68 69 class DiskProbe : public BApplication { 70 public: 71 DiskProbe(); 72 virtual ~DiskProbe(); 73 74 virtual void ReadyToRun(); 75 76 virtual void RefsReceived(BMessage *message); 77 virtual void ArgvReceived(int32 argc, char **argv); 78 virtual void MessageReceived(BMessage *message); 79 80 virtual void AboutRequested(); 81 virtual bool QuitRequested(); 82 83 private: 84 status_t Probe(BEntry &entry, const char *attribute = NULL); 85 86 Settings fSettings; 87 BFilePanel *fFilePanel; 88 BWindow *fOpenWindow; 89 FindWindow *fFindWindow; 90 uint32 fWindowCount; 91 BRect fWindowFrame; 92 BMessenger fFindTarget; 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 { 241 fFilePanel = new BFilePanel(); 242 fWindowFrame = fSettings.Message().FindRect("window_frame"); 243 } 244 245 246 DiskProbe::~DiskProbe() 247 { 248 delete fFilePanel; 249 } 250 251 252 void 253 DiskProbe::ReadyToRun() 254 { 255 // are there already windows open? 256 if (CountWindows() != 1) 257 return; 258 259 // if not, ask the user to open a file 260 PostMessage(kMsgOpenOpenWindow); 261 } 262 263 264 /** Opens a window containing the file pointed to by the entry_ref. 265 * This function will fail if that file doesn't exist or could not 266 * be opened. 267 * It will check if there already is a window that probes the 268 * file in question and will activate it in that case. 269 * This function must be called with the application looper locked. 270 */ 271 272 status_t 273 DiskProbe::Probe(BEntry &entry, const char *attribute) 274 { 275 entry_ref ref; 276 status_t status = entry.GetRef(&ref); 277 if (status < B_OK) 278 return status; 279 280 ProbeWindow *lastWindow(NULL); 281 282 // Do we already have that window open? 283 for (int32 i = CountWindows(); i-- > 0; ) { 284 ProbeWindow *window = dynamic_cast<ProbeWindow *>(WindowAt(i)); 285 if (window == NULL) 286 continue; 287 288 if (window->Contains(ref, attribute)) { 289 window->Activate(true); 290 return B_OK; 291 } 292 if (lastWindow == NULL) 293 lastWindow = window; 294 } 295 296 // Does the file really exist? 297 if (!entry.Exists()) 298 return B_ENTRY_NOT_FOUND; 299 300 entry.GetRef(&ref); 301 302 // cascade window 303 BRect rect; 304 if (lastWindow != NULL) 305 rect = lastWindow->Frame(); 306 else 307 rect = fWindowFrame; 308 309 rect.OffsetBy(kCascadeOffset, kCascadeOffset); 310 311 BWindow *window; 312 if (attribute != NULL) 313 window = new AttributeWindow(rect, &ref, attribute, &fSettings.Message()); 314 else 315 window = new FileWindow(rect, &ref, &fSettings.Message()); 316 317 window->Show(); 318 319 /* adjust the cascading... we can only do this after the window was created 320 * to adjust to the real size */ 321 rect.right = window->Frame().right; 322 rect.bottom = window->Frame().bottom; 323 324 BScreen screen; 325 BRect screenBorder = screen.Frame(); 326 327 float left = rect.left; 328 if (left + rect.Width() > screenBorder.right) 329 left = 7; 330 331 float top = rect.top; 332 if (top + rect.Height() > screenBorder.bottom) 333 top = 26; 334 335 rect.OffsetTo(BPoint(left, top)); 336 window->MoveTo(BPoint(left, top)); 337 338 fWindowCount++; 339 340 return B_OK; 341 } 342 343 344 void 345 DiskProbe::RefsReceived(BMessage *message) 346 { 347 bool traverseLinks = (modifiers() & B_SHIFT_KEY) == 0; 348 349 int32 index = 0; 350 entry_ref ref; 351 while (message->FindRef("refs", index++, &ref) == B_OK) { 352 const char *attribute = NULL; 353 if (message->FindString("attributes", index - 1, &attribute) == B_OK) 354 traverseLinks = false; 355 356 BEntry entry; 357 status_t status = entry.SetTo(&ref, traverseLinks); 358 359 if (status == B_OK) 360 status = Probe(entry, attribute); 361 362 if (status != B_OK) { 363 char buffer[1024]; 364 snprintf(buffer, sizeof(buffer), 365 B_TRANSLATE_COMMENT("Could not open \"%s\":\n" 366 "%s", "Opening of entry reference buffer for a DiskProbe " 367 "request Alert message. The name of entry reference and " 368 "error message is shown."), 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 const char* authors[] = { 490 "Axel Dörfler", 491 NULL 492 }; 493 494 BAboutWindow about(B_TRANSLATE_SYSTEM_NAME("DiskProbe"), 2004, authors, 495 "Original Be version by Robert Polic."); 496 about.Show(); 497 } 498 499 500 bool 501 DiskProbe::QuitRequested() 502 { 503 return true; 504 } 505 506 507 // #pragma mark - 508 509 510 int 511 main(int argc, char **argv) 512 { 513 DiskProbe probe; 514 515 probe.Run(); 516 return 0; 517 } 518