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