]> git.saurik.com Git - wxWidgets.git/blob - src/mac/morefile/MoreDesk.cpp
another attempt to fix wxPanel/wxFrame::m_winLastFocused handling
[wxWidgets.git] / src / mac / morefile / MoreDesk.cpp
1 /*
2 ** Apple Macintosh Developer Technical Support
3 **
4 ** A collection of useful high-level Desktop Manager routines.
5 ** If the Desktop Manager isn't available, use the Desktop file
6 ** for 'read' operations.
7 **
8 ** We do more because we can...
9 **
10 ** by Jim Luther and Nitin Ganatra, Apple Developer Technical Support Emeriti
11 **
12 ** File: MoreDesktopMgr.c
13 **
14 ** Copyright © 1992-1998 Apple Computer, Inc.
15 ** All rights reserved.
16 **
17 ** You may incorporate this sample code into your applications without
18 ** restriction, though the sample code has been provided "AS IS" and the
19 ** responsibility for its operation is 100% yours. However, what you are
20 ** not permitted to do is to redistribute the source as "DSC Sample Code"
21 ** after having made changes. If you're going to re-distribute the source,
22 ** we require that you make it clear in the source that the code was
23 ** descended from Apple Sample Code, but that you've made changes.
24 */
25
26 #include <Types.h>
27 #include <Errors.h>
28 #include <Memory.h>
29 #include <Files.h>
30 #include <Resources.h>
31 #include <Icons.h>
32
33 #define __COMPILINGMOREFILES
34
35 #include "morefile.h"
36 #include "moreextr.h"
37 #include "mfsearch.h"
38 #include "moredesk.h"
39
40 /*****************************************************************************/
41
42 /* Desktop file notes:
43 **
44 ** ¥ The Desktop file is owned by the Finder and is normally open by the
45 ** Finder. That means that we only have read-only access to the Desktop
46 ** file.
47 ** ¥ Since the Resource Manager doesn't support shared access to resource
48 ** files and we're using read-only access, we don't ever leave the
49 ** Desktop file open. We open a path to it, get the data we want out
50 ** of it, and then close the open path. This is the only safe way to
51 ** open a resource file with read-only access since some other program
52 ** could have it open with write access.
53 ** ¥ The bundle related resources in the Desktop file are normally
54 ** purgable, so when we're looking through them, we don't bother to
55 ** release resources we're done looking at - closing the resource file
56 ** (which we always do) will release them.
57 ** ¥ Since we can't assume the Desktop file is named "Desktop"
58 ** (it probably is everywhere but France), we get the Desktop
59 ** file's name by searching the volume's root directory for a file
60 ** with fileType == 'FNDR' and creator == 'ERIK'. The only problem with
61 ** this scheme is that someone could create another file with that type
62 ** and creator in the root directory and we'd find the wrong file.
63 ** The chances of this are very slim.
64 */
65
66 /*****************************************************************************/
67
68 /* local defines */
69
70 enum
71 {
72 kBNDLResType = 'BNDL',
73 kFREFResType = 'FREF',
74 kIconFamResType = 'ICN#',
75 kFCMTResType = 'FCMT',
76 kAPPLResType = 'APPL'
77 };
78
79 /*****************************************************************************/
80
81 /* local data structures */
82
83 #if PRAGMA_STRUCT_ALIGN
84 #pragma options align=mac68k
85 #elif PRAGMA_STRUCT_PACKPUSH
86 #pragma pack(push, 2)
87 #elif PRAGMA_STRUCT_PACK
88 #pragma pack(2)
89 #endif
90
91 struct IDRec
92 {
93 short localID;
94 short rsrcID;
95 };
96 typedef struct IDRec IDRec;
97 typedef IDRec *IDRecPtr;
98
99 struct BundleType
100 {
101 OSType type; /* 'ICN#' or 'FREF' */
102 short count; /* number of IDRecs - 1 */
103 IDRec idArray[1];
104 };
105 typedef struct BundleType BundleType;
106 typedef BundleType *BundleTypePtr;
107
108 struct BNDLRec
109 {
110 OSType signature; /* creator type signature */
111 short versionID; /* version - should always be 0 */
112 short numTypes; /* number of elements in typeArray - 1 */
113 BundleType typeArray[1];
114 };
115 typedef struct BNDLRec BNDLRec;
116 typedef BNDLRec **BNDLRecHandle;
117
118 struct FREFRec
119 {
120 OSType fileType; /* file type */
121 short iconID; /* icon local ID */
122 Str255 fileName; /* file name */
123 };
124 typedef struct FREFRec FREFRec;
125 typedef FREFRec **FREFRecHandle;
126
127 struct APPLRec
128 {
129 OSType creator; /* creator type signature */
130 long parID; /* parent directory ID */
131 Str255 applName; /* application name */
132 };
133 typedef struct APPLRec APPLRec;
134 typedef APPLRec *APPLRecPtr;
135
136 #if PRAGMA_STRUCT_ALIGN
137 #pragma options align=reset
138 #elif PRAGMA_STRUCT_PACKPUSH
139 #pragma pack(pop)
140 #elif PRAGMA_STRUCT_PACK
141 #pragma pack()
142 #endif
143
144 /*****************************************************************************/
145
146 /* static prototypes */
147
148 static OSErr GetDesktopFileName(short vRefNum,
149 Str255 desktopName);
150
151 static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName,
152 short vRefNum,
153 OSType creator,
154 short *applVRefNum,
155 long *applParID,
156 Str255 applName);
157
158 static OSErr FindBundleGivenCreator(OSType creator,
159 BNDLRecHandle *returnBndl);
160
161 static OSErr FindTypeInBundle(OSType typeToFind,
162 BNDLRecHandle theBndl,
163 BundleTypePtr *returnBundleType);
164
165 static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType,
166 OSType fileType,
167 short *iconLocalID);
168
169 static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
170 short iconLocalID,
171 short *iconRsrcID);
172
173 static OSType DTIconToResIcon(short iconType);
174
175 static OSErr GetIconFromDesktopFile(ConstStr255Param volName,
176 short vRefNum,
177 short iconType,
178 OSType fileCreator,
179 OSType fileType,
180 Handle *iconHandle);
181
182 static OSErr GetCommentID(short vRefNum,
183 long dirID,
184 ConstStr255Param name,
185 short *commentID);
186
187 static OSErr GetCommentFromDesktopFile(short vRefNum,
188 long dirID,
189 ConstStr255Param name,
190 Str255 comment);
191
192 /*****************************************************************************/
193
194 /*
195 ** GetDesktopFileName
196 **
197 ** Get the name of the Desktop file.
198 */
199 static OSErr GetDesktopFileName(short vRefNum,
200 Str255 desktopName)
201 {
202 OSErr error;
203 HParamBlockRec pb;
204 short index;
205 Boolean found;
206
207 pb.fileParam.ioNamePtr = desktopName;
208 pb.fileParam.ioVRefNum = vRefNum;
209 pb.fileParam.ioFVersNum = 0;
210 index = 1;
211 found = false;
212 do
213 {
214 pb.fileParam.ioDirID = fsRtDirID;
215 pb.fileParam.ioFDirIndex = index;
216 error = PBHGetFInfoSync(&pb);
217 if ( error == noErr )
218 {
219 if ( (pb.fileParam.ioFlFndrInfo.fdType == 'FNDR') &&
220 (pb.fileParam.ioFlFndrInfo.fdCreator == 'ERIK') )
221 {
222 found = true;
223 }
224 }
225 ++index;
226 } while ( (error == noErr) && !found );
227
228 return ( error );
229 }
230
231 /*****************************************************************************/
232
233 pascal OSErr DTOpen(ConstStr255Param volName,
234 short vRefNum,
235 short *dtRefNum,
236 Boolean *newDTDatabase)
237 {
238 OSErr error;
239 GetVolParmsInfoBuffer volParmsInfo;
240 long infoSize;
241 DTPBRec pb;
242
243 /* Check for volume Desktop Manager support before calling */
244 infoSize = sizeof(GetVolParmsInfoBuffer);
245 error = HGetVolParms(volName, vRefNum, &volParmsInfo, &infoSize);
246 if ( error == noErr )
247 {
248 if ( hasDesktopMgr(volParmsInfo) )
249 {
250 pb.ioNamePtr = (StringPtr)volName;
251 pb.ioVRefNum = vRefNum;
252 error = PBDTOpenInform(&pb);
253 /* PBDTOpenInform informs us if the desktop was just created */
254 /* by leaving the low bit of ioTagInfo clear (0) */
255 *newDTDatabase = ((pb.ioTagInfo & 1L) == 0);
256 if ( error == paramErr )
257 {
258 error = PBDTGetPath(&pb);
259 /* PBDTGetPath doesn't tell us if the database is new */
260 /* so assume it is not new */
261 *newDTDatabase = false;
262 }
263 *dtRefNum = pb.ioDTRefNum;
264 }
265 else
266 {
267 error = paramErr;
268 }
269 }
270 return ( error );
271 }
272
273 /*****************************************************************************/
274
275 /*
276 ** GetAPPLFromDesktopFile
277 **
278 ** Get a application's location from the
279 ** Desktop file's 'APPL' resources.
280 */
281 static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName,
282 short vRefNum,
283 OSType creator,
284 short *applVRefNum,
285 long *applParID,
286 Str255 applName)
287 {
288 OSErr error;
289 short realVRefNum;
290 Str255 desktopName;
291 short savedResFile;
292 short dfRefNum;
293 Handle applResHandle;
294 Boolean foundCreator;
295 Ptr applPtr;
296 long applSize;
297
298 error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
299 if ( error == noErr )
300 {
301 error = GetDesktopFileName(realVRefNum, desktopName);
302 if ( error == noErr )
303 {
304 savedResFile = CurResFile();
305 /*
306 ** Open the 'Desktop' file in the root directory. (because
307 ** opening the resource file could preload unwanted resources,
308 ** bracket the call with SetResLoad(s))
309 */
310 SetResLoad(false);
311 dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
312 SetResLoad(true);
313
314 if ( dfRefNum != -1)
315 {
316 /* Get 'APPL' resource ID 0 */
317 applResHandle = Get1Resource(kAPPLResType, 0);
318 if ( applResHandle != NULL )
319 {
320 #if !TARGET_CARBON
321 applSize = InlineGetHandleSize((Handle)applResHandle);
322 #else
323 applSize = GetHandleSize((Handle)applResHandle);
324 #endif
325 if ( applSize != 0 ) /* make sure the APPL resource isn't empty */
326 {
327 foundCreator = false;
328 applPtr = *applResHandle;
329
330 /* APPL's don't have a count so I have to use the size as the bounds */
331 while ( (foundCreator == false) &&
332 (applPtr < (*applResHandle + applSize)) )
333 {
334 if ( ((APPLRecPtr)applPtr)->creator == creator )
335 {
336 foundCreator = true;
337 }
338 else
339 {
340 /* fun with pointer math... */
341 applPtr += sizeof(OSType) +
342 sizeof(long) +
343 ((APPLRecPtr)applPtr)->applName[0] + 1;
344 /* application mappings are word aligned within the resource */
345 if ( ((unsigned long)applPtr % 2) != 0 )
346 {
347 applPtr += 1;
348 }
349 }
350 }
351 if ( foundCreator == true )
352 {
353 *applVRefNum = realVRefNum;
354 *applParID = ((APPLRecPtr)applPtr)->parID;
355 BlockMoveData(((APPLRecPtr)applPtr)->applName,
356 applName,
357 ((APPLRecPtr)applPtr)->applName[0] + 1);
358 /* error is already noErr */
359 }
360 else
361 {
362 error = afpItemNotFound; /* didn't find a creator match */
363 }
364 }
365 else
366 {
367 error = afpItemNotFound; /* no APPL mapping available */
368 }
369 }
370 else
371 {
372 error = afpItemNotFound; /* no APPL mapping available */
373 }
374
375 /* restore the resource chain and close the Desktop file */
376 UseResFile(savedResFile);
377 CloseResFile(dfRefNum);
378 }
379 else
380 {
381 error = afpItemNotFound;
382 }
383 }
384 }
385
386 return ( error );
387 }
388
389 /*****************************************************************************/
390
391 pascal OSErr DTXGetAPPL(ConstStr255Param volName,
392 short vRefNum,
393 OSType creator,
394 Boolean searchCatalog,
395 short *applVRefNum,
396 long *applParID,
397 Str255 applName)
398 {
399 OSErr error;
400 UniversalFMPB pb;
401 short dtRefNum;
402 Boolean newDTDatabase;
403 short realVRefNum;
404 short index;
405 Boolean applFound;
406 FSSpec spec;
407 long actMatchCount;
408
409 /* get the real vRefNum */
410 error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
411 if ( error == noErr )
412 {
413 error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
414 if ( error == noErr )
415 {
416 if ( !newDTDatabase )
417 {
418 index = 0;
419 applFound = false;
420 do
421 {
422 pb.dtPB.ioNamePtr = applName;
423 pb.dtPB.ioDTRefNum = dtRefNum;
424 pb.dtPB.ioIndex = index;
425 pb.dtPB.ioFileCreator = creator;
426 error = PBDTGetAPPLSync(&pb.dtPB);
427 if ( error == noErr )
428 {
429 /* got a match - see if it is valid */
430
431 *applVRefNum = realVRefNum; /* get the vRefNum now */
432 *applParID = pb.dtPB.ioAPPLParID; /* get the parent ID now */
433
434 /* pb.hPB.fileParam.ioNamePtr is already set */
435 pb.hPB.fileParam.ioVRefNum = realVRefNum;
436 pb.hPB.fileParam.ioFVersNum = 0;
437 pb.hPB.fileParam.ioDirID = *applParID;
438 pb.hPB.fileParam.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
439 if ( PBHGetFInfoSync(&pb.hPB) == noErr )
440 {
441 if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator == creator) &&
442 (pb.hPB.fileParam.ioFlFndrInfo.fdType == 'APPL') )
443 {
444 applFound = true;
445 }
446 }
447 }
448 ++index;
449 } while ( (error == noErr) && !applFound );
450 if ( error != noErr )
451 {
452 error = afpItemNotFound;
453 }
454 }
455 else
456 {
457 /* Desktop database is empty (new), set error to try CatSearch */
458 error = afpItemNotFound;
459 }
460 }
461 /* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */
462 if ( error == paramErr )
463 {
464 /* if paramErr, the volume didn't support the Desktop Manager */
465 /* try the Desktop file */
466
467 error = GetAPPLFromDesktopFile(volName, vRefNum, creator,
468 applVRefNum, applParID, applName);
469 if ( error == noErr )
470 {
471 /* got a match - see if it is valid */
472
473 pb.hPB.fileParam.ioNamePtr = applName;
474 pb.hPB.fileParam.ioVRefNum = *applVRefNum;
475 pb.hPB.fileParam.ioFVersNum = 0;
476 pb.hPB.fileParam.ioDirID = *applParID;
477 pb.hPB.fileParam.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
478 if ( PBHGetFInfoSync(&pb.hPB) == noErr )
479 {
480 if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator != creator) ||
481 (pb.hPB.fileParam.ioFlFndrInfo.fdType != 'APPL') )
482 {
483 error = afpItemNotFound;
484 }
485 }
486 else if ( error == fnfErr )
487 {
488 error = afpItemNotFound;
489 }
490 }
491 }
492 /* acceptable error from DesktopFile code to continue is afpItemNotFound */
493 if ( (error == afpItemNotFound) && searchCatalog)
494 {
495 /* Couldn't be found in the Desktop file either, */
496 /* try searching with CatSearch if requested */
497
498 error = CreatorTypeFileSearch(NULL, realVRefNum, creator, kAPPLResType, &spec, 1,
499 &actMatchCount, true);
500 if ( (error == noErr) || (error == eofErr) )
501 {
502 if ( actMatchCount > 0 )
503 {
504 *applVRefNum = spec.vRefNum;
505 *applParID = spec.parID;
506 BlockMoveData(spec.name, applName, spec.name[0] + 1);
507 }
508 else
509 {
510 error = afpItemNotFound;
511 }
512 }
513 }
514 }
515
516 return ( error );
517 }
518
519 /*****************************************************************************/
520
521 pascal OSErr FSpDTXGetAPPL(ConstStr255Param volName,
522 short vRefNum,
523 OSType creator,
524 Boolean searchCatalog,
525 FSSpec *spec)
526 {
527 return ( DTXGetAPPL(volName, vRefNum, creator, searchCatalog,
528 &(spec->vRefNum), &(spec->parID), spec->name) );
529 }
530
531 /*****************************************************************************/
532
533 pascal OSErr DTGetAPPL(ConstStr255Param volName,
534 short vRefNum,
535 OSType creator,
536 short *applVRefNum,
537 long *applParID,
538 Str255 applName)
539 {
540 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
541 return ( DTXGetAPPL(volName, vRefNum, creator, true,
542 applVRefNum, applParID, applName) );
543 }
544
545 /*****************************************************************************/
546
547 pascal OSErr FSpDTGetAPPL(ConstStr255Param volName,
548 short vRefNum,
549 OSType creator,
550 FSSpec *spec)
551 {
552 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
553 return ( DTXGetAPPL(volName, vRefNum, creator, true,
554 &(spec->vRefNum), &(spec->parID), spec->name) );
555 }
556
557 /*****************************************************************************/
558
559 /*
560 ** FindBundleGivenCreator
561 **
562 ** Search the current resource file for the 'BNDL' resource with the given
563 ** creator and return a handle to it.
564 */
565 static OSErr FindBundleGivenCreator(OSType creator,
566 BNDLRecHandle *returnBndl)
567 {
568 OSErr error;
569 short numOfBundles;
570 short index;
571 BNDLRecHandle theBndl;
572
573 error = afpItemNotFound; /* default to not found */
574
575 /* Search each BNDL resource until we find the one with a matching creator. */
576
577 numOfBundles = Count1Resources(kBNDLResType);
578 index = 1;
579 *returnBndl = NULL;
580
581 while ( (index <= numOfBundles) && (*returnBndl == NULL) )
582 {
583 theBndl = (BNDLRecHandle)Get1IndResource(kBNDLResType, index);
584
585 if ( theBndl != NULL )
586 {
587 if ( (*theBndl)->signature == creator )
588 {
589 /* numTypes and typeArray->count will always be the actual count minus 1, */
590 /* so 0 in both fields is valid. */
591 if ( ((*theBndl)->numTypes >= 0) && ((*theBndl)->typeArray->count >= 0) )
592 {
593 /* got it */
594 *returnBndl = theBndl;
595 error = noErr;
596 }
597 }
598 }
599
600 index ++;
601 }
602
603 return ( error );
604 }
605
606 /*****************************************************************************/
607
608 /*
609 ** FindTypeInBundle
610 **
611 ** Given a Handle to a BNDL return a pointer to the desired type
612 ** in it. If the type is not found, or if the type's count < 0,
613 ** return afpItemNotFound.
614 */
615 static OSErr FindTypeInBundle(OSType typeToFind,
616 BNDLRecHandle theBndl,
617 BundleTypePtr *returnBundleType)
618 {
619 OSErr error;
620 short index;
621 Ptr ptrIterator; /* use a Ptr so we can do ugly pointer math */
622
623 error = afpItemNotFound; /* default to not found */
624
625 ptrIterator = (Ptr)((*theBndl)->typeArray);
626 index = 0;
627 *returnBundleType = NULL;
628
629 while ( (index < ((*theBndl)->numTypes + 1)) &&
630 (*returnBundleType == NULL) )
631 {
632 if ( (((BundleTypePtr)ptrIterator)->type == typeToFind) &&
633 (((BundleTypePtr)ptrIterator)->count >= 0) )
634 {
635 *returnBundleType = (BundleTypePtr)ptrIterator;
636 error = noErr;
637 }
638 else
639 {
640 ptrIterator += ( sizeof(OSType) +
641 sizeof(short) +
642 ( sizeof(IDRec) * (((BundleTypePtr)ptrIterator)->count + 1) ) );
643 ++index;
644 }
645 }
646
647 return ( error );
648 }
649
650 /*****************************************************************************/
651
652 /*
653 ** GetLocalIDFromFREF
654 **
655 ** Given a pointer to a 'FREF' BundleType record, load each 'FREF' resource
656 ** looking for a matching fileType. If a matching fileType is found, return
657 ** its icon local ID. If no match is found, return afpItemNotFound as the
658 ** function result.
659 */
660 static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType,
661 OSType fileType,
662 short *iconLocalID)
663 {
664 OSErr error;
665 short index;
666 IDRecPtr idIterator;
667 FREFRecHandle theFref;
668
669 error = afpItemNotFound; /* default to not found */
670
671 /* For each localID in this type, get the FREF resource looking for fileType */
672 index = 0;
673 idIterator = &theBundleType->idArray[0];
674 *iconLocalID = 0;
675
676 while ( (index <= theBundleType->count) && (*iconLocalID == 0) )
677 {
678 theFref = (FREFRecHandle)Get1Resource(kFREFResType, idIterator->rsrcID);
679 if ( theFref != NULL )
680 {
681 if ( (*theFref)->fileType == fileType )
682 {
683 *iconLocalID = (*theFref)->iconID;
684 error = noErr;
685 }
686 }
687
688 ++idIterator;
689 ++index;
690 }
691
692 return ( error );
693 }
694
695 /*****************************************************************************/
696
697 /*
698 ** GetIconRsrcIDFromLocalID
699 **
700 ** Given a pointer to a 'ICN#' BundleType record, look for the IDRec with
701 ** the localID that matches iconLocalID. If a matching IDRec is found,
702 ** return the IDRec's rsrcID field value. If no match is found, return
703 ** afpItemNotFound as the function result.
704 */
705 static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
706 short iconLocalID,
707 short *iconRsrcID)
708 {
709 OSErr error;
710 short index;
711 IDRecPtr idIterator;
712
713 error = afpItemNotFound; /* default to not found */
714
715 /* Find the rsrcID of the icon family type, given the localID */
716 index = 0;
717 idIterator = &theBundleType->idArray[0];
718 *iconRsrcID = 0;
719
720 while ( (index <= theBundleType->count) && (*iconRsrcID == 0) )
721 {
722 if ( idIterator->localID == iconLocalID )
723 {
724 *iconRsrcID = idIterator->rsrcID;
725 error = noErr;
726 }
727
728 idIterator ++;
729 index ++;
730 }
731
732 return ( error );
733 }
734
735 /*****************************************************************************/
736
737 /*
738 ** DTIconToResIcon
739 **
740 ** Map a Desktop Manager icon type to the corresponding resource type.
741 ** Return (OSType)0 if there is no corresponding resource type.
742 */
743 static OSType DTIconToResIcon(short iconType)
744 {
745 OSType resType;
746
747 switch ( iconType )
748 {
749 case kLargeIcon:
750 resType = large1BitMask;
751 break;
752 case kLarge4BitIcon:
753 resType = large4BitData;
754 break;
755 case kLarge8BitIcon:
756 resType = large8BitData;
757 break;
758 case kSmallIcon:
759 resType = small1BitMask;
760 break;
761 case kSmall4BitIcon:
762 resType = small4BitData;
763 break;
764 case kSmall8BitIcon:
765 resType = small8BitData;
766 break;
767 default:
768 resType = (OSType)0;
769 break;
770 }
771
772 return ( resType );
773 }
774
775 /*****************************************************************************/
776
777 /*
778 ** GetIconFromDesktopFile
779 **
780 ** INPUT a pointer to a non-existent Handle, because we'll allocate one
781 **
782 ** search each BNDL resource for the right fileCreator and once we get it
783 ** find the 'FREF' type in BNDL
784 ** for each localID in the type, open the FREF resource
785 ** if the FREF is the desired fileType
786 ** get its icon localID
787 ** get the ICN# type in BNDL
788 ** get the icon resource number from the icon localID
789 ** get the icon resource type from the desktop mgr's iconType
790 ** get the icon of that type and number
791 */
792 static OSErr GetIconFromDesktopFile(ConstStr255Param volName,
793 short vRefNum,
794 short iconType,
795 OSType fileCreator,
796 OSType fileType,
797 Handle *iconHandle)
798 {
799 OSErr error;
800 short realVRefNum;
801 Str255 desktopName;
802 short savedResFile;
803 short dfRefNum;
804 BNDLRecHandle theBndl = NULL;
805 BundleTypePtr theBundleType;
806 short iconLocalID;
807 short iconRsrcID;
808 OSType iconRsrcType;
809 Handle returnIconHandle;
810 char bndlState;
811
812 *iconHandle = NULL;
813
814 error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
815 if ( error == noErr )
816 {
817 error = GetDesktopFileName(realVRefNum, desktopName);
818 if ( error == noErr )
819 {
820 savedResFile = CurResFile();
821
822 /*
823 ** Open the 'Desktop' file in the root directory. (because
824 ** opening the resource file could preload unwanted resources,
825 ** bracket the call with SetResLoad(s))
826 */
827 SetResLoad(false);
828 dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
829 SetResLoad(true);
830
831 if ( dfRefNum != -1 )
832 {
833 /*
834 ** Find the BNDL resource with the specified creator.
835 */
836 error = FindBundleGivenCreator(fileCreator, &theBndl);
837 if ( error == noErr )
838 {
839 /* Lock the BNDL resource so it won't be purged when other resources are loaded */
840 bndlState = HGetState((Handle)theBndl);
841 HLock((Handle)theBndl);
842
843 /* Find the 'FREF' BundleType record in the BNDL resource. */
844 error = FindTypeInBundle(kFREFResType, theBndl, &theBundleType);
845 if ( error == noErr )
846 {
847 /* Find the local ID in the 'FREF' resource with the specified fileType */
848 error = GetLocalIDFromFREF(theBundleType, fileType, &iconLocalID);
849 if ( error == noErr )
850 {
851 /* Find the 'ICN#' BundleType record in the BNDL resource. */
852 error = FindTypeInBundle(kIconFamResType, theBndl, &theBundleType);
853 if ( error == noErr )
854 {
855 /* Find the icon's resource ID in the 'ICN#' BundleType record */
856 error = GetIconRsrcIDFromLocalID(theBundleType, iconLocalID, &iconRsrcID);
857 if ( error == noErr )
858 {
859 /* Map Desktop Manager icon type to resource type */
860 iconRsrcType = DTIconToResIcon(iconType);
861
862 if ( iconRsrcType != (OSType)0 )
863 {
864 /* Load the icon */
865 returnIconHandle = Get1Resource(iconRsrcType, iconRsrcID);
866 if ( returnIconHandle != NULL )
867 {
868 /* Copy the resource handle, and return the copy */
869 HandToHand(&returnIconHandle);
870 if ( MemError() == noErr )
871 {
872 *iconHandle = returnIconHandle;
873 }
874 else
875 {
876 error = afpItemNotFound;
877 }
878 }
879 else
880 {
881 error = afpItemNotFound;
882 }
883 }
884 }
885 }
886 }
887 }
888 /* Restore the state of the BNDL resource */
889 HSetState((Handle)theBndl, bndlState);
890 }
891 /* Restore the resource chain and close the Desktop file */
892 UseResFile(savedResFile);
893 CloseResFile(dfRefNum);
894 }
895 else
896 {
897 error = ResError(); /* could not open Desktop file */
898 }
899 }
900 if ( (error != noErr) && (error != memFullErr) )
901 {
902 error = afpItemNotFound; /* force an error we should return */
903 }
904 }
905
906 return ( error );
907 }
908
909 /*****************************************************************************/
910
911 pascal OSErr DTGetIcon(ConstStr255Param volName,
912 short vRefNum,
913 short iconType,
914 OSType fileCreator,
915 OSType fileType,
916 Handle *iconHandle)
917 {
918 OSErr error;
919 DTPBRec pb;
920 short dtRefNum;
921 Boolean newDTDatabase;
922 Size bufferSize;
923
924 *iconHandle = NULL;
925 error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
926 if ( error == noErr )
927 {
928 /* there was a desktop database and it's now open */
929
930 if ( !newDTDatabase ) /* don't bother to look in a new (empty) database */
931 {
932 /* get the buffer size for the requested icon type */
933 switch ( iconType )
934 {
935 case kLargeIcon:
936 bufferSize = kLargeIconSize;
937 break;
938 case kLarge4BitIcon:
939 bufferSize = kLarge4BitIconSize;
940 break;
941 case kLarge8BitIcon:
942 bufferSize = kLarge8BitIconSize;
943 break;
944 case kSmallIcon:
945 bufferSize = kSmallIconSize;
946 break;
947 case kSmall4BitIcon:
948 bufferSize = kSmall4BitIconSize;
949 break;
950 case kSmall8BitIcon:
951 bufferSize = kSmall8BitIconSize;
952 break;
953 default:
954 iconType = 0;
955 bufferSize = 0;
956 break;
957 }
958 if ( bufferSize != 0 )
959 {
960 *iconHandle = NewHandle(bufferSize);
961 if ( *iconHandle != NULL )
962 {
963 HLock(*iconHandle);
964
965 pb.ioDTRefNum = dtRefNum;
966 pb.ioTagInfo = 0;
967 pb.ioDTBuffer = **iconHandle;
968 pb.ioDTReqCount = bufferSize;
969 pb.ioIconType = iconType;
970 pb.ioFileCreator = fileCreator;
971 pb.ioFileType = fileType;
972 error = PBDTGetIconSync(&pb);
973
974 HUnlock(*iconHandle);
975
976 if ( error != noErr )
977 {
978 DisposeHandle(*iconHandle); /* dispose of the allocated memory */
979 *iconHandle = NULL;
980 }
981 }
982 else
983 {
984 error = memFullErr; /* handle could not be allocated */
985 }
986 }
987 else
988 {
989 error = paramErr; /* unknown icon type requested */
990 }
991 }
992 else
993 {
994 error = afpItemNotFound; /* the desktop database was empty - nothing to return */
995 }
996 }
997 else
998 {
999 /* There is no desktop database - try the Desktop file */
1000
1001 error = GetIconFromDesktopFile(volName, vRefNum, iconType,
1002 fileCreator, fileType, iconHandle);
1003 }
1004
1005 return ( error );
1006 }
1007
1008 /*****************************************************************************/
1009
1010 pascal OSErr DTSetComment(short vRefNum,
1011 long dirID,
1012 ConstStr255Param name,
1013 ConstStr255Param comment)
1014 {
1015 DTPBRec pb;
1016 OSErr error;
1017 short dtRefNum;
1018 Boolean newDTDatabase;
1019
1020 error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
1021 if ( error == noErr )
1022 {
1023 pb.ioDTRefNum = dtRefNum;
1024 pb.ioNamePtr = (StringPtr)name;
1025 pb.ioDirID = dirID;
1026 pb.ioDTBuffer = (Ptr)&comment[1];
1027 /* Truncate the comment to 200 characters just in case */
1028 /* some file system doesn't range check */
1029 if ( comment[0] <= 200 )
1030 {
1031 pb.ioDTReqCount = comment[0];
1032 }
1033 else
1034 {
1035 pb.ioDTReqCount = 200;
1036 }
1037 error = PBDTSetCommentSync(&pb);
1038 }
1039 return (error);
1040 }
1041
1042 /*****************************************************************************/
1043
1044 pascal OSErr FSpDTSetComment(const FSSpec *spec,
1045 ConstStr255Param comment)
1046 {
1047 return (DTSetComment(spec->vRefNum, spec->parID, spec->name, comment));
1048 }
1049
1050 /*****************************************************************************/
1051
1052 /*
1053 ** GetCommentID
1054 **
1055 ** Get the comment ID number for the Desktop file's 'FCMT' resource ID from
1056 ** the file or folders fdComment (frComment) field.
1057 */
1058 static OSErr GetCommentID(short vRefNum,
1059 long dirID,
1060 ConstStr255Param name,
1061 short *commentID)
1062 {
1063 CInfoPBRec pb;
1064 OSErr error;
1065
1066 error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
1067 *commentID = pb.hFileInfo.ioFlXFndrInfo.fdComment;
1068 return ( error );
1069 }
1070
1071 /*****************************************************************************/
1072
1073 /*
1074 ** GetCommentFromDesktopFile
1075 **
1076 ** Get a file or directory's Finder comment field (if any) from the
1077 ** Desktop file's 'FCMT' resources.
1078 */
1079 static OSErr GetCommentFromDesktopFile(short vRefNum,
1080 long dirID,
1081 ConstStr255Param name,
1082 Str255 comment)
1083 {
1084 OSErr error;
1085 short commentID;
1086 short realVRefNum;
1087 Str255 desktopName;
1088 short savedResFile;
1089 short dfRefNum;
1090 StringHandle commentHandle;
1091
1092 /* Get the comment ID number */
1093 error = GetCommentID(vRefNum, dirID, name, &commentID);
1094 if ( error == noErr )
1095 {
1096 if ( commentID != 0 ) /* commentID == 0 means there's no comment */
1097 {
1098 error = DetermineVRefNum(name, vRefNum, &realVRefNum);
1099 if ( error == noErr )
1100 {
1101 error = GetDesktopFileName(realVRefNum, desktopName);
1102 if ( error == noErr )
1103 {
1104 savedResFile = CurResFile();
1105 /*
1106 ** Open the 'Desktop' file in the root directory. (because
1107 ** opening the resource file could preload unwanted resources,
1108 ** bracket the call with SetResLoad(s))
1109 */
1110 SetResLoad(false);
1111 dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
1112 SetResLoad(true);
1113
1114 if ( dfRefNum != -1)
1115 {
1116 /* Get the comment resource */
1117 commentHandle = (StringHandle)Get1Resource(kFCMTResType,commentID);
1118 if ( commentHandle != NULL )
1119 {
1120 #if !TARGET_CARBON
1121 if ( InlineGetHandleSize((Handle)commentHandle) > 0 )
1122 #else
1123 if ( GetHandleSize((Handle)commentHandle) > 0 )
1124 #endif
1125 {
1126 BlockMoveData(*commentHandle, comment, *commentHandle[0] + 1);
1127 }
1128 else
1129 {
1130 error = afpItemNotFound; /* no comment available */
1131 }
1132 }
1133 else
1134 {
1135 error = afpItemNotFound; /* no comment available */
1136 }
1137
1138 /* restore the resource chain and close the Desktop file */
1139 UseResFile(savedResFile);
1140 CloseResFile(dfRefNum);
1141 }
1142 else
1143 {
1144 error = afpItemNotFound;
1145 }
1146 }
1147 else
1148 {
1149 error = afpItemNotFound;
1150 }
1151 }
1152 }
1153 else
1154 {
1155 error = afpItemNotFound; /* no comment available */
1156 }
1157 }
1158
1159 return ( error );
1160 }
1161
1162 /*****************************************************************************/
1163
1164 pascal OSErr DTGetComment(short vRefNum,
1165 long dirID,
1166 ConstStr255Param name,
1167 Str255 comment)
1168 {
1169 DTPBRec pb;
1170 OSErr error;
1171 short dtRefNum;
1172 Boolean newDTDatabase;
1173
1174 if (comment != NULL)
1175 {
1176 comment[0] = 0; /* return nothing by default */
1177
1178 /* attempt to open the desktop database */
1179 error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
1180 if ( error == noErr )
1181 {
1182 /* There was a desktop database and it's now open */
1183
1184 if ( !newDTDatabase )
1185 {
1186 pb.ioDTRefNum = dtRefNum;
1187 pb.ioNamePtr = (StringPtr)name;
1188 pb.ioDirID = dirID;
1189 pb.ioDTBuffer = (Ptr)&comment[1];
1190 /*
1191 ** IMPORTANT NOTE #1: Inside Macintosh says that comments
1192 ** are up to 200 characters. While that may be correct for
1193 ** the HFS file system's Desktop Manager, other file
1194 ** systems (such as Apple Photo Access) return up to
1195 ** 255 characters. Make sure the comment buffer is a Str255
1196 ** or you'll regret it.
1197 **
1198 ** IMPORTANT NOTE #2: Although Inside Macintosh doesn't
1199 ** mention it, ioDTReqCount is a input field to
1200 ** PBDTGetCommentSync. Some file systems (like HFS) ignore
1201 ** ioDTReqCount and always return the full comment --
1202 ** others (like AppleShare) respect ioDTReqCount and only
1203 ** return up to ioDTReqCount characters of the comment.
1204 */
1205 pb.ioDTReqCount = sizeof(Str255) - 1;
1206 error = PBDTGetCommentSync(&pb);
1207 if (error == noErr)
1208 {
1209 comment[0] = (unsigned char)pb.ioDTActCount;
1210 }
1211 }
1212 }
1213 else
1214 {
1215 /* There is no desktop database - try the Desktop file */
1216 error = GetCommentFromDesktopFile(vRefNum, dirID, name, comment);
1217 if ( error != noErr )
1218 {
1219 error = afpItemNotFound; /* return an expected error */
1220 }
1221 }
1222 }
1223 else
1224 {
1225 error = paramErr;
1226 }
1227
1228 return (error);
1229 }
1230
1231 /*****************************************************************************/
1232
1233 pascal OSErr FSpDTGetComment(const FSSpec *spec,
1234 Str255 comment)
1235 {
1236 return (DTGetComment(spec->vRefNum, spec->parID, spec->name, comment));
1237 }
1238
1239 /*****************************************************************************/
1240
1241 pascal OSErr DTCopyComment(short srcVRefNum,
1242 long srcDirID,
1243 ConstStr255Param srcName,
1244 short dstVRefNum,
1245 long dstDirID,
1246 ConstStr255Param dstName)
1247 /* The destination volume must support the Desktop Manager for this to work */
1248 {
1249 OSErr error;
1250 Str255 comment;
1251
1252 error = DTGetComment(srcVRefNum, srcDirID, srcName, comment);
1253 if ( (error == noErr) && (comment[0] > 0) )
1254 {
1255 error = DTSetComment(dstVRefNum, dstDirID, dstName, comment);
1256 }
1257 return (error);
1258 }
1259
1260 /*****************************************************************************/
1261
1262 pascal OSErr FSpDTCopyComment(const FSSpec *srcSpec,
1263 const FSSpec *dstSpec)
1264 /* The destination volume must support the Desktop Manager for this to work */
1265 {
1266 return (DTCopyComment(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
1267 dstSpec->vRefNum, dstSpec->parID, dstSpec->name));
1268 }
1269
1270 /*****************************************************************************/