xref: /haiku/src/apps/screenshot/ScreenshotWindow.cpp (revision 82a8a20999118b748396cf16a33c47c3b0c0222d)
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 <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 	kShowMouse,
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,
70 						struct stat_beos* stat, 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 includeMouse, 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 	fIncludeMouse(includeMouse),
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 kShowMouse: {
133 			fIncludeMouse = (fShowMouse->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 folder");
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 			if (_SaveScreenshot() != B_OK)
196 				break;
197 
198 		// fall through
199 		case B_QUIT_REQUESTED:
200 			be_app_messenger.SendMessage(B_QUIT_REQUESTED);
201 		break;
202 
203 		case kShowOptions: {
204 			BCardLayout* layout = dynamic_cast<BCardLayout*> (GetLayout());
205 			if (layout)
206 				layout->SetVisibleItem(0L);
207 			SetTitle("Take Screenshot");
208 			fBackToSave->SetEnabled(true);
209 		}	break;
210 
211 		default: {
212 			BWindow::MessageReceived(message);
213 		}	break;
214 	};
215 
216 }
217 
218 
219 void
220 ScreenshotWindow::_InitWindow()
221 {
222 	BCardLayout* layout = new BCardLayout();
223 	SetLayout(layout);
224 
225 	_SetupFirstLayoutItem(layout);
226 	_SetupSecondLayoutItem(layout);
227 
228 	if (!fShowConfigWindow) {
229 		_TakeScreenshot();
230 		_UpdatePreviewPanel();
231 		layout->SetVisibleItem(1L);
232 	} else {
233 		layout->SetVisibleItem(0L);
234 	}
235 }
236 
237 
238 void
239 ScreenshotWindow::_SetupFirstLayoutItem(BCardLayout* layout)
240 {
241 	BStringView* stringView = new BStringView("", "Options");
242 	stringView->SetFont(be_bold_font);
243 	stringView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
244 
245 	fActiveWindow = new BRadioButton("Capture active window",
246 		 new BMessage(kScreenshotType));
247 	fWholeDesktop = new BRadioButton("Capture entire screen",
248 		 new BMessage(kScreenshotType));
249 	fWholeDesktop->SetValue(B_CONTROL_ON);
250 
251 	BString delay;
252 	delay << fDelay / 1000000;
253 	fDelayControl = new BTextControl("", "Take screenshot after a delay of",
254 		delay.String(), NULL);
255 	_DisallowChar(fDelayControl->TextView());
256 	fDelayControl->TextView()->SetAlignment(B_ALIGN_RIGHT);
257 
258 	BStringView* stringView2 = new BStringView("", "seconds");
259 	stringView2->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
260 
261 	fWindowBorder = new BCheckBox("Include window border",
262 		new BMessage(kIncludeBorder));
263 	fWindowBorder->SetEnabled(false);
264 
265 	fShowMouse = new BCheckBox("Include mouse pointer",
266 		new BMessage(kShowMouse));
267 	fShowMouse->SetValue(fIncludeMouse);
268 
269 	BBox* divider = new BBox(B_FANCY_BORDER, NULL);
270 	divider->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
271 
272 	fBackToSave = new BButton("", "Back to save", new BMessage(kBackToSave));
273 	fBackToSave->SetEnabled(false);
274 
275 	fTakeScreenshot = new BButton("", "Take Screenshot",
276 		new BMessage(kTakeScreenshot));
277 
278 	layout->AddView(0, BGroupLayoutBuilder(B_VERTICAL)
279 		.Add(stringView)
280 		.Add(BGridLayoutBuilder()
281 			.Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 0)
282 			.Add(fWholeDesktop, 1, 0)
283 			.Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 1)
284 			.Add(fActiveWindow, 1, 1)
285 			.SetInsets(0.0, 5.0, 0.0, 0.0))
286 		.AddGroup(B_HORIZONTAL)
287 			.AddStrut(30.0)
288 			.Add(fWindowBorder)
289 			.End()
290 		.AddStrut(10.0)
291 		.AddGroup(B_HORIZONTAL)
292 			.AddStrut(15.0)
293 			.Add(fShowMouse)
294 			.End()
295 		.AddStrut(5.0)
296 		.AddGroup(B_HORIZONTAL, 5.0)
297 			.AddStrut(10.0)
298 			.Add(fDelayControl->CreateLabelLayoutItem())
299 			.Add(fDelayControl->CreateTextViewLayoutItem())
300 			.Add(stringView2)
301 			.End()
302 		.AddStrut(10.0)
303 		.AddGlue()
304 		.Add(divider)
305 		.AddStrut(10)
306 		.AddGroup(B_HORIZONTAL, 10.0)
307 			.Add(fBackToSave)
308 			.AddGlue()
309 			.Add(new BButton("", "Cancel", new BMessage(B_QUIT_REQUESTED)))
310 			.Add(fTakeScreenshot)
311 			.End()
312 		.SetInsets(10.0, 10.0, 10.0, 10.0)
313 	);
314 
315 	if (fGrabActiveWindow) {
316 		fWindowBorder->SetEnabled(true);
317 		fActiveWindow->SetValue(B_CONTROL_ON);
318 		fWindowBorder->SetValue(fIncludeBorder);
319 	}
320 }
321 
322 
323 void
324 ScreenshotWindow::_SetupSecondLayoutItem(BCardLayout* layout)
325 {
326 	fPreview = new PreviewView();
327 
328 	fNameControl = new BTextControl("", "Name:", "screenshot1", NULL);
329 
330 	BMessage settings(_ReadSettings());
331 
332 	_SetupTranslatorMenu(new BMenu("Please select"), settings);
333 	BMenuField* menuField = new BMenuField("Save as:", fTranslatorMenu);
334 
335 	_SetupOutputPathMenu(new BMenu("Please select"), settings);
336 	BMenuField* menuField2 = new BMenuField("Save in:", fOutputPathMenu);
337 
338 	fNameControl->SetText(_FindValidFileName("screenshot1").String());
339 
340 	BBox* divider = new BBox(B_FANCY_BORDER, NULL);
341 	divider->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
342 
343 	BGridLayout* gridLayout = BGridLayoutBuilder(0.0, 5.0)
344 		.Add(fNameControl->CreateLabelLayoutItem(), 0, 0)
345 		.Add(fNameControl->CreateTextViewLayoutItem(), 1, 0)
346 		.Add(menuField->CreateLabelLayoutItem(), 0, 1)
347 		.Add(menuField->CreateMenuBarLayoutItem(), 1, 1)
348 		.Add(menuField2->CreateLabelLayoutItem(), 0, 2)
349 		.Add(menuField2->CreateMenuBarLayoutItem(), 1, 2);
350 	gridLayout->SetMinColumnWidth(1, menuField->StringWidth("SomethingLongHere"));
351 
352 	layout->AddView(1, BGroupLayoutBuilder(B_VERTICAL)
353 		.Add(BGroupLayoutBuilder(B_HORIZONTAL, 10.0)
354 			.Add(fPreview)
355 			.AddGroup(B_VERTICAL)
356 				.Add(gridLayout->View())
357 				.AddGlue()
358 				.End())
359 		.AddStrut(10)
360 		.Add(divider)
361 		.AddStrut(10)
362 		.AddGroup(B_HORIZONTAL, 10.0)
363 			.Add(new BButton("", "Options", new BMessage(kShowOptions)))
364 			.AddGlue()
365 			.Add(new BButton("", "Cancel", new BMessage(B_QUIT_REQUESTED)))
366 			.Add(new BButton("", "Save", new BMessage(kFinishScreenshot)))
367 			.End()
368 		.SetInsets(10.0, 10.0, 10.0, 10.0)
369 	);
370 }
371 
372 
373 void
374 ScreenshotWindow::_DisallowChar(BTextView* textView)
375 {
376 	for (uint32 i = 0; i < '0'; ++i)
377 		textView->DisallowChar(i);
378 
379 	for (uint32 i = '9' + 1; i < 255; ++i)
380 		textView->DisallowChar(i);
381 }
382 
383 
384 void
385 ScreenshotWindow::_SetupTranslatorMenu(BMenu* translatorMenu,
386 	const BMessage& settings)
387 {
388 	fTranslatorMenu = translatorMenu;
389 
390 	BMessage message(kImageOutputFormat);
391 	fTranslatorMenu = new BMenu("Please select");
392 	BTranslationUtils::AddTranslationItems(fTranslatorMenu, B_TRANSLATOR_BITMAP,
393 		&message, NULL, NULL, NULL);
394 
395 	fTranslatorMenu->SetLabelFromMarked(true);
396 
397 	if (fTranslatorMenu->ItemAt(0))
398 		fTranslatorMenu->ItemAt(0)->SetMarked(true);
399 
400 	if (settings.FindInt32("be:type", &fImageFileType) != B_OK)
401 		fImageFileType = B_PNG_FORMAT;
402 
403 	int32 imageFileType;
404 	for (int32 i = 0; i < fTranslatorMenu->CountItems(); ++i) {
405 		BMenuItem* item = fTranslatorMenu->ItemAt(i);
406 		if (item && item->Message()) {
407 			item->Message()->FindInt32("be:type", &imageFileType);
408 			if (fImageFileType == imageFileType) {
409 				item->SetMarked(true);
410 				break;
411 			}
412 		}
413 	}
414 }
415 
416 
417 void
418 ScreenshotWindow::_SetupOutputPathMenu(BMenu* outputPathMenu,
419 	const BMessage& settings)
420 {
421 	fOutputPathMenu = outputPathMenu;
422 	fOutputPathMenu->SetLabelFromMarked(true);
423 
424 	BString lastSelectedPath;
425 	settings.FindString("lastSelectedPath", &lastSelectedPath);
426 
427 	BPath path;
428 	find_directory(B_USER_DIRECTORY, &path);
429 
430 	BString label("Home folder");
431 	_AddItemToPathMenu(path.Path(), label, 0, (path.Path() == lastSelectedPath));
432 
433 	path.Append("Desktop");
434 	label.SetTo("Desktop");
435 	_AddItemToPathMenu(path.Path(), label, 0, (path.Path() == lastSelectedPath));
436 
437 	find_directory(B_BEOS_ETC_DIRECTORY, &path);
438 	path.Append("artwork");
439 
440 	label.SetTo("Artwork folder");
441 	_AddItemToPathMenu(path.Path(), label, 2, (path.Path() == lastSelectedPath));
442 
443 	int32 i = 0;
444 	BString userPath;
445 	while (settings.FindString("path", i++, &userPath) == B_OK) {
446 		_AddItemToPathMenu(userPath.String(), userPath, 3,
447 			(userPath == lastSelectedPath));
448 	}
449 
450 	if (!fLastSelectedPath) {
451 		if (settings.IsEmpty() || lastSelectedPath.Length() == 0) {
452 			fOutputPathMenu->ItemAt(1)->SetMarked(true);
453 			fLastSelectedPath = fOutputPathMenu->ItemAt(1);
454 		} else {
455 			_AddItemToPathMenu(lastSelectedPath.String(), lastSelectedPath, 3,
456 				true);
457 		}
458 	}
459 
460 	fOutputPathMenu->AddItem(new BSeparatorItem());
461 	fOutputPathMenu->AddItem(new BMenuItem("Choose folder...",
462 		new BMessage(kChooseLocation)));
463 }
464 
465 
466 void
467 ScreenshotWindow::_AddItemToPathMenu(const char* path, BString& label,
468 	int32 index, bool markItem)
469 {
470 	BMessage* message = new BMessage(kLocationChanged);
471 	message->AddString("path", path);
472 
473 	fOutputPathMenu->TruncateString(&label, B_TRUNCATE_MIDDLE,
474 		fOutputPathMenu->StringWidth("SomethingLongHere"));
475 
476 	fOutputPathMenu->AddItem(new BMenuItem(label.String(), message), index);
477 
478 	if (markItem) {
479 		fOutputPathMenu->ItemAt(index)->SetMarked(true);
480 		fLastSelectedPath = fOutputPathMenu->ItemAt(index);
481 	}
482 }
483 
484 
485 void
486 ScreenshotWindow::_CenterAndShow()
487 {
488 	BSize size = GetLayout()->PreferredSize();
489 	ResizeTo(size.Width(), size.Height());
490 
491 	BRect frame(BScreen(this).Frame());
492 	MoveTo((frame.Width() - size.Width()) / 2.0,
493 		(frame.Height() - size.Height()) / 2.0);
494 
495 	Show();
496 }
497 
498 
499 void
500 ScreenshotWindow::_UpdatePreviewPanel()
501 {
502 	float height = 150.0f;
503 
504 	float width = (fScreenshot->Bounds().Width() /
505 		fScreenshot->Bounds().Height()) * height;
506 
507 	// to prevent a preview way too wide
508 	if (width > 400.0f) {
509 		width = 400.0f;
510 		height = (fScreenshot->Bounds().Height() /
511 			fScreenshot->Bounds().Width()) * width;
512 	}
513 
514 	fPreview->SetExplicitMinSize(BSize(width, height));
515 	fPreview->SetExplicitMaxSize(BSize(width, height));
516 
517 	fPreview->ClearViewBitmap();
518 	fPreview->SetViewBitmap(fScreenshot, fScreenshot->Bounds(),
519 		fPreview->Bounds(), B_FOLLOW_ALL, 0);
520 
521 	BCardLayout* layout = dynamic_cast<BCardLayout*> (GetLayout());
522 	if (layout)
523 		layout->SetVisibleItem(1L);
524 
525 	SetTitle("Save Screenshot");
526 }
527 
528 
529 BString
530 ScreenshotWindow::_FindValidFileName(const char* name) const
531 {
532 	BString fileName(name);
533 	if (!fLastSelectedPath)
534 		return fileName;
535 
536 	const char* path;
537 	BMessage* message = fLastSelectedPath->Message();
538 	if (!message || message->FindString("path", &path) != B_OK)
539 		return fileName;
540 
541 	BPath outputPath(path);
542 	outputPath.Append(name);
543 	if (!BEntry(outputPath.Path()).Exists())
544 		return fileName;
545 
546 	if (strncmp(name, "screenshot", strlen("screenshot")) == 0)
547 		fileName.SetTo("screenshot");
548 
549 	BEntry entry;
550 	int32 index = 1;
551 	char filename[32];
552 	do {
553 		sprintf(filename, "%s%ld", fileName.String(), index++);
554 		outputPath.SetTo(path);
555 		outputPath.Append(filename);
556 		entry.SetTo(outputPath.Path());
557 	} while (entry.Exists());
558 
559 	return BString(filename);
560 }
561 
562 
563 int32
564 ScreenshotWindow::_PathIndexInMenu(const BString& path) const
565 {
566 	BString userPath;
567 	for (int32 i = 0; i < fOutputPathMenu->CountItems(); ++i) {
568 		BMenuItem* item = fOutputPathMenu->ItemAt(i);
569 		if (item && item->Message()
570 			&& item->Message()->FindString("path", &userPath) == B_OK) {
571 			if (userPath == path)
572 				return i;
573 		}
574 	}
575 	return -1;
576 }
577 
578 
579 BMessage
580 ScreenshotWindow::_ReadSettings() const
581 {
582 	BPath settingsPath;
583 	find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath);
584 	settingsPath.Append("screenshot");
585 
586 	BMessage settings;
587 
588 	BFile file(settingsPath.Path(), B_READ_ONLY);
589 	if (file.InitCheck() == B_OK)
590 		settings.Unflatten(&file);
591 
592 	return settings;
593 }
594 
595 
596 void
597 ScreenshotWindow::_WriteSettings() const
598 {
599 	BMessage settings;
600 	settings.AddInt32("be:type", fImageFileType);
601 
602 	BString path;
603 	int32 count = fOutputPathMenu->CountItems();
604 	if (count > 5) {
605 		for (int32 i = count - 3; i > count - 8 && i > 2; --i) {
606 			BMenuItem* item = fOutputPathMenu->ItemAt(i);
607 			if (item) {
608 				BMessage* msg = item->Message();
609 				if (msg && msg->FindString("path", &path) == B_OK)
610 					settings.AddString("path", path.String());
611 			}
612 		}
613 	}
614 
615 	if (fLastSelectedPath) {
616 		BMessage* msg = fLastSelectedPath->Message();
617 		if (msg && msg->FindString("path", &path) == B_OK)
618 			settings.AddString("lastSelectedPath", path.String());
619 	}
620 
621 	BPath settingsPath;
622 	find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath);
623 	settingsPath.Append("screenshot");
624 
625 	BFile file(settingsPath.Path(), B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
626 	if (file.InitCheck() == B_OK) {
627 		ssize_t size;
628 		settings.Flatten(&file, &size);
629 	}
630 }
631 
632 
633 void
634 ScreenshotWindow::_TakeScreenshot()
635 {
636 	if (fDelayControl)
637 		snooze((atoi(fDelayControl->Text()) * 1000000) + 50000);
638 
639 	BRect frame;
640 	delete fScreenshot;
641 	if (_GetActiveWindowFrame(&frame) == B_OK) {
642 		fScreenshot = new BBitmap(frame.OffsetToCopy(B_ORIGIN), B_RGBA32);
643 		BScreen(this).ReadBitmap(fScreenshot, fIncludeMouse, &frame);
644 	} else {
645 		BScreen(this).GetBitmap(&fScreenshot, fIncludeMouse);
646 	}
647 }
648 
649 
650 status_t
651 ScreenshotWindow::_GetActiveWindowFrame(BRect* frame)
652 {
653 	if (!fGrabActiveWindow || !frame)
654 		return B_ERROR;
655 
656 	int32* tokens;
657 	int32 tokenCount;
658 	status_t status = BPrivate::get_window_order(current_workspace(), &tokens,
659 		&tokenCount);
660 	if (status != B_OK || !tokens || tokenCount < 1)
661 		return B_ERROR;
662 
663 	status = B_ERROR;
664 	for (int32 i = 0; i < tokenCount; ++i) {
665 		client_window_info* windowInfo = get_window_info(tokens[i]);
666 		if (!windowInfo->is_mini && !windowInfo->show_hide_level > 0) {
667 			frame->left = windowInfo->window_left;
668 			frame->top = windowInfo->window_top;
669 			frame->right = windowInfo->window_right;
670 			frame->bottom = windowInfo->window_bottom;
671 
672 			status = B_OK;
673 			if (fIncludeBorder) {
674 				float border = (windowInfo->border_size);
675 				frame->InsetBy(-(border), -(border));
676 				frame->top -= windowInfo->tab_height;
677 			}
678 			free(windowInfo);
679 
680 			BRect screenFrame(BScreen(this).Frame());
681 			if (frame->left < screenFrame.left)
682 				frame->left = screenFrame.left;
683 			if (frame->top < screenFrame.top)
684 				frame->top = screenFrame.top;
685 			if (frame->right > screenFrame.right)
686 				frame->right = screenFrame.right;
687 			if (frame->bottom > screenFrame.bottom)
688 				frame->bottom = screenFrame.bottom;
689 
690 			break;
691 		}
692 		free(windowInfo);
693 	}
694 	free(tokens);
695 	return status;
696 }
697 
698 
699 status_t
700 ScreenshotWindow::_SaveScreenshot()
701 {
702 	if (!fScreenshot || !fLastSelectedPath)
703 		return B_ERROR;
704 
705 	const char* _path;
706 	BMessage* message = fLastSelectedPath->Message();
707 	if (!message || message->FindString("path", &_path) != B_OK)
708 		return B_ERROR;
709 
710 	BEntry entry;
711 	BPath path;
712 
713 	path = _path;
714 	path.Append(fNameControl->Text());
715 	entry.SetTo(path.Path());
716 
717 	if (entry.Exists()) {
718 		BAlert *myAlert = new BAlert("overwrite", "This file already exists.\n"
719 			"Are you sure would you like to overwrite it?",
720     		"Cancel", "Overwrite", NULL,
721 			B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
722 
723 			myAlert->SetShortcut(0, B_ESCAPE);
724 			int32 button_index = myAlert->Go();
725 			if (button_index == 0) {
726 				return B_CANCELED;
727 			}
728 	}
729 
730 	BFile file(&entry, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
731 	if (file.InitCheck() != B_OK) {
732 		return B_ERROR;
733 	}
734 	BBitmapStream bitmapStream(fScreenshot);
735 	BTranslatorRoster* roster = BTranslatorRoster::Default();
736 	roster->Translate(&bitmapStream, NULL, NULL, &file, fImageFileType,
737 		B_TRANSLATOR_BITMAP);
738 	fScreenshot = NULL;
739 
740 	BNodeInfo nodeInfo(&file);
741 	if (nodeInfo.InitCheck() != B_OK)
742 		return B_ERROR;
743 
744 	int32 numFormats;
745 	const translation_format* formats = NULL;
746 	if (roster->GetOutputFormats(fTranslator, &formats, &numFormats) != B_OK)
747 		return B_OK;
748 
749 	for (int32 i = 0; i < numFormats; ++i) {
750 		if (formats[i].type == uint32(fImageFileType)) {
751 			nodeInfo.SetType(formats[i].MIME);
752 			break;
753 		}
754 	}
755 	return B_OK;
756 }
757 
758 
759 void
760 ScreenshotWindow::_SaveScreenshotSilent() const
761 {
762 	if (!fScreenshot)
763 		return;
764 
765 	BPath homePath;
766 	if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK) {
767 		fprintf(stderr, "failed to find user home folder\n");
768 		return;
769 	}
770 
771 	BPath path;
772 	BEntry entry;
773 	int32 index = 1;
774 	do {
775 		char filename[32];
776 		sprintf(filename, "screenshot%ld", index++);
777 		path = homePath;
778 		path.Append(filename);
779 		entry.SetTo(path.Path());
780 	} while (entry.Exists());
781 
782 	// Dump to PNG
783 	SaveToPNG(path.Path(), fScreenshot->Bounds(), fScreenshot->ColorSpace(),
784 		fScreenshot->Bits(), fScreenshot->BitsLength(),
785 		fScreenshot->BytesPerRow());
786 }
787