xref: /haiku/src/apps/screenshot/ScreenshotWindow.cpp (revision d0ac609964842f8cdb6d54b3c539c6c15293e172)
1 /*
2  * Copyright 2010-2014, Haiku Inc. All rights reserved.
3  * Copyright 2010 Wim van der Meer <WPJvanderMeer@gmail.com>
4  * Copyright Karsten Heimrich, host.haiku@gmx.de.
5  * All rights reserved. Distributed under the terms of the MIT License.
6  *
7  * Authors:
8  *		Karsten Heimrich
9  *		Fredrik Modéen
10  *		Christophe Huriaux
11  *		Wim van der Meer
12  */
13 
14 
15 #include "ScreenshotWindow.h"
16 
17 #include <stdlib.h>
18 
19 #include <Alert.h>
20 #include <Application.h>
21 #include <Bitmap.h>
22 #include <Box.h>
23 #include <Button.h>
24 #include <Catalog.h>
25 #include <CheckBox.h>
26 #include <ControlLook.h>
27 #include <File.h>
28 #include <FilePanel.h>
29 #include <FindDirectory.h>
30 #include <LayoutBuilder.h>
31 #include <Locale.h>
32 #include <Menu.h>
33 #include <MenuField.h>
34 #include <MenuItem.h>
35 #include <MessageFilter.h>
36 #include <Path.h>
37 #include <Roster.h>
38 #include <SpaceLayoutItem.h>
39 #include <String.h>
40 #include <StringView.h>
41 #include <TextControl.h>
42 #include <TranslationUtils.h>
43 #include <TranslatorRoster.h>
44 
45 #include "Utility.h"
46 
47 
48 #undef B_TRANSLATION_CONTEXT
49 #define B_TRANSLATION_CONTEXT "ScreenshotWindow"
50 
51 
52 enum {
53 	kActiveWindow,
54 	kIncludeBorder,
55 	kIncludeCursor,
56 	kNewScreenshot,
57 	kImageFormat,
58 	kLocationChanged,
59 	kChooseLocation,
60 	kSaveScreenshot,
61 	kSettings,
62 	kCloseTranslatorSettings
63 };
64 
65 
66 // #pragma mark - QuitMessageFilter
67 
68 
69 class QuitMessageFilter : public BMessageFilter {
70 public:
71 	QuitMessageFilter(BWindow* window)
72 		:
73 		BMessageFilter((uint32)B_QUIT_REQUESTED),
74 		fWindow(window)
75 	{
76 	}
77 
78 	virtual filter_result Filter(BMessage* message, BHandler** target)
79 	{
80 		BMessenger(fWindow).SendMessage(kCloseTranslatorSettings);
81 		return B_SKIP_MESSAGE;
82 	}
83 
84 private:
85 	BWindow* fWindow;
86 };
87 
88 
89 // #pragma mark - DirectoryRefFilter
90 
91 
92 class DirectoryRefFilter : public BRefFilter {
93 public:
94 	virtual ~DirectoryRefFilter()
95 	{
96 	}
97 
98 	virtual bool Filter(const entry_ref* ref, BNode* node,
99 		struct stat_beos* stat, const char* filetype)
100 	{
101 		return node->IsDirectory();
102 	}
103 };
104 
105 
106 // #pragma mark - ScreenshotWindow
107 
108 
109 ScreenshotWindow::ScreenshotWindow(const Utility& utility, bool silent,
110 	bool clipboard)
111 	:
112 	BWindow(BRect(0, 0, 200.0, 100.0), B_TRANSLATE_SYSTEM_NAME("Screenshot"),
113 		B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_AVOID_FRONT
114 			| B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS
115 			| B_CLOSE_ON_ESCAPE),
116 	fUtility(utility),
117 	fDelayControl(NULL),
118 	fScreenshot(NULL),
119 	fOutputPathPanel(NULL),
120 	fLastSelectedPath(NULL),
121 	fSettingsWindow(NULL),
122 	fDelay(0),
123 	fIncludeBorder(false),
124 	fIncludeCursor(false),
125 	fGrabActiveWindow(false),
126 	fOutputFilename(NULL),
127 	fExtension(""),
128 	fImageFileType(B_PNG_FORMAT)
129 {
130 	// _ReadSettings() needs a valid fOutputPathMenu
131 	fOutputPathMenu = new BMenu(B_TRANSLATE("Please select"));
132 	_ReadSettings();
133 
134 	// _NewScreenshot() needs a valid fNameControl
135 	BString name(B_TRANSLATE_NOCOLLECT(fUtility.sDefaultFileNameBase));
136 	name << 1;
137 	name = _FindValidFileName(name.String());
138 	fNameControl = new BTextControl("", B_TRANSLATE("Name:"), name, NULL);
139 
140 	// Check if fUtility contains valid data
141 	if (fUtility.wholeScreen == NULL) {
142 		_NewScreenshot(silent, clipboard);
143 		return;
144 	}
145 
146 	fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, fGrabActiveWindow,
147 		fIncludeBorder);
148 
149 	fActiveWindow = new BCheckBox(B_TRANSLATE("Capture active window"),
150 		new BMessage(kActiveWindow));
151 	if (fGrabActiveWindow)
152 		fActiveWindow->SetValue(B_CONTROL_ON);
153 
154 	fWindowBorder = new BCheckBox(B_TRANSLATE("Include window border"),
155 		new BMessage(kIncludeBorder));
156 	if (fIncludeBorder)
157 		fWindowBorder->SetValue(B_CONTROL_ON);
158 	if (!fGrabActiveWindow)
159 		fWindowBorder->SetEnabled(false);
160 
161 	fShowCursor = new BCheckBox(B_TRANSLATE("Include mouse pointer"),
162 		new BMessage(kIncludeCursor));
163 	if (fIncludeCursor)
164 		fShowCursor->SetValue(B_CONTROL_ON);
165 
166 	BString delay;
167 	delay << fDelay / 1000000;
168 	fDelayControl = new BTextControl("", B_TRANSLATE("Delay:"), delay.String(),
169 		NULL);
170 	_DisallowChar(fDelayControl->TextView());
171 	fDelayControl->TextView()->SetAlignment(B_ALIGN_RIGHT);
172 	BStringView* seconds = new BStringView("", B_TRANSLATE("seconds"));
173 	seconds->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
174 
175 	BMenuField* menuLocation = new BMenuField(B_TRANSLATE("Save in:"),
176 		fOutputPathMenu);
177 	menuLocation->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
178 
179 	fTranslatorMenu = new BMenu(B_TRANSLATE("Please select"));
180 	_SetupTranslatorMenu();
181 	BMenuField* menuFormat = new BMenuField(B_TRANSLATE("Save as:"),
182 		fTranslatorMenu);
183 	menuFormat->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
184 
185 	BButton* showSettings =  new BButton("",
186 		B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), new BMessage(kSettings));
187 	showSettings->SetExplicitAlignment(
188 		BAlignment(B_ALIGN_RIGHT, B_ALIGN_BOTTOM));
189 
190 	BBox* divider = new BBox(B_FANCY_BORDER, NULL);
191 	divider->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
192 
193 	BButton* saveScreenshot  = new BButton("", B_TRANSLATE("Save"),
194 		new BMessage(kSaveScreenshot));
195 
196 	const float kSpacing = be_control_look->DefaultItemSpacing();
197 	const float kLabelSpacing = be_control_look->DefaultLabelSpacing();
198 
199 	fPreview = new BView("preview", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
200 	BBox* previewBox = new BBox(B_FANCY_BORDER, fPreview);
201 
202 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
203 		.AddGroup(B_HORIZONTAL)
204 			.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
205 				B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING)
206 			.Add(previewBox)
207 			.AddGroup(B_VERTICAL, 0)
208 				.Add(fActiveWindow)
209 				.Add(fWindowBorder)
210 				.Add(fShowCursor)
211 				.AddStrut(kSpacing)
212 				.AddGrid(0.0, kSpacing / 2)
213 					.Add(fDelayControl->CreateLabelLayoutItem(), 0, 0)
214 					.Add(fDelayControl->CreateTextViewLayoutItem(), 1, 0)
215 					.Add(BSpaceLayoutItem::CreateHorizontalStrut(kLabelSpacing),
216 						2, 0)
217 					.Add(seconds, 3, 0)
218 					.Add(fNameControl->CreateLabelLayoutItem(), 0, 1)
219 					.Add(fNameControl->CreateTextViewLayoutItem(), 1, 1, 3, 1)
220 					.Add(menuLocation->CreateLabelLayoutItem(), 0, 2)
221 					.Add(menuLocation->CreateMenuBarLayoutItem(), 1, 2, 3, 1)
222 					.Add(menuFormat->CreateLabelLayoutItem(), 0, 3)
223 					.Add(menuFormat->CreateMenuBarLayoutItem(), 1, 3, 3, 1)
224 				.End()
225 				.AddStrut(kSpacing / 2)
226 				.Add(showSettings)
227 				.AddGlue()
228 			.End()
229 		.End()
230 		.AddStrut(kSpacing)
231 		.Add(divider)
232 		.AddStrut(kSpacing)
233 		.AddGroup(B_HORIZONTAL, kSpacing)
234 			.Add(new BButton("", B_TRANSLATE("Copy to clipboard"),
235 				new BMessage(B_COPY)))
236 			.Add(new BButton("", B_TRANSLATE("New screenshot"),
237 				new BMessage(kNewScreenshot)))
238 			.AddGlue()
239 			.Add(saveScreenshot);
240 
241 	saveScreenshot->MakeDefault(true);
242 
243 	_UpdatePreviewPanel();
244 	_UpdateFilenameSelection();
245 
246 	CenterOnScreen();
247 	Show();
248 }
249 
250 
251 ScreenshotWindow::~ScreenshotWindow()
252 {
253 	if (fOutputPathPanel)
254 		delete fOutputPathPanel->RefFilter();
255 
256 	delete fOutputPathPanel;
257 	delete fScreenshot;
258 }
259 
260 
261 void
262 ScreenshotWindow::MessageReceived(BMessage* message)
263 {
264 	switch (message->what) {
265 		case kActiveWindow:
266 			fGrabActiveWindow = false;
267 			if (fActiveWindow->Value() == B_CONTROL_ON)
268 				fGrabActiveWindow = true;
269 
270 			fWindowBorder->SetEnabled(fGrabActiveWindow);
271 
272 			delete fScreenshot;
273 			fScreenshot = fUtility.MakeScreenshot(fIncludeCursor,
274 				fGrabActiveWindow, fIncludeBorder);
275 			_UpdatePreviewPanel();
276 			break;
277 
278 		case kIncludeBorder:
279 			fIncludeBorder = (fWindowBorder->Value() == B_CONTROL_ON);
280 			delete fScreenshot;
281 			fScreenshot = fUtility.MakeScreenshot(fIncludeCursor,
282 				fGrabActiveWindow, fIncludeBorder);
283 			_UpdatePreviewPanel();
284 			break;
285 
286 		case kIncludeCursor:
287 			fIncludeCursor = (fShowCursor->Value() == B_CONTROL_ON);
288 			delete fScreenshot;
289 			fScreenshot = fUtility.MakeScreenshot(fIncludeCursor,
290 				fGrabActiveWindow, fIncludeBorder);
291 			_UpdatePreviewPanel();
292 			break;
293 
294 		case kNewScreenshot:
295 			fDelay = (atoi(fDelayControl->Text()) * 1000000) + 50000;
296 			_NewScreenshot();
297 			break;
298 
299 		case kImageFormat:
300 			message->FindInt32("be:type", &fImageFileType);
301 			fNameControl->SetText(_FindValidFileName(
302 				fNameControl->Text()).String());
303 			_UpdateFilenameSelection();
304 			_ShowSettings(false);
305 			break;
306 
307 		case kLocationChanged:
308 		{
309 			void* source = NULL;
310 			if (message->FindPointer("source", &source) == B_OK)
311 				fLastSelectedPath = static_cast<BMenuItem*> (source);
312 
313 			fNameControl->SetText(_FindValidFileName(
314 				fNameControl->Text()).String());
315 
316 			_UpdateFilenameSelection();
317 			break;
318 		}
319 
320 		case kChooseLocation:
321 		{
322 			if (!fOutputPathPanel) {
323 				BMessenger target(this);
324 				fOutputPathPanel = new BFilePanel(B_OPEN_PANEL, &target, NULL,
325 					B_DIRECTORY_NODE, false, NULL, new DirectoryRefFilter());
326 				fOutputPathPanel->Window()->SetTitle(
327 					B_TRANSLATE("Choose folder"));
328 				fOutputPathPanel->SetButtonLabel(B_DEFAULT_BUTTON,
329 					B_TRANSLATE("Select"));
330 				fOutputPathPanel->SetButtonLabel(B_CANCEL_BUTTON,
331 					B_TRANSLATE("Cancel"));
332 			}
333 			fOutputPathPanel->Show();
334 			break;
335 		}
336 
337 		case B_REFS_RECEIVED:
338 		{
339 			entry_ref ref;
340 			if (message->FindRef("refs", &ref) == B_OK) {
341 				BEntry entry(&ref, true);
342 				if (entry.InitCheck() == B_OK) {
343 					BPath path;
344 					if (entry.GetPath(&path) == B_OK) {
345 						BString label(path.Path());
346 						_AddItemToPathMenu(path.Path(), label, 3, true);
347 					}
348 				}
349 			}
350 			break;
351 		}
352 
353 		case B_CANCEL:
354 			fLastSelectedPath->SetMarked(true);
355 			break;
356 
357 		case kSaveScreenshot:
358 			if (_SaveScreenshot() == B_OK)
359 				be_app->PostMessage(B_QUIT_REQUESTED);
360 			break;
361 
362 		case B_COPY:
363 			fUtility.CopyToClipboard(fScreenshot);
364 			break;
365 
366 		case kSettings:
367 			_ShowSettings(true);
368 			break;
369 
370 		case kCloseTranslatorSettings:
371 			fSettingsWindow->Lock();
372 			fSettingsWindow->Quit();
373 			fSettingsWindow = NULL;
374 			break;
375 
376 		default:
377 			BWindow::MessageReceived(message);
378 			break;
379 	}
380 }
381 
382 
383 void
384 ScreenshotWindow::Quit()
385 {
386 	if (fUtility.wholeScreen != NULL)
387 		_WriteSettings();
388 	BWindow::Quit();
389 }
390 
391 
392 void
393 ScreenshotWindow::_NewScreenshot(bool silent, bool clipboard)
394 {
395 	BMessage message(B_ARGV_RECEIVED);
396 	int32 argc = 3;
397 	BString delay;
398 	delay << fDelay / 1000000;
399 	message.AddString("argv", "screenshot");
400 	message.AddString("argv", "--delay");
401 	message.AddString("argv", delay);
402 
403 	if (silent || clipboard) {
404 		if (silent) {
405 			argc++;
406 			message.AddString("argv", "--silent");
407 		}
408 		if (clipboard) {
409 			argc++;
410 			message.AddString("argv", "--clipboard");
411 		}
412 		if (fIncludeBorder) {
413 			argc++;
414 			message.AddString("argv", "--border");
415 		}
416 		if (fIncludeCursor) {
417 			argc++;
418 			message.AddString("argv", "--mouse-pointer");
419 		}
420 		if (fGrabActiveWindow) {
421 			argc++;
422 			message.AddString("argv", "--window");
423 		}
424 		if (fLastSelectedPath) {
425 			BPath path(_GetDirectory());
426 			if (path != NULL) {
427 				path.Append(fNameControl->Text());
428 				argc++;
429 				message.AddString("argv", path.Path());
430 			}
431 		}
432 	}
433 	message.AddInt32("argc", argc);
434 
435 	be_roster->Launch("application/x-vnd.haiku-screenshot-cli", &message);
436 	be_app->PostMessage(B_QUIT_REQUESTED);
437 }
438 
439 
440 void
441 ScreenshotWindow::_UpdatePreviewPanel()
442 {
443 	// Set the height of fPreview to what the layout suggests
444 	fPreview->SetExplicitMinSize(BSize());
445 	fPreview->SetExplicitMaxSize(BSize());
446 	Layout(false);
447 
448 	float height = fPreview->Bounds().Height();
449 	float width = (fScreenshot->Bounds().Width()
450 		/ fScreenshot->Bounds().Height()) * height;
451 
452 	// to prevent a preview way too wide
453 	if (width > 400.0f) {
454 		width = 400.0f;
455 		height = (fScreenshot->Bounds().Height()
456 			/ fScreenshot->Bounds().Width()) * width;
457 	}
458 
459 	fPreview->SetExplicitMinSize(BSize(width, height));
460 	fPreview->SetExplicitMaxSize(BSize(width, height));
461 
462 	fPreview->ClearViewBitmap();
463 	fPreview->SetViewBitmap(fScreenshot, fScreenshot->Bounds(),
464 		fPreview->Bounds(), B_FOLLOW_ALL, B_FILTER_BITMAP_BILINEAR);
465 }
466 
467 
468 void
469 ScreenshotWindow::_DisallowChar(BTextView* textView)
470 {
471 	for (uint32 i = 0; i < '0'; ++i)
472 		textView->DisallowChar(i);
473 
474 	for (uint32 i = '9' + 1; i < 255; ++i)
475 		textView->DisallowChar(i);
476 }
477 
478 
479 void
480 ScreenshotWindow::_SetupOutputPathMenu(const BMessage& settings)
481 {
482 	fOutputPathMenu->SetLabelFromMarked(true);
483 
484 	BString lastSelectedPath;
485 	settings.FindString("lastSelectedPath", &lastSelectedPath);
486 
487 	BPath path;
488 	find_directory(B_USER_DIRECTORY, &path);
489 
490 	BString label(B_TRANSLATE("Home folder"));
491 	_AddItemToPathMenu(path.Path(), label, 0,
492 		path.Path() == lastSelectedPath, 'H');
493 
494 	path.Append("Desktop");
495 	label.SetTo(B_TRANSLATE("Desktop"));
496 	_AddItemToPathMenu(path.Path(), label, 0,
497 		path.Path() == lastSelectedPath, 'D');
498 
499 	find_directory(B_USER_NONPACKAGED_DATA_DIRECTORY, &path);
500 	path.Append("artwork");
501 
502 	label.SetTo(B_TRANSLATE("Artwork folder"));
503 	_AddItemToPathMenu(path.Path(), label, 2,
504 		path.Path() == lastSelectedPath, 'A');
505 
506 	int32 i = 0;
507 	BString userPath;
508 	while (settings.FindString("path", ++i, &userPath) == B_OK) {
509 		_AddItemToPathMenu(userPath.String(), userPath, 3,
510 			userPath == lastSelectedPath);
511 	}
512 
513 	if (!fLastSelectedPath) {
514 		if (settings.IsEmpty() || lastSelectedPath.Length() == 0) {
515 			fOutputPathMenu->ItemAt(1)->SetMarked(true);
516 			fLastSelectedPath = fOutputPathMenu->ItemAt(1);
517 		} else {
518 			_AddItemToPathMenu(lastSelectedPath.String(), lastSelectedPath, 3,
519 				true);
520 		}
521 	}
522 
523 	fOutputPathMenu->AddItem(new BSeparatorItem());
524 	fOutputPathMenu->AddItem(new BMenuItem(
525 		B_TRANSLATE("Choose folder" B_UTF8_ELLIPSIS),
526 		new BMessage(kChooseLocation), 'F'));
527 }
528 
529 
530 void
531 ScreenshotWindow::_AddItemToPathMenu(const char* path, BString& label,
532 	int32 index, bool markItem, uint32 shortcutKey)
533 {
534 	// Make sure that item won't be a duplicate of an existing one
535 	for (int32 i = fOutputPathMenu->CountItems() - 1; i >= 0; --i) {
536 		BMenuItem* menuItem = fOutputPathMenu->ItemAt(i);
537 		BMessage* message = menuItem->Message();
538 		const char* pathFromItem;
539 		if (message != NULL && message->what == kLocationChanged
540 			&& message->FindString("path", &pathFromItem) == B_OK
541 			&& !strcmp(path, pathFromItem)) {
542 
543 			if (markItem) {
544 				fOutputPathMenu->ItemAt(i)->SetMarked(true);
545 				fLastSelectedPath = fOutputPathMenu->ItemAt(i);
546 			}
547 			return;
548 		}
549 	}
550 
551 	BMessage* message = new BMessage(kLocationChanged);
552 	message->AddString("path", path);
553 
554 	fOutputPathMenu->TruncateString(&label, B_TRUNCATE_MIDDLE,
555 		fOutputPathMenu->StringWidth("SomethingLongHere"));
556 
557 	fOutputPathMenu->AddItem(new BMenuItem(label.String(), message,
558 		shortcutKey), index);
559 
560 	if (markItem) {
561 		fOutputPathMenu->ItemAt(index)->SetMarked(true);
562 		fLastSelectedPath = fOutputPathMenu->ItemAt(index);
563 	}
564 }
565 
566 
567 void
568 ScreenshotWindow::_UpdateFilenameSelection()
569 {
570 	fNameControl->MakeFocus(true);
571 	fNameControl->TextView()->Select(0,	fNameControl->TextView()->TextLength()
572 		- fExtension.Length());
573 
574 	fNameControl->TextView()->ScrollToSelection();
575 }
576 
577 
578 void
579 ScreenshotWindow::_SetupTranslatorMenu()
580 {
581 	BMessage message(kImageFormat);
582 	fTranslatorMenu = new BMenu("Please select");
583 	BTranslationUtils::AddTranslationItems(fTranslatorMenu, B_TRANSLATOR_BITMAP,
584 		&message, NULL, NULL, NULL);
585 
586 	fTranslatorMenu->SetLabelFromMarked(true);
587 
588 	if (fTranslatorMenu->ItemAt(0))
589 		fTranslatorMenu->ItemAt(0)->SetMarked(true);
590 
591 	int32 imageFileType;
592 	for (int32 i = 0; i < fTranslatorMenu->CountItems(); ++i) {
593 		BMenuItem* item = fTranslatorMenu->ItemAt(i);
594 		if (item != NULL && item->Message()) {
595 			item->Message()->FindInt32("be:type", &imageFileType);
596 			if (fImageFileType == imageFileType) {
597 				item->SetMarked(true);
598 				MessageReceived(item->Message());
599 				break;
600 			}
601 		}
602 	}
603 }
604 
605 
606 status_t
607 ScreenshotWindow::_SaveScreenshot()
608 {
609 	if (!fScreenshot || !fLastSelectedPath)
610 		return B_ERROR;
611 
612 	BPath path(_GetDirectory());
613 
614 	if (path == NULL)
615 		return B_ERROR;
616 
617 	path.Append(fNameControl->Text());
618 
619 	BEntry entry;
620 	entry.SetTo(path.Path());
621 
622 	if (entry.Exists()) {
623 		BAlert* overwriteAlert = new BAlert(
624 			B_TRANSLATE("overwrite"),
625 			B_TRANSLATE("This file already exists.\n Are you sure you would "
626 				"like to overwrite it?"),
627 			B_TRANSLATE("Cancel"),
628 			B_TRANSLATE("Overwrite"),
629 			NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
630 
631 		overwriteAlert->SetShortcut(0, B_ESCAPE);
632 
633 		if (overwriteAlert->Go() == 0)
634 			return B_CANCELED;
635 	}
636 
637 	return fUtility.Save(fScreenshot, path.Path(), fImageFileType);
638 }
639 
640 
641 void
642 ScreenshotWindow::_ShowSettings(bool activate)
643 {
644 	if (!fSettingsWindow && !activate)
645 		return;
646 
647 	// Find a translator
648 	translator_id translator;
649 	if (fUtility.FindTranslator(fImageFileType, translator) != B_OK)
650 		return;
651 
652 	// Create a window with a configuration view
653 	BView* view;
654 	BRect rect(0, 0, 239, 239);
655 
656 	status_t status = BTranslatorRoster::Default()->MakeConfigurationView(
657 		translator, NULL, &view, &rect);
658 	if (status != B_OK || view == NULL) {
659 		// TODO: proper translation, better error dialog
660 		BAlert* alert = new BAlert(NULL, strerror(status), "OK");
661 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
662 		alert->Go();
663 	} else if (fSettingsWindow != NULL) {
664 		fSettingsWindow->RemoveChild(fSettingsWindow->ChildAt(0));
665 		float width, height;
666 		view->GetPreferredSize(&width, &height);
667 		fSettingsWindow->ResizeTo(width, height);
668 		fSettingsWindow->AddChild(view);
669 		if (activate)
670 			fSettingsWindow->Activate();
671 	} else {
672 		fSettingsWindow = new BWindow(rect,
673 			B_TRANSLATE("Translator Settings"),
674 			B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
675 			B_NOT_ZOOMABLE | B_NOT_RESIZABLE);
676 		fSettingsWindow->AddFilter(new QuitMessageFilter(this));
677 		fSettingsWindow->AddChild(view);
678 		fSettingsWindow->CenterOnScreen();
679 		fSettingsWindow->Show();
680 	}
681 }
682 
683 
684 BString
685 ScreenshotWindow::_FindValidFileName(const char* name)
686 {
687 	BString baseName(name);
688 
689 	if (!fExtension.IsEmpty())
690 		baseName.RemoveLast(fExtension);
691 
692 	if (!fLastSelectedPath)
693 		return baseName;
694 
695 	BPath orgPath(_GetDirectory());
696 	if (orgPath == NULL)
697 		return baseName;
698 
699 	fExtension = fUtility.FileNameExtension(fImageFileType);
700 
701 	BPath outputPath = orgPath;
702 	BString fileName;
703 	fileName << baseName << fExtension;
704 	outputPath.Append(fileName);
705 
706 	if (!BEntry(outputPath.Path()).Exists())
707 		return fileName;
708 
709 	if (baseName.FindFirst(B_TRANSLATE_NOCOLLECT(
710 			fUtility.sDefaultFileNameBase)) == 0)
711 		baseName.SetTo(fUtility.sDefaultFileNameBase);
712 
713 	BEntry entry;
714 	int32 index = 1;
715 
716 	do {
717 		fileName = "";
718 		fileName << baseName << index++ << fExtension;
719 		outputPath.SetTo(orgPath.Path());
720 		outputPath.Append(fileName);
721 		entry.SetTo(outputPath.Path());
722 	} while (entry.Exists());
723 
724 	return fileName;
725 }
726 
727 
728 BPath
729 ScreenshotWindow::_GetDirectory()
730 {
731 	BPath path;
732 
733 	BMessage* message = fLastSelectedPath->Message();
734 	const char* stringPath;
735 	if (message && message->FindString("path", &stringPath) == B_OK)
736 		path.SetTo(stringPath);
737 
738 	return path;
739 }
740 
741 
742 void
743 ScreenshotWindow::_ReadSettings()
744 {
745 	BMessage settings;
746 
747 	BPath settingsPath;
748 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath) != B_OK)
749 		return;
750 
751 	settingsPath.Append("Screenshot_settings");
752 
753 	BFile file(settingsPath.Path(), B_READ_ONLY);
754 	if (file.InitCheck() == B_OK)
755 		settings.Unflatten(&file);
756 
757 	if (settings.FindInt32("type", &fImageFileType) != B_OK)
758 		fImageFileType = B_PNG_FORMAT;
759 	settings.FindBool("includeBorder", &fIncludeBorder);
760 	settings.FindBool("includeCursor", &fIncludeCursor);
761 	settings.FindBool("grabActiveWindow", &fGrabActiveWindow);
762 	settings.FindInt64("delay", &fDelay);
763 	settings.FindString("outputFilename", &fOutputFilename);
764 
765 	_SetupOutputPathMenu(settings);
766 }
767 
768 
769 void
770 ScreenshotWindow::_WriteSettings()
771 {
772 	if (fDelayControl)
773 		fDelay = (atoi(fDelayControl->Text()) * 1000000) + 50000;
774 
775 	BMessage settings;
776 
777 	settings.AddInt32("type", fImageFileType);
778 	settings.AddBool("includeBorder", fIncludeBorder);
779 	settings.AddBool("includeCursor", fIncludeCursor);
780 	settings.AddBool("grabActiveWindow", fGrabActiveWindow);
781 	settings.AddInt64("delay", fDelay);
782 	settings.AddString("outputFilename", fOutputFilename);
783 
784 	BString path;
785 	int32 count = fOutputPathMenu->CountItems();
786 	if (count > 5) {
787 		for (int32 i = count - 3; i > count - 8 && i > 2; --i) {
788 			BMenuItem* item = fOutputPathMenu->ItemAt(i);
789 			if (item) {
790 				BMessage* msg = item->Message();
791 				if (msg && msg->FindString("path", &path) == B_OK)
792 					settings.AddString("path", path.String());
793 			}
794 		}
795 	}
796 
797 	if (fLastSelectedPath) {
798 		BMessage* msg = fLastSelectedPath->Message();
799 		if (msg && msg->FindString("path", &path) == B_OK)
800 			settings.AddString("lastSelectedPath", path.String());
801 	}
802 
803 	BPath settingsPath;
804 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath) != B_OK)
805 		return;
806 	settingsPath.Append("Screenshot_settings");
807 
808 	BFile file(settingsPath.Path(), B_CREATE_FILE | B_ERASE_FILE
809 		| B_WRITE_ONLY);
810 	if (file.InitCheck() == B_OK) {
811 		ssize_t size;
812 		settings.Flatten(&file, &size);
813 	}
814 }
815