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