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