xref: /haiku/src/kits/tracker/PoseView.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 
36 #include "PoseView.h"
37 
38 #include <algorithm>
39 #include <functional>
40 
41 #include <ctype.h>
42 #include <errno.h>
43 #include <float.h>
44 #include <map>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include <compat/sys/stat.h>
49 
50 #include <Alert.h>
51 #include <Application.h>
52 #include <Catalog.h>
53 #include <Clipboard.h>
54 #include <Debug.h>
55 #include <Dragger.h>
56 #include <fs_attr.h>
57 #include <fs_info.h>
58 #include <Screen.h>
59 #include <Query.h>
60 #include <List.h>
61 #include <Locale.h>
62 #include <LongAndDragTrackingFilter.h>
63 #include <MenuItem.h>
64 #include <NodeMonitor.h>
65 #include <Path.h>
66 #include <StopWatch.h>
67 #include <String.h>
68 #include <SymLink.h>
69 #include <TextView.h>
70 #include <VolumeRoster.h>
71 #include <Volume.h>
72 #include <Window.h>
73 
74 #include <ObjectListPrivate.h>
75 
76 #include "Attributes.h"
77 #include "AttributeStream.h"
78 #include "AutoLock.h"
79 #include "BackgroundImage.h"
80 #include "Bitmaps.h"
81 #include "Commands.h"
82 #include "ContainerWindow.h"
83 #include "CountView.h"
84 #include "Cursors.h"
85 #include "DeskWindow.h"
86 #include "DesktopPoseView.h"
87 #include "DirMenu.h"
88 #include "FilePanelPriv.h"
89 #include "FSClipboard.h"
90 #include "FSUtils.h"
91 #include "FunctionObject.h"
92 #include "MimeTypes.h"
93 #include "Navigator.h"
94 #include "NavMenu.h"
95 #include "Pose.h"
96 #include "InfoWindow.h"
97 #include "Utilities.h"
98 #include "Tests.h"
99 #include "Thread.h"
100 #include "Tracker.h"
101 #include "TrackerString.h"
102 #include "WidgetAttributeText.h"
103 #include "WidthBuffer.h"
104 
105 
106 #undef B_TRANSLATION_CONTEXT
107 #define B_TRANSLATION_CONTEXT "PoseView"
108 
109 using std::min;
110 using std::max;
111 
112 
113 const float kDoubleClickTresh = 6;
114 const float kCountViewWidth = 76;
115 
116 const uint32 kAddNewPoses = 'Tanp';
117 const uint32 kAddPosesCompleted = 'Tapc';
118 const int32 kMaxAddPosesChunk = 50;
119 const uint32 kMsgMouseDragged = 'Mdrg';
120 const uint32 kMsgMouseLongDown = 'Mold';
121 
122 const int32 kRoomForLine = 2;
123 
124 namespace BPrivate {
125 extern bool delete_point(void*);
126 	// TODO: exterminate this
127 }
128 
129 const float kSlowScrollBucket = 30;
130 const float kBorderHeight = 20;
131 
132 enum {
133 	kAutoScrollOff,
134 	kWaitForTransition,
135 	kDelayAutoScroll,
136 	kAutoScrollOn
137 };
138 
139 enum {
140 	kWasDragged,
141 	kContextMenuShown,
142 	kNotDragged
143 };
144 
145 enum {
146 	kInsertAtFront,
147 	kInsertAfter
148 };
149 
150 const BPoint kTransparentDragThreshold(256, 192);
151 	// maximum size of the transparent drag bitmap, use a drag rect
152 	// if larger in any direction
153 
154 struct attr_column_relation {
155 	uint32 	attrHash;
156 	int32	fieldMask;
157 };
158 
159 static struct attr_column_relation sAttrColumnMap[] = {
160 	{ AttrHashString(kAttrStatModified, B_TIME_TYPE),
161 		B_STAT_MODIFICATION_TIME },
162 	{ AttrHashString(kAttrStatSize, B_OFF_T_TYPE),
163 		B_STAT_SIZE },
164 	{ AttrHashString(kAttrStatCreated, B_TIME_TYPE),
165 		B_STAT_CREATION_TIME },
166 	{ AttrHashString(kAttrStatMode, B_STRING_TYPE),
167 		B_STAT_MODE }
168 };
169 
170 struct AddPosesResult {
171 	~AddPosesResult();
172 	void ReleaseModels();
173 
174 	Model* fModels[kMaxAddPosesChunk];
175 	PoseInfo fPoseInfos[kMaxAddPosesChunk];
176 	int32 fCount;
177 };
178 
179 
180 AddPosesResult::~AddPosesResult(void)
181 {
182 	for (int32 i = 0; i < fCount; i++)
183 		delete fModels[i];
184 }
185 
186 
187 void
188 AddPosesResult::ReleaseModels(void)
189 {
190 	for (int32 i = 0; i < kMaxAddPosesChunk; i++)
191 		fModels[i] = NULL;
192 }
193 
194 
195 static BPose*
196 BSearch(PoseList* table, const BPose* key, BPoseView* view,
197 	int (*cmp)(const BPose*, const BPose*, BPoseView*),
198 	bool returnClosest = true);
199 
200 static int
201 PoseCompareAddWidget(const BPose* p1, const BPose* p2, BPoseView* view);
202 
203 
204 // #pragma mark -
205 
206 
207 BPoseView::BPoseView(Model* model, BRect bounds, uint32 viewMode,
208 	uint32 resizeMask)
209 	: BView(bounds, "PoseView", resizeMask, B_WILL_DRAW | B_PULSE_NEEDED),
210 	fIsDrawingSelectionRect(false),
211 	fHScrollBar(NULL),
212 	fVScrollBar(NULL),
213 	fModel(model),
214 	fActivePose(NULL),
215 	fExtent(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
216 	fPoseList(new PoseList(40, true)),
217 	fFilteredPoseList(new PoseList()),
218 	fVSPoseList(new PoseList()),
219 	fSelectionList(new PoseList()),
220 	fMimeTypesInSelectionCache(20, true),
221 	fZombieList(new BObjectList<Model>(10, true)),
222 	fColumnList(new BObjectList<BColumn>(4, true)),
223 	fMimeTypeList(new BObjectList<BString>(10, true)),
224 	fBrokenLinks(new BObjectList<Model>(10, false)),
225 	fMimeTypeListIsDirty(false),
226 	fViewState(new BViewState),
227 	fStateNeedsSaving(false),
228 	fCountView(NULL),
229 	fDropTarget(NULL),
230 	fAlreadySelectedDropTarget(NULL),
231 	fSelectionHandler(be_app),
232 	fLastClickPt(INT32_MAX, INT32_MAX),
233 	fLastClickedPose(NULL),
234 	fLastExtent(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
235 	fTitleView(NULL),
236 	fRefFilter(NULL),
237 	fAutoScrollInc(20),
238 	fAutoScrollState(kAutoScrollOff),
239 	fWidgetTextOutline(false),
240 	fSelectionPivotPose(NULL),
241 	fRealPivotPose(NULL),
242 	fKeyRunner(NULL),
243 	fTrackRightMouseUp(false),
244 	fSelectionVisible(true),
245 	fMultipleSelection(true),
246 	fDragEnabled(true),
247 	fDropEnabled(true),
248 	fSelectionRectEnabled(true),
249 	fAlwaysAutoPlace(false),
250 	fAllowPoseEditing(true),
251 	fSelectionChangedHook(false),
252 	fSavePoseLocations(true),
253 	fShowHideSelection(true),
254 	fOkToMapIcons(false),
255 	fEnsurePosesVisible(false),
256 	fShouldAutoScroll(true),
257 	fIsDesktopWindow(false),
258 	fIsWatchingDateFormatChange(false),
259 	fHasPosesInClipboard(false),
260 	fCursorCheck(false),
261 	fFiltering(false),
262 	fFilterStrings(4, true),
263 	fLastFilterStringCount(1),
264 	fLastFilterStringLength(0),
265 	fLastKeyTime(0),
266 	fLastDeskbarFrameCheckTime(LONGLONG_MIN),
267 	fDeskbarFrame(0, 0, -1, -1),
268 	fTextWidgetToCheck(NULL)
269 {
270 	fViewState->SetViewMode(viewMode);
271 	fShowSelectionWhenInactive
272 		= TrackerSettings().ShowSelectionWhenInactive();
273 	fTransparentSelection = TrackerSettings().TransparentSelection();
274 	fFilterStrings.AddItem(new BString(""));
275 }
276 
277 
278 BPoseView::~BPoseView()
279 {
280 	delete fPoseList;
281 	delete fFilteredPoseList;
282 	delete fVSPoseList;
283 	delete fColumnList;
284 	delete fSelectionList;
285 	delete fMimeTypeList;
286 	delete fZombieList;
287 	delete fViewState;
288 	delete fModel;
289 	delete fKeyRunner;
290 	delete fBrokenLinks;
291 
292 	IconCache::sIconCache->Deleting(this);
293 }
294 
295 
296 void
297 BPoseView::Init(AttributeStreamNode* node)
298 {
299 	RestoreState(node);
300 	InitCommon();
301 }
302 
303 
304 void
305 BPoseView::Init(const BMessage &message)
306 {
307 	RestoreState(message);
308 	InitCommon();
309 }
310 
311 
312 void
313 BPoseView::InitCommon()
314 {
315 	BContainerWindow* window = ContainerWindow();
316 
317 	// create title view for window
318 	BRect rect(Frame());
319 	rect.bottom = rect.top + kTitleViewHeight;
320 	fTitleView = new BTitleView(rect, this);
321 	if (ViewMode() == kListMode) {
322 		// resize and move poseview
323 		MoveBy(0, kTitleViewHeight + 1);
324 		ResizeBy(0, -(kTitleViewHeight + 1));
325 
326 		if (Parent())
327 			Parent()->AddChild(fTitleView);
328 		else
329 			Window()->AddChild(fTitleView);
330 	}
331 
332 	if (fHScrollBar)
333 		fHScrollBar->SetTitleView(fTitleView);
334 
335 	BPoint origin;
336 	if (ViewMode() == kListMode)
337 		origin = fViewState->ListOrigin();
338 	else
339 		origin = fViewState->IconOrigin();
340 
341 	PinPointToValidRange(origin);
342 
343 	// init things related to laying out items
344 	fListElemHeight = ceilf(sFontHeight < 20 ? 20 : sFontHeight + 6);
345 	SetIconPoseHeight();
346 	GetLayoutInfo(ViewMode(), &fGrid, &fOffset);
347 	ResetPosePlacementHint();
348 
349 	DisableScrollBars();
350 	ScrollTo(origin);
351 	UpdateScrollRange();
352 	SetScrollBarsTo(origin);
353 	EnableScrollBars();
354 
355 	StartWatching();
356 		// turn on volume node monitor, metamime monitor, etc.
357 
358 	if (window && window->ShouldAddCountView())
359 		AddCountView();
360 
361 	// populate the window
362 	if (window && window->IsTrash())
363 		AddTrashPoses();
364 	else
365 		AddPoses(TargetModel());
366 }
367 
368 
369 static int
370 CompareColumns(const BColumn* c1, const BColumn* c2)
371 {
372 	if (c1->Offset() > c2->Offset())
373 		return 1;
374 	else if (c1->Offset() < c2->Offset())
375 		return -1;
376 
377 	return 0;
378 }
379 
380 
381 void
382 BPoseView::RestoreColumnState(AttributeStreamNode* node)
383 {
384 	fColumnList->MakeEmpty();
385 	if (node) {
386 		const char* columnsAttr;
387 		const char* columnsAttrForeign;
388 		if (TargetModel() && TargetModel()->IsRoot()) {
389 			columnsAttr = kAttrDisksColumns;
390 			columnsAttrForeign = kAttrDisksColumnsForeign;
391 		} else {
392 			columnsAttr = kAttrColumns;
393 			columnsAttrForeign = kAttrColumnsForeign;
394 		}
395 
396 		bool wrongEndianness = false;
397 		const char* name = columnsAttr;
398 		size_t size = (size_t)node->Contains(name, B_RAW_TYPE);
399 		if (!size) {
400 			name = columnsAttrForeign;
401 			wrongEndianness = true;
402 			size = (size_t)node->Contains(name, B_RAW_TYPE);
403 		}
404 
405 		if (size > 0 && size < 10000) {
406 			// check for invalid sizes here to protect against
407 			// munged attributes
408 			char* buffer = new char[size];
409 			off_t result = node->Read(name, 0, B_RAW_TYPE, size, buffer);
410 			if (result) {
411 				BMallocIO stream;
412 				stream.WriteAt(0, buffer, size);
413 				stream.Seek(0, SEEK_SET);
414 
415 				// Clear old column list if neccessary
416 
417 				// Put items in the list in order so they can be checked
418 				// for overlaps below.
419 				BObjectList<BColumn> tempSortedList;
420 				for (;;) {
421 					BColumn* column = BColumn::InstantiateFromStream(&stream,
422 						wrongEndianness);
423 					if (!column)
424 						break;
425 					tempSortedList.AddItem(column);
426 				}
427 				AddColumnList(&tempSortedList);
428 			}
429 			delete [] buffer;
430 		}
431 	}
432 	SetUpDefaultColumnsIfNeeded();
433 	if (!ColumnFor(PrimarySort())) {
434 		fViewState->SetPrimarySort(FirstColumn()->AttrHash());
435 		fViewState->SetPrimarySortType(FirstColumn()->AttrType());
436 	}
437 
438 	if (PrimarySort() == SecondarySort())
439 		fViewState->SetSecondarySort(0);
440 }
441 
442 
443 void
444 BPoseView::RestoreColumnState(const BMessage &message)
445 {
446 	fColumnList->MakeEmpty();
447 
448 	BObjectList<BColumn> tempSortedList;
449 	for (int32 index = 0; ; index++) {
450 		BColumn* column = BColumn::InstantiateFromMessage(message, index);
451 		if (!column)
452 			break;
453 		tempSortedList.AddItem(column);
454 	}
455 
456 	AddColumnList(&tempSortedList);
457 
458 	SetUpDefaultColumnsIfNeeded();
459 	if (!ColumnFor(PrimarySort())) {
460 		fViewState->SetPrimarySort(FirstColumn()->AttrHash());
461 		fViewState->SetPrimarySortType(FirstColumn()->AttrType());
462 	}
463 
464 	if (PrimarySort() == SecondarySort())
465 		fViewState->SetSecondarySort(0);
466 }
467 
468 
469 void
470 BPoseView::AddColumnList(BObjectList<BColumn>* list)
471 {
472 	list->SortItems(&CompareColumns);
473 
474 	float nextLeftEdge = 0;
475 	for (int32 columIndex = 0; columIndex < list->CountItems();
476 			columIndex++) {
477 		BColumn* column = list->ItemAt(columIndex);
478 
479 		// Make sure that columns don't overlap
480 		if (column->Offset() < nextLeftEdge) {
481 			PRINT(("\t**Overlapped columns in archived column state\n"));
482 			column->SetOffset(nextLeftEdge);
483 		}
484 
485 		nextLeftEdge = column->Offset() + column->Width()
486 			- kRoomForLine / 2.0f + kTitleColumnExtraMargin;
487 		fColumnList->AddItem(column);
488 
489 		if (!IsWatchingDateFormatChange()
490 			&& column->AttrType() == B_TIME_TYPE) {
491 			StartWatchDateFormatChange();
492 		}
493 	}
494 }
495 
496 
497 void
498 BPoseView::RestoreState(AttributeStreamNode* node)
499 {
500 	RestoreColumnState(node);
501 
502 	if (node) {
503 		const char* viewStateAttr;
504 		const char* viewStateAttrForeign;
505 
506 		if (TargetModel() && TargetModel()->IsRoot()) {
507 			viewStateAttr = kAttrDisksViewState;
508 			viewStateAttrForeign = kAttrDisksViewStateForeign;
509 		} else {
510 			viewStateAttr = ViewStateAttributeName();
511 			viewStateAttrForeign = ForeignViewStateAttributeName();
512 		}
513 
514 		bool wrongEndianness = false;
515 		const char* name = viewStateAttr;
516 		size_t size = (size_t)node->Contains(name, B_RAW_TYPE);
517 		if (!size) {
518 			name = viewStateAttrForeign;
519 			wrongEndianness = true;
520 			size = (size_t)node->Contains(name, B_RAW_TYPE);
521 		}
522 
523 		if (size > 0 && size < 10000) {
524 			// check for invalid sizes here to protect against
525 			// munged attributes
526 			char* buffer = new char[size];
527 			off_t result = node->Read(name, 0, B_RAW_TYPE, size, buffer);
528 			if (result) {
529 				BMallocIO stream;
530 				stream.WriteAt(0, buffer, size);
531 				stream.Seek(0, SEEK_SET);
532 				BViewState* viewstate
533 					= BViewState::InstantiateFromStream(&stream,
534 						wrongEndianness);
535 				if (viewstate) {
536 					delete fViewState;
537 					fViewState = viewstate;
538 				}
539 			}
540 			delete [] buffer;
541 		}
542 	}
543 
544 	if (IsDesktopWindow() && ViewMode() == kListMode)
545 		// recover if desktop window view state set wrong
546 		fViewState->SetViewMode(kIconMode);
547 }
548 
549 
550 void
551 BPoseView::RestoreState(const BMessage &message)
552 {
553 	RestoreColumnState(message);
554 
555 	BViewState* viewstate = BViewState::InstantiateFromMessage(message);
556 
557 	if (viewstate) {
558 		delete fViewState;
559 		fViewState = viewstate;
560 	}
561 
562 	if (IsDesktopWindow() && ViewMode() == kListMode) {
563 		// recover if desktop window view state set wrong
564 		fViewState->SetViewMode(kIconMode);
565 	}
566 }
567 
568 
569 namespace BPrivate {
570 
571 bool
572 ClearViewOriginOne(const char* DEBUG_ONLY(name), uint32 type, off_t size,
573 	void* viewStateArchive, void*)
574 {
575 	ASSERT(strcmp(name, kAttrViewState) == 0);
576 
577 	if (!viewStateArchive)
578 		return false;
579 
580 	if (type != B_RAW_TYPE)
581 		return false;
582 
583 	BMallocIO stream;
584 	stream.WriteAt(0, viewStateArchive, (size_t)size);
585 	stream.Seek(0, SEEK_SET);
586 	BViewState* viewstate = BViewState::InstantiateFromStream(&stream, false);
587 	if (!viewstate)
588 		return false;
589 
590 	// this is why we are here - zero out
591 	viewstate->SetListOrigin(BPoint(0, 0));
592 	viewstate->SetIconOrigin(BPoint(0, 0));
593 
594 	stream.Seek(0, SEEK_SET);
595 	viewstate->ArchiveToStream(&stream);
596 	stream.ReadAt(0, viewStateArchive, (size_t)size);
597 
598 	return true;
599 }
600 
601 }	// namespace BPrivate
602 
603 
604 void
605 BPoseView::SetUpDefaultColumnsIfNeeded()
606 {
607 	// in case there were errors getting some columns
608 	if (fColumnList->CountItems() != 0)
609 		return;
610 
611 	fColumnList->AddItem(new BColumn(B_TRANSLATE("Name"), kColumnStart, 145,
612 		B_ALIGN_LEFT, kAttrStatName, B_STRING_TYPE, true, true));
613 	fColumnList->AddItem(new BColumn(B_TRANSLATE("Size"), 200, 80,
614 		B_ALIGN_RIGHT, kAttrStatSize, B_OFF_T_TYPE, true, false));
615 	fColumnList->AddItem(new BColumn(B_TRANSLATE("Modified"), 295, 150,
616 		B_ALIGN_LEFT, kAttrStatModified, B_TIME_TYPE, true, false));
617 
618 	if (!IsWatchingDateFormatChange())
619 		StartWatchDateFormatChange();
620 }
621 
622 
623 const char*
624 BPoseView::ViewStateAttributeName() const
625 {
626 	return IsDesktopView() ? kAttrDesktopViewState : kAttrViewState;
627 }
628 
629 
630 const char*
631 BPoseView::ForeignViewStateAttributeName() const
632 {
633 	return IsDesktopView() ? kAttrDesktopViewStateForeign
634 		: kAttrViewStateForeign;
635 }
636 
637 
638 void
639 BPoseView::SaveColumnState(AttributeStreamNode* node)
640 {
641 	BMallocIO stream;
642 	for (int32 index = 0; ; index++) {
643 		const BColumn* column = ColumnAt(index);
644 		if (!column)
645 			break;
646 		column->ArchiveToStream(&stream);
647 	}
648 	const char* columnsAttr;
649 	const char* columnsAttrForeign;
650 	if (TargetModel() && TargetModel()->IsRoot()) {
651 		columnsAttr = kAttrDisksColumns;
652 		columnsAttrForeign = kAttrDisksColumnsForeign;
653 	} else {
654 		columnsAttr = kAttrColumns;
655 		columnsAttrForeign = kAttrColumnsForeign;
656 	}
657 	node->Write(columnsAttr, columnsAttrForeign, B_RAW_TYPE,
658 		stream.Position(), stream.Buffer());
659 }
660 
661 
662 void
663 BPoseView::SaveColumnState(BMessage &message) const
664 {
665 	for (int32 index = 0; ; index++) {
666 		const BColumn* column = ColumnAt(index);
667 		if (!column)
668 			break;
669 		column->ArchiveToMessage(message);
670 	}
671 }
672 
673 
674 void
675 BPoseView::SaveState(AttributeStreamNode* node)
676 {
677 	SaveColumnState(node);
678 
679 	// save view state into object
680 	BMallocIO stream;
681 
682 	stream.Seek(0, SEEK_SET);
683 	fViewState->ArchiveToStream(&stream);
684 
685 	const char* viewStateAttr;
686 	const char* viewStateAttrForeign;
687 	if (TargetModel() && TargetModel()->IsRoot()) {
688 		viewStateAttr = kAttrDisksViewState;
689 		viewStateAttrForeign = kAttrDisksViewStateForeign;
690 	} else {
691 		viewStateAttr = ViewStateAttributeName();
692 		viewStateAttrForeign = ForeignViewStateAttributeName();
693 	}
694 
695 	node->Write(viewStateAttr, viewStateAttrForeign, B_RAW_TYPE,
696 		stream.Position(), stream.Buffer());
697 
698 	fStateNeedsSaving = false;
699 }
700 
701 
702 void
703 BPoseView::SaveState(BMessage &message) const
704 {
705 	SaveColumnState(message);
706 	fViewState->ArchiveToMessage(message);
707 }
708 
709 
710 float
711 BPoseView::StringWidth(const char* str) const
712 {
713 	return BPrivate::gWidthBuffer->StringWidth(str, 0, (int32)strlen(str),
714 		&sCurrentFont);
715 }
716 
717 
718 float
719 BPoseView::StringWidth(const char* str, int32 len) const
720 {
721 	ASSERT(strlen(str) == (uint32)len);
722 	return BPrivate::gWidthBuffer->StringWidth(str, 0, len, &sCurrentFont);
723 }
724 
725 
726 void
727 BPoseView::SavePoseLocations(BRect* frameIfDesktop)
728 {
729 	PoseInfo poseInfo;
730 
731 	if (!fSavePoseLocations)
732 		return;
733 
734 	ASSERT(TargetModel());
735 	ASSERT(Window()->IsLocked());
736 
737 	BVolume volume(TargetModel()->NodeRef()->device);
738 	if (volume.InitCheck() != B_OK)
739 		return;
740 
741 	if (!TargetModel()->IsRoot()
742 		&& (volume.IsReadOnly() || !volume.KnowsAttr())) {
743 		// check that we can write out attrs; Root should always work
744 		// because it gets saved on the boot disk but the above checks
745 		// will fail
746 		return;
747 	}
748 
749 	bool desktop = IsDesktopWindow() && (frameIfDesktop != NULL);
750 
751 	int32 count = fPoseList->CountItems();
752 	for (int32 index = 0; index < count; index++) {
753 		BPose* pose = fPoseList->ItemAt(index);
754 		if (pose->NeedsSaveLocation() && pose->HasLocation()) {
755 			Model* model = pose->TargetModel();
756 			poseInfo.fInvisible = false;
757 
758 			if (model->IsRoot())
759 				poseInfo.fInitedDirectory = TargetModel()->NodeRef()->node;
760 			else
761 				poseInfo.fInitedDirectory = model->EntryRef()->directory;
762 
763 			poseInfo.fLocation = pose->Location(this);
764 
765 			ExtendedPoseInfo* extendedPoseInfo = NULL;
766 			size_t extendedPoseInfoSize = 0;
767 			ModelNodeLazyOpener opener(model, true);
768 
769 			if (desktop) {
770 				opener.OpenNode(true);
771 				// if saving desktop icons, save an extended pose info too
772 				extendedPoseInfo = ReadExtendedPoseInfo(model);
773 					// read the pre-existing one
774 
775 				if (!extendedPoseInfo) {
776 					// don't have one yet, allocate one
777 					size_t size = ExtendedPoseInfo::Size(1);
778 					extendedPoseInfo = (ExtendedPoseInfo*)
779 						new char [size];
780 
781 					memset(extendedPoseInfo, 0, size);
782 					extendedPoseInfo->fWorkspaces = 0xffffffff;
783 					extendedPoseInfo->fInvisible = false;
784 					extendedPoseInfo->fShowFromBootOnly = false;
785 					extendedPoseInfo->fNumFrames = 0;
786 				}
787 				ASSERT(extendedPoseInfo);
788 
789 				extendedPoseInfo->SetLocationForFrame(pose->Location(this),
790 					*frameIfDesktop);
791 				extendedPoseInfoSize = extendedPoseInfo->Size();
792 			}
793 
794 			if (model->InitCheck() != B_OK) {
795 				delete[] (char*)extendedPoseInfo;
796 				continue;
797 			}
798 
799 			ASSERT(model);
800 			ASSERT(model->InitCheck() == B_OK);
801 			// special handling for "root" disks icon
802 			// and trash pose on desktop dir
803 			bool isTrash = model->IsTrash() && IsDesktopView();
804 			if (model->IsRoot() || isTrash) {
805 				BDirectory dir;
806 				if (FSGetDeskDir(&dir) == B_OK) {
807 					const char* poseInfoAttr = isTrash ? kAttrTrashPoseInfo
808 						: kAttrDisksPoseInfo;
809 					const char* poseInfoAttrForeign = isTrash
810 						? kAttrTrashPoseInfoForeign
811 						: kAttrDisksPoseInfoForeign;
812 						if (dir.WriteAttr(poseInfoAttr, B_RAW_TYPE, 0,
813 						&poseInfo, sizeof(poseInfo)) == sizeof(poseInfo))
814 						// nuke opposite endianness
815 						dir.RemoveAttr(poseInfoAttrForeign);
816 
817 					if (!isTrash && desktop
818 						&& dir.WriteAttr(kAttrExtendedDisksPoseInfo,
819 						B_RAW_TYPE, 0, extendedPoseInfo, extendedPoseInfoSize)
820 							== (ssize_t)extendedPoseInfoSize) {
821 						// nuke opposite endianness
822 						dir.RemoveAttr(kAttrExtendedDisksPoseInfoForegin);
823 					}
824 				}
825 			} else {
826 				model->WriteAttrKillForeign(kAttrPoseInfo,
827 					kAttrPoseInfoForeign, B_RAW_TYPE, 0, &poseInfo,
828 					sizeof(poseInfo));
829 
830 				if (desktop) {
831 					model->WriteAttrKillForeign(kAttrExtendedPoseInfo,
832 						kAttrExtendedPoseInfoForegin,
833 						B_RAW_TYPE, 0, extendedPoseInfo,
834 						extendedPoseInfoSize);
835 				}
836 			}
837 
838 			delete[] (char*)extendedPoseInfo;
839 				// TODO: fix up this mess
840 		}
841 	}
842 }
843 
844 
845 void
846 BPoseView::StartWatching()
847 {
848 	// watch volumes
849 	TTracker::WatchNode(NULL, B_WATCH_MOUNT, this);
850 
851 	if (TargetModel() != NULL)
852 		TTracker::WatchNode(TargetModel()->NodeRef(), B_WATCH_ATTR, this);
853 
854 	BMimeType::StartWatching(BMessenger(this));
855 }
856 
857 
858 void
859 BPoseView::StopWatching()
860 {
861 	stop_watching(this);
862 	BMimeType::StopWatching(BMessenger(this));
863 }
864 
865 
866 void
867 BPoseView::DetachedFromWindow()
868 {
869 	if (fTitleView && !fTitleView->Window())
870 		delete fTitleView;
871 
872 	if (TTracker* app = dynamic_cast<TTracker*>(be_app)) {
873 		app->Lock();
874 		app->StopWatching(this, kShowSelectionWhenInactiveChanged);
875 		app->StopWatching(this, kTransparentSelectionChanged);
876 		app->StopWatching(this, kSortFolderNamesFirstChanged);
877 		app->StopWatching(this, kTypeAheadFilteringChanged);
878 		app->Unlock();
879 	}
880 
881 	StopWatching();
882 	CommitActivePose();
883 	SavePoseLocations();
884 
885 	FSClipboardStopWatch(this);
886 }
887 
888 
889 void
890 BPoseView::Pulse()
891 {
892 	BContainerWindow* window = ContainerWindow();
893 	if (!window)
894 		return;
895 
896 	window->PulseTaskLoop();
897 		// make sure task loop gets pulsed properly, if installed
898 
899 	// update item count view in window if necessary
900 	UpdateCount();
901 
902 	if (fAutoScrollState != kAutoScrollOff)
903 		HandleAutoScroll();
904 
905 	// do we need to update scrollbars?
906 	BRect extent = Extent();
907 	if ((fLastExtent != extent) || (fLastLeftTop != LeftTop())) {
908 		uint32 button;
909 		BPoint mouse;
910 		GetMouse(&mouse, &button);
911 		if (!button) {
912 			UpdateScrollRange();
913 			fLastExtent = extent;
914 			fLastLeftTop = LeftTop();
915 		}
916 	}
917 
918 	// do we have a TextWidget waiting for expiracy of its double-click
919 	// check?
920 	if (fTextWidgetToCheck != NULL)
921 		fTextWidgetToCheck->CheckExpiration();
922 }
923 
924 
925 void
926 BPoseView::MoveBy(float x, float y)
927 {
928 	if (fTitleView && fTitleView->Window())
929 		fTitleView->MoveBy(x, y);
930 
931 	_inherited::MoveBy(x, y);
932 }
933 
934 
935 void
936 BPoseView::ScrollTo(BPoint point)
937 {
938 	_inherited::ScrollTo(point);
939 
940 	//keep the view state in sync.
941 	if (ViewMode() == kListMode)
942 		fViewState->SetListOrigin(LeftTop());
943 	else
944 		fViewState->SetIconOrigin(LeftTop());
945 }
946 
947 
948 void
949 BPoseView::AttachedToWindow()
950 {
951 	fIsDesktopWindow = (dynamic_cast<BDeskWindow*>(Window()) != 0);
952 	if (fIsDesktopWindow)
953 		AddFilter(new TPoseViewFilter(this));
954 
955 	AddFilter(new ShortcutFilter(B_RETURN, B_OPTION_KEY, kOpenSelection,
956 		this));
957 		// add Option-Return as a shortcut filter because AddShortcut
958 		// doesn't allow us to have shortcuts without Command yet
959 	AddFilter(new ShortcutFilter(B_ESCAPE, 0, B_CANCEL, this));
960 		// Escape key, used to abort an on-going clipboard cut or filtering
961 	AddFilter(new ShortcutFilter(B_ESCAPE, B_SHIFT_KEY,
962 		kCancelSelectionToClipboard, this));
963 		// Escape + SHIFT will remove current selection from clipboard,
964 		// or all poses from current folder if 0 selected
965 
966 	AddFilter(new LongAndDragTrackingFilter(kMsgMouseLongDown,
967 		kMsgMouseDragged));
968 
969 	fLastLeftTop = LeftTop();
970 	BFont font(be_plain_font);
971 	font.SetSpacing(B_BITMAP_SPACING);
972 	SetFont(&font);
973 	GetFont(&sCurrentFont);
974 
975 	// static - init just once
976 	if (sFontHeight == -1) {
977 		font.GetHeight(&sFontInfo);
978 		sFontHeight = sFontInfo.ascent + sFontInfo.descent
979 			+ sFontInfo.leading;
980 	}
981 
982 	if (TTracker* app = dynamic_cast<TTracker*>(be_app)) {
983 		app->Lock();
984 		app->StartWatching(this, kShowSelectionWhenInactiveChanged);
985 		app->StartWatching(this, kTransparentSelectionChanged);
986 		app->StartWatching(this, kSortFolderNamesFirstChanged);
987 		app->StartWatching(this, kTypeAheadFilteringChanged);
988 		app->Unlock();
989 	}
990 
991 	FSClipboardStartWatch(this);
992 }
993 
994 
995 void
996 BPoseView::SetIconPoseHeight()
997 {
998 	switch (ViewMode()) {
999 		case kIconMode:
1000 			// IconSize should allready be set in MessageReceived()
1001 			fIconPoseHeight = ceilf(IconSizeInt() + sFontHeight + 1);
1002 			break;
1003 
1004 		case kMiniIconMode:
1005 			fViewState->SetIconSize(B_MINI_ICON);
1006 			fIconPoseHeight = ceilf(sFontHeight <
1007 				IconSizeInt() ? IconSizeInt() : sFontHeight + 1);
1008 			break;
1009 
1010 		default:
1011 			fViewState->SetIconSize(B_MINI_ICON);
1012 			fIconPoseHeight = fListElemHeight;
1013 			break;
1014 	}
1015 }
1016 
1017 
1018 void
1019 BPoseView::GetLayoutInfo(uint32 mode, BPoint* grid, BPoint* offset) const
1020 {
1021 	switch (mode) {
1022 		case kMiniIconMode:
1023 			grid->Set(96, 20);
1024 			offset->Set(10, 5);
1025 			break;
1026 
1027 		case kIconMode:
1028 			grid->Set(IconSizeInt() + 28, IconSizeInt() + 28);
1029 			offset->Set(20, 20);
1030 			break;
1031 
1032 		default:
1033 			grid->Set(0, 0);
1034 			offset->Set(5, 5);
1035 			break;
1036 	}
1037 }
1038 
1039 
1040 void
1041 BPoseView::MakeFocus(bool focused)
1042 {
1043 	bool inval = false;
1044 	if (focused != IsFocus())
1045 		inval = true;
1046 
1047 	_inherited::MakeFocus(focused);
1048 
1049 	if (inval) {
1050 		BackgroundView* view = dynamic_cast<BackgroundView*>(Parent());
1051 		if (view)
1052 			view->PoseViewFocused(focused);
1053 	}
1054 }
1055 
1056 
1057 void
1058 BPoseView::WindowActivated(bool activated)
1059 {
1060 	if (activated == false)
1061 		CommitActivePose();
1062 
1063 	if (fShowHideSelection)
1064 		ShowSelection(activated);
1065 
1066 	if (activated && !ActivePose() && !IsFilePanel())
1067 		MakeFocus();
1068 }
1069 
1070 
1071 void
1072 BPoseView::SetActivePose(BPose* pose)
1073 {
1074 	if (pose != ActivePose()) {
1075 		CommitActivePose();
1076 		fActivePose = pose;
1077 	}
1078 }
1079 
1080 
1081 void
1082 BPoseView::CommitActivePose(bool saveChanges)
1083 {
1084 	if (ActivePose()) {
1085 		int32 index = fPoseList->IndexOf(ActivePose());
1086 		if (fFiltering)
1087 			index = fFilteredPoseList->IndexOf(ActivePose());
1088 		BPoint loc(0, index * fListElemHeight);
1089 		if (ViewMode() != kListMode)
1090 			loc = ActivePose()->Location(this);
1091 
1092 		ActivePose()->Commit(saveChanges, loc, this, index);
1093 		BPose *pose = fActivePose;
1094 		fActivePose = NULL;
1095 		if (fFiltering && !FilterPose(pose))
1096 			RemoveFilteredPose(pose, index);
1097 	}
1098 }
1099 
1100 
1101 EntryListBase*
1102 BPoseView::InitDirentIterator(const entry_ref* ref)
1103 {
1104 	// set up a directory iteration
1105 	Model sourceModel(ref, false, true);
1106 	if (sourceModel.InitCheck() != B_OK)
1107 		return NULL;
1108 
1109 	ASSERT(!sourceModel.IsQuery());
1110 	ASSERT(sourceModel.Node());
1111 
1112 	BDirectory* directory = dynamic_cast<BDirectory*>(sourceModel.Node());
1113 	ASSERT(directory);
1114 
1115 	EntryListBase* result = new CachedDirectoryEntryList(*directory);
1116 
1117 	if (result->Rewind() != B_OK) {
1118 		delete result;
1119 		HideBarberPole();
1120 		return NULL;
1121 	}
1122 
1123 	TTracker::WatchNode(sourceModel.NodeRef(), B_WATCH_DIRECTORY
1124 		| B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, this);
1125 
1126 	return result;
1127 }
1128 
1129 
1130 uint32
1131 BPoseView::WatchNewNodeMask()
1132 {
1133 #ifdef __HAIKU__
1134 	return B_WATCH_STAT | B_WATCH_INTERIM_STAT | B_WATCH_ATTR;
1135 #else
1136 	return B_WATCH_STAT | B_WATCH_ATTR;
1137 #endif
1138 }
1139 
1140 
1141 status_t
1142 BPoseView::WatchNewNode(const node_ref* item)
1143 {
1144 	return WatchNewNode(item, WatchNewNodeMask(), BMessenger(this));
1145 }
1146 
1147 
1148 status_t
1149 BPoseView::WatchNewNode(const node_ref* item, uint32 mask, BMessenger messenger)
1150 {
1151 	status_t result = TTracker::WatchNode(item, mask, messenger);
1152 
1153 #if DEBUG
1154 	if (result != B_OK)
1155 		PRINT(("failed to watch node %s\n", strerror(result)));
1156 #endif
1157 
1158 	return result;
1159 }
1160 
1161 
1162 struct AddPosesParams {
1163 	BMessenger target;
1164 	entry_ref ref;
1165 };
1166 
1167 
1168 bool
1169 BPoseView::IsValidAddPosesThread(thread_id currentThread) const
1170 {
1171 	return fAddPosesThreads.find(currentThread) != fAddPosesThreads.end();
1172 }
1173 
1174 
1175 void
1176 BPoseView::AddPoses(Model* model)
1177 {
1178 	// if model is zero, PoseView has other means of iterating through all
1179 	// the entries that it adds
1180 	if (model) {
1181 		TrackerSettings settings;
1182 		if (model->IsRoot()) {
1183 			AddRootPoses(true, settings.MountSharedVolumesOntoDesktop());
1184 			return;
1185 		} else if (IsDesktopView()
1186 			&& (settings.MountVolumesOntoDesktop() || settings.ShowDisksIcon()
1187 				|| (IsFilePanel() && settings.DesktopFilePanelRoot())))
1188 			AddRootPoses(true, settings.MountSharedVolumesOntoDesktop());
1189 	}
1190 
1191 	ShowBarberPole();
1192 
1193 	AddPosesParams* params = new AddPosesParams();
1194 	BMessenger tmp(this);
1195 	params->target = tmp;
1196 
1197 	if (model)
1198 		params->ref = *model->EntryRef();
1199 
1200 	thread_id addPosesThread = spawn_thread(&BPoseView::AddPosesTask,
1201 		"add poses", B_DISPLAY_PRIORITY, params);
1202 
1203 	if (addPosesThread >= B_OK) {
1204 		fAddPosesThreads.insert(addPosesThread);
1205 		resume_thread(addPosesThread);
1206 	} else
1207 		delete params;
1208 }
1209 
1210 
1211 class AutoLockingMessenger {
1212 	// Note:
1213 	// this locker requires that you lock/unlock the messenger and associated
1214 	// looper only through the autolocker interface, otherwise the hasLock
1215 	// flag gets out of sync
1216 	//
1217 	// Also, this class represents the entire BMessenger, not just it's
1218 	// autolocker (unlike MessengerAutoLocker)
1219 	public:
1220 		AutoLockingMessenger(const BMessenger &target, bool lockLater = false)
1221 			: messenger(target),
1222 			hasLock(false)
1223 		{
1224 			if (!lockLater)
1225 				hasLock = messenger.LockTarget();
1226 		}
1227 
1228 		~AutoLockingMessenger()
1229 		{
1230 			if (hasLock) {
1231 				BLooper* looper;
1232 				messenger.Target(&looper);
1233 				ASSERT(looper->IsLocked());
1234 				looper->Unlock();
1235 			}
1236 		}
1237 
1238 		bool Lock()
1239 		{
1240 			if (!hasLock)
1241 				hasLock = messenger.LockTarget();
1242 
1243 			return hasLock;
1244 		}
1245 
1246 		bool IsLocked() const
1247 		{
1248 			return hasLock;
1249 		}
1250 
1251 		void Unlock()
1252 		{
1253 			if (hasLock) {
1254 				BLooper* looper;
1255 				messenger.Target(&looper);
1256 				ASSERT(looper);
1257 				looper->Unlock();
1258 				hasLock = false;
1259 			}
1260 		}
1261 
1262 		BLooper* Looper() const
1263 		{
1264 			BLooper* looper;
1265 			messenger.Target(&looper);
1266 			return looper;
1267 		}
1268 
1269 		BHandler* Handler() const
1270 		{
1271 			ASSERT(hasLock);
1272 			return messenger.Target(0);
1273 		}
1274 
1275 		BMessenger Target() const
1276 		{
1277 			return messenger;
1278 		}
1279 
1280 	private:
1281 		BMessenger messenger;
1282 		bool hasLock;
1283 };
1284 
1285 
1286 class failToLock { /* exception in AddPoses*/ };
1287 
1288 
1289 status_t
1290 BPoseView::AddPosesTask(void* castToParams)
1291 {
1292 	// AddPosesTask reeds a bunch of models and passes them off to
1293 	// the pose placing and drawing routine.
1294 
1295 	AddPosesParams* params = (AddPosesParams*)castToParams;
1296 	BMessenger target(params->target);
1297 	entry_ref ref(params->ref);
1298 
1299 	delete params;
1300 
1301 	AutoLockingMessenger lock(target);
1302 
1303 	if (!lock.IsLocked())
1304 		return B_ERROR;
1305 
1306 	thread_id threadID = find_thread(NULL);
1307 
1308 	BPoseView* view = dynamic_cast<BPoseView*>(lock.Handler());
1309 	ASSERT(view);
1310 
1311 	// BWindow* window = dynamic_cast<BWindow*>(lock.Looper());
1312 	ASSERT(dynamic_cast<BWindow*>(lock.Looper()));
1313 
1314 	// allocate the iterator we will use for adding poses; this
1315 	// can be a directory or any other collection of entry_refs, such
1316 	// as results of a query; subclasses override this to provide
1317 	// other than standard directory iterations
1318 	EntryListBase* container = view->InitDirentIterator(&ref);
1319 	if (!container) {
1320 		view->HideBarberPole();
1321 		return B_ERROR;
1322 	}
1323 
1324 	AddPosesResult* posesResult = new AddPosesResult;
1325 	posesResult->fCount = 0;
1326 	int32 modelChunkIndex = -1;
1327 	bigtime_t nextChunkTime = 0;
1328 	uint32 watchMask = view->WatchNewNodeMask();
1329 
1330 	bool hideDotFiles = TrackerSettings().HideDotFiles();
1331 
1332 #if DEBUG
1333 	for (int32 index = 0; index < kMaxAddPosesChunk; index++)
1334 		posesResult->fModels[index] = (Model*)0xdeadbeef;
1335 #endif
1336 
1337 	try {
1338 		for (;;) {
1339 			lock.Unlock();
1340 
1341 			status_t result = B_OK;
1342 			char entBuf[1024];
1343 			dirent* eptr = (dirent*)entBuf;
1344 			Model* model = 0;
1345 			node_ref dirNode;
1346 			node_ref itemNode;
1347 
1348 			int32 count = container->GetNextDirents(eptr, 1024, 1);
1349 			if (count <= 0 && modelChunkIndex == -1)
1350 				break;
1351 
1352 			if (count) {
1353 				ASSERT(count == 1);
1354 
1355 				if ((!hideDotFiles && (!strcmp(eptr->d_name, ".")
1356 					|| !strcmp(eptr->d_name, "..")))
1357 					|| (hideDotFiles && eptr->d_name[0] == '.')) {
1358 					continue;
1359 				}
1360 
1361 				dirNode.device = eptr->d_pdev;
1362 				dirNode.node = eptr->d_pino;
1363 				itemNode.device = eptr->d_dev;
1364 				itemNode.node = eptr->d_ino;
1365 
1366 				BPoseView::WatchNewNode(&itemNode, watchMask, lock.Target());
1367 					// have to node monitor ahead of time because Model will
1368 					// cache up the file type and preferred app
1369 					// OK to call when poseView is not locked
1370 				model = new Model(&dirNode, &itemNode, eptr->d_name, false);
1371 				result = model->InitCheck();
1372 				modelChunkIndex++;
1373 				posesResult->fModels[modelChunkIndex] = model;
1374 			}
1375 
1376 			// before we access the pose view, lock down the window
1377 
1378 			if (!lock.Lock()) {
1379 				PRINT(("failed to lock\n"));
1380 				posesResult->fCount = modelChunkIndex + 1;
1381 				throw failToLock();
1382 			}
1383 
1384 			if (!view->IsValidAddPosesThread(threadID)) {
1385 				// this handles the case of a file panel when the directory is
1386 				// switched and and old AddPosesTask needs to die.
1387 				// we might no longer be the current async thread
1388 				// for this view - if not then we're done
1389 				view->HideBarberPole();
1390 
1391 				// for now use the same cleanup as failToLock does
1392 				posesResult->fCount = modelChunkIndex + 1;
1393 				throw failToLock();
1394 			}
1395 
1396 			if (count) {
1397 				// try to watch the model, no matter what
1398 
1399 				if (result != B_OK) {
1400 					// failed to init pose, model is a zombie, add to zombie
1401 					// list
1402 					PRINT(("1 adding model %s to zombie list, error %s\n",
1403 						model->Name(), strerror(model->InitCheck())));
1404 					view->fZombieList->AddItem(model);
1405 					modelChunkIndex--;
1406 					continue;
1407 				}
1408 
1409 				view->ReadPoseInfo(model,
1410 					&posesResult->fPoseInfos[modelChunkIndex]);
1411 
1412 				if (!PoseVisible(model,
1413 					&posesResult->fPoseInfos[modelChunkIndex])) {
1414 					modelChunkIndex--;
1415 					continue;
1416 				}
1417 
1418 				if (model->IsSymLink())
1419 					view->CreateSymlinkPoseTarget(model);
1420 			}
1421 
1422 			bigtime_t now = system_time();
1423 
1424 			if (!count || modelChunkIndex >= kMaxAddPosesChunk - 1
1425 				|| now > nextChunkTime) {
1426 				// keep getting models until we get <kMaxAddPosesChunk> of them
1427 				// or until 300000 runs out
1428 
1429 				ASSERT(modelChunkIndex >= 0);
1430 
1431 				// send of the created poses
1432 
1433 				posesResult->fCount = modelChunkIndex + 1;
1434 				BMessage creationData(kAddNewPoses);
1435 				creationData.AddPointer("currentPoses", posesResult);
1436 				creationData.AddRef("ref", &ref);
1437 
1438 				lock.Target().SendMessage(&creationData);
1439 
1440 				modelChunkIndex = -1;
1441 				nextChunkTime = now + 300000;
1442 
1443 				posesResult = new AddPosesResult;
1444 				posesResult->fCount = 0;
1445 
1446 				snooze(500);	// be nice
1447 			}
1448 
1449 			if (!count)
1450 				break;
1451 		}
1452 
1453 		BMessage finishedSending(kAddPosesCompleted);
1454 		lock.Target().SendMessage(&finishedSending);
1455 
1456 	} catch (failToLock) {
1457 		// we are here because the window got closed or otherwise failed to
1458 		// lock
1459 
1460 		PRINT(("add_poses cleanup \n"));
1461 		// failed to lock window, bail
1462 		delete posesResult;
1463 		delete container;
1464 
1465 		return B_ERROR;
1466 	}
1467 
1468 	ASSERT(modelChunkIndex == -1);
1469 
1470 	delete posesResult;
1471 	delete container;
1472 	// build attributes menu based on mime types we've added
1473 
1474  	if (lock.Lock()) {
1475 #ifdef MSIPL_COMPILE_H
1476 	// workaround for broken PPC STL, not needed with the SGI headers for x86
1477  		set<thread_id>::iterator i = view->fAddPosesThreads.find(threadID);
1478  		if (i != view->fAddPosesThreads.end())
1479  			view->fAddPosesThreads.erase(i);
1480 #else
1481 		view->fAddPosesThreads.erase(threadID);
1482 #endif
1483 	}
1484 
1485 	return B_OK;
1486 }
1487 
1488 
1489 void
1490 BPoseView::AddRootPoses(bool watchIndividually, bool mountShared)
1491 {
1492 	BVolumeRoster roster;
1493 	roster.Rewind();
1494 	BVolume volume;
1495 
1496 	if (TrackerSettings().ShowDisksIcon() && !TargetModel()->IsRoot()) {
1497 		BEntry entry("/");
1498 		Model model(&entry);
1499 		if (model.InitCheck() == B_OK) {
1500 			BMessage monitorMsg;
1501 			monitorMsg.what = B_NODE_MONITOR;
1502 
1503 			monitorMsg.AddInt32("opcode", B_ENTRY_CREATED);
1504 
1505 			monitorMsg.AddInt32("device", model.NodeRef()->device);
1506 			monitorMsg.AddInt64("node", model.NodeRef()->node);
1507 			monitorMsg.AddInt64("directory", model.EntryRef()->directory);
1508 			monitorMsg.AddString("name", model.EntryRef()->name);
1509 			if (Window())
1510 				Window()->PostMessage(&monitorMsg, this);
1511 		}
1512 	} else {
1513 		while (roster.GetNextVolume(&volume) == B_OK) {
1514 			if (!volume.IsPersistent())
1515 				continue;
1516 
1517 	 		if (volume.IsShared() && !mountShared)
1518 				continue;
1519 
1520 			CreateVolumePose(&volume, watchIndividually);
1521 		}
1522 	}
1523 
1524 	SortPoses();
1525 	UpdateCount();
1526 	Invalidate();
1527 }
1528 
1529 
1530 void
1531 BPoseView::RemoveRootPoses()
1532 {
1533 	int32 index;
1534 	int32 count = fPoseList->CountItems();
1535 	for (index = 0; index < count;) {
1536 		BPose* pose = fPoseList->ItemAt(index);
1537 		if (pose) {
1538 			Model* model = pose->TargetModel();
1539 			if (model) {
1540 				if (model->IsVolume()) {
1541 					DeletePose(model->NodeRef());
1542 					count--;
1543 				} else
1544 					index++;
1545 			}
1546 		}
1547 	}
1548 
1549 	SortPoses();
1550 	UpdateCount();
1551 	Invalidate();
1552 }
1553 
1554 
1555 void
1556 BPoseView::AddTrashPoses()
1557 {
1558 	// the trash window needs to display a union of all the
1559 	// trash folders from all the mounted volumes
1560 	BVolumeRoster volRoster;
1561 	volRoster.Rewind();
1562 	BVolume volume;
1563 	while (volRoster.GetNextVolume(&volume) == B_OK) {
1564 		if (!volume.IsPersistent())
1565 			continue;
1566 
1567 		BDirectory trashDir;
1568 		BEntry entry;
1569 		if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK
1570 			&& trashDir.GetEntry(&entry) == B_OK) {
1571 			Model model(&entry);
1572 			if (model.InitCheck() == B_OK)
1573 				AddPoses(&model);
1574 		}
1575 	}
1576 }
1577 
1578 
1579 void
1580 BPoseView::AddPosesCompleted()
1581 {
1582 	BContainerWindow* containerWindow = ContainerWindow();
1583 	if (containerWindow)
1584 		containerWindow->AddMimeTypesToMenu();
1585 
1586 	// if we're not in icon mode then we need to check for poses that
1587 	// were "auto" placed to see if they overlap with other icons
1588 	if (ViewMode() != kListMode)
1589 		CheckAutoPlacedPoses();
1590 
1591 	UpdateScrollRange();
1592 	HideBarberPole();
1593 
1594 	// make sure that the last item in the list is not placed
1595 	// above the top of the view (leaving you with an empty window)
1596 	if (ViewMode() == kListMode) {
1597 		BRect bounds(Bounds());
1598 		float lastItemTop
1599 			= (CurrentPoseList()->CountItems() - 1) * fListElemHeight;
1600 		if (bounds.top > lastItemTop)
1601 			BView::ScrollTo(bounds.left, max_c(lastItemTop, 0));
1602 	}
1603 }
1604 
1605 
1606 void
1607 BPoseView::CreateVolumePose(BVolume* volume, bool watchIndividually)
1608 {
1609 	if (volume->InitCheck() != B_OK || !volume->IsPersistent()) {
1610 		// We never want to create poses for those volumes; the file
1611 		// system root, /pipe, /dev, etc. are all non-persistent
1612 		return;
1613 	}
1614 
1615 	BDirectory root;
1616 	if (volume->GetRootDirectory(&root) == B_OK) {
1617 		node_ref itemNode;
1618 		root.GetNodeRef(&itemNode);
1619 
1620 		BEntry entry;
1621 		root.GetEntry(&entry);
1622 
1623 		entry_ref ref;
1624 		entry.GetRef(&ref);
1625 
1626 		node_ref dirNode;
1627 		dirNode.device = ref.device;
1628 		dirNode.node = ref.directory;
1629 
1630 		BPose* pose = EntryCreated(&dirNode, &itemNode, ref.name, 0);
1631 
1632 		if (pose && watchIndividually) {
1633 			// make sure volume names still get watched, even though
1634 			// they are on the desktop which is not their physical parent
1635 			pose->TargetModel()->WatchVolumeAndMountPoint(B_WATCH_NAME | B_WATCH_STAT
1636 				| B_WATCH_ATTR, this);
1637 		}
1638 	}
1639 }
1640 
1641 
1642 void
1643 BPoseView::CreateTrashPose()
1644 {
1645 	BVolume volume;
1646 	if (BVolumeRoster().GetBootVolume(&volume) == B_OK) {
1647 		BDirectory trash;
1648 		BEntry entry;
1649 		node_ref ref;
1650 		if (FSGetTrashDir(&trash, volume.Device()) == B_OK
1651 			&& trash.GetEntry(&entry) == B_OK && entry.GetNodeRef(&ref) == B_OK) {
1652 			WatchNewNode(&ref);
1653 			Model* model = new Model(&entry);
1654 			PoseInfo info;
1655 			ReadPoseInfo(model, &info);
1656 			CreatePose(model, &info, false, NULL, NULL, true);
1657 		}
1658 	}
1659 }
1660 
1661 
1662 BPose*
1663 BPoseView::CreatePose(Model* model, PoseInfo* poseInfo, bool insertionSort,
1664 	int32* indexPtr, BRect* boundsPtr, bool forceDraw)
1665 {
1666 	BPose* result;
1667 	CreatePoses(&model, poseInfo, 1, &result, insertionSort, indexPtr,
1668 		boundsPtr, forceDraw);
1669 	return result;
1670 }
1671 
1672 
1673 void
1674 BPoseView::FinishPendingScroll(float &listViewScrollBy, BRect srcRect)
1675 {
1676 	if (listViewScrollBy == 0.0)
1677 		return;
1678 
1679 	// copy top contents to bottom and
1680 	// redraw from top to top of part that could be copied
1681 
1682 	if (srcRect.Width() > listViewScrollBy) {
1683 		BRect dstRect = srcRect;
1684 		srcRect.bottom -= listViewScrollBy;
1685 		dstRect.top += listViewScrollBy;
1686 		CopyBits(srcRect, dstRect);
1687 		listViewScrollBy = 0;
1688 		srcRect.bottom = dstRect.top;
1689 	}
1690 	SynchronousUpdate(srcRect);
1691 }
1692 
1693 
1694 bool
1695 BPoseView::AddPosesThreadValid(const entry_ref* ref) const
1696 {
1697 	return *(TargetModel()->EntryRef()) == *ref || ContainerWindow()->IsTrash();
1698 }
1699 
1700 
1701 void
1702 BPoseView::AddPoseToList(PoseList* list, bool visibleList, bool insertionSort,
1703 	BPose* pose, BRect &viewBounds, float &listViewScrollBy, bool forceDraw, int32* indexPtr)
1704 {
1705 	int32 poseIndex = list->CountItems();
1706 
1707 	BRect poseBounds;
1708 	bool havePoseBounds = false;
1709 	bool addedItem = false;
1710 	bool needToDraw = true;
1711 
1712 	if (insertionSort && list->CountItems()) {
1713 		int32 orientation = BSearchList(list, pose, &poseIndex, 0);
1714 
1715 		if (orientation == kInsertAfter)
1716 			poseIndex++;
1717 
1718 		if (visibleList) {
1719 			// we only care about the positions if this is a visible list
1720 			poseBounds = CalcPoseRectList(pose, poseIndex);
1721 			havePoseBounds = true;
1722 
1723 			BRect srcRect(Extent());
1724 			srcRect.top = poseBounds.top;
1725 			srcRect = srcRect & viewBounds;
1726 			BRect destRect(srcRect);
1727 			destRect.OffsetBy(0, fListElemHeight);
1728 
1729 			// special case the addition of a pose that scrolls
1730 			// the extent into the view for the first time:
1731 			if (destRect.bottom > viewBounds.top
1732 				&& destRect.top > destRect.bottom) {
1733 				// make destRect valid
1734 				destRect.top = viewBounds.top;
1735 			}
1736 
1737 			if (srcRect.Intersects(viewBounds)
1738 				|| destRect.Intersects(viewBounds)) {
1739 				// The visual area is affected by the insertion.
1740 				// If items have been added above the visual area,
1741 				// delay the scrolling. srcRect.bottom holds the
1742 				// current Extent(). So if the bottom is still above
1743 				// the viewBounds top, it means the view is scrolled
1744 				// to show the area below the items that have already
1745 				// been added.
1746 				if (srcRect.top == viewBounds.top
1747 					&& srcRect.bottom >= viewBounds.top
1748 					&& poseIndex != 0) {
1749 					// if new pose above current view bounds, cache up
1750 					// the draw and do it later
1751 					listViewScrollBy += fListElemHeight;
1752 					needToDraw = false;
1753 				} else {
1754 					FinishPendingScroll(listViewScrollBy, viewBounds);
1755 					list->AddItem(pose, poseIndex);
1756 
1757 					fMimeTypeListIsDirty = true;
1758 					addedItem = true;
1759 					if (srcRect.IsValid()) {
1760 						CopyBits(srcRect, destRect);
1761 						srcRect.bottom = destRect.top;
1762 						SynchronousUpdate(srcRect);
1763 					} else {
1764 						SynchronousUpdate(destRect);
1765 					}
1766 					needToDraw = false;
1767 				}
1768 			}
1769 		}
1770 	}
1771 
1772 	if (!addedItem) {
1773 		list->AddItem(pose, poseIndex);
1774 		fMimeTypeListIsDirty = true;
1775 	}
1776 
1777 	if (visibleList && needToDraw && forceDraw) {
1778 		if (!havePoseBounds)
1779 			poseBounds = CalcPoseRectList(pose, poseIndex);
1780 		if (viewBounds.Intersects(poseBounds))
1781  			SynchronousUpdate(poseBounds);
1782 	}
1783 
1784 	if (indexPtr)
1785 		*indexPtr = poseIndex;
1786 }
1787 
1788 
1789 void
1790 BPoseView::CreatePoses(Model** models, PoseInfo* poseInfoArray, int32 count,
1791 	BPose** resultingPoses, bool insertionSort,	int32* lastPoseIndexPtr,
1792 	BRect* boundsPtr, bool forceDraw)
1793 {
1794 	// were we passed the bounds of the view?
1795 	BRect viewBounds;
1796 	if (boundsPtr)
1797 		viewBounds = *boundsPtr;
1798 	else
1799 		viewBounds = Bounds();
1800 
1801 	bool clipboardLocked = be_clipboard->Lock();
1802 
1803 	int32 poseIndex = 0;
1804 	uint32 clipboardMode = 0;
1805 	float listViewScrollBy = 0;
1806 	for (int32 modelIndex = 0; modelIndex < count; modelIndex++) {
1807 		Model* model = models[modelIndex];
1808 
1809 		// pose adopts model and deletes it when done
1810 		if (fInsertedNodes.find(*(model->NodeRef())) != fInsertedNodes.end()
1811 			|| FindZombie(model->NodeRef())) {
1812 			watch_node(model->NodeRef(), B_STOP_WATCHING, this);
1813 			delete model;
1814 			if (resultingPoses)
1815 				resultingPoses[modelIndex] = NULL;
1816 			continue;
1817 		} else
1818 			fInsertedNodes.insert(*(model->NodeRef()));
1819 
1820 		if ((clipboardMode = FSClipboardFindNodeMode(model, !clipboardLocked,
1821 				true)) != 0 && !HasPosesInClipboard()) {
1822 			SetHasPosesInClipboard(true);
1823 		}
1824 
1825 		model->OpenNode();
1826 		ASSERT(model->IsNodeOpen());
1827 		PoseInfo* poseInfo = &poseInfoArray[modelIndex];
1828 		BPose* pose = new BPose(model, this, clipboardMode);
1829 
1830 		if (resultingPoses)
1831 			resultingPoses[modelIndex] = pose;
1832 
1833 		// set location from poseinfo if saved loc was for this dir
1834 		if (poseInfo->fInitedDirectory != -1LL) {
1835 			PinPointToValidRange(poseInfo->fLocation);
1836 			pose->SetLocation(poseInfo->fLocation, this);
1837 			AddToVSList(pose);
1838 		}
1839 
1840 		BRect poseBounds;
1841 
1842 		switch (ViewMode()) {
1843 			case kListMode:
1844 			{
1845 				AddPoseToList(fPoseList, !fFiltering, insertionSort, pose,
1846 					viewBounds, listViewScrollBy, forceDraw, &poseIndex);
1847 
1848 				if (fFiltering && FilterPose(pose)) {
1849 					AddPoseToList(fFilteredPoseList, true, insertionSort, pose,
1850 						viewBounds, listViewScrollBy, forceDraw, &poseIndex);
1851 				}
1852 
1853 				break;
1854 			}
1855 
1856 			case kIconMode:
1857 			case kMiniIconMode:
1858 				if (poseInfo->fInitedDirectory == -1LL || fAlwaysAutoPlace) {
1859 					if (pose->HasLocation())
1860 						RemoveFromVSList(pose);
1861 
1862 					PlacePose(pose, viewBounds);
1863 
1864 					// we set a flag in the pose here to signify that we were
1865 					// auto placed - after adding all poses to window, we're
1866 					// going to go back and make sure that the auto placed poses
1867 					// don't overlap previously positioned icons. If so, we'll
1868 					// move them to new positions.
1869 					if (!fAlwaysAutoPlace)
1870 						pose->SetAutoPlaced(true);
1871 
1872 					AddToVSList(pose);
1873 				}
1874 
1875 				// add item to list and draw if necessary
1876 				fPoseList->AddItem(pose);
1877 				fMimeTypeListIsDirty = true;
1878 
1879 				poseBounds = pose->CalcRect(this);
1880 
1881 				if (fEnsurePosesVisible && !viewBounds.Intersects(poseBounds)) {
1882 					viewBounds.InsetBy(20, 20);
1883 					RemoveFromVSList(pose);
1884 					BPoint loc(pose->Location(this));
1885 					loc.ConstrainTo(viewBounds);
1886 					pose->SetLocation(loc, this);
1887 					pose->SetSaveLocation();
1888 					AddToVSList(pose);
1889 					poseBounds = pose->CalcRect(this);
1890 					viewBounds.InsetBy(-20, -20);
1891 				}
1892 
1893 	 			if (forceDraw && viewBounds.Intersects(poseBounds))
1894 					Invalidate(poseBounds);
1895 
1896 				// if this is the first item then we set extent here
1897 				if (!fExtent.IsValid())
1898 					fExtent = poseBounds;
1899 				else
1900 					AddToExtent(poseBounds);
1901 
1902 				break;
1903 		}
1904 		if (model->IsSymLink())
1905 			model->ResolveIfLink()->CloseNode();
1906 
1907 		model->CloseNode();
1908 	}
1909 
1910 	if (clipboardLocked)
1911 		be_clipboard->Unlock();
1912 
1913 	FinishPendingScroll(listViewScrollBy, viewBounds);
1914 
1915 	if (lastPoseIndexPtr)
1916 		*lastPoseIndexPtr = poseIndex;
1917 }
1918 
1919 
1920 bool
1921 BPoseView::PoseVisible(const Model* model, const PoseInfo* poseInfo)
1922 {
1923 	return !poseInfo->fInvisible;
1924 }
1925 
1926 
1927 bool
1928 BPoseView::ShouldShowPose(const Model* model, const PoseInfo* poseInfo)
1929 {
1930 	if (!PoseVisible(model, poseInfo))
1931 		return false;
1932 
1933 	// check filter before adding item
1934 	if (!fRefFilter)
1935 		return true;
1936 
1937 	struct stat_beos statBeOS;
1938 	convert_to_stat_beos(model->StatBuf(), &statBeOS);
1939 
1940 	return fRefFilter->Filter(model->EntryRef(), model->Node(), &statBeOS,
1941 		model->MimeType());
1942 }
1943 
1944 
1945 const char*
1946 BPoseView::MimeTypeAt(int32 index)
1947 {
1948 	if (fMimeTypeListIsDirty)
1949 		RefreshMimeTypeList();
1950 
1951 	return fMimeTypeList->ItemAt(index)->String();
1952 }
1953 
1954 
1955 int32
1956 BPoseView::CountMimeTypes()
1957 {
1958 	if (fMimeTypeListIsDirty)
1959 		RefreshMimeTypeList();
1960 
1961 	return fMimeTypeList->CountItems();
1962 }
1963 
1964 
1965 void
1966 BPoseView::AddMimeType(const char* mimeType)
1967 {
1968 	int32 count = fMimeTypeList->CountItems();
1969 	for (int32 index = 0; index < count; index++) {
1970 		if (*fMimeTypeList->ItemAt(index) == mimeType)
1971 			return;
1972 	}
1973 
1974 	fMimeTypeList->AddItem(new BString(mimeType));
1975 }
1976 
1977 
1978 void
1979 BPoseView::RefreshMimeTypeList()
1980 {
1981 	fMimeTypeList->MakeEmpty();
1982 	fMimeTypeListIsDirty = false;
1983 
1984 	for (int32 index = 0;; index++) {
1985 		BPose* pose = PoseAtIndex(index);
1986 		if (!pose)
1987 			break;
1988 
1989 		if (pose->TargetModel())
1990 			AddMimeType(pose->TargetModel()->MimeType());
1991 	}
1992 }
1993 
1994 
1995 void
1996 BPoseView::InsertPoseAfter(BPose* pose, int32* index, int32 orientation,
1997 	BRect* invalidRect)
1998 {
1999 	if (orientation == kInsertAfter) {
2000 		// TODO: get rid of this
2001 		(*index)++;
2002 	}
2003 
2004 	BRect bounds(Bounds());
2005 	// copy the good bits in the list
2006 	BRect srcRect(Extent());
2007 	srcRect.top = CalcPoseRectList(pose, *index).top;
2008 	srcRect = srcRect & bounds;
2009 	BRect destRect(srcRect);
2010 	destRect.OffsetBy(0, fListElemHeight);
2011 
2012 	if (srcRect.Intersects(bounds) || destRect.Intersects(bounds))
2013 		CopyBits(srcRect, destRect);
2014 
2015 	// this is the invalid rectangle
2016 	srcRect.bottom = destRect.top;
2017 	*invalidRect = srcRect;
2018 }
2019 
2020 
2021 void
2022 BPoseView::DisableScrollBars()
2023 {
2024 	if (fHScrollBar)
2025 		fHScrollBar->SetTarget((BView*)NULL);
2026 	if (fVScrollBar)
2027 		fVScrollBar->SetTarget((BView*)NULL);
2028 }
2029 
2030 
2031 void
2032 BPoseView::EnableScrollBars()
2033 {
2034 	if (fHScrollBar)
2035 		fHScrollBar->SetTarget(this);
2036 	if (fVScrollBar)
2037 		fVScrollBar->SetTarget(this);
2038 }
2039 
2040 
2041 void
2042 BPoseView::AddScrollBars()
2043 {
2044 	AutoLock<BWindow> lock(Window());
2045 	if (!lock)
2046 		return;
2047 
2048 	BRect bounds(Frame());
2049 
2050 	// horizontal
2051 	BRect rect(bounds);
2052 	rect.top = rect.bottom + 1;
2053 	rect.bottom = rect.top + (float)B_H_SCROLL_BAR_HEIGHT;
2054 	rect.right++;
2055 	fHScrollBar = new BHScrollBar(rect, "HScrollBar", this);
2056 	if (Parent())
2057 		Parent()->AddChild(fHScrollBar);
2058 	else
2059 		Window()->AddChild(fHScrollBar);
2060 
2061 	// vertical
2062 	rect = bounds;
2063 	rect.left = rect.right + 1;
2064 	rect.right = rect.left + (float)B_V_SCROLL_BAR_WIDTH;
2065 	rect.bottom++;
2066 	fVScrollBar = new BScrollBar(rect, "VScrollBar", this, 0, 100, B_VERTICAL);
2067 	if (Parent())
2068 		Parent()->AddChild(fVScrollBar);
2069 	else
2070 		Window()->AddChild(fVScrollBar);
2071 }
2072 
2073 
2074 void
2075 BPoseView::UpdateCount()
2076 {
2077 	if (fCountView)
2078 		fCountView->CheckCount();
2079 }
2080 
2081 
2082 void
2083 BPoseView::AddCountView()
2084 {
2085 	AutoLock<BWindow> lock(Window());
2086 	if (!lock)
2087 		return;
2088 
2089 	BRect rect(Frame());
2090 	rect.right = rect.left + kCountViewWidth;
2091 	rect.top = rect.bottom + 1;
2092 	rect.bottom = rect.top + (float)B_H_SCROLL_BAR_HEIGHT - 1;
2093 	fCountView = new BCountView(rect, this);
2094 	if (Parent())
2095 		Parent()->AddChild(fCountView);
2096 	else
2097 		Window()->AddChild(fCountView);
2098 
2099 	if (fHScrollBar) {
2100 		fHScrollBar->MoveBy(kCountViewWidth + 1, 0);
2101 		fHScrollBar->ResizeBy(-kCountViewWidth - 1, 0);
2102 	}
2103 }
2104 
2105 
2106 void
2107 BPoseView::MessageReceived(BMessage* message)
2108 {
2109 	if (message->WasDropped() && HandleMessageDropped(message))
2110 		return;
2111 
2112 	if (HandleScriptingMessage(message))
2113 		return;
2114 
2115 	switch (message->what) {
2116 		case kAddNewPoses:
2117 		{
2118 			AddPosesResult* currentPoses;
2119 			entry_ref ref;
2120 			message->FindPointer("currentPoses",
2121 				reinterpret_cast<void**>(&currentPoses));
2122 			message->FindRef("ref", &ref);
2123 
2124 			// check if CreatePoses should be called (abort if dir has been
2125 			// switched under normal circumstances, ignore in several special
2126 			// cases
2127 			if (AddPosesThreadValid(&ref)) {
2128 				CreatePoses(currentPoses->fModels, currentPoses->fPoseInfos,
2129 					currentPoses->fCount, NULL, true, 0, 0, true);
2130 				currentPoses->ReleaseModels();
2131 			}
2132 			delete currentPoses;
2133 			break;
2134 		}
2135 
2136 		case kAddPosesCompleted:
2137 			AddPosesCompleted();
2138 			break;
2139 
2140 		case kRestoreBackgroundImage:
2141 			ContainerWindow()->UpdateBackgroundImage();
2142 			break;
2143 
2144 		case B_META_MIME_CHANGED:
2145 			NoticeMetaMimeChanged(message);
2146 			break;
2147 
2148 		case B_NODE_MONITOR:
2149 		case B_QUERY_UPDATE:
2150 			if (!FSNotification(message))
2151 				pendingNodeMonitorCache.Add(message);
2152 			break;
2153 
2154 		case kIconMode: {
2155 			int32 size;
2156 			int32 scale;
2157 			if (message->FindInt32("size", &size) == B_OK) {
2158 				if (size != (int32)IconSizeInt())
2159 					fViewState->SetIconSize(size);
2160 			} else if (message->FindInt32("scale", &scale) == B_OK
2161 						&& fViewState->ViewMode() == kIconMode) {
2162 				if (scale == 0 && (int32)IconSizeInt() != 32) {
2163 					switch ((int32)IconSizeInt()) {
2164 						case 40:
2165 							fViewState->SetIconSize(32);
2166 							break;
2167 						case 48:
2168 							fViewState->SetIconSize(40);
2169 							break;
2170 						case 64:
2171 							fViewState->SetIconSize(48);
2172 							break;
2173 						case 96:
2174 							fViewState->SetIconSize(64);
2175 							break;
2176 						case 128:
2177 							fViewState->SetIconSize(96);
2178 							break;
2179 					}
2180 				} else if (scale == 1 && (int32)IconSizeInt() != 128) {
2181 					switch ((int32)IconSizeInt()) {
2182 						case 32:
2183 							fViewState->SetIconSize(40);
2184 							break;
2185 						case 40:
2186 							fViewState->SetIconSize(48);
2187 							break;
2188 						case 48:
2189 							fViewState->SetIconSize(64);
2190 							break;
2191 						case 64:
2192 							fViewState->SetIconSize(96);
2193 							break;
2194 						case 96:
2195 							fViewState->SetIconSize(128);
2196 							break;
2197 					}
2198 				}
2199 			} else {
2200 				int32 iconSize = fViewState->LastIconSize();
2201 				if (iconSize < 32 || iconSize > 128) {
2202 					// uninitialized last icon size?
2203 					iconSize = 32;
2204 				}
2205 				fViewState->SetIconSize(iconSize);
2206 			}
2207 		} // fall thru
2208 		case kListMode:
2209 		case kMiniIconMode:
2210 			SetViewMode(message->what);
2211 			break;
2212 
2213 		case kMsgMouseDragged:
2214 			MouseDragged(message);
2215 			break;
2216 
2217 		case kMsgMouseLongDown:
2218 			MouseLongDown(message);
2219 			break;
2220 
2221 		case B_MOUSE_IDLE:
2222 			MouseIdle(message);
2223 			break;
2224 
2225 		case B_SELECT_ALL:
2226 		{
2227 			// Select widget if there is an active one
2228 			BTextWidget* widget;
2229 			if (ActivePose() && ((widget = ActivePose()->ActiveWidget())) != 0)
2230 				widget->SelectAll(this);
2231 			else
2232 				SelectAll();
2233 			break;
2234 		}
2235 
2236 		case B_CUT:
2237 			FSClipboardAddPoses(TargetModel()->NodeRef(), fSelectionList, kMoveSelectionTo, true);
2238 			break;
2239 
2240 		case kCutMoreSelectionToClipboard:
2241 			FSClipboardAddPoses(TargetModel()->NodeRef(), fSelectionList, kMoveSelectionTo, false);
2242 			break;
2243 
2244 		case B_COPY:
2245 			FSClipboardAddPoses(TargetModel()->NodeRef(), fSelectionList, kCopySelectionTo, true);
2246 			break;
2247 
2248 		case kCopyMoreSelectionToClipboard:
2249 			FSClipboardAddPoses(TargetModel()->NodeRef(), fSelectionList, kCopySelectionTo, false);
2250 			break;
2251 
2252 		case B_PASTE:
2253 			FSClipboardPaste(TargetModel());
2254 			break;
2255 
2256 		case kPasteLinksFromClipboard:
2257 			FSClipboardPaste(TargetModel(), kCreateLink);
2258 			break;
2259 
2260 		case B_CANCEL:
2261 			if (FSClipboardHasRefs())
2262 				FSClipboardClear();
2263 			else if (fFiltering)
2264 				StopFiltering();
2265 			break;
2266 
2267 		case kCancelSelectionToClipboard:
2268 			FSClipboardRemovePoses(TargetModel()->NodeRef(),
2269 				(fSelectionList->CountItems() > 0
2270 					? fSelectionList : fPoseList));
2271 			break;
2272 
2273 		case kFSClipboardChanges:
2274 		{
2275 			node_ref node;
2276 			message->FindInt32("device", &node.device);
2277 			message->FindInt64("directory", &node.node);
2278 
2279 			if (*TargetModel()->NodeRef() == node)
2280 				UpdatePosesClipboardModeFromClipboard(message);
2281 			else if (message->FindBool("clearClipboard")
2282 				&& HasPosesInClipboard()) {
2283 				// just remove all poses from clipboard
2284 				SetHasPosesInClipboard(false);
2285 				SetPosesClipboardMode(0);
2286 			}
2287 			break;
2288 		}
2289 
2290 		case kInvertSelection:
2291 			InvertSelection();
2292 			break;
2293 
2294 		case kShowSelectionWindow:
2295 			ShowSelectionWindow();
2296 			break;
2297 
2298 		case kDuplicateSelection:
2299 			DuplicateSelection();
2300 			break;
2301 
2302 		case kOpenSelection:
2303 			OpenSelection();
2304 			break;
2305 
2306 		case kOpenSelectionWith:
2307 			OpenSelectionUsing();
2308 			break;
2309 
2310 		case kRestoreFromTrash:
2311 			RestoreSelectionFromTrash();
2312 			break;
2313 
2314 		case kDelete:
2315 			if (ContainerWindow()->IsTrash())
2316 				// if trash delete instantly
2317 				DeleteSelection(true, false);
2318 			else
2319 				DeleteSelection();
2320 			break;
2321 
2322 		case kMoveToTrash:
2323 		{
2324 			TrackerSettings settings;
2325 
2326 			if ((modifiers() & B_SHIFT_KEY) != 0 || !settings.MoveFilesToTrash())
2327 				DeleteSelection(true, settings.AskBeforeDeleteFile());
2328 			else
2329 				MoveSelectionToTrash();
2330 			break;
2331 		}
2332 
2333 		case kCleanupAll:
2334 			Cleanup(true);
2335 			break;
2336 
2337 		case kCleanup:
2338 			Cleanup();
2339 			break;
2340 
2341 		case kEditQuery:
2342 			EditQueries();
2343 			break;
2344 
2345 		case kRunAutomounterSettings:
2346 			be_app->PostMessage(message);
2347 			break;
2348 
2349 		case kNewEntryFromTemplate:
2350 			if (message->HasRef("refs_template"))
2351 				NewFileFromTemplate(message);
2352 			break;
2353 
2354 		case kNewFolder:
2355 			NewFolder(message);
2356 			break;
2357 
2358 		case kUnmountVolume:
2359 			UnmountSelectedVolumes();
2360 			break;
2361 
2362 		case kEmptyTrash:
2363 			FSEmptyTrash();
2364 			break;
2365 
2366 		case kGetInfo:
2367 			OpenInfoWindows();
2368 			break;
2369 
2370 		case kIdentifyEntry:
2371 		{
2372 			bool force;
2373 			if (message->FindBool("force", &force) != B_OK)
2374 				force = false;
2375 			IdentifySelection(force);
2376 			break;
2377 		}
2378 
2379 		case kEditItem:
2380 		{
2381 			if (ActivePose())
2382 				break;
2383 
2384 			BPose* pose = fSelectionList->FirstItem();
2385 			if (pose) {
2386 				pose->EditFirstWidget(BPoint(0, CurrentPoseList()->IndexOf(pose)
2387 					* fListElemHeight), this);
2388 			}
2389 			break;
2390 		}
2391 
2392 		case kOpenParentDir:
2393 			OpenParent();
2394 			break;
2395 
2396 		case kCopyAttributes:
2397 			if (be_clipboard->Lock()) {
2398 				be_clipboard->Clear();
2399 				BMessage* data = be_clipboard->Data();
2400 				if (data != NULL) {
2401 					// copy attributes to the clipboard
2402 					BMessage state;
2403 					SaveState(state);
2404 
2405 					BMallocIO stream;
2406 					ssize_t size;
2407 					if (state.Flatten(&stream, &size) == B_OK) {
2408 						data->AddData("application/tracker-columns", B_MIME_TYPE, stream.Buffer(), size);
2409 						be_clipboard->Commit();
2410 					}
2411 				}
2412 				be_clipboard->Unlock();
2413 			}
2414 			break;
2415 		case kPasteAttributes:
2416 			if (be_clipboard->Lock()) {
2417 				BMessage* data = be_clipboard->Data();
2418 				if (data != NULL) {
2419 					// find the attributes in the clipboard
2420 					const void* buffer;
2421 					ssize_t size;
2422 					if (data->FindData("application/tracker-columns", B_MIME_TYPE, &buffer, &size) == B_OK) {
2423 						BMessage state;
2424 						if (state.Unflatten((const char*)buffer) == B_OK) {
2425 							// remove all current columns (one always stays)
2426 							BColumn* old;
2427 							while ((old = ColumnAt(0)) != NULL) {
2428 								if (!RemoveColumn(old, false))
2429 									break;
2430 							}
2431 
2432 							// add new columns
2433 							for (int32 index = 0; ; index++) {
2434 								BColumn* column = BColumn::InstantiateFromMessage(state, index);
2435 								if (!column)
2436 									break;
2437 								AddColumn(column);
2438 							}
2439 
2440 							// remove the last old one
2441 							RemoveColumn(old, false);
2442 
2443 							// set sorting mode
2444 							BViewState* viewState = BViewState::InstantiateFromMessage(state);
2445 							if (viewState != NULL) {
2446 								SetPrimarySort(viewState->PrimarySort());
2447 								SetSecondarySort(viewState->SecondarySort());
2448 								SetReverseSort(viewState->ReverseSort());
2449 
2450 								SortPoses();
2451 								Invalidate();
2452 							}
2453 						}
2454 					}
2455 				}
2456 				be_clipboard->Unlock();
2457 			}
2458 			break;
2459 
2460 		case kArrangeBy:
2461 		{
2462 			uint32 attrHash;
2463 			if (message->FindInt32("attr_hash", (int32*)&attrHash) == B_OK) {
2464 				if (ColumnFor(attrHash) == NULL)
2465 					HandleAttrMenuItemSelected(message);
2466 
2467 				if (PrimarySort() == attrHash)
2468 					attrHash = 0;
2469 
2470 				SetPrimarySort(attrHash);
2471 				SetSecondarySort(0);
2472 				Cleanup(true);
2473 			}
2474 			break;
2475 		}
2476 		case kArrangeReverseOrder:
2477 			SetReverseSort(!fViewState->ReverseSort());
2478 			Cleanup(true);
2479 			break;
2480 
2481 		case kAttributeItem:
2482 			HandleAttrMenuItemSelected(message);
2483 			break;
2484 
2485 		case kAddPrinter:
2486 			be_app->PostMessage(message);
2487 			break;
2488 
2489 		case kMakeActivePrinter:
2490 			SetDefaultPrinter();
2491 			break;
2492 
2493 #if DEBUG
2494 		case kTestIconCache:
2495 			RunIconCacheTests();
2496 			break;
2497 
2498 		case 'dbug':
2499 		{
2500 			int32 count = fSelectionList->CountItems();
2501 			for (int32 index = 0; index < count; index++)
2502 				fSelectionList->ItemAt(index)->PrintToStream();
2503 
2504 			break;
2505 		}
2506 #ifdef CHECK_OPEN_MODEL_LEAKS
2507 		case 'dpfl':
2508 			DumpOpenModels(false);
2509 			break;
2510 
2511 		case 'dpfL':
2512 			DumpOpenModels(true);
2513 			break;
2514 #endif
2515 #endif
2516 
2517 		case kCheckTypeahead:
2518 		{
2519 			bigtime_t doubleClickSpeed;
2520 			get_click_speed(&doubleClickSpeed);
2521 			if (system_time() - fLastKeyTime > (doubleClickSpeed * 2)) {
2522 				fCountView->SetTypeAhead("");
2523 				delete fKeyRunner;
2524 				fKeyRunner = NULL;
2525 			}
2526 			break;
2527 		}
2528 
2529 		case B_OBSERVER_NOTICE_CHANGE:
2530 		{
2531 			int32 observerWhat;
2532 			if (message->FindInt32("be:observe_change_what", &observerWhat) == B_OK) {
2533 				switch (observerWhat) {
2534 					case kDateFormatChanged:
2535 						UpdateDateColumns(message);
2536 						break;
2537 
2538 					case kVolumesOnDesktopChanged:
2539 						AdaptToVolumeChange(message);
2540 						break;
2541 
2542 					case kDesktopIntegrationChanged:
2543 						AdaptToDesktopIntegrationChange(message);
2544 						break;
2545 
2546 					case kShowSelectionWhenInactiveChanged:
2547 					{
2548 						// Updating the settings here will propagate setting changed
2549 						// from Tracker to all open file panels as well
2550 						bool showSelection;
2551 						if (message->FindBool("ShowSelectionWhenInactive", &showSelection) == B_OK) {
2552 							fShowSelectionWhenInactive = showSelection;
2553 							TrackerSettings().SetShowSelectionWhenInactive(fShowSelectionWhenInactive);
2554 						}
2555 						Invalidate();
2556 						break;
2557 					}
2558 
2559 					case kTransparentSelectionChanged:
2560 					{
2561 						bool transparentSelection;
2562 						if (message->FindBool("TransparentSelection", &transparentSelection) == B_OK) {
2563 							fTransparentSelection = transparentSelection;
2564 							TrackerSettings().SetTransparentSelection(fTransparentSelection);
2565 						}
2566 						break;
2567 					}
2568 
2569 					case kSortFolderNamesFirstChanged:
2570 						if (ViewMode() == kListMode) {
2571 							TrackerSettings settings;
2572 							bool sortFolderNamesFirst;
2573 							if (message->FindBool("SortFolderNamesFirst", &sortFolderNamesFirst) == B_OK)
2574 								settings.SetSortFolderNamesFirst(sortFolderNamesFirst);
2575 
2576 							NameAttributeText::SetSortFolderNamesFirst(settings.SortFolderNamesFirst());
2577 							RealNameAttributeText::SetSortFolderNamesFirst(
2578 								settings.SortFolderNamesFirst());
2579 							SortPoses();
2580 							Invalidate();
2581 						}
2582 						break;
2583 
2584 					case kTypeAheadFilteringChanged:
2585 					{
2586 						TrackerSettings settings;
2587 						bool typeAheadFiltering;
2588 						if (message->FindBool("TypeAheadFiltering", &typeAheadFiltering) == B_OK)
2589 							settings.SetTypeAheadFiltering(typeAheadFiltering);
2590 
2591 						if (fFiltering && !typeAheadFiltering)
2592 							StopFiltering();
2593 						break;
2594 					}
2595 				}
2596 			}
2597 			break;
2598 		}
2599 
2600 		default:
2601 			_inherited::MessageReceived(message);
2602 			break;
2603 	}
2604 }
2605 
2606 
2607 bool
2608 BPoseView::RemoveColumn(BColumn* columnToRemove, bool runAlert)
2609 {
2610 	// make sure last column is not removed
2611 	if (CountColumns() == 1) {
2612 		if (runAlert) {
2613 			BAlert* alert = new BAlert("",
2614 				B_TRANSLATE("You must have at least one attribute showing."),
2615 				B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
2616 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2617 			alert->Go();
2618 		}
2619 
2620 		return false;
2621 	}
2622 
2623 	// column exists so remove it from list
2624 	int32 columnIndex = IndexOfColumn(columnToRemove);
2625 	float offset = columnToRemove->Offset();
2626 
2627 	int32 count = fPoseList->CountItems();
2628 	for (int32 index = 0; index < count; index++)
2629 		fPoseList->ItemAt(index)->RemoveWidget(this, columnToRemove);
2630 	fColumnList->RemoveItem(columnToRemove, false);
2631 	fTitleView->RemoveTitle(columnToRemove);
2632 
2633 	float attrWidth = columnToRemove->Width();
2634 	delete columnToRemove;
2635 
2636 	count = CountColumns();
2637 	for (int32 index = columnIndex; index < count; index++) {
2638 		BColumn* column = ColumnAt(index);
2639 		column->SetOffset(column->Offset() - (attrWidth + kTitleColumnExtraMargin));
2640 	}
2641 
2642 	BRect rect(Bounds());
2643 	rect.left = offset;
2644 	Invalidate(rect);
2645 
2646 	ContainerWindow()->MarkAttributeMenu();
2647 
2648 	if (IsWatchingDateFormatChange()) {
2649 		int32 columnCount = CountColumns();
2650 		bool anyDateAttributesLeft = false;
2651 
2652 		for (int32 i = 0; i<columnCount; i++) {
2653 			BColumn* col = ColumnAt(i);
2654 
2655 			if (col->AttrType() == B_TIME_TYPE)
2656 				anyDateAttributesLeft = true;
2657 
2658 			if (anyDateAttributesLeft)
2659 				break;
2660 		}
2661 
2662 		if (!anyDateAttributesLeft)
2663 			StopWatchDateFormatChange();
2664 	}
2665 
2666 	fStateNeedsSaving = true;
2667 
2668 	if (fFiltering) {
2669 		// the column we removed might just be the one that was used to filter
2670 		int32 count = fFilteredPoseList->CountItems();
2671 		for (int32 i = count - 1; i >= 0; i--) {
2672 			BPose* pose = fFilteredPoseList->ItemAt(i);
2673 			if (!FilterPose(pose))
2674 				RemoveFilteredPose(pose, i);
2675 		}
2676 	}
2677 
2678 	return true;
2679 }
2680 
2681 
2682 bool
2683 BPoseView::AddColumn(BColumn* newColumn, const BColumn* after)
2684 {
2685 	if (!after)
2686 		after = LastColumn();
2687 
2688 	// add new column after last column
2689 	float offset;
2690 	int32 afterColumnIndex;
2691 	if (after) {
2692 		offset = after->Offset() + after->Width() + kTitleColumnExtraMargin;
2693 		afterColumnIndex = IndexOfColumn(after);
2694 	} else {
2695 		offset = kColumnStart;
2696 		afterColumnIndex = CountColumns() - 1;
2697 	}
2698 
2699 	// add the new column
2700 	fColumnList->AddItem(newColumn, afterColumnIndex + 1);
2701 	fTitleView->AddTitle(newColumn);
2702 
2703 	BRect rect(Bounds());
2704 
2705 	// add widget for all visible poses
2706 	PoseList* poseList = CurrentPoseList();
2707 	int32 count = poseList->CountItems();
2708 	int32 startIndex = (int32)(rect.top / fListElemHeight);
2709 	BPoint loc(0, startIndex* fListElemHeight);
2710 
2711 	for (int32 index = startIndex; index < count; index++) {
2712 		BPose* pose = poseList->ItemAt(index);
2713 		if (!pose->WidgetFor(newColumn->AttrHash()))
2714 			pose->AddWidget(this, newColumn);
2715 
2716 		loc.y += fListElemHeight;
2717 		if (loc.y > rect.bottom)
2718 			break;
2719 	}
2720 
2721 	// rearrange column titles to fit new column
2722 	newColumn->SetOffset(offset);
2723 	float attrWidth = newColumn->Width();
2724 
2725 	count = CountColumns();
2726 	for (int32 index = afterColumnIndex + 2; index < count; index++) {
2727 		BColumn* column = ColumnAt(index);
2728 		ASSERT(newColumn != column);
2729 		column->SetOffset(column->Offset() + (attrWidth
2730 			+ kTitleColumnExtraMargin));
2731 	}
2732 
2733 	rect.left = offset;
2734 	Invalidate(rect);
2735 	ContainerWindow()->MarkAttributeMenu();
2736 
2737 	// Check if this is a time attribute and if so,
2738 	// start watching for changed in time/date format:
2739 	if (!IsWatchingDateFormatChange() && newColumn->AttrType() == B_TIME_TYPE)
2740 		StartWatchDateFormatChange();
2741 
2742 	fStateNeedsSaving =  true;
2743 
2744 	if (fFiltering) {
2745 		// the column we added might just add new poses to be showed
2746 		fFilteredPoseList->MakeEmpty();
2747 		fFiltering = false;
2748 		StartFiltering();
2749 	}
2750 
2751 	return true;
2752 }
2753 
2754 
2755 void
2756 BPoseView::HandleAttrMenuItemSelected(BMessage* message)
2757 {
2758 	// see if source was a menu item
2759 	BMenuItem* item;
2760 	if (message->FindPointer("source", (void**)&item) != B_OK)
2761 		item = NULL;
2762 
2763 	// find out which column was selected
2764 	uint32 attrHash;
2765 	if (message->FindInt32("attr_hash", (int32*)&attrHash) != B_OK)
2766 		return;
2767 
2768 	BColumn* column = ColumnFor(attrHash);
2769 	if (column) {
2770 		RemoveColumn(column, true);
2771 		return;
2772 	} else {
2773 		// collect info about selected attribute
2774 		const char* attrName;
2775 		if (message->FindString("attr_name", &attrName) != B_OK)
2776 			return;
2777 
2778 		uint32 attrType;
2779 		if (message->FindInt32("attr_type", (int32*)&attrType) != B_OK)
2780 			return;
2781 
2782 		float attrWidth;
2783 		if (message->FindFloat("attr_width", &attrWidth) != B_OK)
2784 			return;
2785 
2786 		alignment attrAlign;
2787 		if (message->FindInt32("attr_align", (int32*)&attrAlign) != B_OK)
2788 			return;
2789 
2790 		bool isEditable;
2791 		if (message->FindBool("attr_editable", &isEditable) != B_OK)
2792 			return;
2793 
2794 		bool isStatfield;
2795 		if (message->FindBool("attr_statfield", &isStatfield) != B_OK)
2796 			return;
2797 
2798 		const char* displayAs;
2799 		message->FindString("attr_display_as", &displayAs);
2800 
2801 		column = new BColumn(item->Label(), 0, attrWidth, attrAlign,
2802 			attrName, attrType, displayAs, isStatfield, isEditable);
2803 		AddColumn(column);
2804 		if (item->Menu()->Supermenu() == NULL)
2805 			delete item->Menu();
2806 	}
2807 }
2808 
2809 
2810 const int32 kSanePoseLocation = 50000;
2811 
2812 
2813 void
2814 BPoseView::ReadPoseInfo(Model* model, PoseInfo* poseInfo)
2815 {
2816 	BModelOpener opener(model);
2817 	if (!model->Node())
2818 		return;
2819 
2820 	ReadAttrResult result = kReadAttrFailed;
2821 	BEntry entry;
2822 	model->GetEntry(&entry);
2823 	bool isTrash = model->IsTrash() && IsDesktopView();
2824 
2825 	// special case the "root" disks icon
2826 	// as well as the trash on desktop
2827 	if (model->IsRoot() || isTrash) {
2828 		BDirectory dir;
2829 		if (FSGetDeskDir(&dir) == B_OK) {
2830 			const char* poseInfoAttr = isTrash ? kAttrTrashPoseInfo
2831 				: kAttrDisksPoseInfo;
2832 			const char* poseInfoAttrForeign = isTrash ? kAttrTrashPoseInfoForeign
2833 				: kAttrDisksPoseInfoForeign;
2834 			result = ReadAttr(&dir, poseInfoAttr, poseInfoAttrForeign,
2835 				B_RAW_TYPE, 0, poseInfo, sizeof(*poseInfo), &PoseInfo::EndianSwap);
2836 		}
2837 	} else {
2838 		ASSERT(model->IsNodeOpen());
2839 		time_t now = time(NULL);
2840 
2841 		for (int32 count = 10; count >= 0; count--) {
2842 			if (!model->Node())
2843 				break;
2844 
2845 			result = ReadAttr(model->Node(), kAttrPoseInfo, kAttrPoseInfoForeign,
2846 				B_RAW_TYPE, 0, poseInfo, sizeof(*poseInfo), &PoseInfo::EndianSwap);
2847 
2848 			if (result != kReadAttrFailed) {
2849 				// got it, bail
2850 				break;
2851 			}
2852 
2853 			// if we're in one of the icon modes and it's a newly created item
2854 			// then we're going to retry a few times to see if we can get some
2855 			// pose info to properly place the icon
2856 			if (ViewMode() == kListMode)
2857 				break;
2858 
2859 			const StatStruct* stat = model->StatBuf();
2860 			if (stat->st_crtime < now - 5 || stat->st_crtime > now)
2861 				break;
2862 
2863 			// PRINT(("retrying to read pose info for %s, %d\n", model->Name(), count));
2864 
2865 			snooze(10000);
2866 		}
2867 	}
2868 	if (result == kReadAttrFailed) {
2869 		poseInfo->fInitedDirectory = -1LL;
2870 		poseInfo->fInvisible = false;
2871 	} else if (!TargetModel()
2872 		|| (poseInfo->fInitedDirectory != model->EntryRef()->directory
2873 			&& (poseInfo->fInitedDirectory != TargetModel()->NodeRef()->node))) {
2874 		// info was read properly but it's not for this directory
2875 		poseInfo->fInitedDirectory = -1LL;
2876 	} else if (poseInfo->fLocation.x < -kSanePoseLocation
2877 		|| poseInfo->fLocation.x > kSanePoseLocation
2878 		|| poseInfo->fLocation.y < -kSanePoseLocation
2879 		|| poseInfo->fLocation.y > kSanePoseLocation) {
2880 		// location values not realistic, probably screwed up, force reset
2881 		poseInfo->fInitedDirectory = -1LL;
2882 	}
2883 }
2884 
2885 
2886 ExtendedPoseInfo*
2887 BPoseView::ReadExtendedPoseInfo(Model* model)
2888 {
2889 	BModelOpener opener(model);
2890 	if (!model->Node())
2891 		return NULL;
2892 
2893 	ReadAttrResult result = kReadAttrFailed;
2894 
2895 	const char* extendedPoseInfoAttrName;
2896 	const char* extendedPoseInfoAttrForeignName;
2897 
2898 	// special case the "root" disks icon
2899 	if (model->IsRoot()) {
2900 		BDirectory dir;
2901 		if (FSGetDeskDir(&dir) == B_OK) {
2902 			extendedPoseInfoAttrName = kAttrExtendedDisksPoseInfo;
2903 			extendedPoseInfoAttrForeignName = kAttrExtendedDisksPoseInfoForegin;
2904 		} else
2905 			return NULL;
2906 	} else {
2907 		extendedPoseInfoAttrName = kAttrExtendedPoseInfo;
2908 		extendedPoseInfoAttrForeignName = kAttrExtendedPoseInfoForegin;
2909 	}
2910 
2911 	type_code type;
2912 	size_t size;
2913 	result = GetAttrInfo(model->Node(), extendedPoseInfoAttrName,
2914 		extendedPoseInfoAttrForeignName, &type, &size);
2915 
2916 	if (result == kReadAttrFailed)
2917 		return NULL;
2918 
2919 	char* buffer = new char[ExtendedPoseInfo::SizeWithHeadroom(size)];
2920 	ExtendedPoseInfo* poseInfo = reinterpret_cast<ExtendedPoseInfo*>(buffer);
2921 
2922 	result = ReadAttr(model->Node(), extendedPoseInfoAttrName,
2923 		extendedPoseInfoAttrForeignName,
2924 		B_RAW_TYPE, 0, buffer, size, &ExtendedPoseInfo::EndianSwap);
2925 
2926 	// check that read worked, and data is sane
2927 	if (result == kReadAttrFailed
2928 		|| size > poseInfo->SizeWithHeadroom()
2929 		|| size < poseInfo->Size()) {
2930 		delete [] buffer;
2931 		return NULL;
2932 	}
2933 
2934 	return poseInfo;
2935 }
2936 
2937 
2938 void
2939 BPoseView::SetViewMode(uint32 newMode)
2940 {
2941 	uint32 oldMode = ViewMode();
2942 	uint32 lastIconSize = fViewState->LastIconSize();
2943 
2944 	if (newMode == oldMode) {
2945 		if (newMode != kIconMode || lastIconSize == fViewState->IconSize())
2946 			return;
2947 	}
2948 
2949 	ASSERT(!IsFilePanel());
2950 
2951 	uint32 lastIconMode = fViewState->LastIconMode();
2952 	if (newMode != kListMode) {
2953 		fViewState->SetLastIconMode(newMode);
2954 		if (oldMode == kIconMode)
2955 			fViewState->SetLastIconSize(fViewState->IconSize());
2956 	}
2957 
2958 	fViewState->SetViewMode(newMode);
2959 
2960 	// Try to lock the center of the pose view when scaling icons, but not
2961 	// if we are the desktop.
2962 	BPoint scaleOffset(0, 0);
2963 	bool iconSizeChanged = newMode == kIconMode && oldMode == kIconMode;
2964 	if (!IsDesktopWindow() && iconSizeChanged) {
2965 		// definitely changing the icon size, so we will need to scroll
2966 		BRect bounds(Bounds());
2967 		BPoint center(bounds.LeftTop());
2968 		center.x += bounds.Width() / 2.0;
2969 		center.y += bounds.Height() / 2.0;
2970 		// convert the center into "unscaled icon placement" space
2971 		float oldScale = lastIconSize / 32.0;
2972 		BPoint unscaledCenter(center.x / oldScale, center.y / oldScale);
2973 		// get the new center in "scaled icon placement" place
2974 		float newScale = fViewState->IconSize() / 32.0;
2975 		BPoint newCenter(unscaledCenter.x * newScale,
2976 			unscaledCenter.y * newScale);
2977 		scaleOffset = newCenter - center;
2978 	}
2979 
2980 	// toggle view layout between listmode and non-listmode, if necessary
2981 	BContainerWindow* window = ContainerWindow();
2982 	if (oldMode == kListMode) {
2983 		if (fFiltering)
2984 			ClearFilter();
2985 
2986 		fTitleView->RemoveSelf();
2987 
2988 		if (window)
2989 			window->HideAttributeMenu();
2990 
2991 		MoveBy(0, -(kTitleViewHeight + 1));
2992 		ResizeBy(0, kTitleViewHeight + 1);
2993 	} else if (newMode == kListMode) {
2994 		MoveBy(0, kTitleViewHeight + 1);
2995 		ResizeBy(0, -(kTitleViewHeight + 1));
2996 
2997 		if (window)
2998 			window->ShowAttributeMenu();
2999 
3000 		fTitleView->ResizeTo(Frame().Width(), fTitleView->Frame().Height());
3001 		fTitleView->MoveTo(Frame().left, Frame().top - (kTitleViewHeight + 1));
3002 		if (Parent())
3003 			Parent()->AddChild(fTitleView);
3004 		else
3005 			Window()->AddChild(fTitleView);
3006 	}
3007 
3008 	CommitActivePose();
3009 	SetIconPoseHeight();
3010 	GetLayoutInfo(newMode, &fGrid, &fOffset);
3011 
3012 	// see if we need to map icons into new mode
3013 	bool mapIcons = false;
3014 	if (fOkToMapIcons) {
3015 		mapIcons = (newMode != kListMode) && (newMode != lastIconMode
3016 			|| fViewState->IconSize() != lastIconSize);
3017 	}
3018 
3019 	// check if we need to re-place poses when they are out of view
3020 	bool checkLocations = IsDesktopWindow() && iconSizeChanged;
3021 
3022 	BPoint oldOffset;
3023 	BPoint oldGrid;
3024 	if (mapIcons)
3025 		GetLayoutInfo(lastIconMode, &oldGrid, &oldOffset);
3026 
3027 	BRect bounds(Bounds());
3028 	PoseList newPoseList(30);
3029 
3030 	if (newMode != kListMode) {
3031 		int32 count = fPoseList->CountItems();
3032 		for (int32 index = 0; index < count; index++) {
3033 			BPose* pose = fPoseList->ItemAt(index);
3034 			if (pose->HasLocation() == false) {
3035 				newPoseList.AddItem(pose);
3036 			} else if (checkLocations && !IsValidLocation(pose)) {
3037 				// this icon has a location, but needs to be remapped, because
3038 				// it is going out of view for example
3039 				RemoveFromVSList(pose);
3040 				newPoseList.AddItem(pose);
3041 			} else if (iconSizeChanged) {
3042 				// The pose location is still changed in view coordinates,
3043 				// so it needs to be changed anyways!
3044 				pose->SetSaveLocation();
3045 			} else if (mapIcons) {
3046 				MapToNewIconMode(pose, oldGrid, oldOffset);
3047 			}
3048 		}
3049 	}
3050 
3051 	// invalidate before anything else to avoid flickering, especially when
3052 	// scrolling is also performed (invalidating before scrolling will cause
3053 	// app_server to scroll silently, ie not visibly)
3054 	Invalidate();
3055 
3056 	// update origin in case of a list <-> icon mode transition
3057 	BPoint newOrigin;
3058 	if (newMode == kListMode)
3059 		newOrigin = fViewState->ListOrigin();
3060 	else
3061 		newOrigin = fViewState->IconOrigin() + scaleOffset;
3062 
3063 	PinPointToValidRange(newOrigin);
3064 
3065 	DisableScrollBars();
3066 	ScrollTo(newOrigin);
3067 
3068 	// reset hint and arrange poses which DO NOT have a location yet
3069 	ResetPosePlacementHint();
3070 	int32 count = newPoseList.CountItems();
3071 	for (int32 index = 0; index < count; index++) {
3072 		BPose* pose = newPoseList.ItemAt(index);
3073 		PlacePose(pose, bounds);
3074 		AddToVSList(pose);
3075 	}
3076 
3077 	SortPoses();
3078 	if (newMode != kListMode)
3079 		RecalcExtent();
3080 
3081 	UpdateScrollRange();
3082 	SetScrollBarsTo(newOrigin);
3083 	EnableScrollBars();
3084 	ContainerWindow()->ViewModeChanged(oldMode, newMode);
3085 }
3086 
3087 
3088 void
3089 BPoseView::MapToNewIconMode(BPose* pose, BPoint oldGrid, BPoint oldOffset)
3090 {
3091 	BPoint delta;
3092 	BPoint poseLoc;
3093 
3094 	poseLoc = PinToGrid(pose->Location(this), oldGrid, oldOffset);
3095 	delta = pose->Location(this) - poseLoc;
3096 	poseLoc -= oldOffset;
3097 
3098 	if (poseLoc.x >= 0)
3099 		poseLoc.x = floorf(poseLoc.x / oldGrid.x) * fGrid.x;
3100 	else
3101 		poseLoc.x = ceilf(poseLoc.x / oldGrid.x) * fGrid.x;
3102 
3103 	if (poseLoc.y >= 0)
3104 		poseLoc.y = floorf(poseLoc.y / oldGrid.y) * fGrid.y;
3105 	else
3106 		poseLoc.y = ceilf(poseLoc.y / oldGrid.y) * fGrid.y;
3107 
3108 	if ((delta.x != 0) || (delta.y != 0)) {
3109 		if (delta.x >= 0)
3110 			delta.x = fGrid.x * floorf(delta.x / oldGrid.x);
3111 		else
3112 			delta.x = fGrid.x * ceilf(delta.x / oldGrid.x);
3113 
3114 		if (delta.y >= 0)
3115 			delta.y = fGrid.y * floorf(delta.y / oldGrid.y);
3116 		else
3117 			delta.y = fGrid.y * ceilf(delta.y / oldGrid.y);
3118 
3119 		poseLoc += delta;
3120 	}
3121 
3122 	poseLoc += fOffset;
3123 	pose->SetLocation(poseLoc, this);
3124 	pose->SetSaveLocation();
3125 }
3126 
3127 
3128 void
3129 BPoseView::SetPosesClipboardMode(uint32 clipboardMode)
3130 {
3131 	if (ViewMode() == kListMode) {
3132 		PoseList* poseList = CurrentPoseList();
3133 		int32 count = poseList->CountItems();
3134 
3135 		BPoint loc(0,0);
3136 		for (int32 index = 0; index < count; index++) {
3137 			BPose* pose = poseList->ItemAt(index);
3138 			if (pose->ClipboardMode() != clipboardMode) {
3139 				pose->SetClipboardMode(clipboardMode);
3140 				Invalidate(pose->CalcRect(loc, this, false));
3141 			}
3142 			loc.y += fListElemHeight;
3143 		}
3144 	} else {
3145 		int32 count = fPoseList->CountItems();
3146 		for (int32 index = 0; index < count; index++) {
3147 			BPose* pose = fPoseList->ItemAt(index);
3148 			if (pose->ClipboardMode() != clipboardMode) {
3149 				pose->SetClipboardMode(clipboardMode);
3150 				BRect poseRect(pose->CalcRect(this));
3151 				Invalidate(poseRect);
3152 			}
3153 		}
3154 	}
3155 }
3156 
3157 
3158 void
3159 BPoseView::UpdatePosesClipboardModeFromClipboard(BMessage* clipboardReport)
3160 {
3161 	CommitActivePose();
3162 	fSelectionPivotPose = NULL;
3163 	fRealPivotPose = NULL;
3164 	bool fullInvalidateNeeded = false;
3165 
3166 	node_ref node;
3167 	clipboardReport->FindInt32("device", &node.device);
3168 	clipboardReport->FindInt64("directory", &node.node);
3169 
3170 	bool clearClipboard = false;
3171 	clipboardReport->FindBool("clearClipboard", &clearClipboard);
3172 
3173 	if (clearClipboard && fHasPosesInClipboard) {
3174 		// clear all poses
3175 		int32 count = fPoseList->CountItems();
3176 		for (int32 index = 0; index < count; index++) {
3177 			BPose* pose = fPoseList->ItemAt(index);
3178 			pose->Select(false);
3179 			pose->SetClipboardMode(0);
3180 		}
3181 		SetHasPosesInClipboard(false);
3182 		fullInvalidateNeeded = true;
3183 		fHasPosesInClipboard = false;
3184 	}
3185 
3186 	BRect bounds(Bounds());
3187 	BPoint loc(0, 0);
3188 	bool hasPosesInClipboard = false;
3189 	int32 foundNodeIndex = 0;
3190 
3191 	TClipboardNodeRef* clipNode = NULL;
3192 	ssize_t size;
3193 	for (int32 index = 0; clipboardReport->FindData("tcnode", T_CLIPBOARD_NODE, index,
3194 		(const void**)&clipNode, &size) == B_OK; index++) {
3195 		BPose* pose = fPoseList->FindPose(&clipNode->node, &foundNodeIndex);
3196 		if (pose == NULL)
3197 			continue;
3198 
3199 		if (clipNode->moveMode != pose->ClipboardMode() || pose->IsSelected()) {
3200 			pose->SetClipboardMode(clipNode->moveMode);
3201 			pose->Select(false);
3202 
3203 			if (!fullInvalidateNeeded) {
3204 				if (ViewMode() == kListMode) {
3205 					if (fFiltering) {
3206 						pose = fFilteredPoseList->FindPose(&clipNode->node,
3207 							&foundNodeIndex);
3208 					}
3209 
3210 					if (pose != NULL) {
3211 						loc.y = foundNodeIndex * fListElemHeight;
3212 						if (loc.y <= bounds.bottom && loc.y >= bounds.top)
3213 							Invalidate(pose->CalcRect(loc, this, false));
3214 					}
3215 				} else {
3216 					BRect poseRect(pose->CalcRect(this));
3217 					if (bounds.Contains(poseRect.LeftTop())
3218 						|| bounds.Contains(poseRect.LeftBottom())
3219 						|| bounds.Contains(poseRect.RightBottom())
3220 						|| bounds.Contains(poseRect.RightTop())) {
3221 						Invalidate(poseRect);
3222 					}
3223 				}
3224 			}
3225 			if (clipNode->moveMode)
3226 				hasPosesInClipboard = true;
3227 		}
3228 	}
3229 
3230 	fSelectionList->MakeEmpty();
3231 	fMimeTypesInSelectionCache.MakeEmpty();
3232 
3233 	SetHasPosesInClipboard(hasPosesInClipboard || fHasPosesInClipboard);
3234 
3235 	if (fullInvalidateNeeded)
3236 		Invalidate();
3237 }
3238 
3239 
3240 void
3241 BPoseView::PlaceFolder(const entry_ref* ref, const BMessage* message)
3242 {
3243 	BNode node(ref);
3244 	BPoint location;
3245 	bool setPosition = false;
3246 
3247 	if (message->FindPoint("be:invoke_origin", &location) == B_OK) {
3248 		// new folder created from popup, place on click point
3249 		setPosition = true;
3250 		location = ConvertFromScreen(location);
3251 	} else if (ViewMode() != kListMode) {
3252 		// new folder created by keyboard shortcut
3253 		uint32 buttons;
3254 		GetMouse(&location, &buttons);
3255 		BPoint globalLocation(location);
3256 		ConvertToScreen(&globalLocation);
3257 		// check if mouse over window
3258 		if (Window()->Frame().Contains(globalLocation))
3259 			// create folder under mouse
3260 			setPosition = true;
3261 	}
3262 
3263 	if (setPosition)
3264 		FSSetPoseLocation(TargetModel()->NodeRef()->node, &node,
3265 			location);
3266 }
3267 
3268 
3269 void
3270 BPoseView::NewFileFromTemplate(const BMessage* message)
3271 {
3272 	ASSERT(TargetModel());
3273 
3274 	entry_ref destEntryRef;
3275 	node_ref destNodeRef;
3276 
3277 	BDirectory destDir(TargetModel()->NodeRef());
3278 	if (destDir.InitCheck() != B_OK)
3279 		return;
3280 
3281 	// TODO: Localise this
3282 	char fileName[B_FILE_NAME_LENGTH] = "New ";
3283 	strlcat(fileName, message->FindString("name"), sizeof(fileName));
3284 	FSMakeOriginalName(fileName, &destDir, " copy");
3285 
3286 	entry_ref srcRef;
3287 	message->FindRef("refs_template", &srcRef);
3288 
3289 	BDirectory dir(&srcRef);
3290 
3291 	if (dir.InitCheck() == B_OK) {
3292 		// special handling of directories
3293 		if (FSCreateNewFolderIn(TargetModel()->NodeRef(), &destEntryRef, &destNodeRef) == B_OK) {
3294 			BEntry destEntry(&destEntryRef);
3295 			destEntry.Rename(fileName);
3296 		}
3297 	} else {
3298 		BFile srcFile(&srcRef, B_READ_ONLY);
3299 		BFile destFile(&destDir, fileName, B_READ_WRITE | B_CREATE_FILE);
3300 
3301 		// copy the data from the template file
3302 		char* buffer = new char[1024];
3303 		ssize_t result;
3304 		do {
3305 			result = srcFile.Read(buffer, 1024);
3306 
3307 			if (result > 0) {
3308 				ssize_t written = destFile.Write(buffer, (size_t)result);
3309 				if (written != result)
3310 					result = written < B_OK ? written : B_ERROR;
3311 			}
3312 		} while (result > 0);
3313 		delete[] buffer;
3314 	}
3315 
3316 	// todo: create an UndoItem
3317 
3318 	// copy the attributes from the template file
3319 	BNode srcNode(&srcRef);
3320 	BNode destNode(&destDir, fileName);
3321 	FSCopyAttributesAndStats(&srcNode, &destNode);
3322 
3323 	BEntry entry(&destDir, fileName);
3324 	entry.GetRef(&destEntryRef);
3325 
3326 	// try to place new item at click point or under mouse if possible
3327 	PlaceFolder(&destEntryRef, message);
3328 
3329 	// start renaming the entry
3330 	int32 index;
3331 	BPose* pose = EntryCreated(TargetModel()->NodeRef(), &destNodeRef,
3332 		destEntryRef.name, &index);
3333 
3334 	if (fFiltering) {
3335 		if (fFilteredPoseList->FindPose(&destNodeRef, &index) == NULL) {
3336 			float scrollBy = 0;
3337 			BRect bounds = Bounds();
3338 			AddPoseToList(fFilteredPoseList, true, true, pose, bounds, scrollBy,
3339 				true, &index);
3340 		}
3341 	}
3342 
3343 	if (pose) {
3344 		WatchNewNode(pose->TargetModel()->NodeRef());
3345 		UpdateScrollRange();
3346 		CommitActivePose();
3347 		SelectPose(pose, index);
3348 		pose->EditFirstWidget(BPoint(0, index * fListElemHeight), this);
3349 	}
3350 }
3351 
3352 
3353 void
3354 BPoseView::NewFolder(const BMessage* message)
3355 {
3356 	ASSERT(TargetModel());
3357 
3358 	entry_ref ref;
3359 	node_ref nodeRef;
3360 
3361 	if (FSCreateNewFolderIn(TargetModel()->NodeRef(), &ref, &nodeRef) == B_OK) {
3362 		// try to place new folder at click point or under mouse if possible
3363 
3364 		PlaceFolder(&ref, message);
3365 
3366 		int32 index;
3367 		BPose* pose = EntryCreated(TargetModel()->NodeRef(), &nodeRef, ref.name,
3368 			&index);
3369 
3370 		if (fFiltering) {
3371 			if (fFilteredPoseList->FindPose(&nodeRef, &index) == NULL) {
3372 				float scrollBy = 0;
3373 				BRect bounds = Bounds();
3374 				AddPoseToList(fFilteredPoseList, true, true, pose, bounds,
3375 					scrollBy, true, &index);
3376 			}
3377 		}
3378 
3379 		if (pose) {
3380 			UpdateScrollRange();
3381 			CommitActivePose();
3382 			SelectPose(pose, index);
3383 			pose->EditFirstWidget(BPoint(0, index * fListElemHeight), this);
3384 		}
3385 	}
3386 }
3387 
3388 
3389 void
3390 BPoseView::Cleanup(bool doAll)
3391 {
3392 	if (ViewMode() == kListMode)
3393 		return;
3394 
3395 	BContainerWindow* window = ContainerWindow();
3396 	if (!window)
3397 		return;
3398 
3399 	// replace all icons from the top
3400 	if (doAll) {
3401 		// sort by sort field
3402 		SortPoses();
3403 
3404 		DisableScrollBars();
3405 		ClearExtent();
3406 		ClearSelection();
3407 		ScrollTo(B_ORIGIN);
3408 		UpdateScrollRange();
3409 		SetScrollBarsTo(B_ORIGIN);
3410 		ResetPosePlacementHint();
3411 
3412 		BRect viewBounds(Bounds());
3413 
3414 		// relocate all poses in list (reset vs list)
3415 		fVSPoseList->MakeEmpty();
3416 		int32 count = fPoseList->CountItems();
3417 		for (int32 index = 0; index < count; index++) {
3418 			BPose* pose = fPoseList->ItemAt(index);
3419 			PlacePose(pose, viewBounds);
3420 			AddToVSList(pose);
3421 		}
3422 
3423 		RecalcExtent();
3424 
3425 		// scroll icons into view so that leftmost icon is "fOffset" from left
3426 		UpdateScrollRange();
3427 		EnableScrollBars();
3428 
3429 		if (HScrollBar()) {
3430 			float min;
3431 			float max;
3432 			HScrollBar()->GetRange(&min, &max);
3433 			HScrollBar()->SetValue(min);
3434 		}
3435 
3436 		UpdateScrollRange();
3437 		Invalidate(viewBounds);
3438 
3439 	} else {
3440 		// clean up items to nearest locations
3441 		BRect viewBounds(Bounds());
3442 		int32 count = fPoseList->CountItems();
3443 		for (int32 index = 0; index < count; index++) {
3444 			BPose* pose = fPoseList->ItemAt(index);
3445 			BPoint location(pose->Location(this));
3446 			BPoint newLocation(PinToGrid(location, fGrid, fOffset));
3447 
3448 			bool intersectsDesktopElements = !IsValidLocation(pose);
3449 
3450 			// do we need to move pose to a grid location?
3451 			if (newLocation != location || intersectsDesktopElements) {
3452 				// remove pose from VSlist so it doesn't "bump" into itself
3453 				RemoveFromVSList(pose);
3454 
3455 				// try new grid location
3456 				BRect oldBounds(pose->CalcRect(this));
3457 				BRect poseBounds(oldBounds);
3458 				pose->MoveTo(newLocation, this);
3459 				if (SlotOccupied(oldBounds, viewBounds)
3460 					|| intersectsDesktopElements) {
3461 					ResetPosePlacementHint();
3462 					PlacePose(pose, viewBounds);
3463 					poseBounds = pose->CalcRect(this);
3464 				}
3465 
3466 				AddToVSList(pose);
3467 				AddToExtent(poseBounds);
3468 
3469  				if (viewBounds.Intersects(poseBounds))
3470 					Invalidate(poseBounds);
3471  				if (viewBounds.Intersects(oldBounds))
3472 					Invalidate(oldBounds);
3473 			}
3474 		}
3475 	}
3476 }
3477 
3478 
3479 void
3480 BPoseView::PlacePose(BPose* pose, BRect &viewBounds)
3481 {
3482 	// move pose to probable location
3483 	pose->SetLocation(fHintLocation, this);
3484 	BRect rect(pose->CalcRect(this));
3485 	BPoint deltaFromBounds(fHintLocation - rect.LeftTop());
3486 
3487 	// make pose rect a little bigger to ensure space between poses
3488 	rect.InsetBy(-3, 0);
3489 
3490 	bool checkValidLocation = IsDesktopWindow();
3491 
3492 	// find an empty slot to put pose into
3493 	while (SlotOccupied(rect, viewBounds)
3494 		// check good location on the desktop
3495 		|| (checkValidLocation && !IsValidLocation(rect))) {
3496 		NextSlot(pose, rect, viewBounds);
3497 		// we've scanned the entire desktop without finding an available position,
3498 		// give up and simply place it towards the top left.
3499 		if (checkValidLocation && !rect.Intersects(viewBounds)) {
3500 			fHintLocation = PinToGrid(BPoint(0.0, 0.0), fGrid, fOffset);
3501 			pose->SetLocation(fHintLocation, this);
3502 			rect = pose->CalcRect(this);
3503 			break;
3504 		}
3505 	}
3506 
3507 	rect.InsetBy(3, 0);
3508 
3509 	fHintLocation = pose->Location(this) + BPoint(fGrid.x, 0);
3510 
3511 	pose->SetLocation(rect.LeftTop() + deltaFromBounds, this);
3512 	pose->SetSaveLocation();
3513 }
3514 
3515 
3516 bool
3517 BPoseView::IsValidLocation(const BPose* pose)
3518 {
3519 	if (!IsDesktopWindow())
3520 		return true;
3521 
3522 	BRect rect(pose->CalcRect(this));
3523 	rect.InsetBy(-3, 0);
3524 	return IsValidLocation(rect);
3525 }
3526 
3527 
3528 bool
3529 BPoseView::IsValidLocation(const BRect& rect)
3530 {
3531 	if (!IsDesktopWindow())
3532 		return true;
3533 
3534 	// on the desktop, don't allow icons outside of the view bounds
3535 	if (!Bounds().Contains(rect))
3536 		return false;
3537 
3538 	// also check the deskbar frame
3539 	BRect deskbarFrame;
3540 	if (GetDeskbarFrame(&deskbarFrame) == B_OK) {
3541 		deskbarFrame.InsetBy(-10, -10);
3542 		if (deskbarFrame.Intersects(rect))
3543 			return false;
3544 	}
3545 
3546 	// check replicants
3547 	for (int32 i = 0; BView* child = ChildAt(i); i++) {
3548 		BRect childFrame = child->Frame();
3549 		childFrame.InsetBy(-5, -5);
3550 		if (childFrame.Intersects(rect))
3551 			return false;
3552 	}
3553 
3554 	// location is ok
3555 	return true;
3556 }
3557 
3558 
3559 status_t
3560 BPoseView::GetDeskbarFrame(BRect* frame)
3561 {
3562 	// only really check the Deskbar frame every half a second,
3563 	// use a cached value otherwise
3564 	status_t ret = B_OK;
3565 	bigtime_t now = system_time();
3566 	if (fLastDeskbarFrameCheckTime + 500000 < now) {
3567 		// it's time to check the Deskbar frame again
3568 		ret = get_deskbar_frame(&fDeskbarFrame);
3569 		fLastDeskbarFrameCheckTime = now;
3570 	}
3571 	*frame = fDeskbarFrame;
3572 	return ret;
3573 }
3574 
3575 
3576 void
3577 BPoseView::CheckAutoPlacedPoses()
3578 {
3579 	if (ViewMode() == kListMode)
3580 		return;
3581 
3582 	BRect viewBounds(Bounds());
3583 
3584 	int32 count = fPoseList->CountItems();
3585 	for (int32 index = 0; index < count; index++) {
3586 		BPose* pose = fPoseList->ItemAt(index);
3587 		if (pose->WasAutoPlaced()) {
3588 			RemoveFromVSList(pose);
3589 			fHintLocation = pose->Location(this);
3590 			BRect oldBounds(pose->CalcRect(this));
3591 			PlacePose(pose, viewBounds);
3592 
3593 			BRect newBounds(pose->CalcRect(this));
3594 			AddToVSList(pose);
3595 			pose->SetAutoPlaced(false);
3596 			AddToExtent(newBounds);
3597 
3598 			Invalidate(oldBounds);
3599 			Invalidate(newBounds);
3600 		}
3601 	}
3602 }
3603 
3604 
3605 void
3606 BPoseView::CheckPoseVisibility(BRect* newFrame)
3607 {
3608 	bool desktop = IsDesktopWindow() && newFrame != 0;
3609 
3610 	BRect deskFrame;
3611 	if (desktop) {
3612 		ASSERT(newFrame);
3613 		deskFrame = *newFrame;
3614 	}
3615 
3616 	ASSERT(ViewMode() != kListMode);
3617 
3618 	BRect bounds(Bounds());
3619 	bounds.InsetBy(20, 20);
3620 
3621 	int32 count = fPoseList->CountItems();
3622 	for (int32 index = 0; index < count; index++) {
3623 		BPose* pose = fPoseList->ItemAt(index);
3624 		BPoint newLocation(pose->Location(this));
3625 		bool locationNeedsUpdating = false;
3626 
3627 		if (desktop) {
3628 			// we just switched screen resolution, pick up the right
3629 			// icon locations for the new resolution
3630 			Model* model = pose->TargetModel();
3631 			ExtendedPoseInfo* info = ReadExtendedPoseInfo(model);
3632 			if (info && info->HasLocationForFrame(deskFrame)) {
3633 				BPoint locationForFrame = info->LocationForFrame(deskFrame);
3634 				if (locationForFrame != newLocation) {
3635 					// found one and it is different from the current
3636 					newLocation = locationForFrame;
3637 					locationNeedsUpdating = true;
3638 					Invalidate(pose->CalcRect(this));
3639 						// make sure the old icon gets erased
3640 					RemoveFromVSList(pose);
3641 					pose->SetLocation(newLocation, this);
3642 						// set the new location
3643 				}
3644 			}
3645 			delete [] (char*)info;
3646 				// TODO: fix up this mess
3647 		}
3648 
3649 		BRect rect(pose->CalcRect(this));
3650 		if (!rect.Intersects(bounds)) {
3651 			// pose doesn't fit on screen
3652 			if (!locationNeedsUpdating) {
3653 				// didn't already invalidate and remove in the desktop case
3654 				Invalidate(rect);
3655 				RemoveFromVSList(pose);
3656 			}
3657 			BPoint loc(pose->Location(this));
3658 			loc.ConstrainTo(bounds);
3659 				// place it onscreen
3660 
3661 			pose->SetLocation(loc, this);
3662 				// set the new location
3663 			locationNeedsUpdating = true;
3664 		}
3665 
3666 		if (locationNeedsUpdating) {
3667 			// pose got reposition by one or both of the above
3668 			pose->SetSaveLocation();
3669 			AddToVSList(pose);
3670 				// add it at the new location
3671 			Invalidate(pose->CalcRect(this));
3672 				// make sure the new pose location updates properly
3673 		}
3674 	}
3675 }
3676 
3677 
3678 bool
3679 BPoseView::SlotOccupied(BRect poseRect, BRect viewBounds) const
3680 {
3681 	if (fVSPoseList->IsEmpty())
3682 		return false;
3683 
3684 	// ## be sure to keep this code in sync with calls to NextSlot
3685 	// ## in terms of the comparison of fHintLocation and PinToGrid
3686 	if (poseRect.right >= viewBounds.right) {
3687 		BPoint point(viewBounds.left + fOffset.x, 0);
3688 		point = PinToGrid(point, fGrid, fOffset);
3689 		if (fHintLocation.x != point.x)
3690 			return true;
3691 	}
3692 
3693 	// search only nearby poses (vertically)
3694 	int32 index = FirstIndexAtOrBelow((int32)(poseRect.top - IconPoseHeight()));
3695 	int32 numPoses = fVSPoseList->CountItems();
3696 
3697 	while (index < numPoses && fVSPoseList->ItemAt(index)->Location(this).y
3698 		< poseRect.bottom) {
3699 
3700 		BRect rect(fVSPoseList->ItemAt(index)->CalcRect(this));
3701 		if (poseRect.Intersects(rect))
3702 			return true;
3703 
3704 		index++;
3705 	}
3706 
3707 	return false;
3708 }
3709 
3710 
3711 void
3712 BPoseView::NextSlot(BPose* pose, BRect &poseRect, BRect viewBounds)
3713 {
3714 	// move to next slot
3715 	poseRect.OffsetBy(fGrid.x, 0);
3716 
3717 	// if we reached the end of row go down to next row
3718 	if (poseRect.right > viewBounds.right) {
3719 		fHintLocation.y += fGrid.y;
3720 		fHintLocation.x = viewBounds.left + fOffset.x;
3721 		fHintLocation = PinToGrid(fHintLocation, fGrid, fOffset);
3722 		pose->SetLocation(fHintLocation, this);
3723 		poseRect = pose->CalcRect(this);
3724 		poseRect.InsetBy(-3, 0);
3725 	}
3726 }
3727 
3728 
3729 int32
3730 BPoseView::FirstIndexAtOrBelow(int32 y, bool constrainIndex) const
3731 {
3732 // This method performs a binary search on the vertically sorted pose list
3733 // and returns either the index of the first pose at a given y location or
3734 // the proper index to insert a new pose into the list.
3735 
3736 	int32 index = 0;
3737 	int32 l = 0;
3738 	int32 r = fVSPoseList->CountItems() - 1;
3739 
3740 	while (l <= r) {
3741 		index = (l + r) >> 1;
3742 		int32 result = (int32)(y - fVSPoseList->ItemAt(index)->Location(this).y);
3743 
3744 		if (result < 0)
3745 			r = index - 1;
3746 		else if (result > 0)
3747 			l = index + 1;
3748 		else {
3749 			// compare turned out equal, find first pose
3750 			while (index > 0
3751 				&& y == fVSPoseList->ItemAt(index - 1)->Location(this).y)
3752 				index--;
3753 			return index;
3754 		}
3755 	}
3756 
3757 	// didn't find pose AT location y - bump index to proper insert point
3758 	while (index < fVSPoseList->CountItems()
3759 		&& fVSPoseList->ItemAt(index)->Location(this).y <= y)
3760 			index++;
3761 
3762 	// if flag is true then constrain index to legal value since this
3763 	// method returns the proper insertion point which could be outside
3764 	// the current bounds of the list
3765 	if (constrainIndex && index >= fVSPoseList->CountItems())
3766 		index = fVSPoseList->CountItems() - 1;
3767 
3768 	return index;
3769 }
3770 
3771 
3772 void
3773 BPoseView::AddToVSList(BPose* pose)
3774 {
3775 	int32 index = FirstIndexAtOrBelow((int32)pose->Location(this).y, false);
3776 	fVSPoseList->AddItem(pose, index);
3777 }
3778 
3779 
3780 int32
3781 BPoseView::RemoveFromVSList(const BPose* pose)
3782 {
3783 	//int32 index = FirstIndexAtOrBelow((int32)pose->Location(this).y);
3784 		// This optimisation is buggy and the index returned can be greater
3785 		// than the actual index of the pose we search, thus missing it
3786 		// and failing to remove it. This having severe implications
3787 		// everywhere in the code as it is asserted that it must be always
3788 		// in sync with fPoseList. See ticket #4322.
3789 	int32 index = 0;
3790 
3791 	int32 count = fVSPoseList->CountItems();
3792 	for (; index < count; index++) {
3793 		BPose* matchingPose = fVSPoseList->ItemAt(index);
3794 		ASSERT(matchingPose);
3795 		if (!matchingPose)
3796 			return -1;
3797 
3798 		if (pose == matchingPose) {
3799 			fVSPoseList->RemoveItemAt(index);
3800 			return index;
3801 		}
3802 	}
3803 
3804 	return -1;
3805 }
3806 
3807 
3808 BPoint
3809 BPoseView::PinToGrid(BPoint point, BPoint grid, BPoint offset) const
3810 {
3811 	if (grid.x == 0 || grid.y == 0)
3812 		return point;
3813 
3814 	point -= offset;
3815 	BPoint	gridLoc(point);
3816 
3817 	if (point.x >= 0)
3818 		gridLoc.x = floorf((point.x / grid.x) + 0.5f) * grid.x;
3819 	else
3820 		gridLoc.x = ceilf((point.x / grid.x) - 0.5f) * grid.x;
3821 
3822 	if (point.y >= 0)
3823 		gridLoc.y = floorf((point.y / grid.y) + 0.5f) * grid.y;
3824 	else
3825 		gridLoc.y = ceilf((point.y / grid.y) - 0.5f) * grid.y;
3826 
3827 	gridLoc += offset;
3828 	return gridLoc;
3829 }
3830 
3831 
3832 void
3833 BPoseView::ResetPosePlacementHint()
3834 {
3835 	fHintLocation = PinToGrid(BPoint(LeftTop().x + fOffset.x,
3836 		LeftTop().y + fOffset.y), fGrid, fOffset);
3837 }
3838 
3839 
3840 void
3841 BPoseView::SelectPoses(int32 start, int32 end)
3842 {
3843 	// clear selection list
3844 	fSelectionList->MakeEmpty();
3845 	fMimeTypesInSelectionCache.MakeEmpty();
3846 	fSelectionPivotPose = NULL;
3847 	fRealPivotPose = NULL;
3848 
3849 	bool iconMode = ViewMode() != kListMode;
3850 	BPoint loc(0, start * fListElemHeight);
3851 	BRect bounds(Bounds());
3852 
3853 	PoseList* poseList = CurrentPoseList();
3854 	int32 count = poseList->CountItems();
3855 	for (int32 index = start; index < end && index < count; index++) {
3856 		BPose* pose = poseList->ItemAt(index);
3857 		fSelectionList->AddItem(pose);
3858 		if (index == start)
3859 			fSelectionPivotPose = pose;
3860 		if (!pose->IsSelected()) {
3861 			pose->Select(true);
3862 			BRect poseRect;
3863 			if (iconMode)
3864 				poseRect = pose->CalcRect(this);
3865 			else
3866 				poseRect = pose->CalcRect(loc, this, false);
3867 
3868 			if (bounds.Intersects(poseRect)) {
3869 				Invalidate(poseRect);
3870 			}
3871 		}
3872 
3873 		loc.y += fListElemHeight;
3874 	}
3875 }
3876 
3877 
3878 void
3879 BPoseView::ScrollIntoView(BPose* pose, int32 index)
3880 {
3881 	ScrollIntoView(CalcPoseRect(pose, index, true));
3882 }
3883 
3884 
3885 void
3886 BPoseView::ScrollIntoView(BRect poseRect)
3887 {
3888 	if (IsDesktopWindow())
3889 		return;
3890 
3891 	if (ViewMode() == kListMode) {
3892 		// if we're in list view then we only care that the entire
3893 		// pose is visible vertically, not horizontally
3894 		poseRect.left = 0;
3895 		poseRect.right = poseRect.left + 1;
3896 	}
3897 
3898 	if (!Bounds().Contains(poseRect))
3899 		SetScrollBarsTo(poseRect.LeftTop());
3900 }
3901 
3902 
3903 void
3904 BPoseView::SelectPose(BPose* pose, int32 index, bool scrollIntoView)
3905 {
3906 	if (!pose || fSelectionList->CountItems() > 1 || !pose->IsSelected())
3907 		ClearSelection();
3908 
3909 	AddPoseToSelection(pose, index, scrollIntoView);
3910 }
3911 
3912 
3913 void
3914 BPoseView::AddPoseToSelection(BPose* pose, int32 index, bool scrollIntoView)
3915 {
3916 	// TODO: need to check if pose is member of selection list
3917 	if (pose && !pose->IsSelected()) {
3918 		pose->Select(true);
3919 		fSelectionList->AddItem(pose);
3920 
3921 		BRect poseRect = CalcPoseRect(pose, index);
3922 		Invalidate(poseRect);
3923 
3924 		if (scrollIntoView)
3925 			ScrollIntoView(poseRect);
3926 
3927 		if (fSelectionChangedHook)
3928 			ContainerWindow()->SelectionChanged();
3929 	}
3930 }
3931 
3932 
3933 void
3934 BPoseView::RemovePoseFromSelection(BPose* pose)
3935 {
3936 	if (fSelectionPivotPose == pose)
3937 		fSelectionPivotPose = NULL;
3938 	if (fRealPivotPose == pose)
3939 		fRealPivotPose = NULL;
3940 
3941 	if (!fSelectionList->RemoveItem(pose))
3942 		// wasn't selected to begin with
3943 		return;
3944 
3945 	pose->Select(false);
3946 	if (ViewMode() == kListMode) {
3947 		// TODO: need a simple call to CalcRect that works both in listView and
3948 		// icon view modes without the need for an index/pos
3949 		PoseList* poseList = CurrentPoseList();
3950 		int32 count = poseList->CountItems();
3951 		BPoint loc(0, 0);
3952 		for (int32 index = 0; index < count; index++) {
3953 			if (pose == poseList->ItemAt(index)) {
3954 				Invalidate(pose->CalcRect(loc, this));
3955 				break;
3956 			}
3957 			loc.y += fListElemHeight;
3958 		}
3959 	} else
3960 		Invalidate(pose->CalcRect(this));
3961 
3962 	if (fSelectionChangedHook)
3963 		ContainerWindow()->SelectionChanged();
3964 }
3965 
3966 
3967 bool
3968 BPoseView::EachItemInDraggedSelection(const BMessage* message,
3969 	bool (*func)(BPose*, BPoseView*, void*), BPoseView* poseView, void* passThru)
3970 {
3971 	BContainerWindow* srcWindow;
3972 	message->FindPointer("src_window", (void**)&srcWindow);
3973 
3974 	AutoLock<BWindow> lock(srcWindow);
3975 	if (!lock)
3976 		return false;
3977 
3978 	PoseList* selectionList = srcWindow->PoseView()->SelectionList();
3979 	int32 count = selectionList->CountItems();
3980 
3981 	for (int32 index = 0; index < count; index++) {
3982 		BPose* pose = selectionList->ItemAt(index);
3983 		if (func(pose, poseView, passThru))
3984 			// early iteration termination
3985 			return true;
3986 	}
3987 	return false;
3988 }
3989 
3990 
3991 static bool
3992 ContainsOne(BString* string, const char* matchString)
3993 {
3994 	return strcmp(string->String(), matchString) == 0;
3995 }
3996 
3997 
3998 bool
3999 BPoseView::FindDragNDropAction(const BMessage* dragMessage, bool &canCopy,
4000 	bool &canMove, bool &canLink, bool &canErase)
4001 {
4002 	canCopy = false;
4003 	canMove = false;
4004 	canErase = false;
4005 	canLink = false;
4006 	if (!dragMessage->HasInt32("be:actions"))
4007 		return false;
4008 
4009 	int32 action;
4010 	for (int32 index = 0;
4011 			dragMessage->FindInt32("be:actions", index, &action) == B_OK; index++) {
4012 		switch (action) {
4013 			case B_MOVE_TARGET:
4014 				canMove = true;
4015 				break;
4016 
4017 			case B_COPY_TARGET:
4018 				canCopy = true;
4019 				break;
4020 
4021 			case B_TRASH_TARGET:
4022 				canErase = true;
4023 				break;
4024 
4025 			case B_LINK_TARGET:
4026 				canLink = true;
4027 				break;
4028 		}
4029 	}
4030 	return canCopy || canMove || canErase || canLink;
4031 }
4032 
4033 
4034 bool
4035 BPoseView::CanTrashForeignDrag(const Model* targetModel)
4036 {
4037 	return targetModel->IsTrash();
4038 }
4039 
4040 
4041 bool
4042 BPoseView::CanCopyOrMoveForeignDrag(const Model* targetModel,
4043 	const BMessage* dragMessage)
4044 {
4045 	if (!targetModel->IsDirectory())
4046 		return false;
4047 
4048 	// in order to handle a clipping file, the drag initiator must be able
4049 	// do deal with B_FILE_MIME_TYPE
4050 	for (int32 index = 0; ; index++) {
4051 		const char* type;
4052 		if (dragMessage->FindString("be:types", index, &type) != B_OK)
4053 			break;
4054 
4055 		if (strcasecmp(type, B_FILE_MIME_TYPE) == 0)
4056 			return true;
4057 	}
4058 
4059 	return false;
4060 }
4061 
4062 
4063 bool
4064 BPoseView::CanHandleDragSelection(const Model* target, const BMessage* dragMessage,
4065 	bool ignoreTypes)
4066 {
4067 	if (ignoreTypes)
4068 		return target->IsDropTarget();
4069 
4070 	ASSERT(dragMessage);
4071 
4072 	BContainerWindow* srcWindow;
4073 	dragMessage->FindPointer("src_window", (void**)&srcWindow);
4074 	if (!srcWindow) {
4075 		// handle a foreign drag
4076 		bool canCopy;
4077 		bool canMove;
4078 		bool canErase;
4079 		bool canLink;
4080 		FindDragNDropAction(dragMessage, canCopy, canMove, canLink, canErase);
4081 		if (canErase && CanTrashForeignDrag(target))
4082 			return true;
4083 
4084 		if (canCopy || canMove) {
4085 			if (CanCopyOrMoveForeignDrag(target, dragMessage))
4086 				return true;
4087 
4088 			// TODO: collect all mime types here and pass into
4089 			// target->IsDropTargetForList(mimeTypeList);
4090 		}
4091 
4092 		// handle an old style entry_refs only darg message
4093 		if (dragMessage->HasRef("refs") && target->IsDirectory())
4094 			return true;
4095 
4096 		// handle simple text clipping drag&drop message
4097 		if (dragMessage->HasData(kPlainTextMimeType, B_MIME_TYPE) && target->IsDirectory())
4098 			return true;
4099 
4100 		// handle simple bitmap clipping drag&drop message
4101 		if (target->IsDirectory()
4102 			&& (dragMessage->HasData(kBitmapMimeType, B_MESSAGE_TYPE)
4103 				|| dragMessage->HasData(kLargeIconType, B_MESSAGE_TYPE)
4104 				|| dragMessage->HasData(kMiniIconType, B_MESSAGE_TYPE)))
4105 			return true;
4106 
4107 		// TODO: check for a drag message full of refs, feed a list of their
4108 		// types to target->IsDropTargetForList(mimeTypeList);
4109 		return false;
4110 	}
4111 
4112 	AutoLock<BWindow> lock(srcWindow);
4113 	if (!lock)
4114 		return false;
4115 	BObjectList<BString>* mimeTypeList = srcWindow->PoseView()->MimeTypesInSelection();
4116 	if (mimeTypeList->IsEmpty()) {
4117 		PoseList* selectionList = srcWindow->PoseView()->SelectionList();
4118 		if (!selectionList->IsEmpty()) {
4119 			// no cached data yet, build the cache
4120 			int32 count = selectionList->CountItems();
4121 
4122 			for (int32 index = 0; index < count; index++) {
4123 				// get the mime type of the model, following a possible symlink
4124 				BEntry entry(selectionList->ItemAt(index)->TargetModel()->EntryRef(), true);
4125 				if (entry.InitCheck() != B_OK)
4126 					continue;
4127 
4128  				BFile file(&entry, O_RDONLY);
4129 				BNodeInfo mime(&file);
4130 
4131 				if (mime.InitCheck() != B_OK)
4132 					continue;
4133 
4134 				char mimeType[B_MIME_TYPE_LENGTH];
4135 				mime.GetType(mimeType);
4136 
4137 				// add unique type string
4138 				if (!WhileEachListItem(mimeTypeList, ContainsOne, (const char*)mimeType)) {
4139 					BString* newMimeString = new BString(mimeType);
4140 					mimeTypeList->AddItem(newMimeString);
4141 				}
4142 			}
4143 		}
4144 	}
4145 
4146 	return target->IsDropTargetForList(mimeTypeList);
4147 }
4148 
4149 
4150 void
4151 BPoseView::TrySettingPoseLocation(BNode* node, BPoint point)
4152 {
4153 	if (ViewMode() == kListMode)
4154 		return;
4155 
4156 	if (modifiers() & B_COMMAND_KEY)
4157 		// allign to grid if needed
4158 		point = PinToGrid(point, fGrid, fOffset);
4159 
4160 	if (FSSetPoseLocation(TargetModel()->NodeRef()->node, node, point) == B_OK)
4161 		// get rid of opposite endianness attribute
4162 		node->RemoveAttr(kAttrPoseInfoForeign);
4163 }
4164 
4165 
4166 status_t
4167 BPoseView::CreateClippingFile(BPoseView* poseView, BFile &result, char* resultingName,
4168 	BDirectory* dir, BMessage* message, const char* fallbackName,
4169 	bool setLocation, BPoint dropPoint)
4170 {
4171 	// build a file name
4172 	// try picking it up from the message
4173 	const char* suggestedName;
4174 	if (message && message->FindString("be:clip_name", &suggestedName) == B_OK)
4175 		strncpy(resultingName, suggestedName, B_FILE_NAME_LENGTH - 1);
4176 	else
4177 		strcpy(resultingName, fallbackName);
4178 
4179 	FSMakeOriginalName(resultingName, dir, "");
4180 
4181 	// create a clipping file
4182 	status_t error = dir->CreateFile(resultingName, &result, true);
4183 	if (error != B_OK)
4184 		return error;
4185 
4186 	if (setLocation && poseView)
4187 		poseView->TrySettingPoseLocation(&result, dropPoint);
4188 
4189 	return B_OK;
4190 }
4191 
4192 
4193 static int32
4194 RunMimeTypeDestinationMenu(const char* actionText, const BObjectList<BString>* types,
4195 	const BObjectList<BString>* specificItems, BPoint where)
4196 {
4197 	int32 count;
4198 
4199 	if (types)
4200 		count = types->CountItems();
4201 	else
4202 		count = specificItems->CountItems();
4203 
4204 	if (!count)
4205 		return 0;
4206 
4207 	BPopUpMenu* menu = new BPopUpMenu("create clipping");
4208 	menu->SetFont(be_plain_font);
4209 
4210 	for (int32 index = 0; index < count; index++) {
4211 
4212 		const char* embedTypeAs = NULL;
4213 		char buffer[256];
4214 		if (types) {
4215 			types->ItemAt(index)->String();
4216 			BMimeType mimeType(embedTypeAs);
4217 
4218 			if (mimeType.GetShortDescription(buffer) == B_OK)
4219 				embedTypeAs = buffer;
4220 		}
4221 
4222 		BString description;
4223 		if (specificItems->ItemAt(index)->Length()) {
4224 			description << (const BString &)(*specificItems->ItemAt(index));
4225 
4226 			if (embedTypeAs)
4227 				description << " (" << embedTypeAs << ")";
4228 
4229 		} else if (types)
4230 			description = embedTypeAs;
4231 
4232 		BString labelText;
4233 		if (actionText) {
4234 			int32 length = 1024 - 1 - (int32)strlen(actionText);
4235 			if (length > 0) {
4236 				description.Truncate(length);
4237 				labelText.SetTo(actionText);
4238 				labelText.ReplaceFirst("%s", description.String());
4239 			} else
4240 				labelText.SetTo(B_TRANSLATE("label too long"));
4241 		} else
4242 			labelText = description;
4243 
4244 		menu->AddItem(new BMenuItem(labelText.String(), 0));
4245 	}
4246 
4247 	menu->AddSeparatorItem();
4248 	menu->AddItem(new BMenuItem(B_TRANSLATE("Cancel"), 0));
4249 
4250 	int32 result = -1;
4251 	BMenuItem* resultingItem = menu->Go(where, false, true);
4252 	if (resultingItem) {
4253 		int32 index = menu->IndexOf(resultingItem);
4254 		if (index < count)
4255 			result = index;
4256 	}
4257 
4258 	delete menu;
4259 
4260 	return result;
4261 }
4262 
4263 
4264 bool
4265 BPoseView::HandleMessageDropped(BMessage* message)
4266 {
4267 	ASSERT(message->WasDropped());
4268 
4269 	// reset system cursor in case it was altered by drag and drop
4270 	SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
4271 	fCursorCheck = false;
4272 
4273 	if (!fDropEnabled)
4274 		return false;
4275 
4276 	if (!dynamic_cast<BContainerWindow*>(Window()))
4277 		return false;
4278 
4279  	if (message->HasData("RGBColor", 'RGBC')) {
4280  		// do not handle roColor-style drops here, pass them on to the desktop
4281  		if (dynamic_cast<BDeskWindow*>(Window()))
4282  			BMessenger((BHandler*)Window()).SendMessage(message);
4283 
4284 		return true;
4285  	}
4286 
4287 	if (fDropTarget && !DragSelectionContains(fDropTarget, message))
4288 		HiliteDropTarget(false);
4289 
4290 	fDropTarget = NULL;
4291 
4292 	ASSERT(TargetModel());
4293 	BPoint offset;
4294 	BPoint dropPt(message->DropPoint(&offset));
4295 	ConvertFromScreen(&dropPt);
4296 
4297 	// tenatively figure out the pose we dropped the file onto
4298 	int32 index;
4299 	BPose* targetPose = FindPose(dropPt, &index);
4300 	Model tmpTarget;
4301 	Model* targetModel = NULL;
4302 	if (targetPose) {
4303 		targetModel = targetPose->TargetModel();
4304 		if (targetModel->IsSymLink()
4305 			&& tmpTarget.SetTo(targetPose->TargetModel()->EntryRef(), true, true) == B_OK)
4306 			targetModel = &tmpTarget;
4307 	}
4308 
4309 	return HandleDropCommon(message, targetModel, targetPose, this, dropPt);
4310 }
4311 
4312 
4313 bool
4314 BPoseView::HandleDropCommon(BMessage* message, Model* targetModel, BPose* targetPose,
4315 	BView* view, BPoint dropPt)
4316 {
4317 	uint32 buttons = (uint32)message->FindInt32("buttons");
4318 
4319 	BContainerWindow* containerWindow = NULL;
4320 	BPoseView* poseView = dynamic_cast<BPoseView*>(view);
4321 	if (poseView)
4322 		containerWindow = poseView->ContainerWindow();
4323 
4324 	// look for srcWindow to determine whether drag was initiated in tracker
4325 	BContainerWindow* srcWindow = NULL;
4326 	message->FindPointer("src_window", (void**)&srcWindow);
4327 
4328 	if (!srcWindow) {
4329 		// drag was from another app
4330 
4331 		if (targetModel == NULL)
4332 			targetModel = poseView->TargetModel();
4333 
4334 		// figure out if we dropped a file onto a directory and set the targetDirectory
4335 		// to it, else set it to this pose view
4336 		BDirectory targetDirectory;
4337 		if (targetModel && targetModel->IsDirectory())
4338 			targetDirectory.SetTo(targetModel->EntryRef());
4339 
4340 		if (targetModel->IsRoot())
4341 			// don't drop anyting into the root disk
4342 			return false;
4343 
4344 		bool canCopy;
4345 		bool canMove;
4346 		bool canErase;
4347 		bool canLink;
4348 		if (FindDragNDropAction(message, canCopy, canMove, canLink, canErase)) {
4349 			// new D&D protocol
4350 			// what action can the drag initiator do?
4351 			if (canErase && CanTrashForeignDrag(targetModel)) {
4352 				BMessage reply(B_TRASH_TARGET);
4353 				message->SendReply(&reply);
4354 				return true;
4355 			}
4356 
4357 			if ((canCopy || canMove)
4358 				&& CanCopyOrMoveForeignDrag(targetModel, message)) {
4359 				// handle the promise style drag&drop
4360 
4361 				// fish for specification of specialized menu items
4362 				BObjectList<BString> actionSpecifiers(10, true);
4363 				for (int32 index = 0; ; index++) {
4364 					const char* string;
4365 					if (message->FindString("be:actionspecifier", index, &string) != B_OK)
4366 						break;
4367 
4368 					ASSERT(string);
4369 					actionSpecifiers.AddItem(new BString(string));
4370 				}
4371 
4372 				// build the list of types the drag originator offers
4373 				BObjectList<BString> types(10, true);
4374 				BObjectList<BString> typeNames(10, true);
4375 				for (int32 index = 0; ; index++) {
4376 					const char* string;
4377 					if (message->FindString("be:filetypes", index, &string) != B_OK)
4378 						break;
4379 
4380 					ASSERT(string);
4381 					types.AddItem(new BString(string));
4382 
4383 					const char* typeName = "";
4384 					message->FindString("be:type_descriptions", index, &typeName);
4385 					typeNames.AddItem(new BString(typeName));
4386 				}
4387 
4388 				int32 specificTypeIndex = -1;
4389 				int32 specificActionIndex = -1;
4390 
4391 				// if control down, run a popup menu
4392 				if (canCopy
4393 					&& ((modifiers() & B_CONTROL_KEY) || (buttons & B_SECONDARY_MOUSE_BUTTON))) {
4394 
4395 					if (actionSpecifiers.CountItems() > 0) {
4396 						specificActionIndex = RunMimeTypeDestinationMenu(NULL,
4397 							NULL, &actionSpecifiers, view->ConvertToScreen(dropPt));
4398 
4399 						if (specificActionIndex == -1)
4400 							return false;
4401 					} else if (types.CountItems() > 0) {
4402 						specificTypeIndex = RunMimeTypeDestinationMenu(
4403 							B_TRANSLATE("Create %s clipping"),
4404 							&types, &typeNames, view->ConvertToScreen(dropPt));
4405 
4406 						if (specificTypeIndex == -1)
4407 							return false;
4408 					}
4409 				}
4410 
4411 				char name[B_FILE_NAME_LENGTH];
4412 				BFile file;
4413 				if (CreateClippingFile(poseView, file, name, &targetDirectory, message,
4414 					B_TRANSLATE("Untitled clipping"),
4415 					!targetPose, dropPt) != B_OK)
4416 					return false;
4417 
4418 				// here is a file for the drag initiator, it is up to it now to stuff it
4419 				// with the goods
4420 
4421 				// build the reply message
4422 				BMessage reply(canCopy ? B_COPY_TARGET : B_MOVE_TARGET);
4423 				reply.AddString("be:types", B_FILE_MIME_TYPE);
4424 				if (specificTypeIndex != -1) {
4425 					// we had the user pick a specific type from a menu, use it
4426 					reply.AddString("be:filetypes",
4427 						types.ItemAt(specificTypeIndex)->String());
4428 
4429 					if (typeNames.ItemAt(specificTypeIndex)->Length())
4430 						reply.AddString("be:type_descriptions",
4431 							typeNames.ItemAt(specificTypeIndex)->String());
4432 				}
4433 
4434 				if (specificActionIndex != -1)
4435 					// we had the user pick a specific type from a menu, use it
4436 					reply.AddString("be:actionspecifier",
4437 						actionSpecifiers.ItemAt(specificActionIndex)->String());
4438 
4439 
4440 				reply.AddRef("directory", targetModel->EntryRef());
4441 				reply.AddString("name", name);
4442 
4443 				// Attach any data the originator may have tagged on
4444 				BMessage data;
4445 				if (message->FindMessage("be:originator-data", &data) == B_OK)
4446 					reply.AddMessage("be:originator-data", &data);
4447 
4448 				// copy over all the file types the drag initiator claimed to
4449 				// support
4450 				for (int32 index = 0; ; index++) {
4451 					const char* type;
4452 					if (message->FindString("be:filetypes", index, &type) != B_OK)
4453 						break;
4454 					reply.AddString("be:filetypes", type);
4455 				}
4456 
4457 				message->SendReply(&reply);
4458 				return true;
4459 			}
4460 		}
4461 
4462 		if (message->HasRef("refs")) {
4463 			// TODO: decide here on copy, move or create symlink
4464 			// look for specific command or bring up popup
4465 			// Unify this with local drag&drop
4466 
4467 			if (!targetModel->IsDirectory())
4468 				// bail if we are not a directory
4469 				return false;
4470 
4471 			bool canRelativeLink = false;
4472 			if (!canCopy && !canMove && !canLink && containerWindow) {
4473 				if (((buttons & B_SECONDARY_MOUSE_BUTTON)
4474 					|| (modifiers() & B_CONTROL_KEY))) {
4475 					switch (containerWindow->ShowDropContextMenu(dropPt)) {
4476 						case kCreateRelativeLink:
4477 							canRelativeLink = true;
4478 							break;
4479 						case kCreateLink:
4480 							canLink = true;
4481 							break;
4482 						case kMoveSelectionTo:
4483 							canMove = true;
4484 							break;
4485 						case kCopySelectionTo:
4486 							canCopy = true;
4487 							break;
4488 						case kCancelButton:
4489 						default:
4490 							// user canceled context menu
4491 							return true;
4492 					}
4493 				} else
4494 					canCopy = true;
4495 			}
4496 
4497 			uint32 moveMode;
4498 			if (canCopy)
4499 				moveMode = kCopySelectionTo;
4500 			else if (canMove)
4501 				moveMode = kMoveSelectionTo;
4502 			else if (canLink)
4503 				moveMode = kCreateLink;
4504 			else if (canRelativeLink)
4505 				moveMode = kCreateRelativeLink;
4506 			else {
4507 				TRESPASS();
4508 				return true;
4509 			}
4510 
4511 			// handle refs by performing a copy
4512 			BObjectList<entry_ref>* entryList = new BObjectList<entry_ref>(10, true);
4513 
4514 			for (int32 index = 0; ; index++) {
4515 				// copy all enclosed refs into a list
4516 				entry_ref ref;
4517 				if (message->FindRef("refs", index, &ref) != B_OK)
4518 					break;
4519 				entryList->AddItem(new entry_ref(ref));
4520 			}
4521 
4522 			int32 count = entryList->CountItems();
4523 			if (count) {
4524 				BList* pointList = 0;
4525 				if (poseView && !targetPose) {
4526 					// calculate a pointList to make the icons land were we dropped them
4527 					pointList = new BList(count);
4528 					// force the the icons to lay out in 5 columns
4529 					for (int32 index = 0; count; index++) {
4530 						for (int32 j = 0; count && j < 4; j++, count--) {
4531 							BPoint point(dropPt + BPoint(j * poseView->fGrid.x, index *
4532 								poseView->fGrid.y));
4533 							pointList->AddItem(new BPoint(poseView->PinToGrid(point,
4534 								poseView->fGrid, poseView->fOffset)));
4535 						}
4536 					}
4537 				}
4538 
4539 				// perform asynchronous copy
4540 				FSMoveToFolder(entryList, new BEntry(targetModel->EntryRef()),
4541 					moveMode, pointList);
4542 
4543 				return true;
4544 			}
4545 
4546 			// nothing to copy, list doesn't get consumed
4547 			delete entryList;
4548 			return true;
4549 		}
4550 		if (message->HasData(kPlainTextMimeType, B_MIME_TYPE)) {
4551 			// text dropped, make into a clipping file
4552 			if (!targetModel->IsDirectory())
4553 				// bail if we are not a directory
4554 				return false;
4555 
4556 			// find the text
4557 			ssize_t textLength;
4558 			const char* text;
4559 			if (message->FindData(kPlainTextMimeType, B_MIME_TYPE, (const void**)&text,
4560 				&textLength) != B_OK)
4561 				return false;
4562 
4563 			char name[B_FILE_NAME_LENGTH];
4564 
4565 			BFile file;
4566 			if (CreateClippingFile(poseView, file, name, &targetDirectory, message,
4567 					B_TRANSLATE("Untitled clipping"),
4568 					!targetPose, dropPt) != B_OK)
4569 				return false;
4570 
4571 			// write out the file
4572 			if (file.Seek(0, SEEK_SET) == B_ERROR
4573 				|| file.Write(text, (size_t)textLength) < 0
4574 				|| file.SetSize(textLength) != B_OK) {
4575 				// failed to write file, remove file and bail
4576 				file.Unset();
4577 				BEntry entry(&targetDirectory, name);
4578 				entry.Remove();
4579 				PRINT(("error writing text into file %s\n", name));
4580 			}
4581 
4582 			// pick up TextView styles if available and save them with the file
4583 			const text_run_array* textRuns = NULL;
4584 			ssize_t dataSize = 0;
4585 			if (message->FindData("application/x-vnd.Be-text_run_array", B_MIME_TYPE,
4586 				(const void**)&textRuns, &dataSize) == B_OK && textRuns && dataSize) {
4587 				// save styles the same way StyledEdit does
4588 				int32 tmpSize = dataSize;
4589 				void* data = BTextView::FlattenRunArray(textRuns, &tmpSize);
4590 				file.WriteAttr("styles", B_RAW_TYPE, 0, data, (size_t)tmpSize);
4591 				free(data);
4592 			}
4593 
4594 			// mark as a clipping file
4595 			int32 tmp;
4596 			file.WriteAttr(kAttrClippingFile, B_RAW_TYPE, 0, &tmp, sizeof(int32));
4597 
4598 			// set the file type
4599 			BNodeInfo info(&file);
4600 			info.SetType(kPlainTextMimeType);
4601 
4602 			return true;
4603 		}
4604 		if (message->HasData(kBitmapMimeType, B_MESSAGE_TYPE)
4605 			|| message->HasData(kLargeIconType, B_MESSAGE_TYPE)
4606 			|| message->HasData(kMiniIconType, B_MESSAGE_TYPE)) {
4607 			// bitmap, make into a clipping file
4608 			if (!targetModel->IsDirectory())
4609 				// bail if we are not a directory
4610 				return false;
4611 
4612 			BMessage embeddedBitmap;
4613 			if (message->FindMessage(kBitmapMimeType, &embeddedBitmap) != B_OK
4614 				&& message->FindMessage(kLargeIconType, &embeddedBitmap) != B_OK
4615 				&& message->FindMessage(kMiniIconType, &embeddedBitmap) != B_OK)
4616 				return false;
4617 
4618 			char name[B_FILE_NAME_LENGTH];
4619 
4620 			BFile file;
4621 			if (CreateClippingFile(poseView, file, name, &targetDirectory, message,
4622 				B_TRANSLATE("Untitled bitmap"), !targetPose, dropPt) != B_OK)
4623 				return false;
4624 
4625 			int32 size = embeddedBitmap.FlattenedSize();
4626 			if (size > 1024*1024)
4627 				// bail if too large
4628 				return false;
4629 
4630 			char* buffer = new char [size];
4631 			embeddedBitmap.Flatten(buffer, size);
4632 
4633 			// write out the file
4634 			if (file.Seek(0, SEEK_SET) == B_ERROR
4635 				|| file.Write(buffer, (size_t)size) < 0
4636 				|| file.SetSize(size) != B_OK) {
4637 				// failed to write file, remove file and bail
4638 				file.Unset();
4639 				BEntry entry(&targetDirectory, name);
4640 				entry.Remove();
4641 				PRINT(("error writing bitmap into file %s\n", name));
4642 			}
4643 
4644 			// mark as a clipping file
4645 			int32 tmp;
4646 			file.WriteAttr(kAttrClippingFile, B_RAW_TYPE, 0, &tmp, sizeof(int32));
4647 
4648 			// set the file type
4649 			BNodeInfo info(&file);
4650 			info.SetType(kBitmapMimeType);
4651 
4652 			return true;
4653 		}
4654 		return false;
4655 	}
4656 
4657 	if (srcWindow == containerWindow) {
4658 		// drag started in this window
4659 		containerWindow->Activate();
4660 		containerWindow->UpdateIfNeeded();
4661 		poseView->ResetPosePlacementHint();
4662 	}
4663 
4664 	if (srcWindow == containerWindow && DragSelectionContains(targetPose,
4665 		message)) {
4666 		// drop on self
4667 		targetModel = NULL;
4668 	}
4669 
4670 	bool wasHandled = false;
4671 	bool ignoreTypes = (modifiers() & B_CONTROL_KEY) != 0;
4672 
4673 	if (targetModel && containerWindow != NULL) {
4674 		// TODO: pick files to drop/launch on a case by case basis
4675 		if (targetModel->IsDirectory()) {
4676 			MoveSelectionInto(targetModel, srcWindow, containerWindow, buttons, dropPt,
4677 				false);
4678 			wasHandled = true;
4679 		} else if (CanHandleDragSelection(targetModel, message, ignoreTypes)) {
4680 			LaunchAppWithSelection(targetModel, message, !ignoreTypes);
4681 			wasHandled = true;
4682 		}
4683 	}
4684 
4685 	if (poseView && !wasHandled) {
4686 		BPoint clickPt = message->FindPoint("click_pt");
4687 		// TODO: removed check for root here need to do that, possibly at a
4688 		// different level
4689 		poseView->MoveSelectionTo(dropPt, clickPt, srcWindow);
4690 	}
4691 
4692 	if (poseView && poseView->fEnsurePosesVisible)
4693 		poseView->CheckPoseVisibility();
4694 
4695 	return true;
4696 }
4697 
4698 
4699 struct LaunchParams {
4700 	Model* app;
4701 	bool checkTypes;
4702 	BMessage* refsMessage;
4703 };
4704 
4705 
4706 static bool
4707 AddOneToLaunchMessage(BPose* pose, BPoseView*, void* castToParams)
4708 {
4709 	LaunchParams* params = (LaunchParams*)castToParams;
4710 
4711 	ASSERT(pose->TargetModel());
4712 	if (params->app->IsDropTarget(params->checkTypes ? pose->TargetModel() : 0, true))
4713 		params->refsMessage->AddRef("refs", pose->TargetModel()->EntryRef());
4714 
4715 	return false;
4716 }
4717 
4718 
4719 void
4720 BPoseView::LaunchAppWithSelection(Model* appModel, const BMessage* dragMessage,
4721 	bool checkTypes)
4722 {
4723 	// launch items from the current selection with <appModel>; only pass the same
4724 	// files that we previously decided can be handled by <appModel>
4725 	BMessage refs(B_REFS_RECEIVED);
4726 	LaunchParams params;
4727 	params.app = appModel;
4728 	params.checkTypes = checkTypes;
4729 	params.refsMessage = &refs;
4730 
4731 	// add Tracker token so that refs received recipients can script us
4732 	BContainerWindow* srcWindow;
4733 	dragMessage->FindPointer("src_window", (void**)&srcWindow);
4734 	if (srcWindow)
4735 		params.refsMessage->AddMessenger("TrackerViewToken", BMessenger(
4736 			srcWindow->PoseView()));
4737 
4738 	EachItemInDraggedSelection(dragMessage, AddOneToLaunchMessage, 0, &params);
4739 	if (params.refsMessage->HasRef("refs"))
4740 		TrackerLaunch(appModel->EntryRef(), params.refsMessage, true);
4741 }
4742 
4743 
4744 static bool
4745 OneMatches(BPose* pose, BPoseView*, void* castToPose)
4746 {
4747 	return pose == (const BPose*)castToPose;
4748 }
4749 
4750 
4751 bool
4752 BPoseView::DragSelectionContains(const BPose* target,
4753 	const BMessage* dragMessage)
4754 {
4755 	return EachItemInDraggedSelection(dragMessage, OneMatches, 0, (void*)target);
4756 }
4757 
4758 
4759 static void
4760 CopySelectionListToBListAsEntryRefs(const PoseList* original, BObjectList<entry_ref>* copy)
4761 {
4762 	int32 count = original->CountItems();
4763 	for (int32 index = 0; index < count; index++)
4764 		copy->AddItem(new entry_ref(*(original->ItemAt(index)->TargetModel()->EntryRef())));
4765 }
4766 
4767 
4768 void
4769 BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
4770 	bool forceCopy, bool forceMove, bool createLink, bool relativeLink)
4771 {
4772 	uint32 buttons;
4773 	BPoint loc;
4774 	GetMouse(&loc, &buttons);
4775 	MoveSelectionInto(destFolder, srcWindow, dynamic_cast<BContainerWindow*>(Window()),
4776 		buttons, loc, forceCopy, forceMove, createLink, relativeLink);
4777 }
4778 
4779 
4780 void
4781 BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
4782 	BContainerWindow* destWindow, uint32 buttons, BPoint loc, bool forceCopy,
4783 	bool forceMove, bool createLink, bool relativeLink, BPoint clickPt, bool dropOnGrid)
4784 {
4785 	AutoLock<BWindow> lock(srcWindow);
4786 	if (!lock)
4787 		return;
4788 
4789 	ASSERT(srcWindow->PoseView()->TargetModel());
4790 
4791 	if (srcWindow->PoseView()->SelectionList()->CountItems() == 0)
4792 		return;
4793 
4794 	bool createRelativeLink = relativeLink;
4795 	if (((buttons & B_SECONDARY_MOUSE_BUTTON)
4796 		|| (modifiers() & B_CONTROL_KEY)) && destWindow) {
4797 
4798 		switch (destWindow->ShowDropContextMenu(loc)) {
4799 			case kCreateRelativeLink:
4800 				createRelativeLink = true;
4801 				break;
4802 
4803 			case kCreateLink:
4804 				createLink = true;
4805 				break;
4806 
4807 			case kMoveSelectionTo:
4808 				forceMove = true;
4809 				break;
4810 
4811 			case kCopySelectionTo:
4812 				forceCopy = true;
4813 				break;
4814 
4815 			case kCancelButton:
4816 			default:
4817 				// user canceled context menu
4818 				return;
4819 		}
4820 	}
4821 
4822 	// make sure source and destination folders are different
4823 	if (!createLink && !createRelativeLink && (*srcWindow->PoseView()->TargetModel()->NodeRef()
4824 		== *destFolder->NodeRef())) {
4825 		BPoseView* targetView = srcWindow->PoseView();
4826 		if (forceCopy) {
4827 			targetView->DuplicateSelection(&clickPt, &loc);
4828 			return;
4829 		}
4830 
4831 		if (targetView->ViewMode() == kListMode)                    // can't move in list view
4832 			return;
4833 
4834 		BPoint delta = loc - clickPt;
4835 		int32 count = targetView->fSelectionList->CountItems();
4836 		for (int32 index = 0; index < count; index++) {
4837 			BPose* pose = targetView->fSelectionList->ItemAt(index);
4838 
4839 			// remove pose from VSlist before changing location
4840 			// so that we "find" the correct pose to remove
4841 			// need to do this because bsearch uses top of pose
4842 			// to locate pose to remove
4843 			targetView->RemoveFromVSList(pose);
4844 			BPoint location (pose->Location(targetView) + delta);
4845 			BRect oldBounds(pose->CalcRect(targetView));
4846 			if (dropOnGrid)
4847 				location = targetView->PinToGrid(location, targetView->fGrid, targetView->fOffset);
4848 
4849 			// TODO: don't drop poses under desktop elements
4850 			//		 ie: replicants, deskbar
4851 			pose->MoveTo(location, targetView);
4852 
4853 			targetView->RemoveFromExtent(oldBounds);
4854 			targetView->AddToExtent(pose->CalcRect(targetView));
4855 
4856 			// remove and reinsert pose to keep VSlist sorted
4857 			targetView->AddToVSList(pose);
4858 		}
4859 
4860 		return;
4861 	}
4862 
4863 
4864 	BEntry* destEntry = new BEntry(destFolder->EntryRef());
4865 	bool destIsTrash = destFolder->IsTrash();
4866 
4867 	// perform asynchronous copy/move
4868 	forceCopy = forceCopy || (modifiers() & B_OPTION_KEY);
4869 
4870 	bool okToMove = true;
4871 
4872 	if (destFolder->IsRoot()) {
4873 		BAlert* alert = new BAlert("",
4874 			B_TRANSLATE("You must drop items on one of the disk icons "
4875 			"in the \"Disks\" window."), B_TRANSLATE("Cancel"), NULL, NULL,
4876 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
4877 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
4878 		alert->Go();
4879 		okToMove = false;
4880 	}
4881 
4882 	// can't copy items into the trash
4883 	if (forceCopy && destIsTrash) {
4884 		BAlert* alert = new BAlert("",
4885 			B_TRANSLATE("Sorry, you can't copy items to the Trash."),
4886 			B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
4887 			B_WARNING_ALERT);
4888 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
4889 		alert->Go();
4890 		okToMove = false;
4891 	}
4892 
4893 	// can't create symlinks into the trash
4894 	if (createLink && destIsTrash) {
4895 		BAlert* alert = new BAlert("",
4896 			B_TRANSLATE("Sorry, you can't create links in the Trash."),
4897 			B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
4898 			B_WARNING_ALERT);
4899 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
4900 		alert->Go();
4901 		okToMove = false;
4902 	}
4903 
4904 	// prompt user if drag was from a query
4905 	if (srcWindow->TargetModel()->IsQuery()
4906 		&& !forceCopy && !destIsTrash && !createLink) {
4907 		srcWindow->UpdateIfNeeded();
4908 		BAlert* alert = new BAlert("",
4909 			B_TRANSLATE("Are you sure you want to move or copy the selected "
4910 			"item(s) to this folder?"), B_TRANSLATE("Cancel"),
4911 			B_TRANSLATE("Move"), NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
4912 		alert->SetShortcut(0, B_ESCAPE);
4913 		okToMove = alert->Go() == 1;
4914 	}
4915 
4916 	if (okToMove) {
4917 		PoseList* selectionList = srcWindow->PoseView()->SelectionList();
4918 		BList* pointList = destWindow->PoseView()->GetDropPointList(clickPt, loc, selectionList,
4919 			srcWindow->PoseView()->ViewMode() == kListMode, dropOnGrid);
4920 		BObjectList<entry_ref>* srcList = new BObjectList<entry_ref>(
4921 			selectionList->CountItems(), true);
4922 		CopySelectionListToBListAsEntryRefs(selectionList, srcList);
4923 
4924 		uint32 moveMode;
4925 		if (forceCopy)
4926 			moveMode = kCopySelectionTo;
4927 		else if (forceMove)
4928 			moveMode = kMoveSelectionTo;
4929 		else if (createRelativeLink)
4930 			moveMode = kCreateRelativeLink;
4931 		else if (createLink)
4932 			moveMode = kCreateLink;
4933 		else {
4934 			moveMode = kMoveSelectionTo;
4935 			if (!CheckDevicesEqual(srcList->ItemAt(0), destFolder))
4936 				moveMode = kCopySelectionTo;
4937 		}
4938 
4939 		FSMoveToFolder(srcList, destEntry, moveMode, pointList);
4940 		return;
4941 	}
4942 
4943 	delete destEntry;
4944 }
4945 
4946 
4947 void
4948 BPoseView::MoveSelectionTo(BPoint dropPt, BPoint clickPt,
4949 	BContainerWindow* srcWindow)
4950 {
4951 	// Moves selection from srcWindow into this window, copying if necessary.
4952 
4953 	BContainerWindow* window = ContainerWindow();
4954 	if (!window)
4955 		return;
4956 
4957 	ASSERT(window->PoseView());
4958 	ASSERT(TargetModel());
4959 
4960 	// make sure this window is a legal drop target
4961 	if (srcWindow != window && !TargetModel()->IsDropTarget())
4962 		return;
4963 
4964 	uint32 buttons = (uint32)window->CurrentMessage()->FindInt32("buttons");
4965 	bool pinToGrid = (modifiers() & B_COMMAND_KEY) != 0;
4966 	MoveSelectionInto(TargetModel(), srcWindow, window, buttons, dropPt,
4967 		false, false, false, false, clickPt, pinToGrid);
4968 }
4969 
4970 
4971 inline void
4972 UpdateWasBrokenSymlinkBinder(BPose* pose, Model* model, int32 index,
4973 	BPoseView* poseView, BObjectList<Model>* fBrokenLinks)
4974 {
4975 	if (!model->IsSymLink())
4976 		return;
4977 
4978 	BPoint loc(0, index * poseView->ListElemHeight());
4979 	pose->UpdateWasBrokenSymlink(loc, poseView);
4980 	if (model->LinkTo() != NULL)
4981 		fBrokenLinks->RemoveItem(model);
4982 }
4983 
4984 
4985 void
4986 BPoseView::TryUpdatingBrokenLinks()
4987 {
4988 	AutoLock<BWindow> lock(Window());
4989 	if (!lock)
4990 		return;
4991 
4992 	BObjectList<Model>* brokenLinksCopy = new BObjectList<Model>(*fBrokenLinks);
4993 
4994 	// try fixing broken symlinks, and detecting broken ones.
4995 	EachPoseAndModel(fPoseList, &UpdateWasBrokenSymlinkBinder, this,
4996 		fBrokenLinks);
4997 
4998 	for (int i = brokenLinksCopy->CountItems() - 1; i >= 0; i--) {
4999 		if (!fBrokenLinks->HasItem(brokenLinksCopy->ItemAt(i)))
5000 			StopWatchingParentsOf(brokenLinksCopy->ItemAt(i)->EntryRef());
5001 	}
5002 
5003 	delete brokenLinksCopy;
5004 }
5005 
5006 
5007 void
5008 BPoseView::PoseHandleDeviceUnmounted(BPose* pose, Model* model, int32 index,
5009 	BPoseView* poseView, dev_t device)
5010 {
5011 	if (model->NodeRef()->device == device)
5012 		poseView->DeletePose(model->NodeRef());
5013 	else if (model->IsSymLink()
5014 		&& model->LinkTo() != NULL
5015 		&& model->LinkTo()->NodeRef()->device == device)
5016 		poseView->DeleteSymLinkPoseTarget(model->LinkTo()->NodeRef(), pose, index);
5017 }
5018 
5019 
5020 static void
5021 OneMetaMimeChanged(BPose* pose, Model* model, int32 index,
5022 	BPoseView* poseView, const char* type)
5023 {
5024 	ASSERT(model);
5025 	if (model->IconFrom() != kNode
5026 		&& model->IconFrom() != kUnknownSource
5027 		&& model->IconFrom() != kUnknownNotFromNode
5028 		// TODO: add supertype compare
5029 		&& strcasecmp(model->MimeType(), type) == 0) {
5030 		// metamime change very likely affected the documents icon
5031 
5032 		BPoint poseLoc(0, index * poseView->ListElemHeight());
5033 		pose->UpdateIcon(poseLoc, poseView);
5034 	}
5035 }
5036 
5037 
5038 void
5039 BPoseView::MetaMimeChanged(const char* type, const char* preferredApp)
5040 {
5041 	IconCache::sIconCache->IconChanged(type, preferredApp);
5042 	// wait for other windows to do the same before we start
5043 	// updating poses which causes icon recaching
5044 	// TODO: this is a design problem that should be solved differently
5045 	snooze(10000);
5046 	Window()->UpdateIfNeeded();
5047 
5048 	EachPoseAndResolvedModel(fPoseList, &OneMetaMimeChanged, this, type);
5049 }
5050 
5051 
5052 class MetaMimeChangedAccumulator : public AccumulatingFunctionObject {
5053 // pools up matching metamime change notices, executing them as a single
5054 // update
5055 public:
5056 	MetaMimeChangedAccumulator(void (BPoseView::*func)(const char* type,
5057 		const char* preferredApp),
5058 		BContainerWindow* window, const char* type, const char* preferredApp)
5059 		:	fCallOnThis(window),
5060 			fFunc(func),
5061 			fType(type),
5062 			fPreferredApp(preferredApp)
5063 		{}
5064 
5065 	virtual bool CanAccumulate(const AccumulatingFunctionObject* functor) const
5066 		{
5067 			return dynamic_cast<const MetaMimeChangedAccumulator*>(functor)
5068 				&& dynamic_cast<const MetaMimeChangedAccumulator*>(functor)->fType
5069 					== fType
5070 				&& dynamic_cast<const MetaMimeChangedAccumulator*>(functor)->
5071 					fPreferredApp == fPreferredApp;
5072 		}
5073 
5074 	virtual void Accumulate(AccumulatingFunctionObject* DEBUG_ONLY(functor))
5075 		{
5076 			ASSERT(CanAccumulate(functor));
5077 			// do nothing, no further accumulating needed
5078 		}
5079 
5080 protected:
5081 	virtual void operator()()
5082 		{
5083 			AutoLock<BWindow> lock(fCallOnThis);
5084 			if (!lock)
5085 				return;
5086 
5087 			(fCallOnThis->PoseView()->*fFunc)(fType.String(), fPreferredApp.String());
5088 		}
5089 
5090 	virtual ulong Size() const
5091 		{
5092 			return sizeof (*this);
5093 		}
5094 
5095 private:
5096 	BContainerWindow* fCallOnThis;
5097 	void (BPoseView::*fFunc)(const char* type, const char* preferredApp);
5098 	BString fType;
5099 	BString fPreferredApp;
5100 };
5101 
5102 
5103 bool
5104 BPoseView::NoticeMetaMimeChanged(const BMessage* message)
5105 {
5106 	int32 change;
5107 	if (message->FindInt32("be:which", &change) != B_OK)
5108 		return true;
5109 
5110 	bool iconChanged = (change & B_ICON_CHANGED) != 0;
5111 	bool iconForTypeChanged = (change & B_ICON_FOR_TYPE_CHANGED) != 0;
5112 	bool preferredAppChanged = (change & B_APP_HINT_CHANGED)
5113 		|| (change & B_PREFERRED_APP_CHANGED);
5114 
5115 	const char* type = NULL;
5116 	const char* preferredApp = NULL;
5117 
5118 	if (iconChanged || preferredAppChanged)
5119 		message->FindString("be:type", &type);
5120 
5121 	if (iconForTypeChanged) {
5122 		message->FindString("be:extra_type", &type);
5123 		message->FindString("be:type", &preferredApp);
5124 	}
5125 
5126 	if (iconChanged || preferredAppChanged || iconForTypeChanged) {
5127 		TaskLoop* taskLoop = ContainerWindow()->DelayedTaskLoop();
5128 		ASSERT(taskLoop);
5129 		taskLoop->AccumulatedRunLater(new MetaMimeChangedAccumulator(
5130 			&BPoseView::MetaMimeChanged, ContainerWindow(), type, preferredApp),
5131 			200000, 5000000);
5132 	}
5133 	return true;
5134 }
5135 
5136 
5137 bool
5138 BPoseView::FSNotification(const BMessage* message)
5139 {
5140 	node_ref itemNode;
5141 	dev_t device;
5142 
5143 	switch (message->FindInt32("opcode")) {
5144 		case B_ENTRY_CREATED:
5145 			{
5146 				message->FindInt32("device", &itemNode.device);
5147 				node_ref dirNode;
5148 				dirNode.device = itemNode.device;
5149 				message->FindInt64("directory", (int64*)&dirNode.node);
5150 				message->FindInt64("node", (int64*)&itemNode.node);
5151 
5152 				ASSERT(TargetModel());
5153 
5154 				int32 count = fBrokenLinks->CountItems();
5155 				bool createPose = true;
5156 				// Query windows can get notices on different dirNodes
5157 				// The Disks window can too
5158 				// So can the Desktop, as long as the integrate flag is on
5159 				TrackerSettings settings;
5160 				if (dirNode != *TargetModel()->NodeRef()
5161 					&& !TargetModel()->IsQuery()
5162 					&& !TargetModel()->IsRoot()
5163 					&& (!settings.ShowDisksIcon() || !IsDesktopView())) {
5164 					if (count == 0)
5165 						break;
5166 					createPose = false;
5167 				}
5168 
5169 				const char* name;
5170 				if (message->FindString("name", &name) != B_OK)
5171 					break;
5172 #if DEBUG
5173 				else
5174 					SERIAL_PRINT(("no name in entry creation message\n"));
5175 					break;
5176 #endif
5177 				if (count) {
5178 					// basically, let's say we have a broken link :
5179 					// ./a_link -> ./some_folder/another_folder/a_target
5180 					// and that both some_folder and another_folder didn't
5181 					// exist yet. We are looking if the just created folder
5182 					// is 'some_folder' and watch it, expecting the creation of
5183 					// 'another_folder' later and then report the link as fixed.
5184 					Model* model = new Model(&dirNode, &itemNode, name);
5185 					if (model->IsDirectory()) {
5186 						BString createdPath(BPath(model->EntryRef()).Path());
5187 						BDirectory currentDir(TargetModel()->EntryRef());
5188 						BPath createdDir(model->EntryRef());
5189 						for (int32 i = 0; i < count; i++) {
5190 							BSymLink link(fBrokenLinks->ItemAt(i)->EntryRef());
5191 							BPath path;
5192 							link.MakeLinkedPath(&currentDir, &path);
5193 							BString pathStr(path.Path());
5194 							pathStr.Append("/");
5195 							if (pathStr.Compare(createdPath,
5196 								createdPath.Length()) == 0) {
5197 								if (pathStr[createdPath.Length()] != '/')
5198 									break;
5199 								StopWatchingParentsOf(fBrokenLinks->ItemAt(i)
5200 									->EntryRef());
5201 								watch_node(&itemNode, B_WATCH_DIRECTORY, this);
5202 								break;
5203 							}
5204 						}
5205 					}
5206 					delete model;
5207 				}
5208 				if (createPose)
5209 					EntryCreated(&dirNode, &itemNode, name);
5210 				TryUpdatingBrokenLinks();
5211 				break;
5212 			}
5213 		case B_ENTRY_MOVED:
5214 			return EntryMoved(message);
5215 			break;
5216 
5217 		case B_ENTRY_REMOVED:
5218 			message->FindInt32("device", &itemNode.device);
5219 			message->FindInt64("node", (int64*)&itemNode.node);
5220 
5221 			// our window itself may be deleted
5222 			// we must check to see if this comes as a query
5223 			// notification or a node monitor notification because
5224 			// if it's a query notification then we're just being told we
5225 			// no longer match the query, so we don't want to close the window
5226 			// but it's a node monitor notification then that means our query
5227 			// file has been deleted so we close the window
5228 
5229 			if (message->what == B_NODE_MONITOR
5230 				&& TargetModel() && *(TargetModel()->NodeRef()) == itemNode) {
5231 				if (!TargetModel()->IsRoot()) {
5232 					// it is impossible to watch for ENTRY_REMOVED in "/" because the
5233 					// notification is ambiguous - the vnode is that of the volume but
5234 					// the device is of the parent not the same as the device of the volume
5235 					// that way we may get aliasing for volumes with vnodes of 1
5236 					// (currently the case for iso9660)
5237 					DisableSaveLocation();
5238 					Window()->Close();
5239 				}
5240 			} else {
5241 				int32 index;
5242 				BPose* pose = fPoseList->FindPose(&itemNode, &index);
5243 				if (!pose) {
5244 					// couldn't find pose, first check if the node might be
5245 					// target of a symlink pose;
5246 					//
5247 					// What happens when a node and a symlink to it are in the
5248 					// same window?
5249 					// They get monitored twice, we get two notifications; the
5250 					// first one will get caught by the first FindPose, the
5251 					// second one by the DeepFindPose
5252 					//
5253 					pose = fPoseList->DeepFindPose(&itemNode, &index);
5254 					if (pose) {
5255 						DeleteSymLinkPoseTarget(&itemNode, pose, index);
5256 						break;
5257 					}
5258 				}
5259 
5260 			 	DeletePose(&itemNode);
5261 				TryUpdatingBrokenLinks();
5262 			}
5263 			break;
5264 
5265 		case B_DEVICE_MOUNTED:
5266 			{
5267 				if (message->FindInt32("new device", &device) != B_OK)
5268 					break;
5269 
5270 				if (TargetModel() != NULL && TargetModel()->IsRoot()) {
5271 					BVolume volume(device);
5272 					if (volume.InitCheck() == B_OK)
5273 						CreateVolumePose(&volume, false);
5274 				} else if (ContainerWindow()->IsTrash()) {
5275 					// add trash items from newly mounted volume
5276 
5277 					BDirectory trashDir;
5278 					BEntry entry;
5279 					BVolume volume(device);
5280 					if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK
5281 						&& trashDir.GetEntry(&entry) == B_OK) {
5282 						Model model(&entry);
5283 						if (model.InitCheck() == B_OK)
5284 							AddPoses(&model);
5285 					}
5286 				}
5287 				TaskLoop* taskLoop = ContainerWindow()->DelayedTaskLoop();
5288 				ASSERT(taskLoop);
5289 				taskLoop->RunLater(NewMemberFunctionObject(
5290 					&BPoseView::TryUpdatingBrokenLinks, this), 500000);
5291 					// delay of 500000: wait for volumes to properly finish mounting
5292 					// without this in the Model::FinishSettingUpType a symlink
5293 					// to a volume would get initialized as a symlink to a directory
5294 					// because IsRootDirectory looks like returns false. Either there
5295 					// is a race condition or I was doing something wrong.
5296 				break;
5297 			}
5298 		case B_DEVICE_UNMOUNTED:
5299 			if (message->FindInt32("device", &device) == B_OK) {
5300 				if (TargetModel() && TargetModel()->NodeRef()->device == device) {
5301 					// close the window from a volume that is gone
5302 					DisableSaveLocation();
5303 					Window()->Close();
5304 				} else if (TargetModel())
5305 					EachPoseAndModel(fPoseList, &PoseHandleDeviceUnmounted, this, device);
5306 			}
5307 			break;
5308 
5309 		case B_STAT_CHANGED:
5310 		case B_ATTR_CHANGED:
5311 			return AttributeChanged(message);
5312 			break;
5313 	}
5314 	return true;
5315 }
5316 
5317 
5318 bool
5319 BPoseView::CreateSymlinkPoseTarget(Model* symlink)
5320 {
5321 	Model* newResolvedModel = NULL;
5322 	Model* result = symlink->LinkTo();
5323 	if (!result) {
5324 		BEntry entry(symlink->EntryRef(), true);
5325 		if (entry.InitCheck() == B_OK) {
5326 			node_ref nref;
5327 			entry_ref eref;
5328 			entry.GetNodeRef(&nref);
5329 			entry.GetRef(&eref);
5330 			if (eref.directory != TargetModel()->NodeRef()->node)
5331 				WatchNewNode(&nref, B_WATCH_STAT | B_WATCH_ATTR | B_WATCH_NAME
5332 					| B_WATCH_INTERIM_STAT, this);
5333 			newResolvedModel = new Model(&entry, true);
5334 		} else {
5335 			fBrokenLinks->AddItem(symlink);
5336 			WatchParentOf(symlink->EntryRef());
5337 			return true;
5338 		}
5339 		result = newResolvedModel;
5340 	}
5341 
5342 	symlink->SetLinkTo(result);
5343 	return true;
5344 }
5345 
5346 
5347 BPose*
5348 BPoseView::EntryCreated(const node_ref* dirNode, const node_ref* itemNode,
5349 	const char* name, int32* indexPtr)
5350 {
5351 	// reject notification if pose already exists
5352 	if (fPoseList->FindPose(itemNode) || FindZombie(itemNode))
5353 		return NULL;
5354 	BPoseView::WatchNewNode(itemNode);
5355 		// have to node monitor ahead of time because Model will
5356 		// cache up the file type and preferred app
5357 	Model* model = new Model(dirNode, itemNode, name, true);
5358 
5359 	if (model->InitCheck() != B_OK) {
5360 		// if we have trouble setting up model then we stuff it into
5361 		// a zombie list in a half-alive state until we can properly awaken it
5362 		PRINT(("2 adding model %s to zombie list, error %s\n", model->Name(),
5363 			strerror(model->InitCheck())));
5364 		fZombieList->AddItem(model);
5365 		return NULL;
5366 	}
5367 
5368 	PoseInfo poseInfo;
5369 	ReadPoseInfo(model, &poseInfo);
5370 
5371 	if (!PoseVisible(model, &poseInfo)) {
5372 		watch_node(model->NodeRef(), B_STOP_WATCHING, this);
5373 		delete model;
5374 		return NULL;
5375 	}
5376 
5377 	// model is a symlink, cache up the symlink target or scrap
5378 	// everything if target is invisible
5379 	if (model->IsSymLink() && !CreateSymlinkPoseTarget(model)) {
5380 		watch_node(model->NodeRef(), B_STOP_WATCHING, this);
5381 		delete model;
5382 		return NULL;
5383 	}
5384 
5385 	return CreatePose(model, &poseInfo, true, indexPtr);
5386 }
5387 
5388 
5389 bool
5390 BPoseView::EntryMoved(const BMessage* message)
5391 {
5392 	ino_t oldDir;
5393 	node_ref dirNode;
5394 	node_ref itemNode;
5395 
5396 	message->FindInt32("device", &dirNode.device);
5397 	itemNode.device = dirNode.device;
5398 	message->FindInt64("to directory", (int64*)&dirNode.node);
5399 	message->FindInt64("node", (int64*)&itemNode.node);
5400 	message->FindInt64("from directory", (int64*)&oldDir);
5401 
5402 	const char* name;
5403 	if (message->FindString("name", &name) != B_OK)
5404 		return true;
5405 	// handle special case of notifying a name change for a volume
5406 	// - the notification is not enough, because the volume's device
5407 	// is different than that of the root directory; we have to do a
5408 	// lookup using the new volume name and get the volume device from there
5409 	StatStruct st;
5410 	// get the inode of the root and check if we got a notification on it
5411 	if (stat("/", &st) >= 0
5412 		&& st.st_dev == dirNode.device
5413 		&& st.st_ino == dirNode.node) {
5414 
5415 		BString buffer;
5416 		buffer << "/" << name;
5417 		if (stat(buffer.String(), &st) >= 0) {
5418 			// point the dirNode to the actual volume
5419 			itemNode.node = st.st_ino;
5420 			itemNode.device = st.st_dev;
5421 		}
5422 	}
5423 
5424 	ASSERT(TargetModel());
5425 
5426 	node_ref thisDirNode;
5427 	if (ContainerWindow()->IsTrash()) {
5428 
5429 		BDirectory trashDir;
5430 		if (FSGetTrashDir(&trashDir, itemNode.device) != B_OK)
5431 			return true;
5432 
5433 		trashDir.GetNodeRef(&thisDirNode);
5434 	} else
5435 		thisDirNode = *TargetModel()->NodeRef();
5436 
5437 	// see if we need to update window title (and folder itself)
5438 	if (thisDirNode == itemNode) {
5439 
5440 		TargetModel()->UpdateEntryRef(&dirNode, name);
5441 		assert_cast<BContainerWindow*>(Window())->UpdateTitle();
5442 	}
5443 	if (oldDir == dirNode.node || TargetModel()->IsQuery()) {
5444 
5445 		// rename or move of entry in this directory (or query)
5446 
5447 		int32 index;
5448 		BPose* pose = fPoseList->FindPose(&itemNode, &index);
5449 		int32 poseListIndex = index;
5450 		bool visible = true;
5451 		if (fFiltering)
5452 			visible = fFilteredPoseList->FindPose(&itemNode, &index) != NULL;
5453 
5454 		if (pose) {
5455 			pose->TargetModel()->UpdateEntryRef(&dirNode, name);
5456 			// for queries we check for move to trash and remove item if so
5457 			if (TargetModel()->IsQuery()) {
5458 				PoseInfo poseInfo;
5459 				ReadPoseInfo(pose->TargetModel(), &poseInfo);
5460 				if (!ShouldShowPose(pose->TargetModel(), &poseInfo))
5461 					return DeletePose(&itemNode, pose, index);
5462 				return true;
5463 			}
5464 
5465 			BPoint loc(0, index * fListElemHeight);
5466 			// if we get a rename then we need to assume that we might
5467 			// have missed some other attr changed notifications so we
5468 			// recheck all widgets
5469 			if (pose->TargetModel()->OpenNode() == B_OK) {
5470 				pose->UpdateAllWidgets(index, loc, this);
5471 				pose->TargetModel()->CloseNode();
5472 				_CheckPoseSortOrder(fPoseList, pose, poseListIndex);
5473 				if (fFiltering) {
5474 					if (!visible && FilterPose(pose)) {
5475 						BRect bounds = Bounds();
5476 						float scrollBy = 0;
5477 						AddPoseToList(fFilteredPoseList, true, true, pose,
5478 							bounds, scrollBy, true);
5479 					} else if (visible && !FilterPose(pose))
5480 						RemoveFilteredPose(pose, index);
5481 					else if (visible)
5482 						_CheckPoseSortOrder(fFilteredPoseList, pose, index);
5483 				}
5484 			}
5485 		} else {
5486 			// also must watch for renames on zombies
5487 			Model* zombie = FindZombie(&itemNode, &index);
5488 			if (zombie) {
5489 				PRINT(("converting model %s from a zombie\n", zombie->Name()));
5490 				zombie->UpdateEntryRef(&dirNode, name);
5491 				pose = ConvertZombieToPose(zombie, index);
5492 			} else
5493 				return false;
5494 		}
5495 		if (pose)
5496 			pendingNodeMonitorCache.PoseCreatedOrMoved(this, pose);
5497 	} else if (oldDir == thisDirNode.node)
5498 		DeletePose(&itemNode);
5499 	else if (dirNode.node == thisDirNode.node)
5500 		EntryCreated(&dirNode, &itemNode, name);
5501 
5502 	TryUpdatingBrokenLinks();
5503 	return true;
5504 }
5505 
5506 
5507 void
5508 BPoseView::WatchParentOf(const entry_ref* ref)
5509 {
5510 	BPath currentDir(ref);
5511 	currentDir.GetParent(&currentDir);
5512 	BSymLink symlink(ref);
5513 	BPath path;
5514 
5515 	symlink.MakeLinkedPath(currentDir.Path(), &path);
5516 	status_t status = path.GetParent(&path);
5517 
5518 	while (status == B_BAD_VALUE)
5519 		status = path.GetParent(&path);
5520 
5521 	if (status == B_ENTRY_NOT_FOUND)
5522 		return;
5523 
5524 	node_ref nref;
5525 	BNode(path.Path()).GetNodeRef(&nref);
5526 
5527 	if (nref != *TargetModel()->NodeRef())
5528 		watch_node(&nref, B_WATCH_DIRECTORY, this);
5529 }
5530 
5531 
5532 void
5533 BPoseView::StopWatchingParentsOf(const entry_ref* ref)
5534 {
5535 	BPath path;
5536 	BSymLink symlink(ref);
5537 	BPath currentDir(ref);
5538 	currentDir.GetParent(&currentDir);
5539 	symlink.MakeLinkedPath(currentDir.Path(), &path);
5540 
5541 	if (path.InitCheck() != B_OK)
5542 		return;
5543 
5544 	BObjectList<Model>* brokenLinksCopy = new BObjectList<Model>(*fBrokenLinks);
5545 	int32 count = brokenLinksCopy->CountItems();
5546 
5547 	while (path.GetParent(&path) == B_OK) {
5548 		if (strcmp(path.Path(), "/") == 0)
5549 			break;
5550 
5551 		BNode dir(path.Path());
5552 		node_ref dirNode;
5553 		dir.GetNodeRef(&dirNode);
5554 
5555 		// don't stop watching yourself.
5556 		if (dirNode == *TargetModel()->NodeRef())
5557 			continue;
5558 
5559 		// make sure we don't have another broken links that still requires
5560 		// to watch this directory
5561 		bool keep = false;
5562 		for (int32 i = count - 1; i >= 0; i--) {
5563 			BSymLink link(brokenLinksCopy->ItemAt(i)->EntryRef());
5564 			BPath absolutePath;
5565 			link.MakeLinkedPath(currentDir.Path(), &absolutePath);
5566 			if (BString(absolutePath.Path()).Compare(path.Path(),
5567 					strlen(path.Path())) == 0) {
5568 				// a broken link still needs to watch this folder, but
5569 				// don't let that same link also watch a deeper parent.
5570 				brokenLinksCopy->RemoveItemAt(i);
5571 				count--;
5572 				keep = true;
5573 			}
5574 		}
5575 		if (!keep)
5576 			watch_node(&dirNode, B_STOP_WATCHING, this);
5577 	}
5578 	delete brokenLinksCopy;
5579 }
5580 
5581 
5582 bool
5583 BPoseView::AttributeChanged(const BMessage* message)
5584 {
5585 	node_ref itemNode;
5586 	message->FindInt32("device", &itemNode.device);
5587 	message->FindInt64("node", (int64*)&itemNode.node);
5588 
5589 	const char* attrName;
5590 	message->FindString("attr", &attrName);
5591 
5592 	if (TargetModel() != NULL && *TargetModel()->NodeRef() == itemNode
5593 		&& TargetModel()->AttrChanged(attrName)) {
5594 		// the icon of our target has changed, update drag icon
5595 		// TODO: make this simpler (ie. store the icon with the window)
5596 		BView* view = Window()->FindView("MenuBar");
5597 		if (view != NULL) {
5598 			view = view->FindView("ThisContainer");
5599 			if (view != NULL) {
5600 				IconCache::sIconCache->IconChanged(TargetModel());
5601 				view->Invalidate();
5602 			}
5603 		}
5604 	}
5605 
5606 	int32 index;
5607 	attr_info info;
5608 	PoseList* posesFound = fPoseList->FindAllPoses(&itemNode);
5609 	int32 posesCount = posesFound->CountItems();
5610 	for (int i = 0; i < posesCount; i++) {
5611 		BPose* pose = posesFound->ItemAt(i);
5612 		Model* model = pose->TargetModel();
5613 		if (model->IsSymLink() && *model->NodeRef() != itemNode)
5614 			// change happened on symlink's target
5615 			model = model->ResolveIfLink();
5616 		ASSERT(model);
5617 
5618 		status_t result = B_OK;
5619 		for (int32 count = 0; count < 100; count++) {
5620 			// if node is busy, wait a little, it may be in the
5621 			// middle of mimeset and we wan't to pick up the changes
5622 			result = model->OpenNode();
5623 			if (result == B_OK || result != B_BUSY)
5624 				break;
5625 
5626 			PRINT(("model %s busy, retrying in a bit\n", model->Name()));
5627 			snooze(10000);
5628 		}
5629 		if (result != B_OK) {
5630 			PRINT(("Cache Error %s\n", strerror(result)));
5631 			continue;
5632 		}
5633 
5634 		bool visible = fPoseList->FindPose(pose->TargetModel()->NodeRef(),
5635 				&index) != NULL;
5636 		int32 poseListIndex = index;
5637 
5638 		if (fFiltering)
5639 			visible = fFilteredPoseList->FindPose(pose->TargetModel()
5640 					->NodeRef(), &index) != NULL;
5641 
5642 		BPoint loc(0, index * fListElemHeight);
5643 		if (attrName && model->Node() != NULL) {
5644 			memset(&info, 0, sizeof(attr_info));
5645 			// the call below might fail if the attribute has been removed
5646 			model->Node()->GetAttrInfo(attrName, &info);
5647 			pose->UpdateWidgetAndModel(model, attrName, info.type, index,
5648 				loc, this, visible);
5649 			if (strcmp(attrName, kAttrMIMEType) == 0)
5650 				RefreshMimeTypeList();
5651 		} else {
5652 			pose->UpdateWidgetAndModel(model, 0, 0, index, loc, this,
5653 				visible);
5654 		}
5655 		model->CloseNode();
5656 		if (fFiltering) {
5657 			if (!visible && FilterPose(pose)) {
5658 				visible = true;
5659 				float scrollBy = 0;
5660 				BRect bounds = Bounds();
5661 				AddPoseToList(fFilteredPoseList, true, true, pose, bounds,
5662 					scrollBy, true);
5663 				continue;
5664 			} else if (visible && !FilterPose(pose)) {
5665 				RemoveFilteredPose(pose, index);
5666 				continue;
5667 			}
5668 		}
5669 
5670 		if (attrName) {
5671 			// note: the following code is wrong, because this sort of hashing
5672 			// may overlap and we get aliasing
5673 			uint32 attrHash = AttrHashString(attrName, info.type);
5674 			if (attrHash == PrimarySort() || attrHash == SecondarySort()) {
5675 				_CheckPoseSortOrder(fPoseList, pose, poseListIndex);
5676 				if (fFiltering && visible)
5677 					_CheckPoseSortOrder(fFilteredPoseList, pose, index);
5678 			}
5679 		} else {
5680 			int32 fields;
5681 			if (message->FindInt32("fields", &fields) != B_OK)
5682 				continue;
5683 
5684 			for (int i = sizeof(sAttrColumnMap) / sizeof(attr_column_relation);
5685 				i--;) {
5686 				if (sAttrColumnMap[i].attrHash == PrimarySort()
5687 					|| sAttrColumnMap[i].attrHash == SecondarySort()) {
5688 					if ((fields & sAttrColumnMap[i].fieldMask) != 0) {
5689 						_CheckPoseSortOrder(fPoseList, pose, poseListIndex);
5690 						if (fFiltering && visible)
5691 							_CheckPoseSortOrder(fFilteredPoseList, pose, index);
5692 						break;
5693 					}
5694 				}
5695 			}
5696 		}
5697 	}
5698 	delete posesFound;
5699 	if (posesCount == 0) {
5700 		// we received an attr changed notification for a zombie model, it means
5701 		// that although we couldn't open the node the first time, it seems
5702 		// to be fine now since we're receiving notifications about it, it might
5703 		// be a good time to convert it to a non-zombie state. cf. test in #4130
5704 		Model* zombie = FindZombie(&itemNode, &index);
5705 		if (zombie) {
5706 			PRINT(("converting model %s from a zombie\n", zombie->Name()));
5707 			return ConvertZombieToPose(zombie, index) != NULL;
5708 		} else {
5709 			PRINT(("model has no pose but is not a zombie either!\n"));
5710 			return false;
5711 		}
5712 	}
5713 
5714 	return true;
5715 }
5716 
5717 
5718 void
5719 BPoseView::UpdateIcon(BPose* pose)
5720 {
5721 	BPoint location;
5722 	if (ViewMode() == kListMode) {
5723 		// need to find the index of the pose in the pose list
5724 		bool found = false;
5725 		PoseList* poseList = CurrentPoseList();
5726 		int32 count = poseList->CountItems();
5727 		for (int32 index = 0; index < count; index++) {
5728 			if (poseList->ItemAt(index) == pose) {
5729 				location.Set(0, index * fListElemHeight);
5730 				found = true;
5731 				break;
5732 			}
5733 		}
5734 
5735 		if (!found)
5736 			return;
5737 	}
5738 
5739 	pose->UpdateIcon(location, this);
5740 }
5741 
5742 
5743 BPose*
5744 BPoseView::ConvertZombieToPose(Model* zombie, int32 index)
5745 {
5746 	if (zombie->UpdateStatAndOpenNode() != B_OK)
5747 		return NULL;
5748 
5749 	fZombieList->RemoveItemAt(index);
5750 
5751 	PoseInfo poseInfo;
5752 	ReadPoseInfo(zombie, &poseInfo);
5753 
5754 	if (ShouldShowPose(zombie, &poseInfo))
5755 		// TODO: handle symlinks here
5756 		return CreatePose(zombie, &poseInfo);
5757 
5758 	delete zombie;
5759 
5760 	return NULL;
5761 }
5762 
5763 
5764 BList*
5765 BPoseView::GetDropPointList(BPoint dropStart, BPoint dropEnd, const PoseList* poses,
5766 	bool sourceInListMode, bool dropOnGrid) const
5767 {
5768 	if (ViewMode() == kListMode)
5769 		return NULL;
5770 
5771 	int32 count = poses->CountItems();
5772 	BList* pointList = new BList(count);
5773 	for (int32 index = 0; index < count; index++) {
5774 		BPose* pose = poses->ItemAt(index);
5775 		BPoint poseLoc;
5776 		if (sourceInListMode)
5777 			poseLoc = dropEnd + BPoint(0, index * (IconPoseHeight() + 3));
5778 		else
5779 			poseLoc = dropEnd + (pose->Location(this) - dropStart);
5780 
5781 		if (dropOnGrid)
5782 			poseLoc = PinToGrid(poseLoc, fGrid, fOffset);
5783 
5784 		pointList->AddItem(new BPoint(poseLoc));
5785 	}
5786 
5787 	return pointList;
5788 }
5789 
5790 
5791 void
5792 BPoseView::DuplicateSelection(BPoint* dropStart, BPoint* dropEnd)
5793 {
5794 	// If there is a volume or trash folder, remove them from the list
5795 	// because they cannot get copied
5796 	int32 selectionSize = fSelectionList->CountItems();
5797 	for (int32 index = 0; index < selectionSize; index++) {
5798 		BPose* pose = (BPose*)fSelectionList->ItemAt(index);
5799 		Model* model = pose->TargetModel();
5800 
5801 		// can't duplicate a volume or the trash
5802 		if (model->IsTrash() || model->IsVolume()) {
5803 			fSelectionList->RemoveItemAt(index);
5804 			index--;
5805 			selectionSize--;
5806 			if (fSelectionPivotPose == pose)
5807 				fSelectionPivotPose = NULL;
5808 			if (fRealPivotPose == pose)
5809 				fRealPivotPose = NULL;
5810 			continue;
5811 		}
5812 	}
5813 
5814 	// create entry_ref list from selection
5815 	if (!fSelectionList->IsEmpty()) {
5816 		BObjectList<entry_ref>* srcList = new BObjectList<entry_ref>(
5817 			fSelectionList->CountItems(), true);
5818 		CopySelectionListToBListAsEntryRefs(fSelectionList, srcList);
5819 
5820 		BList* dropPoints = NULL;
5821 		if (dropStart)
5822 			dropPoints = GetDropPointList(*dropStart, *dropEnd, fSelectionList,
5823 				ViewMode() == kListMode, (modifiers() & B_COMMAND_KEY) != 0);
5824 
5825 		// perform asynchronous duplicate
5826 		FSDuplicate(srcList, dropPoints);
5827 	}
5828 }
5829 
5830 
5831 void
5832 BPoseView::SelectPoseAtLocation(BPoint point)
5833 {
5834 	int32 index;
5835 	BPose* pose = FindPose(point, &index);
5836 	if (pose)
5837 		SelectPose(pose, index);
5838 }
5839 
5840 
5841 void
5842 BPoseView::MoveListToTrash(BObjectList<entry_ref>* list, bool selectNext,
5843 	bool deleteDirectly)
5844 {
5845 	if (!list->CountItems())
5846 		return;
5847 
5848 	BObjectList<FunctionObject>* taskList =
5849 		new BObjectList<FunctionObject>(2, true);
5850 		// new owning list of tasks
5851 
5852 	// first move selection to trash,
5853 	if (deleteDirectly)
5854 		taskList->AddItem(NewFunctionObject(FSDeleteRefList, list, false, true));
5855 	else
5856 		taskList->AddItem(NewFunctionObject(FSMoveToTrash, list,
5857 			(BList*)NULL, false));
5858 
5859 	if (selectNext && ViewMode() == kListMode) {
5860 		// next, if in list view mode try selecting the next item after
5861 		BPose* pose = fSelectionList->ItemAt(0);
5862 
5863 		// find a point in the pose
5864 		BPoint pointInPose(kListOffset + 5, 5);
5865 		int32 index = IndexOfPose(pose);
5866 		pointInPose.y += fListElemHeight * index;
5867 
5868 		TTracker* tracker = dynamic_cast<TTracker*>(be_app);
5869 
5870 		ASSERT(TargetModel());
5871 		if (tracker)
5872 			// add a function object to the list of tasks to run
5873 			// that will select the next item after the one we just
5874 			// deleted
5875 			taskList->AddItem(NewMemberFunctionObject(
5876 				&TTracker::SelectPoseAtLocationSoon, tracker,
5877 				*TargetModel()->NodeRef(), pointInPose));
5878 
5879 	}
5880 	// execute the two tasks in order
5881 	ThreadSequence::Launch(taskList, true);
5882 }
5883 
5884 
5885 inline void
5886 CopyOneTrashedRefAsEntry(const entry_ref* ref, BObjectList<entry_ref>* trashList,
5887 	BObjectList<entry_ref>* noTrashList, std::map<int32, bool>* deviceHasTrash)
5888 {
5889 	std::map<int32, bool> &deviceHasTrashTmp = *deviceHasTrash;
5890 		// work around stupid binding problems with EachListItem
5891 
5892 	BDirectory entryDir(ref);
5893 	bool isVolume = entryDir.IsRootDirectory();
5894 		// volumes will get unmounted
5895 
5896 	// see if pose's device has a trash
5897 	int32 device = ref->device;
5898 	BDirectory trashDir;
5899 
5900 	// cache up the result in a map so that we don't have to keep calling
5901 	// FSGetTrashDir over and over
5902 	if (!isVolume
5903 		&& deviceHasTrashTmp.find(device) == deviceHasTrashTmp.end())
5904 		deviceHasTrashTmp[device] = FSGetTrashDir(&trashDir, device) == B_OK;
5905 
5906 	if (isVolume || deviceHasTrashTmp[device])
5907 		trashList->AddItem(new entry_ref(*ref));
5908 	else
5909 		noTrashList->AddItem(new entry_ref(*ref));
5910 }
5911 
5912 
5913 static void
5914 CopyPoseOneAsEntry(BPose* pose, BObjectList<entry_ref>* trashList,
5915 	BObjectList<entry_ref>* noTrashList, std::map<int32, bool>* deviceHasTrash)
5916 {
5917 	CopyOneTrashedRefAsEntry(pose->TargetModel()->EntryRef(), trashList,
5918 		noTrashList, deviceHasTrash);
5919 }
5920 
5921 
5922 static bool
5923 CheckVolumeReadOnly(const entry_ref* ref)
5924 {
5925 	BVolume volume (ref->device);
5926 	if (volume.IsReadOnly()) {
5927 		BAlert* alert = new BAlert ("",
5928 			B_TRANSLATE("Files cannot be moved or deleted from a read-only "
5929 			"volume."), B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
5930 			B_STOP_ALERT);
5931 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
5932 		alert->Go();
5933 		return false;
5934 	}
5935 
5936 	return true;
5937 }
5938 
5939 
5940 void
5941 BPoseView::MoveSelectionOrEntryToTrash(const entry_ref* ref, bool selectNext)
5942 {
5943 	BObjectList<entry_ref>* entriesToTrash = new
5944 		BObjectList<entry_ref>(fSelectionList->CountItems());
5945 	BObjectList<entry_ref>* entriesToDeleteOnTheSpot = new
5946 		BObjectList<entry_ref>(20, true);
5947 	std::map<int32, bool> deviceHasTrash;
5948 
5949 	if (ref) {
5950 		if (!CheckVolumeReadOnly(ref)) {
5951 			delete entriesToTrash;
5952 			delete entriesToDeleteOnTheSpot;
5953 			return;
5954 		}
5955 		CopyOneTrashedRefAsEntry(ref, entriesToTrash, entriesToDeleteOnTheSpot,
5956 			&deviceHasTrash);
5957 	} else {
5958 		if (!CheckVolumeReadOnly(fSelectionList->ItemAt(0)->TargetModel()->EntryRef())) {
5959 			delete entriesToTrash;
5960 			delete entriesToDeleteOnTheSpot;
5961 			return;
5962 		}
5963 		EachListItem(fSelectionList, CopyPoseOneAsEntry, entriesToTrash,
5964 			entriesToDeleteOnTheSpot, &deviceHasTrash);
5965 	}
5966 
5967 	if (entriesToDeleteOnTheSpot->CountItems()) {
5968 		BString alertText;
5969 		if (ref) {
5970 			alertText.SetTo(B_TRANSLATE("The selected item cannot be moved to "
5971 				"the Trash. Would you like to delete it instead? "
5972 				"(This operation cannot be reverted.)"));
5973 		} else {
5974 			alertText.SetTo(B_TRANSLATE("Some of the selected items cannot be "
5975 				"moved to the Trash. Would you like to delete them instead? "
5976 				"(This operation cannot be reverted.)"));
5977 		}
5978 
5979 		BAlert* alert = new BAlert("", alertText.String(),
5980 			B_TRANSLATE("Cancel"), B_TRANSLATE("Delete"));
5981 		alert->SetShortcut(0, B_ESCAPE);
5982 		if (alert->Go() == 0)
5983 			return;
5984 	}
5985 
5986 	MoveListToTrash(entriesToTrash, selectNext, false);
5987 	MoveListToTrash(entriesToDeleteOnTheSpot, selectNext, true);
5988 }
5989 
5990 
5991 void
5992 BPoseView::MoveSelectionToTrash(bool selectNext)
5993 {
5994 	if (fSelectionList->IsEmpty())
5995 		return;
5996 
5997 	// create entry_ref list from selection
5998 	// separate items that can be trashed from ones that cannot
5999 
6000 	MoveSelectionOrEntryToTrash(0, selectNext);
6001 }
6002 
6003 
6004 void
6005 BPoseView::MoveEntryToTrash(const entry_ref* ref, bool selectNext)
6006 {
6007 	MoveSelectionOrEntryToTrash(ref, selectNext);
6008 }
6009 
6010 
6011 void
6012 BPoseView::DeleteSelection(bool selectNext, bool askUser)
6013 {
6014 	int32 count = fSelectionList -> CountItems();
6015 	if (count <= 0)
6016 		return;
6017 
6018 	if (!CheckVolumeReadOnly(fSelectionList->ItemAt(0)->TargetModel()->EntryRef()))
6019 		return;
6020 
6021 	BObjectList<entry_ref>* entriesToDelete = new BObjectList<entry_ref>(count, true);
6022 
6023 	for (int32 index = 0; index < count; index++)
6024 		entriesToDelete->AddItem(new entry_ref((*fSelectionList->ItemAt(index)
6025 			->TargetModel()->EntryRef())));
6026 
6027 	Delete(entriesToDelete, selectNext, askUser);
6028 }
6029 
6030 
6031 void
6032 BPoseView::RestoreSelectionFromTrash(bool selectNext)
6033 {
6034 	int32 count = fSelectionList -> CountItems();
6035 	if (count <= 0)
6036 		return;
6037 
6038 	BObjectList<entry_ref>* entriesToRestore = new BObjectList<entry_ref>(count, true);
6039 
6040 	for (int32 index = 0; index < count; index++)
6041 		entriesToRestore->AddItem(new entry_ref((*fSelectionList->ItemAt(index)
6042 			->TargetModel()->EntryRef())));
6043 
6044 	RestoreItemsFromTrash(entriesToRestore, selectNext);
6045 }
6046 
6047 
6048 void
6049 BPoseView::Delete(const entry_ref &ref, bool selectNext, bool askUser)
6050 {
6051 	BObjectList<entry_ref>* entriesToDelete = new BObjectList<entry_ref>(1, true);
6052 	entriesToDelete->AddItem(new entry_ref(ref));
6053 
6054 	Delete(entriesToDelete, selectNext, askUser);
6055 }
6056 
6057 
6058 void
6059 BPoseView::Delete(BObjectList<entry_ref>* list, bool selectNext, bool askUser)
6060 {
6061 	if (list->CountItems() == 0) {
6062 		delete list;
6063 		return;
6064 	}
6065 
6066 	BObjectList<FunctionObject>* taskList =
6067 		new BObjectList<FunctionObject>(2, true);
6068 
6069 	// first move selection to trash,
6070 	taskList->AddItem(NewFunctionObject(FSDeleteRefList, list, false, askUser));
6071 
6072 	if (selectNext && ViewMode() == kListMode) {
6073 		// next, if in list view mode try selecting the next item after
6074 		BPose* pose = fSelectionList->ItemAt(0);
6075 
6076 		// find a point in the pose
6077 		BPoint pointInPose(kListOffset + 5, 5);
6078 		int32 index = IndexOfPose(pose);
6079 		pointInPose.y += fListElemHeight * index;
6080 
6081 		TTracker* tracker = dynamic_cast<TTracker*>(be_app);
6082 
6083 		ASSERT(TargetModel());
6084 		if (tracker)
6085 			// add a function object to the list of tasks to run
6086 			// that will select the next item after the one we just
6087 			// deleted
6088 			taskList->AddItem(NewMemberFunctionObject(
6089 				&TTracker::SelectPoseAtLocationSoon, tracker,
6090 				*TargetModel()->NodeRef(), pointInPose));
6091 
6092 	}
6093 	// execute the two tasks in order
6094 	ThreadSequence::Launch(taskList, true);
6095 }
6096 
6097 
6098 void
6099 BPoseView::RestoreItemsFromTrash(BObjectList<entry_ref>* list, bool selectNext)
6100 {
6101 	if (list->CountItems() == 0) {
6102 		delete list;
6103 		return;
6104 	}
6105 
6106 	BObjectList<FunctionObject>* taskList =
6107 		new BObjectList<FunctionObject>(2, true);
6108 
6109 	// first restoree selection
6110 	taskList->AddItem(NewFunctionObject(FSRestoreRefList, list, false));
6111 
6112 	if (selectNext && ViewMode() == kListMode) {
6113 		// next, if in list view mode try selecting the next item after
6114 		BPose* pose = fSelectionList->ItemAt(0);
6115 
6116 		// find a point in the pose
6117 		BPoint pointInPose(kListOffset + 5, 5);
6118 		int32 index = IndexOfPose(pose);
6119 		pointInPose.y += fListElemHeight * index;
6120 
6121 		TTracker* tracker = dynamic_cast<TTracker*>(be_app);
6122 
6123 		ASSERT(TargetModel());
6124 		if (tracker)
6125 			// add a function object to the list of tasks to run
6126 			// that will select the next item after the one we just
6127 			// restored
6128 			taskList->AddItem(NewMemberFunctionObject(
6129 				&TTracker::SelectPoseAtLocationSoon, tracker,
6130 				*TargetModel()->NodeRef(), pointInPose));
6131 
6132 	}
6133 	// execute the two tasks in order
6134 	ThreadSequence::Launch(taskList, true);
6135 }
6136 
6137 
6138 void
6139 BPoseView::SelectAll()
6140 {
6141 	BRect bounds(Bounds());
6142 
6143 	// clear selection list
6144 	fSelectionList->MakeEmpty();
6145 	fMimeTypesInSelectionCache.MakeEmpty();
6146 	fSelectionPivotPose = NULL;
6147 	fRealPivotPose = NULL;
6148 
6149 	int32 startIndex = 0;
6150 	BPoint loc(0, fListElemHeight * startIndex);
6151 
6152 	bool iconMode = ViewMode() != kListMode;
6153 
6154 	PoseList* poseList = CurrentPoseList();
6155 	int32 count = poseList->CountItems();
6156 	for (int32 index = startIndex; index < count; index++) {
6157 		BPose* pose = poseList->ItemAt(index);
6158 		fSelectionList->AddItem(pose);
6159 		if (index == startIndex)
6160 			fSelectionPivotPose = pose;
6161 
6162 		if (!pose->IsSelected()) {
6163 			pose->Select(true);
6164 
6165 			BRect poseRect;
6166 			if (iconMode)
6167 				poseRect = pose->CalcRect(this);
6168 			else
6169 				poseRect = pose->CalcRect(loc, this);
6170 
6171 			if (bounds.Intersects(poseRect)) {
6172 				pose->Draw(poseRect, bounds, this, false);
6173 				Flush();
6174 			}
6175 		}
6176 
6177 		loc.y += fListElemHeight;
6178 	}
6179 
6180 	if (fSelectionChangedHook)
6181 		ContainerWindow()->SelectionChanged();
6182 }
6183 
6184 
6185 void
6186 BPoseView::InvertSelection()
6187 {
6188 	// Since this function shares most code with
6189 	// SelectAll(), we could make SelectAll() empty the selection,
6190 	// then call InvertSelection()
6191 
6192 	BRect bounds(Bounds());
6193 
6194 	int32 startIndex = 0;
6195 	BPoint loc(0, fListElemHeight * startIndex);
6196 
6197 	fMimeTypesInSelectionCache.MakeEmpty();
6198 	fSelectionPivotPose = NULL;
6199 	fRealPivotPose = NULL;
6200 
6201 	bool iconMode = ViewMode() != kListMode;
6202 
6203 	PoseList* poseList = CurrentPoseList();
6204 	int32 count = poseList->CountItems();
6205 	for (int32 index = startIndex; index < count; index++) {
6206 		BPose* pose = poseList->ItemAt(index);
6207 
6208 		if (pose->IsSelected()) {
6209 			fSelectionList->RemoveItem(pose);
6210 			pose->Select(false);
6211 		} else {
6212 			if (index == startIndex)
6213 				fSelectionPivotPose = pose;
6214 			fSelectionList->AddItem(pose);
6215 			pose->Select(true);
6216 		}
6217 
6218 		BRect poseRect;
6219 		if (iconMode)
6220 			poseRect = pose->CalcRect(this);
6221 		else
6222 			poseRect = pose->CalcRect(loc, this);
6223 
6224 		if (bounds.Intersects(poseRect))
6225 			Invalidate();
6226 
6227 		loc.y += fListElemHeight;
6228 	}
6229 
6230 	if (fSelectionChangedHook)
6231 		ContainerWindow()->SelectionChanged();
6232 }
6233 
6234 
6235 int32
6236 BPoseView::SelectMatchingEntries(const BMessage* message)
6237 {
6238 	int32 matchCount = 0;
6239 	SetMultipleSelection(true);
6240 
6241 	ClearSelection();
6242 
6243 	TrackerStringExpressionType expressionType;
6244 	BString expression;
6245 	const char* expressionPointer;
6246 	bool invertSelection;
6247 	bool ignoreCase;
6248 
6249 	message->FindInt32("ExpressionType", (int32*)&expressionType);
6250 	message->FindString("Expression", &expressionPointer);
6251 	message->FindBool("InvertSelection", &invertSelection);
6252 	message->FindBool("IgnoreCase", &ignoreCase);
6253 
6254 	expression = expressionPointer;
6255 
6256 	PoseList* poseList = CurrentPoseList();
6257 	int32 count = poseList->CountItems();
6258 	TrackerString name;
6259 
6260 	RegExp regExpression;
6261 
6262 	// Make sure we don't have any errors in the expression
6263 	// before we match the names:
6264 	if (expressionType == kRegexpMatch) {
6265 		regExpression.SetTo(expression);
6266 
6267 		if (regExpression.InitCheck() != B_OK) {
6268 			BString message(
6269 				B_TRANSLATE("Error in regular expression:\n\n'%errstring'"));
6270 			message.ReplaceFirst("%errstring", regExpression.ErrorString());
6271 			BAlert* alert = new BAlert("", message.String(), B_TRANSLATE("OK"),
6272 				NULL, NULL,	B_WIDTH_AS_USUAL, B_STOP_ALERT);
6273 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
6274 			alert->Go();
6275 			return 0;
6276 		}
6277 	}
6278 
6279 	// There is room for optimizations here: If regexp-type match, the Matches()
6280 	// function compiles the expression for every entry. One could use
6281 	// TrackerString::CompileRegExp and reuse the expression. However, then we
6282 	// have to take care of the case sensitivity ourselves.
6283 	for (int32 index = 0; index < count; index++) {
6284 		BPose* pose = poseList->ItemAt(index);
6285 		name = pose->TargetModel()->Name();
6286 		if (name.Matches(expression.String(), !ignoreCase, expressionType) ^ invertSelection) {
6287 			matchCount++;
6288 			AddPoseToSelection(pose, index);
6289 		}
6290 	}
6291 
6292 	Window()->Activate();
6293 		// Make sure the window is activated for
6294 		// subsequent manipulations. Esp. needed
6295 		// for the Desktop window.
6296 
6297 	return matchCount;
6298 }
6299 
6300 
6301 void
6302 BPoseView::ShowSelectionWindow()
6303 {
6304 	Window()->PostMessage(kShowSelectionWindow);
6305 }
6306 
6307 
6308 void
6309 BPoseView::KeyDown(const char* bytes, int32 count)
6310 {
6311 	char key = bytes[0];
6312 
6313 	switch (key) {
6314 		case B_LEFT_ARROW:
6315 		case B_RIGHT_ARROW:
6316 		case B_UP_ARROW:
6317 		case B_DOWN_ARROW:
6318 		{
6319 			int32 index;
6320 			BPose* pose = FindNearbyPose(key, &index);
6321 			if (pose == NULL)
6322 				break;
6323 
6324 			if (fMultipleSelection && modifiers() & B_SHIFT_KEY) {
6325 				if (pose->IsSelected()) {
6326 					RemovePoseFromSelection(fSelectionList->LastItem());
6327 					fSelectionPivotPose = pose;
6328 					ScrollIntoView(pose, index);
6329 				} else
6330 					AddPoseToSelection(pose, index, true);
6331 			} else
6332 				SelectPose(pose, index);
6333 			break;
6334 		}
6335 
6336 		case B_RETURN:
6337 			if (fFiltering && fSelectionList->CountItems() == 0)
6338 				SelectPose(fFilteredPoseList->FirstItem(), 0);
6339 
6340 			OpenSelection();
6341 
6342 			if (fFiltering && (modifiers() & B_SHIFT_KEY) != 0)
6343 				StopFiltering();
6344 			break;
6345 
6346 		case B_HOME:
6347 			// select the first entry (if in listview mode), and
6348 			// scroll to the top of the view
6349 			if (ViewMode() == kListMode) {
6350 				PoseList* poseList = CurrentPoseList();
6351 				BPose* pose = fSelectionList->LastItem();
6352 
6353 				if (pose != NULL && fMultipleSelection && (modifiers() & B_SHIFT_KEY) != 0) {
6354 					int32 index = poseList->IndexOf(pose);
6355 
6356 					// select all items from the current one till the top
6357 					for (int32 i = index; i-- > 0; ) {
6358 						pose = poseList->ItemAt(i);
6359 						if (pose == NULL)
6360 							continue;
6361 
6362 						if (!pose->IsSelected())
6363 							AddPoseToSelection(pose, i, i == 0);
6364 					}
6365 				} else
6366 					SelectPose(poseList->FirstItem(), 0);
6367 
6368 			} else if (fVScrollBar)
6369 				fVScrollBar->SetValue(0);
6370 			break;
6371 
6372 		case B_END:
6373 			// select the last entry (if in listview mode), and
6374 			// scroll to the bottom of the view
6375 			if (ViewMode() == kListMode) {
6376 				PoseList* poseList = CurrentPoseList();
6377 				BPose* pose = fSelectionList->FirstItem();
6378 
6379 				if (pose != NULL && fMultipleSelection && (modifiers() & B_SHIFT_KEY) != 0) {
6380 					int32 index = poseList->IndexOf(pose);
6381 					int32 count = poseList->CountItems() - 1;
6382 
6383 					// select all items from the current one to the bottom
6384 					for (int32 i = index; i <= count; i++) {
6385 						pose = poseList->ItemAt(i);
6386 						if (pose == NULL)
6387 							continue;
6388 
6389 						if (!pose->IsSelected())
6390 							AddPoseToSelection(pose, i, i == count);
6391 					}
6392 				} else
6393 					SelectPose(poseList->LastItem(), poseList->CountItems() - 1);
6394 
6395 			} else if (fVScrollBar) {
6396 				float max, min;
6397 				fVScrollBar->GetRange(&min, &max);
6398 				fVScrollBar->SetValue(max);
6399 			}
6400 			break;
6401 
6402 		case B_PAGE_UP:
6403 			if (fVScrollBar) {
6404 				float max, min;
6405 				fVScrollBar->GetSteps(&min, &max);
6406 				fVScrollBar->SetValue(fVScrollBar->Value() - max);
6407 			}
6408 			break;
6409 
6410 		case B_PAGE_DOWN:
6411 			if (fVScrollBar) {
6412 				float max, min;
6413 				fVScrollBar->GetSteps(&min, &max);
6414 				fVScrollBar->SetValue(fVScrollBar->Value() + max);
6415 			}
6416 			break;
6417 
6418 		case B_TAB:
6419 			if (IsFilePanel())
6420 				_inherited::KeyDown(bytes, count);
6421 			else {
6422 				if (ViewMode() == kListMode
6423 					&& TrackerSettings().TypeAheadFiltering()) {
6424 					break;
6425 				}
6426 
6427 				if (fSelectionList->IsEmpty())
6428 					sMatchString.Truncate(0);
6429 				else {
6430 					BPose* pose = fSelectionList->FirstItem();
6431 					sMatchString.SetTo(pose->TargetModel()->Name());
6432 				}
6433 
6434 				bool reverse = (Window()->CurrentMessage()->FindInt32("modifiers")
6435 					& B_SHIFT_KEY) != 0;
6436 				int32 index;
6437 				BPose* pose = FindNextMatch(&index, reverse);
6438 				if (!pose) {		// wrap around
6439 					if (reverse)
6440 						sMatchString.SetTo(0x7f, 1);
6441 					else
6442 						sMatchString.Truncate(0);
6443 
6444 					pose = FindNextMatch(&index, reverse);
6445 				}
6446 
6447 				SelectPose(pose, index);
6448 			}
6449 			break;
6450 
6451 		case B_DELETE:
6452 		{
6453 			if (TargetModel() == NULL) {
6454 				// Happens if called from within OpenWith window, for example
6455 				break;
6456 			}
6457 			// Make sure user can't trash something already in the trash.
6458 			if (TargetModel()->IsTrash()) {
6459 				// Delete without asking from the trash
6460 				DeleteSelection(true, false);
6461 			} else {
6462 				TrackerSettings settings;
6463 
6464 				if ((modifiers() & B_SHIFT_KEY) != 0 || !settings.MoveFilesToTrash())
6465 					DeleteSelection(true, settings.AskBeforeDeleteFile());
6466 				else
6467 					MoveSelectionToTrash();
6468 			}
6469 			break;
6470 		}
6471 
6472 		case B_BACKSPACE:
6473 		{
6474 			if (fFiltering) {
6475 				BString* lastString = fFilterStrings.LastItem();
6476 				if (lastString->Length() == 0) {
6477 					int32 stringCount = fFilterStrings.CountItems();
6478 					if (stringCount > 1)
6479 						delete fFilterStrings.RemoveItemAt(stringCount - 1);
6480 					else
6481 						break;
6482 				} else
6483 					lastString->TruncateChars(lastString->CountChars() - 1);
6484 
6485 				fCountView->RemoveFilterCharacter();
6486 				FilterChanged();
6487 				break;
6488 			}
6489 
6490 			if (sMatchString.Length() == 0)
6491 				break;
6492 
6493 			// remove last char from the typeahead buffer
6494 			sMatchString.TruncateChars(sMatchString.CountChars() - 1);
6495 
6496 			fLastKeyTime = system_time();
6497 
6498 			fCountView->SetTypeAhead(sMatchString.String());
6499 
6500 			// select our new string
6501 			int32 index;
6502 			BPose* pose = FindBestMatch(&index);
6503 			if (!pose)
6504 				break;
6505 
6506 			SelectPose(pose, index);
6507 			break;
6508 		}
6509 
6510 		case B_FUNCTION_KEY:
6511 			if (BMessage* message = Window()->CurrentMessage()) {
6512 				int32 key;
6513 				if (message->FindInt32("key", &key) == B_OK) {
6514 					switch (key) {
6515 						case B_F2_KEY:
6516 							Window()->PostMessage(kEditItem, this);
6517 							break;
6518 						default:
6519 							break;
6520 					}
6521 				}
6522 			}
6523 			break;
6524 
6525 		case B_INSERT:
6526 			break;
6527 
6528 		default:
6529 		{
6530 			// handle typeahead selection / filtering
6531 
6532 			if (ViewMode() == kListMode
6533 				&& TrackerSettings().TypeAheadFiltering()) {
6534 				if (key == ' ' && modifiers() & B_SHIFT_KEY) {
6535 					if (fFilterStrings.LastItem()->Length() == 0)
6536 						break;
6537 
6538 					fFilterStrings.AddItem(new BString());
6539 					fCountView->AddFilterCharacter("|");
6540 					break;
6541 				}
6542 
6543 				fFilterStrings.LastItem()->AppendChars(bytes, 1);
6544 				fCountView->AddFilterCharacter(bytes);
6545 				FilterChanged();
6546 				break;
6547 			}
6548 
6549 			bigtime_t doubleClickSpeed;
6550 			get_click_speed(&doubleClickSpeed);
6551 
6552 			// start watching
6553 			if (fKeyRunner == NULL) {
6554 				fKeyRunner = new BMessageRunner(this, new BMessage(kCheckTypeahead), doubleClickSpeed);
6555 				if (fKeyRunner->InitCheck() != B_OK)
6556 					return;
6557 			}
6558 
6559 			// figure out the time at which the keypress happened
6560 			bigtime_t eventTime;
6561 			BMessage* message = Window()->CurrentMessage();
6562 			if (!message || message->FindInt64("when", &eventTime) < B_OK) {
6563 				eventTime = system_time();
6564 			}
6565 
6566 			// add char to existing matchString or start new match string
6567 			if (eventTime - fLastKeyTime < (doubleClickSpeed * 2))
6568 				sMatchString.AppendChars(bytes, 1);
6569 			else
6570 				sMatchString.SetToChars(bytes, 1);
6571 
6572 			fLastKeyTime = eventTime;
6573 
6574 			fCountView->SetTypeAhead(sMatchString.String());
6575 
6576 			int32 index;
6577 			BPose* pose = FindBestMatch(&index);
6578 			if (!pose)
6579 				break;
6580 
6581 			SelectPose(pose, index);
6582 			break;
6583 		}
6584 	}
6585 }
6586 
6587 
6588 BPose*
6589 BPoseView::FindNextMatch(int32* matchingIndex, bool reverse)
6590 {
6591 	char bestSoFar[B_FILE_NAME_LENGTH] = { 0 };
6592 	BPose* poseToSelect = NULL;
6593 
6594 	// loop through all poses to find match
6595 	int32 count = fPoseList->CountItems();
6596 	for (int32 index = 0; index < count; index++) {
6597 		BPose* pose = fPoseList->ItemAt(index);
6598 
6599 		if (reverse) {
6600 			if (sMatchString.ICompare(pose->TargetModel()->Name()) > 0)
6601 				if (strcasecmp(pose->TargetModel()->Name(), bestSoFar) >= 0
6602 					|| !bestSoFar[0]) {
6603 					strcpy(bestSoFar, pose->TargetModel()->Name());
6604 					poseToSelect = pose;
6605 					*matchingIndex = index;
6606 				}
6607 		} else if (sMatchString.ICompare(pose->TargetModel()->Name()) < 0)
6608 			if (strcasecmp(pose->TargetModel()->Name(), bestSoFar) <= 0
6609 				|| !bestSoFar[0]) {
6610 				strcpy(bestSoFar, pose->TargetModel()->Name());
6611 				poseToSelect = pose;
6612 				*matchingIndex = index;
6613 			}
6614 
6615 	}
6616 
6617 	return poseToSelect;
6618 }
6619 
6620 
6621 BPose*
6622 BPoseView::FindBestMatch(int32* index)
6623 {
6624 	BPose* poseToSelect = NULL;
6625 	float bestScore = -1;
6626 	int32 count = fPoseList->CountItems();
6627 
6628 	// loop through all poses to find match
6629 	for (int32 j = 0; j < CountColumns(); j++) {
6630 		BColumn* column = ColumnAt(j);
6631 
6632 		for (int32 i = 0; i < count; i++) {
6633 			BPose* pose = fPoseList->ItemAt(i);
6634 			float score = -1;
6635 
6636 			if (ViewMode() == kListMode) {
6637 				ModelNodeLazyOpener modelOpener(pose->TargetModel());
6638 				BTextWidget* widget = pose->WidgetFor(column, this, modelOpener);
6639 				const char* text = NULL;
6640 				if (widget != NULL)
6641 					text = widget->Text(this);
6642 
6643 				if (text != NULL) {
6644 					score = ComputeTypeAheadScore(text, sMatchString.String());
6645 				}
6646 			} else {
6647 				score = ComputeTypeAheadScore(pose->TargetModel()->Name(),
6648 					sMatchString.String());
6649 			}
6650 
6651 			if (score > bestScore) {
6652 				poseToSelect = pose;
6653 				bestScore = score;
6654 				*index = i;
6655 			}
6656 			if (score == kExactMatchScore)
6657 				break;
6658 		}
6659 
6660 		// TODO: we might want to change this to make it always work
6661 		// over all columns, but this would require some more changes
6662 		// to how Tracker represents data (for example we could filter
6663 		// the results out).
6664 		if (bestScore > 0 || ViewMode() != kListMode)
6665 			break;
6666 	}
6667 
6668 	return poseToSelect;
6669 }
6670 
6671 
6672 static bool
6673 LinesIntersect(float s1, float e1, float s2, float e2)
6674 {
6675 	return std::max(s1, s2) < std::min(e1, e2);
6676 }
6677 
6678 
6679 BPose*
6680 BPoseView::FindNearbyPose(char arrowKey, int32* poseIndex)
6681 {
6682 	int32 resultingIndex = -1;
6683 	BPose* poseToSelect = NULL;
6684 	BPose* selectedPose = fSelectionList->LastItem();
6685 
6686 	if (ViewMode() == kListMode) {
6687 		PoseList* poseList = CurrentPoseList();
6688 
6689 		switch (arrowKey) {
6690 			case B_UP_ARROW:
6691 			case B_LEFT_ARROW:
6692 				if (selectedPose) {
6693 					resultingIndex = poseList->IndexOf(selectedPose) - 1;
6694 					poseToSelect = poseList->ItemAt(resultingIndex);
6695 					if (!poseToSelect && arrowKey == B_LEFT_ARROW) {
6696 						resultingIndex = poseList->CountItems() - 1;
6697 						poseToSelect = poseList->LastItem();
6698 					}
6699 				} else {
6700 					resultingIndex = poseList->CountItems() - 1;
6701 					poseToSelect = poseList->LastItem();
6702 				}
6703 				break;
6704 
6705 			case B_DOWN_ARROW:
6706 			case B_RIGHT_ARROW:
6707 				if (selectedPose) {
6708 					resultingIndex = poseList->IndexOf(selectedPose) + 1;
6709 					poseToSelect = poseList->ItemAt(resultingIndex);
6710 					if (!poseToSelect && arrowKey == B_RIGHT_ARROW) {
6711 						resultingIndex = 0;
6712 						poseToSelect = poseList->FirstItem();
6713 					}
6714 				} else {
6715 					resultingIndex = 0;
6716 					poseToSelect = poseList->FirstItem();
6717 				}
6718 				break;
6719 		}
6720 		*poseIndex = resultingIndex;
6721 		return poseToSelect;
6722 	}
6723 
6724 	// must be in one of the icon modes
6725 
6726 	// handle case where there is no current selection
6727 	if (fSelectionList->IsEmpty()) {
6728 		// find the upper-left pose (I know it's ugly!)
6729 		poseToSelect = fVSPoseList->FirstItem();
6730 		for (int32 index = 0; ;index++) {
6731 			BPose* pose = fVSPoseList->ItemAt(++index);
6732 			if (!pose)
6733 				break;
6734 
6735 			BRect selectedBounds(poseToSelect->CalcRect(this));
6736 			BRect poseRect(pose->CalcRect(this));
6737 
6738 			if (poseRect.top > selectedBounds.top)
6739 				break;
6740 
6741 			if (poseRect.left < selectedBounds.left)
6742 				poseToSelect = pose;
6743 		}
6744 
6745 		return poseToSelect;
6746 	}
6747 
6748 	BRect selectionRect(selectedPose->CalcRect(this));
6749 	BRect bestRect;
6750 
6751 	// we're not in list mode so scan visually for pose to select
6752 	int32 count = fPoseList->CountItems();
6753 	for (int32 index = 0; index < count; index++) {
6754 		BPose* pose = fPoseList->ItemAt(index);
6755 		BRect poseRect(pose->CalcRect(this));
6756 
6757 		switch (arrowKey) {
6758 			case B_LEFT_ARROW:
6759 				if (LinesIntersect(poseRect.top, poseRect.bottom,
6760 						   selectionRect.top, selectionRect.bottom))
6761 					if (poseRect.left < selectionRect.left)
6762 						if (poseRect.left > bestRect.left
6763 							|| !bestRect.IsValid()) {
6764 							bestRect = poseRect;
6765 							poseToSelect = pose;
6766 						}
6767 				break;
6768 
6769 			case B_RIGHT_ARROW:
6770 				if (LinesIntersect(poseRect.top, poseRect.bottom,
6771 						   selectionRect.top, selectionRect.bottom))
6772 					if (poseRect.right > selectionRect.right)
6773 						if (poseRect.right < bestRect.right
6774 							|| !bestRect.IsValid()) {
6775 							bestRect = poseRect;
6776 							poseToSelect = pose;
6777 						}
6778 				break;
6779 
6780 			case B_UP_ARROW:
6781 				if (LinesIntersect(poseRect.left, poseRect.right,
6782 						   selectionRect.left, selectionRect.right))
6783 					if (poseRect.top < selectionRect.top)
6784 						if (poseRect.top > bestRect.top
6785 							|| !bestRect.IsValid()) {
6786 							bestRect = poseRect;
6787 							poseToSelect = pose;
6788 						}
6789 				break;
6790 
6791 			case B_DOWN_ARROW:
6792 				if (LinesIntersect(poseRect.left, poseRect.right,
6793 						   selectionRect.left, selectionRect.right))
6794 					if (poseRect.bottom > selectionRect.bottom)
6795 						if (poseRect.bottom < bestRect.bottom
6796 							|| !bestRect.IsValid()) {
6797 							bestRect = poseRect;
6798 							poseToSelect = pose;
6799 						}
6800 				break;
6801 		}
6802 	}
6803 
6804 	if (poseToSelect)
6805 		return poseToSelect;
6806 
6807 	return selectedPose;
6808 }
6809 
6810 
6811 void
6812 BPoseView::ShowContextMenu(BPoint where)
6813 {
6814 	BContainerWindow* window = ContainerWindow();
6815 	if (!window)
6816 		return;
6817 
6818 	// handle pose selection
6819 	int32 index;
6820 	BPose* pose = FindPose(where, &index);
6821 	if (pose) {
6822 		if (!pose->IsSelected()) {
6823 			ClearSelection();
6824 			pose->Select(true);
6825 			fSelectionList->AddItem(pose);
6826 			DrawPose(pose, index, false);
6827 		}
6828 	} else
6829 		ClearSelection();
6830 
6831 	window->Activate();
6832 	window->UpdateIfNeeded();
6833 	window->ShowContextMenu(where, pose ? pose->TargetModel()->EntryRef() : 0,
6834 		this);
6835 
6836 	if (fSelectionChangedHook)
6837 		window->SelectionChanged();
6838 }
6839 
6840 
6841 void
6842 BPoseView::_BeginSelectionRect(const BPoint& point, bool shouldExtend)
6843 {
6844 	// set initial empty selection rectangle
6845 	fSelectionRectInfo.rect = BRect(point, point - BPoint(1, 1));
6846 
6847 	if (!fTransparentSelection) {
6848 		SetDrawingMode(B_OP_INVERT);
6849 		StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS);
6850 		SetDrawingMode(B_OP_OVER);
6851 	}
6852 
6853 	fSelectionRectInfo.lastRect = fSelectionRectInfo.rect;
6854 	fSelectionRectInfo.selection = new BList;
6855 	fSelectionRectInfo.startPoint = point;
6856 	fSelectionRectInfo.lastPoint = point;
6857 	fSelectionRectInfo.isDragging = true;
6858 
6859 	if (fAutoScrollState == kAutoScrollOff) {
6860 		fAutoScrollState = kAutoScrollOn;
6861 		Window()->SetPulseRate(20000);
6862 	}
6863 }
6864 
6865 
6866 static void
6867 AddIfPoseSelected(BPose* pose, PoseList* list)
6868 {
6869 	if (pose->IsSelected())
6870 		list->AddItem(pose);
6871 }
6872 
6873 
6874 void
6875 BPoseView::_UpdateSelectionRect(const BPoint& point)
6876 {
6877 	if (point != fSelectionRectInfo.lastPoint) {
6878 
6879 		fSelectionRectInfo.lastPoint = point;
6880 
6881 		// erase last rect
6882 		if (!fTransparentSelection) {
6883 			SetDrawingMode(B_OP_INVERT);
6884 			StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS);
6885 			SetDrawingMode(B_OP_OVER);
6886 		}
6887 
6888 		fSelectionRectInfo.rect.top = std::min(point.y,
6889 			fSelectionRectInfo.startPoint.y);
6890 		fSelectionRectInfo.rect.left = std::min(point.x,
6891 			fSelectionRectInfo.startPoint.x);
6892 		fSelectionRectInfo.rect.bottom = std::max(point.y,
6893 			fSelectionRectInfo.startPoint.y);
6894 		fSelectionRectInfo.rect.right = std::max(point.x,
6895 			fSelectionRectInfo.startPoint.x);
6896 
6897 		fIsDrawingSelectionRect = true;
6898 
6899 		// use current selection rectangle to scan poses
6900 		if (ViewMode() == kListMode) {
6901 			SelectPosesListMode(fSelectionRectInfo.rect,
6902 				&fSelectionRectInfo.selection);
6903 		} else {
6904 			SelectPosesIconMode(fSelectionRectInfo.rect,
6905 				&fSelectionRectInfo.selection);
6906 		}
6907 
6908 		Window()->UpdateIfNeeded();
6909 
6910 		// draw new rect
6911 		if (!fTransparentSelection) {
6912 			SetDrawingMode(B_OP_INVERT);
6913 			StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS);
6914 			SetDrawingMode(B_OP_OVER);
6915 		} else {
6916 			BRegion updateRegion1;
6917 			BRegion updateRegion2;
6918 
6919 			bool sameWidth = fSelectionRectInfo.rect.Width()
6920 				== fSelectionRectInfo.lastRect.Width();
6921 			bool sameHeight = fSelectionRectInfo.rect.Height()
6922 				== fSelectionRectInfo.lastRect.Height();
6923 
6924 			updateRegion1.Include(fSelectionRectInfo.rect);
6925 			updateRegion1.Exclude(fSelectionRectInfo.lastRect.InsetByCopy(
6926 				sameWidth ? 0 : 1, sameHeight ? 0 : 1));
6927 			updateRegion2.Include(fSelectionRectInfo.lastRect);
6928 			updateRegion2.Exclude(fSelectionRectInfo.rect.InsetByCopy(
6929 				sameWidth ? 0 : 1, sameHeight ? 0 : 1));
6930 			updateRegion1.Include(&updateRegion2);
6931 			BRect unionRect = fSelectionRectInfo.rect
6932 				& fSelectionRectInfo.lastRect;
6933 			updateRegion1.Exclude(unionRect
6934 				& BRect(-2000, fSelectionRectInfo.startPoint.y, 2000,
6935 				fSelectionRectInfo.startPoint.y));
6936 			updateRegion1.Exclude(unionRect
6937 				& BRect(fSelectionRectInfo.startPoint.x, -2000,
6938 				fSelectionRectInfo.startPoint.x, 2000));
6939 
6940 			fSelectionRectInfo.lastRect = fSelectionRectInfo.rect;
6941 
6942 			Invalidate(&updateRegion1);
6943 			Window()->UpdateIfNeeded();
6944 		}
6945 		Flush();
6946 	}
6947 }
6948 
6949 
6950 void
6951 BPoseView::_EndSelectionRect()
6952 {
6953 	delete fSelectionRectInfo.selection;
6954 	fSelectionRectInfo.selection = NULL;
6955 
6956 	fSelectionRectInfo.isDragging = false;
6957 	fIsDrawingSelectionRect = false; // TODO: remove BPose dependency?
6958 
6959 	// do final erase of selection rect
6960 	if (!fTransparentSelection) {
6961 		SetDrawingMode(B_OP_INVERT);
6962 		StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS);
6963 		SetDrawingMode(B_OP_COPY);
6964 		fSelectionRectInfo.rect.Set(0, 0, -1, -1);
6965 	} else {
6966 		Invalidate(fSelectionRectInfo.rect);
6967 		fSelectionRectInfo.rect.Set(0, 0, -1, -1);
6968 		Window()->UpdateIfNeeded();
6969 	}
6970 
6971 	// we now need to update the pose view's selection list by clearing it
6972 	// and then polling each pose for selection state and rebuilding list
6973 	fSelectionList->MakeEmpty();
6974 	fMimeTypesInSelectionCache.MakeEmpty();
6975 
6976 	EachListItem(fPoseList, AddIfPoseSelected, fSelectionList);
6977 
6978 	// and now make sure that the pivot point is in sync
6979 	if (fSelectionPivotPose && !fSelectionList->HasItem(fSelectionPivotPose))
6980 		fSelectionPivotPose = NULL;
6981 	if (fRealPivotPose && !fSelectionList->HasItem(fRealPivotPose))
6982 		fRealPivotPose = NULL;
6983 }
6984 
6985 
6986 void
6987 BPoseView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
6988 {
6989 	if (fSelectionRectInfo.isDragging)
6990 		_UpdateSelectionRect(where);
6991 
6992 	if (!fDropEnabled || dragMessage == NULL)
6993 		return;
6994 
6995 	BContainerWindow* window = ContainerWindow();
6996 	if (!window)
6997 		return;
6998 
6999 	if (!window->Dragging())
7000 		window->DragStart(dragMessage);
7001 
7002 	switch (transit) {
7003 		case B_INSIDE_VIEW:
7004 		case B_ENTERED_VIEW:
7005 			UpdateDropTarget(where, dragMessage, window->ContextMenu());
7006 			if (fAutoScrollState == kAutoScrollOff) {
7007 				// turn on auto scrolling if it's not yet on
7008 				fAutoScrollState = kWaitForTransition;
7009 				window->SetPulseRate(100000);
7010 			}
7011 			break;
7012 
7013 		case B_EXITED_VIEW:
7014 			DragStop();
7015 			// reset cursor in case we set it to the copy cursor
7016 			// in UpdateDropTarget
7017 			SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
7018 			fCursorCheck = false;
7019 			// TODO: autoscroll here
7020 			if (!window->ContextMenu()) {
7021 				HiliteDropTarget(false);
7022 				fDropTarget = NULL;
7023 			}
7024 			break;
7025 	}
7026 }
7027 
7028 
7029 void
7030 BPoseView::MouseDragged(const BMessage* message)
7031 {
7032 	if (fTextWidgetToCheck != NULL)
7033 		fTextWidgetToCheck->CancelWait();
7034 	fTrackRightMouseUp = false;
7035 
7036 	BPoint where;
7037 	uint32 buttons = 0;
7038 	if (message->FindPoint("be:view_where", &where) != B_OK
7039 		|| message->FindInt32("buttons", (int32*)&buttons) != B_OK)
7040 		return;
7041 
7042 	bool extendSelection = (modifiers() & B_COMMAND_KEY) && fMultipleSelection;
7043 
7044 	int32 index;
7045 	BPose* pose = FindPose(where, &index);
7046 	if (pose != NULL)
7047 		DragSelectedPoses(pose, where);
7048 	else if (buttons == B_PRIMARY_MOUSE_BUTTON)
7049 		_BeginSelectionRect(where, extendSelection);
7050 }
7051 
7052 
7053 void
7054 BPoseView::MouseLongDown(const BMessage* message)
7055 {
7056 	fTrackRightMouseUp = false;
7057 
7058 	BPoint where;
7059 	if (message->FindPoint("where", &where) != B_OK)
7060 		return;
7061 
7062 	ShowContextMenu(where);
7063 }
7064 
7065 
7066 void
7067 BPoseView::MouseIdle(const BMessage* message)
7068 {
7069 	BPoint where;
7070 	uint32 buttons = 0;
7071 	GetMouse(&where, &buttons);
7072 		// We could retrieve 'where' from the incoming
7073 		// message but we need the buttons state anyway
7074 		// and B_MOUSE_IDLE message doesn't pass it
7075 	BContainerWindow* window = ContainerWindow();
7076 
7077 	if (buttons == 0 || window == NULL || !window->Dragging())
7078 		return;
7079 
7080 	if (fDropTarget != NULL) {
7081 		FrameForPose(fDropTarget, true, &fStartFrame);
7082 		ShowContextMenu(where);
7083 	} else
7084 		window->Activate();
7085 }
7086 
7087 
7088 void
7089 BPoseView::MouseDown(BPoint where)
7090 {
7091 	// handle disposing of drag data lazily
7092 	DragStop();
7093 	BContainerWindow* window = ContainerWindow();
7094 	if (!window)
7095 		return;
7096 
7097 	if (IsDesktopWindow()) {
7098 		BScreen	screen(Window());
7099 		rgb_color color = screen.DesktopColor();
7100 		SetLowColor(color);
7101 		SetViewColor(color);
7102 	}
7103 
7104 	MakeFocus();
7105 
7106 	uint32 buttons = (uint32)window->CurrentMessage()->FindInt32("buttons");
7107 	uint32 modifs = modifiers();
7108 
7109 	fTrackRightMouseUp = (buttons == B_SECONDARY_MOUSE_BUTTON);
7110 
7111 	bool extendSelection = (modifs & B_COMMAND_KEY) && fMultipleSelection;
7112 
7113 	CommitActivePose();
7114 
7115 	int32 index;
7116 	BPose* pose = FindPose(where, &index);
7117 	if (pose) {
7118 		AddRemoveSelectionRange(where, extendSelection, pose);
7119 
7120 		if (fTextWidgetToCheck != NULL && (pose != fLastClickedPose
7121 				|| (buttons & B_SECONDARY_MOUSE_BUTTON) != 0))
7122 			fTextWidgetToCheck->CancelWait();
7123 
7124 		if (!extendSelection && !fTrackRightMouseUp && WasDoubleClick(pose, where)) {
7125 			// special handling for Path field double-clicks
7126 			if (!WasClickInPath(pose, index, where))
7127 				OpenSelection(pose, &index);
7128 		}
7129 	} else {
7130 		// click was not in any pose
7131 		fLastClickedPose = NULL;
7132 		if (fTextWidgetToCheck != NULL)
7133 			fTextWidgetToCheck->CancelWait();
7134 
7135 		window->Activate();
7136 		window->UpdateIfNeeded();
7137 
7138 		// only clear selection if we are not extending it
7139 		if (!extendSelection || !fSelectionRectEnabled || !fMultipleSelection)
7140 			ClearSelection();
7141 
7142 		// show desktop context menu
7143 		if (buttons == B_SECONDARY_MOUSE_BUTTON
7144 			|| (modifs & B_CONTROL_KEY) != 0) {
7145 			ShowContextMenu(where);
7146 		}
7147 	}
7148 
7149 	if (fSelectionChangedHook)
7150 		window->SelectionChanged();
7151 }
7152 
7153 
7154 void
7155 BPoseView::SetTextWidgetToCheck(BTextWidget* widget, BTextWidget* old)
7156 {
7157 	if (old == NULL || fTextWidgetToCheck == old)
7158 		fTextWidgetToCheck = widget;
7159 }
7160 
7161 
7162 void
7163 BPoseView::MouseUp(BPoint where)
7164 {
7165 	if (fSelectionRectInfo.isDragging)
7166 		_EndSelectionRect();
7167 
7168 	int32 index;
7169 	BPose* pose = FindPose(where, &index);
7170 	uint32 lastButtons = Window()->CurrentMessage()->FindInt32("last_buttons");
7171 	if (pose != NULL && fLastClickedPose != NULL && fAllowPoseEditing
7172 		&& !fTrackRightMouseUp)
7173 		pose->MouseUp(BPoint(0, index * fListElemHeight), this, where, index);
7174 
7175 
7176 		// this handy field has been added by the tracking filter.
7177 		// we need lastButtons for right button mouse-up tracking,
7178 		// because there's currently no way to know wich buttons were
7179 		// released in BView::MouseUp (unlike BView::KeyUp)
7180 
7181 	// Showing the pose context menu is done on mouse up (or long click)
7182 	// to make right button dragging possible
7183 	if (pose != NULL && fTrackRightMouseUp
7184 		&& (lastButtons == B_SECONDARY_MOUSE_BUTTON
7185 			|| (modifiers() & B_CONTROL_KEY) != 0)) {
7186 		if (!pose->IsSelected()) {
7187 			ClearSelection();
7188 			pose->Select(true);
7189 			fSelectionList->AddItem(pose);
7190 			DrawPose(pose, index, false);
7191 		}
7192 		ShowContextMenu(where);
7193 	}
7194 	fTrackRightMouseUp = false;
7195 }
7196 
7197 
7198 bool
7199 BPoseView::WasClickInPath(const BPose* pose, int32 index, BPoint mouseLoc) const
7200 {
7201 	if (!pose || (ViewMode() != kListMode))
7202 		return false;
7203 
7204 	BPoint loc(0, index * fListElemHeight);
7205 	BTextWidget* widget;
7206 	if (!pose->PointInPose(loc, this, mouseLoc, &widget) || !widget)
7207 		return false;
7208 
7209 	// note: the following code is wrong, because this sort of hashing
7210 	// may overlap and we get aliasing
7211 	if (widget->AttrHash() != AttrHashString(kAttrPath, B_STRING_TYPE))
7212 		return false;
7213 
7214 	BEntry entry(widget->Text(this));
7215 	if (entry.InitCheck() != B_OK)
7216 		return false;
7217 
7218 	entry_ref ref;
7219 	if (entry.GetRef(&ref) == B_OK) {
7220 		BMessage message(B_REFS_RECEIVED);
7221 		message.AddRef("refs", &ref);
7222 		be_app->PostMessage(&message);
7223 		return true;
7224 	}
7225 
7226 	return false;
7227 }
7228 
7229 
7230 bool
7231 BPoseView::WasDoubleClick(const BPose* pose, BPoint point)
7232 {
7233 	// check proximity
7234 	BPoint delta = point - fLastClickPt;
7235 	int32 clicks = Window()->CurrentMessage()->FindInt32("clicks");
7236 
7237 	if (clicks == 2
7238 		&& fabs(delta.x) < kDoubleClickTresh
7239 		&& fabs(delta.y) < kDoubleClickTresh
7240 		&& pose == fLastClickedPose) {
7241 		fLastClickPt.Set(INT32_MAX, INT32_MAX);
7242 		fLastClickedPose = NULL;
7243 		if (fTextWidgetToCheck != NULL)
7244 			fTextWidgetToCheck->CancelWait();
7245 		return true;
7246 	}
7247 
7248 	fLastClickPt = point;
7249 	fLastClickedPose = pose;
7250 	return false;
7251 }
7252 
7253 
7254 static void
7255 AddPoseRefToMessage(BPose*, Model* model, BMessage* message)
7256 {
7257 	// Make sure that every file added to the message has its
7258 	// MIME type set.
7259 	BNode node(model->EntryRef());
7260 	if (node.InitCheck() == B_OK) {
7261 		BNodeInfo info(&node);
7262 		char type[B_MIME_TYPE_LENGTH];
7263 		type[0] = '\0';
7264 		if (info.GetType(type) != B_OK) {
7265 			BPath path(model->EntryRef());
7266 			if (path.InitCheck() == B_OK)
7267 				update_mime_info(path.Path(), false, false, false);
7268 		}
7269 	}
7270 	message->AddRef("refs", model->EntryRef());
7271 }
7272 
7273 
7274 void
7275 BPoseView::DragSelectedPoses(const BPose* pose, BPoint clickPoint)
7276 {
7277 	if (!fDragEnabled)
7278 		return;
7279 
7280 	ASSERT(pose);
7281 
7282 	// make sure pose is selected, it could have been deselected as part of
7283 	// a click during selection extention
7284 	if (!pose->IsSelected())
7285 		return;
7286 
7287 	// setup tracking rect by unioning all selected pose rects
7288 	BMessage message(B_SIMPLE_DATA);
7289 	message.AddPointer("src_window", Window());
7290 	message.AddPoint("click_pt", clickPoint);
7291 
7292 	// add Tracker token so that refs received recipients can script us
7293 	message.AddMessenger("TrackerViewToken", BMessenger(this));
7294 
7295 	EachPoseAndModel(fSelectionList, &AddPoseRefToMessage, &message);
7296 
7297 	// make sure button is still down
7298 	uint32 button;
7299 	BPoint tempLoc;
7300 	GetMouse(&tempLoc, &button);
7301 	if (button) {
7302 		int32 index = CurrentPoseList()->IndexOf(pose);
7303 		message.AddInt32("buttons", (int32)button);
7304 		BRect dragRect(GetDragRect(index));
7305 		BBitmap* dragBitmap = NULL;
7306 		BPoint offset;
7307 
7308 		// The bitmap is now always created (if DRAG_FRAME is not defined)
7309 
7310 #ifdef DRAG_FRAME
7311 		if (dragRect.Width() < kTransparentDragThreshold.x
7312 			&& dragRect.Height() < kTransparentDragThreshold.y)
7313 #endif
7314 			dragBitmap = MakeDragBitmap(dragRect, clickPoint, index, offset);
7315 
7316 		if (dragBitmap) {
7317 			DragMessage(&message, dragBitmap, B_OP_ALPHA, offset);
7318 				// this DragMessage supports alpha blending
7319 		} else
7320 			DragMessage(&message, dragRect);
7321 
7322 		// turn on auto scrolling
7323 		fAutoScrollState = kWaitForTransition;
7324 		Window()->SetPulseRate(100000);
7325 	}
7326 }
7327 
7328 
7329 BBitmap*
7330 BPoseView::MakeDragBitmap(BRect dragRect, BPoint clickedPoint,
7331 	int32 clickedPoseIndex, BPoint &offset)
7332 {
7333 	BRect inner(clickedPoint.x - kTransparentDragThreshold.x / 2,
7334 		clickedPoint.y - kTransparentDragThreshold.y / 2,
7335 		clickedPoint.x + kTransparentDragThreshold.x / 2,
7336 		clickedPoint.y + kTransparentDragThreshold.y / 2);
7337 
7338 	// (BRect & BRect) doesn't work correctly if the rectangles don't intersect
7339 	// this catches a bug that is produced somewhere before this function is
7340 	// called
7341 	if (!inner.Intersects(dragRect))
7342 		return NULL;
7343 
7344 	inner = inner & dragRect;
7345 
7346 	// If the selection is bigger than the specified limit, the
7347 	// contents will fade out when they come near the borders
7348 	bool fadeTop = false;
7349 	bool fadeBottom = false;
7350 	bool fadeLeft = false;
7351 	bool fadeRight = false;
7352 	bool fade = false;
7353 	if (inner.left > dragRect.left) {
7354 		inner.left = max(inner.left - 32, dragRect.left);
7355 		fade = fadeLeft = true;
7356 	}
7357 	if (inner.right < dragRect.right) {
7358 		inner.right = min(inner.right + 32, dragRect.right);
7359 		fade = fadeRight = true;
7360 	}
7361 	if (inner.top > dragRect.top) {
7362 		inner.top = max(inner.top - 32, dragRect.top);
7363 		fade = fadeTop = true;
7364 	}
7365 	if (inner.bottom < dragRect.bottom) {
7366 		inner.bottom = min(inner.bottom + 32, dragRect.bottom);
7367 		fade = fadeBottom = true;
7368 	}
7369 
7370 	// set the offset for the dragged bitmap (for the BView::DragMessage() call)
7371 	offset = clickedPoint - BPoint(2, 1) - inner.LeftTop();
7372 
7373 	BRect rect(inner);
7374 	rect.OffsetTo(B_ORIGIN);
7375 
7376 	BBitmap* bitmap = new BBitmap(rect, B_RGBA32, true);
7377 	bitmap->Lock();
7378 	BView* view = new BView(bitmap->Bounds(), "", B_FOLLOW_NONE, 0);
7379 	bitmap->AddChild(view);
7380 
7381 	view->SetOrigin(0, 0);
7382 
7383 	BRect clipRect(view->Bounds());
7384 	BRegion newClip;
7385 	newClip.Set(clipRect);
7386 	view->ConstrainClippingRegion(&newClip);
7387 
7388 	memset(bitmap->Bits(), 0, bitmap->BitsLength());
7389 
7390 	view->SetDrawingMode(B_OP_ALPHA);
7391 	view->SetHighColor(0, 0, 0, uint8(fade ? 164 : 128));
7392 		// set the level of transparency by value
7393 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
7394 
7395 	BRect bounds(Bounds());
7396 
7397 	PoseList* poseList = CurrentPoseList();
7398 	BPose* pose = poseList->ItemAt(clickedPoseIndex);
7399 	if (ViewMode() == kListMode) {
7400 		int32 count = poseList->CountItems();
7401 		int32 startIndex = (int32)(bounds.top / fListElemHeight);
7402 		BPoint loc(0, startIndex * fListElemHeight);
7403 
7404 		for (int32 index = startIndex; index < count; index++) {
7405 			pose = poseList->ItemAt(index);
7406 			if (pose->IsSelected()) {
7407 				BRect poseRect(pose->CalcRect(loc, this, true));
7408 				if (poseRect.Intersects(inner)) {
7409 					BPoint offsetBy(-inner.LeftTop().x, -inner.LeftTop().y);
7410 					pose->Draw(poseRect, poseRect, this, view, true, offsetBy,
7411 						false);
7412 				}
7413 			}
7414 			loc.y += fListElemHeight;
7415 			if (loc.y > bounds.bottom)
7416 				break;
7417 		}
7418 	} else {
7419 		// add rects for visible poses only (uses VSList!!)
7420 		int32 startIndex = FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()));
7421 		int32 count = fVSPoseList->CountItems();
7422 
7423 		for (int32 index = startIndex; index < count; index++) {
7424 			pose = fVSPoseList->ItemAt(index);
7425 			if (pose && pose->IsSelected()) {
7426 				BRect poseRect(pose->CalcRect(this));
7427 				if (!poseRect.Intersects(inner))
7428 					continue;
7429 
7430 				BPoint offsetBy(-inner.LeftTop().x, -inner.LeftTop().y);
7431 				pose->Draw(poseRect, poseRect, this, view, true, offsetBy,
7432 					false);
7433 			}
7434 		}
7435 	}
7436 
7437 	view->Sync();
7438 
7439 	// Fade out the contents if necessary
7440 	if (fade) {
7441 		uint32* bits = (uint32*)bitmap->Bits();
7442 		int32 width = bitmap->BytesPerRow() / 4;
7443 
7444 		if (fadeLeft)
7445 			FadeRGBA32Horizontal(bits, width, int32(rect.bottom), 0, 64);
7446 		if (fadeRight)
7447 			FadeRGBA32Horizontal(bits, width, int32(rect.bottom),
7448 				int32(rect.right), int32(rect.right) - 64);
7449 
7450 		if (fadeTop)
7451 			FadeRGBA32Vertical(bits, width, int32(rect.bottom), 0, 64);
7452 		if (fadeBottom)
7453 			FadeRGBA32Vertical(bits, width, int32(rect.bottom),
7454 				int32(rect.bottom), int32(rect.bottom) - 64);
7455 	}
7456 
7457 	bitmap->Unlock();
7458 	return bitmap;
7459 }
7460 
7461 
7462 BRect
7463 BPoseView::GetDragRect(int32 clickedPoseIndex)
7464 {
7465 	BRect result;
7466 	BRect bounds(Bounds());
7467 
7468 	PoseList* poseList = CurrentPoseList();
7469 	BPose* pose = poseList->ItemAt(clickedPoseIndex);
7470 	if (ViewMode() == kListMode) {
7471 		// get starting rect of clicked pose
7472 		result = CalcPoseRectList(pose, clickedPoseIndex, true);
7473 
7474 		// add rects for visible poses only
7475 		int32 count = poseList->CountItems();
7476 		int32 startIndex = (int32)(bounds.top / fListElemHeight);
7477 		BPoint loc(0, startIndex * fListElemHeight);
7478 
7479 		for (int32 index = startIndex; index < count; index++) {
7480 			pose = poseList->ItemAt(index);
7481 			if (pose->IsSelected())
7482 				result = result | pose->CalcRect(loc, this, true);
7483 
7484 			loc.y += fListElemHeight;
7485 			if (loc.y > bounds.bottom)
7486 				break;
7487 		}
7488 	} else {
7489 		// get starting rect of clicked pose
7490 		result = pose->CalcRect(this);
7491 
7492 		// add rects for visible poses only (uses VSList!!)
7493 		int32 count = fVSPoseList->CountItems();
7494 		for (int32 index = FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()));
7495 			index < count; index++) {
7496 			BPose* pose = fVSPoseList->ItemAt(index);
7497 			if (pose) {
7498 				if (pose->IsSelected())
7499 					result = result | pose->CalcRect(this);
7500 
7501 				if (pose->Location(this).y > bounds.bottom)
7502 					break;
7503 			}
7504 		}
7505 	}
7506 
7507 	return result;
7508 }
7509 
7510 
7511 // TODO: SelectPosesListMode and SelectPosesIconMode are terrible and share
7512 // most code
7513 void
7514 BPoseView::SelectPosesListMode(BRect selectionRect, BList** oldList)
7515 {
7516 	ASSERT(ViewMode() == kListMode);
7517 
7518 	// collect all the poses which are enclosed inside the selection rect
7519 	BList* newList = new BList;
7520 	BRect bounds(Bounds());
7521 	SetDrawingMode(B_OP_COPY);
7522 		// TODO: I _think_ there is no more synchronous drawing here,
7523 		// so this should be save to remove
7524 
7525 	int32 startIndex = (int32)(selectionRect.top / fListElemHeight);
7526 	if (startIndex < 0)
7527 		startIndex = 0;
7528 
7529 	BPoint loc(0, startIndex * fListElemHeight);
7530 
7531 	PoseList* poseList = CurrentPoseList();
7532 	int32 count = poseList->CountItems();
7533 	for (int32 index = startIndex; index < count; index++) {
7534 		BPose* pose = poseList->ItemAt(index);
7535 		BRect poseRect(pose->CalcRect(loc, this));
7536 
7537 		if (selectionRect.Intersects(poseRect)) {
7538 			bool selected = pose->IsSelected();
7539 			pose->Select(!fSelectionList->HasItem(pose));
7540 			newList->AddItem((void*)(addr_t)index);
7541 				// this sucks, need to clean up using a vector class instead
7542 				// of BList
7543 
7544 			if ((selected != pose->IsSelected()) && poseRect.Intersects(bounds)) {
7545 				Invalidate(poseRect);
7546 			}
7547 
7548 			// First Pose selected gets to be the pivot.
7549 			if ((fSelectionPivotPose == NULL) && (selected == false))
7550 				fSelectionPivotPose = pose;
7551 		}
7552 
7553 		loc.y += fListElemHeight;
7554 		if (loc.y > selectionRect.bottom)
7555 			break;
7556 	}
7557 
7558 	// take the old set of enclosed poses and invert selection state
7559 	// on those which are no longer enclosed
7560 	count = (*oldList)->CountItems();
7561 	for (int32 index = 0; index < count; index++) {
7562 		int32 oldIndex = (addr_t)(*oldList)->ItemAt(index);
7563 
7564 		if (!newList->HasItem((void*)(addr_t)oldIndex)) {
7565 			BPose* pose = poseList->ItemAt(oldIndex);
7566 			pose->Select(!pose->IsSelected());
7567 			loc.Set(0, oldIndex * fListElemHeight);
7568 			BRect poseRect(pose->CalcRect(loc, this));
7569 
7570 			if (poseRect.Intersects(bounds)) {
7571 				Invalidate(poseRect);
7572 			}
7573 		}
7574 	}
7575 
7576 	delete* oldList;
7577 	*oldList = newList;
7578 }
7579 
7580 
7581 void
7582 BPoseView::SelectPosesIconMode(BRect selectionRect, BList** oldList)
7583 {
7584 	ASSERT(ViewMode() != kListMode);
7585 
7586 	// collect all the poses which are enclosed inside the selection rect
7587 	BList* newList = new BList;
7588 	BRect bounds(Bounds());
7589 	SetDrawingMode(B_OP_COPY);
7590 
7591 	int32 startIndex = FirstIndexAtOrBelow((int32)(selectionRect.top - IconPoseHeight()), true);
7592 	if (startIndex < 0)
7593 		startIndex = 0;
7594 
7595 	int32 count = fPoseList->CountItems();
7596 	for (int32 index = startIndex; index < count; index++) {
7597 		BPose* pose = fVSPoseList->ItemAt(index);
7598 		if (pose) {
7599 			BRect poseRect(pose->CalcRect(this));
7600 
7601 			if (selectionRect.Intersects(poseRect)) {
7602 				bool selected = pose->IsSelected();
7603 				pose->Select(!fSelectionList->HasItem(pose));
7604 				newList->AddItem((void*)(addr_t)index);
7605 
7606 				if ((selected != pose->IsSelected())
7607 					&& poseRect.Intersects(bounds)) {
7608 					Invalidate(poseRect);
7609 				}
7610 
7611 				// First Pose selected gets to be the pivot.
7612 				if ((fSelectionPivotPose == NULL) && (selected == false))
7613 					fSelectionPivotPose = pose;
7614 			}
7615 
7616 			if (pose->Location(this).y > selectionRect.bottom)
7617 				break;
7618 		}
7619 	}
7620 
7621 	// take the old set of enclosed poses and invert selection state
7622 	// on those which are no longer enclosed
7623 	count = (*oldList)->CountItems();
7624 	for (int32 index = 0; index < count; index++) {
7625 		int32 oldIndex = (addr_t)(*oldList)->ItemAt(index);
7626 
7627 		if (!newList->HasItem((void*)(addr_t)oldIndex)) {
7628 			BPose* pose = fVSPoseList->ItemAt(oldIndex);
7629 			pose->Select(!pose->IsSelected());
7630 			BRect poseRect(pose->CalcRect(this));
7631 
7632 			if (poseRect.Intersects(bounds))
7633 				Invalidate(poseRect);
7634 		}
7635 	}
7636 
7637 	delete* oldList;
7638 	*oldList = newList;
7639 }
7640 
7641 
7642 void
7643 BPoseView::AddRemoveSelectionRange(BPoint where, bool extendSelection, BPose* pose)
7644 {
7645 	ASSERT(pose);
7646 
7647  	if ((pose == fSelectionPivotPose) && !extendSelection)
7648  		return;
7649 
7650 	if ((modifiers() & B_SHIFT_KEY) && fSelectionPivotPose) {
7651 		// Multi Pose extend/shrink current selection
7652 		bool select = !pose->IsSelected() || !extendSelection;
7653 				// This weird bit of logic causes the selection to always
7654 				//  center around the pivot point, unless you choose to hold
7655 				//  down COMMAND, which will unselect between the pivot and
7656 				//  the most recently selected Pose.
7657 
7658 		if (!extendSelection) {
7659 			// Remember fSelectionPivotPose because ClearSelection() NULLs it
7660 			// and we need it to be preserved.
7661 			const BPose* savedPivotPose = fSelectionPivotPose;
7662  			ClearSelection();
7663 	 		fSelectionPivotPose = savedPivotPose;
7664 		}
7665 
7666 		if (ViewMode() == kListMode) {
7667 			PoseList* poseList = CurrentPoseList();
7668 			int32 currSelIndex = poseList->IndexOf(pose);
7669 			int32 lastSelIndex = poseList->IndexOf(fSelectionPivotPose);
7670 
7671 			int32 startRange;
7672 			int32 endRange;
7673 
7674 			if (lastSelIndex < currSelIndex) {
7675 				startRange = lastSelIndex;
7676 				endRange = currSelIndex;
7677 			} else {
7678 				startRange = currSelIndex;
7679 				endRange = lastSelIndex;
7680 			}
7681 
7682 			for (int32 i = startRange; i <= endRange; i++)
7683 				AddRemovePoseFromSelection(poseList->ItemAt(i), i, select);
7684 
7685 		} else {
7686 			BRect selection(where, fSelectionPivotPose->Location(this));
7687 
7688 			// Things will get odd if we don't 'fix' the selection rect.
7689 			if (selection.left > selection.right) {
7690 				float temp = selection.right;
7691 				selection.right = selection.left;
7692 				selection.left = temp;
7693 			}
7694 
7695 			if (selection.top > selection.bottom) {
7696 				float temp = selection.top;
7697 				selection.top = selection.bottom;
7698 				selection.bottom = temp;
7699 			}
7700 
7701 			// If the selection rect is not at least 1 pixel high/wide, things
7702 			//  are also not going to work out.
7703 			if (selection.IntegerWidth() < 1)
7704 				selection.right = selection.left + 1.0f;
7705 
7706 			if (selection.IntegerHeight() < 1)
7707 				selection.bottom = selection.top + 1.0f;
7708 
7709 			ASSERT(selection.IsValid());
7710 
7711 			int32 count = fPoseList->CountItems();
7712 			for (int32 index = count - 1; index >= 0; index--) {
7713 				BPose* currPose = fPoseList->ItemAt(index);
7714 				// TODO: works only in non-list mode?
7715 				if (selection.Intersects(currPose->CalcRect(this)))
7716 					AddRemovePoseFromSelection(currPose, index, select);
7717 			}
7718 		}
7719 	} else {
7720 		int32 index = CurrentPoseList()->IndexOf(pose);
7721 		if (!extendSelection) {
7722 			if (!pose->IsSelected()) {
7723 				// create new selection
7724 				ClearSelection();
7725 				AddRemovePoseFromSelection(pose, index, true);
7726 				fSelectionPivotPose = pose;
7727 			}
7728 		} else {
7729 			fMimeTypesInSelectionCache.MakeEmpty();
7730 			AddRemovePoseFromSelection(pose, index, !pose->IsSelected());
7731 		}
7732 	}
7733 
7734 	// If the list is empty, there cannot be a pivot pose,
7735 	// however if the list is not empty there must be a pivot
7736 	// pose.
7737 	if (fSelectionList->IsEmpty()) {
7738 		fSelectionPivotPose = NULL;
7739 		fRealPivotPose = NULL;
7740 	} else if (fSelectionPivotPose == NULL) {
7741 		fSelectionPivotPose = pose;
7742 		fRealPivotPose = pose;
7743 	}
7744 }
7745 
7746 
7747 void
7748 BPoseView::DeleteSymLinkPoseTarget(const node_ref* itemNode, BPose* pose,
7749 	int32 index)
7750 {
7751 	ASSERT(pose->TargetModel()->IsSymLink());
7752 	watch_node(itemNode, B_STOP_WATCHING, this);
7753 
7754 	// watch the parent of the symlink, so that we know when the symlink
7755 	// can be considered fixed.
7756 	WatchParentOf(pose->TargetModel()->EntryRef());
7757 
7758 	BPoint loc(0, index * fListElemHeight);
7759 	pose->TargetModel()->SetLinkTo(NULL);
7760 	pose->UpdateBrokenSymLink(loc, this);
7761 }
7762 
7763 
7764 bool
7765 BPoseView::DeletePose(const node_ref* itemNode, BPose* pose, int32 index)
7766 {
7767 	watch_node(itemNode, B_STOP_WATCHING, this);
7768 
7769 	if (!pose)
7770 		pose = fPoseList->FindPose(itemNode, &index);
7771 
7772 	if (pose) {
7773 		fInsertedNodes.erase(fInsertedNodes.find(*itemNode));
7774 		if (pose->TargetModel()->IsSymLink()) {
7775 			fBrokenLinks->RemoveItem(pose->TargetModel());
7776 			StopWatchingParentsOf(pose->TargetModel()->EntryRef());
7777 			Model* target = pose->TargetModel()->LinkTo();
7778 			if (target)
7779 				watch_node(target->NodeRef(), B_STOP_WATCHING, this);
7780 		}
7781 
7782 		ASSERT(TargetModel());
7783 
7784 		if (pose == fDropTarget)
7785 			fDropTarget = NULL;
7786 
7787 		if (pose == ActivePose())
7788 			CommitActivePose();
7789 
7790 		Window()->UpdateIfNeeded();
7791 
7792 		// remove it from list no matter what since it might be in list
7793 		// but not "selected" since selection is hidden
7794 		fSelectionList->RemoveItem(pose);
7795 		if (fSelectionPivotPose == pose)
7796 			fSelectionPivotPose = NULL;
7797 		if (fRealPivotPose == pose)
7798 			fRealPivotPose = NULL;
7799 
7800 		if (pose->IsSelected() && fSelectionChangedHook)
7801 			ContainerWindow()->SelectionChanged();
7802 
7803 		fPoseList->RemoveItemAt(index);
7804 
7805 		bool visible = true;
7806 		if (fFiltering) {
7807 			if (fFilteredPoseList->FindPose(itemNode, &index) != NULL)
7808 				fFilteredPoseList->RemoveItemAt(index);
7809 			else
7810 				visible = false;
7811 		}
7812 
7813 		fMimeTypeListIsDirty = true;
7814 
7815 		if (pose->HasLocation())
7816 			RemoveFromVSList(pose);
7817 
7818 		if (visible) {
7819 			BRect invalidRect;
7820 			if (ViewMode() == kListMode)
7821 				invalidRect = CalcPoseRectList(pose, index);
7822 			else
7823 				invalidRect = pose->CalcRect(this);
7824 
7825 			if (ViewMode() == kListMode)
7826 				CloseGapInList(&invalidRect);
7827 			else
7828 				RemoveFromExtent(invalidRect);
7829 
7830 			Invalidate(invalidRect);
7831 			UpdateCount();
7832 			UpdateScrollRange();
7833 			ResetPosePlacementHint();
7834 
7835 			if (ViewMode() == kListMode) {
7836 				BRect bounds(Bounds());
7837 				int32 index = (int32)(bounds.bottom / fListElemHeight);
7838 				BPose* pose = CurrentPoseList()->ItemAt(index);
7839 
7840 				if (!pose && bounds.top > 0) // scroll up a little
7841 					BView::ScrollTo(bounds.left,
7842 						max_c(bounds.top - fListElemHeight, 0));
7843 			}
7844 		}
7845 
7846 		delete pose;
7847 
7848 	} else {
7849 		// we might be getting a delete for an item in the zombie list
7850 		Model* zombie = FindZombie(itemNode, &index);
7851 		if (zombie) {
7852 			PRINT(("deleting zombie model %s\n", zombie->Name()));
7853 			fZombieList->RemoveItemAt(index);
7854 			delete zombie;
7855 		} else
7856 			return false;
7857 	}
7858 	return true;
7859 }
7860 
7861 
7862 Model*
7863 BPoseView::FindZombie(const node_ref* itemNode, int32* resultingIndex)
7864 {
7865 	int32 count = fZombieList->CountItems();
7866 	for (int32 index = 0; index < count; index++) {
7867 		Model* zombie = fZombieList->ItemAt(index);
7868 		if (*zombie->NodeRef() == *itemNode) {
7869 			if (resultingIndex)
7870 				*resultingIndex = index;
7871 			return zombie;
7872 		}
7873 	}
7874 
7875 	return NULL;
7876 }
7877 
7878 // return pose at location h,v (search list starting from bottom so
7879 // drawing and hit detection reflect the same pose ordering)
7880 
7881 BPose*
7882 BPoseView::FindPose(BPoint point, int32* poseIndex) const
7883 {
7884 	if (ViewMode() == kListMode) {
7885 		int32 index = (int32)(point.y / fListElemHeight);
7886 		if (poseIndex)
7887 			*poseIndex = index;
7888 
7889 		BPoint loc(0, index * fListElemHeight);
7890 		BPose* pose = CurrentPoseList()->ItemAt(index);
7891 		if (pose && pose->PointInPose(loc, this, point))
7892 			return pose;
7893 	} else {
7894 		int32 count = fPoseList->CountItems();
7895 		for (int32 index = count - 1; index >= 0; index--) {
7896 			BPose* pose = fPoseList->ItemAt(index);
7897 			if (pose->PointInPose(this, point)) {
7898 				if (poseIndex)
7899 					*poseIndex = index;
7900 				return pose;
7901 			}
7902 		}
7903 	}
7904 
7905 	return NULL;
7906 }
7907 
7908 
7909 void
7910 BPoseView::OpenSelection(BPose* clickedPose, int32* index)
7911 {
7912 	BPose* singleWindowBrowsePose = clickedPose;
7913 	TrackerSettings settings;
7914 
7915 	// Get first selected pose in selection if none was clicked
7916 	if (settings.SingleWindowBrowse()
7917 		&& !singleWindowBrowsePose
7918 		&& fSelectionList->CountItems() == 1
7919 		&& !IsFilePanel())
7920 		singleWindowBrowsePose = fSelectionList->ItemAt(0);
7921 
7922 	// check if we can use the single window mode
7923 	if (settings.SingleWindowBrowse()
7924 		&& !IsDesktopWindow()
7925 		&& !IsFilePanel()
7926 		&& !(modifiers() & B_OPTION_KEY)
7927 		&& TargetModel()->IsDirectory()
7928 		&& singleWindowBrowsePose
7929 		&& singleWindowBrowsePose->ResolvedModel()
7930 		&& singleWindowBrowsePose->ResolvedModel()->IsDirectory()) {
7931 		// Switch to new directory
7932 		BMessage msg(kSwitchDirectory);
7933 		msg.AddRef("refs", singleWindowBrowsePose->ResolvedModel()->EntryRef());
7934 		Window()->PostMessage(&msg);
7935 	} else
7936 		// Otherwise use standard method
7937 		OpenSelectionCommon(clickedPose, index, false);
7938 
7939 }
7940 
7941 
7942 void
7943 BPoseView::OpenSelectionUsing(BPose* clickedPose, int32* index)
7944 {
7945 	OpenSelectionCommon(clickedPose, index, true);
7946 }
7947 
7948 
7949 void
7950 BPoseView::OpenSelectionCommon(BPose* clickedPose, int32* poseIndex,
7951 	bool openWith)
7952 {
7953 	int32 count = fSelectionList->CountItems();
7954 	if (!count)
7955 		return;
7956 
7957 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
7958 
7959 	BMessage message(B_REFS_RECEIVED);
7960 
7961 	for (int32 index = 0; index < count; index++) {
7962 		BPose* pose = fSelectionList->ItemAt(index);
7963 
7964 		message.AddRef("refs", pose->TargetModel()->EntryRef());
7965 
7966 		// close parent window if option down and we're not the desktop
7967 		// and we're not in single window mode
7968 		if (!tracker
7969 			|| (modifiers() & B_OPTION_KEY) == 0
7970 			|| IsFilePanel()
7971 			|| IsDesktopWindow()
7972 			|| TrackerSettings().SingleWindowBrowse())
7973 			continue;
7974 
7975 		ASSERT(TargetModel());
7976 		message.AddData("nodeRefsToClose", B_RAW_TYPE, TargetModel()->NodeRef(),
7977 			sizeof (node_ref));
7978 	}
7979 
7980 	if (openWith)
7981 		message.AddInt32("launchUsingSelector", 0);
7982 
7983 	// add a messenger to the launch message that will be used to
7984 	// dispatch scripting calls from apps to the PoseView
7985 	message.AddMessenger("TrackerViewToken", BMessenger(this));
7986 
7987 	if (fSelectionHandler)
7988 		fSelectionHandler->PostMessage(&message);
7989 
7990 	if (clickedPose) {
7991 		ASSERT(poseIndex);
7992 		if (ViewMode() == kListMode)
7993 			DrawOpenAnimation(CalcPoseRectList(clickedPose, *poseIndex, true));
7994 		else
7995 			DrawOpenAnimation(clickedPose->CalcRect(this));
7996 	}
7997 }
7998 
7999 
8000 void
8001 BPoseView::DrawOpenAnimation(BRect rect)
8002 {
8003 	SetDrawingMode(B_OP_INVERT);
8004 
8005 	BRect box1(rect);
8006 	box1.InsetBy(rect.Width() / 2 - 2, rect.Height() / 2 - 2);
8007 	BRect box2(box1);
8008 
8009 	for (int32 index = 0; index < 7; index++) {
8010 		box2 = box1;
8011 		box2.InsetBy(-2, -2);
8012 		StrokeRect(box1, B_MIXED_COLORS);
8013 		Sync();
8014 		StrokeRect(box2, B_MIXED_COLORS);
8015 		Sync();
8016 		snooze(10000);
8017 		StrokeRect(box1, B_MIXED_COLORS);
8018 		StrokeRect(box2, B_MIXED_COLORS);
8019 		Sync();
8020 		box1 = box2;
8021 	}
8022 
8023 	SetDrawingMode(B_OP_OVER);
8024 }
8025 
8026 
8027 void
8028 BPoseView::UnmountSelectedVolumes()
8029 {
8030 	BVolume boot;
8031 	BVolumeRoster().GetBootVolume(&boot);
8032 
8033 	int32 select_count = fSelectionList->CountItems();
8034 	for (int32 index = 0; index < select_count; index++) {
8035 		BPose* pose = fSelectionList->ItemAt(index);
8036 		if (!pose)
8037 			continue;
8038 
8039 		Model* model = pose->TargetModel();
8040 		if (model->IsVolume()) {
8041 			BVolume volume(model->NodeRef()->device);
8042 			if (volume != boot) {
8043 				dynamic_cast<TTracker*>(be_app)->SaveAllPoseLocations();
8044 
8045 				BMessage message(kUnmountVolume);
8046 				message.AddInt32("device_id", volume.Device());
8047 				be_app->PostMessage(&message);
8048 			}
8049 		}
8050 	}
8051 }
8052 
8053 
8054 void
8055 BPoseView::ClearPoses()
8056 {
8057 	CommitActivePose();
8058 	SavePoseLocations();
8059 	ClearFilter();
8060 
8061 	// clear all pose lists
8062 	fPoseList->MakeEmpty();
8063 	fMimeTypeListIsDirty = true;
8064 	fVSPoseList->MakeEmpty();
8065 	fZombieList->MakeEmpty();
8066 	fSelectionList->MakeEmpty();
8067 	fSelectionPivotPose = NULL;
8068 	fRealPivotPose = NULL;
8069 	fMimeTypesInSelectionCache.MakeEmpty();
8070 	fBrokenLinks->MakeEmpty();
8071 
8072 	DisableScrollBars();
8073 	ScrollTo(BPoint(0, 0));
8074 	UpdateScrollRange();
8075 	SetScrollBarsTo(BPoint(0, 0));
8076 	EnableScrollBars();
8077 	ResetPosePlacementHint();
8078 	ClearExtent();
8079 
8080 	if (fSelectionChangedHook)
8081 		ContainerWindow()->SelectionChanged();
8082 }
8083 
8084 
8085 void
8086 BPoseView::SwitchDir(const entry_ref* newDirRef, AttributeStreamNode* node)
8087 {
8088 	ASSERT(TargetModel());
8089 	if (*newDirRef == *TargetModel()->EntryRef())
8090 		// no change
8091 		return;
8092 
8093 	Model* model = new Model(newDirRef, true);
8094 	if (model->InitCheck() != B_OK || !model->IsDirectory()) {
8095 		delete model;
8096 		return;
8097 	}
8098 
8099 	CommitActivePose();
8100 
8101 	// before clearing and adding new poses, we reset "blessed" async
8102 	// thread id to prevent old add_poses thread from adding any more icons
8103 	// the new add_poses thread will then set fAddPosesThread to its ID and it
8104 	// will be allowed to add icons
8105 	fAddPosesThreads.clear();
8106 	fInsertedNodes.clear();
8107 
8108 	delete fModel;
8109 	fModel = model;
8110 
8111 	// check if model is a trash dir, if so
8112 	// update ContainerWindow's fIsTrash, etc.
8113 	// variables to indicate new state
8114 	ContainerWindow()->UpdateIfTrash(model);
8115 
8116 	StopWatching();
8117 	ClearPoses();
8118 
8119 	// Restore state, might fail if the state has never been saved for this node
8120 	uint32 oldMode = ViewMode();
8121 	bool viewStateRestored = false;
8122 	if (node) {
8123 		BViewState* previousState = fViewState;
8124 		RestoreState(node);
8125 		viewStateRestored = (fViewState != previousState);
8126 	}
8127 
8128 	// Make sure fTitleView is rebuilt, as fColumnList might have changed
8129 	fTitleView->Reset();
8130 
8131 	if (viewStateRestored) {
8132 		if (ViewMode() == kListMode && oldMode != kListMode) {
8133 
8134 			MoveBy(0, kTitleViewHeight + 1);
8135 			ResizeBy(0, -(kTitleViewHeight + 1));
8136 
8137 			if (ContainerWindow())
8138 				ContainerWindow()->ShowAttributeMenu();
8139 
8140 			fTitleView->ResizeTo(Frame().Width(), fTitleView->Frame().Height());
8141 			fTitleView->MoveTo(Frame().left, Frame().top - (kTitleViewHeight + 1));
8142 			if (Parent())
8143 				Parent()->AddChild(fTitleView);
8144 			else
8145 				Window()->AddChild(fTitleView);
8146 		} else if (ViewMode() != kListMode && oldMode == kListMode) {
8147 			fTitleView->RemoveSelf();
8148 
8149 			if (ContainerWindow())
8150 				ContainerWindow()->HideAttributeMenu();
8151 
8152 			MoveBy(0, -(kTitleViewHeight + 1));
8153 			ResizeBy(0, kTitleViewHeight + 1);
8154 		} else if (ViewMode() == kListMode && oldMode == kListMode)
8155 			fTitleView->Invalidate();
8156 
8157 		BPoint origin;
8158 		if (ViewMode() == kListMode)
8159 			origin = fViewState->ListOrigin();
8160 		else
8161 			origin = fViewState->IconOrigin();
8162 
8163 		PinPointToValidRange(origin);
8164 
8165 		SetIconPoseHeight();
8166 		GetLayoutInfo(ViewMode(), &fGrid, &fOffset);
8167 		ResetPosePlacementHint();
8168 
8169 		DisableScrollBars();
8170 		ScrollTo(origin);
8171 		UpdateScrollRange();
8172 		SetScrollBarsTo(origin);
8173 		EnableScrollBars();
8174 	} else {
8175 		ResetOrigin();
8176 		ResetPosePlacementHint();
8177 	}
8178 
8179 	StartWatching();
8180 
8181 	// be sure this happens after origin is set and window is sized
8182 	// properly for proper icon caching!
8183 
8184 	if (ContainerWindow()->IsTrash())
8185 		AddTrashPoses();
8186 	else
8187 		AddPoses(TargetModel());
8188 	TargetModel()->CloseNode();
8189 
8190 	Invalidate();
8191 
8192 	fLastKeyTime = 0;
8193 }
8194 
8195 
8196 void
8197 BPoseView::Refresh()
8198 {
8199 	BEntry entry;
8200 
8201 	ASSERT(TargetModel());
8202 	if (TargetModel()->OpenNode() != B_OK)
8203 		return;
8204 
8205 	StopWatching();
8206 	fInsertedNodes.clear();
8207 	ClearPoses();
8208 	StartWatching();
8209 
8210 	// be sure this happens after origin is set and window is sized
8211 	// properly for proper icon caching!
8212 	AddPoses(TargetModel());
8213 	TargetModel()->CloseNode();
8214 
8215 	if (fRefFilter != NULL) {
8216 		fFiltering = false;
8217 		StartFiltering();
8218 	}
8219 
8220 	Invalidate();
8221 	ResetOrigin();
8222 	ResetPosePlacementHint();
8223 }
8224 
8225 
8226 void
8227 BPoseView::ResetOrigin()
8228 {
8229 	DisableScrollBars();
8230 	ScrollTo(B_ORIGIN);
8231 	UpdateScrollRange();
8232 	SetScrollBarsTo(B_ORIGIN);
8233 	EnableScrollBars();
8234 }
8235 
8236 
8237 void
8238 BPoseView::EditQueries()
8239 {
8240 	// edit selected queries
8241 	SendSelectionAsRefs(kEditQuery, true);
8242 }
8243 
8244 
8245 void
8246 BPoseView::SendSelectionAsRefs(uint32 what, bool onlyQueries)
8247 {
8248 	// fix this by having a proper selection iterator
8249 
8250 	int32 numItems = fSelectionList->CountItems();
8251 	if (!numItems)
8252 		return;
8253 
8254 	bool haveRef = false;
8255 	BMessage message;
8256 	message.what = what;
8257 
8258 	for (int32 index = 0; index < numItems; index++) {
8259 		BPose* pose = fSelectionList->ItemAt(index);
8260 		if (onlyQueries) {
8261 			// to check if pose is a query, follow any symlink first
8262 			BEntry resolvedEntry(pose->TargetModel()->EntryRef(), true);
8263 			if (resolvedEntry.InitCheck() != B_OK)
8264 				continue;
8265 
8266 			Model model(&resolvedEntry);
8267 			if (!model.IsQuery() && !model.IsQueryTemplate())
8268 				continue;
8269 		}
8270 		haveRef = true;
8271 		message.AddRef("refs", pose->TargetModel()->EntryRef());
8272 	}
8273 	if (!haveRef)
8274 		return;
8275 
8276 	if (onlyQueries)
8277 		// this is used to make query templates come up in a special edit window
8278 		message.AddBool("editQueryOnPose", onlyQueries);
8279 
8280 	BMessenger(kTrackerSignature).SendMessage(&message);
8281 }
8282 
8283 
8284 void
8285 BPoseView::OpenInfoWindows()
8286 {
8287 	BMessenger tracker(kTrackerSignature);
8288 	if (!tracker.IsValid()) {
8289 		BAlert* alert = new BAlert("",
8290 			B_TRANSLATE("The Tracker must be running to see Info windows."),
8291 			B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
8292 			B_WARNING_ALERT);
8293 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
8294 		alert->Go();
8295 		return;
8296  	}
8297 	SendSelectionAsRefs(kGetInfo);
8298 }
8299 
8300 
8301 void
8302 BPoseView::SetDefaultPrinter()
8303 {
8304 	BMessenger tracker(kTrackerSignature);
8305 	if (!tracker.IsValid()) {
8306 		BAlert* alert = new BAlert("",
8307 			B_TRANSLATE("The Tracker must be running to see set the default "
8308 			"printer."), B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
8309 			B_WARNING_ALERT);
8310 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
8311 		alert->Go();
8312 		return;
8313  	}
8314 	SendSelectionAsRefs(kMakeActivePrinter);
8315 }
8316 
8317 
8318 void
8319 BPoseView::OpenParent()
8320 {
8321 	if (!TargetModel() || TargetModel()->IsRoot() || IsDesktopWindow())
8322 		return;
8323 
8324 	BEntry entry(TargetModel()->EntryRef());
8325 	BDirectory parent;
8326 	entry_ref ref;
8327 
8328 	if (entry.GetParent(&parent) != B_OK
8329 		|| parent.GetEntry(&entry) != B_OK
8330 		|| entry.GetRef(&ref) != B_OK)
8331 		return;
8332 
8333 	BEntry root("/");
8334 	if (!TrackerSettings().SingleWindowBrowse()
8335 		&& !TrackerSettings().ShowNavigator()
8336 		&& !TrackerSettings().ShowDisksIcon() && entry == root
8337 		&& (modifiers() & B_CONTROL_KEY) == 0)
8338 		return;
8339 
8340 	Model parentModel(&ref);
8341 
8342 	BMessage message(B_REFS_RECEIVED);
8343 	message.AddRef("refs", &ref);
8344 
8345 	if (dynamic_cast<TTracker*>(be_app)) {
8346 		// add information about the child, so that we can select it
8347 		// in the parent view
8348 		message.AddData("nodeRefToSelect", B_RAW_TYPE, TargetModel()->NodeRef(),
8349 			sizeof (node_ref));
8350 
8351 		if ((modifiers() & B_OPTION_KEY) != 0 && !IsFilePanel())
8352 			// if option down, add instructions to close the parent
8353 			message.AddData("nodeRefsToClose", B_RAW_TYPE, TargetModel()->NodeRef(),
8354 				sizeof (node_ref));
8355 	}
8356 
8357 
8358 	if (TrackerSettings().SingleWindowBrowse()) {
8359 		BMessage msg(kSwitchDirectory);
8360 		msg.AddRef("refs", &ref);
8361 		Window()->PostMessage(&msg);
8362 	} else
8363 		be_app->PostMessage(&message);
8364 }
8365 
8366 
8367 void
8368 BPoseView::IdentifySelection(bool force)
8369 {
8370 	int32 count = fSelectionList->CountItems();
8371 	for (int32 index = 0; index < count; index++) {
8372 		BPose* pose = fSelectionList->ItemAt(index);
8373 		BEntry entry(pose->TargetModel()->ResolveIfLink()->EntryRef());
8374 		if (entry.InitCheck() == B_OK) {
8375 			BPath path;
8376 			if (entry.GetPath(&path) == B_OK)
8377 				update_mime_info(path.Path(), true, false, force ? 2 : 1);
8378 		}
8379 	}
8380 }
8381 
8382 
8383 void
8384 BPoseView::ClearSelection()
8385 {
8386 	CommitActivePose();
8387 	fSelectionPivotPose = NULL;
8388 	fRealPivotPose = NULL;
8389 
8390 	if (fSelectionList->CountItems()) {
8391 
8392 		// scan all visible poses first
8393 		BRect bounds(Bounds());
8394 
8395 		if (ViewMode() == kListMode) {
8396 			int32 startIndex = (int32)(bounds.top / fListElemHeight);
8397 			BPoint loc(0, startIndex * fListElemHeight);
8398 
8399 			PoseList* poseList = CurrentPoseList();
8400 			int32 count = poseList->CountItems();
8401 			for (int32 index = startIndex; index < count; index++) {
8402 				BPose* pose = poseList->ItemAt(index);
8403 				if (pose->IsSelected()) {
8404 					pose->Select(false);
8405 					Invalidate(pose->CalcRect(loc, this, false));
8406 				}
8407 
8408 				loc.y += fListElemHeight;
8409 				if (loc.y > bounds.bottom)
8410 					break;
8411 			}
8412 		} else {
8413 			int32 startIndex = FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()), true);
8414 			int32 count = fVSPoseList->CountItems();
8415 			for (int32 index = startIndex; index < count; index++) {
8416 				BPose* pose = fVSPoseList->ItemAt(index);
8417 				if (pose) {
8418 					if (pose->IsSelected()) {
8419 						pose->Select(false);
8420 						Invalidate(pose->CalcRect(this));
8421 					}
8422 
8423 					if (pose->Location(this).y > bounds.bottom)
8424 						break;
8425 				}
8426 			}
8427 		}
8428 
8429 		// clear selection state in all poses
8430 		int32 count = fSelectionList->CountItems();
8431 		for (int32 index = 0; index < count; index++)
8432 			fSelectionList->ItemAt(index)->Select(false);
8433 
8434 		fSelectionList->MakeEmpty();
8435 	}
8436 	fMimeTypesInSelectionCache.MakeEmpty();
8437 }
8438 
8439 
8440 void
8441 BPoseView::ShowSelection(bool show)
8442 {
8443 	if (fSelectionVisible == show)
8444 		return;
8445 
8446 	fSelectionVisible = show;
8447 
8448 	if (fSelectionList->CountItems()) {
8449 
8450 		// scan all visible poses first
8451 		BRect bounds(Bounds());
8452 
8453 		if (ViewMode() == kListMode) {
8454 			int32 startIndex = (int32)(bounds.top / fListElemHeight);
8455 			BPoint loc(0, startIndex * fListElemHeight);
8456 
8457 			PoseList* poseList = CurrentPoseList();
8458 			int32 count = poseList->CountItems();
8459 			for (int32 index = startIndex; index < count; index++) {
8460 				BPose* pose = poseList->ItemAt(index);
8461 				if (fSelectionList->HasItem(pose))
8462 					if (pose->IsSelected() != show || fShowSelectionWhenInactive) {
8463 						if (!fShowSelectionWhenInactive)
8464 							pose->Select(show);
8465 						pose->Draw(BRect(pose->CalcRect(loc, this, false)),
8466 							bounds, this, false);
8467 					}
8468 
8469 				loc.y += fListElemHeight;
8470 				if (loc.y > bounds.bottom)
8471 					break;
8472 			}
8473 		} else {
8474 			int32 startIndex = FirstIndexAtOrBelow(
8475 				(int32)(bounds.top - IconPoseHeight()), true);
8476 			int32 count = fVSPoseList->CountItems();
8477 			for (int32 index = startIndex; index < count; index++) {
8478 				BPose* pose = fVSPoseList->ItemAt(index);
8479 				if (pose) {
8480 					if (fSelectionList->HasItem(pose))
8481 						if (pose->IsSelected() != show || fShowSelectionWhenInactive) {
8482 							if (!fShowSelectionWhenInactive)
8483 								pose->Select(show);
8484 							Invalidate(pose->CalcRect(this));
8485 						}
8486 
8487 					if (pose->Location(this).y > bounds.bottom)
8488 						break;
8489 				}
8490 			}
8491 		}
8492 
8493 		// now set all other poses
8494 		int32 count = fSelectionList->CountItems();
8495 		for (int32 index = 0; index < count; index++) {
8496 			BPose* pose = fSelectionList->ItemAt(index);
8497 			if (pose->IsSelected() != show && !fShowSelectionWhenInactive)
8498 				pose->Select(show);
8499 		}
8500 
8501 		// finally update fRealPivotPose/fSelectionPivotPose
8502 		if (!show) {
8503 			fRealPivotPose = fSelectionPivotPose;
8504 			fSelectionPivotPose = NULL;
8505 		} else {
8506 			if (fRealPivotPose)
8507 				fSelectionPivotPose = fRealPivotPose;
8508 			fRealPivotPose = NULL;
8509 		}
8510 	}
8511 }
8512 
8513 
8514 void
8515 BPoseView::AddRemovePoseFromSelection(BPose* pose, int32 index, bool select)
8516 {
8517 	// Do not allow double selection/deselection.
8518 	if (select == pose->IsSelected())
8519 		return;
8520 
8521 	pose->Select(select);
8522 
8523 	// update display
8524 	if (ViewMode() == kListMode)
8525 		Invalidate(pose->CalcRect(BPoint(0, index * fListElemHeight), this, false));
8526 	else
8527 		Invalidate(pose->CalcRect(this));
8528 
8529 	if (select)
8530 		fSelectionList->AddItem(pose);
8531 	else {
8532 		fSelectionList->RemoveItem(pose);
8533 		if (fSelectionPivotPose == pose)
8534 			fSelectionPivotPose = NULL;
8535 		if (fRealPivotPose == pose)
8536 			fRealPivotPose = NULL;
8537 	}
8538 }
8539 
8540 
8541 void
8542 BPoseView::RemoveFromExtent(const BRect &rect)
8543 {
8544 	ASSERT(ViewMode() != kListMode);
8545 
8546 	if (rect.left <= fExtent.left || rect.right >= fExtent.right
8547 		|| rect.top <= fExtent.top || rect.bottom >= fExtent.bottom)
8548 		RecalcExtent();
8549 }
8550 
8551 
8552 void
8553 BPoseView::RecalcExtent()
8554 {
8555 	ASSERT(ViewMode() != kListMode);
8556 
8557 	ClearExtent();
8558 	int32 count = fPoseList->CountItems();
8559 	for (int32 index = 0; index < count; index++)
8560 		AddToExtent(fPoseList->ItemAt(index)->CalcRect(this));
8561 }
8562 
8563 
8564 BRect
8565 BPoseView::Extent() const
8566 {
8567 	BRect rect;
8568 
8569 	if (ViewMode() == kListMode) {
8570 		BColumn* column = fColumnList->LastItem();
8571 		if (column) {
8572 			rect.left = rect.top = 0;
8573 			rect.right = column->Offset() + column->Width()
8574 				+ kTitleColumnRightExtraMargin - kRoomForLine / 2.0f;
8575 			rect.bottom = fListElemHeight * CurrentPoseList()->CountItems();
8576 		} else
8577 			rect.Set(LeftTop().x, LeftTop().y, LeftTop().x, LeftTop().y);
8578 
8579 	} else {
8580 		rect = fExtent;
8581 		rect.left -= fOffset.x;
8582 		rect.top -= fOffset.y;
8583 		rect.right += fOffset.x;
8584 		rect.bottom += fOffset.y;
8585 		if (!rect.IsValid())
8586 			rect.Set(LeftTop().x, LeftTop().y, LeftTop().x, LeftTop().y);
8587 	}
8588 
8589 	return rect;
8590 }
8591 
8592 
8593 void
8594 BPoseView::SetScrollBarsTo(BPoint point)
8595 {
8596 	if (fHScrollBar && fVScrollBar) {
8597 		fHScrollBar->SetValue(point.x);
8598 		fVScrollBar->SetValue(point.y);
8599 	} else {
8600 		// TODO: I don't know what this was supposed to work around
8601 		// (ie why it wasn't calling ScrollTo(point) simply). Although
8602 		// it cannot have been tested, since it was broken before, I am
8603 		// still leaving this, since I know there can be a subtle change in
8604 		// behaviour (BView<->BScrollBar feedback effects) when scrolling
8605 		// both directions at once versus separately.
8606 		BPoint origin = LeftTop();
8607 		ScrollTo(BPoint(origin.x, point.y));
8608 		ScrollTo(point);
8609 	}
8610 }
8611 
8612 
8613 void
8614 BPoseView::PinPointToValidRange(BPoint& origin)
8615 {
8616 	// !NaN and valid range
8617 	// the following checks are not broken even they look like they are
8618 	if (!(origin.x >= 0) && !(origin.x <= 0))
8619 		origin.x = 0;
8620 	else if (origin.x < -40000.0 || origin.x > 40000.0)
8621 		origin.x = 0;
8622 
8623 	if (!(origin.y >= 0) && !(origin.y <= 0))
8624 		origin.y = 0;
8625 	else if (origin.y < -40000.0 || origin.y > 40000.0)
8626 		origin.y = 0;
8627 }
8628 
8629 
8630 void
8631 BPoseView::UpdateScrollRange()
8632 {
8633 	// TODO: some calls to UpdateScrollRange don't do the right thing because
8634 	// Extent doesn't return the right value (too early in PoseView lifetime??)
8635 	//
8636 	// This happened most with file panels, when opening a parent - added
8637 	// an extra call to UpdateScrollRange in SelectChildInParent to work
8638 	// around this
8639 
8640 	AutoLock<BWindow> lock(Window());
8641 	if (!lock)
8642 		return;
8643 
8644 	BRect bounds(Bounds());
8645 
8646 	BPoint origin(LeftTop());
8647 	BRect extent(Extent());
8648 
8649 	lock.Unlock();
8650 
8651 	BPoint minVal(std::min(extent.left, origin.x), std::min(extent.top, origin.y));
8652 
8653 	BPoint maxVal((extent.right - bounds.right) + origin.x,
8654 		(extent.bottom - bounds.bottom) + origin.y);
8655 
8656 	maxVal.x = std::max(maxVal.x, origin.x);
8657 	maxVal.y = std::max(maxVal.y, origin.y);
8658 
8659 	if (fHScrollBar) {
8660 		float scrollMin;
8661 		float scrollMax;
8662 		fHScrollBar->GetRange(&scrollMin, &scrollMax);
8663 		if (minVal.x != scrollMin || maxVal.x != scrollMax) {
8664 			fHScrollBar->SetRange(minVal.x, maxVal.x);
8665 			fHScrollBar->SetSteps(kSmallStep, bounds.Width());
8666 		}
8667 	}
8668 
8669 	if (fVScrollBar) {
8670 		float scrollMin;
8671 		float scrollMax;
8672 		fVScrollBar->GetRange(&scrollMin, &scrollMax);
8673 
8674 		if (minVal.y != scrollMin || maxVal.y != scrollMax) {
8675 			fVScrollBar->SetRange(minVal.y, maxVal.y);
8676 			fVScrollBar->SetSteps(kSmallStep, bounds.Height());
8677 		}
8678 	}
8679 
8680 	// set proportions for bars
8681 	BRect totalExtent(extent | bounds);
8682 
8683 	if (fHScrollBar && totalExtent.Width() != 0.0) {
8684 		float proportion = bounds.Width() / totalExtent.Width();
8685 		if (fHScrollBar->Proportion() != proportion)
8686 			fHScrollBar->SetProportion(proportion);
8687 	}
8688 
8689 	if (fVScrollBar && totalExtent.Height() != 0.0) {
8690 		float proportion = bounds.Height() / totalExtent.Height();
8691 		if (fVScrollBar->Proportion() != proportion)
8692 			fVScrollBar->SetProportion(proportion);
8693 	}
8694 }
8695 
8696 
8697 void
8698 BPoseView::DrawPose(BPose* pose, int32 index, bool fullDraw)
8699 {
8700 	BRect rect = CalcPoseRect(pose, index, fullDraw);
8701 
8702 	if (TrackerSettings().ShowVolumeSpaceBar()
8703 		&& pose->TargetModel()->IsVolume()) {
8704 		Invalidate(rect);
8705 	} else
8706 		pose->Draw(rect, rect, this, fullDraw);
8707 }
8708 
8709 
8710 rgb_color
8711 BPoseView::DeskTextColor() const
8712 {
8713 	rgb_color color = ViewColor();
8714 	float thresh = color.red + (color.green * 1.5f) + (color.blue * 0.50f);
8715 
8716 	if (thresh >= 300) {
8717 		color.red = 0;
8718 		color.green = 0;
8719 		color.blue = 0;
8720  	} else {
8721 		color.red = 255;
8722 		color.green = 255;
8723 		color.blue = 255;
8724 	}
8725 
8726 	return color;
8727 }
8728 
8729 
8730 rgb_color
8731 BPoseView::DeskTextBackColor() const
8732 {
8733 	// returns black or white color depending on the desktop background
8734 	int32 thresh = 0;
8735 	rgb_color color = LowColor();
8736 
8737 	if (color.red > 150)
8738 		thresh++;
8739 	if (color.green > 150)
8740 		thresh++;
8741 	if (color.blue > 150)
8742 		thresh++;
8743 
8744 	if (thresh > 1) {
8745 		color.red = 255;
8746 		color.green = 255;
8747 		color.blue = 255;
8748  	} else {
8749 		color.red = 0;
8750 		color.green = 0;
8751 		color.blue = 0;
8752 	}
8753 
8754 	return color;
8755 }
8756 
8757 
8758 void
8759 BPoseView::Draw(BRect updateRect)
8760 {
8761 	if (IsDesktopWindow()) {
8762 		BScreen	screen(Window());
8763 		rgb_color color = screen.DesktopColor();
8764 		SetLowColor(color);
8765 		SetViewColor(color);
8766 	}
8767 	DrawViewCommon(updateRect);
8768 
8769 	if ((Flags() & B_DRAW_ON_CHILDREN) == 0)
8770 		DrawAfterChildren(updateRect);
8771 }
8772 
8773 
8774 void
8775 BPoseView::DrawAfterChildren(BRect updateRect)
8776 {
8777 	if (fTransparentSelection && fSelectionRectInfo.rect.IsValid()) {
8778 		SetDrawingMode(B_OP_ALPHA);
8779 		SetHighColor(255, 255, 255, 128);
8780 		if (fSelectionRectInfo.rect.Width() == 0
8781 			|| fSelectionRectInfo.rect.Height() == 0) {
8782 			StrokeLine(fSelectionRectInfo.rect.LeftTop(),
8783 				fSelectionRectInfo.rect.RightBottom());
8784 		} else {
8785 			StrokeRect(fSelectionRectInfo.rect);
8786 			BRect interior = fSelectionRectInfo.rect;
8787 			interior.InsetBy(1, 1);
8788 			if (interior.IsValid()) {
8789 				SetHighColor(80, 80, 80, 90);
8790 				FillRect(interior);
8791 			}
8792 		}
8793 		SetDrawingMode(B_OP_OVER);
8794 	}
8795 }
8796 
8797 
8798 void
8799 BPoseView::SynchronousUpdate(BRect updateRect, bool clip)
8800 {
8801 	if (clip) {
8802 		BRegion updateRegion;
8803 		updateRegion.Set(updateRect);
8804 		ConstrainClippingRegion(&updateRegion);
8805 	}
8806 
8807 	Invalidate(updateRect);
8808 	Window()->UpdateIfNeeded();
8809 
8810 	if (clip)
8811 		ConstrainClippingRegion(NULL);
8812 }
8813 
8814 
8815 void
8816 BPoseView::DrawViewCommon(const BRect &updateRect)
8817 {
8818 	if (ViewMode() == kListMode) {
8819 		PoseList* poseList = CurrentPoseList();
8820 		int32 count = poseList->CountItems();
8821 		int32 startIndex = (int32)((updateRect.top - fListElemHeight) / fListElemHeight);
8822 		if (startIndex < 0)
8823 			startIndex = 0;
8824 
8825 		BPoint loc(0, startIndex * fListElemHeight);
8826 
8827 		for (int32 index = startIndex; index < count; index++) {
8828 			BPose* pose = poseList->ItemAt(index);
8829 			BRect poseRect(pose->CalcRect(loc, this, true));
8830 			pose->Draw(poseRect, updateRect, this, true);
8831 			loc.y += fListElemHeight;
8832 			if (loc.y >= updateRect.bottom)
8833 				break;
8834 		}
8835 	} else {
8836 		int32 count = fPoseList->CountItems();
8837 		for (int32 index = 0; index < count; index++) {
8838 			BPose* pose = fPoseList->ItemAt(index);
8839 			BRect poseRect(pose->CalcRect(this));
8840 			if (updateRect.Intersects(poseRect))
8841 				pose->Draw(poseRect, updateRect, this, true);
8842 		}
8843 	}
8844 }
8845 
8846 
8847 void
8848 BPoseView::ColumnRedraw(BRect updateRect)
8849 {
8850 	// used for dynamic column resizing using an offscreen draw buffer
8851 	ASSERT(ViewMode() == kListMode);
8852 
8853 	if (IsDesktopWindow()) {
8854 		BScreen	screen(Window());
8855 		rgb_color d = screen.DesktopColor();
8856 		SetLowColor(d);
8857 		SetViewColor(d);
8858 	}
8859 
8860 	int32 startIndex = (int32)((updateRect.top - fListElemHeight) / fListElemHeight);
8861 	if (startIndex < 0)
8862 		startIndex = 0;
8863 
8864 	PoseList* poseList = CurrentPoseList();
8865 	int32 count = poseList->CountItems();
8866 	if (!count)
8867 		return;
8868 
8869 	BPoint loc(0, startIndex * fListElemHeight);
8870 	BRect srcRect = poseList->ItemAt(0)->CalcRect(BPoint(0, 0), this, false);
8871 	srcRect.right += 1024;	// need this to erase correctly
8872 	sOffscreen->BeginUsing(srcRect);
8873 	BView* offscreenView = sOffscreen->View();
8874 
8875 	BRegion updateRegion;
8876 	updateRegion.Set(updateRect);
8877 	ConstrainClippingRegion(&updateRegion);
8878 
8879 	for (int32 index = startIndex; index < count; index++) {
8880 		BPose* pose = poseList->ItemAt(index);
8881 
8882 		offscreenView->SetDrawingMode(B_OP_COPY);
8883 		offscreenView->SetLowColor(LowColor());
8884 		offscreenView->FillRect(offscreenView->Bounds(), B_SOLID_LOW);
8885 
8886 		BRect dstRect = srcRect;
8887 		dstRect.OffsetTo(loc);
8888 
8889 		BPoint offsetBy(0, -(index * ListElemHeight()));
8890 		pose->Draw(dstRect, updateRect, this, offscreenView, true,
8891 			offsetBy, pose->IsSelected());
8892 
8893 		offscreenView->Sync();
8894 		SetDrawingMode(B_OP_COPY);
8895 		DrawBitmap(sOffscreen->Bitmap(), srcRect, dstRect);
8896 		loc.y += fListElemHeight;
8897 		if (loc.y > updateRect.bottom)
8898 			break;
8899 	}
8900 	sOffscreen->DoneUsing();
8901 	ConstrainClippingRegion(0);
8902 }
8903 
8904 
8905 void
8906 BPoseView::CloseGapInList(BRect* invalidRect)
8907 {
8908 	(*invalidRect).bottom = Extent().bottom + fListElemHeight;
8909 	BRect bounds(Bounds());
8910 
8911 	if (bounds.Intersects(*invalidRect)) {
8912 		BRect destRect(*invalidRect);
8913 		destRect = destRect & bounds;
8914 		destRect.bottom -= fListElemHeight;
8915 
8916 		BRect srcRect(destRect);
8917 		srcRect.OffsetBy(0, fListElemHeight);
8918 
8919 		if (srcRect.Intersects(bounds) || destRect.Intersects(bounds))
8920 			CopyBits(srcRect, destRect);
8921 
8922 		*invalidRect = srcRect;
8923 		(*invalidRect).top = destRect.bottom;
8924 	}
8925 }
8926 
8927 
8928 void
8929 BPoseView::CheckPoseSortOrder(BPose* pose, int32 oldIndex)
8930 {
8931 	_CheckPoseSortOrder(CurrentPoseList(), pose, oldIndex);
8932 }
8933 
8934 
8935 void
8936 BPoseView::_CheckPoseSortOrder(PoseList* poseList, BPose* pose, int32 oldIndex)
8937 {
8938 	if (ViewMode() != kListMode)
8939 		return;
8940 
8941 	Window()->UpdateIfNeeded();
8942 
8943 	// take pose out of list for BSearch
8944 	poseList->RemoveItemAt(oldIndex);
8945 	int32 afterIndex;
8946 	int32 orientation = BSearchList(poseList, pose, &afterIndex, oldIndex);
8947 
8948 	int32 newIndex;
8949 	if (orientation == kInsertAtFront)
8950 		newIndex = 0;
8951 	else
8952 		newIndex = afterIndex + 1;
8953 
8954 	if (newIndex == oldIndex) {
8955 		poseList->AddItem(pose, oldIndex);
8956 		return;
8957 	}
8958 
8959 	if (fFiltering && poseList != fFilteredPoseList) {
8960 		poseList->AddItem(pose, newIndex);
8961 		return;
8962 	}
8963 
8964 	BRect invalidRect(CalcPoseRectList(pose, oldIndex));
8965 	CloseGapInList(&invalidRect);
8966 	Invalidate(invalidRect);
8967 		// need to invalidate for the last item in the list
8968 	InsertPoseAfter(pose, &afterIndex, orientation, &invalidRect);
8969 	poseList->AddItem(pose, newIndex);
8970 	Invalidate(invalidRect);
8971 }
8972 
8973 
8974 static int
8975 PoseCompareAddWidget(const BPose* p1, const BPose* p2, BPoseView* view)
8976 {
8977 	// pose comparison and lazy text widget adding
8978 
8979 	uint32 sort = view->PrimarySort();
8980 	BColumn* column = view->ColumnFor(sort);
8981 	if (!column)
8982 		return 0;
8983 
8984 	BPose* primary;
8985 	BPose* secondary;
8986 	if (!view->ReverseSort()) {
8987 		primary = const_cast<BPose*>(p1);
8988 		secondary = const_cast<BPose*>(p2);
8989 	} else {
8990 		primary = const_cast<BPose*>(p2);
8991 		secondary = const_cast<BPose*>(p1);
8992 	}
8993 
8994 	int32 result = 0;
8995 	for (int32 count = 0; ; count++) {
8996 
8997 		BTextWidget* widget1 = primary->WidgetFor(sort);
8998 		if (!widget1)
8999 			widget1 = primary->AddWidget(view, column);
9000 
9001 		BTextWidget* widget2 = secondary->WidgetFor(sort);
9002 		if (!widget2)
9003 			widget2 = secondary->AddWidget(view, column);
9004 
9005 		if (!widget1 || !widget2)
9006 			return result;
9007 
9008 		result = widget1->Compare(*widget2, view);
9009 
9010 		if (count)
9011 			return result;
9012 
9013 		// do we need to sort by secondary attribute?
9014 		if (result == 0) {
9015 			sort = view->SecondarySort();
9016 			if (!sort)
9017 				return result;
9018 
9019 			column = view->ColumnFor(sort);
9020 			if (!column)
9021 				return result;
9022 		}
9023 	}
9024 
9025 	return result;
9026 }
9027 
9028 
9029 static BPose*
9030 BSearch(PoseList* table, const BPose* key, BPoseView* view,
9031 	int (*cmp)(const BPose*, const BPose*, BPoseView*), bool returnClosest)
9032 {
9033 	int32 r = table->CountItems();
9034 	BPose* result = 0;
9035 
9036 	for (int32 l = 1; l <= r;) {
9037 		int32 m = (l + r) / 2;
9038 
9039 		result = table->ItemAt(m - 1);
9040 		int32 compareResult = (cmp)(result, key, view);
9041 		if (compareResult == 0)
9042 			return result;
9043 		else if (compareResult < 0)
9044 			l = m + 1;
9045 		else
9046 			r = m - 1;
9047 	}
9048 	if (returnClosest)
9049 		return result;
9050 	return NULL;
9051 }
9052 
9053 
9054 int32
9055 BPoseView::BSearchList(PoseList* poseList, const BPose* pose,
9056 	int32* resultingIndex, int32 oldIndex)
9057 {
9058 	// check to see if insertion should be at beginning of list
9059 	const BPose* firstPose = poseList->FirstItem();
9060 	if (!firstPose)
9061 		return kInsertAtFront;
9062 
9063 	if (PoseCompareAddWidget(pose, firstPose, this) < 0) {
9064 		*resultingIndex = 0;
9065 		return kInsertAtFront;
9066 	}
9067 
9068 	int32 count = poseList->CountItems();
9069 
9070 	// look if old position is still ok, by comparing to siblings
9071 	bool valid = oldIndex > 0 && oldIndex < count - 1;
9072 	valid = valid && PoseCompareAddWidget(pose,
9073 		poseList->ItemAt(oldIndex - 1), this) >= 0;
9074 	// the current item is gone, so not oldIndex+1
9075 	valid = valid && PoseCompareAddWidget(pose,
9076 		poseList->ItemAt(oldIndex), this) <= 0;
9077 
9078 	if (valid) {
9079 		*resultingIndex = oldIndex - 1;
9080 		return kInsertAfter;
9081 	}
9082 
9083 	*resultingIndex = count - 1;
9084 
9085 	const BPose* searchResult = BSearch(poseList, pose, this,
9086 		PoseCompareAddWidget);
9087 
9088 	if (searchResult) {
9089 		// what are we doing here??
9090 		// looks like we are skipping poses with identical search results or
9091 		// something
9092 		int32 index = poseList->IndexOf(searchResult);
9093 		for (; index < count; index++) {
9094 			int32 result = PoseCompareAddWidget(pose, poseList->ItemAt(index),
9095 				this);
9096 			if (result <= 0) {
9097 				--index;
9098 				break;
9099 			}
9100 		}
9101 
9102 		if (index != count)
9103 			*resultingIndex = index;
9104 	}
9105 
9106 	return kInsertAfter;
9107 }
9108 
9109 
9110 void
9111 BPoseView::SetPrimarySort(uint32 attrHash)
9112 {
9113 	BColumn* column = ColumnFor(attrHash);
9114 
9115 	if (column) {
9116 		fViewState->SetPrimarySort(attrHash);
9117 		fViewState->SetPrimarySortType(column->AttrType());
9118 	}
9119 }
9120 
9121 
9122 void
9123 BPoseView::SetSecondarySort(uint32 attrHash)
9124 {
9125 	BColumn* column = ColumnFor(attrHash);
9126 
9127 	if (column) {
9128 		fViewState->SetSecondarySort(attrHash);
9129 		fViewState->SetSecondarySortType(column->AttrType());
9130 	} else {
9131 		fViewState->SetSecondarySort(0);
9132 		fViewState->SetSecondarySortType(0);
9133 	}
9134 }
9135 
9136 
9137 void
9138 BPoseView::SetReverseSort(bool reverse)
9139 {
9140 	fViewState->SetReverseSort(reverse);
9141 }
9142 
9143 
9144 inline int
9145 PoseCompareAddWidgetBinder(const BPose* p1, const BPose* p2, void* castToPoseView)
9146 {
9147 	return PoseCompareAddWidget(p1, p2, (BPoseView*)castToPoseView);
9148 }
9149 
9150 
9151 struct PoseComparator : public std::binary_function<const BPose*,
9152 	const BPose*, bool>
9153 {
9154 	PoseComparator(BPoseView* poseView): fPoseView(poseView) { }
9155 
9156 	bool operator() (const BPose* p1, const BPose* p2) {
9157 		return PoseCompareAddWidget(p1, p2, fPoseView) < 0;
9158 	}
9159 
9160 	BPoseView* fPoseView;
9161 };
9162 
9163 
9164 #if xDEBUG
9165 static BPose*
9166 DumpOne(BPose* pose, void*)
9167 {
9168 	pose->TargetModel()->PrintToStream(0);
9169 	return 0;
9170 }
9171 #endif
9172 
9173 
9174 void
9175 BPoseView::SortPoses()
9176 {
9177 	if (fTextWidgetToCheck != NULL)
9178 		fTextWidgetToCheck->CancelWait();
9179 	CommitActivePose();
9180 	// PRINT(("pose list count %d\n", fPoseList->CountItems()));
9181 #if xDEBUG
9182 	fPoseList->EachElement(DumpOne, 0);
9183 	PRINT(("===================\n"));
9184 #endif
9185 
9186 	BPose** poses = reinterpret_cast<BPose**>(
9187 		PoseList::Private(fPoseList).AsBList()->Items());
9188 	std::stable_sort(poses, &poses[fPoseList->CountItems()], PoseComparator(this));
9189 	if (fFiltering) {
9190 		poses = reinterpret_cast<BPose**>(
9191 			PoseList::Private(fFilteredPoseList).AsBList()->Items());
9192 		std::stable_sort(poses, &poses[fFilteredPoseList->CountItems()],
9193 			PoseComparator(this));
9194 	}
9195 }
9196 
9197 
9198 BColumn*
9199 BPoseView::ColumnFor(uint32 attr) const
9200 {
9201 	int32 count = fColumnList->CountItems();
9202 	for (int32 index = 0; index < count; index++) {
9203 		BColumn* column = ColumnAt(index);
9204 		if (column->AttrHash() == attr)
9205 			return column;
9206 	}
9207 
9208 	return NULL;
9209 }
9210 
9211 
9212 bool		// returns true if actually resized
9213 BPoseView::ResizeColumnToWidest(BColumn* column)
9214 {
9215 	ASSERT(ViewMode() == kListMode);
9216 
9217 	float maxWidth = kMinColumnWidth;
9218 
9219 	PoseList* poseList = CurrentPoseList();
9220 	int32 count = poseList->CountItems();
9221 	for (int32 i = 0; i < count; ++i) {
9222 		BTextWidget* widget = poseList->ItemAt(i)->WidgetFor(column->AttrHash());
9223 		if (widget) {
9224 			float width = widget->PreferredWidth(this);
9225 			if (width > maxWidth)
9226 				maxWidth = width;
9227 		}
9228 	}
9229 
9230 	if (maxWidth > kMinColumnWidth || maxWidth < column->Width()) {
9231 		ResizeColumn(column, maxWidth);
9232 		return true;
9233 	}
9234 
9235 	return false;
9236 }
9237 
9238 
9239 BPoint
9240 BPoseView::ResizeColumn(BColumn* column, float newSize,
9241 	float* lastLineDrawPos,
9242 	void (*drawLineFunc)(BPoseView*, BPoint, BPoint),
9243 	void (*undrawLineFunc)(BPoseView*, BPoint, BPoint))
9244 {
9245 	BRect sourceRect(Bounds());
9246 	BPoint result(sourceRect.RightBottom());
9247 
9248 	BRect destRect(sourceRect);
9249 		// we will use sourceRect and destRect for copyBits
9250 	BRect invalidateRect(sourceRect);
9251 		// this will serve to clean up after the invalidate
9252 	BRect columnDrawRect(sourceRect);
9253 		// we will use columnDrawRect to draw the actual resized column
9254 
9255 	bool shrinking = newSize < column->Width();
9256 	columnDrawRect.left = column->Offset();
9257 	columnDrawRect.right = column->Offset() + kTitleColumnRightExtraMargin
9258 		- kRoomForLine + newSize;
9259 	sourceRect.left = column->Offset() + kTitleColumnRightExtraMargin
9260 		- kRoomForLine + column->Width();
9261 	destRect.left = columnDrawRect.right;
9262 	destRect.right = destRect.left + sourceRect.Width();
9263 	invalidateRect.left = destRect.right;
9264 	invalidateRect.right = sourceRect.right;
9265 
9266 	column->SetWidth(newSize);
9267 
9268 	float offset = kColumnStart;
9269 	BColumn* last = fColumnList->FirstItem();
9270 
9271 
9272 	int32 count = fColumnList->CountItems();
9273 	for (int32 index = 0; index < count; index++) {
9274 		column = fColumnList->ItemAt(index);
9275 		column->SetOffset(offset);
9276 		last = column;
9277 		offset = last->Offset() + last->Width() + kTitleColumnExtraMargin;
9278 	}
9279 
9280 	if (shrinking) {
9281 		ColumnRedraw(columnDrawRect);
9282 		// dont have to undraw when shrinking
9283 		CopyBits(sourceRect, destRect);
9284 		if (drawLineFunc) {
9285 			ASSERT(lastLineDrawPos);
9286 			(drawLineFunc)(this, BPoint(destRect.left + kRoomForLine,
9287 					destRect.top),
9288 				BPoint(destRect.left + kRoomForLine, destRect.bottom));
9289 			*lastLineDrawPos = destRect.left + kRoomForLine;
9290 		}
9291 	} else {
9292 		CopyBits(sourceRect, destRect);
9293 		if (undrawLineFunc) {
9294 			ASSERT(lastLineDrawPos);
9295 			(undrawLineFunc)(this, BPoint(*lastLineDrawPos, sourceRect.top),
9296 				BPoint(*lastLineDrawPos, sourceRect.bottom));
9297 		}
9298 		if (drawLineFunc) {
9299 			ASSERT(lastLineDrawPos);
9300 			(drawLineFunc)(this, BPoint(destRect.left + kRoomForLine,
9301 					destRect.top),
9302 				BPoint(destRect.left + kRoomForLine, destRect.bottom));
9303 			*lastLineDrawPos = destRect.left + kRoomForLine;
9304 		}
9305 		ColumnRedraw(columnDrawRect);
9306 	}
9307 	if (invalidateRect.left < invalidateRect.right)
9308 		SynchronousUpdate(invalidateRect, true);
9309 
9310 	fStateNeedsSaving =  true;
9311 
9312 	return result;
9313 }
9314 
9315 
9316 void
9317 BPoseView::MoveColumnTo(BColumn* src, BColumn* dest)
9318 {
9319 	// find the leftmost boundary of columns we are about to reshuffle
9320 	float miny = src->Offset();
9321 	if (miny > dest->Offset())
9322 		miny = dest->Offset();
9323 
9324 	// ensure columns are in proper order in list
9325 	int32 index = fColumnList->IndexOf(dest);
9326 	fColumnList->RemoveItem(src, false);
9327 	fColumnList->AddItem(src, index);
9328 
9329 	float offset = kColumnStart;
9330 	BColumn* last = fColumnList->FirstItem();
9331 	int32 count = fColumnList->CountItems();
9332 
9333 	for (int32 index = 0; index < count; index++) {
9334 		BColumn* column = fColumnList->ItemAt(index);
9335 		column->SetOffset(offset);
9336 		last = column;
9337 		offset = last->Offset() + last->Width() + kTitleColumnExtraMargin
9338 			- kRoomForLine / 2;
9339 	}
9340 
9341 	// invalidate everything to the right of miny
9342 	BRect bounds(Bounds());
9343 	bounds.left = miny;
9344 	Invalidate(bounds);
9345 
9346 	fStateNeedsSaving =  true;
9347 }
9348 
9349 
9350 bool
9351 BPoseView::UpdateDropTarget(BPoint mouseLoc, const BMessage* dragMessage,
9352 	bool trackingContextMenu)
9353 {
9354 	ASSERT(dragMessage);
9355 
9356 	int32 index;
9357 	BPose* targetPose = FindPose(mouseLoc, &index);
9358 	if (targetPose != NULL && DragSelectionContains(targetPose, dragMessage))
9359 		targetPose = NULL;
9360 
9361 	if ((fCursorCheck && targetPose == fDropTarget)
9362 		|| (trackingContextMenu && !targetPose)) {
9363 		// no change
9364 		return false;
9365 	}
9366 
9367 	fCursorCheck = true;
9368 	if (fDropTarget && !DragSelectionContains(fDropTarget, dragMessage))
9369 		HiliteDropTarget(false);
9370 
9371 	fDropTarget = targetPose;
9372 
9373 	// dereference if symlink
9374 	Model* targetModel = NULL;
9375 	if (targetPose)
9376 		targetModel = targetPose->TargetModel();
9377 	Model tmpTarget;
9378 	if (targetModel && targetModel->IsSymLink()
9379 		&& tmpTarget.SetTo(targetPose->TargetModel()->EntryRef(), true, true)
9380 			== B_OK)
9381 		targetModel = &tmpTarget;
9382 
9383 	bool ignoreTypes = (modifiers() & B_CONTROL_KEY) != 0;
9384 	if (targetPose) {
9385 		if (CanHandleDragSelection(targetModel, dragMessage, ignoreTypes)) {
9386 			// new target is valid, select it
9387 			HiliteDropTarget(true);
9388 		} else {
9389 			fDropTarget = NULL;
9390 			fCursorCheck = false;
9391 		}
9392 	}
9393 	if (targetModel == NULL)
9394 		targetModel = TargetModel();
9395 
9396 	// if this is an OpenWith window, we'll have no target model
9397 	if (targetModel == NULL)
9398 		return false;
9399 
9400 	entry_ref srcRef;
9401 	if (targetModel->IsDirectory() && dragMessage->HasRef("refs")
9402 		&& dragMessage->FindRef("refs", &srcRef) == B_OK) {
9403 		Model srcModel (&srcRef);
9404 		if (!CheckDevicesEqual(&srcRef, targetModel)
9405 			&& !srcModel.IsVolume()
9406 			&& !srcModel.IsRoot()) {
9407 			BCursor copyCursor(B_CURSOR_ID_COPY);
9408 			SetViewCursor(&copyCursor);
9409 			return true;
9410 		}
9411 	}
9412 
9413 	SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
9414 	return true;
9415 }
9416 
9417 
9418 bool
9419 BPoseView::FrameForPose(BPose* targetpose, bool convert, BRect* poseRect)
9420 {
9421 	bool returnvalue = false;
9422 	BRect bounds(Bounds());
9423 
9424 	if (ViewMode() == kListMode) {
9425 		PoseList* poseList = CurrentPoseList();
9426 		int32 count = poseList->CountItems();
9427 		int32 startIndex = (int32)(bounds.top / fListElemHeight);
9428 
9429 		BPoint loc(0, startIndex * fListElemHeight);
9430 
9431 		for (int32 index = startIndex; index < count; index++) {
9432 			if (targetpose == poseList->ItemAt(index)) {
9433 				*poseRect = fDropTarget->CalcRect(loc, this, false);
9434 				returnvalue = true;
9435 			}
9436 
9437 			loc.y += fListElemHeight;
9438 			if (loc.y > bounds.bottom)
9439 				returnvalue = false;
9440 		}
9441 	} else {
9442 		int32 startIndex = FirstIndexAtOrBelow((int32)(bounds.top
9443 			- IconPoseHeight()), true);
9444 		int32 count = fVSPoseList->CountItems();
9445 
9446 		for (int32 index = startIndex; index < count; index++) {
9447 			BPose* pose = fVSPoseList->ItemAt(index);
9448 			if (pose) {
9449 				if (pose == fDropTarget) {
9450 					*poseRect = pose->CalcRect(this);
9451 					returnvalue = true;
9452 					break;
9453 				}
9454 
9455 				if (pose->Location(this).y > bounds.bottom) {
9456 					returnvalue = false;
9457 					break;
9458 				}
9459 			}
9460 		}
9461 	}
9462 
9463 	if (convert)
9464 		ConvertToScreen(poseRect);
9465 
9466 	return returnvalue;
9467 }
9468 
9469 
9470 const int32 kMenuTrackMargin = 20;
9471 bool
9472 BPoseView::MenuTrackingHook(BMenu* menu, void*)
9473 {
9474 	// return true if the menu should go away
9475 	if (!menu->LockLooper())
9476 		return false;
9477 
9478 	uint32 buttons;
9479 	BPoint location;
9480 	menu->GetMouse(&location, &buttons);
9481 
9482 	bool returnvalue = true;
9483 	// don't test for buttons up here and try to circumvent messaging
9484 	// lest you miss an invoke that will happen after the window goes away
9485 
9486 	BRect bounds(menu->Bounds());
9487 	bounds.InsetBy(-kMenuTrackMargin, -kMenuTrackMargin);
9488 	if (bounds.Contains(location))
9489 		// still in menu
9490 		returnvalue =  false;
9491 
9492 
9493 	if (returnvalue) {
9494 		menu->ConvertToScreen(&location);
9495 		int32 count = menu->CountItems();
9496 		for (int32 index = 0 ; index < count; index++) {
9497 			// iterate through all of the items in the menu
9498 			// if the submenu is showing, see if the mouse is in the submenu
9499 			BMenuItem* item = menu->ItemAt(index);
9500 			if (item && item->Submenu()) {
9501 				BWindow* window = item->Submenu()->Window();
9502 				bool inSubmenu = false;
9503 				if (window && window->Lock()) {
9504 					if (!window->IsHidden()) {
9505 						BRect frame(window->Frame());
9506 
9507 						frame.InsetBy(-kMenuTrackMargin, -kMenuTrackMargin);
9508 						inSubmenu = frame.Contains(location);
9509 					}
9510 					window->Unlock();
9511 					if (inSubmenu) {
9512 						// only one menu can have its window open bail now
9513 						returnvalue = false;
9514 						break;
9515 					}
9516 				}
9517 			}
9518 		}
9519 	}
9520 
9521 	menu->UnlockLooper();
9522 
9523 	return returnvalue;
9524 }
9525 
9526 
9527 void
9528 BPoseView::DragStop()
9529 {
9530 	fStartFrame.Set(0, 0, 0, 0);
9531 	BContainerWindow* window = ContainerWindow();
9532 	if (window)
9533 		window->DragStop();
9534 }
9535 
9536 
9537 void
9538 BPoseView::HiliteDropTarget(bool hiliteState)
9539 {
9540 	// hilites current drop target while dragging, does not modify selection list
9541 	if (!fDropTarget)
9542 		return;
9543 
9544 	// note: fAlreadySelectedDropTarget is a trick to avoid to really search
9545 	// fSelectionList. Another solution would be to add Hilite/IsHilited just
9546 	// like Select/IsSelected in BPose and let it handle this case internally
9547 
9548 	// can happen when starting a new drag
9549 	if (fAlreadySelectedDropTarget != fDropTarget)
9550 		fAlreadySelectedDropTarget = NULL;
9551 
9552 	// don't select, this droptarget was already part of a user selection
9553 	if (fDropTarget->IsSelected() && hiliteState) {
9554 		fAlreadySelectedDropTarget = fDropTarget;
9555 		return;
9556 	}
9557 
9558 	// don't unselect the fAlreadySelectedDropTarget
9559 	if ((fAlreadySelectedDropTarget == fDropTarget) && !hiliteState) {
9560 		fAlreadySelectedDropTarget = NULL;
9561 		return;
9562 	}
9563 
9564 	fDropTarget->Select(hiliteState);
9565 
9566 	// scan all visible poses
9567 	BRect bounds(Bounds());
9568 
9569 	if (ViewMode() == kListMode) {
9570 		PoseList* poseList = CurrentPoseList();
9571 		int32 count = poseList->CountItems();
9572 		int32 startIndex = (int32)(bounds.top / fListElemHeight);
9573 
9574 		BPoint loc(0, startIndex * fListElemHeight);
9575 
9576 		for (int32 index = startIndex; index < count; index++) {
9577 			if (fDropTarget == poseList->ItemAt(index)) {
9578 				BRect poseRect = fDropTarget->CalcRect(loc, this, false);
9579 				fDropTarget->Draw(poseRect, poseRect, this, false);
9580 				break;
9581 			}
9582 
9583 			loc.y += fListElemHeight;
9584 			if (loc.y > bounds.bottom)
9585 				break;
9586 		}
9587 	} else {
9588 		int32 startIndex = FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()), true);
9589 		int32 count = fVSPoseList->CountItems();
9590 
9591 		for (int32 index = startIndex; index < count; index++) {
9592 			BPose* pose = fVSPoseList->ItemAt(index);
9593 			if (pose) {
9594 				if (pose == fDropTarget) {
9595 					BRect poseRect = pose->CalcRect(this);
9596 					// TODO: maybe leave just the else part
9597 					if (!hiliteState)
9598 						// deselecting an icon with widget drawn over background
9599 						// have to be a little tricky here - draw just the icon,
9600 						// invalidate the widget
9601 						pose->DeselectWithoutErasingBackground(poseRect, this);
9602 					else
9603 						pose->Draw(poseRect, poseRect, this, false);
9604 					break;
9605 				}
9606 
9607 				if (pose->Location(this).y > bounds.bottom)
9608 					break;
9609 			}
9610 		}
9611 	}
9612 }
9613 
9614 
9615 bool
9616 BPoseView::CheckAutoScroll(BPoint mouseLoc, bool shouldScroll)
9617 {
9618 	if (!fShouldAutoScroll)
9619 		return false;
9620 
9621 	// make sure window is in front before attempting scrolling
9622 	BContainerWindow* window = ContainerWindow();
9623 	if (window == NULL)
9624 		return false;
9625 
9626 	BRect bounds(Bounds());
9627 	BRect extent(Extent());
9628 
9629 	bool wouldScroll = false;
9630 	bool keepGoing;
9631 	float scrollIncrement;
9632 
9633 	BRect border(bounds);
9634 	border.bottom = border.top;
9635 	border.top -= kBorderHeight;
9636 	if (ViewMode() == kListMode)
9637 		border.top -= kTitleViewHeight;
9638 
9639 	bool selectionScrolling = fSelectionRectInfo.isDragging;
9640 
9641 	if (bounds.top > extent.top) {
9642 		if (selectionScrolling) {
9643 			keepGoing = mouseLoc.y < bounds.top;
9644 			if (fabs(bounds.top - mouseLoc.y) > kSlowScrollBucket)
9645 				scrollIncrement = fAutoScrollInc / 1.5f;
9646 			else
9647 				scrollIncrement = fAutoScrollInc / 4;
9648 		} else {
9649 			keepGoing = border.Contains(mouseLoc);
9650 			scrollIncrement = fAutoScrollInc;
9651 		}
9652 
9653 		if (keepGoing) {
9654 			wouldScroll = true;
9655 			if (shouldScroll) {
9656 				if (fVScrollBar != NULL) {
9657 					fVScrollBar->SetValue(
9658 						fVScrollBar->Value() - scrollIncrement);
9659 				} else
9660 					ScrollBy(0, -scrollIncrement);
9661 			}
9662 		}
9663 	}
9664 
9665 	border = bounds;
9666 	border.top = border.bottom;
9667 	border.bottom += (float)B_H_SCROLL_BAR_HEIGHT;
9668 	if (bounds.bottom < extent.bottom) {
9669 		if (selectionScrolling) {
9670 			keepGoing = mouseLoc.y > bounds.bottom;
9671 			if (fabs(bounds.bottom - mouseLoc.y) > kSlowScrollBucket)
9672 				scrollIncrement = fAutoScrollInc / 1.5f;
9673 			else
9674 				scrollIncrement = fAutoScrollInc / 4;
9675 		} else {
9676 			keepGoing = border.Contains(mouseLoc);
9677 			scrollIncrement = fAutoScrollInc;
9678 		}
9679 
9680 		if (keepGoing) {
9681 			wouldScroll = true;
9682 			if (shouldScroll) {
9683 				if (fVScrollBar != NULL) {
9684 					fVScrollBar->SetValue(
9685 						fVScrollBar->Value() + scrollIncrement);
9686 				} else
9687 					ScrollBy(0, scrollIncrement);
9688 			}
9689 		}
9690 	}
9691 
9692 	border = bounds;
9693 	border.right = border.left;
9694 	border.left -= 6;
9695 	if (bounds.left > extent.left) {
9696 		if (selectionScrolling) {
9697 			keepGoing = mouseLoc.x < bounds.left;
9698 			if (fabs(bounds.left - mouseLoc.x) > kSlowScrollBucket)
9699 				scrollIncrement = fAutoScrollInc / 1.5f;
9700 			else
9701 				scrollIncrement = fAutoScrollInc / 4;
9702 		} else {
9703 			keepGoing = border.Contains(mouseLoc);
9704 			scrollIncrement = fAutoScrollInc;
9705 		}
9706 
9707 		if (keepGoing) {
9708 			wouldScroll = true;
9709 			if (shouldScroll) {
9710 				if (fHScrollBar != NULL) {
9711 					fHScrollBar->SetValue(
9712 						fHScrollBar->Value() - scrollIncrement);
9713 				} else
9714 					ScrollBy(-scrollIncrement, 0);
9715 			}
9716 		}
9717 	}
9718 
9719 	border = bounds;
9720 	border.left = border.right;
9721 	border.right += (float)B_V_SCROLL_BAR_WIDTH;
9722 	if (bounds.right < extent.right) {
9723 		if (selectionScrolling) {
9724 			keepGoing = mouseLoc.x > bounds.right;
9725 			if (fabs(bounds.right - mouseLoc.x) > kSlowScrollBucket)
9726 				scrollIncrement = fAutoScrollInc / 1.5f;
9727 			else
9728 				scrollIncrement = fAutoScrollInc / 4;
9729 		} else {
9730 			keepGoing = border.Contains(mouseLoc);
9731 			scrollIncrement = fAutoScrollInc;
9732 		}
9733 
9734 		if (keepGoing) {
9735 			wouldScroll = true;
9736 			if (shouldScroll) {
9737 				if (fHScrollBar != NULL) {
9738 					fHScrollBar->SetValue(
9739 						fHScrollBar->Value() + scrollIncrement);
9740  				} else
9741  					ScrollBy(scrollIncrement, 0);
9742 			}
9743 		}
9744 	}
9745 
9746 	// Force selection rect update to account for the new scrolled coords
9747 	// without a mouse move
9748 	if (selectionScrolling)
9749 		_UpdateSelectionRect(mouseLoc);
9750 
9751 	return wouldScroll;
9752 }
9753 
9754 
9755 void
9756 BPoseView::HandleAutoScroll()
9757 {
9758 	if (!fShouldAutoScroll)
9759 		return;
9760 
9761 	uint32 button;
9762 	BPoint mouseLoc;
9763 	GetMouse(&mouseLoc, &button);
9764 
9765 	if (!button) {
9766 		fAutoScrollState = kAutoScrollOff;
9767 		Window()->SetPulseRate(500000);
9768 		return;
9769 	}
9770 
9771 	switch (fAutoScrollState) {
9772 		case kWaitForTransition:
9773 			if (CheckAutoScroll(mouseLoc, false) == false)
9774 				fAutoScrollState = kDelayAutoScroll;
9775 			break;
9776 
9777 		case kDelayAutoScroll:
9778 			if (CheckAutoScroll(mouseLoc, false) == true) {
9779 				snooze(600000);
9780 				GetMouse(&mouseLoc, &button);
9781 				if (CheckAutoScroll(mouseLoc, false) == true)
9782 					fAutoScrollState = kAutoScrollOn;
9783 			}
9784 			break;
9785 
9786 		case kAutoScrollOn:
9787 			CheckAutoScroll(mouseLoc, true);
9788 			break;
9789 	}
9790 }
9791 
9792 
9793 BRect
9794 BPoseView::CalcPoseRect(const BPose* pose, int32 index,
9795 	bool firstColumnOnly) const
9796 {
9797 	if (ViewMode() == kListMode)
9798 		return CalcPoseRectList(pose, index, firstColumnOnly);
9799 	else
9800 		return CalcPoseRectIcon(pose);
9801 }
9802 
9803 
9804 BRect
9805 BPoseView::CalcPoseRectIcon(const BPose* pose) const
9806 {
9807 	return pose->CalcRect(this);
9808 }
9809 
9810 
9811 BRect
9812 BPoseView::CalcPoseRectList(const BPose* pose, int32 index,
9813 	bool firstColumnOnly) const
9814 {
9815 	return pose->CalcRect(BPoint(0, index * fListElemHeight), this,
9816 		firstColumnOnly);
9817 }
9818 
9819 
9820 bool
9821 BPoseView::Represents(const node_ref* node) const
9822 {
9823 	return *(fModel->NodeRef()) == *node;
9824 }
9825 
9826 
9827 bool
9828 BPoseView::Represents(const entry_ref* ref) const
9829 {
9830 	return *fModel->EntryRef() == *ref;
9831 }
9832 
9833 
9834 void
9835 BPoseView::ShowBarberPole()
9836 {
9837 	if (fCountView) {
9838 		AutoLock<BWindow> lock(Window());
9839 		if (!lock)
9840 			return;
9841 		fCountView->StartBarberPole();
9842 	}
9843 }
9844 
9845 
9846 void
9847 BPoseView::HideBarberPole()
9848 {
9849 	if (fCountView) {
9850 		AutoLock<BWindow> lock(Window());
9851 		if (!lock)
9852 			return;
9853 		fCountView->EndBarberPole();
9854 	}
9855 }
9856 
9857 
9858 bool
9859 BPoseView::IsWatchingDateFormatChange()
9860 {
9861 	return fIsWatchingDateFormatChange;
9862 }
9863 
9864 
9865 void
9866 BPoseView::StartWatchDateFormatChange()
9867 {
9868 // TODO: Workaround for R5 (overful message queue)!
9869 // Unfortunately, this causes a dead-lock under certain circumstances.
9870 #if !defined(HAIKU_TARGET_PLATFORM_HAIKU) && !defined(HAIKU_TARGET_PLATFORM_DANO)
9871 	if (IsFilePanel()) {
9872 #endif
9873 		BMessenger tracker(kTrackerSignature);
9874 		BHandler::StartWatching(tracker, kDateFormatChanged);
9875 #if !defined(HAIKU_TARGET_PLATFORM_HAIKU) && !defined(HAIKU_TARGET_PLATFORM_DANO)
9876 	} else {
9877 		be_app->LockLooper();
9878 		be_app->StartWatching(this, kDateFormatChanged);
9879 		be_app->UnlockLooper();
9880 	}
9881 #endif
9882 
9883 	fIsWatchingDateFormatChange = true;
9884 }
9885 
9886 
9887 void
9888 BPoseView::StopWatchDateFormatChange()
9889 {
9890 	if (IsFilePanel()) {
9891 		BMessenger tracker(kTrackerSignature);
9892 		BHandler::StopWatching(tracker, kDateFormatChanged);
9893 	} else {
9894 		be_app->LockLooper();
9895 		be_app->StopWatching(this, kDateFormatChanged);
9896 		be_app->UnlockLooper();
9897 	}
9898 
9899 	fIsWatchingDateFormatChange = false;
9900 }
9901 
9902 
9903 void
9904 BPoseView::UpdateDateColumns(BMessage* message)
9905 {
9906 	int32 columnCount = CountColumns();
9907 
9908 	BRect columnRect(Bounds());
9909 
9910 	for (int32 i = 0; i < columnCount; i++) {
9911 		BColumn* col = ColumnAt(i);
9912 		if (col && col->AttrType() == B_TIME_TYPE) {
9913 			columnRect.left = col->Offset();
9914 			columnRect.right = columnRect.left + col->Width();
9915 			Invalidate(columnRect);
9916 		}
9917 	}
9918 }
9919 
9920 
9921 void
9922 BPoseView::AdaptToVolumeChange(BMessage*)
9923 {
9924 }
9925 
9926 
9927 void
9928 BPoseView::AdaptToDesktopIntegrationChange(BMessage*)
9929 {
9930 }
9931 
9932 
9933 bool
9934 BPoseView::WidgetTextOutline() const
9935 {
9936 	return fWidgetTextOutline;
9937 }
9938 
9939 
9940 void
9941 BPoseView::SetWidgetTextOutline(bool on)
9942 {
9943 	fWidgetTextOutline = on;
9944 }
9945 
9946 
9947 void
9948 BPoseView::EnsurePoseUnselected(BPose* pose)
9949 {
9950 	if (pose == fDropTarget)
9951 		fDropTarget = NULL;
9952 
9953 	if (pose == ActivePose())
9954 		CommitActivePose();
9955 
9956 	fSelectionList->RemoveItem(pose);
9957 	if (fSelectionPivotPose == pose)
9958 		fSelectionPivotPose = NULL;
9959 	if (fRealPivotPose == pose)
9960 		fRealPivotPose = NULL;
9961 
9962 	if (pose->IsSelected()) {
9963 		pose->Select(false);
9964 		if (fSelectionChangedHook)
9965 			ContainerWindow()->SelectionChanged();
9966 	}
9967 }
9968 
9969 
9970 void
9971 BPoseView::RemoveFilteredPose(BPose* pose, int32 index)
9972 {
9973 	EnsurePoseUnselected(pose);
9974 	fFilteredPoseList->RemoveItemAt(index);
9975 
9976 	BRect invalidRect = CalcPoseRectList(pose, index);
9977 	CloseGapInList(&invalidRect);
9978 
9979 	Invalidate(invalidRect);
9980 }
9981 
9982 
9983 void
9984 BPoseView::FilterChanged()
9985 {
9986 	if (ViewMode() != kListMode)
9987 		return;
9988 
9989 	int32 stringCount = fFilterStrings.CountItems();
9990 	int32 length = fFilterStrings.LastItem()->CountChars();
9991 
9992 	if (!fFiltering && (length > 0 || fRefFilter != NULL))
9993 		StartFiltering();
9994 	else if (fFiltering && stringCount == 1 && length == 0
9995 		&& fRefFilter == NULL)
9996 		ClearFilter();
9997 	else {
9998 		if (fLastFilterStringCount > stringCount
9999 			|| (fLastFilterStringCount == stringCount
10000 				&& fLastFilterStringLength > length)
10001 			|| fRefFilter != NULL) {
10002 			// something was removed, need to start over
10003 			fFilteredPoseList->MakeEmpty();
10004 			fFiltering = false;
10005 			StartFiltering();
10006 		} else {
10007 			int32 count = fFilteredPoseList->CountItems();
10008 			for (int32 i = count - 1; i >= 0; i--) {
10009 				BPose* pose = fFilteredPoseList->ItemAt(i);
10010 				if (!FilterPose(pose))
10011 					RemoveFilteredPose(pose, i);
10012 			}
10013 		}
10014 	}
10015 
10016 	fLastFilterStringCount = stringCount;
10017 	fLastFilterStringLength = length;
10018 	UpdateAfterFilterChange();
10019 }
10020 
10021 
10022 void
10023 BPoseView::UpdateAfterFilterChange()
10024 {
10025 	UpdateCount();
10026 
10027 	BPose* pose = fFilteredPoseList->LastItem();
10028 	if (pose == NULL)
10029 		BView::ScrollTo(0, 0);
10030 	else {
10031 		BRect bounds = Bounds();
10032 		float height = fFilteredPoseList->CountItems() * fListElemHeight;
10033 		if (bounds.top > 0 && bounds.bottom > height)
10034 			BView::ScrollTo(0, max_c(height - bounds.Height(), 0));
10035 	}
10036 
10037 	UpdateScrollRange();
10038 }
10039 
10040 
10041 bool
10042 BPoseView::FilterPose(BPose* pose)
10043 {
10044 	if (!fFiltering || pose == NULL)
10045 		return false;
10046 
10047 	if (fRefFilter != NULL) {
10048 		PoseInfo poseInfo;
10049 		ReadPoseInfo(pose->TargetModel(), &poseInfo);
10050 		pose->TargetModel()->OpenNode();
10051 		if (!ShouldShowPose(pose->TargetModel(), &poseInfo))
10052 			return false;
10053 	}
10054 
10055 	int32 stringCount = fFilterStrings.CountItems();
10056 	int32 matchesLeft = stringCount;
10057 
10058 	bool found[stringCount];
10059 	memset(found, 0, sizeof(found));
10060 
10061 	ModelNodeLazyOpener modelOpener(pose->TargetModel());
10062 	for (int32 i = 0; i < CountColumns(); i++) {
10063 		BTextWidget* widget = pose->WidgetFor(ColumnAt(i), this, modelOpener);
10064 		const char* text = NULL;
10065 		if (widget == NULL)
10066 			continue;
10067 
10068 		text = widget->Text(this);
10069 		if (text == NULL)
10070 			continue;
10071 
10072 		for (int32 j = 0; j < stringCount; j++) {
10073 			if (found[j])
10074 				continue;
10075 
10076 			if (strcasestr(text, fFilterStrings.ItemAt(j)->String()) != NULL) {
10077 				if (--matchesLeft == 0)
10078 					return true;
10079 
10080 				found[j] = true;
10081 			}
10082 		}
10083 	}
10084 	return false;
10085 }
10086 
10087 
10088 void
10089 BPoseView::StartFiltering()
10090 {
10091 	if (fFiltering)
10092 		return;
10093 
10094 	fFiltering = true;
10095 	int32 count = fPoseList->CountItems();
10096 	for (int32 i = 0; i < count; i++) {
10097 		BPose* pose = fPoseList->ItemAt(i);
10098 		if (FilterPose(pose))
10099 			fFilteredPoseList->AddItem(pose);
10100 		else
10101 			EnsurePoseUnselected(pose);
10102 	}
10103 
10104 	Invalidate();
10105 }
10106 
10107 
10108 bool
10109 BPoseView::IsFiltering() const
10110 {
10111 	return fFiltering;
10112 }
10113 
10114 
10115 void
10116 BPoseView::StopFiltering()
10117 {
10118 	ClearFilter();
10119 	UpdateAfterFilterChange();
10120 }
10121 
10122 
10123 void
10124 BPoseView::ClearFilter()
10125 {
10126 	if (!fFiltering)
10127 		return;
10128 
10129 	fCountView->CancelFilter();
10130 
10131 	int32 stringCount = fFilterStrings.CountItems();
10132 	for (int32 i = stringCount - 1; i > 0; i--)
10133 		delete fFilterStrings.RemoveItemAt(i);
10134 
10135 	fFilterStrings.LastItem()->Truncate(0);
10136 	fLastFilterStringCount = 1;
10137 	fLastFilterStringLength = 0;
10138 
10139 	fFiltering = false;
10140 	fFilteredPoseList->MakeEmpty();
10141 
10142 	Invalidate();
10143 }
10144 
10145 
10146 //	#pragma mark -
10147 
10148 
10149 BHScrollBar::BHScrollBar(BRect bounds, const char* name, BView* target)
10150 	:	BScrollBar(bounds, name, target, 0, 1, B_HORIZONTAL),
10151 		fTitleView(0)
10152 {
10153 }
10154 
10155 
10156 void
10157 BHScrollBar::ValueChanged(float value)
10158 {
10159 	if (fTitleView) {
10160 		BPoint origin = fTitleView->LeftTop();
10161 		fTitleView->ScrollTo(BPoint(value, origin.y));
10162 	}
10163 
10164 	_inherited::ValueChanged(value);
10165 }
10166 
10167 
10168 TPoseViewFilter::TPoseViewFilter(BPoseView* pose)
10169 	:	BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
10170 		fPoseView(pose)
10171 {
10172 }
10173 
10174 
10175 TPoseViewFilter::~TPoseViewFilter()
10176 {
10177 }
10178 
10179 
10180 filter_result
10181 TPoseViewFilter::Filter(BMessage* message, BHandler**)
10182 {
10183 	filter_result result = B_DISPATCH_MESSAGE;
10184 
10185 	switch (message->what) {
10186 		case B_ARCHIVED_OBJECT:
10187 			bool handled = fPoseView->HandleMessageDropped(message);
10188 			if (handled)
10189 				result = B_SKIP_MESSAGE;
10190 			break;
10191 	}
10192 
10193 	return result;
10194 }
10195 
10196 
10197 // static member initializations
10198 
10199 float BPoseView::sFontHeight = -1;
10200 font_height BPoseView::sFontInfo = { 0, 0, 0 };
10201 BFont BPoseView::sCurrentFont;
10202 OffscreenBitmap* BPoseView::sOffscreen = new OffscreenBitmap;
10203 BString BPoseView::sMatchString = "";
10204