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 BSize size = GetLayout()->PreferredSize(); 496 ResizeTo(size.Width(), size.Height()); 497 498 BRect frame(BScreen(this).Frame()); 499 MoveTo((frame.Width() - size.Width()) / 2.0, 500 (frame.Height() - size.Height()) / 2.0); 501 502 Show(); 503 } 504 505 506 void 507 ScreenshotWindow::_UpdatePreviewPanel() 508 { 509 float height = 150.0f; 510 511 float width = (fScreenshot->Bounds().Width() / 512 fScreenshot->Bounds().Height()) * height; 513 514 // to prevent a preview way too wide 515 if (width > 400.0f) { 516 width = 400.0f; 517 height = (fScreenshot->Bounds().Height() / 518 fScreenshot->Bounds().Width()) * width; 519 } 520 521 fPreview->SetExplicitMinSize(BSize(width, height)); 522 fPreview->SetExplicitMaxSize(BSize(width, height)); 523 524 fPreview->ClearViewBitmap(); 525 fPreview->SetViewBitmap(fScreenshot, fScreenshot->Bounds(), 526 fPreview->Bounds(), B_FOLLOW_ALL, 0); 527 528 BCardLayout* layout = dynamic_cast<BCardLayout*> (GetLayout()); 529 if (layout) 530 layout->SetVisibleItem(1L); 531 532 SetTitle("Save Screenshot"); 533 } 534 535 536 BString 537 ScreenshotWindow::_FindValidFileName(const char* name) 538 { 539 BString baseName(name); 540 541 if (fExtension.Compare("")) { 542 baseName.RemoveLast(fExtension); 543 } 544 545 if (!fLastSelectedPath) 546 return baseName; 547 548 const char* path; 549 BMessage* message = fLastSelectedPath->Message(); 550 if (!message || message->FindString("path", &path) != B_OK) 551 return baseName; 552 553 BTranslatorRoster* roster = BTranslatorRoster::Default(); 554 const translation_format* formats = NULL; 555 int32 numFormats; 556 557 if (roster->GetOutputFormats(fTranslator, &formats, &numFormats) == B_OK) { 558 for (int32 i = 0; i < numFormats; ++i) { 559 if (formats[i].type == uint32(fImageFileType)) { 560 BMimeType mimeType(formats[i].MIME); 561 BMessage msgExtensions; 562 if (mimeType.GetFileExtensions(&msgExtensions) == B_OK) { 563 const char* extension; 564 if (msgExtensions.FindString("extensions", 0, &extension) == B_OK) { 565 fExtension.SetTo(extension); 566 fExtension.Prepend("."); 567 } 568 else 569 fExtension.SetTo(""); 570 } 571 break; 572 } 573 } 574 } 575 576 BPath outputPath(path); 577 BString fileName; 578 fileName << baseName << fExtension; 579 outputPath.Append(fileName); 580 581 if (!BEntry(outputPath.Path()).Exists()) 582 return fileName; 583 584 if (baseName.FindFirst("screenshot") == 0) 585 baseName.SetTo("screenshot"); 586 587 BEntry entry; 588 int32 index = 1; 589 char filename[B_FILE_NAME_LENGTH]; 590 do { 591 sprintf(filename, "%s%ld%s", baseName.String(), index++, 592 fExtension.String()); 593 outputPath.SetTo(path); 594 outputPath.Append(filename); 595 entry.SetTo(outputPath.Path()); 596 } while (entry.Exists()); 597 598 return BString(filename); 599 } 600 601 602 int32 603 ScreenshotWindow::_PathIndexInMenu(const BString& path) const 604 { 605 BString userPath; 606 for (int32 i = 0; i < fOutputPathMenu->CountItems(); ++i) { 607 BMenuItem* item = fOutputPathMenu->ItemAt(i); 608 if (item && item->Message() 609 && item->Message()->FindString("path", &userPath) == B_OK) { 610 if (userPath == path) 611 return i; 612 } 613 } 614 return -1; 615 } 616 617 618 BMessage 619 ScreenshotWindow::_ReadSettings() const 620 { 621 BPath settingsPath; 622 find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath); 623 settingsPath.Append("screenshot"); 624 625 BMessage settings; 626 627 BFile file(settingsPath.Path(), B_READ_ONLY); 628 if (file.InitCheck() == B_OK) 629 settings.Unflatten(&file); 630 631 return settings; 632 } 633 634 635 void 636 ScreenshotWindow::_WriteSettings() const 637 { 638 BMessage settings; 639 settings.AddInt32("be:type", fImageFileType); 640 641 BString path; 642 int32 count = fOutputPathMenu->CountItems(); 643 if (count > 5) { 644 for (int32 i = count - 3; i > count - 8 && i > 2; --i) { 645 BMenuItem* item = fOutputPathMenu->ItemAt(i); 646 if (item) { 647 BMessage* msg = item->Message(); 648 if (msg && msg->FindString("path", &path) == B_OK) 649 settings.AddString("path", path.String()); 650 } 651 } 652 } 653 654 if (fLastSelectedPath) { 655 BMessage* msg = fLastSelectedPath->Message(); 656 if (msg && msg->FindString("path", &path) == B_OK) 657 settings.AddString("lastSelectedPath", path.String()); 658 } 659 660 BPath settingsPath; 661 find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath); 662 settingsPath.Append("screenshot"); 663 664 BFile file(settingsPath.Path(), B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 665 if (file.InitCheck() == B_OK) { 666 ssize_t size; 667 settings.Flatten(&file, &size); 668 } 669 } 670 671 672 void 673 ScreenshotWindow::_TakeScreenshot() 674 { 675 if (fDelayControl) 676 snooze((atoi(fDelayControl->Text()) * 1000000) + 50000); 677 else if (fDelay > 0) 678 snooze(fDelay); 679 680 BRect frame; 681 delete fScreenshot; 682 if (_GetActiveWindowFrame(&frame) == B_OK) { 683 fScreenshot = new BBitmap(frame.OffsetToCopy(B_ORIGIN), B_RGBA32, true); 684 BScreen(this).ReadBitmap(fScreenshot, fIncludeMouse, &frame); 685 if (fIncludeBorder) 686 _MakeTabSpaceTransparent(&frame); 687 } else { 688 BScreen(this).GetBitmap(&fScreenshot, fIncludeMouse); 689 } 690 } 691 692 693 status_t 694 ScreenshotWindow::_GetActiveWindowFrame(BRect* frame) 695 { 696 if (!fGrabActiveWindow || !frame) 697 return B_ERROR; 698 699 int32* tokens; 700 int32 tokenCount; 701 status_t status = BPrivate::get_window_order(current_workspace(), &tokens, 702 &tokenCount); 703 if (status != B_OK || !tokens || tokenCount < 1) 704 return B_ERROR; 705 706 status = B_ERROR; 707 for (int32 i = 0; i < tokenCount; ++i) { 708 client_window_info* windowInfo = get_window_info(tokens[i]); 709 if (!windowInfo->is_mini && !windowInfo->show_hide_level > 0) { 710 frame->left = windowInfo->window_left; 711 frame->top = windowInfo->window_top; 712 frame->right = windowInfo->window_right; 713 frame->bottom = windowInfo->window_bottom; 714 715 status = B_OK; 716 if (fIncludeBorder) { 717 float border = (windowInfo->border_size); 718 frame->InsetBy(-(border), -(border)); 719 frame->top -= windowInfo->tab_height; 720 fTabHeight = windowInfo->tab_height; 721 } 722 free(windowInfo); 723 724 BRect screenFrame(BScreen(this).Frame()); 725 if (frame->left < screenFrame.left) 726 frame->left = screenFrame.left; 727 if (frame->top < screenFrame.top) 728 frame->top = screenFrame.top; 729 if (frame->right > screenFrame.right) 730 frame->right = screenFrame.right; 731 if (frame->bottom > screenFrame.bottom) 732 frame->bottom = screenFrame.bottom; 733 734 break; 735 } 736 free(windowInfo); 737 } 738 free(tokens); 739 return status; 740 } 741 742 743 status_t 744 ScreenshotWindow::_SaveScreenshot() 745 { 746 if (!fScreenshot || !fLastSelectedPath) 747 return B_ERROR; 748 749 const char* _path; 750 BMessage* message = fLastSelectedPath->Message(); 751 if (!message || message->FindString("path", &_path) != B_OK) 752 return B_ERROR; 753 754 BEntry entry; 755 BPath path; 756 757 path = _path; 758 path.Append(fNameControl->Text()); 759 entry.SetTo(path.Path()); 760 761 if (entry.Exists()) { 762 BAlert *overwriteAlert = new BAlert("overwrite", "This file already exists.\n" 763 "Are you sure would you like to overwrite it?", 764 "Cancel", "Overwrite", NULL, 765 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 766 767 overwriteAlert->SetShortcut(0, B_ESCAPE); 768 int32 buttonIndex = overwriteAlert->Go(); 769 if (buttonIndex == 0) { 770 return B_CANCELED; 771 } 772 } 773 774 BFile file(&entry, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 775 if (file.InitCheck() != B_OK) { 776 return B_ERROR; 777 } 778 BBitmapStream bitmapStream(fScreenshot); 779 BTranslatorRoster* roster = BTranslatorRoster::Default(); 780 roster->Translate(&bitmapStream, NULL, NULL, &file, fImageFileType, 781 B_TRANSLATOR_BITMAP); 782 fScreenshot = NULL; 783 784 BNodeInfo nodeInfo(&file); 785 if (nodeInfo.InitCheck() != B_OK) 786 return B_ERROR; 787 788 int32 numFormats; 789 const translation_format* formats = NULL; 790 if (roster->GetOutputFormats(fTranslator, &formats, &numFormats) != B_OK) 791 return B_OK; 792 793 for (int32 i = 0; i < numFormats; ++i) { 794 if (formats[i].type == uint32(fImageFileType)) { 795 nodeInfo.SetType(formats[i].MIME); 796 break; 797 } 798 } 799 return B_OK; 800 } 801 802 803 void 804 ScreenshotWindow::_SaveScreenshotSilent() const 805 { 806 if (!fScreenshot) 807 return; 808 809 BPath homePath; 810 if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK) { 811 fprintf(stderr, "failed to find user home folder\n"); 812 return; 813 } 814 815 BPath path; 816 BEntry entry; 817 int32 index = 1; 818 do { 819 char filename[32]; 820 sprintf(filename, "screenshot%ld.png", index++); 821 path = homePath; 822 path.Append(filename); 823 entry.SetTo(path.Path()); 824 } while (entry.Exists()); 825 826 // Dump to PNG 827 SaveToPNG(path.Path(), fScreenshot->Bounds(), fScreenshot->ColorSpace(), 828 fScreenshot->Bits(), fScreenshot->BitsLength(), 829 fScreenshot->BytesPerRow()); 830 } 831 832 833 void 834 ScreenshotWindow::_MakeTabSpaceTransparent(BRect* frame) 835 { 836 if (!frame) 837 return; 838 839 if (fScreenshot->ColorSpace() != B_RGBA32) 840 return; 841 842 BRect fullFrame = *frame; 843 844 BMessage message; 845 BMessage reply; 846 847 app_info appInfo; 848 if (be_roster->GetActiveAppInfo(&appInfo) != B_OK) 849 return; 850 851 BMessenger messenger(appInfo.signature, appInfo.team); 852 if (!messenger.IsValid()) 853 return; 854 855 bool foundActiveWindow = false; 856 int32 index = 0; 857 858 while (true) { 859 message.MakeEmpty(); 860 message.what = B_GET_PROPERTY; 861 message.AddSpecifier("Active"); 862 message.AddSpecifier("Window", index); 863 864 reply.MakeEmpty(); 865 messenger.SendMessage(&message, &reply); 866 867 if (reply.what == B_MESSAGE_NOT_UNDERSTOOD) 868 break; 869 870 bool result; 871 if (reply.FindBool("result", &result) == B_OK) { 872 foundActiveWindow = result; 873 874 if (foundActiveWindow) 875 break; 876 } 877 index++; 878 } 879 880 if (!foundActiveWindow) 881 return; 882 883 message.MakeEmpty(); 884 message.what = B_GET_PROPERTY; 885 message.AddSpecifier("TabFrame"); 886 message.AddSpecifier("Window", index); 887 reply.MakeEmpty(); 888 messenger.SendMessage(&message, &reply); 889 890 BRect tabFrame; 891 if (reply.FindRect("result", &tabFrame) != B_OK) 892 return; 893 894 if (!fullFrame.Contains(tabFrame)) 895 return; 896 897 BRegion tabSpace(fullFrame); 898 fullFrame.OffsetBy(0, fTabHeight); 899 tabSpace.Exclude(fullFrame); 900 tabSpace.Exclude(tabFrame); 901 fullFrame.OffsetBy(0, -fTabHeight); 902 tabSpace.OffsetBy(-fullFrame.left, -fullFrame.top); 903 BScreen screen; 904 BRect screenFrame = screen.Frame(); 905 tabSpace.OffsetBy(-screenFrame.left, -screenFrame.top); 906 907 BView view(fScreenshot->Bounds(), "bitmap", B_FOLLOW_ALL_SIDES, 0); 908 fScreenshot->AddChild(&view); 909 if(view.Looper() && view.Looper()->Lock()) { 910 view.SetDrawingMode(B_OP_COPY); 911 view.SetHighColor(B_TRANSPARENT_32_BIT); 912 913 for (int i = 0; i < tabSpace.CountRects(); i++) 914 view.FillRect(tabSpace.RectAt(i)); 915 916 view.Sync(); 917 view.Looper()->Unlock(); 918 } 919 fScreenshot->RemoveChild(&view); 920 } 921 922