xref: /haiku/src/kits/tracker/Pose.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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
660 			&& (poseView->IsDesktopWindow() || windowActive);
661 
662 		if (selectDuringDraw) {
663 			// draw with dark background to select text
664 			drawView->PushState();
665 			drawView->SetLowColor(0, 0, 0);
666 		}
667 
668 		widget->Draw(rect, rect, rect.Width(), poseView, drawView,
669 			selected, fClipboardMode, offset, directDraw);
670 
671 		if (selectDuringDraw)
672 			drawView->PopState();
673 		else if (selected && directDraw) {
674 			if (windowActive || isDrawingSelectionRect) {
675 				rect.OffsetBy(offset);
676 				drawView->InvertRect(rect);
677 			} else if (!windowActive && showSelectionWhenInactive) {
678 				drawView->PushState();
679 				drawView->SetDrawingMode(B_OP_BLEND);
680 				drawView->SetHighColor(128, 128, 128, 255);
681 				drawView->FillRect(rect);
682 				drawView->PopState();
683 			}
684 		}
685 	}
686 }
687 
688 
689 void
690 BPose::DeselectWithoutErasingBackground(BRect, BPoseView* poseView)
691 {
692 	ASSERT(poseView->ViewMode() != kListMode);
693 	ASSERT(!IsSelected());
694 
695 	BPoint location(Location(poseView));
696 
697 	// draw icon directly
698 	if (fPercent == -1)
699 		DrawIcon(location, poseView, poseView->IconSize(), true);
700 	else
701 		UpdateIcon(location, poseView);
702 
703 	BColumn* column = poseView->FirstColumn();
704 	if (column == NULL)
705 		return;
706 
707 	BTextWidget* widget = WidgetFor(column->AttrHash());
708 	if (widget == NULL || !widget->IsVisible())
709 		return;
710 
711 	// just invalidate the background, don't draw anything
712 	poseView->Invalidate(widget->CalcRect(location, NULL, poseView));
713 }
714 
715 
716 void
717 BPose::MoveTo(BPoint point, BPoseView* poseView, bool invalidate)
718 {
719 	point.x = floorf(point.x);
720 	point.y = floorf(point.y);
721 
722 	BRect oldBounds;
723 
724 	BPoint oldLocation = Location(poseView);
725 
726 	ASSERT(poseView->ViewMode() != kListMode);
727 	if (point == oldLocation || poseView->ViewMode() == kListMode)
728 		return;
729 
730 	if (invalidate)
731 		oldBounds = CalcRect(poseView);
732 
733 	// might need to move a text view if we're active
734 	if (poseView->ActivePose() == this) {
735 		BView* border_view = poseView->FindView("BorderView");
736 		if (border_view) {
737 			border_view->MoveBy(point.x - oldLocation.x,
738 				point.y - oldLocation.y);
739 		}
740 	}
741 
742 	float scale = 1.0;
743 	if (poseView->ViewMode() == kIconMode) {
744 		scale = (float)poseView->IconSizeInt() / 32.0;
745 	}
746 	fLocation.x = point.x / scale;
747 	fLocation.y = point.y / scale;
748 
749 	fHasLocation = true;
750 	fNeedsSaveLocation = true;
751 
752 	if (invalidate) {
753 		poseView->Invalidate(oldBounds);
754 		poseView->Invalidate(CalcRect(poseView));
755 	}
756 }
757 
758 
759 BTextWidget*
760 BPose::ActiveWidget() const
761 {
762 	for (int32 i = fWidgetList.CountItems(); i-- > 0;) {
763 		BTextWidget* widget = fWidgetList.ItemAt(i);
764 		if (widget->IsActive())
765 			return widget;
766 	}
767 
768 	return NULL;
769 }
770 
771 
772 BTextWidget*
773 BPose::WidgetFor(uint32 attr, int32* index) const
774 {
775 	int32 count = fWidgetList.CountItems();
776 	for (int32 i = 0; i < count; i++) {
777 		BTextWidget* widget = fWidgetList.ItemAt(i);
778 		if (widget->AttrHash() == attr) {
779 			if (index != NULL)
780 				*index = i;
781 
782 			return widget;
783 		}
784 	}
785 
786 	return NULL;
787 }
788 
789 
790 BTextWidget*
791 BPose::WidgetFor(BColumn* column, BPoseView* poseView,
792 	ModelNodeLazyOpener &opener, int32* index)
793 {
794 	BTextWidget* widget = WidgetFor(column->AttrHash(), index);
795 	if (widget == NULL)
796 		widget = AddWidget(poseView, column, opener);
797 
798 	return widget;
799 }
800 
801 
802 void
803 BPose::DrawIcon(BPoint where, BView* view, BSize size, bool direct,
804 	bool drawUnselected)
805 {
806 	if (fClipboardMode == kMoveSelectionTo) {
807 		view->SetDrawingMode(B_OP_ALPHA);
808 		view->SetHighColor(0, 0, 0, 64);
809 			// set the level of transparency
810 		view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
811 	} else if (direct)
812 		view->SetDrawingMode(B_OP_OVER);
813 
814 	IconCache::sIconCache->Draw(ResolvedModel(), view, where,
815 		fIsSelected && !drawUnselected ? kSelectedIcon : kNormalIcon, size,
816 		true);
817 
818 	if (fPercent != -1)
819 		DrawBar(where, view, size);
820 }
821 
822 
823 void
824 BPose::DrawBar(BPoint where, BView* view, BSize iconSize)
825 {
826 	view->PushState();
827 
828 	int32 size = iconSize.IntegerWidth();
829 	int32 yOffset;
830 	int32 barWidth = (int32)(7.0f / 32.0f * (float)(size + 1));
831 	if (barWidth < 4) {
832 		barWidth = 4;
833 		yOffset = 0;
834 	} else
835 		yOffset = 2;
836 	int32 barHeight = size - 4 - 2 * yOffset;
837 
838 	// the black shadowed line
839 	view->SetHighColor(32, 32, 32, 92);
840 	view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset));
841 	view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset));
842 	view->StrokeLine(BPoint(where.x + size - barWidth + 1,
843 		where.y + size - yOffset));
844 
845 	view->SetDrawingMode(B_OP_ALPHA);
846 
847 	// the gray frame
848 	view->SetHighColor(76, 76, 76, 192);
849 	BRect rect(where.x + size - barWidth,where.y + yOffset,
850 		where.x + size - 1,where.y + size - 1 - yOffset);
851 	view->StrokeRect(rect);
852 
853 	// calculate bar height
854 	int32 percent = fPercent > -1 ? fPercent : -2 - fPercent;
855 	int32 barPos = int32(barHeight * percent / 100.0);
856 	if (barPos < 0)
857 		barPos = 0;
858 	else if (barPos > barHeight)
859 		barPos = barHeight;
860 
861 	// the free space bar
862 	view->SetHighColor(TrackerSettings().FreeSpaceColor());
863 
864 	rect.InsetBy(1,1);
865 	BRect bar(rect);
866 	bar.bottom = bar.top + barPos - 1;
867 	if (barPos > 0)
868 		view->FillRect(bar);
869 
870 	// the used space bar
871 	bar.top = bar.bottom + 1;
872 	bar.bottom = rect.bottom;
873 	view->SetHighColor(fPercent < -1
874 		? TrackerSettings().WarningSpaceColor()
875 		: TrackerSettings().UsedSpaceColor());
876 	view->FillRect(bar);
877 
878 	view->PopState();
879 }
880 
881 
882 void
883 BPose::DrawToggleSwitch(BRect, BPoseView*)
884 {
885 }
886 
887 
888 BPoint
889 BPose::Location(const BPoseView* poseView) const
890 {
891 	float scale = 1.0;
892 	if (poseView->ViewMode() == kIconMode)
893 		scale = (float)poseView->IconSizeInt() / 32.0;
894 
895 	return BPoint(fLocation.x * scale, fLocation.y * scale);
896 }
897 
898 
899 void
900 BPose::SetLocation(BPoint point, const BPoseView* poseView)
901 {
902 	float scale = 1.0;
903 	if (poseView->ViewMode() == kIconMode)
904 		scale = (float)poseView->IconSizeInt() / 32.0;
905 
906 	fLocation = BPoint(floorf(point.x / scale), floorf(point.y / scale));
907 	if (isinff(fLocation.x) || isinff(fLocation.y))
908 		debugger("BPose::SetLocation() - infinite location");
909 
910 	fHasLocation = true;
911 }
912 
913 
914 BRect
915 BPose::CalcRect(BPoint loc, const BPoseView* poseView, bool minimalRect) const
916 {
917 	ASSERT(poseView->ViewMode() == kListMode);
918 
919 	BColumn* column = poseView->LastColumn();
920 	BRect rect;
921 	rect.left = loc.x;
922 	rect.top = loc.y;
923 	rect.right = loc.x + column->Offset() + column->Width();
924 	rect.bottom = rect.top + poseView->ListElemHeight();
925 
926 	if (minimalRect) {
927 		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
928 		if (widget != NULL) {
929 			rect.right = widget->CalcRect(loc, poseView->FirstColumn(),
930 				poseView).right;
931 		}
932 	}
933 
934 	return rect;
935 }
936 
937 
938 BRect
939 BPose::CalcRect(const BPoseView* poseView) const
940 {
941 	ASSERT(poseView->ViewMode() != kListMode);
942 
943 	BRect rect;
944 	BPoint location = Location(poseView);
945 	if (poseView->ViewMode() == kIconMode) {
946 		rect.left = location.x;
947 		rect.right = rect.left + poseView->IconSizeInt();
948 
949 		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
950 		if (widget != NULL) {
951 			float textWidth = ceilf(widget->TextWidth(poseView) + 1);
952 			if (textWidth > poseView->IconSizeInt()) {
953 				rect.left += (poseView->IconSizeInt() - textWidth) / 2;
954 				rect.right = rect.left + textWidth;
955 			}
956 		}
957 
958 		rect.top = location.y;
959 		rect.bottom = rect.top + poseView->IconPoseHeight();
960 	} else {
961 		// MINI_ICON_MODE rect calc
962 		rect.left = location.x;
963 		rect.right = rect.left + B_MINI_ICON + kMiniIconSeparator;
964 
965 		// big font sizes can push top above icon location top
966 		rect.bottom = location.y
967 			+ roundf((B_MINI_ICON + poseView->FontHeight()) / 2);
968 		rect.top = rect.bottom - floorf(poseView->FontHeight());
969 		BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
970 		if (widget != NULL)
971 			rect.right += ceil(widget->TextWidth(poseView) + 1);
972 	}
973 
974 	return rect;
975 }
976 
977 
978 BRect
979 BPose::_IconRect(const BPoseView* poseView, BPoint location) const
980 {
981 	uint32 size = poseView->IconSizeInt();
982 	BRect rect;
983 	rect.left = location.x + poseView->ListOffset();
984 	rect.right = rect.left + size;
985 	rect.top = location.y + (poseView->ListElemHeight() - size) / 2.f;
986 	rect.bottom = rect.top + size;
987 	return rect;
988 }
989 
990 
991 #if DEBUG
992 void
993 BPose::PrintToStream()
994 {
995 	TargetModel()->PrintToStream();
996 	switch (fClipboardMode) {
997 		case kMoveSelectionTo:
998 			PRINT(("clipboardMode: Cut\n"));
999 			break;
1000 
1001 		case kCopySelectionTo:
1002 			PRINT(("clipboardMode: Copy\n"));
1003 			break;
1004 
1005 		default:
1006 			PRINT(("clipboardMode: 0 - not in clipboard\n"));
1007 			break;
1008 	}
1009 	PRINT(("%sselected\n", IsSelected() ? "" : "not "));
1010 	PRINT(("location %s x:%f y:%f\n", HasLocation() ? "" : "unknown ",
1011 		HasLocation() ? fLocation.x : 0,
1012 		HasLocation() ? fLocation.y : 0));
1013 	PRINT(("%s autoplaced \n", WasAutoPlaced() ? "was" : "not"));
1014 }
1015 #endif
1016