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