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_TRANSLATION_CONTEXT 33 #define B_TRANSLATION_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 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 BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"), 371 buffer, B_TRANSLATE("OK"), NULL, NULL, 372 B_WIDTH_AS_USUAL, B_STOP_ALERT); 373 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 374 alert->Go(); 375 } 376 } 377 } 378 379 380 void 381 DiskProbe::ArgvReceived(int32 argc, char **argv) 382 { 383 BMessage *message = CurrentMessage(); 384 385 BDirectory currentDirectory; 386 if (message) 387 currentDirectory.SetTo(message->FindString("cwd")); 388 389 BMessage refs; 390 391 for (int i = 1 ; i < argc ; i++) { 392 BPath path; 393 if (argv[i][0] == '/') 394 path.SetTo(argv[i]); 395 else 396 path.SetTo(¤tDirectory, argv[i]); 397 398 status_t status; 399 entry_ref ref; 400 BEntry entry; 401 402 if ((status = entry.SetTo(path.Path(), false)) != B_OK 403 || (status = entry.GetRef(&ref)) != B_OK) { 404 fprintf(stderr, B_TRANSLATE("Could not open file \"%s\": %s\n"), 405 path.Path(), strerror(status)); 406 continue; 407 } 408 409 refs.AddRef("refs", &ref); 410 } 411 412 RefsReceived(&refs); 413 } 414 415 416 void 417 DiskProbe::MessageReceived(BMessage *message) 418 { 419 switch (message->what) { 420 case kMsgOpenOpenWindow: 421 if (fOpenWindow == NULL) { 422 fOpenWindow = new OpenWindow(); 423 fOpenWindow->Show(); 424 fWindowCount++; 425 } else 426 fOpenWindow->Activate(true); 427 break; 428 429 case kMsgOpenWindowClosed: 430 fOpenWindow = NULL; 431 // supposed to fall through 432 case kMsgWindowClosed: 433 if (--fWindowCount == 0 && !fFilePanel->IsShowing()) 434 PostMessage(B_QUIT_REQUESTED); 435 break; 436 437 case kMsgSettingsChanged: 438 fSettings.UpdateFrom(message); 439 break; 440 441 case kMsgFindWindowClosed: 442 fFindWindow = NULL; 443 break; 444 case kMsgFindTarget: 445 { 446 BMessenger target; 447 if (message->FindMessenger("target", &target) != B_OK) 448 break; 449 450 if (fFindWindow != NULL && fFindWindow->Lock()) { 451 fFindWindow->SetTarget(target); 452 fFindWindow->Unlock(); 453 } 454 break; 455 } 456 case kMsgOpenFindWindow: 457 { 458 BMessenger target; 459 if (message->FindMessenger("target", &target) != B_OK) 460 break; 461 462 if (fFindWindow == NULL) { 463 // open it! 464 fFindWindow = new FindWindow(fWindowFrame.OffsetByCopy(80, 80), *message, 465 target, &fSettings.Message()); 466 fFindWindow->Show(); 467 } else 468 fFindWindow->Activate(); 469 break; 470 } 471 472 case kMsgOpenFilePanel: 473 fFilePanel->Show(); 474 break; 475 case B_CANCEL: 476 if (fWindowCount == 0) 477 PostMessage(B_QUIT_REQUESTED); 478 break; 479 480 default: 481 BApplication::MessageReceived(message); 482 break; 483 } 484 } 485 486 487 bool 488 DiskProbe::QuitRequested() 489 { 490 return true; 491 } 492 493 494 // #pragma mark - 495 496 497 int 498 main(int argc, char **argv) 499 { 500 DiskProbe probe; 501 502 probe.Run(); 503 return 0; 504 } 505