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