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