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