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