BPicture: Fix archive constructor.
[haiku.git] / src / apps / deskbar / StatusView.cpp
blobdde88ed968cda9d9af4db2c65518de37a18ff800
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
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.
37 #include "StatusView.h"
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <algorithm>
46 #include <fs_index.h>
47 #include <fs_info.h>
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>
69 #include "icons.h"
71 #include "BarApp.h"
72 #include "DeskbarUtils.h"
73 #include "ResourceSet.h"
74 #include "StatusViewShelf.h"
75 #include "TimeView.h"
78 using std::max;
80 #ifdef DB_ADDONS
81 // Add-on support
83 // Item - internal item list (node, eref, etc)
84 // Icon - physical replicant handed to the DeskbarClass class
85 // AddOn - attribute based add-on
87 const char* const kInstantiateItemCFunctionName = "instantiate_deskbar_item";
88 const char* const kInstantiateEntryCFunctionName = "instantiate_deskbar_entry";
89 const char* const kReplicantSettingsFile = "replicants";
90 const char* const kReplicantPathField = "replicant_path";
92 float gMinimumWindowWidth = kGutter + kMinimumTrayWidth + kDragRegionWidth;
95 static void
96 DumpItem(DeskbarItemInfo* item)
98 printf("is addon: %i, id: %" B_PRId32 "\n", item->isAddOn, item->id);
99 printf("entry_ref: %" B_PRIdDEV ", %" B_PRIdINO ", %s\n",
100 item->entryRef.device, item->entryRef.directory, item->entryRef.name);
101 printf("node_ref: %" B_PRIdDEV ", %" B_PRIdINO "\n", item->nodeRef.device,
102 item->nodeRef.node);
106 static void
107 DumpList(BList* itemlist)
109 int32 count = itemlist->CountItems() - 1;
110 if (count < 0) {
111 printf("no items in list\n");
112 return;
114 for (int32 i = count; i >= 0; i--) {
115 DeskbarItemInfo* item = (DeskbarItemInfo*)itemlist->ItemAt(i);
116 if (!item)
117 continue;
119 DumpItem(item);
122 #endif /* DB_ADDONS */
125 #undef B_TRANSLATION_CONTEXT
126 #define B_TRANSLATION_CONTEXT "Tray"
128 // don't change the name of this view to anything other than "Status"!
130 TReplicantTray::TReplicantTray(TBarView* parent, bool vertical)
132 BView(BRect(0, 0, 1, 1), "Status", B_FOLLOW_LEFT | B_FOLLOW_TOP,
133 B_WILL_DRAW | B_FRAME_EVENTS),
134 fTime(NULL),
135 fBarView(parent),
136 fShelf(new TReplicantShelf(this)),
137 fMultiRowMode(vertical),
138 fMinimumTrayWidth(kMinimumTrayWidth),
139 fAlignmentSupport(false)
141 // init the minimum window width according to the logo.
142 const BBitmap* logoBitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE,
143 R_LeafLogoBitmap);
144 if (logoBitmap != NULL) {
145 gMinimumWindowWidth = std::max(gMinimumWindowWidth,
146 2 * (logoBitmap->Bounds().Width() + 8));
147 fMinimumTrayWidth = gMinimumWindowWidth - kGutter - kDragRegionWidth;
150 // Create the time view
151 fTime = new TTimeView(fMinimumTrayWidth, kMaxReplicantHeight - 1.0);
155 TReplicantTray::~TReplicantTray()
157 delete fShelf;
158 delete fTime;
162 void
163 TReplicantTray::AttachedToWindow()
165 BView::AttachedToWindow();
167 if (be_control_look != NULL) {
168 SetViewColor(Parent()->ViewColor());
169 } else {
170 SetViewColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
171 B_DARKEN_1_TINT));
173 SetDrawingMode(B_OP_COPY);
175 Window()->SetPulseRate(1000000);
177 clock_settings* clock = ((TBarApp*)be_app)->ClockSettings();
178 fTime->SetShowSeconds(clock->showSeconds);
179 fTime->SetShowDayOfWeek(clock->showDayOfWeek);
180 fTime->SetShowTimeZone(clock->showTimeZone);
182 AddChild(fTime);
183 fTime->MoveTo(Bounds().right - fTime->Bounds().Width() - 1, 2);
185 if (!((TBarApp*)be_app)->Settings()->showClock)
186 fTime->Hide();
188 #ifdef DB_ADDONS
189 // load addons and rehydrate archives
190 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
191 InitAddOnSupport();
192 #endif
193 #endif
194 ResizeToPreferred();
198 void
199 TReplicantTray::DetachedFromWindow()
201 #ifdef DB_ADDONS
202 // clean up add-on support
203 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
204 DeleteAddOnSupport();
205 #endif
206 #endif
207 BView::DetachedFromWindow();
211 /*! Width is set to a minimum of kMinimumReplicantCount by kMaxReplicantWidth
212 if not in multirowmode and greater than kMinimumReplicantCount
213 the width should be calculated based on the actual replicant widths
215 void
216 TReplicantTray::GetPreferredSize(float* preferredWidth, float* preferredHeight)
218 float width = 0, height = kMinimumTrayHeight;
220 if (fMultiRowMode) {
221 if (fShelf->CountReplicants() > 0)
222 height = fRightBottomReplicant.bottom;
224 // the height will be uniform for the number of rows necessary to show
225 // all the reps + any gutters necessary for spacing
226 int32 rowCount = (int32)(height / kMaxReplicantHeight);
227 height = kGutter + (rowCount * kMaxReplicantHeight)
228 + ((rowCount - 1) * kIconGap) + kGutter;
229 height = max(kMinimumTrayHeight, height);
230 width = fMinimumTrayWidth;
231 } else {
232 // if last replicant overruns clock then resize to accomodate
233 if (fShelf->CountReplicants() > 0) {
234 if (!fTime->IsHidden() && fTime->Frame().left
235 < fRightBottomReplicant.right + 6) {
236 width = fRightBottomReplicant.right + 6
237 + fTime->Frame().Width();
238 } else
239 width = fRightBottomReplicant.right + 3;
242 // this view has a fixed minimum width
243 width = max(fMinimumTrayWidth, width);
244 height = kGutter + static_cast<TBarApp*>(be_app)->IconSize() + kGutter;
247 *preferredWidth = width;
248 // add 1 for the border
249 *preferredHeight = height + 1;
253 void
254 TReplicantTray::AdjustPlacement()
256 // called when an add-on has been added or removed
257 // need to resize the parent of this accordingly
259 BRect bounds = Bounds();
260 float width, height;
261 GetPreferredSize(&width, &height);
263 if (width == bounds.Width() && height == bounds.Height()) {
264 // no need to change anything
265 return;
268 Parent()->ResizeToPreferred();
269 fBarView->UpdatePlacement();
270 Parent()->Invalidate();
271 Invalidate();
275 void
276 TReplicantTray::MessageReceived(BMessage* message)
278 switch (message->what) {
279 case B_LOCALE_CHANGED:
281 if (fTime == NULL)
282 return;
284 fTime->Update();
286 // time string reformat -> realign
287 RealignReplicants();
288 AdjustPlacement();
289 break;
292 case kShowHideTime:
293 // from context menu in clock and in this view
294 ShowHideTime();
295 break;
297 case kShowSeconds:
298 if (fTime == NULL)
299 return;
301 fTime->SetShowSeconds(!fTime->ShowSeconds());
303 // time string reformat -> realign
304 RealignReplicants();
305 AdjustPlacement();
306 break;
308 case kShowDayOfWeek:
309 if (fTime == NULL)
310 return;
312 fTime->SetShowDayOfWeek(!fTime->ShowDayOfWeek());
314 // time string reformat -> realign
315 RealignReplicants();
316 AdjustPlacement();
317 break;
319 case kShowTimeZone:
320 if (fTime == NULL)
321 return;
323 fTime->SetShowTimeZone(!fTime->ShowTimeZone());
325 // time string reformat -> realign
326 RealignReplicants();
327 AdjustPlacement();
328 break;
330 case kGetClockSettings:
332 if (fTime == NULL)
333 return;
335 bool showClock = !fTime->IsHidden();
336 bool showSeconds = fTime->ShowSeconds();
337 bool showDayOfWeek = fTime->ShowDayOfWeek();
338 bool showTimeZone = fTime->ShowTimeZone();
340 BMessage reply(kGetClockSettings);
341 reply.AddBool("showClock", showClock);
342 reply.AddBool("showSeconds", showSeconds);
343 reply.AddBool("showDayOfWeek", showDayOfWeek);
344 reply.AddBool("showTimeZone", showTimeZone);
345 message->SendReply(&reply);
346 break;
349 #ifdef DB_ADDONS
350 case B_NODE_MONITOR:
351 HandleEntryUpdate(message);
352 break;
353 #endif
355 default:
356 BView::MessageReceived(message);
357 break;
362 void
363 TReplicantTray::MouseDown(BPoint where)
365 #ifdef DB_ADDONS
366 if (modifiers() & B_CONTROL_KEY)
367 DumpList(fItemList);
368 #endif
370 uint32 buttons;
372 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
373 if (buttons == B_SECONDARY_MOUSE_BUTTON) {
374 ShowReplicantMenu(where);
375 } else {
376 BPoint save = where;
377 bigtime_t doubleClickSpeed;
378 bigtime_t start = system_time();
379 uint32 buttons;
381 get_click_speed(&doubleClickSpeed);
383 do {
384 if (fabs(where.x - save.x) > 4 || fabs(where.y - save.y) > 4)
385 // user moved out of bounds of click area
386 break;
388 if ((system_time() - start) > (2 * doubleClickSpeed)) {
389 ShowReplicantMenu(where);
390 break;
393 snooze(50000);
394 GetMouse(&where, &buttons);
395 } while (buttons);
397 BView::MouseDown(where);
401 void
402 TReplicantTray::ShowReplicantMenu(BPoint point)
404 BPopUpMenu* menu = new BPopUpMenu("", false, false);
405 menu->SetFont(be_plain_font);
407 // If clock is visible show the extended menu, otherwise show "Show clock"
409 if (!fTime->IsHidden())
410 fTime->ShowTimeOptions(ConvertToScreen(point));
411 else {
412 BMenuItem* item = new BMenuItem(B_TRANSLATE("Show clock"),
413 new BMessage(kShowHideTime));
414 menu->AddItem(item);
415 menu->SetTargetForItems(this);
416 BPoint where = ConvertToScreen(point);
417 menu->Go(where, true, true, BRect(where - BPoint(4, 4),
418 where + BPoint(4, 4)), true);
423 void
424 TReplicantTray::SetMultiRow(bool state)
426 fMultiRowMode = state;
430 void
431 TReplicantTray::ShowHideTime()
433 if (fTime == NULL)
434 return;
436 // Check from the point of view of fTime because we need to ignore
437 // whether or not the parent window is hidden.
438 if (fTime->IsHidden(fTime))
439 fTime->Show();
440 else
441 fTime->Hide();
443 RealignReplicants();
444 AdjustPlacement();
446 // Check from the point of view of fTime ignoring parent's state.
447 bool showClock = !fTime->IsHidden(fTime);
449 // Update showClock setting that gets saved to disk on quit
450 ((TBarApp*)be_app)->Settings()->showClock = showClock;
452 // Send a message to Time preferences telling it to update
453 BMessenger messenger("application/x-vnd.Haiku-Time");
454 BMessage message(kShowHideTime);
455 message.AddBool("showClock", showClock);
456 messenger.SendMessage(&message);
460 #ifdef DB_ADDONS
463 void
464 TReplicantTray::InitAddOnSupport()
466 // list to maintain refs to each rep added/deleted
467 fItemList = new BList();
468 BPath path;
470 if (GetDeskbarSettingsDirectory(path, true) == B_OK) {
471 path.Append(kReplicantSettingsFile);
473 BFile file(path.Path(), B_READ_ONLY);
474 if (file.InitCheck() == B_OK) {
475 status_t result;
476 BEntry entry;
477 int32 id;
478 BString path;
479 if (fAddOnSettings.Unflatten(&file) == B_OK) {
480 for (int32 i = 0; fAddOnSettings.FindString(kReplicantPathField,
481 i, &path) == B_OK; i++) {
482 if (entry.SetTo(path.String()) == B_OK && entry.Exists()) {
483 result = LoadAddOn(&entry, &id, false);
484 } else
485 result = B_ENTRY_NOT_FOUND;
487 if (result != B_OK) {
488 fAddOnSettings.RemoveData(kReplicantPathField, i);
489 --i;
498 void
499 TReplicantTray::DeleteAddOnSupport()
501 _SaveSettings();
503 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
504 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->RemoveItem(i);
505 if (item) {
506 if (item->isAddOn)
507 watch_node(&(item->nodeRef), B_STOP_WATCHING, this, Window());
509 delete item;
512 delete fItemList;
514 // stop the volume mount/unmount watch
515 stop_watching(this, Window());
519 DeskbarItemInfo*
520 TReplicantTray::DeskbarItemFor(node_ref& nodeRef)
522 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
523 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
524 if (item == NULL)
525 continue;
527 if (item->nodeRef == nodeRef)
528 return item;
531 return NULL;
535 DeskbarItemInfo*
536 TReplicantTray::DeskbarItemFor(int32 id)
538 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
539 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
540 if (item == NULL)
541 continue;
543 if (item->id == id)
544 return item;
547 return NULL;
551 bool
552 TReplicantTray::NodeExists(node_ref& nodeRef)
554 return DeskbarItemFor(nodeRef) != NULL;
558 /*! This handles B_NODE_MONITOR & B_QUERY_UPDATE messages received
559 for the registered add-ons.
561 void
562 TReplicantTray::HandleEntryUpdate(BMessage* message)
564 int32 opcode;
565 if (message->FindInt32("opcode", &opcode) != B_OK)
566 return;
568 BPath path;
569 switch (opcode) {
570 case B_ENTRY_MOVED:
572 entry_ref ref;
573 ino_t todirectory;
574 ino_t node;
575 const char* name;
576 if (message->FindString("name", &name) == B_OK
577 && message->FindInt64("from directory", &(ref.directory))
578 == B_OK
579 && message->FindInt64("to directory", &todirectory) == B_OK
580 && message->FindInt32("device", &(ref.device)) == B_OK
581 && message->FindInt64("node", &node) == B_OK ) {
583 if (!name)
584 break;
586 ref.set_name(name);
587 // change the directory reference to
588 // the new directory
589 MoveItem(&ref, todirectory);
591 break;
594 case B_ENTRY_REMOVED:
596 // entry was rm'd from the device
597 node_ref nodeRef;
598 if (message->FindInt32("device", &(nodeRef.device)) == B_OK
599 && message->FindInt64("node", &(nodeRef.node)) == B_OK) {
600 DeskbarItemInfo* item = DeskbarItemFor(nodeRef);
601 if (item == NULL)
602 break;
604 // If there is a team running where the add-on comes from,
605 // we don't want to remove the icon yet.
606 if (be_roster->IsRunning(&item->entryRef))
607 break;
609 UnloadAddOn(&nodeRef, NULL, true, false);
611 break;
617 /*! The add-ons must support the exported C function API
618 if they do, they will be loaded and added to deskbar
619 primary function is the Instantiate function
621 status_t
622 TReplicantTray::LoadAddOn(BEntry* entry, int32* id, bool addToSettings)
624 if (!entry)
625 return B_ERROR;
627 node_ref nodeRef;
628 entry->GetNodeRef(&nodeRef);
629 // no duplicates
630 if (NodeExists(nodeRef))
631 return B_ERROR;
633 BNode node(entry);
634 BPath path;
635 status_t status = entry->GetPath(&path);
636 if (status < B_OK)
637 return status;
639 // load the add-on
640 image_id image = load_add_on(path.Path());
641 if (image < B_OK)
642 return image;
644 // get the view loading function symbol
645 // we first look for a symbol that takes an image_id
646 // and entry_ref pointer, if not found, go with normal
647 // instantiate function
648 BView* (*entryFunction)(image_id, const entry_ref*);
649 BView* (*itemFunction)(void);
650 BView* view = NULL;
652 entry_ref ref;
653 entry->GetRef(&ref);
655 if (get_image_symbol(image, kInstantiateEntryCFunctionName,
656 B_SYMBOL_TYPE_TEXT, (void**)&entryFunction) >= B_OK) {
657 view = (*entryFunction)(image, &ref);
658 } else if (get_image_symbol(image, kInstantiateItemCFunctionName,
659 B_SYMBOL_TYPE_TEXT, (void**)&itemFunction) >= B_OK) {
660 view = (*itemFunction)();
661 } else {
662 unload_add_on(image);
663 return B_ERROR;
666 if (view == NULL || IconExists(view->Name())) {
667 delete view;
668 unload_add_on(image);
669 return B_ERROR;
672 BMessage* data = new BMessage;
673 view->Archive(data);
674 delete view;
676 // add the rep; adds info to list
677 if (AddIcon(data, id, &ref) != B_OK)
678 delete data;
680 if (addToSettings) {
681 fAddOnSettings.AddString(kReplicantPathField, path.Path());
682 _SaveSettings();
685 return B_OK;
689 status_t
690 TReplicantTray::AddItem(int32 id, node_ref nodeRef, BEntry& entry, bool isAddOn)
692 DeskbarItemInfo* item = new DeskbarItemInfo;
693 if (item == NULL)
694 return B_NO_MEMORY;
696 item->id = id;
697 item->isAddOn = isAddOn;
699 if (entry.GetRef(&item->entryRef) < B_OK) {
700 item->entryRef.device = -1;
701 item->entryRef.directory = -1;
702 item->entryRef.name = NULL;
704 item->nodeRef = nodeRef;
706 fItemList->AddItem(item);
708 if (isAddOn)
709 watch_node(&nodeRef, B_WATCH_NAME | B_WATCH_ATTR, this, Window());
711 return B_OK;
715 /** from entry_removed message, when attribute removed
716 * or when a device is unmounted (use removeall, by device)
719 void
720 TReplicantTray::UnloadAddOn(node_ref* nodeRef, dev_t* device,
721 bool which, bool removeAll)
723 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
724 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
725 if (!item)
726 continue;
728 if ((which && nodeRef && item->nodeRef == *nodeRef)
729 || (device && item->nodeRef.device == *device)) {
731 if (device && be_roster->IsRunning(&item->entryRef))
732 continue;
734 RemoveIcon(item->id);
736 if (!removeAll)
737 break;
743 void
744 TReplicantTray::RemoveItem(int32 id)
746 DeskbarItemInfo* item = DeskbarItemFor(id);
747 if (item == NULL)
748 return;
750 // attribute was added via Deskbar API (AddItem(entry_ref*, int32*)
751 if (item->isAddOn) {
752 BPath path(&item->entryRef);
753 BString storedPath;
754 for (int32 i = 0;
755 fAddOnSettings.FindString(kReplicantPathField, i, &storedPath)
756 == B_OK; i++) {
757 if (storedPath == path.Path()) {
758 fAddOnSettings.RemoveData(kReplicantPathField, i);
759 break;
762 _SaveSettings();
764 BNode node(&item->entryRef);
765 watch_node(&item->nodeRef, B_STOP_WATCHING, this, Window());
768 fItemList->RemoveItem(item);
769 delete item;
773 /** ENTRY_MOVED message, moving only occurs on a device
774 * copying will occur (ENTRY_CREATED) between devices
777 void
778 TReplicantTray::MoveItem(entry_ref* ref, ino_t toDirectory)
780 if (!ref)
781 return;
783 // scan for a matching entry_ref and update it
785 // don't need to change node info as it does not change
787 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
788 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
789 if (!item)
790 continue;
792 if (!strcmp(item->entryRef.name, ref->name)
793 && item->entryRef.device == ref->device
794 && item->entryRef.directory == ref->directory) {
795 item->entryRef.directory = toDirectory;
796 break;
801 #endif // add-on support
803 // external add-on API routines
804 // called using the new BDeskbar class
806 // existence of icon/replicant by name or ID
807 // returns opposite
808 // note: name and id are semi-private limiting
809 // the ability of non-host apps to remove
810 // icons without a little bit of work
812 /** for a specific id
813 * return the name of the replicant (name of view)
816 status_t
817 TReplicantTray::ItemInfo(int32 id, const char** name)
819 if (id < 0)
820 return B_ERROR;
822 int32 index, temp;
823 BView* view = ViewAt(&index, &temp, id, false);
824 if (view) {
825 *name = view->Name();
826 return B_OK;
829 return B_ERROR;
833 /** for a specific name
834 * return the id (internal to Deskbar)
837 status_t
838 TReplicantTray::ItemInfo(const char* name, int32* id)
840 if (!name || strlen(name) <= 0)
841 return B_ERROR;
843 int32 index;
844 BView* view = ViewAt(&index, id, name);
845 if (view)
846 return B_OK;
848 return B_ERROR;
852 /** at a specific index
853 * return both the name and the id of the replicant
856 status_t
857 TReplicantTray::ItemInfo(int32 index, const char** name, int32* id)
859 if (index < 0)
860 return B_ERROR;
862 BView* view;
863 fShelf->ReplicantAt(index, &view, (uint32*)id, NULL);
864 if (view) {
865 *name = view->Name();
866 return B_OK;
869 return B_ERROR;
873 /** replicant exists, by id/index */
875 bool
876 TReplicantTray::IconExists(int32 target, bool byIndex)
878 int32 index, id;
879 BView* view = ViewAt(&index, &id, target, byIndex);
881 return view && index >= 0;
885 /** replicant exists, by name */
887 bool
888 TReplicantTray::IconExists(const char* name)
890 if (!name || strlen(name) == 0)
891 return false;
893 int32 index, id;
894 BView* view = ViewAt(&index, &id, name);
896 return view && index >= 0;
900 int32
901 TReplicantTray::IconCount() const
903 return fShelf->CountReplicants();
907 /*! Message must contain an archivable view for later rehydration.
908 This function takes over ownership of the provided message on success
909 only.
910 Returns the current replicant ID.
912 status_t
913 TReplicantTray::AddIcon(BMessage* archive, int32* id, const entry_ref* addOn)
915 if (archive == NULL || id == NULL)
916 return B_ERROR;
918 // find entry_ref
920 entry_ref ref;
921 if (addOn) {
922 // Use it if we got it
923 ref = *addOn;
924 } else {
925 const char* signature;
927 status_t status = archive->FindString("add_on", &signature);
928 if (status == B_OK) {
929 BRoster roster;
930 status = roster.FindApp(signature, &ref);
932 if (status < B_OK)
933 return status;
936 BFile file;
937 status_t status = file.SetTo(&ref, B_READ_ONLY);
938 if (status < B_OK)
939 return status;
941 node_ref nodeRef;
942 status = file.GetNodeRef(&nodeRef);
943 if (status < B_OK)
944 return status;
946 BEntry entry(&ref, true);
947 // TODO: this resolves an eventual link for the item being added - this
948 // is okay for now, but in multi-user environments, one might want to
949 // have links that carry the be:deskbar_item_status attribute
950 status = entry.InitCheck();
951 if (status != B_OK)
952 return status;
954 *id = 999;
955 if (archive->what == B_ARCHIVED_OBJECT)
956 archive->what = 0;
958 BRect originalBounds = archive->FindRect("_frame");
959 // this is a work-around for buggy replicants that change their size in
960 // AttachedToWindow() (such as "SVM")
962 // TODO: check for name collisions?
963 status = fShelf->AddReplicant(archive, BPoint(1, 1));
964 if (status != B_OK)
965 return status;
967 int32 count = fShelf->CountReplicants();
968 BView* view;
969 fShelf->ReplicantAt(count - 1, &view, (uint32*)id, NULL);
971 if (originalBounds != view->Bounds()) {
972 // The replicant changed its size when added to the window, so we need
973 // to recompute all over again (it's already done once via
974 // BShelf::AddReplicant() and TReplicantShelf::CanAcceptReplicantView())
975 RealignReplicants();
978 float oldWidth = Bounds().Width();
979 float oldHeight = Bounds().Height();
980 float width, height;
981 GetPreferredSize(&width, &height);
982 if (oldWidth != width || oldHeight != height)
983 AdjustPlacement();
985 // add the item to the add-on list
987 AddItem(*id, nodeRef, entry, addOn != NULL);
988 return B_OK;
992 void
993 TReplicantTray::RemoveIcon(int32 target, bool byIndex)
995 if (target < 0)
996 return;
998 int32 index, id;
999 BView* view = ViewAt(&index, &id, target, byIndex);
1000 if (view && index >= 0) {
1001 // remove the reference from the item list & the shelf
1002 RemoveItem(id);
1003 fShelf->DeleteReplicant(index);
1005 // force a placement update, !! need to fix BShelf
1006 RealReplicantAdjustment(index);
1011 void
1012 TReplicantTray::RemoveIcon(const char* name)
1014 if (!name || strlen(name) <= 0)
1015 return;
1017 int32 id, index;
1018 BView* view = ViewAt(&index, &id, name);
1019 if (view && index >= 0) {
1020 // remove the reference from the item list & shelf
1021 RemoveItem(id);
1022 fShelf->DeleteReplicant(index);
1024 // force a placement update, !! need to fix BShelf
1025 RealReplicantAdjustment(index);
1030 void
1031 TReplicantTray::RealReplicantAdjustment(int32 startIndex)
1033 if (startIndex < 0)
1034 return;
1036 if (startIndex == fLastReplicant)
1037 startIndex = 0;
1039 // reset the locations of all replicants after the one deleted
1040 RealignReplicants(startIndex);
1042 float oldWidth = Bounds().Width();
1043 float oldHeight = Bounds().Height();
1044 float width, height;
1045 GetPreferredSize(&width, &height);
1046 if (oldWidth != width || oldHeight != height) {
1047 // resize view to accomodate the replicants, redraw as necessary
1048 AdjustPlacement();
1053 /** looking for a replicant by id/index
1054 * return the view and index
1057 BView*
1058 TReplicantTray::ViewAt(int32* index, int32* id, int32 target, bool byIndex)
1060 *index = -1;
1062 BView* view;
1063 if (byIndex) {
1064 if (fShelf->ReplicantAt(target, &view, (uint32*)id)) {
1065 if (view) {
1066 *index = target;
1067 return view;
1070 } else {
1071 int32 count = fShelf->CountReplicants() - 1;
1072 int32 localid;
1073 for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) {
1074 fShelf->ReplicantAt(repIndex, &view, (uint32*)&localid);
1075 if (localid == target && view) {
1076 *index = repIndex;
1077 *id = localid;
1078 return view;
1082 return NULL;
1086 /** looking for a replicant with a view by name
1087 * return the view, index and the id of the replicant
1090 BView*
1091 TReplicantTray::ViewAt(int32* index, int32* id, const char* name)
1093 *index = -1;
1094 *id = -1;
1096 BView* view;
1097 int32 count = fShelf->CountReplicants()-1;
1098 for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) {
1099 fShelf->ReplicantAt(repIndex, &view, (uint32*)id);
1100 if (view && view->Name() && strcmp(name, view->Name()) == 0) {
1101 *index = repIndex;
1102 return view;
1105 return NULL;
1109 /** Shelf will call to determine where and if
1110 * the replicant is to be added
1113 bool
1114 TReplicantTray::AcceptAddon(BRect replicantFrame, BMessage* message)
1116 if (!message)
1117 return false;
1119 if (replicantFrame.Height() > kMaxReplicantHeight)
1120 return false;
1122 alignment align = B_ALIGN_LEFT;
1123 if (fAlignmentSupport && message->HasBool("deskbar:dynamic_align")) {
1124 if (!fBarView->Vertical())
1125 align = B_ALIGN_RIGHT;
1126 else
1127 align = fBarView->Left() ? B_ALIGN_LEFT : B_ALIGN_RIGHT;
1128 } else if (message->HasInt32("deskbar:align"))
1129 message->FindInt32("deskbar:align", (int32*)&align);
1131 if (message->HasInt32("deskbar:private_align"))
1132 message->FindInt32("deskbar:private_align", (int32*)&align);
1133 else
1134 align = B_ALIGN_LEFT;
1136 BPoint loc = LocationForReplicant(fShelf->CountReplicants(),
1137 replicantFrame.Width());
1139 message->AddPoint("_pjp_loc", loc);
1140 return true;
1144 /** based on the previous (index - 1) replicant in the list
1145 * calculate where the left point should be for this
1146 * replicant. replicant will flow to the right on its own
1149 BPoint
1150 TReplicantTray::LocationForReplicant(int32 index, float width)
1152 BPoint loc(kIconGap + 1, kGutter + 1);
1154 if (fMultiRowMode) {
1155 // try to find free space in every row
1156 for (int32 row = 0; ; loc.y += kMaxReplicantHeight + kIconGap, row++) {
1157 // determine free space in this row
1158 BRect rect(loc.x, loc.y, loc.x + fMinimumTrayWidth - kIconGap
1159 - 2.0, loc.y + kMaxReplicantHeight);
1160 if (row == 0 && !fTime->IsHidden())
1161 rect.right -= fTime->Frame().Width() + kIconGap;
1163 for (int32 i = 0; i < index; i++) {
1164 BView* view = NULL;
1165 fShelf->ReplicantAt(i, &view);
1166 if (view == NULL || view->Frame().top != rect.top)
1167 continue;
1169 rect.left = view->Frame().right + kIconGap + 1;
1172 if (rect.Width() >= width) {
1173 // the icon fits in this row
1174 loc = rect.LeftTop();
1175 break;
1178 } else {
1179 if (index > 0) {
1180 // get the last replicant added for placement reference
1181 BView* view = NULL;
1182 fShelf->ReplicantAt(index - 1, &view);
1183 if (view) {
1184 // push this rep placement past the last one
1185 loc.x = view->Frame().right + kIconGap + 1;
1186 loc.y = view->Frame().top;
1191 if ((loc.y == fRightBottomReplicant.top && loc.x
1192 > fRightBottomReplicant.left) || loc.y > fRightBottomReplicant.top) {
1193 fRightBottomReplicant.Set(loc.x, loc.y, loc.x + width, loc.y
1194 + kMaxReplicantHeight);
1195 fLastReplicant = index;
1198 return loc;
1202 BRect
1203 TReplicantTray::IconFrame(int32 target, bool byIndex)
1205 int32 index, id;
1206 BView* view = ViewAt(&index, &id, target, byIndex);
1207 if (view)
1208 return view->Frame();
1210 return BRect(0, 0, 0, 0);
1214 BRect
1215 TReplicantTray::IconFrame(const char* name)
1217 if (!name)
1218 return BRect(0, 0, 0, 0);
1220 int32 id, index;
1221 BView* view = ViewAt(&index, &id, name);
1222 if (view)
1223 return view->Frame();
1225 return BRect(0, 0, 0, 0);
1229 /** Scan from the startIndex and reset the location
1230 * as defined in LocationForReplicant()
1233 void
1234 TReplicantTray::RealignReplicants(int32 startIndex)
1236 if (startIndex < 0)
1237 startIndex = 0;
1239 int32 count = fShelf->CountReplicants();
1240 if (count <= 0)
1241 return;
1243 if (startIndex == 0)
1244 fRightBottomReplicant.Set(0, 0, 0, 0);
1246 BView* view = NULL;
1247 for (int32 i = startIndex ; i < count ; i++) {
1248 fShelf->ReplicantAt(i, &view);
1249 if (view != NULL) {
1250 BPoint loc = LocationForReplicant(i, view->Frame().Width());
1251 if (view->Frame().LeftTop() != loc)
1252 view->MoveTo(loc);
1258 status_t
1259 TReplicantTray::_SaveSettings()
1261 status_t result;
1262 BPath path;
1263 if ((result = GetDeskbarSettingsDirectory(path, true)) == B_OK) {
1264 path.Append(kReplicantSettingsFile);
1266 BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1267 if ((result = file.InitCheck()) == B_OK)
1268 result = fAddOnSettings.Flatten(&file);
1271 return result;
1275 void
1276 TReplicantTray::SaveTimeSettings()
1278 if (fTime == NULL)
1279 return;
1281 clock_settings* settings = ((TBarApp*)be_app)->ClockSettings();
1282 settings->showSeconds = fTime->ShowSeconds();
1283 settings->showDayOfWeek = fTime->ShowDayOfWeek();
1284 settings->showTimeZone = fTime->ShowTimeZone();
1288 // #pragma mark -
1291 /*! Draggable region that is asynchronous so that dragging does not block
1292 other activities.
1294 TDragRegion::TDragRegion(TBarView* parent, BView* child)
1296 BControl(BRect(0, 0, 0, 0), "", "", NULL, B_FOLLOW_NONE,
1297 B_WILL_DRAW | B_FRAME_EVENTS),
1298 fBarView(parent),
1299 fChild(child),
1300 fDragLocation(kAutoPlaceDragRegion)
1305 void
1306 TDragRegion::AttachedToWindow()
1308 BView::AttachedToWindow();
1309 if (be_control_look != NULL)
1310 SetViewColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 1.1));
1311 else
1312 SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
1313 ResizeToPreferred();
1317 void
1318 TDragRegion::GetPreferredSize(float* width, float* height)
1320 fChild->ResizeToPreferred();
1321 *width = fChild->Bounds().Width();
1322 *height = fChild->Bounds().Height();
1324 if (fDragLocation != kNoDragRegion)
1325 *width += 7;
1326 else
1327 *width += 6;
1329 *height += 3;
1333 void
1334 TDragRegion::FrameMoved(BPoint)
1336 if (fBarView->Left() && fBarView->Vertical()
1337 && fDragLocation != kNoDragRegion)
1338 fChild->MoveTo(5, 2);
1339 else
1340 fChild->MoveTo(2, 2);
1344 void
1345 TDragRegion::Draw(BRect)
1347 rgb_color menuColor = ViewColor();
1348 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
1349 rgb_color ldark = tint_color(menuColor, 1.02);
1350 rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT);
1351 rgb_color vvdark = tint_color(menuColor, B_DARKEN_4_TINT);
1352 rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT);
1354 BRect frame(Bounds());
1355 BeginLineArray(4);
1357 if (be_control_look != NULL) {
1358 if (fBarView->Vertical()) {
1359 AddLine(frame.LeftTop(), frame.RightTop(), dark);
1360 AddLine(BPoint(frame.left, frame.top + 1),
1361 BPoint(frame.right, frame.top + 1), ldark);
1362 AddLine(frame.LeftBottom(), frame.RightBottom(), hilite);
1363 } else if (fBarView->AcrossTop() || fBarView->AcrossBottom()) {
1364 AddLine(frame.LeftTop(),
1365 BPoint(frame.left, frame.bottom), dark);
1366 AddLine(BPoint(frame.left + 1, frame.top + 1),
1367 BPoint(frame.right - 1, frame.top + 1), light);
1368 AddLine(BPoint(frame.right, frame.top + 2),
1369 BPoint(frame.right, frame.bottom), hilite);
1370 AddLine(BPoint(frame.left + 1, frame.bottom),
1371 BPoint(frame.right - 1, frame.bottom), hilite);
1373 } else {
1374 if (fBarView->Vertical()) {
1375 AddLine(frame.LeftTop(), frame.RightTop(), light);
1376 AddLine(frame.LeftTop(), frame.LeftBottom(), light);
1377 AddLine(frame.RightBottom(), frame.RightTop(), hilite);
1378 } else if (fBarView->AcrossTop()) {
1379 AddLine(BPoint(frame.left, frame.top + 1),
1380 BPoint(frame.right - 1, frame.top + 1), light);
1381 AddLine(frame.RightTop(), frame.RightBottom(), vvdark);
1382 AddLine(BPoint(frame.right - 1, frame.top + 2),
1383 BPoint(frame.right - 1, frame.bottom - 1), hilite);
1384 AddLine(frame.LeftBottom(),
1385 BPoint(frame.right - 1, frame.bottom), hilite);
1386 } else if (fBarView->AcrossBottom()) {
1387 AddLine(BPoint(frame.left, frame.top + 1),
1388 BPoint(frame.right - 1, frame.top + 1), light);
1389 AddLine(frame.LeftBottom(), frame.RightBottom(), hilite);
1390 AddLine(frame.RightTop(), frame.RightBottom(), vvdark);
1391 AddLine(BPoint(frame.right - 1, frame.top + 1),
1392 BPoint(frame.right - 1, frame.bottom - 1), hilite);
1396 EndLineArray();
1398 if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion)
1399 DrawDragRegion();
1403 void
1404 TDragRegion::DrawDragRegion()
1406 BRect dragRegion(DragRegion());
1408 rgb_color menuColor = ViewColor();
1409 rgb_color menuHilite = menuColor;
1410 if (IsTracking()) {
1411 // Draw drag region highlighted if tracking mouse
1412 menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT);
1413 SetHighColor(menuHilite);
1414 FillRect(dragRegion);
1416 rgb_color vdark = tint_color(menuHilite, B_DARKEN_3_TINT);
1417 rgb_color light = tint_color(menuHilite, B_LIGHTEN_2_TINT);
1419 BeginLineArray(dragRegion.IntegerHeight());
1420 BPoint pt;
1421 pt.x = floorf((dragRegion.left + dragRegion.right) / 2 + 0.5) - 1;
1422 pt.y = dragRegion.top + 2;
1424 while (pt.y + 1 <= dragRegion.bottom) {
1425 AddLine(pt, pt, vdark);
1426 AddLine(pt + BPoint(1, 1), pt + BPoint(1, 1), light);
1428 pt.y += 3;
1430 EndLineArray();
1434 BRect
1435 TDragRegion::DragRegion() const
1437 float kTopBottomInset = 2;
1438 float kLeftRightInset = 1;
1439 float kDragWidth = 3;
1440 if (be_control_look != NULL) {
1441 kTopBottomInset = 1;
1442 kLeftRightInset = 0;
1443 kDragWidth = 4;
1446 BRect dragRegion(Bounds());
1447 dragRegion.top += kTopBottomInset;
1448 dragRegion.bottom -= kTopBottomInset;
1450 bool placeOnLeft = false;
1451 if (fDragLocation == kAutoPlaceDragRegion) {
1452 if (fBarView->Vertical() && fBarView->Left())
1453 placeOnLeft = true;
1454 else
1455 placeOnLeft = false;
1456 } else if (fDragLocation == kDragRegionLeft)
1457 placeOnLeft = true;
1458 else if (fDragLocation == kDragRegionRight)
1459 placeOnLeft = false;
1461 if (placeOnLeft) {
1462 dragRegion.left += kLeftRightInset;
1463 dragRegion.right = dragRegion.left + kDragWidth;
1464 } else {
1465 dragRegion.right -= kLeftRightInset;
1466 dragRegion.left = dragRegion.right - kDragWidth;
1469 return dragRegion;
1473 void
1474 TDragRegion::MouseDown(BPoint thePoint)
1476 uint32 buttons;
1477 BPoint where;
1478 BRect dragRegion(DragRegion());
1480 dragRegion.InsetBy(-2.0f, -2.0f);
1481 // DragRegion() is designed for drawing, not clicking
1483 if (!dragRegion.Contains(thePoint))
1484 return;
1486 while (true) {
1487 GetMouse(&where, &buttons);
1488 if (!buttons)
1489 break;
1491 if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) {
1492 fPreviousPosition = thePoint;
1493 SetTracking(true);
1494 SetMouseEventMask(B_POINTER_EVENTS,
1495 B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
1496 Invalidate(DragRegion());
1497 break;
1500 snooze(25000);
1505 void
1506 TDragRegion::MouseUp(BPoint pt)
1508 if (IsTracking()) {
1509 SetTracking(false);
1510 Invalidate(DragRegion());
1511 } else
1512 BControl::MouseUp(pt);
1516 bool
1517 TDragRegion::SwitchModeForRect(BPoint mouse, BRect rect,
1518 bool newVertical, bool newLeft, bool newTop, int32 newState)
1520 if (!rect.Contains(mouse)) {
1521 // not our rect
1522 return false;
1525 if (newVertical == fBarView->Vertical() && newLeft == fBarView->Left()
1526 && newTop == fBarView->Top() && newState == fBarView->State()) {
1527 // already in the correct mode
1528 return true;
1531 fBarView->ChangeState(newState, newVertical, newLeft, newTop, true);
1532 return true;
1536 void
1537 TDragRegion::MouseMoved(BPoint where, uint32 code, const BMessage* message)
1539 if (IsTracking()) {
1540 BScreen screen;
1541 BRect frame = screen.Frame();
1543 float hDivider = frame.Width() / 6;
1544 hDivider = (hDivider < gMinimumWindowWidth + 10.0f)
1545 ? gMinimumWindowWidth + 10.0f : hDivider;
1546 float miniDivider = frame.top + kMiniHeight + 10.0f;
1547 float vDivider = frame.Height() / 2;
1548 #ifdef FULL_MODE
1549 float thirdScreen = frame.Height() / 3;
1550 #endif
1551 BRect topLeft(frame.left, frame.top, frame.left + hDivider,
1552 miniDivider);
1553 BRect topMiddle(frame.left + hDivider, frame.top, frame.right
1554 - hDivider, vDivider);
1555 BRect topRight(frame.right - hDivider, frame.top, frame.right,
1556 miniDivider);
1558 #ifdef FULL_MODE
1559 vDivider = miniDivider + thirdScreen;
1560 #endif
1561 BRect middleLeft(frame.left, miniDivider, frame.left + hDivider,
1562 vDivider);
1563 BRect middleRight(frame.right - hDivider, miniDivider, frame.right,
1564 vDivider);
1566 #ifdef FULL_MODE
1567 BRect leftSide(frame.left, vDivider, frame.left + hDivider,
1568 frame.bottom - thirdScreen);
1569 BRect rightSide(frame.right - hDivider, vDivider, frame.right,
1570 frame.bottom - thirdScreen);
1572 vDivider = frame.bottom - thirdScreen;
1573 #endif
1574 BRect bottomLeft(frame.left, vDivider, frame.left + hDivider,
1575 frame.bottom);
1576 BRect bottomMiddle(frame.left + hDivider, vDivider, frame.right
1577 - hDivider, frame.bottom);
1578 BRect bottomRight(frame.right - hDivider, vDivider, frame.right,
1579 frame.bottom);
1581 if (where != fPreviousPosition) {
1582 fPreviousPosition = where;
1583 ConvertToScreen(&where);
1585 // use short circuit evaluation for convenience
1586 if (SwitchModeForRect(where, topLeft, true, true, true, kMiniState)
1587 || SwitchModeForRect(where, topMiddle, false, true, true,
1588 kExpandoState)
1589 || SwitchModeForRect(where, topRight, true, false, true,
1590 kMiniState)
1591 || SwitchModeForRect(where, middleLeft, true, true, true,
1592 kExpandoState)
1593 || SwitchModeForRect(where, middleRight, true, false, true,
1594 kExpandoState)
1596 #ifdef FULL_MODE
1597 || SwitchModeForRect(where, leftSide, true, true, true,
1598 kFullState)
1599 || SwitchModeForRect(where, rightSide, true, false, true,
1600 kFullState)
1601 #endif
1602 || SwitchModeForRect(where, bottomLeft, true, true, false,
1603 kMiniState)
1604 || SwitchModeForRect(where, bottomMiddle, false, true, false,
1605 kExpandoState)
1606 || SwitchModeForRect(where, bottomRight, true, false, false,
1607 kMiniState))
1610 } else
1611 BControl::MouseMoved(where, code, message);
1615 int32
1616 TDragRegion::DragRegionLocation() const
1618 return fDragLocation;
1622 void
1623 TDragRegion::SetDragRegionLocation(int32 location)
1625 if (location == fDragLocation)
1626 return;
1628 fDragLocation = location;
1629 Invalidate();