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