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 if (message->FindRect("app_type_frame", &frame) == B_OK) 129 fMessage.ReplaceRect("app_type_frame", frame); 130 131 bool showIcons; 132 if (message->FindBool("show_icons", &showIcons) == B_OK) 133 fMessage.ReplaceBool("show_icons", showIcons); 134 135 bool showRule; 136 if (message->FindBool("show_rule", &showRule) == B_OK) 137 fMessage.ReplaceBool("show_rule", showRule); 138 139 float splitWeight; 140 if (message->FindFloat("left_split_weight", &splitWeight) == B_OK) 141 fMessage.ReplaceFloat("left_split_weight", splitWeight); 142 if (message->FindFloat("right_split_weight", &splitWeight) == B_OK) 143 fMessage.ReplaceFloat("right_split_weight", splitWeight); 144 145 fUpdated = true; 146 } 147 148 149 void 150 Settings::_SetDefaults() 151 { 152 fMessage.AddRect("file_types_frame", BRect(80.0f, 80.0f, 600.0f, 480.0f)); 153 fMessage.AddRect("app_types_frame", BRect(100.0f, 100.0f, 540.0f, 480.0f)); 154 fMessage.AddRect("app_type_frame", BRect(100.0f, 110.0f, 250.0f, 340.0f)); 155 fMessage.AddBool("show_icons", true); 156 fMessage.AddBool("show_rule", false); 157 fMessage.AddFloat("left_split_weight", 0.2); 158 fMessage.AddFloat("right_split_weight", 0.8); 159 } 160 161 162 status_t 163 Settings::_Open(BFile* file, int32 mode) 164 { 165 BPath path; 166 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 167 return B_ERROR; 168 169 path.Append("FileTypes settings"); 170 171 return file->SetTo(path.Path(), mode); 172 } 173 174 175 // #pragma mark - 176 177 178 FileTypes::FileTypes() 179 : 180 BApplication(kSignature), 181 fTypesWindow(NULL), 182 fApplicationTypesWindow(NULL), 183 fWindowCount(0), 184 fTypeWindowCount(0) 185 { 186 fFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, 187 B_FILE_NODE, false); 188 } 189 190 191 FileTypes::~FileTypes() 192 { 193 delete fFilePanel; 194 } 195 196 197 void 198 FileTypes::ReadyToRun() 199 { 200 // are there already windows open? 201 if (CountWindows() != 1) 202 return; 203 204 // if not, open the FileTypes window 205 PostMessage(kMsgOpenTypesWindow); 206 } 207 208 209 void 210 FileTypes::RefsReceived(BMessage* message) 211 { 212 bool traverseLinks = (modifiers() & B_SHIFT_KEY) == 0; 213 214 // filter out applications and entries we can't open 215 int32 index = 0; 216 entry_ref ref; 217 while (message->FindRef("refs", index++, &ref) == B_OK) { 218 BEntry entry; 219 BFile file; 220 221 status_t status = entry.SetTo(&ref, traverseLinks); 222 if (status == B_OK) 223 status = file.SetTo(&entry, B_READ_ONLY); 224 225 if (status != B_OK) { 226 // file cannot be opened 227 228 char buffer[1024]; 229 snprintf(buffer, sizeof(buffer), 230 B_TRANSLATE("Could not open \"%s\":\n" 231 "%s"), 232 ref.name, strerror(status)); 233 234 BAlert* alert = new BAlert(B_TRANSLATE("FileTypes request"), 235 buffer, B_TRANSLATE("OK"), NULL, NULL, 236 B_WIDTH_AS_USUAL, B_STOP_ALERT); 237 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 238 alert->Go(); 239 240 message->RemoveData("refs", --index); 241 continue; 242 } 243 244 if (!is_application(file) && !is_resource(file)) { 245 entry_ref target; 246 if (entry.GetRef(&target) == B_OK && target != ref) 247 message->ReplaceRef("refs", index - 1, &ref); 248 continue; 249 } 250 251 // remove application from list 252 message->RemoveData("refs", --index); 253 254 // There are some refs left that want to be handled by the type window 255 BPoint totalOffset(kCascadeOffset * fTypeWindowCount, kCascadeOffset * fTypeWindowCount); 256 257 BWindow* window = new ApplicationTypeWindow(fSettings.Message(), totalOffset, entry); 258 window->Show(); 259 260 fTypeWindowCount++; 261 fWindowCount++; 262 } 263 264 if (message->FindRef("refs", &ref) != B_OK) 265 return; 266 267 // There are some refs left that want to be handled by the type window 268 BPoint point(100.0f + kCascadeOffset * fTypeWindowCount, 269 110.0f + kCascadeOffset * fTypeWindowCount); 270 271 BWindow* window = new FileTypeWindow(point, *message); 272 window->Show(); 273 274 fTypeWindowCount++; 275 fWindowCount++; 276 } 277 278 279 void 280 FileTypes::ArgvReceived(int32 argc, char** argv) 281 { 282 if (argc == 3 && strcmp(argv[1], "-type") == 0) { 283 fArgvType = argv[2]; 284 return; 285 } 286 287 BMessage* message = CurrentMessage(); 288 289 BDirectory currentDirectory; 290 if (message != NULL) 291 currentDirectory.SetTo(message->FindString("cwd")); 292 293 BMessage refs; 294 295 for (int i = 1 ; i < argc ; i++) { 296 BPath path; 297 if (argv[i][0] == '/') 298 path.SetTo(argv[i]); 299 else 300 path.SetTo(¤tDirectory, argv[i]); 301 302 status_t status; 303 entry_ref ref; 304 BEntry entry; 305 306 if ((status = entry.SetTo(path.Path(), false)) != B_OK 307 || (status = entry.GetRef(&ref)) != B_OK) { 308 fprintf(stderr, "Could not open file \"%s\": %s\n", 309 path.Path(), strerror(status)); 310 continue; 311 } 312 313 refs.AddRef("refs", &ref); 314 } 315 316 RefsReceived(&refs); 317 } 318 319 320 void 321 FileTypes::MessageReceived(BMessage* message) 322 { 323 switch (message->what) { 324 case kMsgSettingsChanged: 325 fSettings.UpdateFrom(message); 326 break; 327 328 case kMsgOpenTypesWindow: 329 if (fTypesWindow == NULL) { 330 fTypesWindow = new FileTypesWindow(fSettings.Message()); 331 if (fArgvType.Length() > 0) { 332 // Set the window to the type that was requested on the 333 // command line (-type), we do this only once, if we 334 // ever opened more than one FileTypesWindow. 335 fTypesWindow->SelectType(fArgvType.String()); 336 fArgvType = ""; 337 } 338 fTypesWindow->Show(); 339 fWindowCount++; 340 } else 341 fTypesWindow->Activate(true); 342 break; 343 case kMsgTypesWindowClosed: 344 fTypesWindow = NULL; 345 _WindowClosed(); 346 break; 347 348 case kMsgOpenApplicationTypesWindow: 349 if (fApplicationTypesWindow == NULL) { 350 fApplicationTypesWindow = new ApplicationTypesWindow( 351 fSettings.Message()); 352 fApplicationTypesWindow->Show(); 353 fWindowCount++; 354 } else 355 fApplicationTypesWindow->Activate(true); 356 break; 357 case kMsgApplicationTypesWindowClosed: 358 fApplicationTypesWindow = NULL; 359 _WindowClosed(); 360 break; 361 362 case kMsgTypeWindowClosed: 363 fTypeWindowCount--; 364 // supposed to fall through 365 366 case kMsgWindowClosed: 367 _WindowClosed(); 368 break; 369 370 371 case kMsgOpenFilePanel: 372 { 373 // the open file panel sends us a message when it's done 374 const char* subTitle; 375 if (message->FindString("title", &subTitle) != B_OK) 376 subTitle = B_TRANSLATE("Open file"); 377 378 int32 what; 379 if (message->FindInt32("message", &what) != B_OK) 380 what = B_REFS_RECEIVED; 381 382 BMessenger target; 383 if (message->FindMessenger("target", &target) != B_OK) 384 target = be_app_messenger; 385 386 BString title = B_TRANSLATE("FileTypes"); 387 if (subTitle != NULL && subTitle[0]) { 388 title.Append(": "); 389 title.Append(subTitle); 390 } 391 392 uint32 flavors = B_FILE_NODE; 393 if (message->FindBool("allowDirs")) 394 flavors |= B_DIRECTORY_NODE; 395 fFilePanel->SetNodeFlavors(flavors); 396 397 398 fFilePanel->SetMessage(new BMessage(what)); 399 fFilePanel->Window()->SetTitle(title.String()); 400 fFilePanel->SetTarget(target); 401 402 if (!fFilePanel->IsShowing()) 403 fFilePanel->Show(); 404 break; 405 } 406 407 case B_SILENT_RELAUNCH: 408 // In case we were launched via the add-on, there is no types 409 // window yet. 410 if (fTypesWindow == NULL) 411 PostMessage(kMsgOpenTypesWindow); 412 break; 413 414 case B_CANCEL: 415 if (fWindowCount == 0) 416 PostMessage(B_QUIT_REQUESTED); 417 break; 418 419 case B_SIMPLE_DATA: 420 RefsReceived(message); 421 break; 422 423 default: 424 BApplication::MessageReceived(message); 425 break; 426 } 427 } 428 429 430 bool 431 FileTypes::QuitRequested() 432 { 433 return true; 434 } 435 436 437 void 438 FileTypes::_WindowClosed() 439 { 440 if (--fWindowCount == 0 && !fFilePanel->IsShowing()) 441 PostMessage(B_QUIT_REQUESTED); 442 } 443 444 445 // #pragma mark - 446 447 448 bool 449 is_application(BFile& file) 450 { 451 BAppFileInfo appInfo(&file); 452 if (appInfo.InitCheck() != B_OK) 453 return false; 454 455 char type[B_MIME_TYPE_LENGTH]; 456 if (appInfo.GetType(type) != B_OK 457 || strcasecmp(type, B_APP_MIME_TYPE)) 458 return false; 459 460 return true; 461 } 462 463 464 bool 465 is_resource(BFile& file) 466 { 467 BResources resources(&file); 468 if (resources.InitCheck() != B_OK) 469 return false; 470 471 BNodeInfo nodeInfo(&file); 472 char type[B_MIME_TYPE_LENGTH]; 473 if (nodeInfo.GetType(type) != B_OK 474 || strcasecmp(type, B_RESOURCE_MIME_TYPE)) 475 return false; 476 477 return true; 478 } 479 480 481 void 482 error_alert(const char* message, status_t status, alert_type type) 483 { 484 char warning[512]; 485 if (status != B_OK) { 486 snprintf(warning, sizeof(warning), "%s:\n\t%s\n", message, 487 strerror(status)); 488 } 489 490 BAlert* alert = new BAlert(B_TRANSLATE("FileTypes request"), 491 status == B_OK ? message : warning, 492 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, type); 493 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 494 alert->Go(); 495 } 496 497 498 int 499 main(int argc, char** argv) 500 { 501 FileTypes probe; 502 probe.Run(); 503 return 0; 504 } 505