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 "DeskbarUtils.h"
73#include "ResourceSet.h"
74#include "StatusViewShelf.h"
75#include "TimeView.h"
76
77
78using std::max;
79
80#ifdef DB_ADDONS
81// Add-on support
82//
83// Item - internal item list (node, eref, etc)
84// Icon - physical replicant handed to the DeskbarClass class
85// AddOn - attribute based add-on
86
87const char* const kInstantiateItemCFunctionName = "instantiate_deskbar_item";
88const char* const kInstantiateEntryCFunctionName = "instantiate_deskbar_entry";
89const char* const kReplicantSettingsFile = "Deskbar_replicants";
90const char* const kReplicantPathField = "replicant_path";
91
92float sMinimumWindowWidth = kGutter + kMinimumTrayWidth + kDragRegionWidth;
93
94
95static void
96DumpItem(DeskbarItemInfo* item)
97{
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);
103}
104
105
106static void
107DumpList(BList* itemlist)
108{
109	int32 count = itemlist->CountItems() - 1;
110	if (count < 0) {
111		printf("no items in list\n");
112		return;
113	}
114	for (int32 i = count; i >= 0; i--) {
115		DeskbarItemInfo* item = (DeskbarItemInfo*)itemlist->ItemAt(i);
116		if (!item)
117			continue;
118
119		DumpItem(item);
120	}
121}
122#endif	/* DB_ADDONS */
123
124
125#undef B_TRANSLATION_CONTEXT
126#define B_TRANSLATION_CONTEXT "Tray"
127
128// don't change the name of this view to anything other than "Status"!
129
130TReplicantTray::TReplicantTray(TBarView* parent, bool vertical)
131	:
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)
140{
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		sMinimumWindowWidth = max_c(sMinimumWindowWidth,
146			2 * (logoBitmap->Bounds().Width() + 8));
147		fMinimumTrayWidth = sMinimumWindowWidth - kGutter - kDragRegionWidth;
148	}
149
150	// Create the time view
151	fTime = new TTimeView(fMinimumTrayWidth, kMaxReplicantHeight - 1.0);
152}
153
154
155TReplicantTray::~TReplicantTray()
156{
157	delete fShelf;
158	delete fTime;
159}
160
161
162void
163TReplicantTray::AttachedToWindow()
164{
165	BView::AttachedToWindow();
166
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));
172	}
173	SetDrawingMode(B_OP_COPY);
174
175	Window()->SetPulseRate(1000000);
176
177	// Set clock settings
178	clock_settings* settings = ((TBarApp*)be_app)->ClockSettings();
179	fTime->SetShowSeconds(settings->showSeconds);
180	fTime->SetShowDayOfWeek(settings->showDayOfWeek);
181	fTime->SetShowTimeZone(settings->showTimeZone);
182
183	AddChild(fTime);
184	fTime->MoveTo(Bounds().right - fTime->Bounds().Width() - 1, 2);
185
186#ifdef DB_ADDONS
187	// load addons and rehydrate archives
188#if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
189	InitAddOnSupport();
190#endif
191#endif
192	ResizeToPreferred();
193}
194
195
196void
197TReplicantTray::DetachedFromWindow()
198{
199#ifdef DB_ADDONS
200	// clean up add-on support
201#if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
202	DeleteAddOnSupport();
203#endif
204#endif
205	BView::DetachedFromWindow();
206}
207
208
209/*! Width is set to a minimum of kMinimumReplicantCount by kMaxReplicantWidth
210	if not in multirowmode and greater than kMinimumReplicantCount
211	the width should be calculated based on the actual replicant widths
212*/
213void
214TReplicantTray::GetPreferredSize(float* preferredWidth, float* preferredHeight)
215{
216	float width = 0, height = kMinimumTrayHeight;
217
218	if (fMultiRowMode) {
219		if (fShelf->CountReplicants() > 0)
220			height = fRightBottomReplicant.bottom;
221
222		// the height will be uniform for the number of rows necessary to show
223		// all the reps + any gutters necessary for spacing
224		int32 rowCount = (int32)(height / kMaxReplicantHeight);
225		height = kGutter + (rowCount * kMaxReplicantHeight)
226			+ ((rowCount - 1) * kIconGap) + kGutter;
227		height = max(kMinimumTrayHeight, height);
228		width = fMinimumTrayWidth;
229	} else {
230		// if last replicant overruns clock then resize to accomodate
231		if (fShelf->CountReplicants() > 0) {
232			if (!fTime->IsHidden() && fTime->Frame().left
233				< fRightBottomReplicant.right + 6) {
234				width = fRightBottomReplicant.right + 6
235					+ fTime->Frame().Width();
236			} else
237				width = fRightBottomReplicant.right + 3;
238		}
239
240		// this view has a fixed minimum width
241		width = max(fMinimumTrayWidth, width);
242		height = kGutter + static_cast<TBarApp*>(be_app)->IconSize() + kGutter;
243	}
244
245	*preferredWidth = width;
246	// add 1 for the border
247	*preferredHeight = height + 1;
248}
249
250
251void
252TReplicantTray::AdjustPlacement()
253{
254	// called when an add-on has been added or removed
255	// need to resize the parent of this accordingly
256
257	BRect bounds = Bounds();
258	float width, height;
259	GetPreferredSize(&width, &height);
260
261	if (width == bounds.Width() && height == bounds.Height()) {
262		// no need to change anything
263		return;
264	}
265
266	Parent()->ResizeToPreferred();
267	fBarView->UpdatePlacement();
268	Parent()->Invalidate();
269	Invalidate();
270}
271
272
273void
274TReplicantTray::MessageReceived(BMessage* message)
275{
276	switch (message->what) {
277		case B_LOCALE_CHANGED:
278		{
279			if (fTime == NULL)
280				return;
281
282			fTime->Update();
283
284			// time string reformat -> realign
285			RealignReplicants();
286			AdjustPlacement();
287			break;
288		}
289
290		case kShowHideTime:
291			// from context menu in clock and in this view
292			ShowHideTime();
293			break;
294
295		case kShowSeconds:
296			if (fTime == NULL)
297				return;
298
299			fTime->SetShowSeconds(!fTime->ShowSeconds());
300
301			// time string reformat -> realign
302			RealignReplicants();
303			AdjustPlacement();
304			break;
305
306		case kShowDayOfWeek:
307			if (fTime == NULL)
308				return;
309
310			fTime->SetShowDayOfWeek(!fTime->ShowDayOfWeek());
311
312			// time string reformat -> realign
313			RealignReplicants();
314			AdjustPlacement();
315			break;
316
317		case kShowTimeZone:
318			if (fTime == NULL)
319				return;
320
321			fTime->SetShowTimeZone(!fTime->ShowTimeZone());
322
323			// time string reformat -> realign
324			RealignReplicants();
325			AdjustPlacement();
326			break;
327
328		case kGetClockSettings:
329		{
330			if (fTime == NULL)
331				return;
332
333			bool showClock = !fTime->IsHidden();
334			bool showSeconds = fTime->ShowSeconds();
335			bool showDayOfWeek = fTime->ShowDayOfWeek();
336			bool showTimeZone = fTime->ShowTimeZone();
337
338			BMessage* reply = new BMessage(kGetClockSettings);
339			reply->AddBool("showClock", showClock);
340			reply->AddBool("showSeconds", showSeconds);
341			reply->AddBool("showDayOfWeek", showDayOfWeek);
342			reply->AddBool("showTimeZone", showTimeZone);
343			message->SendReply(reply);
344			break;
345		}
346
347#ifdef DB_ADDONS
348		case B_NODE_MONITOR:
349			HandleEntryUpdate(message);
350			break;
351#endif
352
353		default:
354			BView::MessageReceived(message);
355			break;
356	}
357}
358
359
360void
361TReplicantTray::MouseDown(BPoint where)
362{
363#ifdef DB_ADDONS
364	if (modifiers() & B_CONTROL_KEY)
365		DumpList(fItemList);
366#endif
367
368	uint32 buttons;
369
370	Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
371	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
372		ShowReplicantMenu(where);
373	} else {
374		BPoint save = where;
375		bigtime_t doubleClickSpeed;
376		bigtime_t start = system_time();
377		uint32 buttons;
378
379		get_click_speed(&doubleClickSpeed);
380
381		do {
382			if (fabs(where.x - save.x) > 4 || fabs(where.y - save.y) > 4)
383				// user moved out of bounds of click area
384				break;
385
386			if ((system_time() - start) > (2 * doubleClickSpeed)) {
387				ShowReplicantMenu(where);
388				break;
389			}
390
391			snooze(50000);
392			GetMouse(&where, &buttons);
393		} while (buttons);
394	}
395	BView::MouseDown(where);
396}
397
398
399void
400TReplicantTray::ShowReplicantMenu(BPoint point)
401{
402	BPopUpMenu* menu = new BPopUpMenu("", false, false);
403	menu->SetFont(be_plain_font);
404
405	// If clock is visible show the extended menu, otherwise show "Show clock"
406
407	if (!fTime->IsHidden())
408		fTime->ShowTimeOptions(ConvertToScreen(point));
409	else {
410		BMenuItem* item = new BMenuItem(B_TRANSLATE("Show clock"),
411			new BMessage(kShowHideTime));
412		menu->AddItem(item);
413		menu->SetTargetForItems(this);
414		BPoint where = ConvertToScreen(point);
415		menu->Go(where, true, true, BRect(where - BPoint(4, 4),
416			where + BPoint(4, 4)), true);
417	}
418}
419
420
421void
422TReplicantTray::SetMultiRow(bool state)
423{
424	fMultiRowMode = state;
425}
426
427
428void
429TReplicantTray::ShowHideTime()
430{
431	if (fTime == NULL)
432		return;
433
434	if (fTime->IsHidden())
435		fTime->Show();
436	else
437		fTime->Hide();
438
439	RealignReplicants();
440	AdjustPlacement();
441
442	// message Time preferences to update it's show time setting
443	BMessenger messenger("application/x-vnd.Haiku-Time");
444	BMessage* message = new BMessage(kShowHideTime);
445	message->AddBool("showClock", !fTime->IsHidden());
446	messenger.SendMessage(message);
447}
448
449
450#ifdef DB_ADDONS
451
452
453void
454TReplicantTray::InitAddOnSupport()
455{
456	// list to maintain refs to each rep added/deleted
457	fItemList = new BList();
458	BPath path;
459
460	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) {
461		path.Append(kReplicantSettingsFile);
462
463		BFile file(path.Path(), B_READ_ONLY);
464		if (file.InitCheck() == B_OK) {
465			status_t result;
466			BEntry entry;
467			int32 id;
468			BString path;
469			if (fAddOnSettings.Unflatten(&file) == B_OK) {
470				for (int32 i = 0; fAddOnSettings.FindString(kReplicantPathField,
471					i, &path) == B_OK; i++) {
472					if (entry.SetTo(path.String()) == B_OK && entry.Exists()) {
473						result = LoadAddOn(&entry, &id, false);
474					} else
475						result = B_ENTRY_NOT_FOUND;
476
477					if (result != B_OK) {
478						fAddOnSettings.RemoveData(kReplicantPathField, i);
479						--i;
480					}
481				}
482			}
483		}
484	}
485}
486
487
488void
489TReplicantTray::DeleteAddOnSupport()
490{
491	_SaveSettings();
492
493	for (int32 i = fItemList->CountItems(); i-- > 0 ;) {
494		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->RemoveItem(i);
495		if (item) {
496			if (item->isAddOn)
497				watch_node(&(item->nodeRef), B_STOP_WATCHING, this, Window());
498
499			delete item;
500		}
501	}
502	delete fItemList;
503
504	// stop the volume mount/unmount watch
505	stop_watching(this, Window());
506}
507
508
509DeskbarItemInfo*
510TReplicantTray::DeskbarItemFor(node_ref& nodeRef)
511{
512	for (int32 i = fItemList->CountItems(); i-- > 0 ;) {
513		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
514		if (item == NULL)
515			continue;
516
517		if (item->nodeRef == nodeRef)
518			return item;
519	}
520
521	return NULL;
522}
523
524
525DeskbarItemInfo*
526TReplicantTray::DeskbarItemFor(int32 id)
527{
528	for (int32 i = fItemList->CountItems(); i-- > 0 ;) {
529		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
530		if (item == NULL)
531			continue;
532
533		if (item->id == id)
534			return item;
535	}
536
537	return NULL;
538}
539
540
541bool
542TReplicantTray::NodeExists(node_ref& nodeRef)
543{
544	return DeskbarItemFor(nodeRef) != NULL;
545}
546
547
548/*! This handles B_NODE_MONITOR & B_QUERY_UPDATE messages received
549	for the registered add-ons.
550*/
551void
552TReplicantTray::HandleEntryUpdate(BMessage* message)
553{
554	int32 opcode;
555	if (message->FindInt32("opcode", &opcode) != B_OK)
556		return;
557
558	BPath path;
559	switch (opcode) {
560		case B_ENTRY_MOVED:
561		{
562			entry_ref ref;
563			ino_t todirectory;
564			ino_t node;
565			const char* name;
566			if (message->FindString("name", &name) == B_OK
567				&& message->FindInt64("from directory", &(ref.directory))
568				== B_OK
569				&& message->FindInt64("to directory", &todirectory) == B_OK
570				&& message->FindInt32("device", &(ref.device)) == B_OK
571				&& message->FindInt64("node", &node) == B_OK ) {
572
573				if (!name)
574					break;
575
576				ref.set_name(name);
577				// change the directory reference to
578				// the new directory
579				MoveItem(&ref, todirectory);
580			}
581			break;
582		}
583
584		case B_ENTRY_REMOVED:
585		{
586			// entry was rm'd from the device
587			node_ref nodeRef;
588			if (message->FindInt32("device", &(nodeRef.device)) == B_OK
589				&& message->FindInt64("node", &(nodeRef.node)) == B_OK) {
590				DeskbarItemInfo* item = DeskbarItemFor(nodeRef);
591				if (item == NULL)
592					break;
593
594				// If there is a team running where the add-on comes from,
595				// we don't want to remove the icon yet.
596				if (be_roster->IsRunning(&item->entryRef))
597					break;
598
599				UnloadAddOn(&nodeRef, NULL, true, false);
600			}
601			break;
602		}
603	}
604}
605
606
607/*! The add-ons must support the exported C function API
608	if they do, they will be loaded and added to deskbar
609	primary function is the Instantiate function
610*/
611status_t
612TReplicantTray::LoadAddOn(BEntry* entry, int32* id, bool addToSettings)
613{
614	if (!entry)
615		return B_ERROR;
616
617	node_ref nodeRef;
618	entry->GetNodeRef(&nodeRef);
619	// no duplicates
620	if (NodeExists(nodeRef))
621		return B_ERROR;
622
623	BNode node(entry);
624	BPath path;
625	status_t status = entry->GetPath(&path);
626	if (status < B_OK)
627		return status;
628
629	// load the add-on
630	image_id image = load_add_on(path.Path());
631	if (image < B_OK)
632		return image;
633
634	// get the view loading function symbol
635	//    we first look for a symbol that takes an image_id
636	//    and entry_ref pointer, if not found, go with normal
637	//    instantiate function
638	BView* (*entryFunction)(image_id, const entry_ref*);
639	BView* (*itemFunction)(void);
640	BView* view = NULL;
641
642	entry_ref ref;
643	entry->GetRef(&ref);
644
645	if (get_image_symbol(image, kInstantiateEntryCFunctionName,
646			B_SYMBOL_TYPE_TEXT, (void**)&entryFunction) >= B_OK) {
647		view = (*entryFunction)(image, &ref);
648	} else if (get_image_symbol(image, kInstantiateItemCFunctionName,
649			B_SYMBOL_TYPE_TEXT, (void**)&itemFunction) >= B_OK) {
650		view = (*itemFunction)();
651	} else {
652		unload_add_on(image);
653		return B_ERROR;
654	}
655
656	if (view == NULL || IconExists(view->Name())) {
657		delete view;
658		unload_add_on(image);
659		return B_ERROR;
660	}
661
662	BMessage* data = new BMessage;
663	view->Archive(data);
664	delete view;
665
666	AddIcon(data, id, &ref);
667		// add the rep; adds info to list
668
669	if (addToSettings) {
670		fAddOnSettings.AddString(kReplicantPathField, path.Path());
671		_SaveSettings();
672	}
673
674	return B_OK;
675}
676
677
678status_t
679TReplicantTray::AddItem(int32 id, node_ref nodeRef, BEntry& entry, bool isAddOn)
680{
681	DeskbarItemInfo* item = new DeskbarItemInfo;
682	if (item == NULL)
683		return B_NO_MEMORY;
684
685	item->id = id;
686	item->isAddOn = isAddOn;
687
688	if (entry.GetRef(&item->entryRef) < B_OK) {
689		item->entryRef.device = -1;
690		item->entryRef.directory = -1;
691		item->entryRef.name = NULL;
692	}
693	item->nodeRef = nodeRef;
694
695	fItemList->AddItem(item);
696
697	if (isAddOn)
698		watch_node(&nodeRef, B_WATCH_NAME | B_WATCH_ATTR, this, Window());
699
700	return B_OK;
701}
702
703
704/**	from entry_removed message, when attribute removed
705 *	or when a device is unmounted (use removeall, by device)
706 */
707
708void
709TReplicantTray::UnloadAddOn(node_ref* nodeRef, dev_t* device,
710	bool which, bool removeAll)
711{
712	for (int32 i = fItemList->CountItems(); i-- > 0 ;) {
713		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
714		if (!item)
715			continue;
716
717		if ((which && nodeRef && item->nodeRef == *nodeRef)
718			|| (device && item->nodeRef.device == *device)) {
719
720			if (device && be_roster->IsRunning(&item->entryRef))
721				continue;
722
723			RemoveIcon(item->id);
724
725			if (!removeAll)
726				break;
727		}
728	}
729}
730
731
732void
733TReplicantTray::RemoveItem(int32 id)
734{
735	DeskbarItemInfo* item = DeskbarItemFor(id);
736	if (item == NULL)
737		return;
738
739	// attribute was added via Deskbar API (AddItem(entry_ref*, int32*)
740	if (item->isAddOn) {
741		BPath path(&item->entryRef);
742		BString storedPath;
743		for (int32 i = 0;
744			fAddOnSettings.FindString(kReplicantPathField, i, &storedPath)
745				== B_OK; i++) {
746			if (storedPath == path.Path()) {
747				fAddOnSettings.RemoveData(kReplicantPathField, i);
748				break;
749			}
750		}
751		_SaveSettings();
752
753		BNode node(&item->entryRef);
754		watch_node(&item->nodeRef, B_STOP_WATCHING, this, Window());
755	}
756
757	fItemList->RemoveItem(item);
758	delete item;
759}
760
761
762/**	ENTRY_MOVED message, moving only occurs on a device
763 *	copying will occur (ENTRY_CREATED) between devices
764 */
765
766void
767TReplicantTray::MoveItem(entry_ref* ref, ino_t toDirectory)
768{
769	if (!ref)
770		return;
771
772	// scan for a matching entry_ref and update it
773	//
774	// don't need to change node info as it does not change
775
776	for (int32 i = fItemList->CountItems(); i-- > 0 ;) {
777		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
778		if (!item)
779			continue;
780
781		if (!strcmp(item->entryRef.name, ref->name)
782			&& item->entryRef.device == ref->device
783			&& item->entryRef.directory == ref->directory) {
784			item->entryRef.directory = toDirectory;
785			break;
786		}
787	}
788}
789
790#endif // add-on support
791
792//	external add-on API routines
793//	called using the new BDeskbar class
794
795//	existence of icon/replicant by name or ID
796//	returns opposite
797//	note: name and id are semi-private limiting
798//		the ability of non-host apps to remove
799//		icons without a little bit of work
800
801/**	for a specific id
802 *	return the name of the replicant (name of view)
803 */
804
805status_t
806TReplicantTray::ItemInfo(int32 id, const char** name)
807{
808	if (id < 0)
809		return B_ERROR;
810
811	int32 index, temp;
812	BView* view = ViewAt(&index, &temp, id, false);
813	if (view) {
814		*name = view->Name();
815		return B_OK;
816	}
817
818	return B_ERROR;
819}
820
821
822/**	for a specific name
823 *	return the id (internal to Deskbar)
824 */
825
826status_t
827TReplicantTray::ItemInfo(const char* name, int32* id)
828{
829	if (!name || strlen(name) <= 0)
830		return B_ERROR;
831
832	int32 index;
833	BView* view = ViewAt(&index, id, name);
834	if (view)
835		return B_OK;
836
837	return B_ERROR;
838}
839
840
841/**	at a specific index
842 *	return both the name and the id of the replicant
843 */
844
845status_t
846TReplicantTray::ItemInfo(int32 index, const char** name, int32* id)
847{
848	if (index < 0)
849		return B_ERROR;
850
851	BView* view;
852	fShelf->ReplicantAt(index, &view, (uint32*)id, NULL);
853	if (view) {
854		*name = view->Name();
855		return B_OK;
856	}
857
858	return B_ERROR;
859}
860
861
862/**	replicant exists, by id/index */
863
864bool
865TReplicantTray::IconExists(int32 target, bool byIndex)
866{
867	int32 index, id;
868	BView* view = ViewAt(&index, &id, target, byIndex);
869
870	return view && index >= 0;
871}
872
873
874/**	replicant exists, by name */
875
876bool
877TReplicantTray::IconExists(const char* name)
878{
879	if (!name || strlen(name) == 0)
880		return false;
881
882	int32 index, id;
883	BView* view = ViewAt(&index, &id, name);
884
885	return view && index >= 0;
886}
887
888
889int32
890TReplicantTray::IconCount() const
891{
892	return fShelf->CountReplicants();
893}
894
895
896/*! Message must contain an archivable view for later rehydration.
897	This function takes over ownership of the provided message on success
898	only.
899	Returns the current replicant ID.
900*/
901status_t
902TReplicantTray::AddIcon(BMessage* archive, int32* id, const entry_ref* addOn)
903{
904	if (archive == NULL || id == NULL)
905		return B_ERROR;
906
907	// find entry_ref
908
909	entry_ref ref;
910	if (addOn) {
911		// Use it if we got it
912		ref = *addOn;
913	} else {
914		const char* signature;
915
916		status_t status = archive->FindString("add_on", &signature);
917		if (status == B_OK) {
918			BRoster roster;
919			status = roster.FindApp(signature, &ref);
920		}
921		if (status < B_OK)
922			return status;
923	}
924
925	BFile file;
926	status_t status = file.SetTo(&ref, B_READ_ONLY);
927	if (status < B_OK)
928		return status;
929
930	node_ref nodeRef;
931	status = file.GetNodeRef(&nodeRef);
932	if (status < B_OK)
933		return status;
934
935	BEntry entry(&ref, true);
936		// TODO: this resolves an eventual link for the item being added - this
937		// is okay for now, but in multi-user environments, one might want to
938		// have links that carry the be:deskbar_item_status attribute
939	status = entry.InitCheck();
940	if (status != B_OK)
941		return status;
942
943	*id = 999;
944	if (archive->what == B_ARCHIVED_OBJECT)
945		archive->what = 0;
946
947	BRect originalBounds = archive->FindRect("_frame");
948		// this is a work-around for buggy replicants that change their size in
949		// AttachedToWindow() (such as "SVM")
950
951	// TODO: check for name collisions?
952	status = fShelf->AddReplicant(archive, BPoint(1, 1));
953	if (status != B_OK)
954		return status;
955
956	int32 count = fShelf->CountReplicants();
957	BView* view;
958	fShelf->ReplicantAt(count - 1, &view, (uint32*)id, NULL);
959
960	if (originalBounds != view->Bounds()) {
961		// The replicant changed its size when added to the window, so we need
962		// to recompute all over again (it's already done once via
963		// BShelf::AddReplicant() and TReplicantShelf::CanAcceptReplicantView())
964		RealignReplicants();
965	}
966
967	float oldWidth = Bounds().Width();
968	float oldHeight = Bounds().Height();
969	float width, height;
970	GetPreferredSize(&width, &height);
971	if (oldWidth != width || oldHeight != height)
972		AdjustPlacement();
973
974	// add the item to the add-on list
975
976	AddItem(*id, nodeRef, entry, addOn != NULL);
977	return B_OK;
978}
979
980
981void
982TReplicantTray::RemoveIcon(int32 target, bool byIndex)
983{
984	if (target < 0)
985		return;
986
987	int32 index, id;
988	BView* view = ViewAt(&index, &id, target, byIndex);
989	if (view && index >= 0) {
990		// remove the reference from the item list & the shelf
991		RemoveItem(id);
992		fShelf->DeleteReplicant(index);
993
994		// force a placement update,  !! need to fix BShelf
995		RealReplicantAdjustment(index);
996	}
997}
998
999
1000void
1001TReplicantTray::RemoveIcon(const char* name)
1002{
1003	if (!name || strlen(name) <= 0)
1004		return;
1005
1006	int32 id, index;
1007	BView* view = ViewAt(&index, &id, name);
1008	if (view && index >= 0) {
1009		// remove the reference from the item list & shelf
1010		RemoveItem(id);
1011		fShelf->DeleteReplicant(index);
1012
1013		// force a placement update,  !! need to fix BShelf
1014		RealReplicantAdjustment(index);
1015	}
1016}
1017
1018
1019void
1020TReplicantTray::RealReplicantAdjustment(int32 startIndex)
1021{
1022	if (startIndex < 0)
1023		return;
1024
1025	if (startIndex == fLastReplicant)
1026		startIndex = 0;
1027
1028	// reset the locations of all replicants after the one deleted
1029	RealignReplicants(startIndex);
1030
1031	float oldWidth = Bounds().Width();
1032	float oldHeight = Bounds().Height();
1033	float width, height;
1034	GetPreferredSize(&width, &height);
1035	if (oldWidth != width || oldHeight != height) {
1036		// resize view to accomodate the replicants, redraw as necessary
1037		AdjustPlacement();
1038	}
1039}
1040
1041
1042/**	looking for a replicant by id/index
1043 *	return the view and index
1044 */
1045
1046BView*
1047TReplicantTray::ViewAt(int32* index, int32* id, int32 target, bool byIndex)
1048{
1049	*index = -1;
1050
1051	BView* view;
1052	if (byIndex) {
1053		if (fShelf->ReplicantAt(target, &view, (uint32*)id)) {
1054			if (view) {
1055				*index = target;
1056				return view;
1057			}
1058		}
1059	} else {
1060		int32 count = fShelf->CountReplicants() - 1;
1061		int32 localid;
1062		for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) {
1063			fShelf->ReplicantAt(repIndex, &view, (uint32*)&localid);
1064			if (localid == target && view) {
1065				*index = repIndex;
1066				*id = localid;
1067				return view;
1068			}
1069		}
1070	}
1071	return NULL;
1072}
1073
1074
1075/**	looking for a replicant with a view by name
1076 *	return the view, index and the id of the replicant
1077 */
1078
1079BView*
1080TReplicantTray::ViewAt(int32* index, int32* id, const char* name)
1081{
1082	*index = -1;
1083	*id = -1;
1084
1085	BView* view;
1086	int32 count = fShelf->CountReplicants()-1;
1087	for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) {
1088		fShelf->ReplicantAt(repIndex, &view, (uint32*)id);
1089		if (view && view->Name() && strcmp(name, view->Name()) == 0) {
1090			*index = repIndex;
1091			return view;
1092		}
1093	}
1094	return NULL;
1095}
1096
1097
1098/**	Shelf will call to determine where and if
1099 *	the replicant is to be added
1100 */
1101
1102bool
1103TReplicantTray::AcceptAddon(BRect replicantFrame, BMessage* message)
1104{
1105	if (!message)
1106		return false;
1107
1108	if (replicantFrame.Height() > kMaxReplicantHeight)
1109		return false;
1110
1111	alignment align = B_ALIGN_LEFT;
1112	if (fAlignmentSupport && message->HasBool("deskbar:dynamic_align")) {
1113		if (!fBarView->Vertical())
1114			align = B_ALIGN_RIGHT;
1115		else
1116			align = fBarView->Left() ? B_ALIGN_LEFT : B_ALIGN_RIGHT;
1117	} else if (message->HasInt32("deskbar:align"))
1118		message->FindInt32("deskbar:align", (int32*)&align);
1119
1120	if (message->HasInt32("deskbar:private_align"))
1121		message->FindInt32("deskbar:private_align", (int32*)&align);
1122	else
1123		align = B_ALIGN_LEFT;
1124
1125	BPoint loc = LocationForReplicant(fShelf->CountReplicants(),
1126		replicantFrame.Width());
1127
1128	message->AddPoint("_pjp_loc", loc);
1129	return true;
1130}
1131
1132
1133/**	based on the previous (index - 1) replicant in the list
1134 *	calculate where the left point should be for this
1135 *	replicant.  replicant will flow to the right on its own
1136 */
1137
1138BPoint
1139TReplicantTray::LocationForReplicant(int32 index, float width)
1140{
1141	BPoint loc(kIconGap + 1, kGutter + 1);
1142
1143	if (fMultiRowMode) {
1144		// try to find free space in every row
1145		for (int32 row = 0; ; loc.y += kMaxReplicantHeight + kIconGap, row++) {
1146			// determine free space in this row
1147			BRect rect(loc.x, loc.y, loc.x + fMinimumTrayWidth - kIconGap
1148				- 2.0, loc.y + kMaxReplicantHeight);
1149			if (row == 0 && !fTime->IsHidden())
1150				rect.right -= fTime->Frame().Width() + kIconGap;
1151
1152			for (int32 i = 0; i < index; i++) {
1153				BView* view = NULL;
1154				fShelf->ReplicantAt(i, &view);
1155				if (view == NULL || view->Frame().top != rect.top)
1156					continue;
1157
1158				rect.left = view->Frame().right + kIconGap + 1;
1159			}
1160
1161			if (rect.Width() >= width) {
1162				// the icon fits in this row
1163				loc = rect.LeftTop();
1164				break;
1165			}
1166		}
1167	} else {
1168		if (index > 0) {
1169			// get the last replicant added for placement reference
1170			BView* view = NULL;
1171			fShelf->ReplicantAt(index - 1, &view);
1172			if (view) {
1173				// push this rep placement past the last one
1174				loc.x = view->Frame().right + kIconGap + 1;
1175				loc.y = view->Frame().top;
1176			}
1177		}
1178	}
1179
1180	if ((loc.y == fRightBottomReplicant.top && loc.x
1181		> fRightBottomReplicant.left) || loc.y > fRightBottomReplicant.top) {
1182		fRightBottomReplicant.Set(loc.x, loc.y, loc.x + width, loc.y
1183		+ kMaxReplicantHeight);
1184		fLastReplicant = index;
1185	}
1186
1187	return loc;
1188}
1189
1190
1191BRect
1192TReplicantTray::IconFrame(int32 target, bool byIndex)
1193{
1194	int32 index, id;
1195	BView* view = ViewAt(&index, &id, target, byIndex);
1196	if (view)
1197		return view->Frame();
1198
1199	return BRect(0, 0, 0, 0);
1200}
1201
1202
1203BRect
1204TReplicantTray::IconFrame(const char* name)
1205{
1206	if (!name)
1207		return BRect(0, 0, 0, 0);
1208
1209	int32 id, index;
1210	BView* view = ViewAt(&index, &id, name);
1211	if (view)
1212		return view->Frame();
1213
1214	return BRect(0, 0, 0, 0);
1215}
1216
1217
1218/**	Scan from the startIndex and reset the location
1219 *	as defined in LocationForReplicant()
1220 */
1221
1222void
1223TReplicantTray::RealignReplicants(int32 startIndex)
1224{
1225	if (startIndex < 0)
1226		startIndex = 0;
1227
1228	int32 count = fShelf->CountReplicants();
1229	if (count <= 0)
1230		return;
1231
1232	if (startIndex == 0)
1233		fRightBottomReplicant.Set(0, 0, 0, 0);
1234
1235	BView* view = NULL;
1236	for (int32 i = startIndex ; i < count ; i++) {
1237		fShelf->ReplicantAt(i, &view);
1238		if (view != NULL) {
1239			BPoint loc = LocationForReplicant(i, view->Frame().Width());
1240			if (view->Frame().LeftTop() != loc)
1241				view->MoveTo(loc);
1242		}
1243	}
1244}
1245
1246
1247status_t
1248TReplicantTray::_SaveSettings()
1249{
1250	status_t result;
1251	BPath path;
1252	if ((result = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true))
1253		 == B_OK) {
1254		path.Append(kReplicantSettingsFile);
1255
1256		BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1257		if ((result = file.InitCheck()) == B_OK)
1258			result = fAddOnSettings.Flatten(&file);
1259	}
1260
1261	return result;
1262}
1263
1264
1265void
1266TReplicantTray::SaveTimeSettings()
1267{
1268	if (fTime == NULL)
1269		return;
1270
1271	clock_settings* settings = ((TBarApp*)be_app)->ClockSettings();
1272	settings->showSeconds = fTime->ShowSeconds();
1273	settings->showDayOfWeek = fTime->ShowDayOfWeek();
1274	settings->showTimeZone = fTime->ShowTimeZone();
1275}
1276
1277
1278//	#pragma mark -
1279
1280
1281/*! Draggable region that is asynchronous so that dragging does not block
1282	other activities.
1283*/
1284TDragRegion::TDragRegion(TBarView* parent, BView* child)
1285	:
1286	BControl(BRect(0, 0, 0, 0), "", "", NULL, B_FOLLOW_NONE,
1287		B_WILL_DRAW | B_FRAME_EVENTS),
1288	fBarView(parent),
1289	fChild(child),
1290	fDragLocation(kAutoPlaceDragRegion)
1291{
1292}
1293
1294
1295void
1296TDragRegion::AttachedToWindow()
1297{
1298	BView::AttachedToWindow();
1299	if (be_control_look != NULL)
1300		SetViewColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 1.1));
1301	else
1302		SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
1303	ResizeToPreferred();
1304}
1305
1306
1307void
1308TDragRegion::GetPreferredSize(float* width, float* height)
1309{
1310	fChild->ResizeToPreferred();
1311	*width = fChild->Bounds().Width();
1312	*height = fChild->Bounds().Height();
1313
1314	if (fDragLocation != kNoDragRegion)
1315		*width += 7;
1316	else
1317		*width += 6;
1318
1319	*height += 3;
1320}
1321
1322
1323void
1324TDragRegion::FrameMoved(BPoint)
1325{
1326	if (fBarView->Left() && fBarView->Vertical()
1327		&& fDragLocation != kNoDragRegion)
1328		fChild->MoveTo(5, 2);
1329	else
1330		fChild->MoveTo(2, 2);
1331}
1332
1333
1334void
1335TDragRegion::Draw(BRect)
1336{
1337	rgb_color menuColor = ViewColor();
1338	rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
1339	rgb_color ldark = tint_color(menuColor, 1.02);
1340	rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT);
1341	rgb_color vvdark = tint_color(menuColor, B_DARKEN_4_TINT);
1342	rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT);
1343
1344	BRect frame(Bounds());
1345	BeginLineArray(4);
1346
1347	if (be_control_look != NULL) {
1348		if (fBarView->Vertical()) {
1349			AddLine(frame.LeftTop(), frame.RightTop(), dark);
1350			AddLine(BPoint(frame.left, frame.top + 1),
1351				BPoint(frame.right, frame.top + 1), ldark);
1352			AddLine(frame.LeftBottom(), frame.RightBottom(), hilite);
1353		} else if (fBarView->AcrossTop() || fBarView->AcrossBottom()) {
1354			AddLine(frame.LeftTop(),
1355				BPoint(frame.left, frame.bottom), dark);
1356			AddLine(BPoint(frame.left + 1, frame.top + 1),
1357				BPoint(frame.right - 1, frame.top + 1), light);
1358			AddLine(BPoint(frame.right, frame.top + 2),
1359				BPoint(frame.right, frame.bottom), hilite);
1360			AddLine(BPoint(frame.left + 1, frame.bottom),
1361				BPoint(frame.right - 1, frame.bottom), hilite);
1362		}
1363	} else {
1364		if (fBarView->Vertical()) {
1365			AddLine(frame.LeftTop(), frame.RightTop(), light);
1366			AddLine(frame.LeftTop(), frame.LeftBottom(), light);
1367			AddLine(frame.RightBottom(), frame.RightTop(), hilite);
1368		} else if (fBarView->AcrossTop()) {
1369			AddLine(BPoint(frame.left, frame.top + 1),
1370				BPoint(frame.right - 1, frame.top + 1), light);
1371			AddLine(frame.RightTop(), frame.RightBottom(), vvdark);
1372			AddLine(BPoint(frame.right - 1, frame.top + 2),
1373				BPoint(frame.right - 1, frame.bottom - 1), hilite);
1374			AddLine(frame.LeftBottom(),
1375				BPoint(frame.right - 1, frame.bottom), hilite);
1376		} else if (fBarView->AcrossBottom()) {
1377			AddLine(BPoint(frame.left, frame.top + 1),
1378				BPoint(frame.right - 1, frame.top + 1), light);
1379			AddLine(frame.LeftBottom(), frame.RightBottom(), hilite);
1380			AddLine(frame.RightTop(), frame.RightBottom(), vvdark);
1381			AddLine(BPoint(frame.right - 1, frame.top + 1),
1382				BPoint(frame.right - 1, frame.bottom - 1), hilite);
1383		}
1384	}
1385
1386	EndLineArray();
1387
1388	if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion)
1389		DrawDragRegion();
1390}
1391
1392
1393void
1394TDragRegion::DrawDragRegion()
1395{
1396	BRect dragRegion(DragRegion());
1397
1398	rgb_color menuColor = ViewColor();
1399	rgb_color menuHilite = menuColor;
1400	if (IsTracking()) {
1401		// Draw drag region highlighted if tracking mouse
1402		menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT);
1403		SetHighColor(menuHilite);
1404		FillRect(dragRegion);
1405	}
1406	rgb_color vdark = tint_color(menuHilite, B_DARKEN_3_TINT);
1407	rgb_color light = tint_color(menuHilite, B_LIGHTEN_2_TINT);
1408
1409	BeginLineArray(dragRegion.IntegerHeight());
1410	BPoint pt;
1411	pt.x = floorf((dragRegion.left + dragRegion.right) / 2 + 0.5) - 1;
1412	pt.y = dragRegion.top + 2;
1413
1414	while (pt.y + 1 <= dragRegion.bottom) {
1415		AddLine(pt, pt, vdark);
1416		AddLine(pt + BPoint(1, 1), pt + BPoint(1, 1), light);
1417
1418		pt.y += 3;
1419	}
1420	EndLineArray();
1421}
1422
1423
1424BRect
1425TDragRegion::DragRegion() const
1426{
1427	float kTopBottomInset = 2;
1428	float kLeftRightInset = 1;
1429	float kDragWidth = 3;
1430	if (be_control_look != NULL) {
1431		kTopBottomInset = 1;
1432		kLeftRightInset = 0;
1433		kDragWidth = 4;
1434	}
1435
1436	BRect dragRegion(Bounds());
1437	dragRegion.top += kTopBottomInset;
1438	dragRegion.bottom -= kTopBottomInset;
1439
1440	bool placeOnLeft = false;
1441	if (fDragLocation == kAutoPlaceDragRegion) {
1442		if (fBarView->Vertical() && fBarView->Left())
1443			placeOnLeft = true;
1444		else
1445			placeOnLeft = false;
1446	} else if (fDragLocation == kDragRegionLeft)
1447		placeOnLeft = true;
1448	else if (fDragLocation == kDragRegionRight)
1449		placeOnLeft = false;
1450
1451	if (placeOnLeft) {
1452		dragRegion.left += kLeftRightInset;
1453		dragRegion.right = dragRegion.left + kDragWidth;
1454	} else {
1455		dragRegion.right -= kLeftRightInset;
1456		dragRegion.left = dragRegion.right - kDragWidth;
1457	}
1458
1459	return dragRegion;
1460}
1461
1462
1463void
1464TDragRegion::MouseDown(BPoint thePoint)
1465{
1466	uint32 buttons;
1467	BPoint where;
1468	BRect dragRegion(DragRegion());
1469
1470	dragRegion.InsetBy(-2.0f, -2.0f);
1471		// DragRegion() is designed for drawing, not clicking
1472
1473	if (!dragRegion.Contains(thePoint))
1474		return;
1475
1476	while (true) {
1477		GetMouse(&where, &buttons);
1478		if (!buttons)
1479			break;
1480
1481		if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) {
1482			fPreviousPosition = thePoint;
1483			SetTracking(true);
1484			SetMouseEventMask(B_POINTER_EVENTS,
1485				B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
1486			Invalidate(DragRegion());
1487			break;
1488		}
1489
1490		snooze(25000);
1491	}
1492}
1493
1494
1495void
1496TDragRegion::MouseUp(BPoint pt)
1497{
1498	if (IsTracking()) {
1499		SetTracking(false);
1500		Invalidate(DragRegion());
1501	} else
1502		BControl::MouseUp(pt);
1503}
1504
1505
1506bool
1507TDragRegion::SwitchModeForRect(BPoint mouse, BRect rect,
1508	bool newVertical, bool newLeft, bool newTop, int32 newState)
1509{
1510	if (!rect.Contains(mouse)) {
1511		// not our rect
1512		return false;
1513	}
1514
1515	if (newVertical == fBarView->Vertical() && newLeft == fBarView->Left()
1516		&& newTop == fBarView->Top() && newState == fBarView->State()) {
1517		// already in the correct mode
1518		return true;
1519	}
1520
1521	fBarView->ChangeState(newState, newVertical, newLeft, newTop, true);
1522	return true;
1523}
1524
1525
1526void
1527TDragRegion::MouseMoved(BPoint where, uint32 code, const BMessage* message)
1528{
1529	if (IsTracking()) {
1530		BScreen screen;
1531		BRect frame = screen.Frame();
1532
1533		float hDivider = frame.Width() / 6;
1534		hDivider = (hDivider < sMinimumWindowWidth + 10.0f)
1535			? sMinimumWindowWidth + 10.0f : hDivider;
1536		float miniDivider = frame.top + kMiniHeight + 10.0f;
1537		float vDivider = frame.Height() / 2;
1538#ifdef FULL_MODE
1539		float thirdScreen = frame.Height() / 3;
1540#endif
1541		BRect topLeft(frame.left, frame.top, frame.left + hDivider,
1542			miniDivider);
1543		BRect topMiddle(frame.left + hDivider, frame.top, frame.right
1544			- hDivider, vDivider);
1545		BRect topRight(frame.right - hDivider, frame.top, frame.right,
1546			miniDivider);
1547
1548#ifdef FULL_MODE
1549		vDivider = miniDivider + thirdScreen;
1550#endif
1551		BRect middleLeft(frame.left, miniDivider, frame.left + hDivider,
1552			vDivider);
1553		BRect middleRight(frame.right - hDivider, miniDivider, frame.right,
1554			vDivider);
1555
1556#ifdef FULL_MODE
1557		BRect leftSide(frame.left, vDivider, frame.left + hDivider,
1558			frame.bottom - thirdScreen);
1559		BRect rightSide(frame.right - hDivider, vDivider, frame.right,
1560			frame.bottom - thirdScreen);
1561
1562		vDivider = frame.bottom - thirdScreen;
1563#endif
1564		BRect bottomLeft(frame.left, vDivider, frame.left + hDivider,
1565			frame.bottom);
1566		BRect bottomMiddle(frame.left + hDivider, vDivider, frame.right
1567			- hDivider, frame.bottom);
1568		BRect bottomRight(frame.right - hDivider, vDivider, frame.right,
1569			frame.bottom);
1570
1571		if (where != fPreviousPosition) {
1572			fPreviousPosition = where;
1573			ConvertToScreen(&where);
1574
1575			// use short circuit evaluation for convenience
1576			if (SwitchModeForRect(where, topLeft, true, true, true, kMiniState)
1577				|| SwitchModeForRect(where, topMiddle, false, true, true,
1578					kExpandoState)
1579				|| SwitchModeForRect(where, topRight, true, false, true,
1580					kMiniState)
1581				|| SwitchModeForRect(where, middleLeft, true, true, true,
1582					kExpandoState)
1583				|| SwitchModeForRect(where, middleRight, true, false, true,
1584					kExpandoState)
1585
1586#ifdef FULL_MODE
1587				|| SwitchModeForRect(where, leftSide, true, true, true,
1588					kFullState)
1589				|| SwitchModeForRect(where, rightSide, true, false, true,
1590					kFullState)
1591#endif
1592				|| SwitchModeForRect(where, bottomLeft, true, true, false,
1593					kMiniState)
1594				|| SwitchModeForRect(where, bottomMiddle, false, true, false,
1595					kExpandoState)
1596				|| SwitchModeForRect(where, bottomRight, true, false, false,
1597					kMiniState))
1598				;
1599		}
1600	} else
1601		BControl::MouseMoved(where, code, message);
1602}
1603
1604
1605int32
1606TDragRegion::DragRegionLocation() const
1607{
1608	return fDragLocation;
1609}
1610
1611
1612void
1613TDragRegion::SetDragRegionLocation(int32 location)
1614{
1615	if (location == fDragLocation)
1616		return;
1617
1618	fDragLocation = location;
1619	Invalidate();
1620}
1621