UNIXworkcode

1 /******************************************************************************* 2 * * 3 * Getfiles.c -- File Interface Routines * 4 * * 5 * Copyright (C) 1999 Mark Edel * 6 * * 7 * This is free software; you can redistribute it and/or modify it under the * 8 * terms of the GNU General Public License as published by the Free Software * 9 * Foundation; either version 2 of the License, or (at your option) any later * 10 * version. In addition, you may distribute version of this program linked to * 11 * Motif or Open Motif. See README for details. * 12 * * 13 * This software is distributed in the hope that it will be useful, but WITHOUT * 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * 16 * for more details. * 17 * * 18 * You should have received a copy of the GNU General Public License along with * 19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple * 20 * Place, Suite 330, Boston, MA 02111-1307 USA * 21 * * 22 * Nirvana Text Editor * 23 * May 23, 1991 * 24 * * 25 * Written by Donna Reid * 26 * * 27 * modified 11/5/91 by JMK: integrated changes made by M. Edel; updated for * 28 * destroy widget problem (took out ManageModalDialog * 29 * call; added comments. * 30 * 10/1/92 by MWE: Added help dialog and fixed a few bugs * 31 * 4/7/93 by DR: Port to VMS * 32 * 6/1/93 by JMK: Integrate Port and changes by MWE to make * 33 * directories "sticky" and a fix to prevent opening * 34 * a directory when no filename was specified * 35 * 6/24/92 by MWE: Made filename list and directory list typeable, * 36 * set initial focus to filename list * 37 * 6/25/93 by JMK: Fix memory leaks found by Purify. * 38 * * 39 * Included are two routines written using Motif for accessing files: * 40 * * 41 * GetExistingFilename presents a FileSelectionBox dialog where users can * 42 * choose an existing file to open. * 43 * * 44 *******************************************************************************/ 45 46 #ifdef HAVE_CONFIG_H 47 #include "../config.h" 48 #endif 49 50 #include "getfiles.h" 51 #include "fileUtils.h" 52 #include "misc.h" 53 #include "nedit_malloc.h" 54 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <ctype.h> 59 #include <sys/types.h> 60 #include <dirent.h> 61 62 #include <unistd.h> 63 #include <fcntl.h> 64 #include <dirent.h> 65 #include <sys/param.h> 66 #include <sys/stat.h> 67 68 #include <X11/keysym.h> 69 #include <Xm/Xm.h> 70 #include <Xm/XmAll.h> 71 72 #include "utils.h" 73 #include "fileUtils.h" 74 75 #include "filedialog.h" 76 77 #ifdef HAVE_DEBUG_H 78 #include "../debug.h" 79 #endif 80 81 #define MAX_ARGS 20 /* Maximum number of X arguments */ 82 #define PERMS 0666 /* UNIX file permission, RW for owner, 83 group, world */ 84 #define MAX_LIST_KEYSTROKES 100 /* Max # of keys user can type to 85 a file list */ 86 #define MAX_LIST_KESTROKE_WAIT 2000 /* Allowable delay in milliseconds 87 between characters typed to a list 88 before starting over (throwing 89 out the accumulated characters */ 90 91 #define SET_ONE_RSRC(widget, name, newValue) \ 92 { \ 93 static Arg tmpargs[1] = {{name, (XtArgVal)0}}; \ 94 tmpargs[0].value = (XtArgVal)newValue; \ 95 XtSetValues(widget, tmpargs, 1); \ 96 } 97 98 enum yesNoValues {ynNone, ynYes, ynNo}; 99 100 /* Saved default directory and pattern from last successful call */ 101 static XmString DefaultDirectory = NULL; 102 103 static char* DefaultDirectoryStr = NULL; 104 105 /* Local Callback Routines and variables */ 106 107 static void createYesNoDialog(Widget parent); 108 static void createErrorDialog(Widget parent); 109 static int doYesNoDialog(const char *msg); 110 static void doErrorDialog(const char *errorString, const char *filename); 111 static void errorOKCB(Widget w, caddr_t client_data, caddr_t call_data); 112 static void yesNoOKCB(Widget w, caddr_t client_data, caddr_t call_data); 113 static void yesNoCancelCB(Widget w, caddr_t client_data, caddr_t call_data); 114 115 static Widget YesNoDialog; /* "Overwrite?" dialog widget */ 116 static int YesNoResult; /* Result of overwrite dialog */ 117 static Widget ErrorDialog; /* Dialog widget for error msgs */ 118 static int ErrorDone; /* Flag to mark dialog completed */ 119 120 /* GetExistingFilename */ 121 /* */ 122 /* This routine will popup a file selection box so that the user can */ 123 /* select an existing file from the scrollable list. The user is */ 124 /* prevented from entering a new filename because the edittable text */ 125 /* area of the file selection box widget is unmanaged. After the user */ 126 /* selects a file, GetExistingFilename returns the selected filename and */ 127 /* GFN_OK, indicating that the OK button was pressed. If the user */ 128 /* pressed the cancel button, the return value is GFN_CANCEL, and the */ 129 /* filename character string supplied in the call is not altered. */ 130 /* */ 131 /* Arguments: */ 132 /* */ 133 /* Widget parent - parent widget id */ 134 /* char * promptString - prompt string */ 135 /* char * filename - a string to receive the selected filename */ 136 /* (this string will not be altered if the */ 137 /* user pressed the cancel button) */ 138 /* */ 139 /* Returns: GFN_OK - file was selected and OK button pressed */ 140 /* GFN_CANCEL - Cancel button pressed and no returned file */ 141 /* */ 142 int GetExistingFilename(Widget parent, char *promptString, FileSelection *file) 143 { 144 return FileDialog(parent, promptString, file, FILEDIALOG_OPEN, NULL); 145 } 146 147 /* GetNewFilename 148 * 149 * Same as GetExistingFilename but pick a new file instead of an existing one. 150 * In this case the text area of the FSB is *not* unmanaged, so the user can 151 * enter a new filename. 152 */ 153 int GetNewFilename(Widget parent, char *promptString, FileSelection *file, 154 char *defaultName) 155 { 156 return FileDialog(parent, promptString, file, FILEDIALOG_SAVE, defaultName); 157 } 158 159 160 /* 161 ** Return current default directory used by GetExistingFilename. 162 ** Can return NULL if no default directory has been set (meaning 163 ** use the application's current working directory) String must 164 ** be freed by the caller using NEditFree. 165 */ 166 char *GetFileDialogDefaultDirectory(void) 167 { 168 char *string; 169 170 if (DefaultDirectory == NULL) 171 return NULL; 172 XmStringGetLtoR(DefaultDirectory, XmSTRING_DEFAULT_CHARSET, &string); 173 return string; 174 } 175 176 /* 177 ** Set the current default directory to be used by GetExistingFilename. 178 ** "dir" can be passed as NULL to clear the current default directory 179 ** and use the application's working directory instead. 180 */ 181 void SetFileDialogDefaultDirectory(char *dir) 182 { 183 if (DefaultDirectory != NULL) 184 XmStringFree(DefaultDirectory); 185 DefaultDirectory = dir==NULL ? NULL : XmStringCreateSimple(dir); 186 187 if(DefaultDirectoryStr) { 188 NEditFree(DefaultDirectoryStr); 189 } 190 DefaultDirectoryStr = NEditStrdup(dir); 191 } 192 193 char* GetDefaultDirectoryStr(void) 194 { 195 return DefaultDirectoryStr; 196 } 197 198 /* 199 ** createYesNoDialog, createErrorDialog, doYesNoDialog, doErrorDialog 200 ** 201 ** Error Messages and question dialogs to be used with the file selection 202 ** box. Due to a crash bug in Motif 1.1.1 thru (at least) 1.1.5 203 ** getfiles can not use DialogF. According to OSF, there is an error 204 ** in the creation of pushButtonGadgets involving the creation and 205 ** destruction of some sort of temporary object. These routines create 206 ** the dialogs along with the file selection dialog and manage them 207 ** to display messages. This somehow avoids the problem 208 */ 209 static void createYesNoDialog(Widget parent) 210 { 211 XmString buttonString; /* compound string for dialog buttons */ 212 int n; /* number of arguments */ 213 Arg args[MAX_ARGS]; /* arg list */ 214 215 n = 0; 216 XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++; 217 XtSetArg(args[n], XmNtitle, " "); n++; 218 YesNoDialog = CreateQuestionDialog(parent, "yesNo", args, n); 219 XtAddCallback (YesNoDialog, XmNokCallback, (XtCallbackProc)yesNoOKCB, NULL); 220 XtAddCallback (YesNoDialog, XmNcancelCallback, 221 (XtCallbackProc)yesNoCancelCB, NULL); 222 XtUnmanageChild(XmMessageBoxGetChild (YesNoDialog, XmDIALOG_HELP_BUTTON)); 223 buttonString = XmStringCreateSimple("Yes"); 224 SET_ONE_RSRC(YesNoDialog, XmNokLabelString, buttonString); 225 XmStringFree(buttonString); 226 buttonString = XmStringCreateSimple("No"); 227 SET_ONE_RSRC(YesNoDialog, XmNcancelLabelString, buttonString); 228 XmStringFree(buttonString); 229 } 230 231 static void createErrorDialog(Widget parent) 232 { 233 XmString buttonString; /* compound string for dialog button */ 234 int n; /* number of arguments */ 235 Arg args[MAX_ARGS]; /* arg list */ 236 237 n = 0; 238 XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++; 239 XtSetArg(args[n], XmNtitle, " "); n++; 240 ErrorDialog = CreateErrorDialog(parent, "error", args, n); 241 XtAddCallback(ErrorDialog, XmNcancelCallback, (XtCallbackProc)errorOKCB, 242 NULL); 243 XtUnmanageChild(XmMessageBoxGetChild(ErrorDialog, XmDIALOG_OK_BUTTON)); 244 XtUnmanageChild(XmMessageBoxGetChild(ErrorDialog, XmDIALOG_HELP_BUTTON)); 245 buttonString = XmStringCreateLtoR("OK", XmSTRING_DEFAULT_CHARSET); 246 XtVaSetValues(ErrorDialog, XmNcancelLabelString, buttonString, NULL); 247 XtVaSetValues(XmMessageBoxGetChild(ErrorDialog, XmDIALOG_CANCEL_BUTTON), 248 XmNmarginWidth, BUTTON_WIDTH_MARGIN, 249 NULL); 250 XmStringFree(buttonString); 251 } 252 253 int OverrideFileDialog(Widget parent, const char *filename) 254 { 255 createYesNoDialog(parent); 256 int ret = doYesNoDialog(filename); 257 XtDestroyWidget(YesNoDialog); 258 YesNoDialog = NULL; 259 return ret; 260 } 261 262 263 void FileOpenErrorDialog(Widget parent, const char *filename) 264 { 265 createErrorDialog(parent); 266 doErrorDialog("Error: can''t open %s ", filename); 267 return; 268 } 269 270 static int doYesNoDialog(const char *filename) 271 { 272 char string[255]; 273 XmString mString; 274 275 YesNoResult = ynNone; 276 277 sprintf(string, "File %s already exists,\nOk to overwrite?", filename); 278 mString = XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET); 279 280 SET_ONE_RSRC(YesNoDialog, XmNmessageString, mString); 281 XmStringFree(mString); 282 ManageDialogCenteredOnPointer(YesNoDialog); 283 284 while (YesNoResult == ynNone) 285 XtAppProcessEvent(XtWidgetToApplicationContext(YesNoDialog), XtIMAll); 286 287 XtUnmanageChild(YesNoDialog); 288 289 /* Nasty motif bug here, patched around by waiting for a ReparentNotify 290 event (with timeout) before allowing file selection dialog to pop 291 down. If this routine returns too quickly, and the file selection 292 dialog (and thereby, this dialog as well) are destroyed while X 293 is still sorting through the events generated by the pop-down, 294 something bad happens and we get a crash */ 295 if (YesNoResult == ynYes) 296 PopDownBugPatch(YesNoDialog); 297 298 return YesNoResult == ynYes; 299 } 300 301 static void doErrorDialog(const char *errorString, const char *filename) 302 { 303 char string[255]; 304 XmString mString; 305 306 ErrorDone = False; 307 308 sprintf(string, errorString, filename); 309 mString = XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET); 310 311 SET_ONE_RSRC(ErrorDialog, XmNmessageString, mString); 312 XmStringFree(mString); 313 ManageDialogCenteredOnPointer(ErrorDialog); 314 315 while (!ErrorDone) 316 XtAppProcessEvent (XtWidgetToApplicationContext(ErrorDialog), XtIMAll); 317 318 XtUnmanageChild(ErrorDialog); 319 } 320 321 static void yesNoOKCB(Widget w, caddr_t client_data, caddr_t call_data) 322 { 323 YesNoResult = ynYes; 324 } 325 326 static void errorOKCB(Widget w, caddr_t client_data, caddr_t call_data) 327 { 328 ErrorDone = True; 329 } 330 331 static void yesNoCancelCB(Widget w, caddr_t client_data, caddr_t call_data) 332 { 333 YesNoResult = ynNo; 334 } 335 336 337 /* 338 * code from nedit source/file.c 339 * This function exists only to avoid mixing old nedit code with the 340 * new file dialog code 341 */ 342 Widget CreateFormatButtons( 343 Widget form, 344 Widget bottom, 345 int format, 346 Widget *u, 347 Widget *d, 348 Widget *m) 349 { 350 XmString str; 351 Widget formatBtns = XtVaCreateManagedWidget("formatBtns", 352 xmRowColumnWidgetClass, form, 353 XmNradioBehavior, XmONE_OF_MANY, 354 XmNorientation, XmHORIZONTAL, 355 XmNpacking, XmPACK_TIGHT, 356 XmNbottomAttachment, XmATTACH_WIDGET, 357 XmNbottomWidget, bottom, 358 XmNleftAttachment, XmATTACH_FORM, 359 XmNrightAttachment, XmATTACH_FORM, 360 XmNleftOffset, 5, 361 XmNrightOffset, 5, 362 XmNbottomOffset, 2, 363 NULL); 364 XtVaCreateManagedWidget("formatBtns", xmLabelWidgetClass, formatBtns, 365 XmNlabelString, str=XmStringCreateSimple("Format:"), NULL); 366 XmStringFree(str); 367 *u = XtVaCreateManagedWidget("unixFormat", 368 xmToggleButtonWidgetClass, formatBtns, 369 XmNlabelString, str = XmStringCreateSimple("Unix"), 370 XmNset, format == UNIX_FILE_FORMAT, 371 XmNuserData, (XtPointer)UNIX_FILE_FORMAT, 372 XmNmarginHeight, 0, 373 XmNalignment, XmALIGNMENT_BEGINNING, 374 XmNmnemonic, 'U', 375 NULL); 376 XmStringFree(str); 377 *d = XtVaCreateManagedWidget("dosFormat", 378 xmToggleButtonWidgetClass, formatBtns, 379 XmNlabelString, str = XmStringCreateSimple("DOS"), 380 XmNset, format == DOS_FILE_FORMAT, 381 XmNuserData, (XtPointer)DOS_FILE_FORMAT, 382 XmNmarginHeight, 0, 383 XmNalignment, XmALIGNMENT_BEGINNING, 384 XmNmnemonic, 'O', 385 NULL); 386 XmStringFree(str); 387 *m= XtVaCreateManagedWidget("macFormat", 388 xmToggleButtonWidgetClass, formatBtns, 389 XmNlabelString, str = XmStringCreateSimple("Macintosh"), 390 XmNset, format == MAC_FILE_FORMAT, 391 XmNuserData, (XtPointer)MAC_FILE_FORMAT, 392 XmNmarginHeight, 0, 393 XmNalignment, XmALIGNMENT_BEGINNING, 394 XmNmnemonic, 'M', 395 NULL); 396 XmStringFree(str); 397 return formatBtns; 398 } 399