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 302 int32 index = 0; 303 entry_ref ref; 304 while (message->FindRef("refs", index++, &ref) == B_OK) { 305 const char *attribute = NULL; 306 if (message->FindString("attributes", index - 1, &attribute) == B_OK) 307 traverseLinks = false; 308 309 BEntry entry; 310 status_t status = entry.SetTo(&ref, traverseLinks); 311 312 if (status == B_OK) 313 status = Probe(entry, attribute); 314 315 if (status != B_OK) { 316 char buffer[1024]; 317 snprintf(buffer, sizeof(buffer), 318 "Could not open \"%s\":\n" 319 "%s", 320 ref.name, strerror(status)); 321 322 (new BAlert("DiskProbe request", 323 buffer, "Ok", NULL, NULL, 324 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(); 325 } 326 } 327 } 328 329 330 void 331 DiskProbe::ArgvReceived(int32 argc, char **argv) 332 { 333 BMessage *message = CurrentMessage(); 334 335 BDirectory currentDirectory; 336 if (message) 337 currentDirectory.SetTo(message->FindString("cwd")); 338 339 BMessage refs; 340 341 for (int i = 1 ; i < argc ; i++) { 342 BPath path; 343 if (argv[i][0] == '/') 344 path.SetTo(argv[i]); 345 else 346 path.SetTo(¤tDirectory, argv[i]); 347 348 status_t status; 349 entry_ref ref; 350 BEntry entry; 351 352 if ((status = entry.SetTo(path.Path(), false)) != B_OK 353 || (status = entry.GetRef(&ref)) != B_OK) { 354 fprintf(stderr, "Could not open file \"%s\": %s\n", path.Path(), strerror(status)); 355 continue; 356 } 357 358 refs.AddRef("refs", &ref); 359 } 360 361 RefsReceived(&refs); 362 } 363 364 365 void 366 DiskProbe::MessageReceived(BMessage *message) 367 { 368 switch (message->what) { 369 case kMsgOpenOpenWindow: 370 if (fOpenWindow == NULL) { 371 fOpenWindow = new OpenWindow(); 372 fOpenWindow->Show(); 373 fWindowCount++; 374 } else 375 fOpenWindow->Activate(true); 376 break; 377 378 case kMsgOpenWindowClosed: 379 fOpenWindow = NULL; 380 // supposed to fall through 381 case kMsgWindowClosed: 382 if (--fWindowCount == 0 && !fFilePanel->IsShowing()) 383 PostMessage(B_QUIT_REQUESTED); 384 break; 385 386 case kMsgSettingsChanged: 387 fSettings.UpdateFrom(message); 388 break; 389 390 case kMsgFindWindowClosed: 391 fFindWindow = NULL; 392 break; 393 case kMsgFindTarget: 394 { 395 BMessenger target; 396 if (message->FindMessenger("target", &target) != B_OK) 397 break; 398 399 if (fFindWindow != NULL && fFindWindow->Lock()) { 400 fFindWindow->SetTarget(target); 401 fFindWindow->Unlock(); 402 } 403 break; 404 } 405 case kMsgOpenFindWindow: 406 { 407 BMessenger target; 408 if (message->FindMessenger("target", &target) != B_OK) 409 break; 410 411 if (fFindWindow == NULL) { 412 // open it! 413 fFindWindow = new FindWindow(fWindowFrame.OffsetByCopy(80, 80), *message, 414 target, &fSettings.Message()); 415 fFindWindow->Show(); 416 } else 417 fFindWindow->Activate(); 418 break; 419 } 420 421 case kMsgOpenFilePanel: 422 fFilePanel->Show(); 423 break; 424 case B_CANCEL: 425 if (fWindowCount == 0) 426 PostMessage(B_QUIT_REQUESTED); 427 break; 428 429 default: 430 BApplication::MessageReceived(message); 431 break; 432 } 433 } 434 435 436 void 437 DiskProbe::AboutRequested() 438 { 439 BAlert *alert = new BAlert("about", "DiskProbe\n" 440 "\twritten by Axel Dörfler\n" 441 "\tCopyright 2004-2007, Haiku.\n\n" 442 "original Be version by Robert Polic\n", "Ok"); 443 BTextView *view = alert->TextView(); 444 BFont font; 445 446 view->SetStylable(true); 447 448 view->GetFont(&font); 449 font.SetSize(18); 450 font.SetFace(B_BOLD_FACE); 451 view->SetFontAndColor(0, 9, &font); 452 453 alert->Go(); 454 } 455 456 457 bool 458 DiskProbe::QuitRequested() 459 { 460 return true; 461 } 462 463 464 // #pragma mark - 465 466 467 int 468 main(int argc, char **argv) 469 { 470 DiskProbe probe; 471 472 probe.Run(); 473 return 0; 474 } 475