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