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