Initial release of Maemo 5 port of gnuplot
[gnuplot] / src / win / wmenu.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: wmenu.c,v 1.8 2006/08/09 07:39:34 mikulik Exp $"); }
3 #endif
4
5 /* GNUPLOT - win/wmenu.c */
6 /*[
7  * Copyright 1992, 1993, 1998, 2004   Maurice Castro, Russell Lang
8  *
9  * Permission to use, copy, and distribute this software and its
10  * documentation for any purpose with or without fee is hereby granted,
11  * provided that the above copyright notice appear in all copies and
12  * that both that copyright notice and this permission notice appear
13  * in supporting documentation.
14  *
15  * Permission to modify the software is granted, but not the right to
16  * distribute the complete modified source code.  Modifications are to
17  * be distributed as patches to the released version.  Permission to
18  * distribute binaries produced by compiling modified sources is granted,
19  * provided you
20  *   1. distribute the corresponding source modifications from the
21  *    released version in the form of a patch file along with the binaries,
22  *   2. add special version identification to distinguish your version
23  *    in addition to the base release version number,
24  *   3. provide your name and address as the primary contact for the
25  *    support of your modified version, and
26  *   4. retain our contact information in regard to use of the base
27  *    software.
28  * Permission to distribute the released version of the source code along
29  * with corresponding source modifications in the form of a patch file is
30  * granted with same provisions 2 through 4 for binary distributions.
31  *
32  * This software is provided "as is" without express or implied warranty
33  * to the extent permitted by applicable law.
34 ]*/
35
36 /*
37  * AUTHORS
38  *
39  *   Maurice Castro
40  *   Russell Lang
41  *
42  */
43
44 #define STRICT
45 #define COBJMACROS
46 #define _WIN32_IE 0x0501
47 #include <windows.h>
48 #include <windowsx.h>
49 #if WINVER >= 0x030a
50 # include <commdlg.h>
51 #endif
52 #include <string.h>     /* only use far items */
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include "wgnuplib.h"
56 #include "wresourc.h"
57 #include "stdfn.h"
58 #include "wcommon.h"
59
60 /* WITH_ADV_DIR_DIALOG is defined in wcommon.h */
61 #ifdef WITH_ADV_DIR_DIALOG
62 # include <shlobj.h>
63 # include <winuser.h>
64 # include <shlwapi.h>
65 #endif
66
67 BOOL CALLBACK WINEXPORT InputBoxDlgProc(HWND, UINT, WPARAM, LPARAM);
68 LRESULT CALLBACK WINEXPORT MenuButtonProc(HWND, UINT, WPARAM, LPARAM);
69
70 /* limits */
71 #define MAXSTR 255
72 #define MACROLEN 10000
73 /* #define NUMMENU 256  defined in wresourc.h */
74 #define MENUDEPTH 3
75
76 /* menu tokens */
77 #define CMDMIN 129
78 #define INPUT 129
79 #define EOS 130
80 #define OPEN 131
81 #define SAVE 132
82 #define DIRECTORY 133
83 #define CMDMAX 133
84 char * keyword[] = {
85         "[INPUT]", "[EOS]", "[OPEN]", "[SAVE]", "[DIRECTORY]",
86         "{ENTER}", "{ESC}", "{TAB}",
87         "{^A}", "{^B}", "{^C}", "{^D}", "{^E}", "{^F}", "{^G}", "{^H}",
88         "{^I}", "{^J}", "{^K}", "{^L}", "{^M}", "{^N}", "{^O}", "{^P}",
89         "{^Q}", "{^R}", "{^S}", "{^T}", "{^U}", "{^V}", "{^W}", "{^X}",
90         "{^Y}", "{^Z}", "{^[}", "{^\\}", "{^]}", "{^^}", "{^_}",
91         NULL};
92 BYTE keyeq[] = {
93         INPUT, EOS, OPEN, SAVE, DIRECTORY,
94         13, 27, 9,
95         1, 2, 3, 4, 5, 6, 7, 8,
96         9, 10, 11, 12, 13, 14, 15, 16,
97         17, 18, 19, 20, 21, 22, 23, 24,
98         25, 26, 28, 29, 30, 31,
99         0};
100
101
102 #ifdef WITH_ADV_DIR_DIALOG
103
104 #ifdef SHELL_DIR_DIALOG
105
106 /* This is missing in MingW 2.95 */
107 #ifndef BIF_EDITBOX     
108 # define BIF_EDITBOX 0x0010
109 #endif
110
111 /* Note: this code has been bluntly copied from MSDN article KB179378
112          "How To Browse for Folders from the Current Directory"
113 */
114 INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
115 {
116         TCHAR szDir[MAX_PATH];
117
118         switch (uMsg) {
119         case BFFM_INITIALIZED:
120                 if (GetCurrentDirectory(sizeof(szDir)/sizeof(TCHAR), szDir)) {
121                         /* WParam is TRUE since you are passing a path.
122                           It would be FALSE if you were passing a pidl. */
123                         SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir);
124                 }
125                 break;
126
127         case BFFM_SELCHANGED:
128                 /* Set the status window to the currently selected path. */
129                 if (SHGetPathFromIDList((LPITEMIDLIST) lp, szDir)) {
130                         SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)szDir);
131                 }
132                 break;
133         }
134         return 0;
135 }
136
137 #else /* SHELL_DIR_DIALOG */
138
139 /* Yes, you can use Windows shell functions even without C++ !
140    These functions are not defined in shlobj.h, so we do it ourselves:
141 */
142 #define IShellFolder_BindToObject(This,pidl,pbcReserved,riid,ppvOut) \
143                 (This)->lpVtbl -> BindToObject(This,pidl,pbcReserved,riid,ppvOut)
144 #define IShellFolder_GetDisplayNameOf(This,pidl,uFlags,lpName) \
145                 (This)->lpVtbl -> GetDisplayNameOf(This,pidl,uFlags,lpName)
146
147 /* My windows header files do not define these: */
148 #ifndef WC_NO_BEST_FIT_CHARS      
149 #define WC_NO_BEST_FIT_CHARS      0x00000400  /* do not use best fit chars */
150 #endif
151
152 /* We really need this struct which is used by newer Windows versions */
153 typedef struct tagOFN { 
154   DWORD         lStructSize; 
155   HWND          hwndOwner; 
156   HINSTANCE     hInstance; 
157   LPCTSTR       lpstrFilter; 
158   LPTSTR        lpstrCustomFilter; 
159   DWORD         nMaxCustFilter; 
160   DWORD         nFilterIndex; 
161   LPTSTR        lpstrFile; 
162   DWORD         nMaxFile; 
163   LPTSTR        lpstrFileTitle; 
164   DWORD         nMaxFileTitle; 
165   LPCTSTR       lpstrInitialDir; 
166   LPCTSTR       lpstrTitle; 
167   DWORD         Flags; 
168   WORD          nFileOffset; 
169   WORD          nFileExtension; 
170   LPCTSTR       lpstrDefExt; 
171   LPARAM        lCustData; 
172   LPOFNHOOKPROC lpfnHook; 
173   LPCTSTR       lpTemplateName; 
174 /* #if (_WIN32_WINNT >= 0x0500) */
175   void *        pvReserved;
176   DWORD         dwReserved;
177   DWORD         FlagsEx;
178 /* #endif // (_WIN32_WINNT >= 0x0500) */
179 } NEW_OPENFILENAME, *NEW_LPOPENFILENAME;
180
181
182 UINT_PTR CALLBACK OFNHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
183 {
184         switch(uiMsg) {
185         case WM_INITDIALOG: {
186                 HWND parent;    
187                 parent = GetParent(hdlg);
188                 /* Hint: The control codes for this can be found on MSDN */
189                 /* Hide "file type" display */
190                 CommDlg_OpenSave_HideControl(parent, stc2);
191                 CommDlg_OpenSave_HideControl(parent, cmb1);
192                 /* Hide "current file" display */
193                 CommDlg_OpenSave_HideControl(parent, stc3);
194                 CommDlg_OpenSave_HideControl(parent, cmb13);
195                 break;
196                 }
197
198         case WM_NOTIFY: {
199                 LPOFNOTIFY lpOfNotify = (LPOFNOTIFY) lParam;
200                 switch(lpOfNotify->hdr.code) {
201 #if 0
202                 /* Well, this event is not called for ordinary files (sigh) */
203                 case CDN_INCLUDEITEM:
204                         return 0;
205                         break;
206 #endif
207                 /* But there's a solution which can be found here:
208                         http://msdn.microsoft.com/msdnmag/issues/03/10/CQA/default.aspx
209                         http://msdn.microsoft.com/msdnmag/issues/03/09/CQA/
210                    It's C++ though (sigh again), so here is its analogue in plain C:
211                 */
212                 /* case CDN_SELCHANGE: */
213                 case CDN_FOLDERCHANGE: {
214                         HWND parent, hlst2, list;
215                         LPCITEMIDLIST pidlFolder;
216                         LPMALLOC pMalloc;
217                         signed int count, i;
218                         unsigned len;
219
220                         /* find listbox control */
221                         parent = GetParent(hdlg);
222                         hlst2 = GetDlgItem(parent, lst2);
223                         list = GetDlgItem(hlst2, 1);
224
225                         SHGetMalloc(&pMalloc);
226
227                         /* First, get PIDL of current folder by sending CDM_GETFOLDERIDLIST
228                            get length first, then allocate. */
229                         len = CommDlg_OpenSave_GetFolderIDList(parent, 0, 0);
230                         if (len>0) {
231                                 LPSHELLFOLDER ishDesk;
232                                 LPSHELLFOLDER ishFolder;
233                                 HRESULT hr;
234                                 STRRET str;
235
236                                 pidlFolder = IMalloc_Alloc(pMalloc, len+1);
237                                 CommDlg_OpenSave_GetFolderIDList(parent, (WPARAM)(void*)pidlFolder, len);
238
239                                 /* Now get IShellFolder for pidlFolder */
240                                 SHGetDesktopFolder(&ishDesk);
241                                 hr = IShellFolder_BindToObject(ishDesk, pidlFolder, NULL, &IID_IShellFolder, &ishFolder);
242                                 if (!SUCCEEDED(hr)) {
243                                         ishFolder = ishDesk;
244                                 }
245
246                                 /* Enumerate listview items */
247                                 count = ListView_GetItemCount(list);
248                                 for (i = count-1; i >= 0; i--)
249                                 {
250                                         const ULONG flags = SHGDN_NORMAL | SHGDN_FORPARSING;
251                                         LVITEM lvitem;
252                                         LPCITEMIDLIST pidl;                             
253 #if 0
254                                         /* The normal code to retrieve the item's text is 
255                                            not very useful since user may select "hide common 
256                                            extensions" */
257                                         path = (char *)malloc(MAX_PATH+1);
258                                         ListView_GetItemText(list, i, 0, path, MAX_PATH );
259 #endif
260                                         /* The following code retrieves the real path of every 
261                                            item in any case */
262                                         /* Retrieve PIDL of current item */
263                                         ZeroMemory(&lvitem,sizeof(lvitem));
264                                         lvitem.iItem = i;
265                                         lvitem.mask = LVIF_PARAM;
266                                         ListView_GetItem(list, &lvitem);
267                                         pidl = (LPCITEMIDLIST)lvitem.lParam;
268
269                                         /* Finally, get the path name from pidlFolder */
270                                         str.uType = STRRET_WSTR;
271                                         hr = IShellFolder_GetDisplayNameOf(ishFolder, pidl, flags, &str);
272                                         if (SUCCEEDED(hr)) {
273                                                 struct _stat itemStat;
274                                                 char path[MAX_PATH+1];
275
276                                                 /* (sigh) conversion would have been so easy...
277                                                    hr = StrRetToBuf( str, pidl, path, MAX_PATH); */
278                                                 if (str.uType == STRRET_WSTR) {
279                                                         unsigned wlen = wcslen(str.pOleStr);
280                                                         wlen = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, 
281                                                                                                                 str.pOleStr, wlen+1, path, MAX_PATH, 
282                                                                                                                 NULL, NULL);
283                                                         _wstat(str.pOleStr, &itemStat);                                         
284                                                         /* Must free memory allocated by shell using shell's IMalloc  */
285                                                         IMalloc_Free(pMalloc, (LPVOID)str.pOleStr);
286                                                 } else if (str.uType == STRRET_CSTR) {
287                                                         strncpy(path, str.cStr, MAX_PATH);
288                                                         _stat(str.cStr, &itemStat);
289                                                 } else {
290                                                         /* this shouldn't happen */
291                                                         path[0] = '\0';
292                                                 }
293
294                                                 /* discard all non-directories from list */
295                                                 if ((itemStat.st_mode & _S_IFDIR) == 0) {
296                                                         ListView_DeleteItem(list, i);
297                                                 }                                               
298                                         }
299                                 }  /* Enumerate listview items */                                                       
300                                 IMalloc_Free(pMalloc, (void*)pidlFolder);
301                         }                       
302                         IMalloc_Release(pMalloc);
303                         break;
304                         } /* CDN_FOLDERCHANGE */
305                 } /* switch(hdr.code) */
306                 break;
307                 } /* WM_NOTIFY */                       
308         }; /* switch(uiMsg) */
309         return 0;
310 }
311
312 #endif /* !SHELL_DIR_DIALOG */
313 #endif /* WITH_ADV_DIR_DIALOG */
314
315
316 /* Send a macro to the text window */
317 void
318 SendMacro(LPTW lptw, UINT m)
319 {
320 BYTE FAR *s;
321 char *d;
322 char *buf;
323 BOOL flag=TRUE;
324 int i;
325 LPMW lpmw = lptw->lpmw;
326 #if WINVER >= 0x030a
327 OPENFILENAME ofn;
328 char *szTitle;
329 char *szFile;
330 char *szFilter;
331 #endif
332
333         if ( (buf = LocalAllocPtr(LHND, MAXSTR+1)) == (char *)NULL )
334                 return;
335
336         if (m>=lpmw->nCountMenu)
337                 return;
338         s = lpmw->macro[m];
339         d = buf;
340         *d = '\0';
341         while (s && *s && (d-buf < MAXSTR)) {
342             if (*s>=CMDMIN  && *s<=CMDMAX) {
343                 switch (*s) {
344                         case SAVE: /* [SAVE] - get a save filename from a file list box */
345                         case OPEN: /* [OPEN] - get a filename from a file list box */
346 #if WINVER >= 0x030a
347                                 /* This uses COMMDLG.DLL from Windows 3.1
348                                    COMMDLG.DLL is redistributable */
349                                 {
350                                 BOOL save;
351                                 char cwd[MAX_PATH];
352
353                                 if ( (szTitle = LocalAllocPtr(LHND, MAXSTR+1)) == (char *)NULL )
354                                         return;
355                                 if ( (szFile = LocalAllocPtr(LHND, MAXSTR+1)) == (char *)NULL )
356                                         return;
357                                 if ( (szFilter = LocalAllocPtr(LHND, MAXSTR+1)) == (char *)NULL )
358                                         return;
359
360                                 save = (*s==SAVE);
361                                 s++;
362                                                 for(i=0; (*s >= 32 && *s <= 126); i++)
363                                                         szTitle[i] = *s++;      /* get dialog box title */
364                                 szTitle[i]='\0';
365                                 s++;
366                                                 for(i=0; (*s >= 32 && *s <= 126); i++)
367                                                         szFile[i] = *s++;       /* temporary copy of filter */
368                                 szFile[i++]='\0';
369                                 lstrcpy(szFilter,"Default (");
370                                 lstrcat(szFilter,szFile);
371                                 lstrcat(szFilter,")");
372                                 i=lstrlen(szFilter);
373                                 i++;    /* move past NULL */
374                                 lstrcpy(szFilter+i,szFile);
375                                 i+=lstrlen(szFilter+i);
376                                 i++;    /* move past NULL */
377                                 lstrcpy(szFilter+i,"All Files (*.*)");
378                                 i+=lstrlen(szFilter+i);
379                                 i++;    /* move past NULL */
380                                 lstrcpy(szFilter+i,"*.*");
381                                 i+=lstrlen(szFilter+i);
382                                 i++;    /* move past NULL */
383                                 szFilter[i++]='\0';     /* add a second NULL */
384                                 flag = 0;
385
386                                 /* the Windows 3.1 implentation - MC */
387                                 szFile[0] = '\0';
388                                 /* clear the structrure */
389                                 _fmemset(&ofn, 0, sizeof(OPENFILENAME));
390                                 ofn.lStructSize = sizeof(OPENFILENAME);
391                                 ofn.hwndOwner = lptw->hWndParent;
392                                 ofn.lpstrFilter = szFilter;
393                                 ofn.nFilterIndex = 1;
394                                 ofn.lpstrFile = szFile;
395                                 ofn.nMaxFile = MAXSTR;
396                                 ofn.lpstrFileTitle = szFile;
397                                 ofn.nMaxFileTitle = MAXSTR;
398                                 ofn.lpstrTitle = szTitle;
399                                 /* Windows XP has it's very special meaning of 'default directory'  */
400                                 /* (search for OPENFILENAME on MSDN). So we set it here explicitly: */
401                                 /* ofn.lpstrInitialDir = (LPSTR)NULL; */
402                                 _getcwd(&cwd, sizeof(cwd));
403                                 ofn.lpstrInitialDir = cwd;
404                                 ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
405                                 flag = (save ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn));
406                                 if( flag ) {
407                                         lpmw->nChar = lstrlen(ofn.lpstrFile);
408                                         for (i=0; i<lpmw->nChar; i++)
409                                                 *d++=ofn.lpstrFile[i];
410                                 }
411
412                                 LocalFreePtr((void NEAR *)OFFSETOF(szTitle));
413                                 LocalFreePtr((void NEAR *)OFFSETOF(szFilter));
414                                 LocalFreePtr((void NEAR *)OFFSETOF(szFile));
415
416                                 }
417                                 break;
418 #else
419                                 /* Use InputBox if you don't have COMMDLG.DLL */
420                                 s++;    /* skip list box title */
421                                 for(i=0; (*s >= 32 && *s <= 126); i++)
422                                         s++;
423 #endif
424
425 #ifndef WITH_ADV_DIR_DIALOG
426                         case DIRECTORY: /* [DIRECTORY] fall back to INPUT dialog */
427 #endif
428                         case INPUT: /* [INPUT] - input a string of characters */
429                                 s++;
430                                 for(i=0; (*s >= 32 && *s <= 126); i++)
431                                         lpmw->szPrompt[i] = *s++;
432                                 lpmw->szPrompt[i]='\0';
433 #ifdef WIN32
434                                 flag = DialogBox( hdllInstance, "InputDlgBox", lptw->hWndParent, InputBoxDlgProc);
435 #else
436 #ifdef __DLL__
437                                 lpmw->lpProcInput = (DLGPROC)GetProcAddress(hdllInstance, "InputBoxDlgProc");
438 #else
439                                 lpmw->lpProcInput = (DLGPROC)MakeProcInstance((FARPROC)InputBoxDlgProc, hdllInstance);
440 #endif
441                                 flag = DialogBox( hdllInstance, "InputDlgBox", lptw->hWndParent, lpmw->lpProcInput);
442 #endif
443                                 if( flag ) {
444                                         for (i=0; i<lpmw->nChar; i++)
445                                                 *d++ = lpmw->szAnswer[i];
446                                 }
447 #ifndef WIN32
448 #ifndef __DLL__
449                                 FreeProcInstance((FARPROC)lpmw->lpProcInput);
450 #endif
451 #endif
452                                 break;
453
454 #ifdef WITH_ADV_DIR_DIALOG
455                         case DIRECTORY: /* [DIRECTORY] - show standard directory dialog */
456                                 {
457 #ifdef SHELL_DIR_DIALOG
458                                         BROWSEINFO bi;
459                                         LPITEMIDLIST pidl;
460 #else
461                                         NEW_OPENFILENAME ofn;
462 #endif
463                                         /* allocate some space */
464                                         if ( (szTitle = LocalAllocPtr(LHND, MAXSTR+1)) == (char *)NULL )
465                                                 return;
466
467                                         /* get dialog box title */
468                                         s++;
469                                         for(i=0; (*s >= 32 && *s <= 126); i++)
470                                                 szTitle[i] = *s++;      
471                                         szTitle[i] = '\0';
472
473                                         flag = 0;
474
475 #ifdef SHELL_DIR_DIALOG
476                                         /* Option 1:
477                                                         use the Shell's internal directory chooser
478                                         */
479                                         /* Note: This code does not work NT 3.51 and Win32s
480                                                          Windows 95 has shell32.dll version 4.0, but does not
481                                                  have a version number, so this will return FALSE.
482                                         */
483                                         /* Make sure that the installed shell version supports this approach */
484                                         if (GetDllVersion(TEXT("shell32.dll")) >= PACKVERSION(4,0)) {
485                                                 ZeroMemory(&bi,sizeof(bi));
486                                                 bi.hwndOwner = lptw->hWndParent;
487                                                 bi.pidlRoot = NULL;
488                                                 bi.pszDisplayName = NULL;
489                                                 bi.lpszTitle = szTitle;
490                                                 bi.ulFlags = BIF_EDITBOX | 
491                                                                          BIF_STATUSTEXT |
492                                                                          BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS;
493                                                 bi.lpfn = BrowseCallbackProc;
494                                                 bi.lParam = 0;
495                                                 bi.iImage = 0;
496                                                 pidl = SHBrowseForFolder( &bi );
497
498                                                 if( pidl != NULL ) {
499                                                         LPMALLOC pMalloc;
500                                                         HRESULT hr;
501                                                         char szPath[MAX_PATH];
502                                                         unsigned int len;
503
504                                                         /* Convert the item ID list's binary
505                                                            representation into a file system path */
506                                                         BOOL f = SHGetPathFromIDList(pidl, szPath);
507
508                                                         len = strlen( szPath );
509                                                         flag = len > 0;
510                                                         if (flag) 
511                                                                 for (i=0; i<len; i++)
512                                                                         *d++ = szPath[i];
513
514                                                         /* Allocate a pointer to an IMalloc interface
515                                                            Get the address of our task allocator's IMalloc interface */
516                                                         hr = SHGetMalloc(&pMalloc) ;
517
518                                                         /* Free the item ID list allocated by SHGetSpecialFolderLocation */
519                                                         IMalloc_Free( pMalloc, pidl );
520
521                                                         /* Free our task allocator */
522                                                         IMalloc_Release( pMalloc );
523                                                 }
524                                         }
525 #else /* SHELL_DIR_DIALOG */
526                                         /* Option 2:
527                                                         use (abuse ?) standard "file open" dialog and discard the filename
528                                                         from result, have all non-directory items removed.
529                                         */
530                                         /* Note: This code does not work NT 3.51 and Win32s
531                                                          Windows 95 has shell32.dll version 4.0, but does not
532                                                  have a version number, so this will return FALSE.
533                                         */
534                                         /* Make sure that the installed shell version supports this approach */
535                                         if (GetDllVersion(TEXT("shell32.dll")) >= PACKVERSION(4,0)) {
536                                                 /* copy current working directory to szFile */
537                                                 if ( (szFile = LocalAllocPtr(LHND, MAX_PATH+1)) == (char *)NULL )
538                                                         return;
539                                                 GP_GETCWD( szFile, MAX_PATH );
540                                                 strcat( szFile, "\\*.*" );
541
542                                                 ZeroMemory(&ofn,sizeof(ofn));
543                                                 ofn.lStructSize = sizeof(ofn);
544                                                 ofn.hwndOwner = lptw->hWndParent;
545                                                 ofn.lpstrFilter = (LPSTR)NULL;
546                                                 ofn.nFilterIndex = 0;
547                                                 ofn.lpstrFile = szFile;
548                                                 ofn.nMaxFile = MAX_PATH;
549                                                 ofn.lpstrFileTitle = szFile;
550                                                 ofn.nMaxFileTitle = MAXSTR;
551                                                 ofn.lpstrTitle = szTitle;
552                                                 ofn.lpstrInitialDir = (LPSTR)NULL;
553                                                 ofn.Flags = OFN_PATHMUSTEXIST | OFN_NOVALIDATE | 
554                                                                         OFN_HIDEREADONLY | OFN_ENABLESIZING | 
555                                                                         OFN_EXPLORER | OFN_ENABLEHOOK;
556                                                 ofn.lpfnHook = OFNHookProc;
557                                                 flag = GetOpenFileName((LPOPENFILENAME)&ofn);
558                                 
559                                                 if ((flag) && (ofn.nFileOffset >0)) {
560                                                         unsigned int len;
561                                                 
562                                                         /* strip filename from result */
563                                                         len = ofn.nFileOffset - 1;
564                                                         ofn.lpstrFile[len] = '\0';                      
565                                                         for (i=0; i<len; i++)
566                                                                 *d++ = ofn.lpstrFile[i];
567                                                 }                       
568                                                 LocalFreePtr((void NEAR *)OFFSETOF(szFile));
569                                         }
570 #endif /* !SHELL_DIR_DIALOG */
571                                         else {
572                                                 strcpy(lpmw->szPrompt, szTitle);
573                                                 flag = DialogBox( hdllInstance, "InputDlgBox", lptw->hWndParent, InputBoxDlgProc);
574                                                 if( flag ) {
575                                                         for (i=0; i<lpmw->nChar; i++)
576                                                                 *d++ = lpmw->szAnswer[i];
577                                                 }
578                                         }       
579                                         LocalFreePtr((void NEAR *)OFFSETOF(szTitle));
580                                 }
581                                 break;
582 #endif /* WITH_ADV_DIR_DIALOG */
583
584                     case EOS: /* [EOS] - End Of String - do nothing */
585                                 default:
586                                 s++;
587                                 break;
588                 }
589                 if (!flag) { /* abort */
590                         d = buf;
591                         s = (BYTE FAR *)"";
592                 }
593             }
594             else {
595                 *d++ = *s++;
596             }
597         }
598         *d = '\0';
599         if (buf[0]!='\0') {
600                 d = buf;
601                 while (*d) {
602                         SendMessage(lptw->hWndText,WM_CHAR,*d,1L);
603                         d++;
604                 }
605         }
606 }
607
608
609 #define GBUFSIZE 512
610 typedef struct tagGFILE {
611         HFILE   hfile;
612         char    getbuf[GBUFSIZE];
613         int     getnext;
614         int     getleft;
615 } GFILE;
616
617 GFILE * Gfopen(LPSTR lpszFileName, int fnOpenMode)
618 {
619 GFILE *gfile;
620
621         gfile = (GFILE *)LocalAllocPtr(LHND, sizeof(GFILE));
622         if (!gfile)
623                 return NULL;
624
625         gfile->hfile = _lopen(lpszFileName, fnOpenMode);
626         if (gfile->hfile == HFILE_ERROR) {
627                 LocalFreePtr((void NEAR *)OFFSETOF(gfile));
628                 return NULL;
629         }
630         gfile->getleft = 0;
631         gfile->getnext = 0;
632         return gfile;
633 }
634
635 void Gfclose(GFILE * gfile)
636 {
637
638         _lclose(gfile->hfile);
639         LocalFreePtr((void NEAR *)OFFSETOF(gfile));
640         return;
641 }
642
643 /* returns number of characters read */
644 int
645 Gfgets(LPSTR lp, int size, GFILE *gfile)
646 {
647 int i;
648 int ch;
649         for (i=0; i<size; i++) {
650                 if (gfile->getleft <= 0) {
651                         if ( (gfile->getleft = _lread(gfile->hfile, gfile->getbuf, GBUFSIZE)) == 0)
652                                 break;
653                         gfile->getnext = 0;
654                 }
655                 ch = *lp++ = gfile->getbuf[gfile->getnext++];
656                 gfile->getleft --;
657                 if (ch == '\r') {
658                         i--;
659                         lp--;
660                 }
661                 if (ch == '\n') {
662                         i++;
663                         break;
664                 }
665         }
666         if (i<size)
667                 *lp++ = '\0';
668         return i;
669 }
670
671 /* Get a line from the menu file */
672 /* Return number of lines read from file including comment lines */
673 int GetLine(char * buffer, int len, GFILE *gfile)
674 {
675 BOOL  status;
676 int nLine = 0;
677
678    status = (Gfgets(buffer,len,gfile) != 0);
679    nLine++;
680    while( status && ( buffer[0] == 0 || buffer[0] == '\n' || buffer[0] == ';' ) ) {
681       /* blank line or comment - ignore */
682           status = (Gfgets(buffer,len,gfile) != 0);
683       nLine++;
684    }
685    if (lstrlen(buffer)>0)
686       buffer[lstrlen(buffer)-1] = '\0'; /* remove trailing \n */
687
688    if (!status)
689       nLine = 0;        /* zero lines if file error */
690
691     return nLine;
692 }
693
694 /* Left justify string */
695 void LeftJustify(char *d, char *s)
696 {
697         while ( *s && (*s==' ' || *s=='\t') )
698                 s++;    /* skip over space */
699         do {
700                 *d++ = *s;
701         } while (*s++);
702 }
703
704 /* Translate string to tokenized macro */
705 void TranslateMacro(char *string)
706 {
707 int i, len;
708 LPSTR ptr;
709
710     for( i=0; keyword[i]!=(char *)NULL; i++ ) {
711         if( (ptr = _fstrstr( string, keyword[i] )) != NULL ) {
712             len = lstrlen( keyword[i] );
713             *ptr = keyeq[i];
714             lstrcpy( ptr+1, ptr+len );
715             i--;       /* allows for more than one occurrence of keyword */
716             }
717         }
718 }
719
720 /* Load Macros, and create Menu from Menu file */
721 void
722 LoadMacros(LPTW lptw)
723 {
724 GFILE *menufile;
725 BYTE FAR *macroptr;
726 char *buf;
727 int nMenuLevel;
728 HMENU hMenu[MENUDEPTH+1];
729 LPMW lpmw;
730 int nLine = 1;
731 int nInc;
732 HGLOBAL hmacro, hmacrobuf;
733
734 int i;
735 HDC hdc;
736 TEXTMETRIC tm;
737 RECT rect;
738 int ButtonX, ButtonY;
739 char FAR *ButtonText[BUTTONMAX];
740
741         lpmw = lptw->lpmw;
742
743         /* mark all buffers and menu file as unused */
744         buf = (char *)NULL;
745         hmacro = 0;
746         hmacrobuf = 0;
747         lpmw->macro = (BYTE FAR * FAR *)NULL;
748         lpmw->macrobuf = (BYTE FAR *)NULL;
749         lpmw->szPrompt = (char *)NULL;
750         lpmw->szAnswer = (char *)NULL;
751         menufile = (GFILE *)NULL;
752
753         /* open menu file */
754         if ((menufile=Gfopen(lpmw->szMenuName,OF_READ)) == (GFILE *)NULL)
755                 goto errorcleanup;
756
757         /* allocate buffers */
758         if ((buf = LocalAllocPtr(LHND, MAXSTR)) == (char *)NULL)
759                 goto nomemory;
760         hmacro = GlobalAlloc(GHND,(NUMMENU) * sizeof(BYTE FAR *));
761         if ((lpmw->macro = (BYTE FAR * FAR *)GlobalLock(hmacro))  == (BYTE FAR * FAR *)NULL)
762                 goto nomemory;
763         hmacrobuf = GlobalAlloc(GHND, MACROLEN);
764         if ((lpmw->macrobuf = (BYTE FAR*)GlobalLock(hmacrobuf)) == (BYTE FAR *)NULL)
765                 goto nomemory;
766         if ((lpmw->szPrompt = LocalAllocPtr(LHND, MAXSTR)) == (char *)NULL)
767                 goto nomemory;
768         if ((lpmw->szAnswer = LocalAllocPtr(LHND, MAXSTR)) == (char *)NULL)
769                 goto nomemory;
770
771         macroptr = lpmw->macrobuf;
772         lpmw->nButton = 0;
773         lpmw->nCountMenu = 0;
774         lpmw->hMenu = hMenu[0] = CreateMenu();
775         nMenuLevel = 0;
776
777         while ((nInc = GetLine(buf,MAXSTR,menufile)) != 0) {
778           nLine += nInc;
779           LeftJustify(buf,buf);
780           if (buf[0]=='\0') {
781                 /* ignore blank lines */
782           }
783           else if (!lstrcmpi(buf,"[Menu]")) {
784                 /* new menu */
785                 if (!(nInc = GetLine(buf,MAXSTR,menufile))) {
786                         nLine += nInc;
787                         wsprintf(buf,"Problem on line %d of %s\n",nLine,lpmw->szMenuName);
788                         MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
789                         goto errorcleanup;
790                 }
791                 LeftJustify(buf,buf);
792                 if (nMenuLevel<MENUDEPTH)
793                         nMenuLevel++;
794                 else {
795                         wsprintf(buf,"Menu is too deep at line %d of %s\n",nLine,lpmw->szMenuName);
796                         MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
797                         goto errorcleanup;
798                 }
799                 hMenu[nMenuLevel] = CreateMenu();
800                 AppendMenu(hMenu[nMenuLevel > 0 ? nMenuLevel-1 : 0],
801                         MF_STRING | MF_POPUP, (UINT)hMenu[nMenuLevel], (LPCSTR)buf);
802           }
803           else if (!lstrcmpi(buf,"[EndMenu]")) {
804                 if (nMenuLevel > 0)
805                         nMenuLevel--;   /* back up one menu */
806           }
807           else if (!lstrcmpi(buf,"[Button]")) {
808                 /* button macro */
809                 if (lpmw->nButton >= BUTTONMAX) {
810                         wsprintf(buf,"Too many buttons at line %d of %s\n",nLine,lpmw->szMenuName);
811                                 MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
812                         goto errorcleanup;
813                 }
814                 if (!(nInc = GetLine(buf,MAXSTR,menufile))) {
815                         nLine += nInc;
816                         wsprintf(buf,"Problem on line %d of %s\n",nLine,lpmw->szMenuName);
817                         MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
818                         goto errorcleanup;
819                 }
820                 LeftJustify(buf,buf);
821                 if (lstrlen(buf)+1 < MACROLEN - (macroptr-lpmw->macrobuf))
822                         lstrcpy((char FAR *)macroptr,buf);
823                 else {
824                         wsprintf(buf,"Out of space for storing menu macros\n at line %d of \n",nLine,lpmw->szMenuName);
825                                 MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
826                         goto errorcleanup;
827                 }
828                 ButtonText[lpmw->nButton] = (char FAR *)macroptr;
829                 macroptr += lstrlen((char FAR *)macroptr)+1;
830                 *macroptr = '\0';
831                 if (!(nInc = GetLine(buf,MAXSTR,menufile))) {
832                         nLine += nInc;
833                         wsprintf(buf,"Problem on line %d of %s\n",nLine,lpmw->szMenuName);
834                                 MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
835                         goto errorcleanup;
836                 }
837                 LeftJustify(buf,buf);
838                 TranslateMacro(buf);
839                 if (lstrlen(buf)+1 < MACROLEN - (macroptr - lpmw->macrobuf))
840                         lstrcpy((char FAR *)macroptr,buf);
841                 else {
842                         wsprintf(buf,"Out of space for storing menu macros\n at line %d of \n",nLine,lpmw->szMenuName);
843                                 MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
844                         goto errorcleanup;
845                 }
846                 lpmw->hButtonID[lpmw->nButton] = lpmw->nCountMenu;
847                 lpmw->macro[lpmw->nCountMenu] = macroptr;
848                 macroptr += lstrlen((char FAR *)macroptr)+1;
849                 *macroptr = '\0';
850                 lpmw->nCountMenu++;
851                 lpmw->nButton++;
852           }
853           else {
854                 /* menu item */
855                 if (lpmw->nCountMenu>=NUMMENU) {
856                         wsprintf(buf,"Too many menu items at line %d of %s\n",nLine,lpmw->szMenuName);
857                                 MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
858                         goto errorcleanup;
859                 }
860                 LeftJustify(buf,buf);
861 /* HBB 981202: added MF_SEPARATOR to the MF_MENU*BREAK items. This  is meant
862  * to maybe avoid a CodeGuard warning about passing last argument zero
863  * when item style is not SEPARATOR... Actually, a better solution would
864  * have been to combine the '|' divider with the next menu item. */
865                 if (buf[0]=='-') {
866                     if (nMenuLevel == 0)
867                                 AppendMenu(hMenu[0], MF_SEPARATOR | MF_MENUBREAK, 0, (LPSTR)NULL);
868                     else
869                         AppendMenu(hMenu[nMenuLevel], MF_SEPARATOR, 0, (LPSTR)NULL);
870                 }
871                 else if (buf[0]=='|') {
872                         AppendMenu(hMenu[nMenuLevel], MF_SEPARATOR | MF_MENUBARBREAK, 0, (LPSTR)NULL);
873                 }
874                 else {
875                         AppendMenu(hMenu[nMenuLevel],MF_STRING, lpmw->nCountMenu, (LPSTR)buf);
876                         if (!(nInc = GetLine(buf,MAXSTR,menufile))) {
877                                 nLine += nInc;
878                                 wsprintf(buf,"Problem on line %d of %s\n",nLine,lpmw->szMenuName);
879                                 MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
880                                 goto errorcleanup;
881                         }
882                         LeftJustify(buf,buf);
883                         TranslateMacro(buf);
884                         if (lstrlen(buf)+1 < MACROLEN - (macroptr - lpmw->macrobuf))
885                                 lstrcpy((char FAR *)macroptr,buf);
886                         else {
887                                 wsprintf(buf,"Out of space for storing menu macros\n at line %d of %s\n",nLine,lpmw->szMenuName);
888                                 MessageBox(lptw->hWndParent,(LPSTR) buf,lptw->Title, MB_ICONEXCLAMATION);
889                                 goto errorcleanup;
890                         }
891                         lpmw->macro[lpmw->nCountMenu] = macroptr;
892                         macroptr += lstrlen((char FAR *)macroptr)+1;
893                         *macroptr = '\0';
894                         lpmw->nCountMenu++;
895                 }
896           }
897         }
898
899         if ( (lpmw->nCountMenu - lpmw->nButton) > 0 ) {
900                 /* we have a menu bar so put it on the window */
901                 SetMenu(lptw->hWndParent,lpmw->hMenu);
902                 DrawMenuBar(lptw->hWndParent);
903         }
904
905         if (!lpmw->nButton)
906                 goto cleanup;           /* no buttons */
907
908         /* calculate size of buttons */
909         hdc = GetDC(lptw->hWndParent);
910         SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
911         GetTextMetrics(hdc, &tm);
912         ButtonX = 8 * tm.tmAveCharWidth;
913         ButtonY = 6 * (tm.tmHeight + tm.tmExternalLeading) / 4;
914         ReleaseDC(lptw->hWndParent,hdc);
915
916         /* move top of client text window down to allow space for buttons */
917         lptw->ButtonHeight = ButtonY+1;
918         GetClientRect(lptw->hWndParent, &rect);
919         SetWindowPos(lptw->hWndText, (HWND)NULL, 0, lptw->ButtonHeight,
920                         rect.right, rect.bottom-lptw->ButtonHeight,
921                         SWP_NOZORDER | SWP_NOACTIVATE);
922
923         /* create the buttons */
924 #ifdef __DLL__
925         lpmw->lpfnMenuButtonProc = (WNDPROC)GetProcAddress(hdllInstance, "MenuButtonProc");
926 #else
927         lpmw->lpfnMenuButtonProc = (WNDPROC)MakeProcInstance((FARPROC)MenuButtonProc, hdllInstance);
928 #endif
929         for (i=0; i<lpmw->nButton; i++) {
930                 lpmw->hButton[i] = CreateWindow("button", ButtonText[i],
931                         WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
932                                 i * ButtonX, 0,
933                                 ButtonX, ButtonY,
934                                 lptw->hWndParent, (HMENU)i,
935                                 lptw->hInstance, lptw);
936                 lpmw->lpfnButtonProc[i] = (WNDPROC) GetWindowLong(lpmw->hButton[i], GWL_WNDPROC);
937                 SetWindowLong(lpmw->hButton[i], GWL_WNDPROC, (LONG)lpmw->lpfnMenuButtonProc);
938         }
939
940         goto cleanup;
941
942
943 nomemory:
944         MessageBox(lptw->hWndParent,"Out of memory",lptw->Title, MB_ICONEXCLAMATION);
945 errorcleanup:
946         if (hmacro) {
947                 GlobalUnlock(hmacro);
948                 GlobalFree(hmacro);
949         }
950         if (hmacrobuf) {
951                 GlobalUnlock(hmacrobuf);
952                 GlobalFree(hmacrobuf);
953         }
954         if (lpmw->szPrompt != (char *)NULL)
955                 LocalFreePtr((void NEAR *)OFFSETOF(lpmw->szPrompt));
956         if (lpmw->szAnswer != (char *)NULL)
957                 LocalFreePtr((void NEAR *)OFFSETOF(lpmw->szAnswer));
958
959 cleanup:
960         if (buf != (char *)NULL)
961                 LocalFreePtr((void NEAR *)OFFSETOF(buf));
962         if (menufile != (GFILE *)NULL)
963                 Gfclose(menufile);
964         return;
965
966 }
967
968 void
969 CloseMacros(LPTW lptw)
970 {
971 HGLOBAL hglobal;
972 LPMW lpmw;
973         lpmw = lptw->lpmw;
974
975 #ifndef WIN32
976 #ifndef __DLL__
977         if (lpmw->lpfnMenuButtonProc)
978                 FreeProcInstance((FARPROC)lpmw->lpfnMenuButtonProc);
979 #endif
980 #endif
981         hglobal = (HGLOBAL)GlobalHandle( SELECTOROF(lpmw->macro) );
982         if (hglobal) {
983                 GlobalUnlock(hglobal);
984                 GlobalFree(hglobal);
985         }
986         hglobal = (HGLOBAL)GlobalHandle( SELECTOROF(lpmw->macrobuf) );
987         if (hglobal) {
988                 GlobalUnlock(hglobal);
989                 GlobalFree(hglobal);
990         }
991         if (lpmw->szPrompt != (char *)NULL)
992                 LocalFreePtr((void NEAR *)OFFSETOF(lpmw->szPrompt));
993         if (lpmw->szAnswer != (char *)NULL)
994                 LocalFreePtr((void NEAR *)OFFSETOF(lpmw->szAnswer));
995 }
996
997
998 /***********************************************************************/
999 /* InputBoxDlgProc() -  Message handling routine for Input dialog box         */
1000 /***********************************************************************/
1001
1002 BOOL CALLBACK WINEXPORT
1003 InputBoxDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
1004 {
1005 LPTW lptw;
1006 LPMW lpmw;
1007     lptw = (LPTW)GetWindowLong(GetParent(hDlg), 0);
1008     lpmw = lptw->lpmw;
1009
1010     switch( message) {
1011         case WM_INITDIALOG:
1012             SetDlgItemText( hDlg, ID_PROMPT, lpmw->szPrompt);
1013             return( TRUE);
1014
1015         case WM_COMMAND:
1016             switch(LOWORD(wParam)) {
1017                 case ID_ANSWER:
1018                     return( TRUE);
1019
1020                 case IDOK:
1021                     lpmw->nChar = GetDlgItemText( hDlg, ID_ANSWER, lpmw->szAnswer, MAXSTR);
1022                     EndDialog( hDlg, TRUE);
1023                     return( TRUE);
1024
1025                 case IDCANCEL:
1026                     lpmw->szAnswer[0] = 0;
1027                     EndDialog( hDlg, FALSE);
1028                     return( TRUE);
1029
1030                 default:
1031                     return( FALSE);
1032                 }
1033         default:
1034             return( FALSE);
1035         }
1036     }
1037
1038
1039 LRESULT CALLBACK WINEXPORT
1040 MenuButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1041 {
1042 LPTW lptw;
1043 LPMW lpmw;
1044 #ifdef WIN32
1045 LONG n = GetWindowLong(hwnd, GWL_ID);
1046 #else
1047 WORD n = GetWindowWord(hwnd, GWW_ID);
1048 #endif
1049         lptw = (LPTW)GetWindowLong(GetParent(hwnd), 0);
1050         lpmw = lptw->lpmw;
1051
1052         switch(message) {
1053                 case WM_LBUTTONUP:
1054                         {
1055                         RECT rect;
1056                         POINT pt;
1057                         GetWindowRect(hwnd, &rect);
1058                         GetCursorPos(&pt);
1059                         if (PtInRect(&rect, pt))
1060                                 SendMessage(lptw->hWndText, WM_COMMAND, lpmw->hButtonID[n], 0L);
1061                         SetFocus(lptw->hWndText);
1062                         }
1063                         break;
1064         }
1065         return CallWindowProc((lpmw->lpfnButtonProc[n]), hwnd, message, wParam, lParam);
1066 }