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