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