1 /* 2 * Copyright Karsten Heimrich, host.haiku@gmx.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Karsten Heimrich 7 * Fredrik Modéen 8 * Christophe Huriaux 9 * Wim van der Meer, WPJvanderMeer@gmail.com 10 */ 11 12 13 #include "ScreenshotWindow.h" 14 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 19 20 #include <Alert.h> 21 #include <Application.h> 22 #include <Bitmap.h> 23 #include <Box.h> 24 #include <BitmapStream.h> 25 #include <Button.h> 26 #include <CardLayout.h> 27 #include <Catalog.h> 28 #include <CheckBox.h> 29 #include <Directory.h> 30 #include <Entry.h> 31 #include <File.h> 32 #include <FindDirectory.h> 33 #include <FilePanel.h> 34 #include <GridLayoutBuilder.h> 35 #include <GroupLayoutBuilder.h> 36 #include <LayoutItem.h> 37 #include <Locale.h> 38 #include <Menu.h> 39 #include <MenuField.h> 40 #include <MenuItem.h> 41 #include <Message.h> 42 #include <NodeInfo.h> 43 #include <Path.h> 44 #include <RadioButton.h> 45 #include <Region.h> 46 #include <Roster.h> 47 #include <Screen.h> 48 #include <String.h> 49 #include <StringView.h> 50 #include <SpaceLayoutItem.h> 51 #include <TextControl.h> 52 #include <TranslatorFormats.h> 53 #include <TranslationUtils.h> 54 #include <TranslatorRoster.h> 55 #include <View.h> 56 #include <WindowInfo.h> 57 58 59 #include "PreviewView.h" 60 61 62 enum { 63 kScreenshotType, 64 kIncludeBorder, 65 kShowMouse, 66 kBackToSave, 67 kTakeScreenshot, 68 kImageOutputFormat, 69 kLocationChanged, 70 kChooseLocation, 71 kFinishScreenshot, 72 kShowOptions 73 }; 74 75 76 // #pragma mark - DirectoryRefFilter 77 78 79 class DirectoryRefFilter : public BRefFilter { 80 public: 81 virtual ~DirectoryRefFilter() 82 { 83 } 84 85 virtual bool Filter(const entry_ref* ref, BNode* node, 86 struct stat_beos* stat, const char* filetype) 87 { 88 return node->IsDirectory(); 89 } 90 }; 91 92 93 // #pragma mark - ScreenshotWindow 94 95 96 #undef TR_CONTEXT 97 #define TR_CONTEXT "ScreenshotWindow" 98 99 100 ScreenshotWindow::ScreenshotWindow() 101 : 102 BWindow(BRect(0, 0, 200.0, 100.0), B_TRANSLATE("Retake screenshot"), 103 B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE 104 | B_QUIT_ON_WINDOW_CLOSE | B_AVOID_FRONT 105 | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE), 106 fDelayControl(NULL), 107 fScreenshot(NULL), 108 fOutputPathPanel(NULL), 109 fLastSelectedPath(NULL), 110 fDelay(0), 111 fTabHeight(0), 112 fIncludeBorder(false), 113 fIncludeMouse(false), 114 fGrabActiveWindow(false), 115 fShowConfigWindow(false), 116 fSaveScreenshotSilent(false), 117 fOutputFilename(NULL), 118 fExtension(""), 119 fImageFileType(B_PNG_FORMAT) 120 { 121 BMessage settings = _ReadSettings(); 122 123 if (settings.FindInt32("type", &fImageFileType) != B_OK) 124 fImageFileType = B_PNG_FORMAT; 125 settings.FindBool("includeBorder", &fIncludeBorder); 126 settings.FindBool("includeMouse", &fIncludeMouse); 127 settings.FindBool("grabActiveWindow", &fGrabActiveWindow); 128 settings.FindInt64("delay", &fDelay); 129 settings.FindString("outputFilename", &fOutputFilename); 130 131 _InitWindow(settings); 132 CenterOnScreen(); 133 Show(); 134 } 135 136 137 ScreenshotWindow::ScreenshotWindow(bigtime_t delay, bool includeBorder, 138 bool includeMouse, bool grabActiveWindow, bool showConfigWindow, 139 bool saveScreenshotSilent, int32 imageFileType, const char* outputFilename) 140 : 141 BWindow(BRect(0, 0, 200.0, 100.0), B_TRANSLATE("Retake screenshot"), 142 B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE 143 | B_QUIT_ON_WINDOW_CLOSE | B_AVOID_FRONT 144 | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE), 145 fDelayControl(NULL), 146 fScreenshot(NULL), 147 fOutputPathPanel(NULL), 148 fLastSelectedPath(NULL), 149 fDelay(delay), 150 fTabHeight(0), 151 fIncludeBorder(includeBorder), 152 fIncludeMouse(includeMouse), 153 fGrabActiveWindow(grabActiveWindow), 154 fShowConfigWindow(showConfigWindow), 155 fSaveScreenshotSilent(saveScreenshotSilent), 156 fOutputFilename(outputFilename), 157 fExtension(""), 158 fImageFileType(imageFileType) 159 { 160 if (fSaveScreenshotSilent) { 161 _TakeScreenshot(fDelay); 162 _SaveScreenshot(); 163 be_app_messenger.SendMessage(B_QUIT_REQUESTED); 164 } else { 165 BMessage settings = _ReadSettings(); 166 _InitWindow(settings); 167 CenterOnScreen(); 168 Show(); 169 } 170 } 171 172 173 ScreenshotWindow::~ScreenshotWindow() 174 { 175 if (fOutputPathPanel) 176 delete fOutputPathPanel->RefFilter(); 177 178 delete fScreenshot; 179 delete fOutputPathPanel; 180 } 181 182 183 void 184 ScreenshotWindow::MessageReceived(BMessage* message) 185 { 186 // message->PrintToStream(); 187 switch (message->what) { 188 case kScreenshotType: 189 fGrabActiveWindow = false; 190 if (fActiveWindow->Value() == B_CONTROL_ON) 191 fGrabActiveWindow = true; 192 fWindowBorder->SetEnabled(fGrabActiveWindow); 193 break; 194 195 case kIncludeBorder: 196 fIncludeBorder = (fWindowBorder->Value() == B_CONTROL_ON); 197 break; 198 199 case kShowMouse: 200 fIncludeMouse = (fShowMouse->Value() == B_CONTROL_ON); 201 break; 202 203 case kBackToSave: 204 { 205 BCardLayout* layout = dynamic_cast<BCardLayout*> (GetLayout()); 206 if (layout) 207 layout->SetVisibleItem(1L); 208 209 fSaveScreenshot->MakeDefault(true); 210 211 SetTitle(B_TRANSLATE("Save screenshot")); 212 break; 213 } 214 215 case kTakeScreenshot: 216 fDelay = (atoi(fDelayControl->Text()) * 1000000) + 50000; 217 Hide(); 218 _TakeScreenshot(fDelay); 219 _UpdatePreviewPanel(); 220 Show(); 221 _UpdateFilenameSelection(); 222 break; 223 224 case kImageOutputFormat: 225 message->FindInt32("be:type", &fImageFileType); 226 fNameControl->SetText(_FindValidFileName( 227 fNameControl->Text()).String()); 228 _UpdateFilenameSelection(); 229 break; 230 231 case kLocationChanged: 232 { 233 void* source = NULL; 234 if (message->FindPointer("source", &source) == B_OK) 235 fLastSelectedPath = static_cast<BMenuItem*> (source); 236 237 fNameControl->SetText(_FindValidFileName( 238 fNameControl->Text()).String()); 239 _UpdateFilenameSelection(); 240 break; 241 } 242 243 case kChooseLocation: 244 { 245 if (!fOutputPathPanel) { 246 BMessenger target(this); 247 fOutputPathPanel = new BFilePanel(B_OPEN_PANEL, &target, NULL, 248 B_DIRECTORY_NODE, false, NULL, new DirectoryRefFilter()); 249 fOutputPathPanel->Window()->SetTitle( 250 B_TRANSLATE("Choose folder")); 251 fOutputPathPanel->SetButtonLabel(B_DEFAULT_BUTTON, 252 B_TRANSLATE("Select")); 253 fOutputPathPanel->SetButtonLabel(B_CANCEL_BUTTON, 254 B_TRANSLATE("Cancel")); 255 } 256 fOutputPathPanel->Show(); 257 break; 258 } 259 260 case B_CANCEL: 261 fLastSelectedPath->SetMarked(true); 262 break; 263 264 case B_REFS_RECEIVED: 265 { 266 entry_ref ref; 267 if (message->FindRef("refs", &ref) == B_OK) { 268 BString path(BPath(&ref).Path()); 269 int32 index = _PathIndexInMenu(path); 270 if (index < 0) 271 _AddItemToPathMenu(path.String(), path, 272 fOutputPathMenu->CountItems() - 2, true); 273 else 274 fOutputPathMenu->ItemAt(index)->SetMarked(true); 275 } 276 break; 277 } 278 279 case kFinishScreenshot: 280 _WriteSettings(); 281 if (_SaveScreenshot() != B_OK) 282 break; 283 284 // fall through 285 case B_QUIT_REQUESTED: 286 _WriteSettings(); 287 be_app_messenger.SendMessage(B_QUIT_REQUESTED); 288 break; 289 290 case kShowOptions: 291 { 292 BCardLayout* layout = dynamic_cast<BCardLayout*> (GetLayout()); 293 294 if (layout) 295 layout->SetVisibleItem(0L); 296 SetDefaultButton(NULL); 297 298 SetTitle(B_TRANSLATE("Take Screenshot")); 299 fBackToSave->SetEnabled(true); 300 break; 301 } 302 303 default: 304 BWindow::MessageReceived(message); 305 break; 306 } 307 } 308 309 310 BPath 311 ScreenshotWindow::_GetDirectory() 312 { 313 BPath path; 314 if (!fSaveScreenshotSilent) { 315 BMessage* message = fLastSelectedPath->Message(); 316 const char* stringPath; 317 if (!message || message->FindString("path", &stringPath) != B_OK) { 318 fprintf(stderr, "failed to find path in message\n"); 319 } else 320 path.SetTo(stringPath); 321 } else { 322 if (find_directory(B_USER_DIRECTORY, &path) != B_OK) 323 fprintf(stderr, "failed to find user home folder\n"); 324 } 325 return path; 326 } 327 328 329 void 330 ScreenshotWindow::_InitWindow(const BMessage& settings) 331 { 332 BCardLayout* layout = new BCardLayout(); 333 SetLayout(layout); 334 335 _SetupFirstLayoutItem(layout); 336 _SetupSecondLayoutItem(layout, settings); 337 338 if (!fShowConfigWindow) { 339 _TakeScreenshot(0); 340 _UpdatePreviewPanel(); 341 layout->SetVisibleItem(1L); 342 fSaveScreenshot->MakeDefault(true); 343 } else { 344 layout->SetVisibleItem(0L); 345 SetDefaultButton(NULL); 346 } 347 } 348 349 350 void 351 ScreenshotWindow::_SetupFirstLayoutItem(BCardLayout* layout) 352 { 353 BStringView* stringView = new BStringView("", B_TRANSLATE("Options")); 354 stringView->SetFont(be_bold_font); 355 stringView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 356 357 fActiveWindow = new BRadioButton(B_TRANSLATE("Capture active window"), 358 new BMessage(kScreenshotType)); 359 fWholeDesktop = new BRadioButton(B_TRANSLATE("Capture entire screen"), 360 new BMessage(kScreenshotType)); 361 fWholeDesktop->SetValue(B_CONTROL_ON); 362 363 BString delay; 364 delay << fDelay / 1000000; 365 fDelayControl = new BTextControl("", 366 B_TRANSLATE("Take screenshot after a delay of"), delay.String(), NULL); 367 _DisallowChar(fDelayControl->TextView()); 368 fDelayControl->TextView()->SetAlignment(B_ALIGN_RIGHT); 369 370 BStringView* stringView2 = new BStringView("", B_TRANSLATE("seconds")); 371 stringView2->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 372 373 fWindowBorder = new BCheckBox(B_TRANSLATE("Include window border"), 374 new BMessage(kIncludeBorder)); 375 fWindowBorder->SetEnabled(false); 376 377 fShowMouse = new BCheckBox(B_TRANSLATE("Include mouse pointer"), 378 new BMessage(kShowMouse)); 379 fShowMouse->SetValue(fIncludeMouse); 380 381 BBox* divider = new BBox(B_FANCY_BORDER, NULL); 382 divider->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1)); 383 384 fBackToSave = new BButton("", B_TRANSLATE("Back to saving"), 385 new BMessage(kBackToSave)); 386 fBackToSave->SetEnabled(false); 387 388 fTakeScreenshot = new BButton("", B_TRANSLATE("Take screenshot"), 389 new BMessage(kTakeScreenshot)); 390 391 layout->AddView(0, BGroupLayoutBuilder(B_VERTICAL) 392 .Add(stringView) 393 .Add(BGridLayoutBuilder() 394 .Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 0) 395 .Add(fWholeDesktop, 1, 0) 396 .Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 1) 397 .Add(fActiveWindow, 1, 1) 398 .SetInsets(0.0, 5.0, 0.0, 0.0)) 399 .AddGroup(B_HORIZONTAL) 400 .AddStrut(30.0) 401 .Add(fWindowBorder) 402 .End() 403 .AddStrut(10.0) 404 .AddGroup(B_HORIZONTAL) 405 .AddStrut(15.0) 406 .Add(fShowMouse) 407 .End() 408 .AddStrut(5.0) 409 .AddGroup(B_HORIZONTAL, 5.0) 410 .AddStrut(10.0) 411 .Add(fDelayControl->CreateLabelLayoutItem()) 412 .Add(fDelayControl->CreateTextViewLayoutItem()) 413 .Add(stringView2) 414 .End() 415 .AddStrut(10.0) 416 .AddGlue() 417 .Add(divider) 418 .AddStrut(10) 419 .AddGroup(B_HORIZONTAL, 10.0) 420 .Add(fBackToSave) 421 .AddGlue() 422 .Add(new BButton("", B_TRANSLATE("Cancel"), 423 new BMessage(B_QUIT_REQUESTED))) 424 .Add(fTakeScreenshot) 425 .End() 426 .SetInsets(10.0, 10.0, 10.0, 10.0) 427 ); 428 429 if (fGrabActiveWindow) { 430 fWindowBorder->SetEnabled(true); 431 fActiveWindow->SetValue(B_CONTROL_ON); 432 fWindowBorder->SetValue(fIncludeBorder); 433 } 434 } 435 436 437 void 438 ScreenshotWindow::_SetupSecondLayoutItem(BCardLayout* layout, 439 const BMessage& settings) 440 { 441 fPreview = new PreviewView(); 442 443 fNameControl = new BTextControl("", B_TRANSLATE("Name:"), 444 B_TRANSLATE_COMMENT("screenshot1", "!! Filename of first " 445 "screenshot !!"), NULL); 446 447 _SetupOutputPathMenu(new BMenu(B_TRANSLATE("Please select")), settings); 448 449 BMenuField* menuField2 = new BMenuField(B_TRANSLATE("Save in:"), 450 fOutputPathMenu); 451 452 fNameControl->SetText(_FindValidFileName( 453 B_TRANSLATE_COMMENT("screenshot1", "!! Filename of first " 454 "screenshot !!")).String()); 455 456 _SetupTranslatorMenu(new BMenu(B_TRANSLATE("Please select"))); 457 BMenuField* menuField = new BMenuField(B_TRANSLATE("Save as:"), 458 fTranslatorMenu); 459 460 BBox* divider = new BBox(B_FANCY_BORDER, NULL); 461 divider->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1)); 462 463 fSaveScreenshot = new BButton("", 464 B_TRANSLATE("Save"), new BMessage(kFinishScreenshot)); 465 466 BGridLayout* gridLayout = BGridLayoutBuilder(0.0, 5.0) 467 .Add(fNameControl->CreateLabelLayoutItem(), 0, 0) 468 .Add(fNameControl->CreateTextViewLayoutItem(), 1, 0) 469 .Add(menuField->CreateLabelLayoutItem(), 0, 1) 470 .Add(menuField->CreateMenuBarLayoutItem(), 1, 1) 471 .Add(menuField2->CreateLabelLayoutItem(), 0, 2) 472 .Add(menuField2->CreateMenuBarLayoutItem(), 1, 2); 473 gridLayout->SetMinColumnWidth(1, 474 menuField->StringWidth("SomethingLongHere")); 475 476 layout->AddView(1, BGroupLayoutBuilder(B_VERTICAL) 477 .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10.0) 478 .Add(fPreview) 479 .AddGroup(B_VERTICAL) 480 .Add(gridLayout->View()) 481 .AddGlue() 482 .End()) 483 .AddStrut(10) 484 .Add(divider) 485 .AddStrut(10) 486 .AddGroup(B_HORIZONTAL, 10.0) 487 .Add(new BButton("", B_TRANSLATE("Options"), 488 new BMessage(kShowOptions))) 489 .AddGlue() 490 .Add(new BButton("", B_TRANSLATE("Cancel"), 491 new BMessage(B_QUIT_REQUESTED))) 492 .Add(fSaveScreenshot) 493 .End() 494 .SetInsets(10.0, 10.0, 10.0, 10.0) 495 ); 496 } 497 498 499 void 500 ScreenshotWindow::_DisallowChar(BTextView* textView) 501 { 502 for (uint32 i = 0; i < '0'; ++i) 503 textView->DisallowChar(i); 504 505 for (uint32 i = '9' + 1; i < 255; ++i) 506 textView->DisallowChar(i); 507 } 508 509 510 void 511 ScreenshotWindow::_SetupTranslatorMenu(BMenu* translatorMenu) 512 { 513 fTranslatorMenu = translatorMenu; 514 515 BMessage message(kImageOutputFormat); 516 fTranslatorMenu = new BMenu("Please select"); 517 BTranslationUtils::AddTranslationItems(fTranslatorMenu, B_TRANSLATOR_BITMAP, 518 &message, NULL, NULL, NULL); 519 520 fTranslatorMenu->SetLabelFromMarked(true); 521 522 if (fTranslatorMenu->ItemAt(0)) 523 fTranslatorMenu->ItemAt(0)->SetMarked(true); 524 525 int32 imageFileType; 526 for (int32 i = 0; i < fTranslatorMenu->CountItems(); ++i) { 527 BMenuItem* item = fTranslatorMenu->ItemAt(i); 528 if (item && item->Message()) { 529 item->Message()->FindInt32("be:type", &imageFileType); 530 if (fImageFileType == imageFileType) { 531 item->SetMarked(true); 532 MessageReceived(item->Message()); 533 break; 534 } 535 } 536 } 537 } 538 539 540 void 541 ScreenshotWindow::_SetupOutputPathMenu(BMenu* outputPathMenu, 542 const BMessage& settings) 543 { 544 fOutputPathMenu = outputPathMenu; 545 fOutputPathMenu->SetLabelFromMarked(true); 546 547 BString lastSelectedPath; 548 settings.FindString("lastSelectedPath", &lastSelectedPath); 549 550 BPath path; 551 find_directory(B_USER_DIRECTORY, &path); 552 553 BString label(B_TRANSLATE("Home folder")); 554 _AddItemToPathMenu(path.Path(), label, 0, 555 (path.Path() == lastSelectedPath)); 556 557 path.Append("Desktop"); 558 label.SetTo(B_TRANSLATE("Desktop")); 559 _AddItemToPathMenu(path.Path(), label, 0, ( 560 path.Path() == lastSelectedPath)); 561 562 find_directory(B_BEOS_ETC_DIRECTORY, &path); 563 path.Append("artwork"); 564 565 label.SetTo(B_TRANSLATE("Artwork folder")); 566 _AddItemToPathMenu(path.Path(), label, 2, 567 (path.Path() == lastSelectedPath)); 568 569 int32 i = 0; 570 BString userPath; 571 while (settings.FindString("path", i++, &userPath) == B_OK) { 572 _AddItemToPathMenu(userPath.String(), userPath, 3, 573 (userPath == lastSelectedPath)); 574 } 575 576 if (!fLastSelectedPath) { 577 if (settings.IsEmpty() || lastSelectedPath.Length() == 0) { 578 fOutputPathMenu->ItemAt(1)->SetMarked(true); 579 fLastSelectedPath = fOutputPathMenu->ItemAt(1); 580 } else 581 _AddItemToPathMenu(lastSelectedPath.String(), lastSelectedPath, 3, 582 true); 583 } 584 585 fOutputPathMenu->AddItem(new BSeparatorItem()); 586 fOutputPathMenu->AddItem(new BMenuItem(B_TRANSLATE("Choose folder..."), 587 new BMessage(kChooseLocation))); 588 } 589 590 591 void 592 ScreenshotWindow::_AddItemToPathMenu(const char* path, BString& label, 593 int32 index, bool markItem) 594 { 595 BMessage* message = new BMessage(kLocationChanged); 596 message->AddString("path", path); 597 598 fOutputPathMenu->TruncateString(&label, B_TRUNCATE_MIDDLE, 599 fOutputPathMenu->StringWidth("SomethingLongHere")); 600 601 fOutputPathMenu->AddItem(new BMenuItem(label.String(), message), index); 602 603 if (markItem) { 604 fOutputPathMenu->ItemAt(index)->SetMarked(true); 605 fLastSelectedPath = fOutputPathMenu->ItemAt(index); 606 } 607 } 608 609 610 void 611 ScreenshotWindow::_UpdatePreviewPanel() 612 { 613 float height = 150.0f; 614 615 float width = (fScreenshot->Bounds().Width() 616 / fScreenshot->Bounds().Height()) * height; 617 618 // to prevent a preview way too wide 619 if (width > 400.0f) { 620 width = 400.0f; 621 height = (fScreenshot->Bounds().Height() 622 / fScreenshot->Bounds().Width()) * width; 623 } 624 625 fPreview->SetExplicitMinSize(BSize(width, height)); 626 fPreview->SetExplicitMaxSize(BSize(width, height)); 627 628 fPreview->ClearViewBitmap(); 629 fPreview->SetViewBitmap(fScreenshot, fScreenshot->Bounds(), 630 fPreview->Bounds(), B_FOLLOW_ALL, 0); 631 632 BCardLayout* layout = dynamic_cast<BCardLayout*> (GetLayout()); 633 if (layout) 634 layout->SetVisibleItem(1L); 635 636 _UpdateFilenameSelection(); 637 638 SetTitle(B_TRANSLATE("Save screenshot")); 639 } 640 641 642 void 643 ScreenshotWindow::_UpdateFilenameSelection() 644 { 645 fNameControl->MakeFocus(true); 646 fNameControl->TextView()->Select(0, 647 fNameControl->TextView()->TextLength() 648 - fExtension.Length()); 649 fNameControl->TextView()->ScrollToSelection(); 650 } 651 652 653 BString 654 ScreenshotWindow::_FindValidFileName(const char* name) 655 { 656 BString baseName(name); 657 658 if (fExtension.Compare("")) 659 baseName.RemoveLast(fExtension); 660 661 if (!fSaveScreenshotSilent && !fLastSelectedPath) 662 return baseName; 663 664 BPath orgPath(_GetDirectory()); 665 if (orgPath == NULL) 666 return baseName; 667 668 BTranslatorRoster* roster = BTranslatorRoster::Default(); 669 670 translator_id id = 0; 671 if (_FindTranslator(fImageFileType, &id) == B_OK) { 672 673 const translation_format* formats = NULL; 674 675 int32 numFormats; 676 if (roster->GetOutputFormats(id, &formats, &numFormats) == B_OK) { 677 for (int32 i = 0; i < numFormats; ++i) { 678 if (formats[i].type == uint32(fImageFileType)) { 679 BMimeType mimeType(formats[i].MIME); 680 BMessage msgExtensions; 681 if (mimeType.GetFileExtensions(&msgExtensions) == B_OK) { 682 const char* extension; 683 if (msgExtensions.FindString("extensions", 0, 684 &extension) == B_OK) { 685 fExtension.SetTo(extension); 686 fExtension.Prepend("."); 687 } else 688 fExtension.SetTo(""); 689 } 690 break; 691 } 692 } 693 } 694 } 695 696 BPath outputPath = orgPath; 697 BString fileName; 698 fileName << baseName << fExtension; 699 outputPath.Append(fileName); 700 701 if (!BEntry(outputPath.Path()).Exists()) 702 return fileName; 703 704 if (baseName.FindFirst(B_TRANSLATE_COMMENT("screenshot", 705 "!! Basename of screenshot files. !!")) == 0) 706 baseName.SetTo(B_TRANSLATE_COMMENT("screenshot", 707 "!! Basename of screenshot files. !!")); 708 709 BEntry entry; 710 int32 index = 1; 711 char filename[B_FILE_NAME_LENGTH]; 712 do { 713 sprintf(filename, "%s%ld%s", baseName.String(), index++, 714 fExtension.String()); 715 outputPath.SetTo(orgPath.Path()); 716 outputPath.Append(filename); 717 entry.SetTo(outputPath.Path()); 718 } while (entry.Exists()); 719 720 return BString(filename); 721 } 722 723 724 int32 725 ScreenshotWindow::_PathIndexInMenu(const BString& path) const 726 { 727 BString userPath; 728 for (int32 i = 0; i < fOutputPathMenu->CountItems(); ++i) { 729 BMenuItem* item = fOutputPathMenu->ItemAt(i); 730 if (item && item->Message() 731 && item->Message()->FindString("path", &userPath) == B_OK) { 732 if (userPath == path) 733 return i; 734 } 735 } 736 return -1; 737 } 738 739 740 BMessage 741 ScreenshotWindow::_ReadSettings() const 742 { 743 BMessage settings; 744 745 BPath settingsPath; 746 if (find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath) != B_OK) 747 return settings; 748 settingsPath.Append("Screenshot_settings"); 749 750 BFile file(settingsPath.Path(), B_READ_ONLY); 751 if (file.InitCheck() == B_OK) 752 settings.Unflatten(&file); 753 754 return settings; 755 } 756 757 758 void 759 ScreenshotWindow::_WriteSettings() const 760 { 761 BMessage settings; 762 763 settings.AddInt32("type", fImageFileType); 764 settings.AddBool("includeBorder", fIncludeBorder); 765 settings.AddBool("includeMouse", fIncludeMouse); 766 settings.AddBool("grabActiveWindow", fGrabActiveWindow); 767 settings.AddInt64("delay", fDelay); 768 settings.AddString("outputFilename", fOutputFilename); 769 770 BString path; 771 int32 count = fOutputPathMenu->CountItems(); 772 if (count > 5) { 773 for (int32 i = count - 3; i > count - 8 && i > 2; --i) { 774 BMenuItem* item = fOutputPathMenu->ItemAt(i); 775 if (item) { 776 BMessage* msg = item->Message(); 777 if (msg && msg->FindString("path", &path) == B_OK) 778 settings.AddString("path", path.String()); 779 } 780 } 781 } 782 783 if (fLastSelectedPath) { 784 BMessage* msg = fLastSelectedPath->Message(); 785 if (msg && msg->FindString("path", &path) == B_OK) 786 settings.AddString("lastSelectedPath", path.String()); 787 } 788 789 BPath settingsPath; 790 if (find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath) != B_OK) 791 return; 792 settingsPath.Append("Screenshot_settings"); 793 794 BFile file(settingsPath.Path(), B_CREATE_FILE | B_ERASE_FILE 795 | B_WRITE_ONLY); 796 if (file.InitCheck() == B_OK) { 797 ssize_t size; 798 settings.Flatten(&file, &size); 799 } 800 } 801 802 803 void 804 ScreenshotWindow::_TakeScreenshot(bigtime_t delay) 805 { 806 if (delay > 0) 807 snooze(delay); 808 809 BRect frame; 810 delete fScreenshot; 811 if (_GetActiveWindowFrame(&frame) == B_OK) { 812 fScreenshot = new BBitmap(frame.OffsetToCopy(B_ORIGIN), B_RGBA32, true); 813 BScreen(this).ReadBitmap(fScreenshot, fIncludeMouse, &frame); 814 if (fIncludeBorder) 815 _MakeTabSpaceTransparent(&frame); 816 } else 817 BScreen(this).GetBitmap(&fScreenshot, fIncludeMouse); 818 } 819 820 821 status_t 822 ScreenshotWindow::_GetActiveWindowFrame(BRect* frame) 823 { 824 if (!fGrabActiveWindow || !frame) 825 return B_ERROR; 826 827 int32* tokens; 828 int32 tokenCount; 829 status_t status = BPrivate::get_window_order(current_workspace(), &tokens, 830 &tokenCount); 831 if (status != B_OK || !tokens || tokenCount < 1) 832 return B_ERROR; 833 834 status = B_ERROR; 835 for (int32 i = 0; i < tokenCount; ++i) { 836 client_window_info* windowInfo = get_window_info(tokens[i]); 837 if (!windowInfo->is_mini && !windowInfo->show_hide_level > 0) { 838 frame->left = windowInfo->window_left; 839 frame->top = windowInfo->window_top; 840 frame->right = windowInfo->window_right; 841 frame->bottom = windowInfo->window_bottom; 842 843 status = B_OK; 844 if (fIncludeBorder) { 845 float border = (windowInfo->border_size); 846 frame->InsetBy(-(border), -(border)); 847 frame->top -= windowInfo->tab_height; 848 fTabHeight = windowInfo->tab_height; 849 } 850 free(windowInfo); 851 852 BRect screenFrame(BScreen(this).Frame()); 853 if (frame->left < screenFrame.left) 854 frame->left = screenFrame.left; 855 if (frame->top < screenFrame.top) 856 frame->top = screenFrame.top; 857 if (frame->right > screenFrame.right) 858 frame->right = screenFrame.right; 859 if (frame->bottom > screenFrame.bottom) 860 frame->bottom = screenFrame.bottom; 861 862 break; 863 } 864 free(windowInfo); 865 } 866 free(tokens); 867 return status; 868 } 869 870 871 status_t 872 ScreenshotWindow::_SaveScreenshot() 873 { 874 if (!fScreenshot || (!fSaveScreenshotSilent && !fLastSelectedPath)) 875 return B_ERROR; 876 877 BPath path(_GetDirectory()); 878 879 if (path == NULL) 880 return B_ERROR; 881 882 if (fSaveScreenshotSilent) { 883 if (!fOutputFilename.Compare("")) { 884 path.Append(_FindValidFileName(B_TRANSLATE_COMMENT("screenshot1", 885 "!! Filename of first screenshot !!")).String()); 886 } else 887 path.SetTo(fOutputFilename); 888 } else 889 path.Append(fNameControl->Text()); 890 891 BEntry entry; 892 entry.SetTo(path.Path()); 893 894 if (!fSaveScreenshotSilent) { 895 if (entry.Exists()) { 896 BAlert* overwriteAlert = new BAlert( 897 B_TRANSLATE("overwrite"), 898 B_TRANSLATE("This file already exists.\n Are you sure would " 899 "you like to overwrite it?"), 900 B_TRANSLATE("Cancel"), 901 B_TRANSLATE("Overwrite"), 902 NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 903 904 overwriteAlert->SetShortcut(0, B_ESCAPE); 905 906 if (overwriteAlert->Go() == 0) 907 return B_CANCELED; 908 } 909 } 910 911 BFile file(&entry, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 912 if (file.InitCheck() != B_OK) 913 return B_ERROR; 914 915 BBitmapStream bitmapStream(fScreenshot); 916 BTranslatorRoster* roster = BTranslatorRoster::Default(); 917 roster->Translate(&bitmapStream, NULL, NULL, &file, fImageFileType, 918 B_TRANSLATOR_BITMAP); 919 fScreenshot = NULL; 920 921 BNodeInfo nodeInfo(&file); 922 if (nodeInfo.InitCheck() != B_OK) 923 return B_ERROR; 924 925 translator_id id = 0; 926 if (_FindTranslator(fImageFileType, &id) != B_OK) 927 return B_ERROR; 928 929 int32 numFormats; 930 const translation_format* formats = NULL; 931 932 if (roster->GetOutputFormats(id, &formats, &numFormats) != B_OK) 933 return B_ERROR; 934 935 for (int32 i = 0; i < numFormats; ++i) { 936 if (formats[i].type == uint32(fImageFileType)) { 937 nodeInfo.SetType(formats[i].MIME); 938 break; 939 } 940 } 941 942 return B_OK; 943 } 944 945 946 void 947 ScreenshotWindow::_MakeTabSpaceTransparent(BRect* frame) 948 { 949 if (!frame) 950 return; 951 952 if (fScreenshot->ColorSpace() != B_RGBA32) 953 return; 954 955 BRect fullFrame = *frame; 956 957 BMessage message; 958 BMessage reply; 959 960 app_info appInfo; 961 if (be_roster->GetActiveAppInfo(&appInfo) != B_OK) 962 return; 963 964 BMessenger messenger(appInfo.signature, appInfo.team); 965 if (!messenger.IsValid()) 966 return; 967 968 bool foundActiveWindow = false; 969 int32 index = 0; 970 971 while (true) { 972 message.MakeEmpty(); 973 message.what = B_GET_PROPERTY; 974 message.AddSpecifier("Active"); 975 message.AddSpecifier("Window", index); 976 977 reply.MakeEmpty(); 978 messenger.SendMessage(&message, &reply); 979 980 if (reply.what == B_MESSAGE_NOT_UNDERSTOOD) 981 break; 982 983 bool result; 984 if (reply.FindBool("result", &result) == B_OK) { 985 foundActiveWindow = result; 986 987 if (foundActiveWindow) 988 break; 989 } 990 index++; 991 } 992 993 if (!foundActiveWindow) 994 return; 995 996 message.MakeEmpty(); 997 message.what = B_GET_PROPERTY; 998 message.AddSpecifier("TabFrame"); 999 message.AddSpecifier("Window", index); 1000 reply.MakeEmpty(); 1001 messenger.SendMessage(&message, &reply); 1002 1003 BRect tabFrame; 1004 if (reply.FindRect("result", &tabFrame) != B_OK) 1005 return; 1006 1007 if (!fullFrame.Contains(tabFrame)) 1008 return; 1009 1010 BRegion tabSpace(fullFrame); 1011 fullFrame.OffsetBy(0, fTabHeight); 1012 tabSpace.Exclude(fullFrame); 1013 tabSpace.Exclude(tabFrame); 1014 fullFrame.OffsetBy(0, -fTabHeight); 1015 tabSpace.OffsetBy(-fullFrame.left, -fullFrame.top); 1016 BScreen screen; 1017 BRect screenFrame = screen.Frame(); 1018 tabSpace.OffsetBy(-screenFrame.left, -screenFrame.top); 1019 1020 BView view(fScreenshot->Bounds(), "bitmap", B_FOLLOW_ALL_SIDES, 0); 1021 fScreenshot->AddChild(&view); 1022 if (view.Looper() && view.Looper()->Lock()) { 1023 view.SetDrawingMode(B_OP_COPY); 1024 view.SetHighColor(B_TRANSPARENT_32_BIT); 1025 1026 for (int i = 0; i < tabSpace.CountRects(); i++) 1027 view.FillRect(tabSpace.RectAt(i)); 1028 1029 view.Sync(); 1030 view.Looper()->Unlock(); 1031 } 1032 fScreenshot->RemoveChild(&view); 1033 } 1034 1035 1036 status_t 1037 ScreenshotWindow::_FindTranslator(uint32 imageType, translator_id* id) 1038 { 1039 translator_id* translators = NULL; 1040 int32 numTranslators = 0; 1041 BTranslatorRoster* roster = BTranslatorRoster::Default(); 1042 status_t status = roster->GetAllTranslators(&translators, &numTranslators); 1043 if (status != B_OK) 1044 return status; 1045 1046 status = B_ERROR; 1047 for (int32 x = 0; x < numTranslators && status != B_OK; x++) { 1048 int32 numFormats; 1049 const translation_format* formats = NULL; 1050 1051 if (roster->GetOutputFormats(x, &formats, &numFormats) == B_OK) { 1052 for (int32 i = 0; i < numFormats; ++i) { 1053 if (formats[i].type == imageType) { 1054 *id = x; 1055 status = B_OK; 1056 break; 1057 } 1058 } 1059 } 1060 } 1061 1062 delete [] translators; 1063 1064 return status; 1065 } 1066