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