xref: /haiku/src/apps/screenshot/ScreenshotWindow.cpp (revision 8f1c695124347ac6005f6df4081ee352962d7849)
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