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