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