1 /* 2 * Copyright 2006-2007, Axel Dörfler, axeld@pinc-software.de. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ApplicationTypesWindow.h" 8 #include "ApplicationTypeWindow.h" 9 #include "FileTypes.h" 10 #include "FileTypesWindow.h" 11 #include "FileTypeWindow.h" 12 13 #include <AppFileInfo.h> 14 #include <Application.h> 15 #include <Alert.h> 16 #include <Catalog.h> 17 #include <Locale.h> 18 #include <TextView.h> 19 #include <FilePanel.h> 20 #include <FindDirectory.h> 21 #include <Directory.h> 22 #include <Entry.h> 23 #include <Path.h> 24 #include <Resources.h> 25 26 #include <stdio.h> 27 #include <strings.h> 28 29 30 #undef B_TRANSLATION_CONTEXT 31 #define B_TRANSLATION_CONTEXT "FileTypes" 32 33 34 const char* kSignature = "application/x-vnd.Haiku-FileTypes"; 35 36 static const uint32 kMsgFileTypesSettings = 'FTst'; 37 static const uint32 kCascadeOffset = 20; 38 39 40 class Settings { 41 public: 42 Settings(); 43 ~Settings(); 44 45 const BMessage& Message() const { return fMessage; } 46 void UpdateFrom(BMessage* message); 47 48 private: 49 void _SetDefaults(); 50 status_t _Open(BFile* file, int32 mode); 51 52 BMessage fMessage; 53 bool fUpdated; 54 }; 55 56 class FileTypes : public BApplication { 57 public: 58 FileTypes(); 59 virtual ~FileTypes(); 60 61 virtual void ReadyToRun(); 62 63 virtual void RefsReceived(BMessage* message); 64 virtual void ArgvReceived(int32 argc, char** argv); 65 virtual void MessageReceived(BMessage* message); 66 67 virtual bool QuitRequested(); 68 69 private: 70 void _WindowClosed(); 71 72 Settings fSettings; 73 BFilePanel* fFilePanel; 74 BMessenger fFilePanelTarget; 75 FileTypesWindow* fTypesWindow; 76 BWindow* fApplicationTypesWindow; 77 uint32 fWindowCount; 78 uint32 fTypeWindowCount; 79 BString fArgvType; 80 }; 81 82 83 Settings::Settings() 84 : 85 fMessage(kMsgFileTypesSettings), 86 fUpdated(false) 87 { 88 _SetDefaults(); 89 90 BFile file; 91 if (_Open(&file, B_READ_ONLY) != B_OK) 92 return; 93 94 BMessage settings; 95 if (settings.Unflatten(&file) == B_OK) { 96 // We don't unflatten into our default message to make sure 97 // nothing is lost (because of old or corrupted on disk settings) 98 UpdateFrom(&settings); 99 fUpdated = false; 100 } 101 } 102 103 104 Settings::~Settings() 105 { 106 // only save the settings if something has changed 107 if (!fUpdated) 108 return; 109 110 BFile file; 111 if (_Open(&file, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY) != B_OK) 112 return; 113 114 fMessage.Flatten(&file); 115 } 116 117 118 void 119 Settings::UpdateFrom(BMessage* message) 120 { 121 BRect frame; 122 if (message->FindRect("file_types_frame", &frame) == B_OK) 123 fMessage.ReplaceRect("file_types_frame", frame); 124 125 if (message->FindRect("app_types_frame", &frame) == B_OK) 126 fMessage.ReplaceRect("app_types_frame", frame); 127 128 bool showIcons; 129 if (message->FindBool("show_icons", &showIcons) == B_OK) 130 fMessage.ReplaceBool("show_icons", showIcons); 131 132 bool showRule; 133 if (message->FindBool("show_rule", &showRule) == B_OK) 134 fMessage.ReplaceBool("show_rule", showRule); 135 136 float splitWeight; 137 if (message->FindFloat("left_split_weight", &splitWeight) == B_OK) 138 fMessage.ReplaceFloat("left_split_weight", splitWeight); 139 if (message->FindFloat("right_split_weight", &splitWeight) == B_OK) 140 fMessage.ReplaceFloat("right_split_weight", splitWeight); 141 142 fUpdated = true; 143 } 144 145 146 void 147 Settings::_SetDefaults() 148 { 149 fMessage.AddRect("file_types_frame", BRect(80.0f, 80.0f, 600.0f, 480.0f)); 150 fMessage.AddRect("app_types_frame", BRect(100.0f, 100.0f, 540.0f, 480.0f)); 151 fMessage.AddBool("show_icons", true); 152 fMessage.AddBool("show_rule", false); 153 fMessage.AddFloat("left_split_weight", 0.2); 154 fMessage.AddFloat("right_split_weight", 0.8); 155 } 156 157 158 status_t 159 Settings::_Open(BFile* file, int32 mode) 160 { 161 BPath path; 162 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 163 return B_ERROR; 164 165 path.Append("FileTypes settings"); 166 167 return file->SetTo(path.Path(), mode); 168 } 169 170 171 // #pragma mark - 172 173 174 FileTypes::FileTypes() 175 : 176 BApplication(kSignature), 177 fTypesWindow(NULL), 178 fApplicationTypesWindow(NULL), 179 fWindowCount(0), 180 fTypeWindowCount(0) 181 { 182 fFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, 183 B_FILE_NODE, false); 184 } 185 186 187 FileTypes::~FileTypes() 188 { 189 delete fFilePanel; 190 } 191 192 193 void 194 FileTypes::ReadyToRun() 195 { 196 // are there already windows open? 197 if (CountWindows() != 1) 198 return; 199 200 // if not, open the FileTypes window 201 PostMessage(kMsgOpenTypesWindow); 202 } 203 204 205 void 206 FileTypes::RefsReceived(BMessage* message) 207 { 208 bool traverseLinks = (modifiers() & B_SHIFT_KEY) == 0; 209 210 // filter out applications and entries we can't open 211 int32 index = 0; 212 entry_ref ref; 213 while (message->FindRef("refs", index++, &ref) == B_OK) { 214 BEntry entry; 215 BFile file; 216 217 status_t status = entry.SetTo(&ref, traverseLinks); 218 if (status == B_OK) 219 status = file.SetTo(&entry, B_READ_ONLY); 220 221 if (status != B_OK) { 222 // file cannot be opened 223 224 char buffer[1024]; 225 snprintf(buffer, sizeof(buffer), 226 B_TRANSLATE("Could not open \"%s\":\n" 227 "%s"), 228 ref.name, strerror(status)); 229 230 BAlert* alert = new BAlert(B_TRANSLATE("FileTypes request"), 231 buffer, B_TRANSLATE("OK"), NULL, NULL, 232 B_WIDTH_AS_USUAL, B_STOP_ALERT); 233 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 234 alert->Go(); 235 236 message->RemoveData("refs", --index); 237 continue; 238 } 239 240 if (!is_application(file) && !is_resource(file)) { 241 entry_ref target; 242 if (entry.GetRef(&target) == B_OK && target != ref) 243 message->ReplaceRef("refs", index - 1, &ref); 244 continue; 245 } 246 247 // remove application from list 248 message->RemoveData("refs", --index); 249 250 // There are some refs left that want to be handled by the type window 251 BPoint point(100.0f + kCascadeOffset * fTypeWindowCount, 252 110.0f + kCascadeOffset * fTypeWindowCount); 253 254 BWindow* window = new ApplicationTypeWindow(point, entry); 255 window->Show(); 256 257 fTypeWindowCount++; 258 fWindowCount++; 259 } 260 261 if (message->FindRef("refs", &ref) != B_OK) 262 return; 263 264 // There are some refs left that want to be handled by the type window 265 BPoint point(100.0f + kCascadeOffset * fTypeWindowCount, 266 110.0f + kCascadeOffset * fTypeWindowCount); 267 268 BWindow* window = new FileTypeWindow(point, *message); 269 window->Show(); 270 271 fTypeWindowCount++; 272 fWindowCount++; 273 } 274 275 276 void 277 FileTypes::ArgvReceived(int32 argc, char** argv) 278 { 279 if (argc == 3 && strcmp(argv[1], "-type") == 0) { 280 fArgvType = argv[2]; 281 return; 282 } 283 284 BMessage* message = CurrentMessage(); 285 286 BDirectory currentDirectory; 287 if (message != NULL) 288 currentDirectory.SetTo(message->FindString("cwd")); 289 290 BMessage refs; 291 292 for (int i = 1 ; i < argc ; i++) { 293 BPath path; 294 if (argv[i][0] == '/') 295 path.SetTo(argv[i]); 296 else 297 path.SetTo(¤tDirectory, argv[i]); 298 299 status_t status; 300 entry_ref ref; 301 BEntry entry; 302 303 if ((status = entry.SetTo(path.Path(), false)) != B_OK 304 || (status = entry.GetRef(&ref)) != B_OK) { 305 fprintf(stderr, "Could not open file \"%s\": %s\n", 306 path.Path(), strerror(status)); 307 continue; 308 } 309 310 refs.AddRef("refs", &ref); 311 } 312 313 RefsReceived(&refs); 314 } 315 316 317 void 318 FileTypes::MessageReceived(BMessage* message) 319 { 320 switch (message->what) { 321 case kMsgSettingsChanged: 322 fSettings.UpdateFrom(message); 323 break; 324 325 case kMsgOpenTypesWindow: 326 if (fTypesWindow == NULL) { 327 fTypesWindow = new FileTypesWindow(fSettings.Message()); 328 if (fArgvType.Length() > 0) { 329 // Set the window to the type that was requested on the 330 // command line (-type), we do this only once, if we 331 // ever opened more than one FileTypesWindow. 332 fTypesWindow->SelectType(fArgvType.String()); 333 fArgvType = ""; 334 } 335 fTypesWindow->Show(); 336 fWindowCount++; 337 } else 338 fTypesWindow->Activate(true); 339 break; 340 case kMsgTypesWindowClosed: 341 fTypesWindow = NULL; 342 _WindowClosed(); 343 break; 344 345 case kMsgOpenApplicationTypesWindow: 346 if (fApplicationTypesWindow == NULL) { 347 fApplicationTypesWindow = new ApplicationTypesWindow( 348 fSettings.Message()); 349 fApplicationTypesWindow->Show(); 350 fWindowCount++; 351 } else 352 fApplicationTypesWindow->Activate(true); 353 break; 354 case kMsgApplicationTypesWindowClosed: 355 fApplicationTypesWindow = NULL; 356 _WindowClosed(); 357 break; 358 359 case kMsgTypeWindowClosed: 360 fTypeWindowCount--; 361 // supposed to fall through 362 363 case kMsgWindowClosed: 364 _WindowClosed(); 365 break; 366 367 368 case kMsgOpenFilePanel: 369 { 370 // the open file panel sends us a message when it's done 371 const char* subTitle; 372 if (message->FindString("title", &subTitle) != B_OK) 373 subTitle = B_TRANSLATE("Open file"); 374 375 int32 what; 376 if (message->FindInt32("message", &what) != B_OK) 377 what = B_REFS_RECEIVED; 378 379 BMessenger target; 380 if (message->FindMessenger("target", &target) != B_OK) 381 target = be_app_messenger; 382 383 BString title = B_TRANSLATE("FileTypes"); 384 if (subTitle != NULL && subTitle[0]) { 385 title.Append(": "); 386 title.Append(subTitle); 387 } 388 389 uint32 flavors = B_FILE_NODE; 390 if (message->FindBool("allowDirs")) 391 flavors |= B_DIRECTORY_NODE; 392 fFilePanel->SetNodeFlavors(flavors); 393 394 395 fFilePanel->SetMessage(new BMessage(what)); 396 fFilePanel->Window()->SetTitle(title.String()); 397 fFilePanel->SetTarget(target); 398 399 if (!fFilePanel->IsShowing()) 400 fFilePanel->Show(); 401 break; 402 } 403 404 case B_SILENT_RELAUNCH: 405 // In case we were launched via the add-on, there is no types 406 // window yet. 407 if (fTypesWindow == NULL) 408 PostMessage(kMsgOpenTypesWindow); 409 break; 410 411 case B_CANCEL: 412 if (fWindowCount == 0) 413 PostMessage(B_QUIT_REQUESTED); 414 break; 415 416 case B_SIMPLE_DATA: 417 RefsReceived(message); 418 break; 419 420 default: 421 BApplication::MessageReceived(message); 422 break; 423 } 424 } 425 426 427 bool 428 FileTypes::QuitRequested() 429 { 430 return true; 431 } 432 433 434 void 435 FileTypes::_WindowClosed() 436 { 437 if (--fWindowCount == 0 && !fFilePanel->IsShowing()) 438 PostMessage(B_QUIT_REQUESTED); 439 } 440 441 442 // #pragma mark - 443 444 445 bool 446 is_application(BFile& file) 447 { 448 BAppFileInfo appInfo(&file); 449 if (appInfo.InitCheck() != B_OK) 450 return false; 451 452 char type[B_MIME_TYPE_LENGTH]; 453 if (appInfo.GetType(type) != B_OK 454 || strcasecmp(type, B_APP_MIME_TYPE)) 455 return false; 456 457 return true; 458 } 459 460 461 bool 462 is_resource(BFile& file) 463 { 464 BResources resources(&file); 465 if (resources.InitCheck() != B_OK) 466 return false; 467 468 BNodeInfo nodeInfo(&file); 469 char type[B_MIME_TYPE_LENGTH]; 470 if (nodeInfo.GetType(type) != B_OK 471 || strcasecmp(type, B_RESOURCE_MIME_TYPE)) 472 return false; 473 474 return true; 475 } 476 477 478 void 479 error_alert(const char* message, status_t status, alert_type type) 480 { 481 char warning[512]; 482 if (status != B_OK) { 483 snprintf(warning, sizeof(warning), "%s:\n\t%s\n", message, 484 strerror(status)); 485 } 486 487 BAlert* alert = new BAlert(B_TRANSLATE("FileTypes request"), 488 status == B_OK ? message : warning, 489 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, type); 490 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 491 alert->Go(); 492 } 493 494 495 int 496 main(int argc, char** argv) 497 { 498 FileTypes probe; 499 probe.Run(); 500 return 0; 501 } 502