1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24 */
25
26 namespace juce
27 {
28
29 class FileChooserDialogBox::ContentComponent : public Component
30 {
31 public:
ContentComponent(const String & name,const String & desc,FileBrowserComponent & chooser)32 ContentComponent (const String& name, const String& desc, FileBrowserComponent& chooser)
33 : Component (name),
34 chooserComponent (chooser),
35 okButton (chooser.getActionVerb()),
36 cancelButton (TRANS ("Cancel")),
37 newFolderButton (TRANS ("New Folder")),
38 instructions (desc)
39 {
40 addAndMakeVisible (chooserComponent);
41
42 addAndMakeVisible (okButton);
43 okButton.addShortcut (KeyPress (KeyPress::returnKey));
44
45 addAndMakeVisible (cancelButton);
46 cancelButton.addShortcut (KeyPress (KeyPress::escapeKey));
47
48 addChildComponent (newFolderButton);
49
50 setInterceptsMouseClicks (false, true);
51 }
52
paint(Graphics & g)53 void paint (Graphics& g) override
54 {
55 text.draw (g, getLocalBounds().reduced (6)
56 .removeFromTop ((int) text.getHeight()).toFloat());
57 }
58
resized()59 void resized() override
60 {
61 const int buttonHeight = 26;
62
63 auto area = getLocalBounds();
64
65 text.createLayout (getLookAndFeel().createFileChooserHeaderText (getName(), instructions),
66 (float) getWidth() - 12.0f);
67
68 area.removeFromTop (roundToInt (text.getHeight()) + 10);
69
70 chooserComponent.setBounds (area.removeFromTop (area.getHeight() - buttonHeight - 20));
71 auto buttonArea = area.reduced (16, 10);
72
73 okButton.changeWidthToFitText (buttonHeight);
74 okButton.setBounds (buttonArea.removeFromRight (okButton.getWidth() + 16));
75
76 buttonArea.removeFromRight (16);
77
78 cancelButton.changeWidthToFitText (buttonHeight);
79 cancelButton.setBounds (buttonArea.removeFromRight (cancelButton.getWidth()));
80
81 newFolderButton.changeWidthToFitText (buttonHeight);
82 newFolderButton.setBounds (buttonArea.removeFromLeft (newFolderButton.getWidth()));
83 }
84
85 FileBrowserComponent& chooserComponent;
86 TextButton okButton, cancelButton, newFolderButton;
87 String instructions;
88 TextLayout text;
89 };
90
91 //==============================================================================
FileChooserDialogBox(const String & name,const String & instructions,FileBrowserComponent & chooserComponent,bool shouldWarn,Colour backgroundColour,Component * parentComp)92 FileChooserDialogBox::FileChooserDialogBox (const String& name,
93 const String& instructions,
94 FileBrowserComponent& chooserComponent,
95 bool shouldWarn,
96 Colour backgroundColour,
97 Component* parentComp)
98 : ResizableWindow (name, backgroundColour, parentComp == nullptr),
99 warnAboutOverwritingExistingFiles (shouldWarn)
100 {
101 content = new ContentComponent (name, instructions, chooserComponent);
102 setContentOwned (content, false);
103
104 setResizable (true, true);
105 setResizeLimits (300, 300, 1200, 1000);
106
107 content->okButton.onClick = [this] { okButtonPressed(); };
108 content->cancelButton.onClick = [this] { closeButtonPressed(); };
109 content->newFolderButton.onClick = [this] { createNewFolder(); };
110
111 content->chooserComponent.addListener (this);
112
113 FileChooserDialogBox::selectionChanged();
114
115 if (parentComp != nullptr)
116 parentComp->addAndMakeVisible (this);
117 else
118 setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
119 }
120
~FileChooserDialogBox()121 FileChooserDialogBox::~FileChooserDialogBox()
122 {
123 content->chooserComponent.removeListener (this);
124 }
125
126 //==============================================================================
127 #if JUCE_MODAL_LOOPS_PERMITTED
show(int w,int h)128 bool FileChooserDialogBox::show (int w, int h)
129 {
130 return showAt (-1, -1, w, h);
131 }
132
showAt(int x,int y,int w,int h)133 bool FileChooserDialogBox::showAt (int x, int y, int w, int h)
134 {
135 if (w <= 0) w = getDefaultWidth();
136 if (h <= 0) h = 500;
137
138 if (x < 0 || y < 0)
139 centreWithSize (w, h);
140 else
141 setBounds (x, y, w, h);
142
143 const bool ok = (runModalLoop() != 0);
144 setVisible (false);
145 return ok;
146 }
147 #endif
148
centreWithDefaultSize(Component * componentToCentreAround)149 void FileChooserDialogBox::centreWithDefaultSize (Component* componentToCentreAround)
150 {
151 centreAroundComponent (componentToCentreAround, getDefaultWidth(), 500);
152 }
153
getDefaultWidth() const154 int FileChooserDialogBox::getDefaultWidth() const
155 {
156 if (auto* previewComp = content->chooserComponent.getPreviewComponent())
157 return 400 + previewComp->getWidth();
158
159 return 600;
160 }
161
162 //==============================================================================
closeButtonPressed()163 void FileChooserDialogBox::closeButtonPressed()
164 {
165 setVisible (false);
166 }
167
selectionChanged()168 void FileChooserDialogBox::selectionChanged()
169 {
170 content->okButton.setEnabled (content->chooserComponent.currentFileIsValid());
171
172 content->newFolderButton.setVisible (content->chooserComponent.isSaveMode()
173 && content->chooserComponent.getRoot().isDirectory());
174 }
175
fileDoubleClicked(const File &)176 void FileChooserDialogBox::fileDoubleClicked (const File&)
177 {
178 selectionChanged();
179 content->okButton.triggerClick();
180 }
181
fileClicked(const File &,const MouseEvent &)182 void FileChooserDialogBox::fileClicked (const File&, const MouseEvent&) {}
browserRootChanged(const File &)183 void FileChooserDialogBox::browserRootChanged (const File&) {}
184
okToOverwriteFileCallback(int result,FileChooserDialogBox * box)185 void FileChooserDialogBox::okToOverwriteFileCallback (int result, FileChooserDialogBox* box)
186 {
187 if (result != 0 && box != nullptr)
188 box->exitModalState (1);
189 }
190
okButtonPressed()191 void FileChooserDialogBox::okButtonPressed()
192 {
193 if (warnAboutOverwritingExistingFiles
194 && content->chooserComponent.isSaveMode()
195 && content->chooserComponent.getSelectedFile(0).exists())
196 {
197 AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
198 TRANS("File already exists"),
199 TRANS("There's already a file called: FLNM")
200 .replace ("FLNM", content->chooserComponent.getSelectedFile(0).getFullPathName())
201 + "\n\n"
202 + TRANS("Are you sure you want to overwrite it?"),
203 TRANS("Overwrite"),
204 TRANS("Cancel"),
205 this,
206 ModalCallbackFunction::forComponent (okToOverwriteFileCallback, this));
207 }
208 else
209 {
210 exitModalState (1);
211 }
212 }
213
createNewFolderCallback(int result,FileChooserDialogBox * box,Component::SafePointer<AlertWindow> alert)214 void FileChooserDialogBox::createNewFolderCallback (int result, FileChooserDialogBox* box,
215 Component::SafePointer<AlertWindow> alert)
216 {
217 if (result != 0 && alert != nullptr && box != nullptr)
218 {
219 alert->setVisible (false);
220 box->createNewFolderConfirmed (alert->getTextEditorContents ("Folder Name"));
221 }
222 }
223
createNewFolder()224 void FileChooserDialogBox::createNewFolder()
225 {
226 auto parent = content->chooserComponent.getRoot();
227
228 if (parent.isDirectory())
229 {
230 auto* aw = new AlertWindow (TRANS("New Folder"),
231 TRANS("Please enter the name for the folder"),
232 AlertWindow::NoIcon, this);
233
234 aw->addTextEditor ("Folder Name", String(), String(), false);
235 aw->addButton (TRANS("Create Folder"), 1, KeyPress (KeyPress::returnKey));
236 aw->addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey));
237
238 aw->enterModalState (true,
239 ModalCallbackFunction::forComponent (createNewFolderCallback, this,
240 Component::SafePointer<AlertWindow> (aw)),
241 true);
242 }
243 }
244
createNewFolderConfirmed(const String & nameFromDialog)245 void FileChooserDialogBox::createNewFolderConfirmed (const String& nameFromDialog)
246 {
247 auto name = File::createLegalFileName (nameFromDialog);
248
249 if (! name.isEmpty())
250 {
251 auto parent = content->chooserComponent.getRoot();
252
253 if (! parent.getChildFile (name).createDirectory())
254 AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
255 TRANS ("New Folder"),
256 TRANS ("Couldn't create the folder!"));
257
258 content->chooserComponent.refresh();
259 }
260 }
261
262 } // namespace juce
263