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