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