1 /* 2 * Copyright 2010-2014, Haiku Inc. All rights reserved. 3 * Copyright 2010 Wim van der Meer <WPJvanderMeer@gmail.com> 4 * Copyright Karsten Heimrich, host.haiku@gmx.de. 5 * All rights reserved. Distributed under the terms of the MIT License. 6 * 7 * Authors: 8 * Karsten Heimrich 9 * Fredrik Modéen 10 * Christophe Huriaux 11 * Wim van der Meer 12 */ 13 14 15 #include "ScreenshotWindow.h" 16 17 #include <stdlib.h> 18 19 #include <Alert.h> 20 #include <Application.h> 21 #include <Bitmap.h> 22 #include <Box.h> 23 #include <Button.h> 24 #include <Catalog.h> 25 #include <CheckBox.h> 26 #include <ControlLook.h> 27 #include <File.h> 28 #include <FilePanel.h> 29 #include <FindDirectory.h> 30 #include <LayoutBuilder.h> 31 #include <Locale.h> 32 #include <Menu.h> 33 #include <MenuField.h> 34 #include <MenuItem.h> 35 #include <MessageFilter.h> 36 #include <Path.h> 37 #include <Roster.h> 38 #include <SeparatorView.h> 39 #include <SpaceLayoutItem.h> 40 #include <String.h> 41 #include <StringView.h> 42 #include <TextControl.h> 43 #include <TranslationUtils.h> 44 #include <TranslatorRoster.h> 45 46 #include "Utility.h" 47 48 49 #undef B_TRANSLATION_CONTEXT 50 #define B_TRANSLATION_CONTEXT "ScreenshotWindow" 51 52 53 enum { 54 kActiveWindow, 55 kIncludeBorder, 56 kIncludeCursor, 57 kNewScreenshot, 58 kImageFormat, 59 kLocationChanged, 60 kChooseLocation, 61 kSaveScreenshot, 62 kSettings, 63 kCloseTranslatorSettings 64 }; 65 66 67 // #pragma mark - QuitMessageFilter 68 69 70 class QuitMessageFilter : public BMessageFilter { 71 public: 72 QuitMessageFilter(BWindow* window) 73 : 74 BMessageFilter((uint32)B_QUIT_REQUESTED), 75 fWindow(window) 76 { 77 } 78 79 virtual filter_result Filter(BMessage* message, BHandler** target) 80 { 81 BMessenger(fWindow).SendMessage(kCloseTranslatorSettings); 82 return B_SKIP_MESSAGE; 83 } 84 85 private: 86 BWindow* fWindow; 87 }; 88 89 90 // #pragma mark - DirectoryRefFilter 91 92 93 class DirectoryRefFilter : public BRefFilter { 94 public: 95 virtual ~DirectoryRefFilter() 96 { 97 } 98 99 virtual bool Filter(const entry_ref* ref, BNode* node, 100 struct stat_beos* stat, const char* filetype) 101 { 102 // ToDo: Fix this properly in Tracker 103 // If you create a folder, then rename it, node is NULL. 104 // The BeBook says: "Note that the function is never sent an 105 // abstract entry, so the node, st, and filetype arguments will 106 // always be valid." 107 return node == NULL ? false : node->IsDirectory(); 108 } 109 }; 110 111 112 // #pragma mark - ScreenshotWindow 113 114 115 ScreenshotWindow::ScreenshotWindow(const Utility& utility, bool silent, 116 bool clipboard) 117 : 118 BWindow(BRect(0, 0, 200.0, 100.0), B_TRANSLATE_SYSTEM_NAME("Screenshot"), 119 B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_AVOID_FRONT 120 | B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS 121 | B_CLOSE_ON_ESCAPE), 122 fUtility(utility), 123 fDelayControl(NULL), 124 fScreenshot(NULL), 125 fOutputPathPanel(NULL), 126 fLastSelectedPath(NULL), 127 fSettingsWindow(NULL), 128 fDelay(0), 129 fIncludeBorder(false), 130 fIncludeCursor(false), 131 fGrabActiveWindow(false), 132 fOutputFilename(NULL), 133 fExtension(""), 134 fImageFileType(B_PNG_FORMAT) 135 { 136 // _ReadSettings() needs a valid fOutputPathMenu 137 fOutputPathMenu = new BMenu(B_TRANSLATE("Please select")); 138 _ReadSettings(); 139 140 // _NewScreenshot() needs a valid fNameControl 141 BString name(B_TRANSLATE_NOCOLLECT(fUtility.sDefaultFileNameBase)); 142 name << 1; 143 name = _FindValidFileName(name.String()); 144 fNameControl = new BTextControl("", B_TRANSLATE("Name:"), name, NULL); 145 146 // Check if fUtility contains valid data 147 if (fUtility.wholeScreen == NULL) { 148 _NewScreenshot(silent, clipboard, true); 149 return; 150 } 151 152 fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, fGrabActiveWindow, 153 fIncludeBorder); 154 155 fActiveWindow = new BCheckBox(B_TRANSLATE("Capture active window"), 156 new BMessage(kActiveWindow)); 157 if (fGrabActiveWindow) 158 fActiveWindow->SetValue(B_CONTROL_ON); 159 160 fWindowBorder = new BCheckBox(B_TRANSLATE("Include window border"), 161 new BMessage(kIncludeBorder)); 162 if (fIncludeBorder) 163 fWindowBorder->SetValue(B_CONTROL_ON); 164 if (!fGrabActiveWindow) 165 fWindowBorder->SetEnabled(false); 166 167 fShowCursor = new BCheckBox(B_TRANSLATE("Include mouse pointer"), 168 new BMessage(kIncludeCursor)); 169 if (fIncludeCursor) 170 fShowCursor->SetValue(B_CONTROL_ON); 171 172 BString delay; 173 delay << fDelay / 1000000; 174 fDelayControl = new BTextControl("", B_TRANSLATE("Delay:"), delay.String(), 175 NULL); 176 _DisallowChar(fDelayControl->TextView()); 177 fDelayControl->TextView()->SetAlignment(B_ALIGN_RIGHT); 178 BStringView* seconds = new BStringView("", B_TRANSLATE("seconds")); 179 seconds->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 180 181 BMenuField* menuLocation = new BMenuField(B_TRANSLATE("Save in:"), 182 fOutputPathMenu); 183 menuLocation->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 184 185 fTranslatorMenu = new BMenu(B_TRANSLATE("Please select")); 186 _SetupTranslatorMenu(); 187 BMenuField* menuFormat = new BMenuField(B_TRANSLATE("Save as:"), 188 fTranslatorMenu); 189 menuFormat->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 190 191 BButton* showSettings = new BButton("", 192 B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), new BMessage(kSettings)); 193 showSettings->SetExplicitAlignment( 194 BAlignment(B_ALIGN_RIGHT, B_ALIGN_BOTTOM)); 195 196 BBox* divider = new BBox(B_FANCY_BORDER, NULL); 197 divider->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1)); 198 199 BButton* saveScreenshot = new BButton("", B_TRANSLATE("Save"), 200 new BMessage(kSaveScreenshot)); 201 202 const float kSpacing = be_control_look->DefaultItemSpacing(); 203 const float kLabelSpacing = be_control_look->DefaultLabelSpacing(); 204 205 fPreview = new BView("preview", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); 206 BBox* previewBox = new BBox(B_FANCY_BORDER, fPreview); 207 208 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 209 .AddGroup(B_HORIZONTAL) 210 .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING, 211 B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING) 212 .Add(previewBox) 213 .AddGroup(B_VERTICAL, 0) 214 .Add(fActiveWindow) 215 .Add(fWindowBorder) 216 .Add(fShowCursor) 217 .AddStrut(kSpacing) 218 .AddGrid(0.0, kSpacing / 2) 219 .Add(fDelayControl->CreateLabelLayoutItem(), 0, 0) 220 .Add(fDelayControl->CreateTextViewLayoutItem(), 1, 0) 221 .Add(BSpaceLayoutItem::CreateHorizontalStrut(kLabelSpacing), 222 2, 0) 223 .Add(seconds, 3, 0) 224 .Add(fNameControl->CreateLabelLayoutItem(), 0, 1) 225 .Add(fNameControl->CreateTextViewLayoutItem(), 1, 1, 3, 1) 226 .Add(menuLocation->CreateLabelLayoutItem(), 0, 2) 227 .Add(menuLocation->CreateMenuBarLayoutItem(), 1, 2, 3, 1) 228 .Add(menuFormat->CreateLabelLayoutItem(), 0, 3) 229 .Add(menuFormat->CreateMenuBarLayoutItem(), 1, 3, 3, 1) 230 .End() 231 .AddStrut(kSpacing / 2) 232 .Add(showSettings) 233 .AddGlue() 234 .End() 235 .End() 236 .Add(new BSeparatorView(B_HORIZONTAL)) 237 .AddGroup(B_HORIZONTAL) 238 .SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING, 239 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING) 240 .Add(new BButton("", B_TRANSLATE("Copy to clipboard"), 241 new BMessage(B_COPY))) 242 .Add(new BButton("", B_TRANSLATE("New screenshot"), 243 new BMessage(kNewScreenshot))) 244 .AddGlue() 245 .Add(saveScreenshot); 246 247 saveScreenshot->MakeDefault(true); 248 249 _UpdatePreviewPanel(); 250 _UpdateFilenameSelection(); 251 252 CenterOnScreen(); 253 Show(); 254 } 255 256 257 ScreenshotWindow::~ScreenshotWindow() 258 { 259 if (fOutputPathPanel) 260 delete fOutputPathPanel->RefFilter(); 261 262 delete fOutputPathPanel; 263 delete fScreenshot; 264 } 265 266 267 void 268 ScreenshotWindow::MessageReceived(BMessage* message) 269 { 270 switch (message->what) { 271 case kActiveWindow: 272 fGrabActiveWindow = false; 273 if (fActiveWindow->Value() == B_CONTROL_ON) 274 fGrabActiveWindow = true; 275 276 fWindowBorder->SetEnabled(fGrabActiveWindow); 277 278 delete fScreenshot; 279 fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, 280 fGrabActiveWindow, fIncludeBorder); 281 _UpdatePreviewPanel(); 282 break; 283 284 case kIncludeBorder: 285 fIncludeBorder = (fWindowBorder->Value() == B_CONTROL_ON); 286 delete fScreenshot; 287 fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, 288 fGrabActiveWindow, fIncludeBorder); 289 _UpdatePreviewPanel(); 290 break; 291 292 case kIncludeCursor: 293 fIncludeCursor = (fShowCursor->Value() == B_CONTROL_ON); 294 delete fScreenshot; 295 fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, 296 fGrabActiveWindow, fIncludeBorder); 297 _UpdatePreviewPanel(); 298 break; 299 300 case kNewScreenshot: 301 fDelay = (atoi(fDelayControl->Text()) * 1000000) + 50000; 302 _NewScreenshot(); 303 break; 304 305 case kImageFormat: 306 message->FindInt32("be:type", &fImageFileType); 307 fNameControl->SetText(_FindValidFileName( 308 fNameControl->Text()).String()); 309 _UpdateFilenameSelection(); 310 _ShowSettings(false); 311 break; 312 313 case kLocationChanged: 314 { 315 void* source = NULL; 316 if (message->FindPointer("source", &source) == B_OK) 317 fLastSelectedPath = static_cast<BMenuItem*> (source); 318 319 fNameControl->SetText(_FindValidFileName( 320 fNameControl->Text()).String()); 321 322 _UpdateFilenameSelection(); 323 break; 324 } 325 326 case kChooseLocation: 327 { 328 if (!fOutputPathPanel) { 329 BMessenger target(this); 330 fOutputPathPanel = new BFilePanel(B_OPEN_PANEL, &target, NULL, 331 B_DIRECTORY_NODE, false, NULL, new DirectoryRefFilter()); 332 fOutputPathPanel->Window()->SetTitle( 333 B_TRANSLATE("Choose folder")); 334 fOutputPathPanel->SetButtonLabel(B_DEFAULT_BUTTON, 335 B_TRANSLATE("Select")); 336 fOutputPathPanel->SetButtonLabel(B_CANCEL_BUTTON, 337 B_TRANSLATE("Cancel")); 338 } 339 fOutputPathPanel->Show(); 340 break; 341 } 342 343 case B_REFS_RECEIVED: 344 { 345 entry_ref ref; 346 if (message->FindRef("refs", &ref) == B_OK) { 347 BEntry entry(&ref, true); 348 if (entry.InitCheck() == B_OK) { 349 BPath path; 350 if (entry.GetPath(&path) == B_OK) { 351 BString label(path.Path()); 352 _AddItemToPathMenu(path.Path(), label, 3, true); 353 } 354 } 355 } 356 break; 357 } 358 359 case B_CANCEL: 360 fLastSelectedPath->SetMarked(true); 361 break; 362 363 case kSaveScreenshot: 364 if (_SaveScreenshot() == B_OK) 365 be_app->PostMessage(B_QUIT_REQUESTED); 366 break; 367 368 case B_COPY: 369 fUtility.CopyToClipboard(fScreenshot); 370 break; 371 372 case kSettings: 373 _ShowSettings(true); 374 break; 375 376 case kCloseTranslatorSettings: 377 fSettingsWindow->Lock(); 378 fSettingsWindow->Quit(); 379 fSettingsWindow = NULL; 380 break; 381 382 default: 383 BWindow::MessageReceived(message); 384 break; 385 } 386 } 387 388 389 void 390 ScreenshotWindow::Quit() 391 { 392 if (fUtility.wholeScreen != NULL) 393 _WriteSettings(); 394 BWindow::Quit(); 395 } 396 397 398 void 399 ScreenshotWindow::_NewScreenshot(bool silent, bool clipboard, bool ignoreDelay) 400 { 401 BMessage message(B_ARGV_RECEIVED); 402 int32 argc = 1; 403 message.AddString("argv", "screenshot"); 404 405 if (!ignoreDelay) { 406 argc += 2; 407 BString delay; 408 delay << fDelay / 1000000; 409 message.AddString("argv", "--delay"); 410 message.AddString("argv", delay); 411 } 412 413 if (silent || clipboard) { 414 if (silent) { 415 argc++; 416 message.AddString("argv", "--silent"); 417 } 418 if (clipboard) { 419 argc++; 420 message.AddString("argv", "--clipboard"); 421 } 422 if (fIncludeBorder) { 423 argc++; 424 message.AddString("argv", "--border"); 425 } 426 if (fIncludeCursor) { 427 argc++; 428 message.AddString("argv", "--mouse-pointer"); 429 } 430 if (fGrabActiveWindow) { 431 argc++; 432 message.AddString("argv", "--window"); 433 } 434 if (fLastSelectedPath) { 435 BPath path(_GetDirectory()); 436 if (path != NULL) { 437 path.Append(fNameControl->Text()); 438 argc++; 439 message.AddString("argv", path.Path()); 440 } 441 } 442 } 443 message.AddInt32("argc", argc); 444 445 be_roster->Launch("application/x-vnd.haiku-screenshot-cli", &message); 446 be_app->PostMessage(B_QUIT_REQUESTED); 447 } 448 449 450 void 451 ScreenshotWindow::_UpdatePreviewPanel() 452 { 453 // Set the height of fPreview to what the layout suggests 454 fPreview->SetExplicitMinSize(BSize()); 455 fPreview->SetExplicitMaxSize(BSize()); 456 Layout(false); 457 458 float height = fPreview->Bounds().Height(); 459 float width = (fScreenshot->Bounds().Width() 460 / fScreenshot->Bounds().Height()) * height; 461 462 // to prevent a preview way too wide 463 if (width > 400.0f) { 464 width = 400.0f; 465 height = (fScreenshot->Bounds().Height() 466 / fScreenshot->Bounds().Width()) * width; 467 } 468 469 fPreview->SetExplicitMinSize(BSize(width, height)); 470 fPreview->SetExplicitMaxSize(BSize(width, height)); 471 472 fPreview->ClearViewBitmap(); 473 fPreview->SetViewBitmap(fScreenshot, fScreenshot->Bounds(), 474 fPreview->Bounds(), B_FOLLOW_ALL, B_FILTER_BITMAP_BILINEAR); 475 } 476 477 478 void 479 ScreenshotWindow::_DisallowChar(BTextView* textView) 480 { 481 for (uint32 i = 0; i < '0'; ++i) 482 textView->DisallowChar(i); 483 484 for (uint32 i = '9' + 1; i < 255; ++i) 485 textView->DisallowChar(i); 486 } 487 488 489 void 490 ScreenshotWindow::_SetupOutputPathMenu(const BMessage& settings) 491 { 492 fOutputPathMenu->SetLabelFromMarked(true); 493 494 BString lastSelectedPath; 495 settings.FindString("lastSelectedPath", &lastSelectedPath); 496 497 BPath path; 498 find_directory(B_USER_DIRECTORY, &path); 499 500 BString label(B_TRANSLATE("Home folder")); 501 _AddItemToPathMenu(path.Path(), label, 0, 502 path.Path() == lastSelectedPath, 'H'); 503 504 path.Append("Desktop"); 505 label.SetTo(B_TRANSLATE("Desktop")); 506 _AddItemToPathMenu(path.Path(), label, 0, 507 path.Path() == lastSelectedPath, 'D'); 508 509 find_directory(B_USER_NONPACKAGED_DATA_DIRECTORY, &path); 510 path.Append("artwork"); 511 512 label.SetTo(B_TRANSLATE("Artwork folder")); 513 _AddItemToPathMenu(path.Path(), label, 2, 514 path.Path() == lastSelectedPath, 'A'); 515 516 int32 i = 0; 517 BString userPath; 518 while (settings.FindString("path", ++i, &userPath) == B_OK) { 519 _AddItemToPathMenu(userPath.String(), userPath, 3, 520 userPath == lastSelectedPath); 521 } 522 523 if (!fLastSelectedPath) { 524 if (settings.IsEmpty() || lastSelectedPath.Length() == 0) { 525 fOutputPathMenu->ItemAt(1)->SetMarked(true); 526 fLastSelectedPath = fOutputPathMenu->ItemAt(1); 527 } else { 528 _AddItemToPathMenu(lastSelectedPath.String(), lastSelectedPath, 3, 529 true); 530 } 531 } 532 533 fOutputPathMenu->AddItem(new BSeparatorItem()); 534 fOutputPathMenu->AddItem(new BMenuItem( 535 B_TRANSLATE("Choose folder" B_UTF8_ELLIPSIS), 536 new BMessage(kChooseLocation), 'F')); 537 } 538 539 540 void 541 ScreenshotWindow::_AddItemToPathMenu(const char* path, BString& label, 542 int32 index, bool markItem, uint32 shortcutKey) 543 { 544 // Make sure that item won't be a duplicate of an existing one 545 for (int32 i = fOutputPathMenu->CountItems() - 1; i >= 0; --i) { 546 BMenuItem* menuItem = fOutputPathMenu->ItemAt(i); 547 BMessage* message = menuItem->Message(); 548 const char* pathFromItem; 549 if (message != NULL && message->what == kLocationChanged 550 && message->FindString("path", &pathFromItem) == B_OK 551 && !strcmp(path, pathFromItem)) { 552 553 if (markItem) { 554 fOutputPathMenu->ItemAt(i)->SetMarked(true); 555 fLastSelectedPath = fOutputPathMenu->ItemAt(i); 556 } 557 return; 558 } 559 } 560 561 BMessage* message = new BMessage(kLocationChanged); 562 message->AddString("path", path); 563 564 fOutputPathMenu->TruncateString(&label, B_TRUNCATE_MIDDLE, 565 fOutputPathMenu->StringWidth("SomethingLongHere")); 566 567 fOutputPathMenu->AddItem(new BMenuItem(label.String(), message, 568 shortcutKey), index); 569 570 if (markItem) { 571 fOutputPathMenu->ItemAt(index)->SetMarked(true); 572 fLastSelectedPath = fOutputPathMenu->ItemAt(index); 573 } 574 } 575 576 577 void 578 ScreenshotWindow::_UpdateFilenameSelection() 579 { 580 fNameControl->MakeFocus(true); 581 fNameControl->TextView()->Select(0, fNameControl->TextView()->TextLength() 582 - fExtension.Length()); 583 584 fNameControl->TextView()->ScrollToSelection(); 585 } 586 587 588 void 589 ScreenshotWindow::_SetupTranslatorMenu() 590 { 591 BMessage message(kImageFormat); 592 fTranslatorMenu = new BMenu("Please select"); 593 BTranslationUtils::AddTranslationItems(fTranslatorMenu, B_TRANSLATOR_BITMAP, 594 &message, NULL, NULL, NULL); 595 596 fTranslatorMenu->SetLabelFromMarked(true); 597 598 if (fTranslatorMenu->ItemAt(0)) 599 fTranslatorMenu->ItemAt(0)->SetMarked(true); 600 601 int32 imageFileType; 602 for (int32 i = 0; i < fTranslatorMenu->CountItems(); ++i) { 603 BMenuItem* item = fTranslatorMenu->ItemAt(i); 604 if (item != NULL && item->Message()) { 605 item->Message()->FindInt32("be:type", &imageFileType); 606 if (fImageFileType == imageFileType) { 607 item->SetMarked(true); 608 MessageReceived(item->Message()); 609 break; 610 } 611 } 612 } 613 } 614 615 616 void 617 ScreenshotWindow::_DisplaySaveError(BString _message) { 618 BString alertText; 619 alertText.SetToFormat(B_TRANSLATE("Error saving \"%s\":\n\t%s"), 620 fNameControl->Text(), _message.String()); 621 622 BAlert* alert = new BAlert(B_TRANSLATE("Failed to save screenshot"), 623 alertText.String(), B_TRANSLATE("OK"), 624 NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 625 626 alert->SetShortcut(0, B_ESCAPE); 627 alert->Go(); 628 } 629 630 631 status_t 632 ScreenshotWindow::_SaveScreenshot() 633 { 634 if (!fScreenshot || !fLastSelectedPath) 635 return B_ERROR; 636 637 BPath path(_GetDirectory()); 638 639 if (path == NULL) 640 return B_ERROR; 641 642 BEntry directoryEntry; 643 directoryEntry.SetTo(path.Path()); 644 645 // create folder if it doesn't exist 646 // necessary, for example, when the user selects the Artwork folder from 647 // the list of predefined folders. 648 if (!directoryEntry.Exists()) { 649 status_t directoryCreateStatus = create_directory(path.Path(), 0755); 650 if (directoryCreateStatus != B_OK) { 651 _DisplaySaveError(strerror(directoryCreateStatus)); 652 653 return directoryCreateStatus; 654 } 655 } else if (!directoryEntry.IsDirectory()) { 656 // the entry exists but is not a directory. 657 // not much we can do 658 _DisplaySaveError( 659 B_TRANSLATE("The destination path exists but is not a folder.")); 660 661 return B_NOT_A_DIRECTORY; 662 } 663 664 path.Append(fNameControl->Text()); 665 666 BEntry entry; 667 entry.SetTo(path.Path()); 668 669 if (entry.Exists()) { 670 BAlert* overwriteAlert = new BAlert( 671 B_TRANSLATE("overwrite"), 672 B_TRANSLATE("This file already exists.\n Are you sure you would " 673 "like to overwrite it?"), 674 B_TRANSLATE("Cancel"), 675 B_TRANSLATE("Overwrite"), 676 NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 677 678 overwriteAlert->SetShortcut(0, B_ESCAPE); 679 680 if (overwriteAlert->Go() == 0) 681 return B_CANCELED; 682 } 683 684 status_t saveStatus = fUtility.Save(fScreenshot, 685 path.Path(), fImageFileType); 686 687 if (saveStatus != B_OK) { 688 _DisplaySaveError(strerror(saveStatus)); 689 return saveStatus; 690 } 691 return B_OK; 692 } 693 694 695 void 696 ScreenshotWindow::_ShowSettings(bool activate) 697 { 698 if (!fSettingsWindow && !activate) 699 return; 700 701 // Find a translator 702 translator_id translator; 703 if (fUtility.FindTranslator(fImageFileType, translator) != B_OK) 704 return; 705 706 // Create a window with a configuration view 707 BView* view; 708 BRect rect(0, 0, 239, 239); 709 710 status_t status = BTranslatorRoster::Default()->MakeConfigurationView( 711 translator, NULL, &view, &rect); 712 if (status != B_OK || view == NULL) { 713 // TODO: proper translation, better error dialog 714 BAlert* alert = new BAlert(NULL, strerror(status), "OK"); 715 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 716 alert->Go(); 717 } else if (fSettingsWindow != NULL) { 718 fSettingsWindow->RemoveChild(fSettingsWindow->ChildAt(0)); 719 float width, height; 720 view->GetPreferredSize(&width, &height); 721 fSettingsWindow->ResizeTo(width, height); 722 fSettingsWindow->AddChild(view); 723 if (activate) 724 fSettingsWindow->Activate(); 725 } else { 726 fSettingsWindow = new BWindow(rect, 727 B_TRANSLATE("Translator Settings"), 728 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 729 B_NOT_ZOOMABLE | B_NOT_RESIZABLE); 730 fSettingsWindow->AddFilter(new QuitMessageFilter(this)); 731 fSettingsWindow->AddChild(view); 732 fSettingsWindow->CenterOnScreen(); 733 fSettingsWindow->Show(); 734 } 735 } 736 737 738 BString 739 ScreenshotWindow::_FindValidFileName(const char* name) 740 { 741 BString baseName(name); 742 743 if (!fExtension.IsEmpty()) 744 baseName.RemoveLast(fExtension); 745 746 if (!fLastSelectedPath) 747 return baseName; 748 749 BPath orgPath(_GetDirectory()); 750 if (orgPath == NULL) 751 return baseName; 752 753 fExtension = fUtility.FileNameExtension(fImageFileType); 754 755 BPath outputPath = orgPath; 756 BString fileName; 757 fileName << baseName << fExtension; 758 outputPath.Append(fileName); 759 760 if (!BEntry(outputPath.Path()).Exists()) 761 return fileName; 762 763 if (baseName.FindFirst(B_TRANSLATE_NOCOLLECT( 764 fUtility.sDefaultFileNameBase)) == 0) 765 baseName.SetTo(fUtility.sDefaultFileNameBase); 766 767 BEntry entry; 768 int32 index = 1; 769 770 do { 771 fileName = ""; 772 fileName << baseName << index++ << fExtension; 773 outputPath.SetTo(orgPath.Path()); 774 outputPath.Append(fileName); 775 entry.SetTo(outputPath.Path()); 776 } while (entry.Exists()); 777 778 return fileName; 779 } 780 781 782 BPath 783 ScreenshotWindow::_GetDirectory() 784 { 785 BPath path; 786 787 BMessage* message = fLastSelectedPath->Message(); 788 const char* stringPath; 789 if (message && message->FindString("path", &stringPath) == B_OK) 790 path.SetTo(stringPath); 791 792 return path; 793 } 794 795 796 void 797 ScreenshotWindow::_ReadSettings() 798 { 799 BMessage settings; 800 801 BPath settingsPath; 802 if (find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath) != B_OK) 803 return; 804 805 settingsPath.Append("Screenshot_settings"); 806 807 BFile file(settingsPath.Path(), B_READ_ONLY); 808 if (file.InitCheck() == B_OK) 809 settings.Unflatten(&file); 810 811 if (settings.FindInt32("type", &fImageFileType) != B_OK) 812 fImageFileType = B_PNG_FORMAT; 813 settings.FindBool("includeBorder", &fIncludeBorder); 814 settings.FindBool("includeCursor", &fIncludeCursor); 815 settings.FindBool("grabActiveWindow", &fGrabActiveWindow); 816 settings.FindInt64("delay", &fDelay); 817 settings.FindString("outputFilename", &fOutputFilename); 818 819 _SetupOutputPathMenu(settings); 820 } 821 822 823 void 824 ScreenshotWindow::_WriteSettings() 825 { 826 if (fDelayControl) 827 fDelay = (atoi(fDelayControl->Text()) * 1000000) + 50000; 828 829 BMessage settings; 830 831 settings.AddInt32("type", fImageFileType); 832 settings.AddBool("includeBorder", fIncludeBorder); 833 settings.AddBool("includeCursor", fIncludeCursor); 834 settings.AddBool("grabActiveWindow", fGrabActiveWindow); 835 settings.AddInt64("delay", fDelay); 836 settings.AddString("outputFilename", fOutputFilename); 837 838 BString path; 839 int32 count = fOutputPathMenu->CountItems(); 840 if (count > 5) { 841 for (int32 i = count - 3; i > count - 8 && i > 2; --i) { 842 BMenuItem* item = fOutputPathMenu->ItemAt(i); 843 if (item) { 844 BMessage* msg = item->Message(); 845 if (msg && msg->FindString("path", &path) == B_OK) 846 settings.AddString("path", path.String()); 847 } 848 } 849 } 850 851 if (fLastSelectedPath) { 852 BMessage* msg = fLastSelectedPath->Message(); 853 if (msg && msg->FindString("path", &path) == B_OK) 854 settings.AddString("lastSelectedPath", path.String()); 855 } 856 857 BPath settingsPath; 858 if (find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath) != B_OK) 859 return; 860 settingsPath.Append("Screenshot_settings"); 861 862 BFile file(settingsPath.Path(), B_CREATE_FILE | B_ERASE_FILE 863 | B_WRITE_ONLY); 864 if (file.InitCheck() == B_OK) { 865 ssize_t size; 866 settings.Flatten(&file, &size); 867 } 868 } 869