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_TRANSLATION_CONTEXT 31#define B_TRANSLATION_CONTEXT "FileTypes" 32 33 34const char* kSignature = "application/x-vnd.Haiku-FileTypes"; 35 36static const uint32 kMsgFileTypesSettings = 'FTst'; 37static const uint32 kCascadeOffset = 20; 38 39 40class Settings { 41public: 42 Settings(); 43 ~Settings(); 44 45 const BMessage& Message() const { return fMessage; } 46 void UpdateFrom(BMessage* message); 47 48private: 49 void _SetDefaults(); 50 status_t _Open(BFile* file, int32 mode); 51 52 BMessage fMessage; 53 bool fUpdated; 54}; 55 56class FileTypes : public BApplication { 57public: 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 69private: 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 83Settings::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 104Settings::~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 118void 119Settings::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 146void 147Settings::_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 158status_t 159Settings::_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 174FileTypes::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 187FileTypes::~FileTypes() 188{ 189 delete fFilePanel; 190} 191 192 193void 194FileTypes::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 205void 206FileTypes::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 275void 276FileTypes::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 316void 317FileTypes::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 420bool 421FileTypes::QuitRequested() 422{ 423 return true; 424} 425 426 427void 428FileTypes::_WindowClosed() 429{ 430 if (--fWindowCount == 0 && !fFilePanel->IsShowing()) 431 PostMessage(B_QUIT_REQUESTED); 432} 433 434 435// #pragma mark - 436 437 438bool 439is_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 454bool 455is_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 471void 472error_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 488int 489main(int argc, char** argv) 490{ 491 FileTypes probe; 492 probe.Run(); 493 return 0; 494} 495