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