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 | B_DIRECTORY_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 if (entry.GetRef(&ref) == B_OK) 242 message->ReplaceRef("refs", index - 1, &ref); 243 continue; 244 } 245 246 // remove application from list 247 message->RemoveData("refs", --index); 248 249 // There are some refs left that want to be handled by the type window 250 BPoint point(100.0f + kCascadeOffset * fTypeWindowCount, 251 110.0f + kCascadeOffset * fTypeWindowCount); 252 253 BWindow* window = new ApplicationTypeWindow(point, entry); 254 window->Show(); 255 256 fTypeWindowCount++; 257 fWindowCount++; 258 } 259 260 if (message->FindRef("refs", &ref) != B_OK) 261 return; 262 263 // There are some refs left that want to be handled by the type window 264 BPoint point(100.0f + kCascadeOffset * fTypeWindowCount, 265 110.0f + kCascadeOffset * fTypeWindowCount); 266 267 BWindow* window = new FileTypeWindow(point, *message); 268 window->Show(); 269 270 fTypeWindowCount++; 271 fWindowCount++; 272 } 273 274 275 void 276 FileTypes::ArgvReceived(int32 argc, char** argv) 277 { 278 if (argc == 3 && strcmp(argv[1], "-type") == 0) { 279 fArgvType = argv[2]; 280 return; 281 } 282 283 BMessage* message = CurrentMessage(); 284 285 BDirectory currentDirectory; 286 if (message != NULL) 287 currentDirectory.SetTo(message->FindString("cwd")); 288 289 BMessage refs; 290 291 for (int i = 1 ; i < argc ; i++) { 292 BPath path; 293 if (argv[i][0] == '/') 294 path.SetTo(argv[i]); 295 else 296 path.SetTo(¤tDirectory, argv[i]); 297 298 status_t status; 299 entry_ref ref; 300 BEntry entry; 301 302 if ((status = entry.SetTo(path.Path(), false)) != B_OK 303 || (status = entry.GetRef(&ref)) != B_OK) { 304 fprintf(stderr, "Could not open file \"%s\": %s\n", 305 path.Path(), strerror(status)); 306 continue; 307 } 308 309 refs.AddRef("refs", &ref); 310 } 311 312 RefsReceived(&refs); 313 } 314 315 316 void 317 FileTypes::MessageReceived(BMessage* message) 318 { 319 switch (message->what) { 320 case kMsgSettingsChanged: 321 fSettings.UpdateFrom(message); 322 break; 323 324 case kMsgOpenTypesWindow: 325 if (fTypesWindow == NULL) { 326 fTypesWindow = new FileTypesWindow(fSettings.Message()); 327 if (fArgvType.Length() > 0) { 328 // Set the window to the type that was requested on the 329 // command line (-type), we do this only once, if we 330 // ever opened more than one FileTypesWindow. 331 fTypesWindow->SelectType(fArgvType.String()); 332 fArgvType = ""; 333 } 334 fTypesWindow->Show(); 335 fWindowCount++; 336 } else 337 fTypesWindow->Activate(true); 338 break; 339 case kMsgTypesWindowClosed: 340 fTypesWindow = NULL; 341 _WindowClosed(); 342 break; 343 344 case kMsgOpenApplicationTypesWindow: 345 if (fApplicationTypesWindow == NULL) { 346 fApplicationTypesWindow = new ApplicationTypesWindow( 347 fSettings.Message()); 348 fApplicationTypesWindow->Show(); 349 fWindowCount++; 350 } else 351 fApplicationTypesWindow->Activate(true); 352 break; 353 case kMsgApplicationTypesWindowClosed: 354 fApplicationTypesWindow = NULL; 355 _WindowClosed(); 356 break; 357 358 case kMsgTypeWindowClosed: 359 fTypeWindowCount--; 360 // supposed to fall through 361 362 case kMsgWindowClosed: 363 _WindowClosed(); 364 break; 365 366 367 case kMsgOpenFilePanel: 368 { 369 // the open file panel sends us a message when it's done 370 const char* subTitle; 371 if (message->FindString("title", &subTitle) != B_OK) 372 subTitle = B_TRANSLATE("Open file"); 373 374 int32 what; 375 if (message->FindInt32("message", &what) != B_OK) 376 what = B_REFS_RECEIVED; 377 378 BMessenger target; 379 if (message->FindMessenger("target", &target) != B_OK) 380 target = be_app_messenger; 381 382 BString title = B_TRANSLATE("FileTypes"); 383 if (subTitle != NULL && subTitle[0]) { 384 title.Append(": "); 385 title.Append(subTitle); 386 } 387 388 fFilePanel->SetMessage(new BMessage(what)); 389 fFilePanel->Window()->SetTitle(title.String()); 390 fFilePanel->SetTarget(target); 391 392 if (!fFilePanel->IsShowing()) 393 fFilePanel->Show(); 394 break; 395 } 396 397 case B_SILENT_RELAUNCH: 398 // In case we were launched via the add-on, there is no types 399 // window yet. 400 if (fTypesWindow == NULL) 401 PostMessage(kMsgOpenTypesWindow); 402 break; 403 404 case B_CANCEL: 405 if (fWindowCount == 0) 406 PostMessage(B_QUIT_REQUESTED); 407 break; 408 409 case B_SIMPLE_DATA: 410 RefsReceived(message); 411 break; 412 413 default: 414 BApplication::MessageReceived(message); 415 break; 416 } 417 } 418 419 420 bool 421 FileTypes::QuitRequested() 422 { 423 return true; 424 } 425 426 427 void 428 FileTypes::_WindowClosed() 429 { 430 if (--fWindowCount == 0 && !fFilePanel->IsShowing()) 431 PostMessage(B_QUIT_REQUESTED); 432 } 433 434 435 // #pragma mark - 436 437 438 bool 439 is_application(BFile& file) 440 { 441 BAppFileInfo appInfo(&file); 442 if (appInfo.InitCheck() != B_OK) 443 return false; 444 445 char type[B_MIME_TYPE_LENGTH]; 446 if (appInfo.GetType(type) != B_OK 447 || strcasecmp(type, B_APP_MIME_TYPE)) 448 return false; 449 450 return true; 451 } 452 453 454 bool 455 is_resource(BFile& file) 456 { 457 BResources resources(&file); 458 if (resources.InitCheck() != B_OK) 459 return false; 460 461 BNodeInfo nodeInfo(&file); 462 char type[B_MIME_TYPE_LENGTH]; 463 if (nodeInfo.GetType(type) != B_OK 464 || strcasecmp(type, B_RESOURCE_MIME_TYPE)) 465 return false; 466 467 return true; 468 } 469 470 471 void 472 error_alert(const char* message, status_t status, alert_type type) 473 { 474 char warning[512]; 475 if (status != B_OK) { 476 snprintf(warning, sizeof(warning), "%s:\n\t%s\n", message, 477 strerror(status)); 478 } 479 480 BAlert* alert = new BAlert(B_TRANSLATE("FileTypes request"), 481 status == B_OK ? message : warning, 482 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, type); 483 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 484 alert->Go(); 485 } 486 487 488 int 489 main(int argc, char** argv) 490 { 491 FileTypes probe; 492 probe.Run(); 493 return 0; 494 } 495