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