1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered
30trademarks of Be Incorporated in the United States and other countries. Other
31brand product names are registered trademarks or trademarks of their respective
32holders.
33All 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
80static const float kVerticalMiniMultiplier = 2.9f;
81
82float sIconGap = 0.0f;
83float gDragWidth, gDragRegionWidth = 0.0f;
84float gMinReplicantHeight, gMinReplicantWidth = 0.0f;
85float 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
95const char* const kInstantiateItemCFunctionName = "instantiate_deskbar_item";
96const char* const kInstantiateEntryCFunctionName = "instantiate_deskbar_entry";
97const char* const kReplicantSettingsFile = "replicants";
98const char* const kReplicantPathField = "replicant_path";
99
100
101static void
102DumpItem(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
112static void
113DumpList(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
136TReplicantTray::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
172TReplicantTray::~TReplicantTray()
173{
174	delete fShelf;
175	delete fTime;
176}
177
178
179void
180TReplicantTray::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
219void
220TReplicantTray::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*/
236void
237TReplicantTray::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
287void
288TReplicantTray::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
309void
310TReplicantTray::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:
380realignReplicants:
381			RealignReplicants();
382			AdjustPlacement();
383			break;
384
385		default:
386			BView::MessageReceived(message);
387			break;
388	}
389}
390
391
392void
393TReplicantTray::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
431void
432TReplicantTray::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
452void
453TReplicantTray::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
485void
486TReplicantTray::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
520void
521TReplicantTray::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
541DeskbarItemInfo*
542TReplicantTray::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
557DeskbarItemInfo*
558TReplicantTray::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
573bool
574TReplicantTray::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*/
583void
584TReplicantTray::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*/
643status_t
644TReplicantTray::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
712status_t
713TReplicantTray::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
742void
743TReplicantTray::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
766void
767TReplicantTray::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
800void
801TReplicantTray::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
839status_t
840TReplicantTray::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
861status_t
862TReplicantTray::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
878status_t
879TReplicantTray::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
897bool
898TReplicantTray::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
910bool
911TReplicantTray::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
924int32
925TReplicantTray::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*/
936status_t
937TReplicantTray::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
1016void
1017TReplicantTray::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
1036void
1037TReplicantTray::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
1056void
1057TReplicantTray::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
1083BView*
1084TReplicantTray::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
1119BView*
1120TReplicantTray::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
1145bool
1146TReplicantTray::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
1181BPoint
1182TReplicantTray::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
1269BRect
1270TReplicantTray::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
1280BRect
1281TReplicantTray::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
1298void
1299TReplicantTray::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
1325status_t
1326TReplicantTray::_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
1342void
1343TReplicantTray::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*/
1361TDragRegion::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
1372void
1373TDragRegion::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
1388void
1389TDragRegion::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
1407void
1408TDragRegion::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
1441void
1442TDragRegion::DrawAfterChildren(BRect updateRect)
1443{
1444	if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion)
1445		DrawDragger();
1446}
1447
1448
1449void
1450TDragRegion::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
1495BRect
1496TDragRegion::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
1516void
1517TDragRegion::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
1548void
1549TDragRegion::MouseUp(BPoint where)
1550{
1551	if (IsTracking()) {
1552		SetTracking(false);
1553		Invalidate(DragRegion());
1554	} else
1555		BControl::MouseUp(where);
1556}
1557
1558
1559bool
1560TDragRegion::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
1613void
1614TDragRegion::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
1695void
1696TDragRegion::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
1753int32
1754TDragRegion::DragRegionLocation() const
1755{
1756	return fDragLocation;
1757}
1758
1759
1760void
1761TDragRegion::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*/
1776TResizeControl::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
1785TResizeControl::~TResizeControl()
1786{
1787}
1788
1789
1790void
1791TResizeControl::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
1802void
1803TResizeControl::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
1844void
1845TResizeControl::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
1868void
1869TResizeControl::MouseUp(BPoint where)
1870{
1871	if (IsTracking()) {
1872		SetTracking(false);
1873		Invalidate();
1874	} else
1875		BControl::MouseUp(where);
1876}
1877
1878
1879void
1880TResizeControl::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