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