1 /*
2 FXiTe - The Free eXtensIble Text Editor
3 Copyright (c) 2009-2011 Jeffrey Pohlmeyer <yetanothergeek@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License version 3 as
7 published by the Free Software Foundation.
8
9 This software is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include <fx.h>
20 #include <fxkeys.h>
21
22 #include "intl.h"
23 #include "filedlg.h"
24
25
26 class MyDirBox: public FXDirBox {
27 public:
fld()28 FXButton*fld() { return field; }
btn()29 FXMenuButton*btn() { return button; }
pn()30 FXPopup*pn() { return pane; }
31 };
32
33
34
35 class FileSel: public FXFileSelector {
36 public:
txtfld()37 FXTextField*txtfld() { return filename; }
fbox()38 FXFileList*fbox() { return filebox; }
nav()39 FXHorizontalFrame* nav() { return navbuttons; }
cncl()40 FXButton* cncl() { return cancel; }
dbox()41 MyDirBox* dbox() { return (MyDirBox*)dirbox; }
icobtn()42 FXButton *icobtn() {
43 for (FXWindow*w=navbuttons->getFirst(); w; w=w->getNext()) {
44 if ((dynamic_cast<FXButton*>(w)!=NULL) && (((FXButton*)w)->getIcon()==iconsicon)) {
45 return (FXButton*)w;
46 }
47 }
48 return NULL;
49 }
ToggleMulti(bool multi)50 void ToggleMulti(bool multi) {
51 // Give some indication that the user can't manually enter file names in multi mode.
52 FXString label_text;
53 if (multi) {
54 FXColor bgcolor=getApp()->getBaseColor();
55 label_text=_("Selected : ");
56 filename->setEditable(false);
57 filename->setBackColor(bgcolor);
58 filename->setHiliteColor(bgcolor);
59 filename->setShadowColor(bgcolor);
60 filename->setBorderColor(bgcolor);
61 filename->setTextColor(getApp()->getForeColor());
62 } else {
63 label_text=_("&File Name:");
64 filename->setEditable(true);
65 filename->setBackColor(getApp()->getBackColor());
66 filename->setHiliteColor(getApp()->getHiliteColor());
67 filename->setShadowColor(getApp()->getShadowColor());
68 filename->setBorderColor(getApp()->getBorderColor());
69 filename->setTextColor(getApp()->getForeColor());
70 }
71 FXLabel*label=dynamic_cast<FXLabel*>(filename->getPrev());
72 if (label) { label->setText(label_text); }
73 }
74 };
75
76
77
78
79 FXDEFMAP(FileDlg) MyFileDlgMap[]={
80 FXMAPFUNC(SEL_COMMAND,FileDlg::ID_TOGGLE_MULTI,FileDlg::onToggleMulti),
81 FXMAPFUNC(SEL_KEYPRESS,0,FileDlg::onKeyPress),
82 };
83
84 FXIMPLEMENT(FileDlg,FXFileDialog,MyFileDlgMap,ARRAYNUMBER(MyFileDlgMap));
85
86
87
88
FileDlg(FXWindow * win,const FXString & caption,bool optmulti)89 FileDlg::FileDlg(FXWindow*win, const FXString&caption, bool optmulti):FXFileDialog(win,caption)
90 {
91 #ifdef WIN32
92 filenames=NULL;
93 #endif
94 ((FileSel*)filebox)->icobtn()->hide();
95 multi_btn=new FXToggleButton( fsel()->nav(),_(" &M "),_(" &M "),NULL,NULL,this,
96 ID_TOGGLE_MULTI,TOGGLEBUTTON_TOOLBAR|FRAME_RAISED );
97 multi_btn->setTipText(_("Enable selection of multiple files"));
98 multi_btn->setAltTipText(_("Enable manual entry of filename"));
99 if (!optmulti) { multi_btn->hide(); }
100 }
101
102
103
create()104 void FileDlg::create()
105 {
106 FXFileDialog::create();
107 switch (getSelectMode()) {
108 case SELECTFILE_ANY: {
109 fsel()->txtfld()->setFocus();
110 break;
111 }
112 case SELECTFILE_MULTIPLE: {
113 fsel()->fbox()->setFocus();
114 fsel()->fbox()->setCurrentItem(0);
115 break;
116 }
117 }
118 }
119
120
121 // Make the F4 key behave like it does with some other "toolkits"
122 // where it drops down the directory selection panel.
123 // Also makes tab-key navigation feel a little more intuitive (at least for me.)
onKeyPress(FXObject * o,FXSelector sel,void * p)124 long FileDlg::onKeyPress(FXObject*o,FXSelector sel,void*p)
125 {
126 FXEvent*ev=(FXEvent*)p;
127 switch (ev->code) {
128 case KEY_F4: {
129 #ifdef FOX_1_6
130 fsel()->dbox()->btn()->handle(fsel()->dbox()->btn(),FXSEL(SEL_COMMAND,ID_POST),NULL);
131 #else
132 fsel()->dbox()->btn()->showMenu(true);
133 #endif
134 fsel()->dbox()->setFocus();
135 fsel()->dbox()->pn()->getFirst()->setFocus();
136 break;
137 }
138 case KEY_Escape: {
139 #ifdef FOX_1_6
140 if (fsel()->dbox()->isPaneShown()) {
141 fsel()->dbox()->btn()->handle(fsel()->dbox()->btn(),FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
142 return 1;
143 }
144 #else
145 if (fsel()->dbox()->btn()->isMenuShown()) {
146 fsel()->dbox()->btn()->showMenu(false);
147 return 1;
148 }
149 #endif
150 break;
151 }
152 case KEY_Return: {
153 #ifdef FOX_1_6
154 if (fsel()->dbox()->isPaneShown()||fsel()->dbox()->fld()->hasFocus()) {
155 fsel()->dbox()->btn()->handle(fsel()->dbox()->btn(),FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
156 setDirectory(fsel()->dbox()->getDirectory());
157 return 1;
158 }
159 #else
160 if (fsel()->dbox()->btn()->isMenuShown()||fsel()->dbox()->fld()->hasFocus()) {
161 fsel()->dbox()->btn()->showMenu(false);
162 setDirectory(fsel()->dbox()->getDirectory());
163 return 1;
164 }
165 #endif
166 break;
167 }
168 case KEY_Tab: {
169 if (fsel()->dbox()->fld()->hasFocus()) {
170 if (ev->state & SHIFTMASK) {
171 fsel()->setFocus();
172 fsel()->txtfld()->setFocus();
173 } else {
174 fsel()->dbox()->getNext()->getNext()->setFocus();
175 }
176 return 1;
177 } else {
178 if (multi_btn->hasFocus()||(multi_btn->getPrev()->hasFocus()&&!multi_btn->shown())) {
179 fsel()->fbox()->setFocus();
180 return 1;
181 } else {
182 if (fsel()->fbox()->hasFocus()) {
183 fsel()->txtfld()->setFocus();
184 return 1;
185 } else {
186 if (fsel()->cncl()->hasFocus()) {
187 fsel()->dbox()->fld()->setFocus();
188 return 1;
189 }
190 }
191 }
192 }
193 break;
194 }
195 case KEY_ISO_Left_Tab: {
196 if (ev->state & SHIFTMASK) {
197 if (fsel()->dbox()->fld()->hasFocus()) {
198 fsel()->cncl()->setFocus();
199 return 1;
200 } else {
201 if (fsel()->fbox()->hasFocus()) {
202 if (multi_btn->shown()) {
203 multi_btn->setFocus();
204 } else {
205 multi_btn->getPrev()->setFocus();
206 }
207 return 1;
208 }
209 }
210 }
211 break;
212 }
213 }
214 return FXFileDialog::handle(o,sel,p);
215 }
216
217
218
onToggleMulti(FXObject * o,FXSelector sel,void * p)219 long FileDlg::onToggleMulti(FXObject*o,FXSelector sel,void*p)
220 {
221 setSelectMode(p?SELECTFILE_MULTIPLE:SELECTFILE_EXISTING);
222 fsel()->txtfld()->setText(FXString::null);
223 fsel()->fbox()->killSelection(true);
224 if (p) {
225 fsel()->fbox()->setFocus();
226 fsel()->fbox()->setCurrentItem(0);
227 } else {
228 fsel()->txtfld()->setFocus();
229 }
230 return 1;
231 }
232
233
234
setSelectMode(FXuint mode)235 void FileDlg::setSelectMode(FXuint mode) {
236 FXFileDialog::setSelectMode(mode);
237 if (multi_btn->shown()) {
238 FXString label_text;
239 if ( mode == SELECTFILE_MULTIPLE ) {
240 fsel()->ToggleMulti(true);
241 multi_btn->setState(true);
242 multi_btn->setBackColor(getApp()->getSelbackColor());
243 multi_btn->setTextColor(getApp()->getSelforeColor());
244 } else {
245 fsel()->ToggleMulti(false);
246 multi_btn->setState(false);
247 multi_btn->setBackColor(getApp()->getBackColor());
248 multi_btn->setTextColor(getApp()->getForeColor());
249 }
250 }
251 }
252
253
254
fsel()255 FileSel*FileDlg::fsel()
256 {
257 return (FileSel*)filebox;
258 }
259
260
261
262 #ifdef WIN32
263
264 extern "C" {
265 int ReadShortcut(char **dst, const char *src);
266 }
267
268
269 // True if file extension is '*.lnk'
270 #define IsLinkExt(s) (FXPath::extension(s).lower()=="lnk")
271
272
273 /*
274 Fox doesn't automatically handle MS-Windows shortcut files, so...
275 Iterate through each filename in the file dialog's getFilenames() list,
276 if any of them are shortcut (*.lnk) files, dereference the link and
277 make the string point to the "real" disk file. If we have multiple files,
278 remove any links that point to a directory. But if we only have one string
279 in the list, and the string is a link pointing to a directory, we will
280 dereference it so the dialog can change into that folder.
281 */
FixupShortcuts(FXWindow * w,FXString * filenames)282 static void FixupShortcuts(FXWindow*w, FXString* filenames)
283 {
284 if (!filenames) return;
285 FXString* fn;
286 FXString* tail=filenames;
287 FXuint count=0;
288 for (fn=filenames; !fn->empty(); fn++) {
289 if (IsLinkExt(*fn)) {
290 char*tmp=NULL;
291 if (ReadShortcut(&tmp, fn->text())) {
292 *fn=tmp;
293 } else {
294 FXMessageBox::error(w,MBOX_OK,_("Error in shortcut"),"%s\n%s",fn->text(),tmp);
295 }
296 free(tmp);
297 }
298 tail=fn;
299 count++;
300 }
301 if (count>1) {
302 for (fn=filenames; !fn->empty(); fn++) {
303 if (FXStat::isDirectory(*fn)) {
304 *fn=tail->text();
305 *tail="";
306 tail--;
307 }
308 }
309 }
310 }
311
312 /*
313 Read the Windows shortcut (*.lnk) file passed in as "filename".
314 If the filename does not end with the *.lnk extension, returns
315 true, the filename parameter is unchanged.
316 If the link cannot be read (e.g. corrupted file) it displays an
317 error dialog describing the reason for the failure and returns
318 false, the filename parameter is unchanged.
319 If reading of the link is successful, it returns true and the
320 "filename" parameter is modified and will contain the name of
321 the file that the shortcut points to.
322 */
ReadShortcut(FXWindow * w,FXString & filename)323 bool FileDlg::ReadShortcut(FXWindow*w, FXString &filename)
324 {
325 bool rv=true;
326 if (IsLinkExt(filename)) {
327 char*tmp=NULL;
328 if (::ReadShortcut(&tmp, filename.text())) {
329 filename=FXPath::simplify(FXPath::absolute(tmp));
330 } else {
331 FXMessageBox::error(w,MBOX_OK,_("Error in shortcut"),"%s\n%s",filename.text(),tmp);
332 rv=false;
333 }
334 free(tmp);
335 }
336 return rv;
337 }
338
339
340
DeleteFilenames()341 void FileDlg::DeleteFilenames()
342 {
343 if (filenames&&own_filenames) {
344 delete[] filenames;
345 filenames=NULL;
346 }
347 }
348
349
350
~FileDlg()351 FileDlg::~FileDlg()
352 {
353 DeleteFilenames();
354 }
355
356
357
getFilenames()358 FXString*FileDlg::getFilenames()
359 {
360 own_filenames=false;
361 return filenames;
362 }
363
364
365
getFilename()366 FXString FileDlg::getFilename() {
367 if (getSelectMode()==SELECTFILE_ANY) {
368 FXString fn=FXFileDialog::getFilename();
369 if (ReadShortcut(getShell(), fn)) {
370 FXFileDialog::setFilename(fn);
371 }
372 return FXFileDialog::getFilename();
373 } else {
374 return filenames ? (*filenames) : FXFileDialog::getFilename();
375 }
376 }
377
378
379
execute(FXuint placement)380 FXuint FileDlg::execute(FXuint placement) {
381 DeleteFilenames();
382 FXuint rv=FXFileDialog::execute(placement);
383 if (rv) {
384 filenames = FXFileDialog::getFilenames();
385 own_filenames=true;
386 if (filenames) {
387 if (getSelectMode()!=SELECTFILE_MULTIPLE) {
388 filenames[0]=FXFileDialog::getFilename();
389 }
390 FixupShortcuts(getShell(), filenames);
391 if (FXStat::isDirectory(filenames->text())) {
392 setFilename("*");
393 filenames->append("\\.");
394 setDirectory(filenames->text());
395 DeleteFilenames();
396 return execute(placement);
397 }
398 }
399 }
400 return rv;
401 }
402 #endif
403
404