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