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