1 /*
2   FXiTe - The Free eXtensIble Text Editor
3   Copyright (c) 2009-2012 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 
20 #include <unistd.h>
21 #include <cerrno>
22 #include <fx.h>
23 #include <fxkeys.h>
24 
25 #include "scidoc_util.h"
26 #include "export.h"
27 #include "prefs_base.h"
28 #include "compat.h"
29 #include "filedlg.h"
30 #include "doctabs.h"
31 
32 #include "intl.h"
33 #include "filer.h"
34 
35 
36 FXIMPLEMENT(FileDialogs, FXObject, NULL, 0);
37 
38 
FileDialogs(FXObject * tgt,FXSelector sel)39 FileDialogs::FileDialogs(FXObject*tgt, FXSelector sel):FXObject()
40 {
41   user_data=NULL;
42   target=tgt;
43   message=sel;
44 }
45 
46 #ifdef WIN32
ReadShortcut(FXWindow * w,FXString & filename)47 bool FileDialogs::ReadShortcut(FXWindow*w, FXString &filename)
48 {
49   return FileDlg::ReadShortcut(w,filename);
50 }
51 #endif
52 
GetPathForDlg(SciDoc * sci,FXString & path)53 static void GetPathForDlg(SciDoc*sci, FXString &path)
54 {
55   if (SciDocUtils::Filename(sci).empty()) {
56     path=FXSystem::getCurrentDirectory();
57   } else {
58     path=FXPath::directory(SciDocUtils::Filename(sci));
59   }
60 #ifndef WIN32
61   path.append(PATHSEP);
62 #endif
63 }
64 
65 
66 
67 #define prefs SettingsBase::instance()
68 
SaveFileAs(SciDoc * sci,bool as_itself,const FXString & suggestion)69 bool FileDialogs::SaveFileAs(SciDoc*sci, bool as_itself, const FXString &suggestion)
70 {
71   FXString result="";
72   FXString path;
73   GetPathForDlg(sci,path);
74   FileDlg dlg((FXMainWindow*)target,_("Save file as"));
75   dlg.setPatternList(prefs->FileFilters);
76   dlg.setCurrentPattern(prefs->FileFilterIndex);
77   if (!suggestion.empty()) { dlg.setFilename(suggestion); }
78   dlg.setDirectory(path);
79   if (dlg.execute(PLACEMENT_OWNER)) {
80     result=dlg.getFilename();
81     prefs->FileFilterIndex=prefs->KeepFileFilter?dlg.getCurrentPattern():0;
82   }
83   if (!result.empty()) {
84     if (FXStat::exists(result)) {
85       switch (FXMessageBox::question((FXMainWindow*)target, MBOX_YES_NO_CANCEL, _("Overwrite?"),
86                 "%s:\n%s\n\n%s", _("File exists"), result.text(), _("Do you want to replace it?"))
87              ) {
88         case MBOX_CLICKED_YES: { break; }
89         case MBOX_CLICKED_NO: { return SaveFileAs(sci); }
90         case MBOX_CLICKED_CANCEL: {return false;}
91       }
92     }
93     return SaveFile(sci,result,as_itself);
94   }
95   return false;
96 }
97 
98 
99 
Export(SciDoc * sci,const char * title,const char * patts,const char * ext,ExportFunc func,const char * filename)100 bool FileDialogs::Export(SciDoc*sci,
101   const char* title, const char*patts, const char*ext, ExportFunc func, const char*filename)
102 {
103   FXString path;
104   FXString saveName="";
105   FXString tmp;
106   if (!sci) { return false; }
107   if (filename) {
108     saveName=filename;
109   } else {
110     FileDlg dlg((FXMainWindow*)target,title);
111     if (SciDocUtils::Filename(sci).empty()) {
112       path=FXSystem::getCurrentDirectory();
113       tmp=_("Untitled");
114       tmp.append(ext);
115       dlg.setFilename(tmp);
116     } else {
117       path=FXPath::directory(SciDocUtils::Filename(sci));
118       tmp=FXPath::name(SciDocUtils::Filename(sci));
119       tmp.substitute('.','_');
120       tmp.append(ext);
121       dlg.setFilename(tmp);
122     }
123 #ifndef WIN32
124     path.append(PATHSEP);
125 #endif
126     dlg.setPatternList(patts);
127     dlg.setDirectory(path);
128     if (dlg.execute(PLACEMENT_OWNER)) {
129       saveName=dlg.getFilename();
130     }
131   }
132   if (!saveName.empty()) {
133     if (FXStat::exists(saveName)) {
134       switch (FXMessageBox::question((FXMainWindow*)target, MBOX_YES_NO_CANCEL, _("Overwrite?"),
135                 "%s:\n%s\n\n%s", _("File exists"), saveName.text(), _("Do you want to replace it?")
136                 )) {
137         case MBOX_CLICKED_YES: { break; }
138         case MBOX_CLICKED_NO: { return Export(sci,title,patts,ext,func); }
139         case MBOX_CLICKED_CANCEL: {return false;}
140       }
141     }
142     FILE *fp = fopen(saveName.text(), "wt");
143     if (fp) {
144       func(sci,fp);
145       if (fclose(fp)==0) { return true; }
146     }
147     FXMessageBox::error((FXMainWindow*)target,MBOX_OK,
148       _("Export error"), "%s:\n%s\n\n%s", _("Failed to save file"), saveName.text(), SystemErrorStr());
149   }
150   return false;
151 }
152 
153 
154 
ExportHtml(SciDoc * sci,const char * filename)155 bool FileDialogs::ExportHtml(SciDoc*sci, const char* filename) {
156   return Export(sci,
157            _("Export to HTML"), _("HTML files (*.html;*.htm)\nAll files (*)"), ".html", SaveToHTML, filename);
158 }
159 
160 
161 
ExportPdf(SciDoc * sci,const char * filename)162 bool FileDialogs::ExportPdf(SciDoc*sci, const char* filename) {
163   return Export(sci, _("Export to PDF"),
164             _("Portable Document Format (*.pdf)\nAll files (*)"), ".pdf", SaveToPDF, filename);
165 }
166 
167 
168 
SaveFile(SciDoc * sci,const FXString & filename,bool as_itself)169 bool FileDialogs::SaveFile(SciDoc*sci, const FXString &filename, bool as_itself)
170 {
171   if (filename.empty()) { return SaveFileAs(sci,as_itself); }
172   if (SciDocUtils::SaveToFile(sci,filename.text(),as_itself)) {
173     if (target && message) { target->handle(this, FXSEL(SEL_COMMAND,message), (void*)sci); }
174     return true;
175   } else {
176     FXMessageBox::error( (FXMainWindow*)target,MBOX_OK,_("Error saving file"), "%s:\n%s\n%s", _("Failed to save file"),
177                            filename.text(), SciDocUtils::GetLastError(sci).text() );
178     return false;
179   }
180 }
181 
182 
183 
TryClose(SciDoc * sci,const char * alt)184 bool FileDialogs::TryClose(SciDoc*sci, const char*alt)
185 {
186   if (!sci) return false;
187   if (SciDocUtils::Dirty(sci)) {
188     switch ( FXMessageBox::question((FXMainWindow*)target, MBOX_YES_NO_CANCEL, _("Save file?"),
189                 _("File has been modified:\n%s\n\nSave before closing?"),
190                 SciDocUtils::Filename(sci).empty()?(alt?alt:_("Untitled")):SciDocUtils::Filename(sci).text())
191            )
192     {
193       case MBOX_CLICKED_YES: {
194         if (!SaveFile(sci,SciDocUtils::Filename(sci))) { return false; }
195         break;
196       }
197       case MBOX_CLICKED_NO: {
198         break;
199       }
200       case MBOX_CLICKED_CANCEL: {
201         return false;
202         break;
203       }
204       default:{return false;}
205     }
206   }
207   return true;
208 }
209 
210 
211 
GetOpenFilenames(SciDoc * sci,FXString * & filenames,bool multi)212 bool FileDialogs::GetOpenFilenames(SciDoc*sci, FXString* &filenames, bool multi)
213 {
214   const char* caption=multi?_("Select files to open"):_("Select file to open");
215   FXString path="";
216   GetPathForDlg(sci,path);
217   FileDlg dlg((FXMainWindow*)target, caption, multi);
218   dlg.setPatternList(prefs->FileFilters);
219   dlg.setCurrentPattern(prefs->FileFilterIndex);
220   dlg.setDirectory(path);
221   dlg.setSelectMode(multi&&prefs->FileOpenMulti?SELECTFILE_MULTIPLE:SELECTFILE_EXISTING);
222   if (dlg.execute(PLACEMENT_OWNER)) {
223     filenames=dlg.getFilenames();
224     if ( (dlg.getSelectMode()==SELECTFILE_EXISTING) && (!filenames) ) {
225       if (!dlg.getFilename().empty()) {
226         filenames=new FXString[2];
227         filenames[0]=dlg.getFilename();
228         filenames[1]="";
229       } else { filenames=NULL; }
230     }
231     prefs->FileFilterIndex=prefs->KeepFileFilter?dlg.getCurrentPattern():0;
232     if (multi) { prefs->FileOpenMulti=dlg.MultiMode(); }
233   } else { filenames=NULL; }
234   return filenames!=NULL;
235 }
236 
237 
238 
GetOpenTagFilename(SciDoc * sci,FXString & filename)239 bool FileDialogs::GetOpenTagFilename(SciDoc*sci, FXString &filename)
240 {
241   const char* caption=_("Open tags file");
242   FXString path="";
243   GetPathForDlg(sci,path);
244   FileDlg dlg((FXMainWindow*)target, caption);
245   dlg.setPatternList(_("Tag Files (TAGS,tags)\nAll Files (*)"));
246   dlg.setDirectory(path);
247   dlg.setSelectMode(SELECTFILE_EXISTING);
248   if (dlg.execute(PLACEMENT_OWNER)) {
249     filename=dlg.getFilename().text();
250   } else filename="";
251   return (!filename.empty());
252 }
253 
254 
255 
AskReload(SciDoc * sci)256 bool FileDialogs::AskReload(SciDoc*sci) {
257   if (SciDocUtils::Dirty(sci)) {
258     if ( FXMessageBox::warning((FXMainWindow*)target,
259            MBOX_YES_NO, _("Unsaved changes"), "%s - \n%s\n\n%s",
260            _("Existing buffer has unsaved changes"),
261            _("These changes will be lost if you proceed!"),
262            _("Proceed with reloading?")
263        )!=MBOX_CLICKED_YES ) { return false; }
264   }
265   return SciDocUtils::Reload(sci);
266 }
267 
268 
269 
AskReloadForExternalChanges(SciDoc * sci)270 bool FileDialogs::AskReloadForExternalChanges(SciDoc*sci)
271 {
272   if ( FXMessageBox::question((FXMainWindow*)target, MBOX_YES_NO, _("File changed"),
273          "%s\n%s\n\n%s",
274          SciDocUtils::Filename(sci).text(),
275          _("was modified externally."),
276          _("Reload from disk?")
277        )==MBOX_CLICKED_YES ) { return AskReload(sci); } else { return false; }
278 }
279 
280 
281 
AskSaveMissingFile(SciDoc * sci)282 bool FileDialogs::AskSaveMissingFile(SciDoc*sci)
283 {
284   return FXMessageBox::question((FXMainWindow*)target, MBOX_YES_NO, _("File status error"),
285                "%s:\n%s\n(%s)\n\n%s",
286                _("Error checking the status of"),
287                SciDocUtils::Filename(sci).text(), SciDocUtils::GetLastError(sci).text(),
288                _("Save to disk now?")
289                )==MBOX_CLICKED_YES;
290 }
291 
292 
293 
AskSaveModifiedCommand(SciDoc * sci,const FXString & script)294 bool FileDialogs::AskSaveModifiedCommand(SciDoc*sci, const FXString &script)
295 {
296   if (SciDocUtils::Dirty(sci) && (SciDocUtils::Filename(sci)==script)) {
297     switch (FXMessageBox::warning((FXMainWindow*)target,
298           MBOX_YES_NO_CANCEL,_("Unsaved changes"),
299           _("The disk file for the \"%s\" command is currently\n"
300             " open in the editor, and has unsaved changes.\n\n"
301             "  Save the file before continuing?"), SciDocUtils::Filename(sci).text()))
302     {
303       case MBOX_CLICKED_YES: { return SaveFile(sci,SciDocUtils::Filename(sci)); }
304       case MBOX_CLICKED_NO: { return true; }
305       default: { return false; }
306     }
307   } else { return true; }
308 }
309 
310 
311 
312 class WkDirDlg: public FXDirDialog {
313 private:
314   class DirSel: public FXDirSelector {
315     public:
list()316     FXDirList* list()  { return dirbox; }
317   };
318 public:
WkDirDlg(FXWindow * win)319   WkDirDlg(FXWindow* win):FXDirDialog(win, _("Set Working Directory")) {
320     setHeight(420);
321     setDirectory(FXSystem::getCurrentDirectory()+PATHSEP);
322   }
setDirectory(const FXString & path)323   void setDirectory(const FXString& path) {
324     FXDirDialog::setDirectory((FXPath::simplify(path)));
325     if (FXPath::isTopDirectory(getDirectory())) {
326       FXDirList*list=((DirSel*)dirbox)->list();
327       list->expandTree(list->getFirstItem());
328     }
329   }
execute(FXuint placement=PLACEMENT_CURSOR)330   virtual FXuint execute(FXuint placement=PLACEMENT_CURSOR) {
331     FXuint rv=FXDirDialog::execute(placement);
332     if (rv) {
333       FXSystem::setCurrentDirectory(getDirectory());
334     }
335     return rv;
336   }
337 };
338 
339 
340 
SetWorkingDirectory(FXWindow * w)341 void FileDialogs::SetWorkingDirectory(FXWindow*w)
342 {
343   WkDirDlg(w).execute(PLACEMENT_OWNER);
344 }
345 
346 
347 
348 class MyMessageBox: public FXMessageBox {
349 public:
execute(FXuint placement)350   virtual FXuint execute(FXuint placement) {
351     create();
352     show(placement);
353     getApp()->refresh();
354     WaitForWindowFocus(this,3000);
355     return getApp()->runModalFor(this);
356   }
MyMessageBox(FXWindow * w,const FXString & c,const FXString & t,FXIcon * i,FXuint o)357   MyMessageBox(FXWindow* w,const FXString&c,const FXString&t,FXIcon*i,FXuint o):FXMessageBox(w,c,t,i,o){}
358 
question(FXWindow * owner,FXuint opts,const char * caption,const char * message,...)359   static FXuint question(FXWindow* owner,FXuint opts,const char* caption,const char* message,...){
360     FXGIFIcon icon(owner->getApp(),NULL);
361     va_list arguments;
362     va_start(arguments,message);
363 #ifdef FOX_1_6
364     const FXString fmt=FXStringVFormat(message,arguments);
365 #else
366     const FXString fmt=FXString::vvalue(message,arguments);
367 #endif
368     MyMessageBox box(owner,caption,fmt,&icon,opts|DECOR_TITLE|DECOR_BORDER);
369     va_end(arguments);
370     return box.execute(PLACEMENT_OWNER);
371   }
372 };
373 
374 
FileExistsOrConfirmCreate(FXMainWindow * w,const FXString & fn)375 bool FileDialogs::FileExistsOrConfirmCreate(FXMainWindow*w, const FXString &fn)
376 {
377   if ((!fn.empty())&&(!FXStat::exists(fn))) {
378     WaitForWindowFocus(w);
379     if (MyMessageBox::question( w, MBOX_YES_NO, _("File not found"),
380           "%s:\n%s\n %s",
381           _("Can't find the file"),
382           fn.text(),
383           _("Would you like to create it?")
384      )==MBOX_CLICKED_YES) {
385        if (!FXStat::exists(fn)) { /* <-maybe someone created it while we were waiting for a response? */
386          FXFile fh(fn, FXFile::Writing);
387          if (!(fh.isOpen() && fh.close())) {
388            FXMessageBox::error(w, MBOX_OK, _("File error"),
389             "%s:\n%s\n%s",
390              _("Failed to create the file"),
391              fn.text(),
392              SystemErrorStr());
393            return false;
394          }
395        }
396     } else { return false; }
397   }
398   return true;
399 }
400 
401