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