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