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