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