xref: /haiku/src/kits/tracker/Model.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 //	Dedicated to BModel
36 
37 // ToDo:
38 // Consider moving iconFrom logic to BPose
39 // use a more efficient way of storing file type and preferred app strings
40 
41 
42 #include "Model.h"
43 
44 #include <stdlib.h>
45 #include <strings.h>
46 
47 #include <fs_info.h>
48 #include <fs_attr.h>
49 
50 #include <AppDefs.h>
51 #include <Bitmap.h>
52 #include <Catalog.h>
53 #include <Debug.h>
54 #include <Directory.h>
55 #include <Entry.h>
56 #include <File.h>
57 #include <Locale.h>
58 #include <NodeInfo.h>
59 #include <NodeMonitor.h>
60 #include <Path.h>
61 #include <SymLink.h>
62 #include <Query.h>
63 #include <Volume.h>
64 #include <VolumeRoster.h>
65 
66 #include "Attributes.h"
67 #include "Bitmaps.h"
68 #include "FindPanel.h"
69 #include "FSUtils.h"
70 #include "MimeTypes.h"
71 #include "Thumbnails.h"
72 #include "Tracker.h"
73 #include "Utilities.h"
74 
75 
76 #undef B_TRANSLATION_CONTEXT
77 #define B_TRANSLATION_CONTEXT "Model"
78 
79 
80 #ifdef CHECK_OPEN_MODEL_LEAKS
81 BObjectList<Model>* writableOpenModelList = NULL;
82 BObjectList<Model>* readOnlyOpenModelList = NULL;
83 #endif
84 
85 
86 //	#pragma mark - Model()
87 
88 
89 Model::Model()
90 	:
91 	fPreferredAppName(NULL),
92 	fBaseType(kUnknownNode),
93 	fIconFrom(kUnknownSource),
94 	fWritable(false),
95 	fNode(NULL),
96 	fStatus(B_NO_INIT),
97 	fHasLocalizedName(false),
98 	fLocalizedNameIsCached(false)
99 {
100 }
101 
102 
103 Model::Model(const Model& other)
104 	:
105 	fEntryRef(other.fEntryRef),
106 	fMimeType(other.fMimeType),
107 	fPreferredAppName(NULL),
108 	fBaseType(other.fBaseType),
109 	fIconFrom(other.fIconFrom),
110 	fWritable(false),
111 	fNode(NULL),
112 	fLocalizedName(other.fLocalizedName),
113 	fHasLocalizedName(other.fHasLocalizedName),
114 	fLocalizedNameIsCached(other.fLocalizedNameIsCached)
115 {
116 	fStatBuf.st_dev = other.NodeRef()->device;
117 	fStatBuf.st_ino = other.NodeRef()->node;
118 
119 	if (other.IsSymLink() && other.LinkTo())
120 		fLinkTo = new Model(*other.LinkTo());
121 
122 	fStatus = OpenNode(other.IsNodeOpenForWriting());
123 	if (fStatus == B_OK) {
124 		ASSERT(fNode);
125 		fNode->GetStat(&fStatBuf);
126 		ASSERT(fStatBuf.st_dev == other.NodeRef()->device);
127 		ASSERT(fStatBuf.st_ino == other.NodeRef()->node);
128 	}
129 	if (!other.IsNodeOpen())
130 		CloseNode();
131 }
132 
133 
134 Model::Model(const node_ref* dirNode, const node_ref* node, const char* name,
135 	bool open, bool writable)
136 	:
137 	fPreferredAppName(NULL),
138 	fWritable(false),
139 	fNode(NULL),
140 	fHasLocalizedName(false),
141 	fLocalizedNameIsCached(false)
142 {
143 	SetTo(dirNode, node, name, open, writable);
144 }
145 
146 
147 Model::Model(const BEntry* entry, bool open, bool writable)
148 	:
149 	fPreferredAppName(NULL),
150 	fWritable(false),
151 	fNode(NULL),
152 	fHasLocalizedName(false),
153 	fLocalizedNameIsCached(false)
154 {
155 	SetTo(entry, open, writable);
156 }
157 
158 
159 Model::Model(const entry_ref* ref, bool traverse, bool open, bool writable)
160 	:
161 	fPreferredAppName(NULL),
162 	fBaseType(kUnknownNode),
163 	fIconFrom(kUnknownSource),
164 	fWritable(false),
165 	fNode(NULL),
166 	fHasLocalizedName(false),
167 	fLocalizedNameIsCached(false)
168 {
169 	BEntry entry(ref, traverse);
170 	fStatus = entry.InitCheck();
171 	if (fStatus == B_OK)
172 		SetTo(&entry, open, writable);
173 }
174 
175 
176 void
177 Model::DeletePreferredAppVolumeNameLinkTo()
178 {
179 	if (IsSymLink()) {
180 		Model* tmp = fLinkTo;
181 			// deal with link to link to self
182 		fLinkTo = NULL;
183 		delete tmp;
184 	} else if (IsVolume())
185 		free(fVolumeName);
186 	else
187 		free(fPreferredAppName);
188 
189 	fPreferredAppName = NULL;
190 }
191 
192 
193 Model::~Model()
194 {
195 #ifdef CHECK_OPEN_MODEL_LEAKS
196 	if (writableOpenModelList != NULL)
197 		writableOpenModelList->RemoveItem(this);
198 
199 	if (readOnlyOpenModelList != NULL)
200 		readOnlyOpenModelList->RemoveItem(this);
201 #endif
202 
203 	DeletePreferredAppVolumeNameLinkTo();
204 	if (IconCache::NeedsDeletionNotification((IconSource)fIconFrom)) {
205 		// this check allows us to use temporary Model in the IconCache
206 		// without the danger of a deadlock
207 		IconCache::sIconCache->Deleting(this);
208 	}
209 #if xDEBUG
210 	if (fNode != NULL)
211 		PRINT(("destructor closing node for %s\n", Name()));
212 #endif
213 
214 	delete fNode;
215 }
216 
217 
218 status_t
219 Model::SetTo(const BEntry* entry, bool open, bool writable)
220 {
221 	delete fNode;
222 	fNode = NULL;
223 	DeletePreferredAppVolumeNameLinkTo();
224 	fIconFrom = kUnknownSource;
225 	fBaseType = kUnknownNode;
226 	fMimeType = "";
227 
228 	fStatus = entry->GetRef(&fEntryRef);
229 	if (fStatus != B_OK)
230 		return fStatus;
231 
232 	fStatus = entry->GetStat(&fStatBuf);
233 	if (fStatus != B_OK)
234 		return fStatus;
235 
236 	fStatus = OpenNode(writable);
237 	if (!open)
238 		CloseNode();
239 
240 	return fStatus;
241 }
242 
243 
244 status_t
245 Model::SetTo(const entry_ref* newRef, bool traverse, bool open, bool writable)
246 {
247 	delete fNode;
248 	fNode = NULL;
249 	DeletePreferredAppVolumeNameLinkTo();
250 	fIconFrom = kUnknownSource;
251 	fBaseType = kUnknownNode;
252 	fMimeType = "";
253 
254 	BEntry tmpEntry(newRef, traverse);
255 	fStatus = tmpEntry.InitCheck();
256 	if (fStatus != B_OK)
257 		return fStatus;
258 
259 	if (traverse)
260 		tmpEntry.GetRef(&fEntryRef);
261 	else
262 		fEntryRef = *newRef;
263 
264 	fStatus = tmpEntry.GetStat(&fStatBuf);
265 	if (fStatus != B_OK)
266 		return fStatus;
267 
268 	fStatus = OpenNode(writable);
269 	if (!open)
270 		CloseNode();
271 
272 	return fStatus;
273 }
274 
275 
276 status_t
277 Model::SetTo(const node_ref* dirNode, const node_ref* nodeRef,
278 	const char* name, bool open, bool writable)
279 {
280 	delete fNode;
281 	fNode = NULL;
282 	DeletePreferredAppVolumeNameLinkTo();
283 	fIconFrom = kUnknownSource;
284 	fBaseType = kUnknownNode;
285 	fMimeType = "";
286 
287 	fStatBuf.st_dev = nodeRef->device;
288 	fStatBuf.st_ino = nodeRef->node;
289 	fEntryRef.device = dirNode->device;
290 	fEntryRef.directory = dirNode->node;
291 	fEntryRef.name = strdup(name);
292 
293 	BEntry tmpNode(&fEntryRef);
294 	fStatus = tmpNode.InitCheck();
295 	if (fStatus != B_OK)
296 		return fStatus;
297 
298 	fStatus = tmpNode.GetStat(&fStatBuf);
299 	if (fStatus != B_OK)
300 		return fStatus;
301 
302 	fStatus = OpenNode(writable);
303 
304 	if (!open)
305 		CloseNode();
306 
307 	return fStatus;
308 }
309 
310 
311 status_t
312 Model::InitCheck() const
313 {
314 	return fStatus;
315 }
316 
317 
318 int
319 Model::CompareFolderNamesFirst(const Model* compareModel) const
320 {
321 	if (compareModel == NULL)
322 		return -1;
323 
324 	const Model* resolvedCompareModel = compareModel->ResolveIfLink();
325 	const Model* resolvedMe = ResolveIfLink();
326 
327 	bool meIsDirOrVolume = resolvedMe->IsDirectory() || resolvedMe->IsVolume()
328 		|| resolvedMe->IsVirtualDirectory();
329 	bool otherIsDirOrVolume = resolvedCompareModel->IsDirectory()
330 		|| resolvedCompareModel->IsVolume()
331 		|| resolvedCompareModel->IsVirtualDirectory();
332 
333 	if (meIsDirOrVolume) {
334 		if (!otherIsDirOrVolume)
335 			return -1;
336 	} else if (otherIsDirOrVolume)
337 		return 1;
338 
339 	return NaturalCompare(Name(), compareModel->Name());
340 }
341 
342 
343 const char*
344 Model::Name() const
345 {
346 	static const char* kRootNodeName = B_TRANSLATE_MARK(B_DISKS_DIR_NAME);
347 	static const char* kTrashNodeName = B_TRANSLATE_MARK(B_TRASH_DIR_NAME);
348 	static const char* kDesktopNodeName = B_TRANSLATE_MARK(B_DESKTOP_DIR_NAME);
349 
350 	switch (fBaseType) {
351 		case kRootNode:
352 			return B_TRANSLATE_NOCOLLECT(kRootNodeName);
353 
354 		case kVolumeNode:
355 			if (fVolumeName != NULL)
356 				return fVolumeName;
357 			break;
358 
359 		case kTrashNode:
360 			return B_TRANSLATE_NOCOLLECT(kTrashNodeName);
361 
362 		case kDesktopNode:
363 			return B_TRANSLATE_NOCOLLECT(kDesktopNodeName);
364 
365 		default:
366 			break;
367 	}
368 
369 	if (fHasLocalizedName && gLocalizedNamePreferred)
370 		return fLocalizedName.String();
371 	else
372 		return fEntryRef.name;
373 }
374 
375 
376 status_t
377 Model::OpenNode(bool writable)
378 {
379 	if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
380 		return B_OK;
381 
382 	OpenNodeCommon(writable);
383 
384 	return fStatus;
385 }
386 
387 
388 status_t
389 Model::UpdateStatAndOpenNode(bool writable)
390 {
391 	if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
392 		return B_OK;
393 
394 	// try reading the stat structure again
395 	BEntry tmpEntry(&fEntryRef);
396 	fStatus = tmpEntry.InitCheck();
397 	if (fStatus != B_OK)
398 		return fStatus;
399 
400 	fStatus = tmpEntry.GetStat(&fStatBuf);
401 	if (fStatus != B_OK)
402 		return fStatus;
403 
404 	OpenNodeCommon(writable);
405 
406 	return fStatus;
407 }
408 
409 
410 status_t
411 Model::OpenNodeCommon(bool writable)
412 {
413 #if xDEBUG
414 	PRINT(("opening node for %s\n", Name()));
415 #endif
416 
417 #ifdef CHECK_OPEN_MODEL_LEAKS
418 	if (writableOpenModelList != NULL)
419 		writableOpenModelList->RemoveItem(this);
420 
421 	if (readOnlyOpenModelList != NULL)
422 		readOnlyOpenModelList->RemoveItem(this);
423 #endif
424 
425 	if (fBaseType == kUnknownNode)
426 		SetupBaseType();
427 
428 	switch (fBaseType) {
429 		case kPlainNode:
430 		case kExecutableNode:
431 		case kQueryNode:
432 		case kQueryTemplateNode:
433 		case kVirtualDirectoryNode:
434 			// open or reopen
435 			delete fNode;
436 			fNode = new BFile(&fEntryRef,
437 				(uint32)(writable ? O_RDWR : O_RDONLY));
438 			break;
439 
440 		case kDirectoryNode:
441 		case kVolumeNode:
442 		case kRootNode:
443 		case kTrashNode:
444 		case kDesktopNode:
445 			if (!IsNodeOpen())
446 				fNode = new BDirectory(&fEntryRef);
447 
448 			if (fBaseType == kDirectoryNode
449 				&& static_cast<BDirectory*>(fNode)->IsRootDirectory()) {
450 				// promote from directory to volume
451 				fBaseType = kVolumeNode;
452 			}
453 			break;
454 
455 		case kLinkNode:
456 			if (!IsNodeOpen()) {
457 				BEntry entry(&fEntryRef);
458 				fNode = new BSymLink(&entry);
459 			}
460 			break;
461 
462 		default:
463 #if DEBUG
464 			PrintToStream();
465 #endif
466 			TRESPASS();
467 				// this can only happen if GetStat failed before,
468 				// in which case we shouldn't be here
469 
470 			// ToDo: Obviously, we can also be here if the type could not
471 			// be determined, for example for block devices (so the TRESPASS()
472 			// macro shouldn't be used here)!
473 			return fStatus = B_ERROR;
474 	}
475 
476 	fStatus = fNode->InitCheck();
477 	if (fStatus != B_OK) {
478 		delete fNode;
479 		fNode = NULL;
480 		// original code snoozed an error here and returned B_OK
481 		return fStatus;
482 	}
483 
484 	fWritable = writable;
485 
486 	if (fMimeType.Length() <= 0)
487 		FinishSettingUpType();
488 
489 #ifdef CHECK_OPEN_MODEL_LEAKS
490 	if (fWritable) {
491 		if (!writableOpenModelList) {
492 			TRACE();
493 			writableOpenModelList = new BObjectList<Model>(100);
494 		}
495 		writableOpenModelList->AddItem(this);
496 	} else {
497 		if (!readOnlyOpenModelList) {
498 			TRACE();
499 			readOnlyOpenModelList = new BObjectList<Model>(100);
500 		}
501 		readOnlyOpenModelList->AddItem(this);
502 	}
503 #endif
504 
505 	if (gLocalizedNamePreferred)
506 		CacheLocalizedName();
507 
508 	return fStatus;
509 }
510 
511 
512 void
513 Model::CloseNode()
514 {
515 #if xDEBUG
516 	PRINT(("closing node for %s\n", Name()));
517 #endif
518 
519 #ifdef CHECK_OPEN_MODEL_LEAKS
520 	if (writableOpenModelList != NULL)
521 		writableOpenModelList->RemoveItem(this);
522 
523 	if (readOnlyOpenModelList != NULL)
524 		readOnlyOpenModelList->RemoveItem(this);
525 #endif
526 
527 	delete fNode;
528 	fNode = NULL;
529 }
530 
531 
532 bool
533 Model::IsNodeOpen() const
534 {
535 	return fNode != NULL;
536 }
537 
538 
539 
540 bool
541 Model::IsNodeOpenForWriting() const
542 {
543 	return fNode != NULL && fWritable;
544 }
545 
546 
547 void
548 Model::SetupBaseType()
549 {
550 	switch (fStatBuf.st_mode & S_IFMT) {
551 		case S_IFDIR:
552 			// folder
553 			fBaseType = kDirectoryNode;
554 			break;
555 
556 		case S_IFREG:
557 			// regular file
558 			if ((fStatBuf.st_mode & S_IXUSR) != 0) {
559 				// executable
560 				fBaseType = kExecutableNode;
561 			} else {
562 				// non-executable
563 				fBaseType = kPlainNode;
564 			}
565 			break;
566 
567 		case S_IFLNK:
568 			// symlink
569 			fBaseType = kLinkNode;
570 			break;
571 
572 		default:
573 			fBaseType = kUnknownNode;
574 			break;
575 	}
576 }
577 
578 
579 void
580 Model::CacheLocalizedName()
581 {
582 	if (!fLocalizedNameIsCached) {
583 		fLocalizedNameIsCached = true;
584 		if (BLocaleRoster::Default()->GetLocalizedFileName(
585 				fLocalizedName, fEntryRef, true) == B_OK)
586 			fHasLocalizedName = true;
587 		else
588 			fHasLocalizedName = false;
589 	}
590 }
591 
592 
593 void
594 Model::FinishSettingUpType()
595 {
596 	char type[B_MIME_TYPE_LENGTH];
597 	BEntry entry;
598 
599 	// While we are reading the node, do a little snooping to see if it even
600 	// makes sense to look for a node-based icon. This serves as a hint to the
601 	// icon cache, allowing it to not hit the disk again for models that do not
602 	// have an icon defined by the node.
603 	if (fBaseType != kLinkNode && !CheckAppIconHint())
604 		fIconFrom = kUnknownNotFromNode;
605 
606 	if (fBaseType != kDirectoryNode
607 		&& fBaseType != kVolumeNode
608 		&& fBaseType != kLinkNode
609 		&& IsNodeOpen()) {
610 		BNodeInfo info(fNode);
611 
612 		// check if a specific mime type is set
613 		if (info.GetType(type) == B_OK) {
614 			// node has a specific mime type
615 			fMimeType = type;
616 			if (strcmp(type, B_QUERY_MIMETYPE) == 0)
617 				fBaseType = kQueryNode;
618 			else if (strcmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0)
619 				fBaseType = kQueryTemplateNode;
620 			else if (strcmp(type, kVirtualDirectoryMimeType) == 0)
621 				fBaseType = kVirtualDirectoryNode;
622 
623 			if (ShouldGenerateThumbnail(type))
624 				fIconFrom = kNode;
625 
626 			if (info.GetPreferredApp(type) == B_OK) {
627 				if (fPreferredAppName)
628 					DeletePreferredAppVolumeNameLinkTo();
629 
630 				if (*type != '\0')
631 					fPreferredAppName = strdup(type);
632 			}
633 		}
634 	}
635 
636 	switch (fBaseType) {
637 		case kDirectoryNode:
638 			entry.SetTo(&fEntryRef);
639 			if (entry.InitCheck() == B_OK) {
640 				if (FSIsTrashDir(&entry))
641 					fBaseType = kTrashNode;
642 				else if (FSIsDeskDir(&entry))
643 					fBaseType = kDesktopNode;
644 			}
645 
646 			fMimeType = B_DIR_MIMETYPE;
647 				// should use a shared string here
648 			if (IsNodeOpen()) {
649 				BNodeInfo info(fNode);
650 				if (info.GetType(type) == B_OK)
651 					fMimeType = type;
652 
653 				if (fIconFrom == kUnknownNotFromNode
654 					&& WellKnowEntryList::Match(NodeRef())
655 						> (directory_which)-1) {
656 					// one of home, beos, system, boot, etc.
657 					fIconFrom = kTrackerSupplied;
658 				}
659 			}
660 			break;
661 
662 		case kVolumeNode:
663 		{
664 			if (NodeRef()->node == fEntryRef.directory
665 				&& NodeRef()->device == fEntryRef.device) {
666 				// promote from volume to file system root
667 				fBaseType = kRootNode;
668 				fMimeType = B_ROOT_MIMETYPE;
669 				break;
670 			}
671 
672 			// volumes have to have a B_VOLUME_MIMETYPE type
673 			fMimeType = B_VOLUME_MIMETYPE;
674 			if (fIconFrom == kUnknownNotFromNode) {
675 				if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1)
676 					fIconFrom = kTrackerSupplied;
677 				else
678 					fIconFrom = kVolume;
679 			}
680 
681 			char name[B_FILE_NAME_LENGTH];
682 			BVolume volume(NodeRef()->device);
683 			if (volume.InitCheck() == B_OK && volume.GetName(name) == B_OK) {
684 				if (fVolumeName != NULL)
685 					DeletePreferredAppVolumeNameLinkTo();
686 
687 				fVolumeName = strdup(name);
688 			}
689 #if DEBUG
690 			else
691 				PRINT(("get volume name failed for %s\n", fEntryRef.name));
692 #endif
693 			break;
694 		}
695 
696 		case kLinkNode:
697 			fMimeType = B_LINK_MIMETYPE;
698 				// should use a shared string here
699 			break;
700 
701 		case kExecutableNode:
702 			if (IsNodeOpen()) {
703 				char signature[B_MIME_TYPE_LENGTH];
704 				if (GetAppSignatureFromAttr(dynamic_cast<BFile*>(fNode),
705 						signature) == B_OK) {
706 					if (fPreferredAppName)
707 						DeletePreferredAppVolumeNameLinkTo();
708 
709 					if (signature[0])
710 						fPreferredAppName = strdup(signature);
711 				}
712 			}
713 			if (fMimeType.Length() <= 0)
714 				fMimeType = B_APP_MIME_TYPE;
715 					// should use a shared string here
716 			break;
717 
718 		default:
719 			if (fMimeType.Length() <= 0)
720 				fMimeType = B_FILE_MIMETYPE;
721 			break;
722 	}
723 }
724 
725 
726 bool
727 Model::ShouldUseWellKnownIcon() const
728 {
729 	if (fBaseType == kDirectoryNode || fBaseType == kVolumeNode
730 		|| fBaseType == kTrashNode || fBaseType == kDesktopNode)
731 		return !CheckAppIconHint();
732 	return false;
733 }
734 
735 
736 bool
737 Model::CheckAppIconHint() const
738 {
739 	attr_info info;
740 	if (fNode == NULL) {
741 		// Node is not open.
742 		return false;
743 	}
744 
745 	if (fNode->GetAttrInfo(kAttrIcon, &info) == B_OK) {
746 		// Node has a vector icon
747 		return true;
748 	}
749 
750 	if (fNode->GetAttrInfo(kAttrMiniIcon, &info) == B_OK
751 		&& fNode->GetAttrInfo(kAttrLargeIcon, &info) == B_OK) {
752 		// Node has a mini _and_ large icon
753 		return true;
754 	}
755 
756 	// If there isn't either of these, we can't use the icon attribute from the node.
757 	return false;
758 }
759 
760 
761 void
762 Model::ResetIconFrom()
763 {
764 	BModelOpener opener(this);
765 
766 	if (InitCheck() != B_OK)
767 		return;
768 
769 	if (ShouldUseWellKnownIcon()) {
770 		BDirectory* directory = dynamic_cast<BDirectory*>(fNode);
771 		if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) {
772 			fIconFrom = kTrackerSupplied;
773 			return;
774 		} else if (directory != NULL && directory->IsRootDirectory()) {
775 			fIconFrom = kVolume;
776 			return;
777 		}
778 	}
779 	fIconFrom = kUnknownSource;
780 }
781 
782 
783 const char*
784 Model::PreferredAppSignature() const
785 {
786 	if (IsVolume() || IsSymLink())
787 		return "";
788 
789 	return fPreferredAppName ? fPreferredAppName : "";
790 }
791 
792 
793 void
794 Model::SetPreferredAppSignature(const char* signature)
795 {
796 	ASSERT(!IsVolume() && !IsSymLink());
797 	ASSERT(signature != fPreferredAppName);
798 		// self assignment should not be an option
799 
800 	free(fPreferredAppName);
801 	if (signature)
802 		fPreferredAppName = strdup(signature);
803 	else
804 		fPreferredAppName = NULL;
805 }
806 
807 
808 const Model*
809 Model::ResolveIfLink() const
810 {
811 	if (!IsSymLink())
812 		return this;
813 
814 	if (!fLinkTo)
815 		return this;
816 
817 	return fLinkTo;
818 }
819 
820 
821 Model*
822 Model::ResolveIfLink()
823 {
824 	if (!IsSymLink())
825 		return this;
826 
827 	if (!fLinkTo)
828 		return this;
829 
830 	return fLinkTo;
831 }
832 
833 
834 void
835 Model::SetLinkTo(Model* model)
836 {
837 	ASSERT(IsSymLink());
838 	ASSERT(!fLinkTo || (fLinkTo != model));
839 
840 	delete fLinkTo;
841 	fLinkTo = model;
842 }
843 
844 
845 //	#pragma mark - Node monitor updating methods
846 
847 
848 void
849 Model::UpdateEntryRef(const node_ref* dirNode, const char* name)
850 {
851 	if (IsVolume()) {
852 		if (fVolumeName != NULL)
853 			DeletePreferredAppVolumeNameLinkTo();
854 
855 		fVolumeName = strdup(name);
856 	}
857 
858 	fEntryRef.device = dirNode->device;
859 	fEntryRef.directory = dirNode->node;
860 
861 	if (fEntryRef.name != NULL && strcmp(fEntryRef.name, name) == 0)
862 		return;
863 
864 	fEntryRef.set_name(name);
865 }
866 
867 
868 status_t
869 Model::WatchVolumeAndMountPoint(uint32 , BHandler* target)
870 {
871 	ASSERT(IsVolume());
872 
873 	if (fEntryRef.name != NULL && fVolumeName != NULL
874 		&& strcmp(fEntryRef.name, "boot") == 0) {
875 		// watch mount point for boot volume
876 		BString bootMountPoint("/");
877 		bootMountPoint += fVolumeName;
878 		BEntry mountPointEntry(bootMountPoint.String());
879 		Model mountPointModel(&mountPointEntry);
880 
881 		TTracker::WatchNode(mountPointModel.NodeRef(),
882 			B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target);
883 	}
884 
885 	return TTracker::WatchNode(NodeRef(),
886 		B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target);
887 }
888 
889 
890 bool
891 Model::AttrChanged(const char* attrName)
892 {
893 	// called on an attribute changed node monitor
894 	// sync up cached values of mime type and preferred app and
895 	// return true if icon needs updating
896 
897 	ASSERT(IsNodeOpen());
898 	if (attrName != NULL
899 		&& (strcmp(attrName, kAttrIcon) == 0
900 			|| strcmp(attrName, kAttrMiniIcon) == 0
901 			|| strcmp(attrName, kAttrLargeIcon) == 0
902 			|| strcmp(attrName, kAttrThumbnail) == 0)) {
903 		return true;
904 	}
905 
906 	if (attrName == NULL
907 		|| strcmp(attrName, kAttrMIMEType) == 0
908 		|| strcmp(attrName, kAttrPreferredApp) == 0) {
909 		char type[B_MIME_TYPE_LENGTH];
910 		BNodeInfo info(fNode);
911 		if (info.GetType(type) != B_OK)
912 			fMimeType = "";
913 		else {
914 			// node has a specific mime type
915 			fMimeType = type;
916 			if (!IsVolume() && !IsSymLink()
917 				&& info.GetPreferredApp(type) == B_OK) {
918 				SetPreferredAppSignature(type);
919 			}
920 		}
921 
922 #if xDEBUG
923 		if (fIconFrom != kNode) {
924 			PRINT(("%s, %s:updating icon because file type changed\n",
925 				Name(), attrName != NULL ? attrName : ""));
926 		} else {
927 			PRINT(("Not updating icon even though type changed "
928 				"because icon is from node.\n"));
929 		}
930 #endif
931 
932 		return fIconFrom != kNode;
933 			// update icon unless it is coming from a node
934 	}
935 
936 	return attrName == NULL;
937 }
938 
939 
940 bool
941 Model::StatChanged()
942 {
943 	ASSERT(IsNodeOpen());
944 	mode_t oldMode = fStatBuf.st_mode;
945 	fStatus = fNode->GetStat(&fStatBuf);
946 
947 	if (oldMode != fStatBuf.st_mode) {
948 		bool forWriting = IsNodeOpenForWriting();
949 		CloseNode();
950 		//SetupBaseType();
951 			// the node type can't change with a stat update...
952 		OpenNodeCommon(forWriting);
953 		return true;
954 	}
955 
956 	return false;
957 }
958 
959 
960 //	#pragma mark - Mime handling methods
961 
962 
963 bool
964 Model::IsDropTarget(const Model* forDocument, bool traverse) const
965 {
966 	switch (CanHandleDrops()) {
967 		case kCanHandle:
968 			return true;
969 
970 		case kCannotHandle:
971 			return false;
972 
973 		default:
974 			break;
975 	}
976 
977 	if (forDocument == NULL)
978 		return true;
979 
980 	if (traverse) {
981 		BEntry entry(forDocument->EntryRef(), true);
982 		if (entry.InitCheck() != B_OK)
983 			return false;
984 
985 		BFile file(&entry, O_RDONLY);
986 		BNodeInfo mime(&file);
987 
988 		if (mime.InitCheck() != B_OK)
989 			return false;
990 
991 		char mimeType[B_MIME_TYPE_LENGTH];
992 		mime.GetType(mimeType);
993 
994 		return SupportsMimeType(mimeType, 0) != kDoesNotSupportType;
995 	}
996 
997 	// do some mime-based matching
998 	const char* documentMimeType = forDocument->MimeType();
999 	if (documentMimeType == NULL)
1000 		return false;
1001 
1002 	return SupportsMimeType(documentMimeType, 0) != kDoesNotSupportType;
1003 }
1004 
1005 
1006 Model::CanHandleResult
1007 Model::CanHandleDrops() const
1008 {
1009 	if (IsDirectory()) {
1010 		// directories take anything
1011 		// resolve permissions here
1012 		return kCanHandle;
1013 	}
1014 
1015 	if (IsSymLink()) {
1016 		// descend into symlink and try again on it's target
1017 
1018 		BEntry entry(&fEntryRef, true);
1019 		if (entry.InitCheck() != B_OK)
1020 			return kCannotHandle;
1021 
1022 		if (entry == BEntry(EntryRef()))
1023 			// self-referencing link, avoid infinite recursion
1024 			return kCannotHandle;
1025 
1026 		Model model(&entry);
1027 		if (model.InitCheck() != B_OK)
1028 			return kCannotHandle;
1029 
1030 		return model.CanHandleDrops();
1031 	}
1032 
1033 	if (IsExecutable())
1034 		return kNeedToCheckType;
1035 
1036 	return kCannotHandle;
1037 }
1038 
1039 
1040 inline bool
1041 IsSuperHandlerSignature(const char* signature)
1042 {
1043 	return strcasecmp(signature, B_FILE_MIMETYPE) == 0;
1044 }
1045 
1046 
1047 enum {
1048 	kDontMatch = 0,
1049 	kMatchSupertype,
1050 	kMatch
1051 };
1052 
1053 
1054 static int32
1055 MatchMimeTypeString(/*const */BString* documentType, const char* handlerType)
1056 {
1057 	// perform a mime type wildcard match
1058 	// handler types of the form "text"
1059 	// handle every handled type with same supertype,
1060 	// for everything else a full string match is used
1061 
1062 	int32 supertypeOnlyLength = 0;
1063 	const char* tmp = strstr(handlerType, "/");
1064 
1065 	if (tmp == NULL) {
1066 		// no subtype - supertype string only
1067 		supertypeOnlyLength = (int32)strlen(handlerType);
1068 	}
1069 
1070 	if (supertypeOnlyLength) {
1071 		// compare just the supertype
1072 		tmp = strstr(documentType->String(), "/");
1073 		if (tmp && (tmp - documentType->String() == supertypeOnlyLength)) {
1074 			if (documentType->ICompare(handlerType, supertypeOnlyLength) == 0)
1075 				return kMatchSupertype;
1076 			else
1077 				return kDontMatch;
1078 		}
1079 	}
1080 
1081 	if (documentType->ICompare(handlerType) == 0)
1082 		return kMatch;
1083 
1084 	return kDontMatch;
1085 }
1086 
1087 
1088 int32
1089 Model::SupportsMimeType(const char* type, const BObjectList<BString>* list,
1090 	bool exactReason) const
1091 {
1092 	ASSERT((type == 0) != (list == 0));
1093 		// pass in one or the other
1094 
1095 	int32 result = kDoesNotSupportType;
1096 
1097 	BFile file(EntryRef(), O_RDONLY);
1098 	BAppFileInfo handlerInfo(&file);
1099 
1100 	BMessage message;
1101 	if (handlerInfo.GetSupportedTypes(&message) != B_OK)
1102 		return kDoesNotSupportType;
1103 
1104 	for (int32 index = 0; ; index++) {
1105 		// check if this model lists the type of dropped document as supported
1106 
1107 		const char* mimeSignature;
1108 		ssize_t bufferLength;
1109 
1110 		if (message.FindData("types", 'CSTR', index,
1111 				(const void**)&mimeSignature, &bufferLength)) {
1112 			return result;
1113 		}
1114 
1115 		if (IsSuperHandlerSignature(mimeSignature)) {
1116 			if (!exactReason)
1117 				return kSuperhandlerModel;
1118 
1119 			if (result == kDoesNotSupportType)
1120 				result = kSuperhandlerModel;
1121 		}
1122 
1123 		int32 match;
1124 
1125 		if (type != NULL || (list != NULL && list->IsEmpty())) {
1126 			BString typeString(type);
1127 			match = MatchMimeTypeString(&typeString, mimeSignature);
1128 		} else {
1129 			match = WhileEachListItem(const_cast<BObjectList<BString>*>(list),
1130 				MatchMimeTypeString, mimeSignature);
1131 			// const_cast shouldnt be here, have to have it until
1132 			// MW cleans up
1133 		}
1134 		if (match == kMatch)
1135 			// supports the actual type, it can't get any better
1136 			return kModelSupportsType;
1137 		else if (match == kMatchSupertype) {
1138 			if (!exactReason)
1139 				return kModelSupportsSupertype;
1140 
1141 			// we already know this model supports the file as a supertype,
1142 			// now find out if it matches the type
1143 			result = kModelSupportsSupertype;
1144 		}
1145 	}
1146 
1147 	return result;
1148 }
1149 
1150 
1151 bool
1152 Model::IsDropTargetForList(const BObjectList<BString>* list) const
1153 {
1154 	switch (CanHandleDrops()) {
1155 		case kCanHandle:
1156 			return true;
1157 
1158 		case kCannotHandle:
1159 			return false;
1160 
1161 		default:
1162 			break;
1163 	}
1164 
1165 	return SupportsMimeType(0, list) != kDoesNotSupportType;
1166 }
1167 
1168 
1169 bool
1170 Model::IsSuperHandler() const
1171 {
1172 	ASSERT(CanHandleDrops() == kNeedToCheckType);
1173 
1174 	BFile file(EntryRef(), O_RDONLY);
1175 	BAppFileInfo handlerInfo(&file);
1176 
1177 	BMessage message;
1178 	if (handlerInfo.GetSupportedTypes(&message) != B_OK)
1179 		return false;
1180 
1181 	for (int32 index = 0; ; index++) {
1182 		const char* mimeSignature;
1183 		ssize_t bufferLength;
1184 
1185 		if (message.FindData("types", 'CSTR', index,
1186 			(const void**)&mimeSignature, &bufferLength)) {
1187 			return false;
1188 		}
1189 
1190 		if (IsSuperHandlerSignature(mimeSignature))
1191 			return true;
1192 	}
1193 	return false;
1194 }
1195 
1196 
1197 void
1198 Model::GetEntry(BEntry* entry) const
1199 {
1200 	entry->SetTo(EntryRef());
1201 }
1202 
1203 
1204 void
1205 Model::GetPath(BPath* path) const
1206 {
1207 	BEntry entry(EntryRef());
1208 	entry.GetPath(path);
1209 }
1210 
1211 
1212 bool
1213 Model::Mimeset(bool force)
1214 {
1215 	BString oldType = MimeType();
1216 	BPath path;
1217 	GetPath(&path);
1218 
1219 	update_mime_info(path.Path(), 0, 1, force ? 2 : 0);
1220 	ModelNodeLazyOpener opener(this);
1221 	opener.OpenNode();
1222 	AttrChanged(NULL);
1223 
1224 	return !oldType.ICompare(MimeType());
1225 }
1226 
1227 
1228 ssize_t
1229 Model::WriteAttr(const char* attr, type_code type, off_t offset,
1230 	const void* buffer, size_t length)
1231 {
1232 	BModelWriteOpener opener(this);
1233 	if (!fNode)
1234 		return 0;
1235 
1236 	ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
1237 	return result;
1238 }
1239 
1240 
1241 ssize_t
1242 Model::WriteAttrKillForeign(const char* attr, const char* foreignAttr,
1243 	type_code type, off_t offset, const void* buffer, size_t length)
1244 {
1245 	BModelWriteOpener opener(this);
1246 	if (!fNode)
1247 		return 0;
1248 
1249 	ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
1250 	if (result == (ssize_t)length)
1251 		// nuke attribute in opposite endianness
1252 		fNode->RemoveAttr(foreignAttr);
1253 	return result;
1254 }
1255 
1256 
1257 status_t
1258 Model::GetLongVersionString(BString &result, version_kind kind)
1259 {
1260 	BFile file(EntryRef(), O_RDONLY);
1261 	status_t error = file.InitCheck();
1262 	if (error != B_OK)
1263 		return error;
1264 
1265 	BAppFileInfo info(&file);
1266 	error = info.InitCheck();
1267 	if (error != B_OK)
1268 		return error;
1269 
1270 	version_info version;
1271 	error = info.GetVersionInfo(&version, kind);
1272 	if (error != B_OK)
1273 		return error;
1274 
1275 	result = version.long_info;
1276 	return B_OK;
1277 }
1278 
1279 
1280 status_t
1281 Model::GetVersionString(BString &result, version_kind kind)
1282 {
1283 	BFile file(EntryRef(), O_RDONLY);
1284 	status_t error = file.InitCheck();
1285 	if (error != B_OK)
1286 		return error;
1287 
1288 	BAppFileInfo info(&file);
1289 	error = info.InitCheck();
1290 	if (error != B_OK)
1291 		return error;
1292 
1293 	version_info version;
1294 	error = info.GetVersionInfo(&version, kind);
1295 	if (error != B_OK)
1296 		return error;
1297 
1298 	result.SetToFormat("%" B_PRId32 ".%" B_PRId32 ".%" B_PRId32, version.major,
1299 		version.middle, version.minor);
1300 
1301 	return B_OK;
1302 }
1303 
1304 #if DEBUG
1305 
1306 void
1307 Model::PrintToStream(int32 level, bool deep)
1308 {
1309 	PRINT(("model name %s, entry name %s, inode %" B_PRIdINO ", dev %"
1310 		B_PRIdDEV ", directory inode %" B_PRIdINO "\n",
1311 		Name() ? Name() : "**empty name**",
1312 		EntryRef()->name ? EntryRef()->name : "**empty ref name**",
1313 		NodeRef()->node,
1314 		NodeRef()->device,
1315 		EntryRef()->directory));
1316 	PRINT(("type %s \n", MimeType()));
1317 
1318 	PRINT(("model type: "));
1319 	switch (fBaseType) {
1320 		case kPlainNode:
1321 			PRINT(("plain\n"));
1322 			break;
1323 
1324 		case kQueryNode:
1325 			PRINT(("query\n"));
1326 			break;
1327 
1328 		case kQueryTemplateNode:
1329 			PRINT(("query template\n"));
1330 			break;
1331 
1332 		case kExecutableNode:
1333 			PRINT(("exe\n"));
1334 			break;
1335 
1336 		case kDirectoryNode:
1337 		case kTrashNode:
1338 		case kDesktopNode:
1339 			PRINT(("dir\n"));
1340 			break;
1341 
1342 		case kLinkNode:
1343 			PRINT(("link\n"));
1344 			break;
1345 
1346 		case kRootNode:
1347 			PRINT(("root\n"));
1348 			break;
1349 
1350 		case kVolumeNode:
1351 			PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : ""));
1352 			break;
1353 
1354 		case kVirtualDirectoryNode:
1355 			PRINT(("virtual directory\n"));
1356 			break;
1357 
1358 		default:
1359 			PRINT(("unknown\n"));
1360 			break;
1361 	}
1362 
1363 	if (level < 1)
1364 		return;
1365 
1366 	if (!IsVolume()) {
1367 		PRINT(("preferred app %s\n",
1368 			fPreferredAppName ? fPreferredAppName : ""));
1369 	}
1370 
1371 	PRINT(("icon from: "));
1372 	switch (IconFrom()) {
1373 		case kUnknownSource:
1374 			PRINT(("unknown\n"));
1375 			break;
1376 
1377 		case kUnknownNotFromNode:
1378 			PRINT(("unknown but not from a node\n"));
1379 			break;
1380 
1381 		case kTrackerDefault:
1382 			PRINT(("tracker default\n"));
1383 			break;
1384 
1385 		case kTrackerSupplied:
1386 			PRINT(("tracker supplied\n"));
1387 			break;
1388 
1389 		case kMetaMime:
1390 			PRINT(("metamime\n"));
1391 			break;
1392 
1393 		case kPreferredAppForType:
1394 			PRINT(("preferred app for type\n"));
1395 			break;
1396 
1397 		case kPreferredAppForNode:
1398 			PRINT(("preferred app for node\n"));
1399 			break;
1400 
1401 		case kNode:
1402 			PRINT(("node\n"));
1403 			break;
1404 
1405 		case kVolume:
1406 			PRINT(("volume\n"));
1407 			break;
1408 
1409 		default:
1410 			break;
1411 	}
1412 
1413 	PRINT(("model %s opened %s \n", !IsNodeOpen() ? "not " : "",
1414 		IsNodeOpenForWriting() ? "for writing" : ""));
1415 
1416 	if (IsNodeOpen()) {
1417 		node_ref nodeRef;
1418 		fNode->GetNodeRef(&nodeRef);
1419 		PRINT(("node ref of open Node %" B_PRIdINO " %" B_PRIdDEV "\n",
1420 			nodeRef.node, nodeRef.device));
1421 	}
1422 
1423 	if (deep && IsSymLink()) {
1424 		BEntry tmpEntry(EntryRef(), true);
1425 		Model tmp(&tmpEntry);
1426 		PRINT(("symlink to:\n"));
1427 		tmp.PrintToStream();
1428 	}
1429 	TrackIconSource(B_MINI_ICON);
1430 	TrackIconSource(B_LARGE_ICON);
1431 }
1432 
1433 
1434 void
1435 Model::TrackIconSource(icon_size size)
1436 {
1437 	PRINT(("tracking %s icon\n", size == B_LARGE_ICON ? "large" : "small"));
1438 	BRect rect;
1439 	if (size == B_MINI_ICON)
1440 		rect.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
1441 	else
1442 		rect.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1);
1443 
1444 	BBitmap bitmap(rect, B_CMAP8);
1445 
1446 	BModelOpener opener(this);
1447 
1448 	if (Node() == NULL) {
1449 		PRINT(("track icon error - no node\n"));
1450 		return;
1451 	}
1452 
1453 	if (IsSymLink()) {
1454 		PRINT(("tracking symlink icon\n"));
1455 		if (fLinkTo) {
1456 			fLinkTo->TrackIconSource(size);
1457 			return;
1458 		}
1459 	}
1460 
1461 	if (fBaseType == kVolumeNode) {
1462 		BVolume volume(NodeRef()->device);
1463 		status_t result = volume.GetIcon(&bitmap, size);
1464 		PRINT(("getting icon from volume %s\n", strerror(result)));
1465 	} else {
1466 		BNodeInfo nodeInfo(Node());
1467 
1468 		status_t err = nodeInfo.GetIcon(&bitmap, size);
1469 		if (err == B_OK) {
1470 			// file knew which icon to use, we are done
1471 			PRINT(("track icon - got icon from file\n"));
1472 			return;
1473 		}
1474 
1475 		char preferredApp[B_MIME_TYPE_LENGTH];
1476 		err = nodeInfo.GetPreferredApp(preferredApp);
1477 		if (err == B_OK && preferredApp[0]) {
1478 			BMimeType preferredAppType(preferredApp);
1479 			err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
1480 			if (err == B_OK) {
1481 				PRINT(
1482 					("track icon - got icon for type %s from preferred "
1483 					 "app %s for file\n", MimeType(), preferredApp));
1484 				return;
1485 			}
1486 		}
1487 
1488 		BMimeType mimeType(MimeType());
1489 		err = mimeType.GetIcon(&bitmap, size);
1490 		if (err == B_OK) {
1491 			// the system knew what icon to use for the type, we are done
1492 			PRINT(("track icon - signature %s, got icon from system\n",
1493 				MimeType()));
1494 			return;
1495 		}
1496 
1497 		err = mimeType.GetPreferredApp(preferredApp);
1498 		if (err != B_OK) {
1499 			// no preferred App for document, give up
1500 			PRINT(("track icon - signature %s, no prefered app, error %s\n",
1501 				MimeType(), strerror(err)));
1502 			return;
1503 		}
1504 
1505 		BMimeType preferredAppType(preferredApp);
1506 		err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
1507 		if (err == B_OK) {
1508 			// the preferred app knew icon to use for the type, we are done
1509 			PRINT(
1510 				("track icon - signature %s, got icon from preferred "
1511 				 "app %s\n", MimeType(), preferredApp));
1512 			return;
1513 		}
1514 		PRINT(
1515 			("track icon - signature %s, preferred app %s, no icon, "
1516 			 "error %s\n", MimeType(), preferredApp, strerror(err)));
1517 	}
1518 }
1519 
1520 #endif	// DEBUG
1521 
1522 #ifdef CHECK_OPEN_MODEL_LEAKS
1523 
1524 namespace BPrivate {
1525 
1526 #include <stdio.h>
1527 
1528 void
1529 DumpOpenModels(bool extensive)
1530 {
1531 	if (readOnlyOpenModelList) {
1532 		int32 count = readOnlyOpenModelList->CountItems();
1533 		printf("%ld models open read-only:\n", count);
1534 		printf("==========================\n");
1535 		for (int32 index = 0; index < count; index++) {
1536 			if (extensive) {
1537 				printf("---------------------------\n");
1538 				readOnlyOpenModelList->ItemAt(index)->PrintToStream();
1539 			} else
1540 				printf("%s\n", readOnlyOpenModelList->ItemAt(index)->Name());
1541 		}
1542 	}
1543 
1544 	if (writableOpenModelList) {
1545 		int32 count = writableOpenModelList->CountItems();
1546 		printf("%ld models open writable:\n", count);
1547 		printf("models open writable:\n");
1548 		printf("======================\n");
1549 		for (int32 index = 0; index < count; index++) {
1550 			if (extensive) {
1551 				printf("---------------------------\n");
1552 				writableOpenModelList->ItemAt(index)->PrintToStream();
1553 			} else
1554 				printf("%s\n", writableOpenModelList->ItemAt(index)->Name());
1555 		}
1556 	}
1557 }
1558 
1559 
1560 void
1561 InitOpenModelDumping()
1562 {
1563 	readOnlyOpenModelList = 0;
1564 	writableOpenModelList = 0;
1565 }
1566 
1567 }	// namespace BPrivate
1568 
1569 #endif	// CHECK_OPEN_MODEL_LEAKS
1570