xref: /haiku/src/apps/screenshot/ScreenshotWindow.cpp (revision f4352262989a54c7e18d0ad82861279088525ab3)
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:
QuitMessageFilter(BWindow * window)72 	QuitMessageFilter(BWindow* window)
73 		:
74 		BMessageFilter((uint32)B_QUIT_REQUESTED),
75 		fWindow(window)
76 	{
77 	}
78 
Filter(BMessage * message,BHandler ** target)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:
~DirectoryRefFilter()95 	virtual ~DirectoryRefFilter()
96 	{
97 	}
98 
Filter(const entry_ref * ref,BNode * node,struct stat_beos * stat,const char * filetype)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 
ScreenshotWindow(const Utility & utility,bool silent,bool clipboard)110 ScreenshotWindow::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 
~ScreenshotWindow()252 ScreenshotWindow::~ScreenshotWindow()
253 {
254 	if (fOutputPathPanel)
255 		delete fOutputPathPanel->RefFilter();
256 
257 	delete fOutputPathPanel;
258 	delete fScreenshot;
259 }
260 
261 
262 void
MessageReceived(BMessage * message)263 ScreenshotWindow::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 
384 void
Quit()385 ScreenshotWindow::Quit()
386 {
387 	if (fUtility.wholeScreen != NULL)
388 		_WriteSettings();
389 	BWindow::Quit();
390 }
391 
392 
393 void
_NewScreenshot(bool silent,bool clipboard,bool ignoreDelay)394 ScreenshotWindow::_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 
445 void
_UpdatePreviewPanel()446 ScreenshotWindow::_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 
473 void
_DisallowChar(BTextView * textView)474 ScreenshotWindow::_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 
484 void
_SetupOutputPathMenu(const BMessage & settings)485 ScreenshotWindow::_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 
535 void
_AddItemToPathMenu(const char * path,BString & label,int32 index,bool markItem,uint32 shortcutKey)536 ScreenshotWindow::_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 
572 void
_UpdateFilenameSelection()573 ScreenshotWindow::_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 
583 void
_SetupTranslatorMenu()584 ScreenshotWindow::_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 
611 void
_DisplaySaveError(BString _message)612 ScreenshotWindow::_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 
626 status_t
_SaveScreenshot()627 ScreenshotWindow::_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 
690 void
_ShowSettings(bool activate)691 ScreenshotWindow::_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 
733 BString
_FindValidFileName(const char * name)734 ScreenshotWindow::_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 
777 BPath
_GetDirectory()778 ScreenshotWindow::_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 
791 void
_ReadSettings()792 ScreenshotWindow::_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 
818 void
_WriteSettings()819 ScreenshotWindow::_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