xref: /haiku/src/kits/tracker/Pose.cpp (revision 1b80286772b529a3d6de3bbeb0720c62e6a32fed)
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 		fIsSelected(selected),
75 		fDelayedEdit(true),
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, pose->DelayedEdit());
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);
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)
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))
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);
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);
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 			UpdateIcon(poseLoc, poseView);
278 		}
279 
280 		// distribute stat changes
281 		for (int32 index = 0; ; index++) {
282 			BColumn *column = poseView->ColumnAt(index);
283 			if (!column)
284 				break;
285 
286 			if (column->StatField()) {
287 				BTextWidget *widget = WidgetFor(column->AttrHash());
288 				if (widget)
289 					widget->CheckAndUpdate(poseLoc, column, poseView);
290 			}
291 		}
292 	}
293 }
294 
295 
296 bool
297 BPose::_PeriodicUpdateCallback(BPose *pose, void *cookie)
298 {
299 	return pose->UpdateVolumeSpaceBar((BVolume *)cookie);
300 }
301 
302 
303 bool
304 BPose::UpdateVolumeSpaceBar(BVolume *volume)
305 {
306 	bool enabled = TrackerSettings().ShowVolumeSpaceBar();
307 	if (!enabled) {
308 		if (fPercent == -1)
309 			return false;
310 
311 		fPercent = -1;
312 		return true;
313 	}
314 
315 	int32 percent = CalcFreeSpace(volume);
316 	if (fPercent != percent) {
317 		if (percent > 100)
318 			fPercent = 100;
319 		else
320 			fPercent = percent;
321 
322 		return true;
323 	}
324 	return false;
325 }
326 
327 
328 void
329 BPose::UpdateIcon(BPoint poseLoc, BPoseView *poseView)
330 {
331 	IconCache::sIconCache->IconChanged(ResolvedModel());
332 
333 	int32 iconSize = poseView->IconSizeInt();
334 
335 	BRect rect;
336 	if (poseView->ViewMode() == kListMode) {
337 		rect = CalcRect(poseLoc, poseView);
338 		rect.left += kListOffset;
339 		rect.right = rect.left + iconSize;
340 		rect.top = rect.bottom - iconSize;
341 	} else {
342 		BPoint location = Location(poseView);
343 		rect.left = location.x;
344 		rect.top = location.y;
345 		rect.right = rect.left + iconSize;
346 		rect.bottom = rect.top + iconSize;
347 	}
348 
349 	poseView->Invalidate(rect);
350 }
351 
352 
353 void
354 BPose::UpdateBrokenSymLink(BPoint poseLoc, BPoseView *poseView)
355 {
356 	ASSERT(TargetModel()->IsSymLink());
357 	ASSERT(!TargetModel()->LinkTo());
358 	UpdateIcon(poseLoc, poseView);
359 }
360 
361 
362 void
363 BPose::UpdateWasBrokenSymlink(BPoint poseLoc, BPoseView *poseView)
364 {
365 	if (!fModel->IsSymLink())
366 		return;
367 
368 	if (fModel->LinkTo())
369 		return;
370 
371 	poseView->CreateSymlinkPoseTarget(fModel);
372 	if (!fModel->LinkTo())
373 		return;
374 
375 	UpdateIcon(poseLoc, poseView);
376 	fModel->LinkTo()->CloseNode();
377 }
378 
379 
380 void
381 BPose::EditFirstWidget(BPoint poseLoc, BPoseView *poseView)
382 {
383 	// find first editable widget
384 	BColumn *column;
385 	for (int32 i = 0;(column = poseView->ColumnAt(i)) != NULL;i++) {
386 		BTextWidget *widget = WidgetFor(column->AttrHash());
387 
388 		if (widget && widget->IsEditable()) {
389 			BRect bounds;
390 			// ToDo:
391 			// fold the three StartEdit code sequences into a cover call
392 			if (poseView->ViewMode() == kListMode)
393 				bounds = widget->CalcRect(poseLoc, column, poseView);
394 			else
395 				bounds = widget->CalcRect(Location(poseView), NULL, poseView);
396 			widget->StartEdit(bounds, poseView, this);
397 			break;
398 		}
399 	}
400 }
401 
402 
403 void
404 BPose::EditPreviousNextWidgetCommon(BPoseView *poseView, bool next)
405 {
406 	bool found = false;
407 	int32 delta = next ? 1 : -1;
408 	for (int32 index = next ? 0 : poseView->CountColumns() - 1; ; index += delta) {
409 		BColumn *column = poseView->ColumnAt(index);
410 		if (!column)
411 			break;
412 
413 		BTextWidget *widget = WidgetFor(column->AttrHash());
414 		if (widget && widget->IsActive()) {
415 			poseView->CommitActivePose();
416 			found = true;
417 			continue;
418 		}
419 
420 		if (found && column->Editable()) {
421 			BRect bounds;
422 			if (poseView->ViewMode() == kListMode) {
423 				int32 poseIndex = poseView->IndexOfPose(this);
424 				BPoint poseLoc(0, poseIndex * poseView->ListElemHeight());
425 				bounds = widget->CalcRect(poseLoc, column, poseView);
426 			} else
427 				bounds = widget->CalcRect(Location(poseView), 0, poseView);
428 
429 			widget->StartEdit(bounds, poseView, this);
430 			break;
431 		}
432 	}
433 }
434 
435 
436 void
437 BPose::EditNextWidget(BPoseView *poseView)
438 {
439 	EditPreviousNextWidgetCommon(poseView, true);
440 }
441 
442 
443 void
444 BPose::EditPreviousWidget(BPoseView *poseView)
445 {
446 	EditPreviousNextWidgetCommon(poseView, false);
447 }
448 
449 
450 bool
451 BPose::PointInPose(const BPoseView *poseView, BPoint where) const
452 {
453 	ASSERT(poseView->ViewMode() != kListMode);
454 
455 	BPoint location = Location(poseView);
456 
457 	if (poseView->ViewMode() == kIconMode) {
458 		// check icon rect, then actual icon pixel
459 		BRect rect(location, location);
460 		rect.right += poseView->IconSizeInt() - 1;
461 		rect.bottom += poseView->IconSizeInt() - 1;
462 
463 		if (rect.Contains(where))
464 			return IconCache::sIconCache->IconHitTest(where - location,
465 													  ResolvedModel(),
466 													  kNormalIcon,
467 													  poseView->IconSize());
468 
469 		BTextWidget *widget = WidgetFor(poseView->FirstColumn()->AttrHash());
470 		if (widget) {
471 			float textWidth = ceilf(widget->TextWidth(poseView) + 1);
472 			rect.left += (poseView->IconSizeInt() - textWidth) / 2;
473 			rect.right = rect.left + textWidth;
474 		}
475 
476 		rect.top = location.y + poseView->IconSizeInt();
477 		rect.bottom = rect.top + poseView->FontHeight();
478 
479 		return rect.Contains(where);
480 	}
481 
482 	// MINI_ICON_MODE rect calc
483 	BRect rect(location, location);
484 	rect.right += B_MINI_ICON + kMiniIconSeparator;
485 	rect.bottom += poseView->IconPoseHeight();
486 	BTextWidget *widget = WidgetFor(poseView->FirstColumn()->AttrHash());
487 	if (widget)
488 		rect.right += ceil(widget->TextWidth(poseView) + 1);
489 
490 	return rect.Contains(where);
491 }
492 
493 
494 bool
495 BPose::PointInPose(BPoint loc, const BPoseView *poseView, BPoint where,
496 	BTextWidget **hitWidget) const
497 {
498 	if (hitWidget)
499 		*hitWidget = NULL;
500 
501 	// check intersection with icon
502 	BRect rect;
503 	rect.left = loc.x + kListOffset;
504 	rect.right = rect.left + B_MINI_ICON;
505 	rect.bottom = loc.y + poseView->ListElemHeight();
506 	rect.top = rect.bottom - B_MINI_ICON;
507 	if (rect.Contains(where))
508 		return true;
509 
510 	for (int32 index = 0; ; index++) {
511 		BColumn *column = poseView->ColumnAt(index);
512 		if (!column)
513 			break;
514 		BTextWidget *widget = WidgetFor(column->AttrHash());
515 		if (widget && widget->CalcClickRect(loc, column, poseView).Contains(where)) {
516 			if (hitWidget)
517 				*hitWidget = widget;
518 			return true;
519 		}
520 	}
521 
522 	return false;
523 }
524 
525 
526 void
527 BPose::Draw(BRect rect, const BRect& updateRect, BPoseView *poseView, BView *drawView,
528 	bool fullDraw, BPoint offset, bool selected)
529 {
530 	// If the background wasn't cleared and Draw() is not called after
531 	// having edited a name or similar (with fullDraw)
532 	if (!fBackgroundClean && !fullDraw) {
533 		fBackgroundClean = true;
534 		poseView->Invalidate(rect);
535 		return;
536 	} else
537 		fBackgroundClean = false;
538 
539 	bool directDraw = (drawView == poseView);
540 	bool windowActive = poseView->Window()->IsActive();
541 	bool showSelectionWhenInactive = poseView->fShowSelectionWhenInactive;
542 	bool isDrawingSelectionRect = poseView->fIsDrawingSelectionRect;
543 
544 	ModelNodeLazyOpener modelOpener(fModel);
545 
546 	if (poseView->ViewMode() == kListMode) {
547 		uint32 size = poseView->IconSizeInt();
548 		BRect iconRect(rect);
549 		iconRect.OffsetBy(offset);
550 		iconRect.left += kListOffset;
551 		iconRect.right = iconRect.left + size;
552 		iconRect.top = iconRect.bottom - size;
553 		if (updateRect.Intersects(iconRect)) {
554 			DrawIcon(iconRect.LeftTop(), drawView, poseView->IconSize(), directDraw,
555 				!windowActive && !showSelectionWhenInactive);
556 		}
557 
558 		// draw text
559 		int32 columnsToDraw = 1;
560 		if (fullDraw)
561 			columnsToDraw = poseView->CountColumns();
562 
563 		for (int32 index = 0; index < columnsToDraw; index++) {
564 			BColumn *column = poseView->ColumnAt(index);
565 			if (!column)
566 				break;
567 
568 			// if widget doesn't exist, create it
569 			BTextWidget *widget = WidgetFor(column, poseView, modelOpener);
570 
571 			if (widget && widget->IsVisible()) {
572 				BRect widgetRect(widget->ColumnRect(rect.LeftTop(), column,
573 					poseView));
574 
575 				if (updateRect.Intersects(widgetRect)) {
576 					BRect widgetTextRect(widget->CalcRect(rect.LeftTop(), column,
577 						poseView));
578 
579 					bool selectDuringDraw = directDraw && selected
580 						&& windowActive;
581 
582 					if (index == 0 && selectDuringDraw) {
583 						//draw with dark background to select text
584 						drawView->PushState();
585 						drawView->SetLowColor(0, 0, 0);
586 					}
587 
588 					if (index == 0)
589 						widget->Draw(widgetRect, widgetTextRect, column->Width(),
590 							poseView, drawView, selected, fClipboardMode, offset, directDraw);
591 					else
592 						widget->Draw(widgetTextRect, widgetTextRect, column->Width(),
593 							poseView, drawView, false, fClipboardMode, offset, directDraw);
594 
595 					if (index == 0 && selectDuringDraw)
596 						drawView->PopState();
597 					else if (index == 0 && selected) {
598 						if (windowActive || isDrawingSelectionRect) {
599 							widgetTextRect.OffsetBy(offset);
600 							drawView->InvertRect(widgetTextRect);
601 						} else if (!windowActive && showSelectionWhenInactive) {
602 							widgetTextRect.OffsetBy(offset);
603 							drawView->PushState();
604 							drawView->SetDrawingMode(B_OP_BLEND);
605 							drawView->SetHighColor(128, 128, 128, 255);
606 							drawView->FillRect(widgetTextRect);
607 							drawView->PopState();
608 						}
609 					}
610 				}
611 			}
612 		}
613 	} else {
614 
615 		// draw in icon mode
616 		BPoint location(Location(poseView));
617 		BPoint iconOrigin(location);
618 		iconOrigin += offset;
619 
620 		DrawIcon(iconOrigin, drawView, poseView->IconSize(), directDraw,
621 			!windowActive && !showSelectionWhenInactive);
622 
623 		BColumn *column = poseView->FirstColumn();
624 		if (!column)
625 			return;
626 
627 		BTextWidget *widget = WidgetFor(column, poseView, modelOpener);
628 		if (!widget || !widget->IsVisible())
629 			return;
630 
631 		rect = widget->CalcRect(location, 0, poseView);
632 
633 		bool selectDuringDraw = directDraw && selected
634 			&& (poseView->IsDesktopWindow() || windowActive);
635 
636 		if (selectDuringDraw) {
637 			// draw with dark background to select text
638 			drawView->PushState();
639 			drawView->SetLowColor(0, 0, 0);
640 		}
641 
642 		widget->Draw(rect, rect, rect.Width(), poseView, drawView,
643 			selected, fClipboardMode, offset, directDraw);
644 
645 		if (selectDuringDraw)
646 			drawView->PopState();
647 		else if (selected && directDraw) {
648 			if (windowActive || isDrawingSelectionRect) {
649 				rect.OffsetBy(offset);
650 				drawView->InvertRect(rect);
651 			} else if (!windowActive && showSelectionWhenInactive) {
652 				drawView->PushState();
653 				drawView->SetDrawingMode(B_OP_BLEND);
654 				drawView->SetHighColor(128, 128, 128, 255);
655 				drawView->FillRect(rect);
656 				drawView->PopState();
657 			}
658 		}
659 	}
660 }
661 
662 
663 void
664 BPose::DeselectWithoutErasingBackground(BRect, BPoseView *poseView)
665 {
666 	ASSERT(poseView->ViewMode() != kListMode);
667 	ASSERT(!IsSelected());
668 
669 	BPoint location(Location(poseView));
670 
671 	// draw icon directly
672 	if (fPercent == -1)
673 		DrawIcon(location, poseView, poseView->IconSize(), true);
674 	else
675 		UpdateIcon(location, poseView);
676 
677 	BColumn *column = poseView->FirstColumn();
678 	if (!column)
679 		return;
680 
681 	BTextWidget *widget = WidgetFor(column->AttrHash());
682 	if (!widget || !widget->IsVisible())
683 		return;
684 
685 	// just invalidate the background, don't draw anything
686 	poseView->Invalidate(widget->CalcRect(location, 0, poseView));
687 }
688 
689 
690 void
691 BPose::MoveTo(BPoint point, BPoseView *poseView, bool inval)
692 {
693 	point.x = floorf(point.x);
694 	point.y = floorf(point.y);
695 
696 	BRect oldBounds;
697 
698 	BPoint oldLocation = Location(poseView);
699 
700 	ASSERT(poseView->ViewMode() != kListMode);
701 	if (point == oldLocation || poseView->ViewMode() == kListMode)
702 		return;
703 
704 	if (inval)
705 		oldBounds = CalcRect(poseView);
706 
707 	// might need to move a text view if we're active
708 	if (poseView->ActivePose() == this) {
709 		BView *border_view = poseView->FindView("BorderView");
710 		if (border_view)
711 			border_view->MoveBy(point.x - oldLocation.x, point.y - oldLocation.y);
712 	}
713 
714 	float scale = 1.0;
715 	if (poseView->ViewMode() == kIconMode) {
716 		scale = poseView->IconSize() / 32.0;
717 	}
718 	fLocation.x = point.x / scale;
719 	fLocation.y = point.y / scale;
720 
721 	fHasLocation = true;
722 	fNeedsSaveLocation = true;
723 
724 	if (inval) {
725 		poseView->Invalidate(oldBounds);
726 		poseView->Invalidate(CalcRect(poseView));
727 	}
728 }
729 
730 
731 BTextWidget *
732 BPose::ActiveWidget() const
733 {
734 	for (int32 i = fWidgetList.CountItems(); i-- > 0;) {
735 		BTextWidget *widget = fWidgetList.ItemAt(i);
736 		if (widget->IsActive())
737 			return widget;
738 	}
739 	return NULL;
740 }
741 
742 
743 BTextWidget *
744 BPose::WidgetFor(uint32 attr, int32 *index) const
745 {
746 	int32 count = fWidgetList.CountItems();
747 	for (int32 i = 0; i < count; i++) {
748 		BTextWidget *widget = fWidgetList.ItemAt(i);
749 		if (widget->AttrHash() == attr) {
750 			if (index)
751 				*index = i;
752 			return widget;
753 		}
754 	}
755 
756 	return 0;
757 }
758 
759 
760 BTextWidget *
761 BPose::WidgetFor(BColumn *column, BPoseView *poseView, ModelNodeLazyOpener &opener,
762 	int32 *index)
763 {
764 	BTextWidget *widget = WidgetFor(column->AttrHash(), index);
765 	if (!widget)
766 		widget = AddWidget(poseView, column, opener);
767 
768 	return widget;
769 }
770 
771 
772 /* deprecated */
773 bool
774 BPose::TestLargeIconPixel(BPoint point) const
775 {
776 	return IconCache::sIconCache->IconHitTest(point, ResolvedModel(),
777 		kNormalIcon, B_LARGE_ICON);
778 }
779 /* deprecated */
780 
781 
782 void
783 BPose::DrawIcon(BPoint where, BView *view, icon_size kind, bool direct, bool drawUnselected)
784 {
785 	if (fClipboardMode == kMoveSelectionTo) {
786 		view->SetDrawingMode(B_OP_ALPHA);
787 		view->SetHighColor(0, 0, 0, 64);	// set the level of transparency
788 		view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
789 	} else if (direct)
790 		view->SetDrawingMode(B_OP_OVER);
791 
792 	IconCache::sIconCache->Draw(ResolvedModel(), view, where,
793 		fIsSelected && !drawUnselected ? kSelectedIcon : kNormalIcon, kind, true);
794 
795 	if (fPercent != -1)
796 		DrawBar(where, view, kind);
797 }
798 
799 
800 void
801 BPose::DrawBar(BPoint where,BView *view,icon_size kind)
802 {
803 	view->PushState();
804 
805 	int32 size, barWidth, barHeight, yOffset;
806 	if (kind >= B_LARGE_ICON) {
807 		size = kind - 1;
808 		barWidth = (int32)((float)7 / (float)32 * (float)kind);
809 		yOffset = 2;
810 		barHeight = size - 4 - 2 * yOffset;
811 	} else {
812 		size = B_MINI_ICON;
813 		barWidth = 4;
814 		yOffset = 0;
815 		barHeight = size - 4 - 2 * yOffset;
816 	}
817 
818 	// the black shadowed line
819 	view->SetHighColor(32, 32, 32, 92);
820 	view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset));
821 	view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset));
822 	view->StrokeLine(BPoint(where.x + size - barWidth + 1, where.y + size - yOffset));
823 
824 	view->SetDrawingMode(B_OP_ALPHA);
825 
826 	// the gray frame
827 	view->SetHighColor(76, 76, 76, 192);
828 	BRect rect(	where.x + size - barWidth,where.y + yOffset,
829 				where.x + size - 1,where.y + size - 1 - yOffset);
830 	view->StrokeRect(rect);
831 
832 	// calculate bar height
833 	int32 percent = fPercent > -1 ? fPercent : -2 - fPercent;
834 	int32 barPos = int32(barHeight * percent / 100.0);
835 	if (barPos < 0)
836 		barPos = 0;
837 	else if (barPos > barHeight)
838 		barPos = barHeight;
839 
840 	// the free space bar
841 	view->SetHighColor(TrackerSettings().FreeSpaceColor());
842 
843 	rect.InsetBy(1,1);
844 	BRect bar(rect);
845 	bar.bottom = bar.top + barPos - 1;
846 	if (barPos > 0)
847 		view->FillRect(bar);
848 
849 	// the used space bar
850 	bar.top = bar.bottom + 1;
851 	bar.bottom = rect.bottom;
852 	view->SetHighColor(fPercent < -1 ? TrackerSettings().WarningSpaceColor() : TrackerSettings().UsedSpaceColor());
853 	view->FillRect(bar);
854 
855 	view->PopState();
856 }
857 
858 
859 void
860 BPose::DrawToggleSwitch(BRect, BPoseView *)
861 {
862 	return;
863 }
864 
865 
866 BPoint
867 BPose::Location(const BPoseView *poseView) const
868 {
869 	float scale = 1.0;
870 	if (poseView->ViewMode() == kIconMode)
871 		scale = poseView->IconSize() / 32.0;
872 
873 	return BPoint(fLocation.x * scale, fLocation.y * scale);
874 }
875 
876 
877 void
878 BPose::SetLocation(BPoint point, const BPoseView *poseView)
879 {
880 	float scale = 1.0;
881 	if (poseView->ViewMode() == kIconMode)
882 		scale = poseView->IconSize() / 32.0;
883 
884 	fLocation = BPoint(floorf(point.x / scale), floorf(point.y / scale));
885 if (isinff(fLocation.x) || isinff(fLocation.y))
886 debugger("BPose::SetLocation() - infinite location");
887 	fHasLocation = true;
888 }
889 
890 
891 BRect
892 BPose::CalcRect(BPoint loc, const BPoseView *poseView, bool minimalRect) const
893 {
894 	ASSERT(poseView->ViewMode() == kListMode);
895 
896 	BColumn *column = poseView->LastColumn();
897 	BRect rect;
898 	rect.left = loc.x;
899 	rect.top = loc.y;
900 	rect.right = loc.x + column->Offset() + column->Width();
901 	rect.bottom = rect.top + poseView->ListElemHeight();
902 
903 	if (minimalRect) {
904 		BTextWidget *widget = WidgetFor(poseView->FirstColumn()->AttrHash());
905 		if (widget)
906 			rect.right = widget->CalcRect(loc, poseView->FirstColumn(), poseView).right;
907 	}
908 
909 	return rect;
910 }
911 
912 
913 BRect
914 BPose::CalcRect(const BPoseView *poseView) const
915 {
916 	ASSERT(poseView->ViewMode() != kListMode);
917 
918 	BRect rect;
919 	BPoint location = Location(poseView);
920 	if (poseView->ViewMode() == kIconMode) {
921 		rect.left = location.x;
922 		rect.right = rect.left + poseView->IconSizeInt();
923 
924 		BTextWidget *widget = WidgetFor(poseView->FirstColumn()->AttrHash());
925 		if (widget) {
926 			float textWidth = ceilf(widget->TextWidth(poseView) + 1);
927 			if (textWidth > poseView->IconSizeInt()) {
928 				rect.left += (poseView->IconSizeInt() - textWidth) / 2;
929 				rect.right = rect.left + textWidth;
930 			}
931 		}
932 
933 		rect.top = location.y;
934 		rect.bottom = rect.top + poseView->IconPoseHeight();
935 	} else {
936 		// MINI_ICON_MODE rect calc
937 		rect.left = location.x;
938 		rect.top = location.y;
939 		rect.right = rect.left + B_MINI_ICON + kMiniIconSeparator;
940 		rect.bottom = rect.top + poseView->IconPoseHeight();
941 		BTextWidget *widget = WidgetFor(poseView->FirstColumn()->AttrHash());
942 		if (widget)
943 			rect.right += ceil(widget->TextWidth(poseView) + 1);
944 	}
945 
946 	return rect;
947 }
948 
949 
950 #if DEBUG
951 
952 void
953 BPose::PrintToStream()
954 {
955 	TargetModel()->PrintToStream();
956 	switch (fClipboardMode) {
957 		case kMoveSelectionTo:
958 			PRINT(("clipboardMode: Cut\n"));
959 			break;
960 		case kCopySelectionTo:
961 			PRINT(("clipboardMode: Copy\n"));
962 			break;
963 		default:
964 			PRINT(("clipboardMode: 0 - not in clipboard\n"));
965 	}
966 	PRINT(("%sselected\n", IsSelected() ? "" : "not "));
967 	PRINT(("location %s x:%f y:%f\n", HasLocation() ? "" : "unknown ",
968 		HasLocation() ? fLocation.x : 0,
969 		HasLocation() ? fLocation.y : 0));
970 	PRINT(("%s autoplaced \n", WasAutoPlaced() ? "was" : "not"));
971 }
972 
973 #endif
974