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