1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All rights reserved.
33*/
34
35
36#include <stdlib.h>
37#include <string.h>
38
39#include <Debug.h>
40#include <NodeMonitor.h>
41#include <Volume.h>
42#include <fs_info.h>
43
44#include "Attributes.h"
45#include "Commands.h"
46#include "FSClipboard.h"
47#include "IconCache.h"
48#include "Pose.h"
49#include "PoseView.h"
50
51
52int32
53CalcFreeSpace(BVolume* volume)
54{
55	off_t capacity = volume->Capacity();
56	if (capacity == 0)
57		return 100;
58
59	int32 percent
60		= static_cast<int32>(volume->FreeBytes() / (capacity / 100));
61
62	// warn below 5% of free space
63	if (percent < 5)
64		return -2 - percent;
65	return percent;
66}
67
68
69// SymLink handling:
70// symlink pose uses the resolved model to retrieve the icon, if not broken
71// everything else, like the attributes, etc. is retrieved directly from the
72// symlink itself
73BPose::BPose(Model* model, BPoseView* view, uint32 clipboardMode,
74	bool selected)
75	:
76	fModel(model),
77	fWidgetList(4, false),
78	fClipboardMode(clipboardMode),
79	fPercent(-1),
80	fSelectionTime(0),
81	fIsSelected(selected),
82	fHasLocation(false),
83	fNeedsSaveLocation(false),
84	fListModeInited(false),
85	fWasAutoPlaced(false),
86	fBrokenSymLink(false),
87	fBackgroundClean(false)
88{
89	CreateWidgets(view);
90
91	if (model->IsVolume()) {
92		fs_info info;
93		dev_t device = model->NodeRef()->device;
94		BVolume* volume = new BVolume(device);
95		if (volume->InitCheck() == B_OK
96			&& fs_stat_dev(device, &info) == B_OK) {
97			// Philosophy here:
98			// Bars go on all read/write volumes
99			// Exceptions: Not on CDDA
100			if (strcmp(info.fsh_name,"cdda") != 0
101				&& !volume->IsReadOnly()) {
102				// The volume is ok and we want space bars on it
103				gPeriodicUpdatePoses.AddPose(this, view,
104					_PeriodicUpdateCallback, volume);
105				if (TrackerSettings().ShowVolumeSpaceBar())
106					fPercent = CalcFreeSpace(volume);
107			} else
108				delete volume;
109		} else
110			delete volume;
111	}
112}
113
114
115BPose::~BPose()
116{
117	if (fModel->IsVolume()) {
118		// we might be registered for periodic updates
119		BVolume* volume = NULL;
120		if (gPeriodicUpdatePoses.RemovePose(this, (void**)&volume))
121			delete volume;
122	}
123	int32 count = fWidgetList.CountItems();
124	for (int32 i = 0; i < count; i++)
125		delete fWidgetList.ItemAt(i);
126
127	delete fModel;
128}
129
130
131void
132BPose::CreateWidgets(BPoseView* poseView)
133{
134	for (int32 index = 0; ; index++) {
135		BColumn* column = poseView->ColumnAt(index);
136		if (column == NULL)
137			break;
138
139		fWidgetList.AddItem(new BTextWidget(fModel, column, poseView));
140	}
141}
142
143
144BTextWidget*
145BPose::AddWidget(BPoseView* poseView, BColumn* column)
146{
147	BModelOpener opener(fModel);
148	if (fModel->InitCheck() != B_OK)
149		return NULL;
150
151	BTextWidget* widget = new BTextWidget(fModel, column, poseView);
152	fWidgetList.AddItem(widget);
153
154	return widget;
155}
156
157
158BTextWidget*
159BPose::AddWidget(BPoseView* poseView, BColumn* column,
160	ModelNodeLazyOpener &opener)
161{
162	opener.OpenNode();
163	if (fModel->InitCheck() != B_OK)
164		return NULL;
165
166	BTextWidget* widget = new BTextWidget(fModel, column, poseView);
167	fWidgetList.AddItem(widget);
168
169	return widget;
170}
171
172
173void
174BPose::RemoveWidget(BPoseView*, BColumn* column)
175{
176	int32 index;
177	BTextWidget* widget = WidgetFor(column->AttrHash(), &index);
178	if (widget != NULL)
179		delete fWidgetList.RemoveItemAt(index);
180}
181
182
183void
184BPose::Commit(bool saveChanges, BPoint loc, BPoseView* poseView,
185	int32 poseIndex)
186{
187	int32 count = fWidgetList.CountItems();
188	for (int32 index = 0; index < count; index++) {
189		BTextWidget* widget = fWidgetList.ItemAt(index);
190		if (widget != NULL && widget->IsActive()) {
191			widget->StopEdit(saveChanges, loc, poseView, this, poseIndex);
192			break;
193		}
194	}
195}
196
197
198inline bool
199OneMouseUp(BTextWidget* widget, BPose* pose, BPoseView* poseView,
200	BColumn* column, BPoint poseLoc, BPoint where)
201{
202	BRect rect;
203	if (poseView->ViewMode() == kListMode)
204		rect = widget->CalcClickRect(poseLoc, column, poseView);
205	else
206		rect = widget->CalcClickRect(pose->Location(poseView), NULL, poseView);
207
208	if (rect.Contains(where)) {
209		widget->MouseUp(rect, poseView, pose, where);
210		return true;
211	}
212
213	return false;
214}
215
216
217void
218BPose::MouseUp(BPoint poseLoc, BPoseView* poseView, BPoint where, int32)
219{
220	WhileEachTextWidget(this, poseView, OneMouseUp, poseLoc, where);
221}
222
223
224inline void
225OneCheckAndUpdate(BTextWidget* widget, BPose*, BPoseView* poseView,
226	BColumn* column, BPoint poseLoc)
227{
228	widget->CheckAndUpdate(poseLoc, column, poseView, true);
229}
230
231
232void
233BPose::UpdateAllWidgets(int32, BPoint poseLoc, BPoseView* poseView)
234{
235	if (poseView->ViewMode() != kListMode)
236		poseLoc = Location(poseView);
237
238	ASSERT(fModel->IsNodeOpen());
239	EachTextWidget(this, poseView, OneCheckAndUpdate, poseLoc);
240}
241
242
243void
244BPose::UpdateWidgetAndModel(Model* resolvedModel, const char* attrName,
245	uint32 attrType, int32, BPoint poseLoc, BPoseView* poseView, bool visible)
246{
247	if (poseView->ViewMode() != kListMode)
248		poseLoc = Location(poseView);
249
250	ASSERT(resolvedModel == NULL || resolvedModel->IsNodeOpen());
251
252	if (attrName != NULL) {
253		// pick up new attributes and find out if icon needs updating
254		if (resolvedModel->AttrChanged(attrName) && visible)
255			UpdateIcon(poseLoc, poseView);
256
257		// ToDo: the following code is wrong, because this sort of hashing
258		// may overlap and we get aliasing
259		uint32 attrHash = AttrHashString(attrName, attrType);
260		BTextWidget* widget = WidgetFor(attrHash);
261		if (widget != NULL) {
262			BColumn* column = poseView->ColumnFor(attrHash);
263			if (column != NULL)
264				widget->CheckAndUpdate(poseLoc, column, poseView, visible);
265		} else if (attrType == 0) {
266			// attribute got likely removed, so let's search the
267			// column for the matching attribute name
268			int32 count = fWidgetList.CountItems();
269			for (int32 i = 0; i < count; i++) {
270				BTextWidget* widget = fWidgetList.ItemAt(i);
271				BColumn* column = poseView->ColumnFor(widget->AttrHash());
272				if (column != NULL
273					&& strcmp(column->AttrName(), attrName) == 0) {
274					widget->CheckAndUpdate(poseLoc, column, poseView, visible);
275					break;
276				}
277			}
278		}
279	} else {
280		// no attr name means check all widgets for stat info changes
281
282		// pick up stat changes
283		if (resolvedModel && resolvedModel->StatChanged()) {
284			if (resolvedModel->InitCheck() != B_OK)
285				return;
286
287			if (visible)
288				UpdateIcon(poseLoc, poseView);
289		}
290
291		// distribute stat changes
292		for (int32 index = 0; ; index++) {
293			BColumn* column = poseView->ColumnAt(index);
294			if (column == NULL)
295				break;
296
297			if (column->StatField()) {
298				BTextWidget* widget = WidgetFor(column->AttrHash());
299				if (widget != NULL)
300					widget->CheckAndUpdate(poseLoc, column, poseView, visible);
301			}
302		}
303	}
304}
305
306
307bool
308BPose::_PeriodicUpdateCallback(BPose* pose, void* cookie)
309{
310	return pose->UpdateVolumeSpaceBar((BVolume*)cookie);
311}
312
313
314bool
315BPose::UpdateVolumeSpaceBar(BVolume* volume)
316{
317	bool enabled = TrackerSettings().ShowVolumeSpaceBar();
318	if (!enabled) {
319		if (fPercent == -1)
320			return false;
321
322		fPercent = -1;
323		return true;
324	}
325
326	int32 percent = CalcFreeSpace(volume);
327	if (fPercent != percent) {
328		if (percent > 100)
329			fPercent = 100;
330		else
331			fPercent = percent;
332
333		return true;
334	}
335
336	return false;
337}
338
339
340void
341BPose::UpdateIcon(BPoint poseLoc, BPoseView* poseView)
342{
343	IconCache::sIconCache->IconChanged(ResolvedModel());
344
345	int32 iconSize = poseView->IconSizeInt();
346
347	BRect rect;
348	if (poseView->ViewMode() == kListMode) {
349		rect = CalcRect(poseLoc, poseView);
350		rect.left += kListOffset;
351		rect.right = rect.left + iconSize;
352		rect.top = rect.bottom - iconSize;
353	} else {
354		BPoint location = Location(poseView);
355		rect.left = location.x;
356		rect.top = location.y;
357		rect.right = rect.left + iconSize;
358		rect.bottom = rect.top + iconSize;
359	}
360
361	poseView->Invalidate(rect);
362}
363
364
365void
366BPose::UpdateBrokenSymLink(BPoint poseLoc, BPoseView* poseView)
367{
368	ASSERT(TargetModel()->IsSymLink());
369	ASSERT(TargetModel()->LinkTo() == NULL);
370	if (!TargetModel()->IsSymLink() || TargetModel()->LinkTo() != NULL)
371		return;
372
373	UpdateIcon(poseLoc, poseView);
374}
375
376
377void
378BPose::UpdateWasBrokenSymlink(BPoint poseLoc, BPoseView* poseView)
379{
380	if (!fModel->IsSymLink())
381		return;
382
383	if (fModel->LinkTo() != NULL) {
384		BEntry entry(fModel->EntryRef(), true);
385		if (entry.InitCheck() != B_OK) {
386			watch_node(fModel->LinkTo()->NodeRef(), B_STOP_WATCHING, poseView);
387			fModel->SetLinkTo(NULL);
388			UpdateIcon(poseLoc, poseView);
389		}
390		return;
391	}
392
393	poseView->CreateSymlinkPoseTarget(fModel);
394	UpdateIcon(poseLoc, poseView);
395	if (fModel->LinkTo() != NULL)
396		fModel->LinkTo()->CloseNode();
397}
398
399
400void
401BPose::EditFirstWidget(BPoint poseLoc, BPoseView* poseView)
402{
403	// find first editable widget
404	BColumn* column;
405	for (int32 i = 0; (column = poseView->ColumnAt(i)) != NULL; i++) {
406		BTextWidget* widget = WidgetFor(column->AttrHash());
407
408		if (widget != NULL && widget->IsEditable()) {
409			BRect bounds;
410			// ToDo:
411			// fold the three StartEdit code sequences into a cover call
412			if (poseView->ViewMode() == kListMode)
413				bounds = widget->CalcRect(poseLoc, column, poseView);
414			else
415				bounds = widget->CalcRect(Location(poseView), NULL, poseView);
416
417			widget->StartEdit(bounds, poseView, this);
418			break;
419		}
420	}
421}
422
423
424void
425BPose::EditPreviousNextWidgetCommon(BPoseView* poseView, bool next)
426{
427	bool found = false;
428	int32 delta = next ? 1 : -1;
429	for (int32 index = next ? 0 : poseView->CountColumns() - 1; ;
430			index += delta) {
431		BColumn* column = poseView->ColumnAt(index);
432		if (column == NULL) {
433			// out of columns
434			break;
435		}
436
437		BTextWidget* widget = WidgetFor(column->AttrHash());
438		if (widget == NULL) {
439			// no widget for this column, next
440			continue;
441		}
442
443		if (widget->IsActive()) {
444			poseView->CommitActivePose();
445			found = true;
446			continue;
447		}
448
449		if (found && column->Editable()) {
450			BRect bounds;
451			if (poseView->ViewMode() == kListMode) {
452				int32 poseIndex = poseView->IndexOfPose(this);
453				BPoint poseLoc(0, poseIndex* poseView->ListElemHeight());
454				bounds = widget->CalcRect(poseLoc, column, poseView);
455			} else
456				bounds = widget->CalcRect(Location(poseView), NULL, poseView);
457
458			widget->StartEdit(bounds, poseView, this);
459			break;
460		}
461	}
462}
463
464
465void
466BPose::EditNextWidget(BPoseView* poseView)
467{
468	EditPreviousNextWidgetCommon(poseView, true);
469}
470
471
472void
473BPose::EditPreviousWidget(BPoseView* poseView)
474{
475	EditPreviousNextWidgetCommon(poseView, false);
476}
477
478
479bool
480BPose::PointInPose(const BPoseView* poseView, BPoint where) const
481{
482	ASSERT(poseView->ViewMode() != kListMode);
483
484	BPoint location = Location(poseView);
485
486	if (poseView->ViewMode() == kIconMode) {
487		// check icon rect, then actual icon pixel
488		BRect rect(location, location);
489		rect.right += poseView->IconSizeInt() - 1;
490		rect.bottom += poseView->IconSizeInt() - 1;
491
492		if (rect.Contains(where)) {
493			return IconCache::sIconCache->IconHitTest(where - location,
494				ResolvedModel(), kNormalIcon, poseView->IconSize());
495		}
496
497		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
498		if (widget) {
499			float textWidth = ceilf(widget->TextWidth(poseView) + 1);
500			rect.left += (poseView->IconSizeInt() - textWidth) / 2;
501			rect.right = rect.left + textWidth;
502		}
503
504		rect.top = location.y + poseView->IconSizeInt();
505		rect.bottom = rect.top + poseView->FontHeight();
506
507		return rect.Contains(where);
508	}
509
510	// MINI_ICON_MODE rect calc
511	BRect rect(location, location);
512	rect.right += B_MINI_ICON + kMiniIconSeparator;
513	rect.bottom += poseView->IconPoseHeight();
514	BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
515	if (widget != NULL)
516		rect.right += ceil(widget->TextWidth(poseView) + 1);
517
518	return rect.Contains(where);
519}
520
521
522bool
523BPose::PointInPose(BPoint loc, const BPoseView* poseView, BPoint where,
524	BTextWidget** hitWidget) const
525{
526	if (hitWidget != NULL)
527		*hitWidget = NULL;
528
529	// check intersection with icon
530	BRect rect = _IconRect(poseView, loc);
531	if (rect.Contains(where))
532		return true;
533
534	for (int32 index = 0; ; index++) {
535		BColumn* column = poseView->ColumnAt(index);
536		if (column == NULL)
537			break;
538
539		BTextWidget* widget = WidgetFor(column->AttrHash());
540		if (widget != NULL
541			&& widget->CalcClickRect(loc, column, poseView).Contains(where)) {
542			if (hitWidget != NULL)
543				*hitWidget = widget;
544
545			return true;
546		}
547	}
548
549	return false;
550}
551
552
553void
554BPose::Draw(BRect rect, const BRect& updateRect, BPoseView* poseView,
555	BView* drawView, bool fullDraw, BPoint offset, bool selected)
556{
557	// If the background wasn't cleared and Draw() is not called after
558	// having edited a name or similar (with fullDraw)
559	if (!fBackgroundClean && !fullDraw) {
560		fBackgroundClean = true;
561		poseView->Invalidate(rect);
562		return;
563	} else
564		fBackgroundClean = false;
565
566	bool directDraw = (drawView == poseView);
567	bool windowActive = poseView->Window()->IsActive();
568	bool showSelectionWhenInactive = poseView->fShowSelectionWhenInactive;
569	bool isDrawingSelectionRect = poseView->fIsDrawingSelectionRect;
570
571	ModelNodeLazyOpener modelOpener(fModel);
572
573	if (poseView->ViewMode() == kListMode) {
574		BRect iconRect = _IconRect(poseView, rect.LeftTop());
575		if (updateRect.Intersects(iconRect)) {
576			iconRect.OffsetBy(offset);
577			DrawIcon(iconRect.LeftTop(), drawView, poseView->IconSize(),
578				directDraw, !windowActive && !showSelectionWhenInactive);
579		}
580
581		// draw text
582		int32 columnsToDraw = 1;
583		if (fullDraw)
584			columnsToDraw = poseView->CountColumns();
585
586		for (int32 index = 0; index < columnsToDraw; index++) {
587			BColumn* column = poseView->ColumnAt(index);
588			if (column == NULL)
589				break;
590
591			// if widget doesn't exist, create it
592			BTextWidget* widget = WidgetFor(column, poseView, modelOpener);
593
594			if (widget != NULL && widget->IsVisible()) {
595				BRect widgetRect(widget->ColumnRect(rect.LeftTop(), column,
596					poseView));
597
598				if (updateRect.Intersects(widgetRect)) {
599					BRect widgetTextRect(widget->CalcRect(rect.LeftTop(),
600						column, poseView));
601
602					bool selectDuringDraw = directDraw && selected
603						&& windowActive;
604
605					if (index == 0 && selectDuringDraw) {
606						// draw with dark background to select text
607						drawView->PushState();
608						drawView->SetLowColor(0, 0, 0);
609					}
610
611					if (index == 0) {
612						widget->Draw(widgetRect, widgetTextRect,
613							column->Width(), poseView, drawView, selected,
614							fClipboardMode, offset, directDraw);
615					} else {
616						widget->Draw(widgetTextRect, widgetTextRect,
617							column->Width(), poseView, drawView, false,
618							fClipboardMode, offset, directDraw);
619					}
620
621					if (index == 0 && selectDuringDraw)
622						drawView->PopState();
623					else if (index == 0 && selected) {
624						if (windowActive || isDrawingSelectionRect) {
625							widgetTextRect.OffsetBy(offset);
626							drawView->InvertRect(widgetTextRect);
627						} else if (!windowActive
628							&& showSelectionWhenInactive) {
629							widgetTextRect.OffsetBy(offset);
630							drawView->PushState();
631							drawView->SetDrawingMode(B_OP_BLEND);
632							drawView->SetHighColor(128, 128, 128, 255);
633							drawView->FillRect(widgetTextRect);
634							drawView->PopState();
635						}
636					}
637				}
638			}
639		}
640	} else {
641		// draw in icon mode
642		BPoint location(Location(poseView));
643		BPoint iconOrigin(location);
644		iconOrigin += offset;
645
646		DrawIcon(iconOrigin, drawView, poseView->IconSize(), directDraw,
647			!windowActive && !showSelectionWhenInactive);
648
649		BColumn* column = poseView->FirstColumn();
650		if (column == NULL)
651			return;
652
653		BTextWidget* widget = WidgetFor(column, poseView, modelOpener);
654		if (widget == NULL || !widget->IsVisible())
655			return;
656
657		rect = widget->CalcRect(location, NULL, poseView);
658
659		bool selectDuringDraw = directDraw && selected
660			&& (poseView->IsDesktopWindow() || windowActive);
661
662		if (selectDuringDraw) {
663			// draw with dark background to select text
664			drawView->PushState();
665			drawView->SetLowColor(0, 0, 0);
666		}
667
668		widget->Draw(rect, rect, rect.Width(), poseView, drawView,
669			selected, fClipboardMode, offset, directDraw);
670
671		if (selectDuringDraw)
672			drawView->PopState();
673		else if (selected && directDraw) {
674			if (windowActive || isDrawingSelectionRect) {
675				rect.OffsetBy(offset);
676				drawView->InvertRect(rect);
677			} else if (!windowActive && showSelectionWhenInactive) {
678				drawView->PushState();
679				drawView->SetDrawingMode(B_OP_BLEND);
680				drawView->SetHighColor(128, 128, 128, 255);
681				drawView->FillRect(rect);
682				drawView->PopState();
683			}
684		}
685	}
686}
687
688
689void
690BPose::DeselectWithoutErasingBackground(BRect, BPoseView* poseView)
691{
692	ASSERT(poseView->ViewMode() != kListMode);
693	ASSERT(!IsSelected());
694
695	BPoint location(Location(poseView));
696
697	// draw icon directly
698	if (fPercent == -1)
699		DrawIcon(location, poseView, poseView->IconSize(), true);
700	else
701		UpdateIcon(location, poseView);
702
703	BColumn* column = poseView->FirstColumn();
704	if (column == NULL)
705		return;
706
707	BTextWidget* widget = WidgetFor(column->AttrHash());
708	if (widget == NULL || !widget->IsVisible())
709		return;
710
711	// just invalidate the background, don't draw anything
712	poseView->Invalidate(widget->CalcRect(location, NULL, poseView));
713}
714
715
716void
717BPose::MoveTo(BPoint point, BPoseView* poseView, bool invalidate)
718{
719	point.x = floorf(point.x);
720	point.y = floorf(point.y);
721
722	BRect oldBounds;
723
724	BPoint oldLocation = Location(poseView);
725
726	ASSERT(poseView->ViewMode() != kListMode);
727	if (point == oldLocation || poseView->ViewMode() == kListMode)
728		return;
729
730	if (invalidate)
731		oldBounds = CalcRect(poseView);
732
733	// might need to move a text view if we're active
734	if (poseView->ActivePose() == this) {
735		BView* border_view = poseView->FindView("BorderView");
736		if (border_view) {
737			border_view->MoveBy(point.x - oldLocation.x,
738				point.y - oldLocation.y);
739		}
740	}
741
742	float scale = 1.0;
743	if (poseView->ViewMode() == kIconMode) {
744		scale = poseView->IconSize() / 32.0;
745	}
746	fLocation.x = point.x / scale;
747	fLocation.y = point.y / scale;
748
749	fHasLocation = true;
750	fNeedsSaveLocation = true;
751
752	if (invalidate) {
753		poseView->Invalidate(oldBounds);
754		poseView->Invalidate(CalcRect(poseView));
755	}
756}
757
758
759BTextWidget*
760BPose::ActiveWidget() const
761{
762	for (int32 i = fWidgetList.CountItems(); i-- > 0;) {
763		BTextWidget* widget = fWidgetList.ItemAt(i);
764		if (widget->IsActive())
765			return widget;
766	}
767
768	return NULL;
769}
770
771
772BTextWidget*
773BPose::WidgetFor(uint32 attr, int32* index) const
774{
775	int32 count = fWidgetList.CountItems();
776	for (int32 i = 0; i < count; i++) {
777		BTextWidget* widget = fWidgetList.ItemAt(i);
778		if (widget->AttrHash() == attr) {
779			if (index != NULL)
780				*index = i;
781
782			return widget;
783		}
784	}
785
786	return NULL;
787}
788
789
790BTextWidget*
791BPose::WidgetFor(BColumn* column, BPoseView* poseView,
792	ModelNodeLazyOpener &opener, int32* index)
793{
794	BTextWidget* widget = WidgetFor(column->AttrHash(), index);
795	if (widget == NULL)
796		widget = AddWidget(poseView, column, opener);
797
798	return widget;
799}
800
801
802// the following method is deprecated
803bool
804BPose::TestLargeIconPixel(BPoint point) const
805{
806	return IconCache::sIconCache->IconHitTest(point, ResolvedModel(),
807		kNormalIcon, B_LARGE_ICON);
808}
809
810
811void
812BPose::DrawIcon(BPoint where, BView* view, icon_size which, bool direct,
813	bool drawUnselected)
814{
815	if (fClipboardMode == kMoveSelectionTo) {
816		view->SetDrawingMode(B_OP_ALPHA);
817		view->SetHighColor(0, 0, 0, 64);
818			// set the level of transparency
819		view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
820	} else if (direct)
821		view->SetDrawingMode(B_OP_OVER);
822
823	IconCache::sIconCache->Draw(ResolvedModel(), view, where,
824		fIsSelected && !drawUnselected ? kSelectedIcon : kNormalIcon, which,
825		true);
826
827	if (fPercent != -1)
828		DrawBar(where, view, which);
829}
830
831
832void
833BPose::DrawBar(BPoint where, BView* view, icon_size which)
834{
835	view->PushState();
836
837	int32 size = which - 1;
838	int32 yOffset;
839	int32 barWidth = (int32)(7.0f / 32.0f * (float)which);
840	if (barWidth < 4) {
841		barWidth = 4;
842		yOffset = 0;
843	} else
844		yOffset = 2;
845	int32 barHeight = size - 4 - 2 * yOffset;
846
847	// the black shadowed line
848	view->SetHighColor(32, 32, 32, 92);
849	view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset));
850	view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset));
851	view->StrokeLine(BPoint(where.x + size - barWidth + 1,
852		where.y + size - yOffset));
853
854	view->SetDrawingMode(B_OP_ALPHA);
855
856	// the gray frame
857	view->SetHighColor(76, 76, 76, 192);
858	BRect rect(where.x + size - barWidth,where.y + yOffset,
859		where.x + size - 1,where.y + size - 1 - yOffset);
860	view->StrokeRect(rect);
861
862	// calculate bar height
863	int32 percent = fPercent > -1 ? fPercent : -2 - fPercent;
864	int32 barPos = int32(barHeight * percent / 100.0);
865	if (barPos < 0)
866		barPos = 0;
867	else if (barPos > barHeight)
868		barPos = barHeight;
869
870	// the free space bar
871	view->SetHighColor(TrackerSettings().FreeSpaceColor());
872
873	rect.InsetBy(1,1);
874	BRect bar(rect);
875	bar.bottom = bar.top + barPos - 1;
876	if (barPos > 0)
877		view->FillRect(bar);
878
879	// the used space bar
880	bar.top = bar.bottom + 1;
881	bar.bottom = rect.bottom;
882	view->SetHighColor(fPercent < -1
883		? TrackerSettings().WarningSpaceColor()
884		: TrackerSettings().UsedSpaceColor());
885	view->FillRect(bar);
886
887	view->PopState();
888}
889
890
891void
892BPose::DrawToggleSwitch(BRect, BPoseView*)
893{
894}
895
896
897BPoint
898BPose::Location(const BPoseView* poseView) const
899{
900	float scale = 1.0;
901	if (poseView->ViewMode() == kIconMode)
902		scale = poseView->IconSize() / 32.0;
903
904	return BPoint(fLocation.x * scale, fLocation.y * scale);
905}
906
907
908void
909BPose::SetLocation(BPoint point, const BPoseView* poseView)
910{
911	float scale = 1.0;
912	if (poseView->ViewMode() == kIconMode)
913		scale = poseView->IconSize() / 32.0;
914
915	fLocation = BPoint(floorf(point.x / scale), floorf(point.y / scale));
916	if (isinff(fLocation.x) || isinff(fLocation.y))
917		debugger("BPose::SetLocation() - infinite location");
918
919	fHasLocation = true;
920}
921
922
923BRect
924BPose::CalcRect(BPoint loc, const BPoseView* poseView, bool minimalRect) const
925{
926	ASSERT(poseView->ViewMode() == kListMode);
927
928	BColumn* column = poseView->LastColumn();
929	BRect rect;
930	rect.left = loc.x;
931	rect.top = loc.y;
932	rect.right = loc.x + column->Offset() + column->Width();
933	rect.bottom = rect.top + poseView->ListElemHeight();
934
935	if (minimalRect) {
936		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
937		if (widget != NULL) {
938			rect.right = widget->CalcRect(loc, poseView->FirstColumn(),
939				poseView).right;
940		}
941	}
942
943	return rect;
944}
945
946
947BRect
948BPose::CalcRect(const BPoseView* poseView) const
949{
950	ASSERT(poseView->ViewMode() != kListMode);
951
952	BRect rect;
953	BPoint location = Location(poseView);
954	if (poseView->ViewMode() == kIconMode) {
955		rect.left = location.x;
956		rect.right = rect.left + poseView->IconSizeInt();
957
958		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
959		if (widget != NULL) {
960			float textWidth = ceilf(widget->TextWidth(poseView) + 1);
961			if (textWidth > poseView->IconSizeInt()) {
962				rect.left += (poseView->IconSizeInt() - textWidth) / 2;
963				rect.right = rect.left + textWidth;
964			}
965		}
966
967		rect.top = location.y;
968		rect.bottom = rect.top + poseView->IconPoseHeight();
969	} else {
970		// MINI_ICON_MODE rect calc
971		rect.left = location.x;
972		rect.top = location.y;
973		rect.right = rect.left + B_MINI_ICON + kMiniIconSeparator;
974		rect.bottom = rect.top + poseView->IconPoseHeight();
975		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
976		if (widget != NULL)
977			rect.right += ceil(widget->TextWidth(poseView) + 1);
978	}
979
980	return rect;
981}
982
983
984BRect
985BPose::_IconRect(const BPoseView* poseView, BPoint location) const
986{
987	uint32 size = poseView->IconSizeInt();
988	BRect rect;
989	rect.left = location.x + kListOffset;
990	rect.right = rect.left + size;
991	rect.top = location.y + (poseView->ListElemHeight() - size) / 2.f;
992	rect.bottom = rect.top + size;
993	return rect;
994}
995
996
997#if DEBUG
998void
999BPose::PrintToStream()
1000{
1001	TargetModel()->PrintToStream();
1002	switch (fClipboardMode) {
1003		case kMoveSelectionTo:
1004			PRINT(("clipboardMode: Cut\n"));
1005			break;
1006
1007		case kCopySelectionTo:
1008			PRINT(("clipboardMode: Copy\n"));
1009			break;
1010
1011		default:
1012			PRINT(("clipboardMode: 0 - not in clipboard\n"));
1013			break;
1014	}
1015	PRINT(("%sselected\n", IsSelected() ? "" : "not "));
1016	PRINT(("location %s x:%f y:%f\n", HasLocation() ? "" : "unknown ",
1017		HasLocation() ? fLocation.x : 0,
1018		HasLocation() ? fLocation.y : 0));
1019	PRINT(("%s autoplaced \n", WasAutoPlaced() ? "was" : "not"));
1020}
1021#endif
1022