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