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