xref: /haiku/src/kits/tracker/Pose.cpp (revision a629567a9001547736cfe892cdf992be16868fed)
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 			break;
432 
433 		BTextWidget* widget = WidgetFor(column->AttrHash());
434 		if (widget != NULL && widget->IsActive()) {
435 			poseView->CommitActivePose();
436 			found = true;
437 			continue;
438 		}
439 
440 		if (found && column->Editable()) {
441 			BRect bounds;
442 			if (poseView->ViewMode() == kListMode) {
443 				int32 poseIndex = poseView->IndexOfPose(this);
444 				BPoint poseLoc(0, poseIndex* poseView->ListElemHeight());
445 				bounds = widget->CalcRect(poseLoc, column, poseView);
446 			} else
447 				bounds = widget->CalcRect(Location(poseView), 0, poseView);
448 
449 			widget->StartEdit(bounds, poseView, this);
450 			break;
451 		}
452 	}
453 }
454 
455 
456 void
457 BPose::EditNextWidget(BPoseView* poseView)
458 {
459 	EditPreviousNextWidgetCommon(poseView, true);
460 }
461 
462 
463 void
464 BPose::EditPreviousWidget(BPoseView* poseView)
465 {
466 	EditPreviousNextWidgetCommon(poseView, false);
467 }
468 
469 
470 bool
471 BPose::PointInPose(const BPoseView* poseView, BPoint where) const
472 {
473 	ASSERT(poseView->ViewMode() != kListMode);
474 
475 	BPoint location = Location(poseView);
476 
477 	if (poseView->ViewMode() == kIconMode) {
478 		// check icon rect, then actual icon pixel
479 		BRect rect(location, location);
480 		rect.right += poseView->IconSizeInt() - 1;
481 		rect.bottom += poseView->IconSizeInt() - 1;
482 
483 		if (rect.Contains(where)) {
484 			return IconCache::sIconCache->IconHitTest(where - location,
485 				ResolvedModel(), kNormalIcon, poseView->IconSize());
486 		}
487 
488 		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
489 		if (widget) {
490 			float textWidth = ceilf(widget->TextWidth(poseView) + 1);
491 			rect.left += (poseView->IconSizeInt() - textWidth) / 2;
492 			rect.right = rect.left + textWidth;
493 		}
494 
495 		rect.top = location.y + poseView->IconSizeInt();
496 		rect.bottom = rect.top + poseView->FontHeight();
497 
498 		return rect.Contains(where);
499 	}
500 
501 	// MINI_ICON_MODE rect calc
502 	BRect rect(location, location);
503 	rect.right += B_MINI_ICON + kMiniIconSeparator;
504 	rect.bottom += poseView->IconPoseHeight();
505 	BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
506 	if (widget != NULL)
507 		rect.right += ceil(widget->TextWidth(poseView) + 1);
508 
509 	return rect.Contains(where);
510 }
511 
512 
513 bool
514 BPose::PointInPose(BPoint loc, const BPoseView* poseView, BPoint where,
515 	BTextWidget** hitWidget) const
516 {
517 	if (hitWidget)
518 		*hitWidget = NULL;
519 
520 	// check intersection with icon
521 	BRect rect;
522 	rect.left = loc.x + kListOffset;
523 	rect.right = rect.left + B_MINI_ICON;
524 	rect.bottom = loc.y + poseView->ListElemHeight();
525 	rect.top = rect.bottom - B_MINI_ICON;
526 	if (rect.Contains(where))
527 		return true;
528 
529 	for (int32 index = 0; ; index++) {
530 		BColumn* column = poseView->ColumnAt(index);
531 		if (column == NULL)
532 			break;
533 		BTextWidget* widget = WidgetFor(column->AttrHash());
534 		if (widget
535 			&& widget->CalcClickRect(loc, column, poseView).Contains(where)) {
536 			if (hitWidget)
537 				*hitWidget = widget;
538 			return true;
539 		}
540 	}
541 
542 	return false;
543 }
544 
545 
546 void
547 BPose::Draw(BRect rect, const BRect& updateRect, BPoseView* poseView,
548 	BView* drawView, bool fullDraw, BPoint offset, bool selected)
549 {
550 	// If the background wasn't cleared and Draw() is not called after
551 	// having edited a name or similar (with fullDraw)
552 	if (!fBackgroundClean && !fullDraw) {
553 		fBackgroundClean = true;
554 		poseView->Invalidate(rect);
555 		return;
556 	} else
557 		fBackgroundClean = false;
558 
559 	bool directDraw = (drawView == poseView);
560 	bool windowActive = poseView->Window()->IsActive();
561 	bool showSelectionWhenInactive = poseView->fShowSelectionWhenInactive;
562 	bool isDrawingSelectionRect = poseView->fIsDrawingSelectionRect;
563 
564 	ModelNodeLazyOpener modelOpener(fModel);
565 
566 	if (poseView->ViewMode() == kListMode) {
567 		uint32 size = poseView->IconSizeInt();
568 		BRect iconRect(rect);
569 		iconRect.left += kListOffset;
570 		iconRect.right = iconRect.left + size;
571 		iconRect.top = iconRect.bottom - size;
572 		if (updateRect.Intersects(iconRect)) {
573 			iconRect.OffsetBy(offset);
574 			DrawIcon(iconRect.LeftTop(), drawView, poseView->IconSize(),
575 				directDraw, !windowActive && !showSelectionWhenInactive);
576 		}
577 
578 		// draw text
579 		int32 columnsToDraw = 1;
580 		if (fullDraw)
581 			columnsToDraw = poseView->CountColumns();
582 
583 		for (int32 index = 0; index < columnsToDraw; index++) {
584 			BColumn* column = poseView->ColumnAt(index);
585 			if (column == NULL)
586 				break;
587 
588 			// if widget doesn't exist, create it
589 			BTextWidget* widget = WidgetFor(column, poseView, modelOpener);
590 
591 			if (widget && widget->IsVisible()) {
592 				BRect widgetRect(widget->ColumnRect(rect.LeftTop(), column,
593 					poseView));
594 
595 				if (updateRect.Intersects(widgetRect)) {
596 					BRect widgetTextRect(widget->CalcRect(rect.LeftTop(),
597 						column, poseView));
598 
599 					bool selectDuringDraw = directDraw && selected
600 						&& windowActive;
601 
602 					if (index == 0 && selectDuringDraw) {
603 						//draw with dark background to select text
604 						drawView->PushState();
605 						drawView->SetLowColor(0, 0, 0);
606 					}
607 
608 					if (index == 0) {
609 						widget->Draw(widgetRect, widgetTextRect,
610 							column->Width(), poseView, drawView, selected,
611 							fClipboardMode, offset, directDraw);
612 					} else {
613 						widget->Draw(widgetTextRect, widgetTextRect,
614 							column->Width(), poseView, drawView, false,
615 							fClipboardMode, offset, directDraw);
616 					}
617 
618 					if (index == 0 && selectDuringDraw)
619 						drawView->PopState();
620 					else if (index == 0 && selected) {
621 						if (windowActive || isDrawingSelectionRect) {
622 							widgetTextRect.OffsetBy(offset);
623 							drawView->InvertRect(widgetTextRect);
624 						} else if (!windowActive
625 							&& showSelectionWhenInactive) {
626 							widgetTextRect.OffsetBy(offset);
627 							drawView->PushState();
628 							drawView->SetDrawingMode(B_OP_BLEND);
629 							drawView->SetHighColor(128, 128, 128, 255);
630 							drawView->FillRect(widgetTextRect);
631 							drawView->PopState();
632 						}
633 					}
634 				}
635 			}
636 		}
637 	} else {
638 		// draw in icon mode
639 		BPoint location(Location(poseView));
640 		BPoint iconOrigin(location);
641 		iconOrigin += offset;
642 
643 		DrawIcon(iconOrigin, drawView, poseView->IconSize(), directDraw,
644 			!windowActive && !showSelectionWhenInactive);
645 
646 		BColumn* column = poseView->FirstColumn();
647 		if (column == NULL)
648 			return;
649 
650 		BTextWidget* widget = WidgetFor(column, poseView, modelOpener);
651 		if (widget == NULL || !widget->IsVisible())
652 			return;
653 
654 		rect = widget->CalcRect(location, 0, poseView);
655 
656 		bool selectDuringDraw = directDraw && selected
657 			&& (poseView->IsDesktopWindow() || windowActive);
658 
659 		if (selectDuringDraw) {
660 			// draw with dark background to select text
661 			drawView->PushState();
662 			drawView->SetLowColor(0, 0, 0);
663 		}
664 
665 		widget->Draw(rect, rect, rect.Width(), poseView, drawView,
666 			selected, fClipboardMode, offset, directDraw);
667 
668 		if (selectDuringDraw)
669 			drawView->PopState();
670 		else if (selected && directDraw) {
671 			if (windowActive || isDrawingSelectionRect) {
672 				rect.OffsetBy(offset);
673 				drawView->InvertRect(rect);
674 			} else if (!windowActive && showSelectionWhenInactive) {
675 				drawView->PushState();
676 				drawView->SetDrawingMode(B_OP_BLEND);
677 				drawView->SetHighColor(128, 128, 128, 255);
678 				drawView->FillRect(rect);
679 				drawView->PopState();
680 			}
681 		}
682 	}
683 }
684 
685 
686 void
687 BPose::DeselectWithoutErasingBackground(BRect, BPoseView* poseView)
688 {
689 	ASSERT(poseView->ViewMode() != kListMode);
690 	ASSERT(!IsSelected());
691 
692 	BPoint location(Location(poseView));
693 
694 	// draw icon directly
695 	if (fPercent == -1)
696 		DrawIcon(location, poseView, poseView->IconSize(), true);
697 	else
698 		UpdateIcon(location, poseView);
699 
700 	BColumn* column = poseView->FirstColumn();
701 	if (column == NULL)
702 		return;
703 
704 	BTextWidget* widget = WidgetFor(column->AttrHash());
705 	if (widget == NULL || !widget->IsVisible())
706 		return;
707 
708 	// just invalidate the background, don't draw anything
709 	poseView->Invalidate(widget->CalcRect(location, 0, poseView));
710 }
711 
712 
713 void
714 BPose::MoveTo(BPoint point, BPoseView* poseView, bool invalidate)
715 {
716 	point.x = floorf(point.x);
717 	point.y = floorf(point.y);
718 
719 	BRect oldBounds;
720 
721 	BPoint oldLocation = Location(poseView);
722 
723 	ASSERT(poseView->ViewMode() != kListMode);
724 	if (point == oldLocation || poseView->ViewMode() == kListMode)
725 		return;
726 
727 	if (invalidate)
728 		oldBounds = CalcRect(poseView);
729 
730 	// might need to move a text view if we're active
731 	if (poseView->ActivePose() == this) {
732 		BView* border_view = poseView->FindView("BorderView");
733 		if (border_view) {
734 			border_view->MoveBy(point.x - oldLocation.x,
735 				point.y - oldLocation.y);
736 		}
737 	}
738 
739 	float scale = 1.0;
740 	if (poseView->ViewMode() == kIconMode) {
741 		scale = poseView->IconSize() / 32.0;
742 	}
743 	fLocation.x = point.x / scale;
744 	fLocation.y = point.y / scale;
745 
746 	fHasLocation = true;
747 	fNeedsSaveLocation = true;
748 
749 	if (invalidate) {
750 		poseView->Invalidate(oldBounds);
751 		poseView->Invalidate(CalcRect(poseView));
752 	}
753 }
754 
755 
756 BTextWidget*
757 BPose::ActiveWidget() const
758 {
759 	for (int32 i = fWidgetList.CountItems(); i-- > 0;) {
760 		BTextWidget* widget = fWidgetList.ItemAt(i);
761 		if (widget->IsActive())
762 			return widget;
763 	}
764 
765 	return NULL;
766 }
767 
768 
769 BTextWidget*
770 BPose::WidgetFor(uint32 attr, int32* index) const
771 {
772 	int32 count = fWidgetList.CountItems();
773 	for (int32 i = 0; i < count; i++) {
774 		BTextWidget* widget = fWidgetList.ItemAt(i);
775 		if (widget->AttrHash() == attr) {
776 			if (index != NULL)
777 				*index = i;
778 
779 			return widget;
780 		}
781 	}
782 
783 	return 0;
784 }
785 
786 
787 BTextWidget*
788 BPose::WidgetFor(BColumn* column, BPoseView* poseView,
789 	ModelNodeLazyOpener &opener, int32* index)
790 {
791 	BTextWidget* widget = WidgetFor(column->AttrHash(), index);
792 	if (widget == NULL)
793 		widget = AddWidget(poseView, column, opener);
794 
795 	return widget;
796 }
797 
798 
799 // the following method is deprecated
800 bool
801 BPose::TestLargeIconPixel(BPoint point) const
802 {
803 	return IconCache::sIconCache->IconHitTest(point, ResolvedModel(),
804 		kNormalIcon, B_LARGE_ICON);
805 }
806 
807 
808 void
809 BPose::DrawIcon(BPoint where, BView* view, icon_size kind, bool direct,
810 	bool drawUnselected)
811 {
812 	if (fClipboardMode == kMoveSelectionTo) {
813 		view->SetDrawingMode(B_OP_ALPHA);
814 		view->SetHighColor(0, 0, 0, 64);
815 			// set the level of transparency
816 		view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
817 	} else if (direct)
818 		view->SetDrawingMode(B_OP_OVER);
819 
820 	IconCache::sIconCache->Draw(ResolvedModel(), view, where,
821 		fIsSelected && !drawUnselected ? kSelectedIcon : kNormalIcon, kind,
822 		true);
823 
824 	if (fPercent != -1)
825 		DrawBar(where, view, kind);
826 }
827 
828 
829 void
830 BPose::DrawBar(BPoint where, BView* view, icon_size kind)
831 {
832 	view->PushState();
833 
834 	int32 size;
835 	int32 barWidth;
836 	int32 barHeight;
837 	int32 yOffset;
838 	if (kind >= B_LARGE_ICON) {
839 		size = kind - 1;
840 		barWidth = (int32)(7.0f / 32.0f * (float)kind);
841 		yOffset = 2;
842 		barHeight = size - 4 - 2 * yOffset;
843 	} else {
844 		size = B_MINI_ICON;
845 		barWidth = 4;
846 		yOffset = 0;
847 		barHeight = size - 4 - 2 * yOffset;
848 	}
849 
850 	// the black shadowed line
851 	view->SetHighColor(32, 32, 32, 92);
852 	view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset));
853 	view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset));
854 	view->StrokeLine(BPoint(where.x + size - barWidth + 1,
855 		where.y + size - yOffset));
856 
857 	view->SetDrawingMode(B_OP_ALPHA);
858 
859 	// the gray frame
860 	view->SetHighColor(76, 76, 76, 192);
861 	BRect rect(where.x + size - barWidth,where.y + yOffset,
862 		where.x + size - 1,where.y + size - 1 - yOffset);
863 	view->StrokeRect(rect);
864 
865 	// calculate bar height
866 	int32 percent = fPercent > -1 ? fPercent : -2 - fPercent;
867 	int32 barPos = int32(barHeight * percent / 100.0);
868 	if (barPos < 0)
869 		barPos = 0;
870 	else if (barPos > barHeight)
871 		barPos = barHeight;
872 
873 	// the free space bar
874 	view->SetHighColor(TrackerSettings().FreeSpaceColor());
875 
876 	rect.InsetBy(1,1);
877 	BRect bar(rect);
878 	bar.bottom = bar.top + barPos - 1;
879 	if (barPos > 0)
880 		view->FillRect(bar);
881 
882 	// the used space bar
883 	bar.top = bar.bottom + 1;
884 	bar.bottom = rect.bottom;
885 	view->SetHighColor(fPercent < -1
886 		? TrackerSettings().WarningSpaceColor()
887 		: TrackerSettings().UsedSpaceColor());
888 	view->FillRect(bar);
889 
890 	view->PopState();
891 }
892 
893 
894 void
895 BPose::DrawToggleSwitch(BRect, BPoseView*)
896 {
897 }
898 
899 
900 BPoint
901 BPose::Location(const BPoseView* poseView) const
902 {
903 	float scale = 1.0;
904 	if (poseView->ViewMode() == kIconMode)
905 		scale = poseView->IconSize() / 32.0;
906 
907 	return BPoint(fLocation.x * scale, fLocation.y * scale);
908 }
909 
910 
911 void
912 BPose::SetLocation(BPoint point, const BPoseView* poseView)
913 {
914 	float scale = 1.0;
915 	if (poseView->ViewMode() == kIconMode)
916 		scale = poseView->IconSize() / 32.0;
917 
918 	fLocation = BPoint(floorf(point.x / scale), floorf(point.y / scale));
919 	if (isinff(fLocation.x) || isinff(fLocation.y))
920 		debugger("BPose::SetLocation() - infinite location");
921 
922 	fHasLocation = true;
923 }
924 
925 
926 BRect
927 BPose::CalcRect(BPoint loc, const BPoseView* poseView, bool minimalRect) const
928 {
929 	ASSERT(poseView->ViewMode() == kListMode);
930 
931 	BColumn* column = poseView->LastColumn();
932 	BRect rect;
933 	rect.left = loc.x;
934 	rect.top = loc.y;
935 	rect.right = loc.x + column->Offset() + column->Width();
936 	rect.bottom = rect.top + poseView->ListElemHeight();
937 
938 	if (minimalRect) {
939 		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
940 		if (widget != NULL) {
941 			rect.right = widget->CalcRect(loc, poseView->FirstColumn(),
942 				poseView).right;
943 		}
944 	}
945 
946 	return rect;
947 }
948 
949 
950 BRect
951 BPose::CalcRect(const BPoseView* poseView) const
952 {
953 	ASSERT(poseView->ViewMode() != kListMode);
954 
955 	BRect rect;
956 	BPoint location = Location(poseView);
957 	if (poseView->ViewMode() == kIconMode) {
958 		rect.left = location.x;
959 		rect.right = rect.left + poseView->IconSizeInt();
960 
961 		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
962 		if (widget) {
963 			float textWidth = ceilf(widget->TextWidth(poseView) + 1);
964 			if (textWidth > poseView->IconSizeInt()) {
965 				rect.left += (poseView->IconSizeInt() - textWidth) / 2;
966 				rect.right = rect.left + textWidth;
967 			}
968 		}
969 
970 		rect.top = location.y;
971 		rect.bottom = rect.top + poseView->IconPoseHeight();
972 	} else {
973 		// MINI_ICON_MODE rect calc
974 		rect.left = location.x;
975 		rect.top = location.y;
976 		rect.right = rect.left + B_MINI_ICON + kMiniIconSeparator;
977 		rect.bottom = rect.top + poseView->IconPoseHeight();
978 		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
979 		if (widget)
980 			rect.right += ceil(widget->TextWidth(poseView) + 1);
981 	}
982 
983 	return rect;
984 }
985 
986 
987 #if DEBUG
988 void
989 BPose::PrintToStream()
990 {
991 	TargetModel()->PrintToStream();
992 	switch (fClipboardMode) {
993 		case kMoveSelectionTo:
994 			PRINT(("clipboardMode: Cut\n"));
995 			break;
996 
997 		case kCopySelectionTo:
998 			PRINT(("clipboardMode: Copy\n"));
999 			break;
1000 
1001 		default:
1002 			PRINT(("clipboardMode: 0 - not in clipboard\n"));
1003 			break;
1004 	}
1005 	PRINT(("%sselected\n", IsSelected() ? "" : "not "));
1006 	PRINT(("location %s x:%f y:%f\n", HasLocation() ? "" : "unknown ",
1007 		HasLocation() ? fLocation.x : 0,
1008 		HasLocation() ? fLocation.y : 0));
1009 	PRINT(("%s autoplaced \n", WasAutoPlaced() ? "was" : "not"));
1010 }
1011 #endif
1012