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