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