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