Rev 199 | Rev 268 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
259 | daniel-mar | 1 | /* |
2 | This file is part of a common library |
||
3 | Copyright (C) 2002-6 Toby Thain, toby@telegraphics.com.au |
||
4 | |||
5 | This program is free software; you can redistribute it and/or modify |
||
6 | it under the terms of the GNU General Public License as published by |
||
7 | the Free Software Foundation; either version 2 of the License, or |
||
8 | (at your option) any later version. |
||
9 | |||
10 | This program is distributed in the hope that it will be useful, |
||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | GNU General Public License for more details. |
||
14 | |||
15 | You should have received a copy of the GNU General Public License |
||
16 | along with this program; if not, write to the Free Software |
||
17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
18 | */ |
||
19 | |||
20 | /* Choose file dialog using Navigation Services |
||
21 | (C) 2002-6 Toby Thain <toby@telegraphics.com.au> */ |
||
22 | |||
23 | #include <memory.h> |
||
24 | #include <script.h> |
||
25 | #include <plstringfuncs.h> |
||
26 | #include <stringcompare.h> |
||
27 | #include <aedatamodel.h> |
||
28 | |||
29 | #include <string.h> |
||
30 | #include <ctype.h> |
||
31 | #include <stdio.h> |
||
32 | |||
33 | #include "choosefile.h" |
||
34 | #include "dbg.h" |
||
35 | #include "str.h" |
||
36 | |||
37 | #if ! OPAQUE_TOOLBOX_STRUCTS |
||
38 | #define AEGetDescData BlockMove |
||
39 | #endif |
||
40 | |||
41 | struct exts_types{ |
||
42 | char *filter; |
||
43 | int numtypes; |
||
44 | OSType *typelist; |
||
45 | }; |
||
46 | |||
47 | Boolean matchext(StringPtr name,char *ext); |
||
48 | Boolean matchextfilter(StringPtr name,struct exts_types *ud); |
||
49 | Boolean matchtypelist(NavFileOrFolderInfo* info,struct exts_types *ud); |
||
50 | pascal Boolean myFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode); |
||
51 | |||
52 | int strincmp(char *s1, char *s2, int n) { |
||
53 | /* case insensitive comparison */ |
||
54 | int d; |
||
55 | while (--n >= 0) { |
||
56 | d = tolower(*s1) - tolower(*s2); |
||
57 | if (d != 0 || *s1 == '\0' || *s2 == '\0') return d; |
||
58 | ++s1; |
||
59 | ++s2; |
||
60 | } |
||
61 | return 0; |
||
62 | } |
||
63 | |||
64 | Boolean matchext(StringPtr name,char *ext){ |
||
65 | Ptr pos = PLstrrchr(name,'.'); |
||
66 | int len = strlen(ext); |
||
67 | return pos && (name[0] - (pos-(Ptr)name) == len) && !strincmp(pos+1,ext,len); |
||
68 | } |
||
69 | |||
70 | /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/CommonDialogBoxLibrary/CommonDialogBoxReference/CommonDialogBoxStructures/OPENFILENAME.asp |
||
71 | lpstrFilter |
||
72 | Pointer to a buffer containing pairs of null-terminated filter strings. |
||
73 | The last string in the buffer must be terminated by two NULL characters. |
||
74 | The first string in each pair is a display string that describes the filter |
||
75 | (for example, "Text Files"), and the second string specifies the filter pattern |
||
76 | (for example, "*.TXT"). To specify multiple filter patterns for a single display string, |
||
77 | use a semicolon to separate the patterns (for example, "*.TXT;*.DOC;*.BAK"). |
||
78 | A pattern string can be a combination of valid file name characters |
||
79 | and the asterisk (*) wildcard character. Do not include spaces in the pattern string. |
||
80 | */ |
||
81 | // example: |
||
82 | // "All supported files (.AFS, .8BF, .TXT)\0*.AFS;*.8BF;*.TXT\0All files (*.*)\0*.*\0\0" |
||
83 | |||
84 | Boolean matchextfilter(StringPtr name,struct exts_types *ud){ |
||
85 | Ptr pos = PLstrrchr(name,'.'); |
||
86 | char *p = ud->filter,*q; |
||
87 | int len; |
||
88 | |||
89 | if(p){ |
||
90 | // process only the FIRST string pair (in Windows GetFile box, |
||
91 | // the user gets to choose which filter string to apply) |
||
92 | |||
93 | // skip over display string |
||
94 | while(*p++) |
||
95 | ; |
||
96 | |||
97 | // keep going until second string ends |
||
98 | while(*p){ |
||
99 | |||
100 | if(p[0] == '*' && p[1] == '.'){ // only match entries of '*.XXX' form |
||
101 | |||
102 | if(p[2] == '*') |
||
103 | return true; // looks like it's "*.*" |
||
104 | |||
105 | p += 2; |
||
106 | |||
107 | if(pos){ // file name does have an extension |
||
108 | q = pos+1; // point to first char of extension |
||
109 | len = name[0] - (pos-(Ptr)name); |
||
110 | |||
111 | // match characters until filename ends, pattern ends (NUL or ';') or mismatch |
||
112 | while( len && *p && *p != ';' && toupper(*q++) == toupper(*p++) ) |
||
113 | --len; |
||
114 | |||
115 | // check if whole pattern matched |
||
116 | if( len == 0 && (!*p || *p == ';') ) |
||
117 | return true; |
||
118 | } |
||
119 | } |
||
120 | |||
121 | // skip to next item (after ';') |
||
122 | while( *p && *p != ';' ) |
||
123 | ++p; |
||
124 | if(*p) ++p; // skip over semicolon |
||
125 | |||
126 | } |
||
127 | |||
128 | } |
||
129 | return false; |
||
130 | } |
||
131 | |||
132 | Boolean matchtypelist(NavFileOrFolderInfo* info,struct exts_types *ud){ |
||
133 | int i; |
||
134 | |||
135 | if(ud->typelist) |
||
136 | for(i=0;i<ud->numtypes;++i) |
||
137 | if(info->fileAndFolder.fileInfo.finderInfo.fdType == ud->typelist[i]) |
||
138 | return true; |
||
139 | return false; |
||
140 | } |
||
141 | |||
142 | pascal Boolean myFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) |
||
143 | { |
||
144 | NavFileOrFolderInfo *theInfo = (NavFileOrFolderInfo*)info; |
||
145 | OSStatus e = noErr; |
||
146 | AEDesc theDesc; |
||
147 | FSSpec fss; |
||
148 | |||
149 | if( !(e = AECoerceDesc(theItem,typeFSS,&theDesc)) ){ |
||
150 | AEGetDescData(&theDesc,&fss,sizeof(FSSpec)); |
||
151 | AEDisposeDesc(&theDesc); |
||
152 | return e || theItem->descriptorType != typeFSS || theInfo->isFolder |
||
153 | || matchextfilter(fss.name,(struct exts_types*)callBackUD) |
||
154 | || matchtypelist(theInfo,(struct exts_types*)callBackUD); |
||
155 | } |
||
156 | return true; |
||
157 | } |
||
158 | |||
159 | Boolean choosefiletypes(StringPtr prompt,StandardFileReply *sfr,NavReplyRecord *reply, |
||
160 | OSType types[],int ntypes,const char *lpstrFilter) |
||
161 | { |
||
162 | //NavTypeListHandle tl = (NavTypeListHandle)NewHandle(sizeof(NavTypeList) + ntypes*sizeof(OSType)); |
||
163 | OSErr e; |
||
164 | long count; |
||
165 | AEKeyword theKeyword; |
||
166 | DescType actualType; |
||
167 | Size actualSize; |
||
168 | NavDialogOptions dopts; |
||
169 | NavObjectFilterUPP filter_upp = NewNavObjectFilterUPP(myFilterProc); |
||
170 | struct exts_types ud; |
||
171 | |||
172 | sfr->sfGood = false; |
||
173 | |||
174 | if(!(e = NavGetDefaultDialogOptions(&dopts))){ |
||
175 | PLstrcpy(dopts.message,prompt); |
||
176 | ud.filter = lpstrFilter; |
||
177 | ud.numtypes = ntypes; |
||
178 | ud.typelist = types; |
||
179 | e = NavChooseFile(NULL,reply,&dopts,NULL,NULL,filter_upp,NULL/*tl*/,&ud); |
||
180 | } |
||
181 | |||
182 | if(!e && reply->validRecord ){ |
||
183 | if ( !(e = AECountItems(&reply->selection, &count)) |
||
184 | && count==1 |
||
185 | && !(e = AEGetNthPtr(&reply->selection,1,typeFSS,&theKeyword,&actualType, |
||
186 | &sfr->sfFile,sizeof(FSSpec),&actualSize)) ){ |
||
187 | sfr->sfScript = reply->keyScript; |
||
188 | sfr->sfGood = true; |
||
189 | } |
||
190 | // NavDisposeReply(&reply); // caller must dispose |
||
191 | } |
||
192 | |||
193 | DisposeNavObjectFilterUPP(filter_upp); |
||
194 | return sfr->sfGood; |
||
195 | } |
||
196 | |||
197 | Boolean choosefile(StringPtr prompt,StandardFileReply *sfr, |
||
198 | NavReplyRecord *reply,OSType type,const char *lpstrFilter) |
||
199 | { |
||
200 | return choosefiletypes(prompt,sfr,reply,&type,1,lpstrFilter); |
||
201 | } |
||
202 | |||
203 | Boolean putfile(StringPtr prompt,StringPtr fname,OSType fileType,OSType fileCreator, |
||
204 | NavReplyRecord *reply,StandardFileReply *sfr, |
||
205 | char *lpstrDefExt,const char *lpstrFilter,int nFilterIndex){ |
||
206 | // NavReplyRecord reply; |
||
207 | NavDialogOptions dopts; |
||
208 | // AEDesc defaultLocation; |
||
209 | AEKeyword theKeyword; |
||
210 | DescType actualType; |
||
211 | Size actualSize; |
||
212 | OSErr e; |
||
213 | |||
214 | sfr->sfGood = false; |
||
215 | |||
216 | NavGetDefaultDialogOptions(&dopts); |
||
217 | dopts.dialogOptionFlags |= kNavNoTypePopup; |
||
218 | dopts.dialogOptionFlags &= ~kNavAllowStationery; |
||
219 | PLstrcpy(dopts.savedFileName,fname); /* default name for text box in NavPutFile (or null string for default) */ |
||
220 | /* in two minds whether to append default extension or not, on Mac; skip for now. |
||
221 | if(lpstrDefExt && fname[0]){ |
||
222 | dopts.savedFileName[++*dopts.savedFileName] = '.'; |
||
223 | memcpy(dopts.savedFileName+1+*dopts.savedFileName,lpstrDefExt,strlen(lpstrDefExt)); |
||
224 | *dopts.savedFileName += strlen(lpstrDefExt); |
||
225 | } |
||
226 | */ |
||
227 | PLstrcpy(dopts.message,prompt); /* custom message prompt (or null string for default) */ |
||
228 | |||
229 | if( !(e = NavPutFile(NULL,reply,&dopts,NULL,fileType,fileCreator,NULL)) && reply->validRecord ){ |
||
230 | if( !(e = AEGetNthPtr(&(reply->selection), 1, typeFSS, |
||
231 | &theKeyword, &actualType, &sfr->sfFile, sizeof(FSSpec), &actualSize)) ){ |
||
232 | sfr->sfScript = reply->keyScript; |
||
233 | sfr->sfGood = true; |
||
234 | } |
||
235 | } |
||
236 | |||
237 | return sfr->sfGood; |
||
238 | } |
||
239 | |||
240 | OSErr completesave(NavReplyRecord *reply){ |
||
241 | OSErr e = NavCompleteSave(reply,kNavTranslateInPlace); |
||
242 | NavDisposeReply(reply); |
||
243 | return e; |
||
244 | } |