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