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