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