xref: /haiku/src/apps/screenshot/ScreenshotWindow.cpp (revision adb0d19d561947362090081e81d90dde59142026)
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 
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 <Screen.h>
34 #include <String.h>
35 #include <StringView.h>
36 #include <SpaceLayoutItem.h>
37 #include <TextControl.h>
38 #include <TranslatorFormats.h>
39 #include <TranslationUtils.h>
40 #include <TranslatorRoster.h>
41 #include <View.h>
42 #include <WindowInfo.h>
43 
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 
48 
49 enum {
50 	kScreenshotType,
51 	kIncludeBorder,
52 	kShowCursor,
53 	kBackToSave,
54 	kTakeScreenshot,
55 	kImageOutputFormat,
56 	kLocationChanged,
57 	kChooseLocation,
58 	kFinishScreenshot,
59 	kShowOptions
60 };
61 
62 
63 // #pragma mark - DirectoryRefFilter
64 
65 
66 class DirectoryRefFilter : public BRefFilter {
67 public:
68 	virtual			~DirectoryRefFilter() {}
69 			bool	Filter(const entry_ref* ref, BNode* node, struct stat* stat,
70 						const char* filetype)
71 					{
72 						return node->IsDirectory();
73 					}
74 };
75 
76 
77 // #pragma mark - ScreenshotWindow
78 
79 
80 ScreenshotWindow::ScreenshotWindow(bigtime_t delay, bool includeBorder,
81 	bool includeCursor, bool grabActiveWindow, bool showConfigWindow,
82 	bool saveScreenshotSilent)
83 	: BWindow(BRect(0, 0, 200.0, 100.0), "Take Screenshot", B_TITLED_WINDOW,
84 		B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_QUIT_ON_WINDOW_CLOSE |
85 		B_AVOID_FRONT | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE),
86 	fDelayControl(NULL),
87 	fScreenshot(NULL),
88 	fOutputPathPanel(NULL),
89 	fLastSelectedPath(NULL),
90 	fDelay(delay),
91 	fIncludeBorder(includeBorder),
92 	fIncludeCursor(includeCursor),
93 	fGrabActiveWindow(grabActiveWindow),
94 	fShowConfigWindow(showConfigWindow)
95 {
96 	if (saveScreenshotSilent) {
97 		_TakeScreenshot();
98 		_SaveScreenshotSilent();
99 		be_app_messenger.SendMessage(B_QUIT_REQUESTED);
100 	} else {
101 		_InitWindow();
102 		_CenterAndShow();
103 	}
104 }
105 
106 
107 ScreenshotWindow::~ScreenshotWindow()
108 {
109 	if (fOutputPathPanel)
110 		delete fOutputPathPanel->RefFilter();
111 
112 	delete fScreenshot;
113 	delete fOutputPathPanel;
114 }
115 
116 
117 void
118 ScreenshotWindow::MessageReceived(BMessage* message)
119 {
120 	switch (message->what) {
121 		case kScreenshotType: {
122 			fGrabActiveWindow = false;
123 			if (fActiveWindow->Value() == B_CONTROL_ON)
124 				fGrabActiveWindow = true;
125 			fWindowBorder->SetEnabled(fGrabActiveWindow);
126 		}	break;
127 
128 		case kIncludeBorder: {
129 				fIncludeBorder = (fWindowBorder->Value() == B_CONTROL_ON);
130 		}	break;
131 
132 		case kShowCursor: {
133 			fIncludeCursor = (fShowCursor->Value() == B_CONTROL_ON);
134 		}	break;
135 
136 		case kBackToSave: {
137 			BCardLayout* layout = dynamic_cast<BCardLayout*> (GetLayout());
138 			if (layout)
139 				layout->SetVisibleItem(1L);
140 			SetTitle("Save Screenshot");
141 		}	break;
142 
143 		case kTakeScreenshot: {
144 			Hide();
145 			_TakeScreenshot();
146 			_UpdatePreviewPanel();
147 			Show();
148 		}	break;
149 
150 		case kImageOutputFormat: {
151 			message->FindInt32("be:type", &fImageFileType);
152 			message->FindInt32("be:translator", &fTranslator);
153 		}	break;
154 
155 		case kLocationChanged: {
156 			void* source = NULL;
157 			if (message->FindPointer("source", &source) == B_OK)
158 				fLastSelectedPath = static_cast<BMenuItem*> (source);
159 
160 			const char* text = fNameControl->Text();
161 			fNameControl->SetText(_FindValidFileName(text).String());
162 		}	break;
163 
164 		case kChooseLocation: {
165 			if (!fOutputPathPanel) {
166 				BMessenger target(this);
167 				fOutputPathPanel = new BFilePanel(B_OPEN_PANEL, &target,
168 					NULL, B_DIRECTORY_NODE, false, NULL, new DirectoryRefFilter());
169 				fOutputPathPanel->Window()->SetTitle("Choose directory");
170 				fOutputPathPanel->SetButtonLabel(B_DEFAULT_BUTTON, "Select");
171 			}
172 			fOutputPathPanel->Show();
173 		}	break;
174 
175 		case B_CANCEL: {
176 			fLastSelectedPath->SetMarked(true);
177 		}	break;
178 
179 		case B_REFS_RECEIVED: {
180 			entry_ref ref;
181 			if (message->FindRef("refs", &ref) == B_OK) {
182 				BString path(BPath(&ref).Path());
183 				int32 index = _PathIndexInMenu(path);
184 				if (index < 0) {
185 					_AddItemToPathMenu(path.String(),
186 						path, fOutputPathMenu->CountItems() - 2, true);
187 				} else {
188 					fOutputPathMenu->ItemAt(index)->SetMarked(true);
189 				}
190 			}
191 		}	break;
192 
193 		case kFinishScreenshot:
194 			_WriteSettings();
195 			_SaveScreenshot();
196 
197 		// fall through
198 		case B_QUIT_REQUESTED:
199 			be_app_messenger.SendMessage(B_QUIT_REQUESTED);
200 		break;
201 
202 		case kShowOptions: {
203 			BCardLayout* layout = dynamic_cast<BCardLayout*> (GetLayout());
204 			if (layout)
205 				layout->SetVisibleItem(0L);
206 			SetTitle("Take Screenshot");
207 			fBackToSave->SetEnabled(true);
208 		}	break;
209 
210 		default: {
211 			BWindow::MessageReceived(message);
212 		}	break;
213 	};
214 
215 }
216 
217 
218 void
219 ScreenshotWindow::_InitWindow()
220 {
221 	BCardLayout* layout = new BCardLayout();
222 	SetLayout(layout);
223 
224 	_SetupFirstLayoutItem(layout);
225 	_SetupSecondLayoutItem(layout);
226 
227 	if (!fShowConfigWindow) {
228 		_TakeScreenshot();
229 		_UpdatePreviewPanel();
230 		layout->SetVisibleItem(1L);
231 	} else {
232 		layout->SetVisibleItem(0L);
233 	}
234 }
235 
236 
237 void
238 ScreenshotWindow::_SetupFirstLayoutItem(BCardLayout* layout)
239 {
240 	BStringView* stringView = new BStringView("", "Options");
241 	stringView->SetFont(be_bold_font);
242 	stringView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
243 
244 	fActiveWindow = new BRadioButton("Take active window",
245 		 new BMessage(kScreenshotType));
246 	fWholeDesktop = new BRadioButton("Take whole Desktop",
247 		 new BMessage(kScreenshotType));
248 	fWholeDesktop->SetValue(B_CONTROL_ON);
249 
250 	BString delay;
251 	delay << fDelay / 1000000;
252 	fDelayControl = new BTextControl("", "Take screenshot after a delay of",
253 		delay.String(), NULL);
254 	_DisallowChar(fDelayControl->TextView());
255 	fDelayControl->TextView()->SetAlignment(B_ALIGN_RIGHT);
256 
257 	BStringView* stringView2 = new BStringView("", "seconds");
258 	stringView2->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
259 
260 	fWindowBorder = new BCheckBox("Include window border",
261 		new BMessage(kIncludeBorder));
262 	fWindowBorder->SetEnabled(false);
263 
264 	fShowCursor = new BCheckBox("Include cursor in screenshot",
265 		new BMessage(kShowCursor));
266 	fShowCursor->SetValue(fIncludeCursor);
267 
268 	BBox* divider = new BBox(B_FANCY_BORDER, NULL);
269 	divider->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
270 
271 	fBackToSave = new BButton("", "Back to save", new BMessage(kBackToSave));
272 	fBackToSave->SetEnabled(false);
273 
274 	fTakeScreenshot = new BButton("", "Take Screenshot",
275 		new BMessage(kTakeScreenshot));
276 
277 	layout->AddView(0, BGroupLayoutBuilder(B_VERTICAL)
278 		.Add(stringView)
279 		.Add(BGridLayoutBuilder()
280 			.Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 0)
281 			.Add(fWholeDesktop, 1, 0)
282 			.Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 1)
283 			.Add(fActiveWindow, 1, 1)
284 			.SetInsets(0.0, 5.0, 0.0, 0.0))
285 		.AddGroup(B_HORIZONTAL)
286 			.AddStrut(30.0)
287 			.Add(fWindowBorder)
288 			.End()
289 		.AddStrut(10.0)
290 		.AddGroup(B_HORIZONTAL)
291 			.AddStrut(15.0)
292 			.Add(fShowCursor)
293 			.End()
294 		.AddStrut(5.0)
295 		.AddGroup(B_HORIZONTAL, 5.0)
296 			.AddStrut(10.0)
297 			.Add(fDelayControl->CreateLabelLayoutItem())
298 			.Add(fDelayControl->CreateTextViewLayoutItem())
299 			.Add(stringView2)
300 			.End()
301 		.AddStrut(10.0)
302 		.AddGlue()
303 		.Add(divider)
304 		.AddStrut(10)
305 		.AddGroup(B_HORIZONTAL, 10.0)
306 			.Add(fBackToSave)
307 			.AddGlue()
308 			.Add(new BButton("", "Cancel", new BMessage(B_QUIT_REQUESTED)))
309 			.Add(fTakeScreenshot)
310 			.End()
311 		.SetInsets(10.0, 10.0, 10.0, 10.0)
312 	);
313 
314 	if (fGrabActiveWindow) {
315 		fWindowBorder->SetEnabled(true);
316 		fActiveWindow->SetValue(B_CONTROL_ON);
317 		fWindowBorder->SetValue(fIncludeBorder);
318 	}
319 }
320 
321 
322 void
323 ScreenshotWindow::_SetupSecondLayoutItem(BCardLayout* layout)
324 {
325 	fPreviewBox = new BBox(BRect(0.0, 0.0, 200.0, 150.0));
326 	fPreviewBox->SetExplicitMinSize(BSize(200.0, B_SIZE_UNSET));
327 	fPreviewBox->SetFlags(fPreviewBox->Flags() | B_FULL_UPDATE_ON_RESIZE);
328 
329 	fNameControl = new BTextControl("", "Name:", "screenshot1", NULL);
330 
331 	BMessage settings(_ReadSettings());
332 
333 	_SetupTranslatorMenu(new BMenu("Please select"), settings);
334 	BMenuField* menuField = new BMenuField("Save as:", fTranslatorMenu);
335 
336 	_SetupOutputPathMenu(new BMenu("Please select"), settings);
337 	BMenuField* menuField2 = new BMenuField("Save in:", fOutputPathMenu);
338 
339 	fNameControl->SetText(_FindValidFileName("screenshot1").String());
340 
341 	BBox* divider = new BBox(B_FANCY_BORDER, NULL);
342 	divider->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
343 
344 	BGridLayout* gridLayout = BGridLayoutBuilder(0.0, 5.0)
345 		.Add(fNameControl->CreateLabelLayoutItem(), 0, 0)
346 		.Add(fNameControl->CreateTextViewLayoutItem(), 1, 0)
347 		.Add(menuField->CreateLabelLayoutItem(), 0, 1)
348 		.Add(menuField->CreateMenuBarLayoutItem(), 1, 1)
349 		.Add(menuField2->CreateLabelLayoutItem(), 0, 2)
350 		.Add(menuField2->CreateMenuBarLayoutItem(), 1, 2);
351 	gridLayout->SetMinColumnWidth(1, menuField->StringWidth("SomethingLongHere"));
352 
353 	layout->AddView(1, BGroupLayoutBuilder(B_VERTICAL)
354 		.Add(BGroupLayoutBuilder(B_HORIZONTAL, 10.0)
355 			.Add(fPreviewBox)
356 			.AddGroup(B_VERTICAL)
357 				.Add(gridLayout->View())
358 				.AddGlue()
359 				.End())
360 		.AddStrut(10)
361 		.Add(divider)
362 		.AddStrut(10)
363 		.AddGroup(B_HORIZONTAL, 10.0)
364 			.Add(new BButton("", "Options", new BMessage(kShowOptions)))
365 			.AddGlue()
366 			.Add(new BButton("", "Cancel", new BMessage(B_QUIT_REQUESTED)))
367 			.Add(new BButton("", "Save", new BMessage(kFinishScreenshot)))
368 			.End()
369 		.SetInsets(10.0, 10.0, 10.0, 10.0)
370 	);
371 }
372 
373 
374 void
375 ScreenshotWindow::_DisallowChar(BTextView* textView)
376 {
377 	for (uint32 i = 0; i < '0'; ++i)
378 		textView->DisallowChar(i);
379 
380 	for (uint32 i = '9' + 1; i < 255; ++i)
381 		textView->DisallowChar(i);
382 }
383 
384 
385 void
386 ScreenshotWindow::_SetupTranslatorMenu(BMenu* translatorMenu,
387 	const BMessage& settings)
388 {
389 	fTranslatorMenu = translatorMenu;
390 
391 	BMessage message(kImageOutputFormat);
392 	fTranslatorMenu = new BMenu("Please select");
393 	BTranslationUtils::AddTranslationItems(fTranslatorMenu, B_TRANSLATOR_BITMAP,
394 		&message, NULL, NULL, NULL);
395 
396 	fTranslatorMenu->SetLabelFromMarked(true);
397 
398 	if (fTranslatorMenu->ItemAt(0))
399 		fTranslatorMenu->ItemAt(0)->SetMarked(true);
400 
401 	if (settings.FindInt32("be:type", &fImageFileType) != B_OK)
402 		fImageFileType = B_PNG_FORMAT;
403 
404 	int32 imageFileType;
405 	for (int32 i = 0; i < fTranslatorMenu->CountItems(); ++i) {
406 		BMenuItem* item = fTranslatorMenu->ItemAt(i);
407 		if (item && item->Message()) {
408 			item->Message()->FindInt32("be:type", &imageFileType);
409 			if (fImageFileType == imageFileType) {
410 				item->SetMarked(true);
411 				break;
412 			}
413 		}
414 	}
415 }
416 
417 
418 void
419 ScreenshotWindow::_SetupOutputPathMenu(BMenu* outputPathMenu,
420 	const BMessage& settings)
421 {
422 	fOutputPathMenu = outputPathMenu;
423 	fOutputPathMenu->SetLabelFromMarked(true);
424 
425 	BString lastSelectedPath;
426 	settings.FindString("lastSelectedPath", &lastSelectedPath);
427 
428 	BPath path;
429 	find_directory(B_USER_DIRECTORY, &path);
430 
431 	BString label("Home directory");
432 	_AddItemToPathMenu(path.Path(), label, 0, (path.Path() == lastSelectedPath));
433 
434 	path.Append("Desktop");
435 	label.SetTo("Desktop");
436 	_AddItemToPathMenu(path.Path(), label, 0, (path.Path() == lastSelectedPath));
437 
438 	find_directory(B_BEOS_ETC_DIRECTORY, &path);
439 	path.Append("artwork");
440 
441 	label.SetTo("Artwork directory");
442 	_AddItemToPathMenu(path.Path(), label, 2, (path.Path() == lastSelectedPath));
443 
444 	int32 i = 0;
445 	BString userPath;
446 	while (settings.FindString("path", i++, &userPath) == B_OK) {
447 		_AddItemToPathMenu(userPath.String(), userPath, 3,
448 			(userPath == lastSelectedPath));
449 	}
450 
451 	if (!fLastSelectedPath) {
452 		if (settings.IsEmpty() || lastSelectedPath.Length() == 0) {
453 			fOutputPathMenu->ItemAt(1)->SetMarked(true);
454 			fLastSelectedPath = fOutputPathMenu->ItemAt(1);
455 		} else {
456 			_AddItemToPathMenu(lastSelectedPath.String(), lastSelectedPath, 3,
457 				true);
458 		}
459 	}
460 
461 	fOutputPathMenu->AddItem(new BSeparatorItem());
462 	fOutputPathMenu->AddItem(new BMenuItem("Choose directory...",
463 		new BMessage(kChooseLocation)));
464 }
465 
466 
467 void
468 ScreenshotWindow::_AddItemToPathMenu(const char* path, BString& label,
469 	int32 index, bool markItem)
470 {
471 	BMessage* message = new BMessage(kLocationChanged);
472 	message->AddString("path", path);
473 
474 	fOutputPathMenu->TruncateString(&label, B_TRUNCATE_MIDDLE,
475 		fOutputPathMenu->StringWidth("SomethingLongHere"));
476 
477 	fOutputPathMenu->AddItem(new BMenuItem(label.String(), message), index);
478 
479 	if (markItem) {
480 		fOutputPathMenu->ItemAt(index)->SetMarked(true);
481 		fLastSelectedPath = fOutputPathMenu->ItemAt(index);
482 	}
483 }
484 
485 
486 void
487 ScreenshotWindow::_CenterAndShow()
488 {
489 	BSize size = GetLayout()->PreferredSize();
490 	ResizeTo(size.Width(), size.Height());
491 
492 	BRect frame(BScreen(this).Frame());
493 	MoveTo((frame.Width() - size.Width()) / 2.0,
494 		(frame.Height() - size.Height()) / 2.0);
495 
496 	Show();
497 }
498 
499 
500 void
501 ScreenshotWindow::_UpdatePreviewPanel()
502 {
503 	fPreviewBox->ClearViewBitmap();
504 	fPreviewBox->SetViewBitmap(fScreenshot, fScreenshot->Bounds(),
505 		fPreviewBox->Bounds(), B_FOLLOW_ALL, 0);
506 
507 	BCardLayout* layout = dynamic_cast<BCardLayout*> (GetLayout());
508 	if (layout)
509 		layout->SetVisibleItem(1L);
510 
511 	SetTitle("Save Screenshot");
512 }
513 
514 
515 BString
516 ScreenshotWindow::_FindValidFileName(const char* name) const
517 {
518 	BString fileName(name);
519 	if (!fLastSelectedPath)
520 		return fileName;
521 
522 	const char* path;
523 	BMessage* message = fLastSelectedPath->Message();
524 	if (!message || message->FindString("path", &path) != B_OK)
525 		return fileName;
526 
527 	BPath outputPath(path);
528 	outputPath.Append(name);
529 	if (!BEntry(outputPath.Path()).Exists())
530 		return fileName;
531 
532 	if (strncmp(name, "screenshot", strlen("screenshot")) == 0)
533 		fileName.SetTo("screenshot");
534 
535 	BEntry entry;
536 	int32 index = 1;
537 	char filename[32];
538 	do {
539 		sprintf(filename, "%s%ld", fileName.String(), index++);
540 		outputPath.SetTo(path);
541 		outputPath.Append(filename);
542 		entry.SetTo(outputPath.Path());
543 	} while (entry.Exists());
544 
545 	return BString(filename);
546 }
547 
548 
549 int32
550 ScreenshotWindow::_PathIndexInMenu(const BString& path) const
551 {
552 	BString userPath;
553 	for (int32 i = 0; i < fOutputPathMenu->CountItems(); ++i) {
554 		BMenuItem* item = fOutputPathMenu->ItemAt(i);
555 		if (item && item->Message()
556 			&& item->Message()->FindString("path", &userPath) == B_OK) {
557 			if (userPath == path)
558 				return i;
559 		}
560 	}
561 	return -1;
562 }
563 
564 
565 BMessage
566 ScreenshotWindow::_ReadSettings() const
567 {
568 	BPath settingsPath;
569 	find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath);
570 	settingsPath.Append("screenshot");
571 
572 	BMessage settings;
573 
574 	BFile file(settingsPath.Path(), B_READ_ONLY);
575 	if (file.InitCheck() == B_OK)
576 		settings.Unflatten(&file);
577 
578 	return settings;
579 }
580 
581 
582 void
583 ScreenshotWindow::_WriteSettings() const
584 {
585 	BMessage settings;
586 	settings.AddInt32("be:type", fImageFileType);
587 
588 	BString path;
589 	int32 count = fOutputPathMenu->CountItems();
590 	if (count > 5) {
591 		for (int32 i = count - 3; i > count - 8 && i > 2; --i) {
592 			BMenuItem* item = fOutputPathMenu->ItemAt(i);
593 			if (item) {
594 				BMessage* msg = item->Message();
595 				if (msg && msg->FindString("path", &path) == B_OK)
596 					settings.AddString("path", path.String());
597 			}
598 		}
599 	}
600 
601 	if (fLastSelectedPath) {
602 		BMessage* msg = fLastSelectedPath->Message();
603 		if (msg && msg->FindString("path", &path) == B_OK)
604 			settings.AddString("lastSelectedPath", path.String());
605 	}
606 
607 	BPath settingsPath;
608 	find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath);
609 	settingsPath.Append("screenshot");
610 
611 	BFile file(settingsPath.Path(), B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
612 	if (file.InitCheck() == B_OK) {
613 		ssize_t size;
614 		settings.Flatten(&file, &size);
615 	}
616 }
617 
618 
619 void
620 ScreenshotWindow::_TakeScreenshot()
621 {
622 	if (fDelayControl)
623 		snooze((atoi(fDelayControl->Text()) * 1000000) + 50000);
624 
625 	BRect frame;
626 	delete fScreenshot;
627 	if (_GetActiveWindowFrame(&frame) == B_OK) {
628 		fScreenshot = new BBitmap(frame.OffsetToCopy(B_ORIGIN), B_RGBA32);
629 		BScreen(this).ReadBitmap(fScreenshot, fIncludeCursor, &frame);
630 	} else {
631 		BScreen(this).GetBitmap(&fScreenshot, fIncludeCursor);
632 	}
633 }
634 
635 
636 status_t
637 ScreenshotWindow::_GetActiveWindowFrame(BRect* frame)
638 {
639 	if (!fGrabActiveWindow || !frame)
640 		return B_ERROR;
641 
642 	int32* tokens;
643 	int32 tokenCount;
644 	status_t status = BPrivate::get_window_order(current_workspace(), &tokens,
645 		&tokenCount);
646 	if (status != B_OK || !tokens || tokenCount < 1)
647 		return B_ERROR;
648 
649 	status = B_ERROR;
650 	for (int32 i = 0; i < tokenCount; ++i) {
651 		client_window_info* windowInfo = get_window_info(tokens[i]);
652 		if (!windowInfo->is_mini && !windowInfo->show_hide_level > 0) {
653 			frame->left = windowInfo->window_left;
654 			frame->top = windowInfo->window_top;
655 			frame->right = windowInfo->window_right;
656 			frame->bottom = windowInfo->window_bottom;
657 
658 			status = B_OK;
659 			if (fIncludeBorder) {
660 				float border = (windowInfo->border_size);
661 				frame->InsetBy(-(border), -(border));
662 				frame->top -= windowInfo->tab_height;
663 			}
664 			free(windowInfo);
665 
666 			BRect screenFrame(BScreen(this).Frame());
667 			if (frame->left < screenFrame.left)
668 				frame->left = screenFrame.left;
669 			if (frame->top < screenFrame.top)
670 				frame->top = screenFrame.top;
671 			if (frame->right > screenFrame.right)
672 				frame->right = screenFrame.right;
673 			if (frame->bottom > screenFrame.bottom)
674 				frame->bottom = screenFrame.bottom;
675 
676 			break;
677 		}
678 		free(windowInfo);
679 	}
680 	free(tokens);
681 	return status;
682 }
683 
684 
685 void
686 ScreenshotWindow::_SaveScreenshot()
687 {
688 	if (!fScreenshot || !fLastSelectedPath)
689 		return;
690 
691 	const char* path;
692 	BMessage* message = fLastSelectedPath->Message();
693 	if (!message || message->FindString("path", &path) != B_OK)
694 		return;
695 
696 	BDirectory dir(path);
697 	BFile file(&dir, fNameControl->Text(), B_CREATE_FILE |
698 		B_ERASE_FILE | B_WRITE_ONLY);
699 	if (file.InitCheck() != B_OK)
700 		return;
701 
702 	BBitmapStream bitmapStream(fScreenshot);
703 	BTranslatorRoster* roster = BTranslatorRoster::Default();
704 	roster->Translate(&bitmapStream, NULL, NULL, &file, fImageFileType,
705 		B_TRANSLATOR_BITMAP);
706 	fScreenshot = NULL;
707 
708 	BNodeInfo nodeInfo(&file);
709 	if (nodeInfo.InitCheck() != B_OK)
710 		return;
711 
712 	int32 numFormats;
713 	const translation_format* formats = NULL;
714 	if (roster->GetOutputFormats(fTranslator, &formats, &numFormats) != B_OK)
715 		return;
716 
717 	for (int32 i = 0; i < numFormats; ++i) {
718 		if (formats[i].type == uint32(fImageFileType)) {
719 			nodeInfo.SetType(formats[i].MIME);
720 			break;
721 		}
722 	}
723 }
724 
725 
726 void
727 ScreenshotWindow::_SaveScreenshotSilent() const
728 {
729 	if (!fScreenshot)
730 		return;
731 
732 	BPath homePath;
733 	if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK) {
734 		fprintf(stderr, "failed to find user home directory\n");
735 		return;
736 	}
737 
738 	BPath path;
739 	BEntry entry;
740 	int32 index = 1;
741 	do {
742 		char filename[32];
743 		sprintf(filename, "screenshot%ld", index++);
744 		path = homePath;
745 		path.Append(filename);
746 		entry.SetTo(path.Path());
747 	} while (entry.Exists());
748 
749 	// Dump to PNG
750 	SaveToPNG(path.Path(), fScreenshot->Bounds(), fScreenshot->ColorSpace(),
751 		fScreenshot->Bits(), fScreenshot->BitsLength(),
752 		fScreenshot->BytesPerRow());
753 }
754