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
30 trademarks of Be Incorporated in the United States and other countries. Other
31 brand product names are registered trademarks or trademarks of their respective
32 holders.
33 All rights reserved.
34 */
35
36
37 #include "StatusView.h"
38
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <algorithm>
45
46 #include <fs_index.h>
47 #include <fs_info.h>
48
49 #include <Application.h>
50 #include <Beep.h>
51 #include <Bitmap.h>
52 #include <Catalog.h>
53 #include <ControlLook.h>
54 #include <Debug.h>
55 #include <Directory.h>
56 #include <FindDirectory.h>
57 #include <Locale.h>
58 #include <MenuItem.h>
59 #include <NodeInfo.h>
60 #include <NodeMonitor.h>
61 #include <Path.h>
62 #include <PopUpMenu.h>
63 #include <Roster.h>
64 #include <Screen.h>
65 #include <Volume.h>
66 #include <VolumeRoster.h>
67 #include <Window.h>
68
69 #include "icons.h"
70
71 #include "BarApp.h"
72 #include "BarMenuBar.h"
73 #include "DeskbarUtils.h"
74 #include "ExpandoMenuBar.h"
75 #include "ResourceSet.h"
76 #include "StatusViewShelf.h"
77 #include "TimeView.h"
78
79
80 static const float kVerticalMiniMultiplier = 2.9f;
81
82 float sIconGap = 0.0f;
83 float gDragWidth, gDragRegionWidth = 0.0f;
84 float gMinReplicantHeight, gMinReplicantWidth = 0.0f;
85 float gMinimumTrayWidth, gMinimumWindowWidth, gMaximumWindowWidth = 0.0f;
86
87
88 #ifdef DB_ADDONS
89 // Add-on support
90 //
91 // Item - internal item list (node, eref, etc)
92 // Icon - physical replicant handed to the DeskbarClass class
93 // AddOn - attribute based add-on
94
95 const char* const kInstantiateItemCFunctionName = "instantiate_deskbar_item";
96 const char* const kInstantiateEntryCFunctionName = "instantiate_deskbar_entry";
97 const char* const kReplicantSettingsFile = "replicants";
98 const char* const kReplicantPathField = "replicant_path";
99
100
101 static void
DumpItem(DeskbarItemInfo * item)102 DumpItem(DeskbarItemInfo* item)
103 {
104 printf("is addon: %i, id: %" B_PRId32 "\n", item->isAddOn, item->id);
105 printf("entry_ref: %" B_PRIdDEV ", %" B_PRIdINO ", %s\n",
106 item->entryRef.device, item->entryRef.directory, item->entryRef.name);
107 printf("node_ref: %" B_PRIdDEV ", %" B_PRIdINO "\n", item->nodeRef.device,
108 item->nodeRef.node);
109 }
110
111
112 static void
DumpList(BList * itemlist)113 DumpList(BList* itemlist)
114 {
115 int32 count = itemlist->CountItems() - 1;
116 if (count < 0) {
117 printf("no items in list\n");
118 return;
119 }
120 for (int32 i = count; i >= 0; i--) {
121 DeskbarItemInfo* item = (DeskbarItemInfo*)itemlist->ItemAt(i);
122 if (!item)
123 continue;
124
125 DumpItem(item);
126 }
127 }
128 #endif /* DB_ADDONS */
129
130
131 #undef B_TRANSLATION_CONTEXT
132 #define B_TRANSLATION_CONTEXT "Tray"
133
134 // don't change the name of this view to anything other than "Status"!
135
TReplicantTray(TBarView * barView)136 TReplicantTray::TReplicantTray(TBarView* barView)
137 :
138 BView(BRect(0, 0, 1, 1), "Status", B_FOLLOW_LEFT | B_FOLLOW_TOP,
139 B_WILL_DRAW | B_FRAME_EVENTS),
140 fTime(NULL),
141 fBarView(barView),
142 fShelf(new TReplicantShelf(this)),
143 fMinimumTrayWidth(gMinimumTrayWidth),
144 fTrayPadding(3.0f),
145 fClockMargin(12.0f),
146 fAlignmentSupport(false)
147 {
148 // scale replicants by font size
149 fMaxReplicantHeight = std::max(gMinReplicantHeight,
150 float(((TBarApp*)be_app)->TeamIconSize()));
151 // but not bigger than TabHeight which depends on be_bold_font
152 // TODO this should only apply to mini-mode but we set it once here for all
153 fMaxReplicantHeight = std::min(fMaxReplicantHeight,
154 fBarView->TabHeight() - 4);
155 // TODO: depends on window size... (so use something like
156 // max(129, height * 3), and restrict the minimum window width for it)
157 // Use bold font because it depends on the window tab height.
158 fMaxReplicantWidth = 129;
159
160 fMinTrayHeight = kGutter + fMaxReplicantHeight + kGutter;
161 if (fBarView != NULL && fBarView->Vertical()
162 && (fBarView->ExpandoState() || fBarView->FullState())) {
163 fMinimumTrayWidth = gMinimumWindowWidth - kGutter - gDragRegionWidth;
164 }
165
166 // Create the time view
167 fTime = new TTimeView(fMinimumTrayWidth, fMaxReplicantHeight - 1.0,
168 fBarView);
169 }
170
171
~TReplicantTray()172 TReplicantTray::~TReplicantTray()
173 {
174 delete fShelf;
175 delete fTime;
176 }
177
178
179 void
AttachedToWindow()180 TReplicantTray::AttachedToWindow()
181 {
182 BView::AttachedToWindow();
183
184 if (be_control_look != NULL) {
185 AdoptParentColors();
186 } else {
187 SetViewUIColor(B_MENU_BACKGROUND_COLOR, B_DARKEN_1_TINT);
188 }
189 SetDrawingMode(B_OP_COPY);
190
191 Window()->SetPulseRate(1000000);
192
193 fTrayPadding = ceilf(be_control_look->ComposeSpacing(kTrayPadding) / 2);
194 fClockMargin = fTrayPadding * 4;
195
196 clock_settings* clock = ((TBarApp*)be_app)->ClockSettings();
197 fTime->SetShowSeconds(clock->showSeconds);
198 fTime->SetShowDayOfWeek(clock->showDayOfWeek);
199 fTime->SetShowTimeZone(clock->showTimeZone);
200
201 AddChild(fTime);
202
203 fTime->MoveTo(Bounds().right - fTime->Bounds().Width() - fTrayPadding, 2);
204 // will be moved into place later
205
206 if (!((TBarApp*)be_app)->Settings()->showClock)
207 fTime->Hide();
208
209 #ifdef DB_ADDONS
210 // load addons and rehydrate archives
211 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
212 InitAddOnSupport();
213 #endif
214 #endif
215 ResizeToPreferred();
216 }
217
218
219 void
DetachedFromWindow()220 TReplicantTray::DetachedFromWindow()
221 {
222 #ifdef DB_ADDONS
223 // clean up add-on support
224 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
225 DeleteAddOnSupport();
226 #endif
227 #endif
228 BView::DetachedFromWindow();
229 }
230
231
232 /*! Width is set to a minimum of kMinimumReplicantCount by kMaxReplicantWidth
233 if not in multirowmode and greater than kMinimumReplicantCount
234 the width should be calculated based on the actual replicant widths
235 */
236 void
GetPreferredSize(float * preferredWidth,float * preferredHeight)237 TReplicantTray::GetPreferredSize(float* preferredWidth, float* preferredHeight)
238 {
239 float width = 0;
240 float height = fMinTrayHeight;
241
242 if (fBarView->Vertical()) {
243 width = static_cast<TBarApp*>(be_app)->Settings()->width
244 - gDragWidth - kGutter;
245 width = std::max(gMinimumTrayWidth, width);
246
247 if (fRightBottomReplicant.IsValid())
248 height = fRightBottomReplicant.bottom;
249 else if (ReplicantCount() > 0) {
250 // The height will be uniform for the number of rows necessary
251 // to show all the replicants and gutters.
252 int32 rowCount = (int32)(height / fMaxReplicantHeight);
253 height = kGutter + (rowCount * fMaxReplicantHeight)
254 + ((rowCount - 1) * sIconGap) + kGutter;
255 height = std::max(fMinTrayHeight, height);
256 } else
257 height = fMinTrayHeight;
258 } else {
259 // if last replicant overruns clock then resize to accomodate
260 if (ReplicantCount() > 0) {
261 if (!fTime->IsHidden(fTime) && Bounds().right - fTrayPadding - 2
262 - fTime->Frame().Width() - fClockMargin
263 < fRightBottomReplicant.right + fClockMargin) {
264 width = fRightBottomReplicant.right + fClockMargin
265 + fTime->Frame().Width() + fTrayPadding + 2;
266 } else
267 width = fRightBottomReplicant.right + sIconGap + kGutter;
268 }
269
270 // this view has a fixed minimum width
271 width = std::max(gMinimumTrayWidth, width);
272
273 // if mini-mode set to tab height
274 // else if horizontal mode set to team menu item height
275 if (fBarView->MiniState())
276 height = std::max(fMinTrayHeight, fBarView->TabHeight());
277 else
278 height = fBarView->TeamMenuItemHeight();
279 }
280
281 *preferredWidth = width;
282 // add 1 for the border
283 *preferredHeight = height + 1;
284 }
285
286
287 void
AdjustPlacement()288 TReplicantTray::AdjustPlacement()
289 {
290 // called when an add-on has been added or removed
291 // need to resize the parent of this accordingly
292
293 BRect bounds = Bounds();
294 float width, height;
295 GetPreferredSize(&width, &height);
296
297 if (width == bounds.Width() && height == bounds.Height()) {
298 // no need to change anything
299 return;
300 }
301
302 Parent()->ResizeToPreferred();
303 fBarView->UpdatePlacement();
304 Parent()->Invalidate();
305 Invalidate();
306 }
307
308
309 void
MessageReceived(BMessage * message)310 TReplicantTray::MessageReceived(BMessage* message)
311 {
312 switch (message->what) {
313 case B_LOCALE_CHANGED:
314 if (fTime == NULL)
315 return;
316
317 fTime->UpdateTimeFormat();
318 fTime->Update();
319 // time string reformat -> realign
320 goto realignReplicants;
321
322 case kShowHideTime:
323 // from context menu in clock and in this view
324 ShowHideTime();
325 break;
326
327 case kShowSeconds:
328 if (fTime == NULL)
329 return;
330
331 fTime->SetShowSeconds(!fTime->ShowSeconds());
332
333 // time string reformat -> realign
334 goto realignReplicants;
335
336 case kShowDayOfWeek:
337 if (fTime == NULL)
338 return;
339
340 fTime->SetShowDayOfWeek(!fTime->ShowDayOfWeek());
341
342 // time string reformat -> realign
343 goto realignReplicants;
344
345 case kShowTimeZone:
346 if (fTime == NULL)
347 return;
348
349 fTime->SetShowTimeZone(!fTime->ShowTimeZone());
350
351 // time string reformat -> realign
352 goto realignReplicants;
353
354 case kGetClockSettings:
355 {
356 if (fTime == NULL)
357 return;
358
359 bool showClock = !fTime->IsHidden(fTime);
360 bool showSeconds = fTime->ShowSeconds();
361 bool showDayOfWeek = fTime->ShowDayOfWeek();
362 bool showTimeZone = fTime->ShowTimeZone();
363
364 BMessage reply(kGetClockSettings);
365 reply.AddBool("showClock", showClock);
366 reply.AddBool("showSeconds", showSeconds);
367 reply.AddBool("showDayOfWeek", showDayOfWeek);
368 reply.AddBool("showTimeZone", showTimeZone);
369 message->SendReply(&reply);
370 break;
371 }
372
373 #ifdef DB_ADDONS
374 case B_NODE_MONITOR:
375 HandleEntryUpdate(message);
376 break;
377 #endif
378
379 case kRealignReplicants:
380 realignReplicants:
381 RealignReplicants();
382 AdjustPlacement();
383 break;
384
385 default:
386 BView::MessageReceived(message);
387 break;
388 }
389 }
390
391
392 void
MouseDown(BPoint where)393 TReplicantTray::MouseDown(BPoint where)
394 {
395 #ifdef DB_ADDONS
396 if (modifiers() & B_CONTROL_KEY)
397 DumpList(fItemList);
398 #endif
399
400 uint32 buttons;
401
402 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
403 if (buttons == B_SECONDARY_MOUSE_BUTTON) {
404 ShowReplicantMenu(where);
405 } else {
406 BPoint save = where;
407 bigtime_t doubleClickSpeed;
408 bigtime_t start = system_time();
409 uint32 buttons;
410
411 get_click_speed(&doubleClickSpeed);
412
413 do {
414 if (fabs(where.x - save.x) > 4 || fabs(where.y - save.y) > 4)
415 // user moved out of bounds of click area
416 break;
417
418 if ((system_time() - start) > (2 * doubleClickSpeed)) {
419 ShowReplicantMenu(where);
420 break;
421 }
422
423 snooze(50000);
424 GetMouse(&where, &buttons);
425 } while (buttons);
426 }
427 BView::MouseDown(where);
428 }
429
430
431 void
ShowReplicantMenu(BPoint point)432 TReplicantTray::ShowReplicantMenu(BPoint point)
433 {
434 BPopUpMenu* menu = new BPopUpMenu("", false, false);
435
436 // If clock is visible show the extended menu, otherwise show "Show clock"
437
438 if (!fTime->IsHidden(fTime))
439 fTime->ShowTimeOptions(ConvertToScreen(point));
440 else {
441 BMenuItem* item = new BMenuItem(B_TRANSLATE("Show clock"),
442 new BMessage(kShowHideTime));
443 menu->AddItem(item);
444 menu->SetTargetForItems(this);
445 BPoint where = ConvertToScreen(point);
446 menu->Go(where, true, true, BRect(where - BPoint(4, 4),
447 where + BPoint(4, 4)), true);
448 }
449 }
450
451
452 void
ShowHideTime()453 TReplicantTray::ShowHideTime()
454 {
455 if (fTime == NULL)
456 return;
457
458 // Check from the point of view of fTime because we need to ignore
459 // whether or not the parent window is hidden.
460 if (fTime->IsHidden(fTime))
461 fTime->Show();
462 else
463 fTime->Hide();
464
465 RealignReplicants();
466 AdjustPlacement();
467
468 // Check from the point of view of fTime ignoring parent's state.
469 bool showClock = !fTime->IsHidden(fTime);
470
471 // Update showClock setting that gets saved to disk on quit
472 static_cast<TBarApp*>(be_app)->Settings()->showClock = showClock;
473
474 // Send a message to Time preferences telling it to update
475 BMessenger messenger("application/x-vnd.Haiku-Time");
476 BMessage message(kShowHideTime);
477 message.AddBool("showClock", showClock);
478 messenger.SendMessage(&message);
479 }
480
481
482 #ifdef DB_ADDONS
483
484
485 void
InitAddOnSupport()486 TReplicantTray::InitAddOnSupport()
487 {
488 // list to maintain refs to each rep added/deleted
489 fItemList = new BList();
490 BPath path;
491
492 if (GetDeskbarSettingsDirectory(path, true) == B_OK) {
493 path.Append(kReplicantSettingsFile);
494
495 BFile file(path.Path(), B_READ_ONLY);
496 if (file.InitCheck() == B_OK) {
497 status_t result;
498 BEntry entry;
499 int32 id;
500 BString path;
501 if (fAddOnSettings.Unflatten(&file) == B_OK) {
502 for (int32 i = 0; fAddOnSettings.FindString(kReplicantPathField,
503 i, &path) == B_OK; i++) {
504 if (entry.SetTo(path.String()) == B_OK && entry.Exists()) {
505 result = LoadAddOn(&entry, &id, false);
506 } else
507 result = B_ENTRY_NOT_FOUND;
508
509 if (result != B_OK) {
510 fAddOnSettings.RemoveData(kReplicantPathField, i);
511 --i;
512 }
513 }
514 }
515 }
516 }
517 }
518
519
520 void
DeleteAddOnSupport()521 TReplicantTray::DeleteAddOnSupport()
522 {
523 _SaveSettings();
524
525 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
526 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->RemoveItem(i);
527 if (item) {
528 if (item->isAddOn)
529 watch_node(&(item->nodeRef), B_STOP_WATCHING, this, Window());
530
531 delete item;
532 }
533 }
534 delete fItemList;
535
536 // stop the volume mount/unmount watch
537 stop_watching(this, Window());
538 }
539
540
541 DeskbarItemInfo*
DeskbarItemFor(node_ref & nodeRef)542 TReplicantTray::DeskbarItemFor(node_ref& nodeRef)
543 {
544 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
545 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
546 if (item == NULL)
547 continue;
548
549 if (item->nodeRef == nodeRef)
550 return item;
551 }
552
553 return NULL;
554 }
555
556
557 DeskbarItemInfo*
DeskbarItemFor(int32 id)558 TReplicantTray::DeskbarItemFor(int32 id)
559 {
560 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
561 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
562 if (item == NULL)
563 continue;
564
565 if (item->id == id)
566 return item;
567 }
568
569 return NULL;
570 }
571
572
573 bool
NodeExists(node_ref & nodeRef)574 TReplicantTray::NodeExists(node_ref& nodeRef)
575 {
576 return DeskbarItemFor(nodeRef) != NULL;
577 }
578
579
580 /*! This handles B_NODE_MONITOR & B_QUERY_UPDATE messages received
581 for the registered add-ons.
582 */
583 void
HandleEntryUpdate(BMessage * message)584 TReplicantTray::HandleEntryUpdate(BMessage* message)
585 {
586 int32 opcode;
587 if (message->FindInt32("opcode", &opcode) != B_OK)
588 return;
589
590 BPath path;
591 switch (opcode) {
592 case B_ENTRY_MOVED:
593 {
594 entry_ref ref;
595 ino_t todirectory;
596 ino_t node;
597 const char* name;
598 if (message->FindString("name", &name) == B_OK
599 && message->FindInt64("from directory", &(ref.directory))
600 == B_OK
601 && message->FindInt64("to directory", &todirectory) == B_OK
602 && message->FindInt32("device", &(ref.device)) == B_OK
603 && message->FindInt64("node", &node) == B_OK ) {
604
605 if (name == NULL)
606 break;
607
608 ref.set_name(name);
609 // change the directory reference to
610 // the new directory
611 MoveItem(&ref, todirectory);
612 }
613 break;
614 }
615
616 case B_ENTRY_REMOVED:
617 {
618 // entry was rm'd from the device
619 node_ref nodeRef;
620 if (message->FindInt32("device", &(nodeRef.device)) == B_OK
621 && message->FindInt64("node", &(nodeRef.node)) == B_OK) {
622 DeskbarItemInfo* item = DeskbarItemFor(nodeRef);
623 if (item == NULL)
624 break;
625
626 // If there is a team running where the add-on comes from,
627 // we don't want to remove the icon yet.
628 if (be_roster->IsRunning(&item->entryRef))
629 break;
630
631 UnloadAddOn(&nodeRef, NULL, true, false);
632 }
633 break;
634 }
635 }
636 }
637
638
639 /*! The add-ons must support the exported C function API
640 if they do, they will be loaded and added to deskbar
641 primary function is the Instantiate function
642 */
643 status_t
LoadAddOn(BEntry * entry,int32 * id,bool addToSettings)644 TReplicantTray::LoadAddOn(BEntry* entry, int32* id, bool addToSettings)
645 {
646 if (entry == NULL)
647 return B_BAD_VALUE;
648
649 node_ref nodeRef;
650 entry->GetNodeRef(&nodeRef);
651 // no duplicates
652 if (NodeExists(nodeRef))
653 return B_ERROR;
654
655 BNode node(entry);
656 BPath path;
657 status_t status = entry->GetPath(&path);
658 if (status != B_OK)
659 return status;
660
661 // load the add-on
662 image_id image = load_add_on(path.Path());
663 if (image < B_OK)
664 return image;
665
666 // get the view loading function symbol
667 // we first look for a symbol that takes an image_id
668 // and entry_ref pointer, if not found, go with normal
669 // instantiate function
670 BView* (*entryFunction)(image_id, const entry_ref*, float, float);
671 BView* (*itemFunction)(float, float);
672 BView* view = NULL;
673
674 entry_ref ref;
675 entry->GetRef(&ref);
676
677 if (get_image_symbol(image, kInstantiateEntryCFunctionName,
678 B_SYMBOL_TYPE_TEXT, (void**)&entryFunction) >= B_OK) {
679 view = (*entryFunction)(image, &ref, fMaxReplicantWidth,
680 fMaxReplicantHeight);
681 } else if (get_image_symbol(image, kInstantiateItemCFunctionName,
682 B_SYMBOL_TYPE_TEXT, (void**)&itemFunction) >= B_OK) {
683 view = (*itemFunction)(fMaxReplicantWidth, fMaxReplicantHeight);
684 } else {
685 unload_add_on(image);
686 return B_ERROR;
687 }
688
689 if (view == NULL || IconExists(view->Name())) {
690 delete view;
691 unload_add_on(image);
692 return B_ERROR;
693 }
694
695 BMessage* data = new BMessage;
696 view->Archive(data);
697 delete view;
698
699 // add the rep; adds info to list
700 if (AddIcon(data, id, &ref) != B_OK)
701 delete data;
702
703 if (addToSettings) {
704 fAddOnSettings.AddString(kReplicantPathField, path.Path());
705 _SaveSettings();
706 }
707
708 return B_OK;
709 }
710
711
712 status_t
AddItem(int32 id,node_ref nodeRef,BEntry & entry,bool isAddOn)713 TReplicantTray::AddItem(int32 id, node_ref nodeRef, BEntry& entry, bool isAddOn)
714 {
715 DeskbarItemInfo* item = new DeskbarItemInfo;
716 if (item == NULL)
717 return B_NO_MEMORY;
718
719 item->id = id;
720 item->isAddOn = isAddOn;
721
722 if (entry.GetRef(&item->entryRef) != B_OK) {
723 item->entryRef.device = -1;
724 item->entryRef.directory = -1;
725 item->entryRef.name = NULL;
726 }
727 item->nodeRef = nodeRef;
728
729 fItemList->AddItem(item);
730
731 if (isAddOn)
732 watch_node(&nodeRef, B_WATCH_NAME | B_WATCH_ATTR, this, Window());
733
734 return B_OK;
735 }
736
737
738 /** from entry_removed message, when attribute removed
739 * or when a device is unmounted (use removeall, by device)
740 */
741
742 void
UnloadAddOn(node_ref * nodeRef,dev_t * device,bool which,bool removeAll)743 TReplicantTray::UnloadAddOn(node_ref* nodeRef, dev_t* device, bool which,
744 bool removeAll)
745 {
746 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
747 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
748 if (item == NULL)
749 continue;
750
751 if ((which && nodeRef != NULL && item->nodeRef == *nodeRef)
752 || (device != NULL && item->nodeRef.device == *device)) {
753
754 if (device != NULL && be_roster->IsRunning(&item->entryRef))
755 continue;
756
757 RemoveIcon(item->id);
758
759 if (!removeAll)
760 break;
761 }
762 }
763 }
764
765
766 void
RemoveItem(int32 id)767 TReplicantTray::RemoveItem(int32 id)
768 {
769 DeskbarItemInfo* item = DeskbarItemFor(id);
770 if (item == NULL)
771 return;
772
773 // attribute was added via Deskbar API (AddItem(entry_ref*, int32*)
774 if (item->isAddOn) {
775 BPath path(&item->entryRef);
776 BString storedPath;
777 for (int32 i = 0;
778 fAddOnSettings.FindString(kReplicantPathField, i, &storedPath)
779 == B_OK; i++) {
780 if (storedPath == path.Path()) {
781 fAddOnSettings.RemoveData(kReplicantPathField, i);
782 break;
783 }
784 }
785 _SaveSettings();
786
787 BNode node(&item->entryRef);
788 watch_node(&item->nodeRef, B_STOP_WATCHING, this, Window());
789 }
790
791 fItemList->RemoveItem(item);
792 delete item;
793 }
794
795
796 /** ENTRY_MOVED message, moving only occurs on a device
797 * copying will occur (ENTRY_CREATED) between devices
798 */
799
800 void
MoveItem(entry_ref * ref,ino_t toDirectory)801 TReplicantTray::MoveItem(entry_ref* ref, ino_t toDirectory)
802 {
803 if (ref == NULL)
804 return;
805
806 // scan for a matching entry_ref and update it
807 //
808 // don't need to change node info as it does not change
809
810 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
811 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
812 if (item == NULL)
813 continue;
814
815 if (strcmp(item->entryRef.name, ref->name) == 0
816 && item->entryRef.device == ref->device
817 && item->entryRef.directory == ref->directory) {
818 item->entryRef.directory = toDirectory;
819 break;
820 }
821 }
822 }
823
824 #endif // add-on support
825
826 // external add-on API routines
827 // called using the new BDeskbar class
828
829 // existence of icon/replicant by name or ID
830 // returns opposite
831 // note: name and id are semi-private limiting
832 // the ability of non-host apps to remove
833 // icons without a little bit of work
834
835 /** for a specific id
836 * return the name of the replicant (name of view)
837 */
838
839 status_t
ItemInfo(int32 id,const char ** name)840 TReplicantTray::ItemInfo(int32 id, const char** name)
841 {
842 if (id < 0)
843 return B_BAD_VALUE;
844
845 int32 index;
846 int32 temp;
847 BView* view = ViewAt(&index, &temp, id, false);
848 if (view != NULL) {
849 *name = view->Name();
850 return B_OK;
851 }
852
853 return B_ERROR;
854 }
855
856
857 /** for a specific name
858 * return the id (internal to Deskbar)
859 */
860
861 status_t
ItemInfo(const char * name,int32 * id)862 TReplicantTray::ItemInfo(const char* name, int32* id)
863 {
864 if (name == NULL || *name == '\0')
865 return B_BAD_VALUE;
866
867 int32 index;
868 BView* view = ViewAt(&index, id, name);
869
870 return view != NULL ? B_OK : B_ERROR;
871 }
872
873
874 /** at a specific index
875 * return both the name and the id of the replicant
876 */
877
878 status_t
ItemInfo(int32 index,const char ** name,int32 * id)879 TReplicantTray::ItemInfo(int32 index, const char** name, int32* id)
880 {
881 if (index < 0)
882 return B_BAD_VALUE;
883
884 BView* view;
885 fShelf->ReplicantAt(index, &view, (uint32*)id, NULL);
886 if (view != NULL) {
887 *name = view->Name();
888 return B_OK;
889 }
890
891 return B_ERROR;
892 }
893
894
895 /** replicant exists, by id/index */
896
897 bool
IconExists(int32 target,bool byIndex)898 TReplicantTray::IconExists(int32 target, bool byIndex)
899 {
900 int32 index;
901 int32 id;
902 BView* view = ViewAt(&index, &id, target, byIndex);
903
904 return view && index >= 0;
905 }
906
907
908 /** replicant exists, by name */
909
910 bool
IconExists(const char * name)911 TReplicantTray::IconExists(const char* name)
912 {
913 if (name == NULL || *name == '\0')
914 return false;
915
916 int32 index;
917 int32 id;
918 BView* view = ViewAt(&index, &id, name);
919
920 return view != NULL && index >= 0;
921 }
922
923
924 int32
ReplicantCount() const925 TReplicantTray::ReplicantCount() const
926 {
927 return fShelf->CountReplicants();
928 }
929
930
931 /*! Message must contain an archivable view for later rehydration.
932 This function takes over ownership of the provided message on success
933 only.
934 Returns the current replicant ID.
935 */
936 status_t
AddIcon(BMessage * archive,int32 * id,const entry_ref * addOn)937 TReplicantTray::AddIcon(BMessage* archive, int32* id, const entry_ref* addOn)
938 {
939 if (archive == NULL || id == NULL)
940 return B_BAD_VALUE;
941
942 // find entry_ref
943
944 entry_ref ref;
945 if (addOn != NULL) {
946 // Use it if we got it
947 ref = *addOn;
948 } else {
949 const char* signature;
950
951 status_t status = archive->FindString("add_on", &signature);
952 if (status == B_OK) {
953 BRoster roster;
954 status = roster.FindApp(signature, &ref);
955 }
956 if (status != B_OK)
957 return status;
958 }
959
960 BFile file;
961 status_t status = file.SetTo(&ref, B_READ_ONLY);
962 if (status != B_OK)
963 return status;
964
965 node_ref nodeRef;
966 status = file.GetNodeRef(&nodeRef);
967 if (status != B_OK)
968 return status;
969
970 BEntry entry(&ref, true);
971 // TODO: this resolves an eventual link for the item being added - this
972 // is okay for now, but in multi-user environments, one might want to
973 // have links that carry the be:deskbar_item_status attribute
974 status = entry.InitCheck();
975 if (status != B_OK)
976 return status;
977
978 *id = 999;
979 if (archive->what == B_ARCHIVED_OBJECT)
980 archive->what = 0;
981
982 BRect originalBounds = archive->FindRect("_frame");
983 // this is a work-around for buggy replicants that change their size in
984 // AttachedToWindow() (such as "SVM")
985
986 // TODO: check for name collisions?
987 status = fShelf->AddReplicant(archive, BPoint(1, 1));
988 if (status != B_OK)
989 return status;
990
991 int32 count = ReplicantCount();
992 BView* view;
993 fShelf->ReplicantAt(count - 1, &view, (uint32*)id, NULL);
994
995 if (view != NULL && originalBounds != view->Bounds()) {
996 // The replicant changed its size when added to the window, so we need
997 // to recompute all over again (it's already done once via
998 // BShelf::AddReplicant() and TReplicantShelf::CanAcceptReplicantView())
999 RealignReplicants();
1000 }
1001
1002 float oldWidth = Bounds().Width();
1003 float oldHeight = Bounds().Height();
1004 float width, height;
1005 GetPreferredSize(&width, &height);
1006 if (oldWidth != width || oldHeight != height)
1007 AdjustPlacement();
1008
1009 // add the item to the add-on list
1010
1011 AddItem(*id, nodeRef, entry, addOn != NULL);
1012 return B_OK;
1013 }
1014
1015
1016 void
RemoveIcon(int32 target,bool byIndex)1017 TReplicantTray::RemoveIcon(int32 target, bool byIndex)
1018 {
1019 if (target < 0)
1020 return;
1021
1022 int32 index;
1023 int32 id;
1024 BView* view = ViewAt(&index, &id, target, byIndex);
1025 if (view != NULL && index >= 0) {
1026 // remove the reference from the item list & the shelf
1027 RemoveItem(id);
1028 fShelf->DeleteReplicant(index);
1029
1030 // force a placement update, !! need to fix BShelf
1031 RealReplicantAdjustment(index);
1032 }
1033 }
1034
1035
1036 void
RemoveIcon(const char * name)1037 TReplicantTray::RemoveIcon(const char* name)
1038 {
1039 if (name == NULL || *name == '\0')
1040 return;
1041
1042 int32 index;
1043 int32 id;
1044 BView* view = ViewAt(&index, &id, name);
1045 if (view != NULL && index >= 0) {
1046 // remove the reference from the item list & shelf
1047 RemoveItem(id);
1048 fShelf->DeleteReplicant(index);
1049
1050 // force a placement update, !! need to fix BShelf
1051 RealReplicantAdjustment(index);
1052 }
1053 }
1054
1055
1056 void
RealReplicantAdjustment(int32 startIndex)1057 TReplicantTray::RealReplicantAdjustment(int32 startIndex)
1058 {
1059 if (startIndex < 0)
1060 return;
1061
1062 if (startIndex == fLastReplicant)
1063 startIndex = 0;
1064
1065 // reset the locations of all replicants after the one deleted
1066 RealignReplicants(startIndex);
1067
1068 float oldWidth = Bounds().Width();
1069 float oldHeight = Bounds().Height();
1070 float width, height;
1071 GetPreferredSize(&width, &height);
1072 if (oldWidth != width || oldHeight != height) {
1073 // resize view to accomodate the replicants, redraw as necessary
1074 AdjustPlacement();
1075 }
1076 }
1077
1078
1079 /** looking for a replicant by id/index
1080 * return the view and index
1081 */
1082
1083 BView*
ViewAt(int32 * index,int32 * id,int32 target,bool byIndex)1084 TReplicantTray::ViewAt(int32* index, int32* id, int32 target, bool byIndex)
1085 {
1086 *index = -1;
1087
1088 BView* view;
1089 if (byIndex) {
1090 if (fShelf->ReplicantAt(target, &view, (uint32*)id)) {
1091 if (view != NULL) {
1092 *index = target;
1093
1094 return view;
1095 }
1096 }
1097 } else {
1098 int32 count = ReplicantCount() - 1;
1099 int32 localid;
1100 for (int32 repIndex = count; repIndex >= 0; repIndex--) {
1101 fShelf->ReplicantAt(repIndex, &view, (uint32*)&localid);
1102 if (localid == target && view != NULL) {
1103 *index = repIndex;
1104 *id = localid;
1105
1106 return view;
1107 }
1108 }
1109 }
1110
1111 return NULL;
1112 }
1113
1114
1115 /** looking for a replicant with a view by name
1116 * return the view, index and the id of the replicant
1117 */
1118
1119 BView*
ViewAt(int32 * index,int32 * id,const char * name)1120 TReplicantTray::ViewAt(int32* index, int32* id, const char* name)
1121 {
1122 *index = -1;
1123 *id = -1;
1124
1125 BView* view;
1126 int32 count = ReplicantCount() - 1;
1127 for (int32 repIndex = count; repIndex >= 0; repIndex--) {
1128 fShelf->ReplicantAt(repIndex, &view, (uint32*)id);
1129 if (view != NULL && view->Name() != NULL
1130 && strcmp(name, view->Name()) == 0) {
1131 *index = repIndex;
1132
1133 return view;
1134 }
1135 }
1136
1137 return NULL;
1138 }
1139
1140
1141 /** Shelf will call to determine where and if
1142 * the replicant is to be added
1143 */
1144
1145 bool
AcceptAddon(BRect replicantFrame,BMessage * message)1146 TReplicantTray::AcceptAddon(BRect replicantFrame, BMessage* message)
1147 {
1148 if (message == NULL)
1149 return false;
1150
1151 if (replicantFrame.Height() > fMaxReplicantHeight)
1152 return false;
1153
1154 alignment align = B_ALIGN_LEFT;
1155 if (fAlignmentSupport && message->HasBool("deskbar:dynamic_align")) {
1156 if (!fBarView->Vertical() && !fBarView->MiniState())
1157 align = B_ALIGN_RIGHT;
1158 else
1159 align = fBarView->Left() ? B_ALIGN_LEFT : B_ALIGN_RIGHT;
1160 } else if (message->HasInt32("deskbar:align"))
1161 message->FindInt32("deskbar:align", (int32*)&align);
1162
1163 if (message->HasInt32("deskbar:private_align"))
1164 message->FindInt32("deskbar:private_align", (int32*)&align);
1165 else
1166 align = B_ALIGN_LEFT;
1167
1168 BPoint loc = LocationForReplicant(ReplicantCount(),
1169 replicantFrame.Width());
1170 message->AddPoint("_pjp_loc", loc);
1171
1172 return true;
1173 }
1174
1175
1176 /** based on the previous (index - 1) replicant in the list
1177 * calculate where the left point should be for this
1178 * replicant. replicant will flow to the right on its own
1179 */
1180
1181 BPoint
LocationForReplicant(int32 index,float replicantWidth)1182 TReplicantTray::LocationForReplicant(int32 index, float replicantWidth)
1183 {
1184 BPoint loc(fTrayPadding, 0);
1185 if (fBarView->Vertical() || fBarView->MiniState()) {
1186 if (fBarView->Vertical() && !fBarView->Left())
1187 loc.x += gDragWidth; // move past dragger on left
1188
1189 loc.y = floorf((fBarView->TabHeight() - fMaxReplicantHeight) / 2) - 1;
1190 } else {
1191 loc.x -= 2; // keeps everything lined up nicely
1192 const int32 iconSize = static_cast<TBarApp*>(be_app)->TeamIconSize();
1193 float yOffset = iconSize > B_MINI_ICON ? 3 : 2;
1194 // squeeze icons in there at 16x16, reduce border by 1px
1195
1196 if (fBarView->Top()) {
1197 // align top
1198 loc.y = yOffset;
1199 } else {
1200 // align bottom
1201 loc.y = (fBarView->TeamMenuItemHeight() + 1)
1202 - fMaxReplicantHeight - yOffset;
1203 }
1204 }
1205
1206 // move clock vertically centered in first row next to replicants
1207 fTime->MoveTo(Bounds().right - fTime->Bounds().Width() - fTrayPadding,
1208 loc.y + floorf((fMaxReplicantHeight - fTime->fHeight) / 2));
1209
1210 if (fBarView->Vertical()) {
1211 // try to find free space in every row
1212 for (int32 row = 0; ; loc.y += fMaxReplicantHeight + sIconGap, row++) {
1213 // determine free space in this row
1214 BRect rowRect(loc.x, loc.y,
1215 loc.x + Bounds().Width() - fTrayPadding,
1216 loc.y + fMaxReplicantHeight);
1217 if (row == 0 && !fTime->IsHidden(fTime))
1218 rowRect.right -= fClockMargin + fTime->Frame().Width();
1219
1220 BRect replicantRect = rowRect;
1221 for (int32 i = 0; i < index; i++) {
1222 BView* view = NULL;
1223 fShelf->ReplicantAt(i, &view);
1224 if (view == NULL || view->Frame().top != rowRect.top)
1225 continue;
1226
1227 // push this replicant placement past the last one
1228 replicantRect.left = view->Frame().right + sIconGap + 1;
1229 }
1230
1231 // calculated left position, add replicantWidth to get the
1232 // right position
1233 replicantRect.right = replicantRect.left + replicantWidth;
1234
1235 // check if replicant fits in this row
1236 if (replicantRect.right < rowRect.right) {
1237 // replicant fits in this row
1238 loc = replicantRect.LeftTop();
1239 break;
1240 }
1241
1242 // check next row
1243 }
1244 } else {
1245 // horizontal
1246 if (index > 0) {
1247 // get the last replicant added for placement reference
1248 BView* view = NULL;
1249 fShelf->ReplicantAt(index - 1, &view);
1250 if (view != NULL) {
1251 // push this replicant placement past the last one
1252 loc.x = view->Frame().right + sIconGap + 1;
1253 }
1254 }
1255 }
1256
1257 if (loc.y > fRightBottomReplicant.top
1258 || (loc.y == fRightBottomReplicant.top
1259 && loc.x > fRightBottomReplicant.left)) {
1260 fRightBottomReplicant.Set(loc.x, loc.y, loc.x + replicantWidth,
1261 loc.y + fMaxReplicantHeight);
1262 fLastReplicant = index;
1263 }
1264
1265 return loc;
1266 }
1267
1268
1269 BRect
IconFrame(int32 target,bool byIndex)1270 TReplicantTray::IconFrame(int32 target, bool byIndex)
1271 {
1272 int32 index;
1273 int32 id;
1274 BView* view = ViewAt(&index, &id, target, byIndex);
1275
1276 return view != NULL ? view->Frame() : BRect(0, 0, 0, 0);
1277 }
1278
1279
1280 BRect
IconFrame(const char * name)1281 TReplicantTray::IconFrame(const char* name)
1282 {
1283 if (name == NULL)
1284 return BRect(0, 0, 0, 0);
1285
1286 int32 index;
1287 int32 id;
1288 BView* view = ViewAt(&index, &id, name);
1289
1290 return view != NULL ? view->Frame() : BRect(0, 0, 0, 0);
1291 }
1292
1293
1294 /** Scan from the startIndex and reset the location
1295 * as defined in LocationForReplicant()
1296 */
1297
1298 void
RealignReplicants(int32 startIndex)1299 TReplicantTray::RealignReplicants(int32 startIndex)
1300 {
1301 if (startIndex < 0)
1302 startIndex = 0;
1303
1304 int32 replicantCount = ReplicantCount();
1305 if (replicantCount <= 0)
1306 return;
1307
1308 if (startIndex == 0)
1309 fRightBottomReplicant.Set(0, 0, 0, 0);
1310
1311 BView* view = NULL;
1312 for (int32 index = startIndex; index < replicantCount; index++) {
1313 fShelf->ReplicantAt(index, &view);
1314 if (view == NULL)
1315 continue;
1316
1317 float replicantWidth = view->Frame().Width();
1318 BPoint loc = LocationForReplicant(index, replicantWidth);
1319 if (view->Frame().LeftTop() != loc)
1320 view->MoveTo(loc);
1321 }
1322 }
1323
1324
1325 status_t
_SaveSettings()1326 TReplicantTray::_SaveSettings()
1327 {
1328 status_t result;
1329 BPath path;
1330 if ((result = GetDeskbarSettingsDirectory(path, true)) == B_OK) {
1331 path.Append(kReplicantSettingsFile);
1332
1333 BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1334 if ((result = file.InitCheck()) == B_OK)
1335 result = fAddOnSettings.Flatten(&file);
1336 }
1337
1338 return result;
1339 }
1340
1341
1342 void
SaveTimeSettings()1343 TReplicantTray::SaveTimeSettings()
1344 {
1345 if (fTime == NULL)
1346 return;
1347
1348 clock_settings* settings = ((TBarApp*)be_app)->ClockSettings();
1349 settings->showSeconds = fTime->ShowSeconds();
1350 settings->showDayOfWeek = fTime->ShowDayOfWeek();
1351 settings->showTimeZone = fTime->ShowTimeZone();
1352 }
1353
1354
1355 // #pragma mark - TDragRegion
1356
1357
1358 /*! Draggable region that is asynchronous so that dragging does not block
1359 other activities.
1360 */
TDragRegion(TBarView * barView,BView * replicantTray)1361 TDragRegion::TDragRegion(TBarView* barView, BView* replicantTray)
1362 :
1363 BControl(BRect(0, 0, 0, 0), "", "", NULL, B_FOLLOW_NONE,
1364 B_WILL_DRAW | B_DRAW_ON_CHILDREN | B_FRAME_EVENTS),
1365 fBarView(barView),
1366 fReplicantTray(replicantTray),
1367 fDragLocation(kAutoPlaceDragRegion)
1368 {
1369 }
1370
1371
1372 void
AttachedToWindow()1373 TDragRegion::AttachedToWindow()
1374 {
1375 BView::AttachedToWindow();
1376
1377 CalculateRegions();
1378
1379 if (be_control_look != NULL)
1380 SetViewUIColor(B_MENU_BACKGROUND_COLOR, 1.1);
1381 else
1382 SetViewUIColor(B_MENU_BACKGROUND_COLOR);
1383
1384 ResizeToPreferred();
1385 }
1386
1387
1388 void
GetPreferredSize(float * width,float * height)1389 TDragRegion::GetPreferredSize(float* width, float* height)
1390 {
1391 fReplicantTray->ResizeToPreferred();
1392 *width = fReplicantTray->Bounds().Width();
1393 *height = fReplicantTray->Bounds().Height();
1394
1395 if (fDragLocation != kNoDragRegion)
1396 *width += gDragWidth + kGutter;
1397 else
1398 *width += 6;
1399
1400 if (fBarView->Vertical() && !fBarView->MiniState())
1401 *height += 3; // add a pixel for an extra border on top
1402 else
1403 *height += 2; // all other modes have a 1px border on top and bottom
1404 }
1405
1406
1407 void
Draw(BRect updateRect)1408 TDragRegion::Draw(BRect updateRect)
1409 {
1410 rgb_color menuColor = ViewColor();
1411 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
1412 rgb_color ldark = tint_color(menuColor, 1.02);
1413 rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT);
1414
1415 BRect frame(Bounds());
1416 BeginLineArray(4);
1417
1418 if (fBarView->Vertical()) {
1419 // vertical expando full or mini state, draw 2 lines at the top
1420 AddLine(frame.LeftTop(), frame.RightTop(), dark);
1421 AddLine(BPoint(frame.left, frame.top + 1),
1422 BPoint(frame.right, frame.top + 1), ldark);
1423 // add hilight along bottom
1424 AddLine(BPoint(frame.left + 1, frame.bottom),
1425 BPoint(frame.right - 1, frame.bottom), hilite);
1426 } else {
1427 // mini-mode or horizontal, draw hilight along top left and bottom
1428 AddLine(frame.LeftTop(), frame.RightTop(), hilite);
1429 AddLine(BPoint(frame.left, frame.top + 1), frame.LeftBottom(), hilite);
1430 if (!fBarView->Vertical()) {
1431 // only draw bottom hilight in horizontal mode
1432 AddLine(BPoint(frame.left + 1, frame.bottom - 3),
1433 BPoint(frame.right - 1, frame.bottom - 3), hilite);
1434 }
1435 }
1436
1437 EndLineArray();
1438 }
1439
1440
1441 void
DrawAfterChildren(BRect updateRect)1442 TDragRegion::DrawAfterChildren(BRect updateRect)
1443 {
1444 if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion)
1445 DrawDragger();
1446 }
1447
1448
1449 void
DrawDragger()1450 TDragRegion::DrawDragger()
1451 {
1452 BRect dragRegion(DragRegion());
1453
1454 rgb_color menuColor = ViewColor();
1455 rgb_color menuHilite = menuColor;
1456 if (IsTracking()) {
1457 // draw drag region highlighted if tracking mouse
1458 menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT);
1459 SetHighColor(menuHilite);
1460 FillRect(dragRegion.InsetByCopy(0, -1));
1461 } else {
1462 SetHighColor(menuColor);
1463 FillRect(dragRegion.InsetByCopy(0, 1));
1464 }
1465
1466 rgb_color vdark = tint_color(menuHilite, B_DARKEN_3_TINT);
1467 rgb_color light = tint_color(menuHilite, B_LIGHTEN_2_TINT);
1468
1469 rgb_color dark = tint_color(menuHilite, B_DARKEN_2_TINT);
1470
1471 BeginLineArray(dragRegion.IntegerHeight() + 2);
1472 BPoint where;
1473 where.x = floorf((dragRegion.left + dragRegion.right) / 2 + 0.5) - 1;
1474 where.y = dragRegion.top + 2;
1475
1476 while (where.y + 1 <= dragRegion.bottom - 2) {
1477 AddLine(where, where, vdark);
1478 AddLine(where + BPoint(1, 1), where + BPoint(1, 1), light);
1479
1480 where.y += 3;
1481 }
1482
1483 if (fBarView != NULL && fBarView->Vertical() && fBarView->MiniState()
1484 && !fBarView->Top()) {
1485 // extend bottom border in bottom mini-mode
1486 AddLine(BPoint(dragRegion.left, dragRegion.bottom - 2),
1487 BPoint(dragRegion.right, dragRegion.bottom - 2),
1488 IsTracking() ? menuHilite : dark);
1489 }
1490
1491 EndLineArray();
1492 }
1493
1494
1495 BRect
DragRegion() const1496 TDragRegion::DragRegion() const
1497 {
1498 BRect dragRegion(Bounds());
1499
1500 bool placeOnLeft = false;
1501 if (fDragLocation == kAutoPlaceDragRegion) {
1502 placeOnLeft = fBarView->Left()
1503 && (fBarView->Vertical() || fBarView->MiniState());
1504 } else
1505 placeOnLeft = fDragLocation == kDragRegionLeft;
1506
1507 if (placeOnLeft)
1508 dragRegion.right = dragRegion.left + gDragWidth;
1509 else
1510 dragRegion.left = dragRegion.right - gDragWidth;
1511
1512 return dragRegion;
1513 }
1514
1515
1516 void
MouseDown(BPoint where)1517 TDragRegion::MouseDown(BPoint where)
1518 {
1519 uint32 buttons;
1520 BPoint mouseLoc;
1521
1522 BRect dragRegion(DragRegion());
1523 dragRegion.InsetBy(-2, -2);
1524 // DragRegion() is designed for drawing, not clicking
1525
1526 if (!dragRegion.Contains(where))
1527 return;
1528
1529 while (true) {
1530 GetMouse(&mouseLoc, &buttons);
1531 if (buttons == 0)
1532 break;
1533
1534 if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) {
1535 fPreviousPosition = where;
1536 SetTracking(true);
1537 SetMouseEventMask(B_POINTER_EVENTS,
1538 B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
1539 Invalidate(DragRegion());
1540 break;
1541 }
1542
1543 snooze(25000);
1544 }
1545 }
1546
1547
1548 void
MouseUp(BPoint where)1549 TDragRegion::MouseUp(BPoint where)
1550 {
1551 if (IsTracking()) {
1552 SetTracking(false);
1553 Invalidate(DragRegion());
1554 } else
1555 BControl::MouseUp(where);
1556 }
1557
1558
1559 bool
SwitchModeForRegion(BPoint where,BRegion region,bool newVertical,bool newLeft,bool newTop,int32 newState)1560 TDragRegion::SwitchModeForRegion(BPoint where, BRegion region,
1561 bool newVertical, bool newLeft, bool newTop, int32 newState)
1562 {
1563 if (!region.Contains(where)) {
1564 // not our region
1565 return false;
1566 }
1567
1568 if (newVertical == fBarView->Vertical() && newLeft == fBarView->Left()
1569 && newTop == fBarView->Top() && newState == fBarView->State()) {
1570 // already in the correct mode
1571 return true;
1572 }
1573
1574 fBarView->ChangeState(newState, newVertical, newLeft, newTop, true);
1575
1576 return true;
1577 }
1578
1579
1580 //! Deskbar regions
1581 //
1582 // ┌───────3──────────┬─────────────────────────────────┬──────────3───────┐
1583 // ├────────────────┬─┘ └─┬────────────────┤
1584 // │ 2 │ │ 2 │
1585 // ├────────────────┤ ├────────────────┤
1586 // │ │ │ │
1587 // │ │ │ │
1588 // │ │ 4 │ │
1589 // │ │ │ │
1590 // │ │ │ │
1591 // │ 1 │ │ 1 │
1592 // │ │ │ │
1593 // │ │ │ │
1594 // │ ├─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤ │
1595 // │ │ │ │
1596 // │ │ │ │
1597 // │ │ │ │
1598 // ├────────────────┘ └────────────────┤
1599 // │ │
1600 // │ 4 │
1601 // │ │
1602 // │ │
1603 // ├────────────────┐ ┌────────────────┤
1604 // │ 2 │ │ 2 │
1605 // ├────────────────┴─┐ ┌─┴────────────────┤
1606 // └───────3──────────┴─────────────────────────────────┴──────────3───────┘
1607 //
1608 // 1. Vertical expando-mode, right (default) or left
1609 // 2. Vertical mini-mode, right-top left-top left-bottom or right-bottom
1610 // 3. Horizontal mini-mode, right-top left-top left-bottom or right-bottom
1611 // 4. Horizontal expando-mode top or bottom
1612
1613 void
CalculateRegions()1614 TDragRegion::CalculateRegions()
1615 {
1616 const BRect screenFrame((BScreen(Window())).Frame());
1617
1618 float menuBarHeight = fBarView->BarMenuBar()->Frame().Height();
1619 float hDivider = floorf(screenFrame.Width() / 4);
1620 float halfScreen = floorf(screenFrame.Height() / 2);
1621
1622 // corners
1623 fTopLeftVertical.Set(BRect(screenFrame.left,
1624 screenFrame.top + menuBarHeight, screenFrame.left + hDivider,
1625 screenFrame.top + floorf(menuBarHeight * kVerticalMiniMultiplier)));
1626 fTopRightVertical.Set(BRect(screenFrame.right - hDivider,
1627 screenFrame.top + menuBarHeight, screenFrame.right,
1628 screenFrame.top + floorf(menuBarHeight * kVerticalMiniMultiplier)));
1629 fBottomLeftVertical.Set(BRect(screenFrame.left,
1630 screenFrame.bottom - floorf(menuBarHeight * kVerticalMiniMultiplier),
1631 screenFrame.left + hDivider, screenFrame.bottom - menuBarHeight));
1632 fBottomRightVertical.Set(BRect(screenFrame.right - hDivider,
1633 screenFrame.bottom - floorf(menuBarHeight * kVerticalMiniMultiplier),
1634 screenFrame.right, screenFrame.bottom - menuBarHeight));
1635
1636 fTopLeftHorizontal.Set(BRect(screenFrame.left, screenFrame.top,
1637 screenFrame.left + hDivider, screenFrame.top + menuBarHeight));
1638 fTopRightHorizontal.Set(BRect(screenFrame.right - hDivider, screenFrame.top,
1639 screenFrame.right, screenFrame.top + menuBarHeight));
1640 fBottomLeftHorizontal.Set(BRect(screenFrame.left, screenFrame.bottom - menuBarHeight,
1641 screenFrame.left + hDivider, screenFrame.bottom));
1642 fBottomRightHorizontal.Set(BRect(screenFrame.right - hDivider,
1643 screenFrame.bottom - menuBarHeight, screenFrame.right,
1644 screenFrame.bottom));
1645
1646 // left/right expando
1647 fMiddleLeft.Set(BRect(screenFrame.left, screenFrame.top,
1648 screenFrame.left + hDivider, screenFrame.bottom));
1649 fMiddleLeft.Exclude(&fTopLeftHorizontal);
1650 fMiddleLeft.Exclude(&fBottomLeftHorizontal);
1651 fMiddleLeft.Exclude(&fTopLeftVertical);
1652 fMiddleLeft.Exclude(&fBottomLeftVertical);
1653
1654 fMiddleRight.Set(BRect(screenFrame.right - hDivider,
1655 screenFrame.top, screenFrame.right, screenFrame.bottom));
1656 fMiddleRight.Exclude(&fTopRightHorizontal);
1657 fMiddleRight.Exclude(&fBottomRightHorizontal);
1658 fMiddleRight.Exclude(&fTopRightVertical);
1659 fMiddleRight.Exclude(&fBottomRightVertical);
1660
1661 #ifdef FULL_MODE
1662 // left/right full
1663 fLeftSide.Set(BRect(screenFrame.left, screenFrame.bottom - halfScreen,
1664 screenFrame.left + hDivider, screenFrame.bottom));
1665 fLeftSide.Exclude(&fBottomLeftHorizontal);
1666 fLeftSide.Exclude(&fBottomLeftVertical);
1667 fMiddleLeft.Exclude(&fLeftSide);
1668
1669 fRightSide.Set(BRect(screenFrame.right - hDivider,
1670 screenFrame.bottom - halfScreen, screenFrame.right,
1671 screenFrame.bottom));
1672 fRightSide.Exclude(&fBottomRightHorizontal);
1673 fRightSide.Exclude(&fBottomRightVertical);
1674 fMiddleRight.Exclude(&fRightSide);
1675 #endif
1676
1677 // top/bottom
1678 BRect leftSideRect(screenFrame.left, screenFrame.top,
1679 screenFrame.left + hDivider, screenFrame.bottom);
1680 BRect rightSideRect(screenFrame.right - hDivider, screenFrame.top,
1681 screenFrame.right, screenFrame.bottom);
1682
1683 fTopHalf.Set(BRect(screenFrame.left, screenFrame.top, screenFrame.right,
1684 screenFrame.top + halfScreen));
1685 fTopHalf.Exclude(leftSideRect);
1686 fTopHalf.Exclude(rightSideRect);
1687
1688 fBottomHalf.Set(BRect(screenFrame.left, screenFrame.bottom - halfScreen,
1689 screenFrame.right, screenFrame.bottom));
1690 fBottomHalf.Exclude(leftSideRect);
1691 fBottomHalf.Exclude(rightSideRect);
1692 }
1693
1694
1695 void
MouseMoved(BPoint where,uint32 transit,const BMessage * dragMessage)1696 TDragRegion::MouseMoved(BPoint where, uint32 transit,
1697 const BMessage* dragMessage)
1698 {
1699 if (!IsTracking() || where == fPreviousPosition)
1700 return BControl::MouseMoved(where, transit, dragMessage);
1701
1702 fPreviousPosition = where;
1703
1704 // TODO: can't trust the passed in where param, get screen_where from
1705 // Window()->CurrentMessage() instead, why is this necessary?
1706 BPoint whereScreen;
1707 BMessage* currentMessage = Window()->CurrentMessage();
1708 if (currentMessage == NULL || currentMessage->FindPoint("screen_where",
1709 &whereScreen) != B_OK) {
1710 whereScreen = ConvertToScreen(where);
1711 }
1712
1713 // use short circuit evaluation for convenience
1714 if (// vertical mini
1715 SwitchModeForRegion(whereScreen, fTopLeftVertical, true,
1716 true, true, kMiniState)
1717 || SwitchModeForRegion(whereScreen, fTopRightVertical, true,
1718 false, true, kMiniState)
1719 || SwitchModeForRegion(whereScreen, fBottomLeftVertical, true,
1720 true, false, kMiniState)
1721 || SwitchModeForRegion(whereScreen, fBottomRightVertical, true,
1722 false, false, kMiniState)
1723 // horizontal mini
1724 || SwitchModeForRegion(whereScreen, fTopLeftHorizontal, false,
1725 true, true, kMiniState)
1726 || SwitchModeForRegion(whereScreen, fTopRightHorizontal, false,
1727 false, true, kMiniState)
1728 || SwitchModeForRegion(whereScreen, fBottomLeftHorizontal, false,
1729 true, false, kMiniState)
1730 || SwitchModeForRegion(whereScreen, fBottomRightHorizontal, false,
1731 false, false, kMiniState)
1732 // expando
1733 || SwitchModeForRegion(whereScreen, fMiddleLeft, true, true, true,
1734 kExpandoState)
1735 || SwitchModeForRegion(whereScreen, fMiddleRight, true, false, true,
1736 kExpandoState)
1737 #ifdef FULL_MODE
1738 // full
1739 || SwitchModeForRegion(whereScreen, fLeftSide, true, true, true,
1740 kFullState)
1741 || SwitchModeForRegion(whereScreen, fRightSide, true, false, true,
1742 kFullState)
1743 #endif
1744 // horizontal
1745 || SwitchModeForRegion(whereScreen, fTopHalf, false, true, true,
1746 kExpandoState)
1747 || SwitchModeForRegion(whereScreen, fBottomHalf, false, true, false,
1748 kExpandoState)
1749 );
1750 }
1751
1752
1753 int32
DragRegionLocation() const1754 TDragRegion::DragRegionLocation() const
1755 {
1756 return fDragLocation;
1757 }
1758
1759
1760 void
SetDragRegionLocation(int32 location)1761 TDragRegion::SetDragRegionLocation(int32 location)
1762 {
1763 if (location == fDragLocation)
1764 return;
1765
1766 fDragLocation = location;
1767 Invalidate();
1768 }
1769
1770
1771 // #pragma mark - TResizeControl
1772
1773
1774 /*! Draggable region that is asynchronous so that resizing does not block.
1775 */
TResizeControl(TBarView * barView)1776 TResizeControl::TResizeControl(TBarView* barView)
1777 :
1778 BControl(BRect(0, gDragWidth, 0, kMenuBarHeight), "", "", NULL,
1779 B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS),
1780 fBarView(barView)
1781 {
1782 }
1783
1784
~TResizeControl()1785 TResizeControl::~TResizeControl()
1786 {
1787 }
1788
1789
1790 void
AttachedToWindow()1791 TResizeControl::AttachedToWindow()
1792 {
1793 BView::AttachedToWindow();
1794
1795 if (be_control_look != NULL)
1796 SetViewUIColor(B_MENU_BACKGROUND_COLOR, 1.1);
1797 else
1798 SetViewUIColor(B_MENU_BACKGROUND_COLOR);
1799 }
1800
1801
1802 void
Draw(BRect updateRect)1803 TResizeControl::Draw(BRect updateRect)
1804 {
1805 if (!fBarView->Vertical())
1806 return;
1807
1808 BRect dragRegion(Bounds());
1809
1810 int32 height = dragRegion.IntegerHeight();
1811 if (height <= 0)
1812 return;
1813
1814 rgb_color menuColor = ViewColor();
1815 rgb_color menuHilite = menuColor;
1816 if (IsTracking()) {
1817 // draw drag region highlighted if tracking mouse
1818 menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT);
1819 SetHighColor(menuHilite);
1820 FillRect(dragRegion);
1821 } else {
1822 SetHighColor(menuColor);
1823 FillRect(dragRegion);
1824 }
1825
1826 rgb_color vdark = tint_color(menuHilite, B_DARKEN_3_TINT);
1827 rgb_color light = tint_color(menuHilite, B_LIGHTEN_2_TINT);
1828
1829 BeginLineArray(height);
1830 BPoint where;
1831 where.x = floorf((dragRegion.left + dragRegion.right) / 2 + 0.5) - 1;
1832 where.y = dragRegion.top + 2;
1833
1834 while (where.y + 1 <= dragRegion.bottom) {
1835 AddLine(where, where, vdark);
1836 AddLine(where + BPoint(1, 1), where + BPoint(1, 1), light);
1837
1838 where.y += 3;
1839 }
1840 EndLineArray();
1841 }
1842
1843
1844 void
MouseDown(BPoint where)1845 TResizeControl::MouseDown(BPoint where)
1846 {
1847 uint32 buttons;
1848 BPoint mouseLoc;
1849
1850 while (true) {
1851 GetMouse(&mouseLoc, &buttons);
1852 if (buttons == 0)
1853 break;
1854
1855 if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) {
1856 SetTracking(true);
1857 SetMouseEventMask(B_POINTER_EVENTS,
1858 B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
1859 Invalidate();
1860 break;
1861 }
1862
1863 snooze(25000);
1864 }
1865 }
1866
1867
1868 void
MouseUp(BPoint where)1869 TResizeControl::MouseUp(BPoint where)
1870 {
1871 if (IsTracking()) {
1872 SetTracking(false);
1873 Invalidate();
1874 } else
1875 BControl::MouseUp(where);
1876 }
1877
1878
1879 void
MouseMoved(BPoint where,uint32 code,const BMessage * dragMessage)1880 TResizeControl::MouseMoved(BPoint where, uint32 code,
1881 const BMessage* dragMessage)
1882 {
1883 if (!fBarView->Vertical() || !IsResizing())
1884 return BControl::MouseMoved(where, code, dragMessage);
1885
1886 float windowWidth = Window()->Frame().Width();
1887 float delta = 0;
1888 BPoint whereScreen = ConvertToScreen(where);
1889
1890 if (fBarView->Left()) {
1891 delta = whereScreen.x - Window()->Frame().right;
1892 if (delta > 0 && windowWidth >= gMaximumWindowWidth)
1893 ; // do nothing
1894 else if (delta < 0 && windowWidth <= gMinimumWindowWidth)
1895 ; // do nothing
1896 else
1897 Window()->ResizeBy(delta, 0);
1898 } else {
1899 delta = Window()->Frame().left - whereScreen.x;
1900 if (delta > 0 && windowWidth >= gMaximumWindowWidth)
1901 ; // do nothing
1902 else if (delta < 0 && windowWidth <= gMinimumWindowWidth)
1903 ; // do nothing
1904 else {
1905 Window()->MoveBy(delta, 0);
1906 Window()->ResizeBy(delta, 0);
1907 }
1908 }
1909
1910 windowWidth = Window()->Frame().Width();
1911
1912 BControl::MouseMoved(where, code, dragMessage);
1913 }
1914