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