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
CalcFreeSpace(BVolume * volume)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
BPose(Model * model,BPoseView * view,uint32 clipboardMode,bool selected)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
~BPose()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
CreateWidgets(BPoseView * poseView)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*
AddWidget(BPoseView * poseView,BColumn * column)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*
AddWidget(BPoseView * poseView,BColumn * column,ModelNodeLazyOpener & opener)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
RemoveWidget(BPoseView *,BColumn * column)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
Commit(bool saveChanges,BPoint loc,BPoseView * poseView,int32 poseIndex)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
OneMouseUp(BTextWidget * widget,BPose * pose,BPoseView * poseView,BColumn * column,BPoint poseLoc,BPoint where)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
MouseUp(BPoint poseLoc,BPoseView * poseView,BPoint where,int32)218 BPose::MouseUp(BPoint poseLoc, BPoseView* poseView, BPoint where, int32)
219 {
220 WhileEachTextWidget(this, poseView, OneMouseUp, poseLoc, where);
221 }
222
223
224 inline void
OneCheckAndUpdate(BTextWidget * widget,BPose *,BPoseView * poseView,BColumn * column,BPoint poseLoc)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
UpdateAllWidgets(int32,BPoint poseLoc,BPoseView * poseView)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
UpdateWidgetAndModel(Model * resolvedModel,const char * attrName,uint32 attrType,int32,BPoint poseLoc,BPoseView * poseView,bool visible)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
_PeriodicUpdateCallback(BPose * pose,void * cookie)308 BPose::_PeriodicUpdateCallback(BPose* pose, void* cookie)
309 {
310 return pose->UpdateVolumeSpaceBar((BVolume*)cookie);
311 }
312
313
314 bool
UpdateVolumeSpaceBar(BVolume * volume)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
UpdateIcon(BPoint poseLoc,BPoseView * poseView)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 } else {
353 BPoint location = Location(poseView);
354 rect.left = location.x;
355 rect.top = location.y;
356 rect.right = rect.left + iconSize;
357 rect.bottom = rect.top + iconSize;
358 }
359
360 poseView->Invalidate(rect);
361 }
362
363
364 void
UpdateBrokenSymLink(BPoint poseLoc,BPoseView * poseView)365 BPose::UpdateBrokenSymLink(BPoint poseLoc, BPoseView* poseView)
366 {
367 ASSERT(TargetModel()->IsSymLink());
368 ASSERT(TargetModel()->LinkTo() == NULL);
369 if (!TargetModel()->IsSymLink() || TargetModel()->LinkTo() != NULL)
370 return;
371
372 UpdateIcon(poseLoc, poseView);
373 }
374
375
376 void
UpdateWasBrokenSymlink(BPoint poseLoc,BPoseView * poseView)377 BPose::UpdateWasBrokenSymlink(BPoint poseLoc, BPoseView* poseView)
378 {
379 if (!fModel->IsSymLink())
380 return;
381
382 if (fModel->LinkTo() != NULL) {
383 BEntry entry(fModel->EntryRef(), true);
384 if (entry.InitCheck() != B_OK) {
385 watch_node(fModel->LinkTo()->NodeRef(), B_STOP_WATCHING, poseView);
386 fModel->SetLinkTo(NULL);
387 UpdateIcon(poseLoc, poseView);
388 }
389 return;
390 }
391
392 poseView->CreateSymlinkPoseTarget(fModel);
393 UpdateIcon(poseLoc, poseView);
394 if (fModel->LinkTo() != NULL)
395 fModel->LinkTo()->CloseNode();
396 }
397
398
399 void
EditFirstWidget(BPoint poseLoc,BPoseView * poseView)400 BPose::EditFirstWidget(BPoint poseLoc, BPoseView* poseView)
401 {
402 // find first editable widget
403 BColumn* column;
404 for (int32 i = 0; (column = poseView->ColumnAt(i)) != NULL; i++) {
405 BTextWidget* widget = WidgetFor(column->AttrHash());
406
407 if (widget != NULL && widget->IsEditable()) {
408 BRect bounds;
409 // ToDo:
410 // fold the three StartEdit code sequences into a cover call
411 if (poseView->ViewMode() == kListMode)
412 bounds = widget->CalcRect(poseLoc, column, poseView);
413 else
414 bounds = widget->CalcRect(Location(poseView), NULL, poseView);
415
416 widget->StartEdit(bounds, poseView, this);
417 break;
418 }
419 }
420 }
421
422
423 void
EditPreviousNextWidgetCommon(BPoseView * poseView,bool next)424 BPose::EditPreviousNextWidgetCommon(BPoseView* poseView, bool next)
425 {
426 bool found = false;
427 int32 delta = next ? 1 : -1;
428 for (int32 index = next ? 0 : poseView->CountColumns() - 1; ;
429 index += delta) {
430 BColumn* column = poseView->ColumnAt(index);
431 if (column == NULL) {
432 // out of columns
433 break;
434 }
435
436 BTextWidget* widget = WidgetFor(column->AttrHash());
437 if (widget == NULL) {
438 // no widget for this column, next
439 continue;
440 }
441
442 if (widget->IsActive()) {
443 poseView->CommitActivePose();
444 found = true;
445 continue;
446 }
447
448 if (found && column->Editable()) {
449 BRect bounds;
450 if (poseView->ViewMode() == kListMode) {
451 int32 poseIndex = poseView->IndexOfPose(this);
452 BPoint poseLoc(0, poseIndex* poseView->ListElemHeight());
453 bounds = widget->CalcRect(poseLoc, column, poseView);
454 } else
455 bounds = widget->CalcRect(Location(poseView), NULL, poseView);
456
457 widget->StartEdit(bounds, poseView, this);
458 break;
459 }
460 }
461 }
462
463
464 void
EditNextWidget(BPoseView * poseView)465 BPose::EditNextWidget(BPoseView* poseView)
466 {
467 EditPreviousNextWidgetCommon(poseView, true);
468 }
469
470
471 void
EditPreviousWidget(BPoseView * poseView)472 BPose::EditPreviousWidget(BPoseView* poseView)
473 {
474 EditPreviousNextWidgetCommon(poseView, false);
475 }
476
477
478 bool
PointInPose(const BPoseView * poseView,BPoint where) const479 BPose::PointInPose(const BPoseView* poseView, BPoint where) const
480 {
481 ASSERT(poseView->ViewMode() != kListMode);
482
483 BPoint location = Location(poseView);
484
485 if (poseView->ViewMode() == kIconMode) {
486 // check icon rect, then actual icon pixel
487 BRect rect(location, location);
488 rect.right += poseView->IconSizeInt() - 1;
489 rect.bottom += poseView->IconSizeInt() - 1;
490
491 if (rect.Contains(where)) {
492 return IconCache::sIconCache->IconHitTest(where - location,
493 ResolvedModel(), kNormalIcon, poseView->IconSize());
494 }
495
496 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
497 if (widget) {
498 float textWidth = ceilf(widget->TextWidth(poseView) + 1);
499 rect.left += (poseView->IconSizeInt() - textWidth) / 2;
500 rect.right = rect.left + textWidth;
501 }
502
503 rect.top = location.y + poseView->IconSizeInt();
504 rect.bottom = rect.top + ActualFontHeight(poseView);
505
506 return rect.Contains(where);
507 }
508
509 // MINI_ICON_MODE rect calc
510 BRect rect(location, location);
511 rect.right += B_MINI_ICON + kMiniIconSeparator;
512 rect.bottom += poseView->IconPoseHeight();
513 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
514 if (widget != NULL)
515 rect.right += ceil(widget->TextWidth(poseView) + 1);
516
517 return rect.Contains(where);
518 }
519
520
521 bool
PointInPose(BPoint where,const BPoseView * poseView,BPoint point,BTextWidget ** hitWidget) const522 BPose::PointInPose(BPoint where, const BPoseView* poseView, BPoint point,
523 BTextWidget** hitWidget) const
524 {
525 if (hitWidget != NULL)
526 *hitWidget = NULL;
527
528 // check intersection with icon
529 BRect rect = _IconRect(poseView, where);
530 if (rect.Contains(point))
531 return true;
532
533 for (int32 index = 0; ; index++) {
534 BColumn* column = poseView->ColumnAt(index);
535 if (column == NULL)
536 break;
537
538 BTextWidget* widget = WidgetFor(column->AttrHash());
539 if (widget != NULL
540 && widget->CalcClickRect(where, column, poseView).Contains(point)) {
541 if (hitWidget != NULL)
542 *hitWidget = widget;
543
544 return true;
545 }
546 }
547
548 return false;
549 }
550
551
552 void
Draw(BRect rect,const BRect & updateRect,BPoseView * poseView,BView * drawView,bool fullDraw,BPoint offset,bool selected)553 BPose::Draw(BRect rect, const BRect& updateRect, BPoseView* poseView,
554 BView* drawView, bool fullDraw, BPoint offset, bool selected)
555 {
556 // If the background wasn't cleared and Draw() is not called after
557 // having edited a name or similar (with fullDraw)
558 if (!fBackgroundClean && !fullDraw) {
559 fBackgroundClean = true;
560 poseView->Invalidate(rect);
561 return;
562 } else
563 fBackgroundClean = false;
564
565 bool direct = (drawView == poseView);
566 bool windowActive = poseView->Window()->IsActive();
567 bool showSelectionWhenInactive = poseView->fShowSelectionWhenInactive;
568 bool isDrawingSelectionRect = poseView->fIsDrawingSelectionRect;
569
570 ModelNodeLazyOpener modelOpener(fModel);
571
572 if (poseView->ViewMode() == kListMode) {
573 // draw in list mode
574 BRect iconRect = _IconRect(poseView, rect.LeftTop());
575 if (updateRect.Intersects(iconRect)) {
576 iconRect.OffsetBy(offset);
577 DrawIcon(iconRect.LeftTop(), drawView, poseView->IconSize(),
578 direct, !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 if (widget == NULL || !widget->IsVisible())
594 continue;
595
596 BRect widgetRect(widget->ColumnRect(rect.LeftTop(), column,
597 poseView));
598 if (!updateRect.Intersects(widgetRect))
599 continue;
600
601 BRect widgetTextRect(widget->CalcRect(rect.LeftTop(),
602 column, poseView));
603
604 bool selectDuringDraw = direct && selected
605 && windowActive;
606
607 if (index == 0 && selectDuringDraw) {
608 // draw with "reverse video" to select text
609 drawView->PushState();
610 drawView->SetLowColor(poseView->BackColor(true));
611 } else if (!direct && index == 0 && selected
612 && (windowActive || isDrawingSelectionRect)) {
613 drawView->SetLowColor(poseView->TextColor(true)); // inverse
614 drawView->FillRect(widgetTextRect.OffsetByCopy(offset), B_SOLID_LOW);
615 }
616
617 if (index == 0) {
618 widget->Draw(widgetRect, widgetTextRect,
619 column->Width(), poseView, drawView, selected,
620 fClipboardMode, offset, direct);
621 } else {
622 widget->Draw(widgetTextRect, widgetTextRect,
623 column->Width(), poseView, drawView, false,
624 fClipboardMode, offset, direct);
625 }
626
627 if (index == 0 && selected) {
628 if (selectDuringDraw)
629 drawView->PopState();
630 else if (windowActive || isDrawingSelectionRect) {
631 drawView->SetLowColor(poseView->LowColor());
632 drawView->InvertRect(widgetTextRect.OffsetByCopy(offset));
633 } else if (!windowActive && 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 } else {
644 // draw in icon mode
645 BPoint location(Location(poseView));
646 BPoint iconOrigin(location);
647 iconOrigin += offset;
648
649 DrawIcon(iconOrigin, drawView, poseView->IconSize(), direct,
650 !windowActive && !showSelectionWhenInactive);
651
652 BColumn* column = poseView->FirstColumn();
653 if (column == NULL)
654 return;
655
656 BTextWidget* widget = WidgetFor(column, poseView, modelOpener);
657 if (widget == NULL || !widget->IsVisible())
658 return;
659
660 rect = widget->CalcRect(location, NULL, poseView);
661
662 bool selectDuringDraw = direct && selected && windowActive;
663
664 if (selectDuringDraw) {
665 // draw with "reverse video" to select text
666 drawView->PushState();
667 drawView->SetLowColor(poseView->BackColor(true));
668 }
669
670 widget->Draw(rect, rect, rect.Width(), poseView, drawView,
671 selected, fClipboardMode, offset, direct);
672
673 if (selectDuringDraw)
674 drawView->PopState();
675 else if (selected && direct) {
676 if (windowActive || isDrawingSelectionRect) {
677 rect.OffsetBy(offset);
678 drawView->InvertRect(rect);
679 } else if (!windowActive && showSelectionWhenInactive) {
680 drawView->PushState();
681 drawView->SetDrawingMode(B_OP_BLEND);
682 drawView->SetHighColor(128, 128, 128, 255);
683 drawView->FillRect(rect);
684 drawView->PopState();
685 }
686 }
687 }
688 }
689
690
691 void
DeselectWithoutErasingBackground(BRect,BPoseView * poseView)692 BPose::DeselectWithoutErasingBackground(BRect, BPoseView* poseView)
693 {
694 ASSERT(poseView->ViewMode() != kListMode);
695 ASSERT(!IsSelected());
696
697 BPoint location(Location(poseView));
698
699 // draw icon directly
700 if (fPercent == -1)
701 DrawIcon(location, poseView, poseView->IconSize(), true);
702 else
703 UpdateIcon(location, poseView);
704
705 BColumn* column = poseView->FirstColumn();
706 if (column == NULL)
707 return;
708
709 BTextWidget* widget = WidgetFor(column->AttrHash());
710 if (widget == NULL || !widget->IsVisible())
711 return;
712
713 // just invalidate the background, don't draw anything
714 poseView->Invalidate(widget->CalcRect(location, NULL, poseView));
715 }
716
717
718 void
MoveTo(BPoint point,BPoseView * poseView,bool invalidate)719 BPose::MoveTo(BPoint point, BPoseView* poseView, bool invalidate)
720 {
721 point.x = floorf(point.x);
722 point.y = floorf(point.y);
723
724 BRect oldBounds;
725
726 BPoint oldLocation = Location(poseView);
727
728 ASSERT(poseView->ViewMode() != kListMode);
729 if (point == oldLocation || poseView->ViewMode() == kListMode)
730 return;
731
732 if (invalidate)
733 oldBounds = CalcRect(poseView);
734
735 // might need to move a text view if we're active
736 if (poseView->ActivePose() == this) {
737 BView* border_view = poseView->FindView("BorderView");
738 if (border_view) {
739 border_view->MoveBy(point.x - oldLocation.x,
740 point.y - oldLocation.y);
741 }
742 }
743
744 float scale = 1.0;
745 if (poseView->ViewMode() == kIconMode) {
746 scale = (float)poseView->IconSizeInt() / 32.0;
747 }
748 fLocation.x = point.x / scale;
749 fLocation.y = point.y / scale;
750
751 fHasLocation = true;
752 fNeedsSaveLocation = true;
753
754 if (invalidate) {
755 poseView->Invalidate(oldBounds);
756 poseView->Invalidate(CalcRect(poseView));
757 }
758 }
759
760
761 BTextWidget*
ActiveWidget() const762 BPose::ActiveWidget() const
763 {
764 for (int32 i = fWidgetList.CountItems(); i-- > 0;) {
765 BTextWidget* widget = fWidgetList.ItemAt(i);
766 if (widget->IsActive())
767 return widget;
768 }
769
770 return NULL;
771 }
772
773
774 BTextWidget*
WidgetFor(uint32 attr,int32 * index) const775 BPose::WidgetFor(uint32 attr, int32* index) const
776 {
777 int32 count = fWidgetList.CountItems();
778 for (int32 i = 0; i < count; i++) {
779 BTextWidget* widget = fWidgetList.ItemAt(i);
780 if (widget->AttrHash() == attr) {
781 if (index != NULL)
782 *index = i;
783
784 return widget;
785 }
786 }
787
788 return NULL;
789 }
790
791
792 BTextWidget*
WidgetFor(BColumn * column,BPoseView * poseView,ModelNodeLazyOpener & opener,int32 * index)793 BPose::WidgetFor(BColumn* column, BPoseView* poseView,
794 ModelNodeLazyOpener &opener, int32* index)
795 {
796 BTextWidget* widget = WidgetFor(column->AttrHash(), index);
797 if (widget == NULL)
798 widget = AddWidget(poseView, column, opener);
799
800 return widget;
801 }
802
803
804 void
DrawIcon(BPoint where,BView * view,BSize size,bool direct,bool drawUnselected)805 BPose::DrawIcon(BPoint where, BView* view, BSize size, 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, size,
818 true);
819
820 if (fPercent != -1)
821 DrawBar(where, view, size);
822 }
823
824
825 void
DrawBar(BPoint where,BView * view,BSize iconSize)826 BPose::DrawBar(BPoint where, BView* view, BSize iconSize)
827 {
828 view->PushState();
829
830 int32 size = iconSize.IntegerWidth();
831 int32 yOffset;
832 int32 barWidth = (int32)(7.0f / 32.0f * (float)(size + 1));
833 if (barWidth < 4) {
834 barWidth = 4;
835 yOffset = 0;
836 } else
837 yOffset = 2;
838 int32 barHeight = size - 4 - 2 * yOffset;
839
840 // the black shadowed line
841 view->SetHighColor(32, 32, 32, 92);
842 view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset));
843 view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset));
844 view->StrokeLine(BPoint(where.x + size - barWidth + 1,
845 where.y + size - yOffset));
846
847 view->SetDrawingMode(B_OP_ALPHA);
848
849 // the gray frame
850 view->SetHighColor(76, 76, 76, 192);
851 BRect rect(where.x + size - barWidth,where.y + yOffset,
852 where.x + size - 1,where.y + size - 1 - yOffset);
853 view->StrokeRect(rect);
854
855 // calculate bar height
856 int32 percent = fPercent > -1 ? fPercent : -2 - fPercent;
857 int32 barPos = int32(barHeight * percent / 100.0);
858 if (barPos < 0)
859 barPos = 0;
860 else if (barPos > barHeight)
861 barPos = barHeight;
862
863 // the free space bar
864 view->SetHighColor(TrackerSettings().FreeSpaceColor());
865
866 rect.InsetBy(1,1);
867 BRect bar(rect);
868 bar.bottom = bar.top + barPos - 1;
869 if (barPos > 0)
870 view->FillRect(bar);
871
872 // the used space bar
873 bar.top = bar.bottom + 1;
874 bar.bottom = rect.bottom;
875 view->SetHighColor(fPercent < -1
876 ? TrackerSettings().WarningSpaceColor()
877 : TrackerSettings().UsedSpaceColor());
878 view->FillRect(bar);
879
880 view->PopState();
881 }
882
883
884 void
DrawToggleSwitch(BRect,BPoseView *)885 BPose::DrawToggleSwitch(BRect, BPoseView*)
886 {
887 }
888
889
890 BPoint
Location(const BPoseView * poseView) const891 BPose::Location(const BPoseView* poseView) const
892 {
893 float scale = 1.0;
894 if (poseView->ViewMode() == kIconMode)
895 scale = (float)poseView->IconSizeInt() / 32.0;
896
897 return BPoint(fLocation.x * scale, fLocation.y * scale);
898 }
899
900
901 void
SetLocation(BPoint point,const BPoseView * poseView)902 BPose::SetLocation(BPoint point, const BPoseView* poseView)
903 {
904 float scale = 1.0;
905 if (poseView->ViewMode() == kIconMode)
906 scale = (float)poseView->IconSizeInt() / 32.0;
907
908 fLocation = BPoint(floorf(point.x / scale), floorf(point.y / scale));
909 if (isinff(fLocation.x) || isinff(fLocation.y))
910 debugger("BPose::SetLocation() - infinite location");
911
912 fHasLocation = true;
913 }
914
915
916 BRect
CalcRect(BPoint loc,const BPoseView * poseView,bool minimalRect) const917 BPose::CalcRect(BPoint loc, const BPoseView* poseView, bool minimalRect) const
918 {
919 ASSERT(poseView->ViewMode() == kListMode);
920
921 BColumn* column = poseView->LastColumn();
922 BRect rect;
923 rect.left = loc.x;
924 rect.top = loc.y;
925 rect.right = loc.x + column->Offset() + column->Width();
926 rect.bottom = rect.top + poseView->ListElemHeight();
927
928 if (minimalRect) {
929 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
930 if (widget != NULL) {
931 rect.right = widget->CalcRect(loc, poseView->FirstColumn(),
932 poseView).right;
933 }
934 }
935
936 return rect;
937 }
938
939
940 BRect
CalcRect(const BPoseView * poseView) const941 BPose::CalcRect(const BPoseView* poseView) const
942 {
943 ASSERT(poseView->ViewMode() != kListMode);
944
945 BRect rect;
946 BPoint location = Location(poseView);
947 if (poseView->ViewMode() == kIconMode) {
948 rect.left = location.x;
949 rect.right = rect.left + poseView->IconSizeInt();
950
951 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
952 if (widget != NULL) {
953 float textWidth = ceilf(widget->TextWidth(poseView) + 1);
954 if (textWidth > poseView->IconSizeInt()) {
955 rect.left += (poseView->IconSizeInt() - textWidth) / 2;
956 rect.right = rect.left + textWidth;
957 }
958 }
959
960 rect.top = location.y;
961 rect.bottom = rect.top + poseView->IconPoseHeight();
962 } else {
963 // MINI_ICON_MODE rect calc
964 rect.left = location.x;
965 rect.right = rect.left + B_MINI_ICON + kMiniIconSeparator;
966
967 // big font sizes can push top above icon location top
968 rect.bottom = location.y
969 + roundf((B_MINI_ICON + ActualFontHeight(poseView)) / 2) + 1;
970 rect.top = rect.bottom - floorf(ActualFontHeight(poseView));
971 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash());
972 if (widget != NULL)
973 rect.right += ceil(widget->TextWidth(poseView) + 1);
974 }
975
976 return rect;
977 }
978
979
980 BRect
_IconRect(const BPoseView * poseView,BPoint location) const981 BPose::_IconRect(const BPoseView* poseView, BPoint location) const
982 {
983 uint32 size = poseView->IconSizeInt();
984 BRect rect;
985 rect.left = location.x + poseView->ListOffset();
986 rect.right = rect.left + size;
987 rect.top = location.y + (poseView->ListElemHeight() - size) / 2.f;
988 rect.bottom = rect.top + size;
989 return rect;
990 }
991
992
993 #if DEBUG
994 void
PrintToStream()995 BPose::PrintToStream()
996 {
997 TargetModel()->PrintToStream();
998 switch (fClipboardMode) {
999 case kMoveSelectionTo:
1000 PRINT(("clipboardMode: Cut\n"));
1001 break;
1002
1003 case kCopySelectionTo:
1004 PRINT(("clipboardMode: Copy\n"));
1005 break;
1006
1007 default:
1008 PRINT(("clipboardMode: 0 - not in clipboard\n"));
1009 break;
1010 }
1011 PRINT(("%sselected\n", IsSelected() ? "" : "not "));
1012 PRINT(("location %s x:%f y:%f\n", HasLocation() ? "" : "unknown ",
1013 HasLocation() ? fLocation.x : 0,
1014 HasLocation() ? fLocation.y : 0));
1015 PRINT(("%s autoplaced \n", WasAutoPlaced() ? "was" : "not"));
1016 }
1017 #endif
1018