xref: /haiku/src/apps/deskbar/StatusView.cpp (revision 0e50eab75e25d0d82090e22dbff766dfaa6f5e86)
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 	// find entry_ref
1029 
1030 	entry_ref ref;
1031 	if (addOn) {
1032 		// Use it if we got it
1033 		ref = *addOn;
1034 	} else {
1035 		const char *signature;
1036 		status_t status = icon->FindString("add_on", &signature);
1037 		if (status == B_OK) {
1038 			BRoster roster;
1039 			status = roster.FindApp(signature, &ref);
1040 		}
1041 		if (status < B_OK)
1042 			return status;
1043 	}
1044 
1045 	BFile file;
1046 	status_t status = file.SetTo(&ref, B_READ_ONLY);
1047 	if (status < B_OK)
1048 		return status;
1049 
1050 	node_ref nodeRef;
1051 	status = file.GetNodeRef(&nodeRef);
1052 	if (status < B_OK)
1053 		return status;
1054 
1055 	BEntry entry(&ref, true);
1056 		// ToDo: this resolves an eventual link for the item
1057 		// being added - this is okay for now, but in multi-user
1058 		// environments, one might want to have links that
1059 		// carry the be:deskbar_item_status attribute
1060 	status = entry.InitCheck();
1061 	if (status != B_OK)
1062 		return status;
1063 
1064 	*id = 999;
1065 	if (icon->what == B_ARCHIVED_OBJECT)
1066 		icon->what = 0;
1067 
1068 	// !! check for name collisions?
1069 	status = fShelf->AddReplicant(icon, BPoint(1, 1));
1070 	if (status != B_OK)
1071 		return status;
1072 
1073 	float oldWidth = Bounds().Width();
1074 	float oldHeight = Bounds().Height();
1075 	float width, height;
1076 	GetPreferredSize(&width, &height);
1077 	if (oldWidth != width || oldHeight != height)
1078 		AdjustPlacement();
1079 
1080 	int32 count = fShelf->CountReplicants();
1081 	BView *view;
1082 	fShelf->ReplicantAt(count-1, &view, (uint32 *)id, NULL);
1083 
1084 	// add the item to the add-on list
1085 
1086 	AddItem(*id, nodeRef, entry, addOn != NULL);
1087  	return B_OK;
1088 }
1089 
1090 
1091 void
1092 TReplicantTray::RemoveIcon(int32 target, bool byIndex)
1093 {
1094 	if (target < 0)
1095 		return;
1096 
1097 	int32 index, id;
1098 	BView *view = ViewAt(&index, &id, target, byIndex);
1099 	if (view && index >= 0) {
1100 		// remove the reference from the item list & the shelf
1101 		RemoveItem(id);
1102 		fShelf->DeleteReplicant(index);
1103 
1104 		// force a placement update,  !! need to fix BShelf
1105 		RealReplicantAdjustment(index);
1106 	}
1107 }
1108 
1109 
1110 void
1111 TReplicantTray::RemoveIcon(const char *name)
1112 {
1113 	if (!name || strlen(name) <= 0)
1114 		return;
1115 
1116 	int32 id, index;
1117 	BView *view = ViewAt(&index, &id, name);
1118 	if (view && index >= 0) {
1119 		// remove the reference from the item list & shelf
1120 		RemoveItem(id);
1121 		fShelf->DeleteReplicant(index);
1122 
1123 		// force a placement update,  !! need to fix BShelf
1124 		RealReplicantAdjustment(index);
1125 	}
1126 }
1127 
1128 
1129 void
1130 TReplicantTray::RealReplicantAdjustment(int32 startIndex)
1131 {
1132 	if (startIndex < 0)
1133 		return;
1134 
1135 	if (startIndex == fLastReplicant)
1136 		startIndex = 0;
1137 
1138 	// reset the locations of all replicants after the one deleted
1139 	RealignReplicants(startIndex);
1140 
1141 	float oldWidth = Bounds().Width();
1142 	float oldHeight = Bounds().Height();
1143 	float width, height;
1144 	GetPreferredSize(&width, &height);
1145 	if (oldWidth != width || oldHeight != height) {
1146 		// resize view to accomodate the replicants
1147 		// redraw as necessary
1148 		AdjustPlacement();
1149 	}
1150 }
1151 
1152 
1153 /**	looking for a replicant by id/index
1154  *	return the view and index
1155  */
1156 
1157 BView *
1158 TReplicantTray::ViewAt(int32 *index, int32 *id, int32 target, bool byIndex)
1159 {
1160 	*index = -1;
1161 
1162 	BView *view;
1163 	if (byIndex){
1164 		if (fShelf->ReplicantAt(target, &view, (uint32 *)id)) {
1165 			if (view) {
1166 				*index = target;
1167 				return view;
1168 			}
1169 		}
1170 	} else {
1171 		int32 count = fShelf->CountReplicants()-1;
1172 		int32 localid;
1173 		for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) {
1174 			fShelf->ReplicantAt(repIndex, &view, (uint32 *)&localid);
1175 			if (localid == target && view) {
1176 				*index = repIndex;
1177 				*id = localid;
1178 				return view;
1179 			}
1180 		}
1181 	}
1182 
1183 	return NULL;
1184 }
1185 
1186 
1187 /**	looking for a replicant with a view by name
1188  *	return the view, index and the id of the replicant
1189  */
1190 
1191 BView *
1192 TReplicantTray::ViewAt(int32 *index, int32 *id, const char *name)
1193 {
1194 	*index = -1;
1195 	*id = -1;
1196 
1197 	BView *view;
1198 	int32 count = fShelf->CountReplicants()-1;
1199 	for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) {
1200 		fShelf->ReplicantAt(repIndex, &view, (uint32 *)id);
1201 		if (view && view->Name() && strcmp(name, view->Name()) == 0) {
1202 			*index = repIndex;
1203 			return view;
1204 		}
1205 	}
1206 
1207 	return NULL;
1208 }
1209 
1210 
1211 /**	Shelf will call to determine where and if
1212  *	the replicant is to be added
1213  */
1214 
1215 bool
1216 TReplicantTray::AcceptAddon(BRect replicantFrame, BMessage *message)
1217 {
1218 	if (!message)
1219 		return false;
1220 
1221 	if (replicantFrame.Height() > kMaxReplicantHeight)
1222 		return false;
1223 
1224 	alignment align = B_ALIGN_LEFT;
1225 	if (fAlignmentSupport && message->HasBool("deskbar:dynamic_align")) {
1226 		if (!fBarView->Vertical())
1227 			align = B_ALIGN_RIGHT;
1228 		else
1229 			align = fBarView->Left() ? B_ALIGN_LEFT : B_ALIGN_RIGHT;
1230 	} else if (message->HasInt32("deskbar:align"))
1231 		message->FindInt32("deskbar:align", (int32 *)&align);
1232 
1233 	if (message->HasInt32("deskbar:private_align"))
1234 		message->FindInt32("deskbar:private_align", (int32 *)&align);
1235 	else
1236 		align = B_ALIGN_LEFT;
1237 
1238 	BPoint loc = LocationForReplicant(fShelf->CountReplicants(),
1239 		replicantFrame.Width());
1240 
1241 	message->AddPoint("_pjp_loc", loc);
1242 	return true;
1243 }
1244 
1245 
1246 /**	based on the previous (index - 1) replicant in the list
1247  *	calculate where the left point should be for this
1248  *	replicant.  replicant will flow to the right on its own
1249  */
1250 
1251 BPoint
1252 TReplicantTray::LocationForReplicant(int32 index, float width)
1253 {
1254 	BPoint loc(kIconGap + 1, kGutter + 1);
1255 
1256 	if (fMultiRowMode) {
1257 		// try to find free space in every row
1258 		for (int32 row = 0; ; loc.y += kMaxReplicantHeight + kIconGap, row++) {
1259 			// determine free space in this row
1260 			BRect rect(loc.x, loc.y, loc.x + kMinimumTrayWidth + 2, kMaxReplicantHeight);
1261 			if (row == 0 && fBarView->ShowingClock())
1262 				rect.right -= fClock->Frame().Width() + kIconGap;
1263 
1264 			for (int32 i = 0; i < index; i++) {
1265 				BView *view = NULL;
1266 				fShelf->ReplicantAt(i, &view);
1267 				if (view == NULL || view->Frame().top != rect.top)
1268 					continue;
1269 
1270 				rect.left = view->Frame().right + kIconGap + 1;
1271 			}
1272 
1273 			if (rect.Width() >= width) {
1274 				// the icon fits in this row
1275 				loc = rect.LeftTop();
1276 				break;
1277 			}
1278 		}
1279 	} else {
1280 		if (index > 0) {
1281 			// get the last replicant added for placement reference
1282 			BView *view = NULL;
1283 			fShelf->ReplicantAt(index - 1, &view);
1284 			if (view) {
1285 				// push this rep placement past the last one
1286 				loc.x = view->Frame().right + kIconGap + 1;
1287 				loc.y = view->Frame().top;
1288 			}
1289 		}
1290 	}
1291 
1292 	if ((loc.y == fRightBottomReplicant.top && loc.x > fRightBottomReplicant.left)
1293 		|| loc.y > fRightBottomReplicant.top) {
1294 		fRightBottomReplicant.Set(loc.x, loc.y, loc.x + width, loc.y + kMaxReplicantHeight);
1295 		fLastReplicant = index;
1296 	}
1297 
1298 	return loc;
1299 }
1300 
1301 
1302 BRect
1303 TReplicantTray::IconFrame(int32 target, bool byIndex)
1304 {
1305 	int32 index, id;
1306 	BView *view = ViewAt(&index, &id, target, byIndex);
1307 	if (view)
1308 		return view->Frame();
1309 
1310 	return BRect(0, 0, 0, 0);
1311 }
1312 
1313 
1314 BRect
1315 TReplicantTray::IconFrame(const char *name)
1316 {
1317 	if (!name)
1318 		return BRect(0, 0, 0, 0);
1319 
1320 	int32 id, index;
1321 	BView *view = ViewAt(&index, &id, name);
1322 	if (view)
1323 		return view->Frame();
1324 
1325 	return BRect(0, 0, 0, 0);
1326 }
1327 
1328 
1329 /**	Scan from the startIndex and reset the location
1330  *	as defined in LocationForReplicant()
1331  */
1332 
1333 void
1334 TReplicantTray::RealignReplicants(int32 startIndex)
1335 {
1336 	if (startIndex < 0)
1337 		startIndex = 0;
1338 
1339 	int32 count = fShelf->CountReplicants();
1340 	if (count <= 0)
1341 		return;
1342 
1343 	if (startIndex == 0)
1344 		fRightBottomReplicant.Set(0, 0, 0, 0);
1345 
1346 	BView *view = NULL;
1347 	for (int32 i = startIndex ; i < count ; i++){
1348 		fShelf->ReplicantAt(i, &view);
1349 		BPoint loc = LocationForReplicant(i, view->Frame().Width());
1350 		if (view && (view->Frame().LeftTop() != loc)) {
1351 			view->MoveTo(loc);
1352 		}
1353 	}
1354 }
1355 
1356 
1357 void
1358 TReplicantTray::SetMultiRow(bool state)
1359 {
1360 	fMultiRowMode = state;
1361 
1362 	// in multi-row state, we only want the short date
1363 
1364 	if (fClock != NULL)
1365 		fClock->AllowFullDate(!state);
1366 }
1367 
1368 
1369 //	#pragma mark -
1370 
1371 
1372 /**	draggable region that is asynchronous so that
1373  *	dragging does not block other activities
1374  */
1375 
1376 TDragRegion::TDragRegion(TBarView *parent, BView *child)
1377 	:	BControl(BRect(0, 0, 0, 0), "", "", NULL, B_FOLLOW_NONE,
1378 			B_WILL_DRAW | B_FRAME_EVENTS),
1379 		fBarView(parent),
1380 		fChild(child),
1381 		fDragLocation(kAutoPlaceDragRegion)
1382 {
1383 }
1384 
1385 
1386 void
1387 TDragRegion::AttachedToWindow()
1388 {
1389 	BView::AttachedToWindow();
1390 	SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
1391 	ResizeToPreferred();
1392 }
1393 
1394 
1395 void
1396 TDragRegion::GetPreferredSize(float *width, float *height)
1397 {
1398 	fChild->ResizeToPreferred();
1399 	*width = fChild->Bounds().Width();
1400 	*height = fChild->Bounds().Height();
1401 
1402 	if (fDragLocation != kNoDragRegion)
1403 		*width += 7;
1404 	else
1405 		*width += 6;
1406 
1407 	*height += 3;
1408 }
1409 
1410 
1411 void
1412 TDragRegion::FrameMoved(BPoint)
1413 {
1414 	if (fBarView->Left() && fBarView->Vertical() && fDragLocation != kNoDragRegion)
1415 		fChild->MoveTo(5,2);
1416 	else
1417 		fChild->MoveTo(2,2);
1418 }
1419 
1420 
1421 void
1422 TDragRegion::Draw(BRect)
1423 {
1424 	rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR);
1425 	rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
1426 	rgb_color vdark = tint_color(menuColor, B_DARKEN_3_TINT);
1427 	rgb_color vvdark = tint_color(menuColor, B_DARKEN_4_TINT);
1428 	rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT);
1429 
1430 	BRect frame(Bounds());
1431 	BeginLineArray(4);
1432 
1433 	if (fBarView->Vertical()) {
1434 		AddLine(frame.LeftTop(), frame.RightTop(), light);
1435 		AddLine(frame.LeftTop(), frame.LeftBottom(), light);
1436 		AddLine(frame.RightBottom(), frame.RightTop(), hilite);
1437 	} else if (fBarView->AcrossTop()) {
1438 		AddLine(frame.LeftTop()+BPoint(0, 1), frame.RightTop()+BPoint(-1, 1),
1439 			light);
1440 		AddLine(frame.RightTop(), frame.RightBottom(), vvdark);
1441 		AddLine(frame.RightTop()+BPoint(-1, 2),frame.RightBottom()+BPoint(-1, -1),
1442 			hilite);
1443 		AddLine(frame.LeftBottom(), frame.RightBottom()+BPoint(-1, 0), hilite);
1444 	} else if (fBarView->AcrossBottom()) {
1445 		AddLine(frame.LeftTop()+BPoint(0, 1), frame.RightTop()+BPoint(-1, 1), light);
1446 		AddLine(frame.LeftBottom(), frame.RightBottom(), hilite);
1447 		AddLine(frame.RightTop(), frame.RightBottom(), vvdark);
1448 		AddLine(frame.RightTop()+BPoint(-1, 1),frame.RightBottom()+BPoint(-1, -1),
1449 			hilite);
1450 	}
1451 
1452 	EndLineArray();
1453 
1454 	if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion)
1455 		DrawDragRegion();
1456 }
1457 
1458 
1459 void
1460 TDragRegion::DrawDragRegion()
1461 {
1462 	rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR);
1463 	rgb_color menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT);
1464 	rgb_color vdark = tint_color(menuColor, B_DARKEN_3_TINT);
1465 	rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT);
1466 
1467 	BRect dragRegion(DragRegion());
1468 
1469 	BeginLineArray(dragRegion.IntegerHeight());
1470 	BPoint pt = dragRegion.LeftTop() + BPoint(1,1);
1471 
1472 	// Draw drag region highlighted if tracking mouse
1473 	if (IsTracking()) {
1474 		SetHighColor(menuHilite);
1475 		FillRect(dragRegion);
1476 		while (pt.y + 1 <= dragRegion.bottom) {
1477 			AddLine(pt, pt, light);
1478 			AddLine(pt+BPoint(1,1), pt+BPoint(1,1), vdark);
1479 
1480 			pt.y += 3;
1481 		}
1482 	} else {
1483 		while (pt.y + 1 <= dragRegion.bottom) {
1484 			AddLine(pt, pt, vdark);
1485 			AddLine(pt+BPoint(1,1), pt+BPoint(1,1), light);
1486 
1487 			pt.y += 3;
1488 		}
1489 	}
1490 	EndLineArray();
1491 }
1492 
1493 
1494 BRect
1495 TDragRegion::DragRegion() const
1496 {
1497 	BRect dragRegion(Bounds());
1498 	dragRegion.top += 2;
1499 	dragRegion.bottom -= 2;
1500 
1501 	bool placeOnLeft=false;
1502 	if (fDragLocation == kAutoPlaceDragRegion) {
1503 		if (fBarView->Vertical() && fBarView->Left())
1504 			placeOnLeft = true;
1505 		else
1506 			placeOnLeft = false;
1507 	} else if (fDragLocation == kDragRegionLeft)
1508 		placeOnLeft = true;
1509 	else if (fDragLocation == kDragRegionRight)
1510 		placeOnLeft = false;
1511 
1512 	if (placeOnLeft) {
1513 		dragRegion.left += 1;
1514 		dragRegion.right = dragRegion.left + 3;
1515 	} else {
1516 		dragRegion.right -= 1;
1517 		dragRegion.left = dragRegion.right - 3;
1518 	}
1519 
1520 	return dragRegion;
1521 }
1522 
1523 
1524 void
1525 TDragRegion::MouseDown(BPoint thePoint)
1526 {
1527 	ulong buttons;
1528 	BPoint where;
1529 	BRect dragRegion(DragRegion());
1530 
1531 	dragRegion.InsetBy(-2.0f, -2.0f);
1532 		// DragRegion() is designed for drawing, not clicking
1533 
1534 	if (!dragRegion.Contains(thePoint))
1535 		return;
1536 
1537 	while(true) {
1538 		GetMouse(&where, &buttons);
1539 		if (!buttons)
1540 			break;
1541 
1542 		if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) {
1543 			fPreviousPosition = thePoint;
1544 			SetTracking(true);
1545 			SetMouseEventMask(B_POINTER_EVENTS,
1546 				B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
1547 			Invalidate(DragRegion());
1548 			break;
1549 		}
1550 
1551 		snooze(25000);
1552 	}
1553 }
1554 
1555 
1556 void
1557 TDragRegion::MouseUp(BPoint pt)
1558 {
1559 	if (IsTracking()) {
1560 		SetTracking(false);
1561 		Invalidate(DragRegion());
1562 	} else
1563 		BControl::MouseUp(pt);
1564 }
1565 
1566 
1567 bool
1568 TDragRegion::SwitchModeForRect(BPoint mouse, BRect rect,
1569 	bool newVertical, bool newLeft, bool newTop, int32 newState)
1570 {
1571 	if (!rect.Contains(mouse))
1572 		// not our rect
1573 		return false;
1574 
1575 	if (newVertical == fBarView->Vertical()
1576 		&& newLeft == fBarView->Left()
1577 		&& newTop == fBarView->Top()
1578 		&& newState == fBarView->State())
1579 		// already in the correct mode
1580 		return true;
1581 
1582 	fBarView->ChangeState(newState, newVertical, newLeft, newTop);
1583 	return true;
1584 }
1585 
1586 
1587 void
1588 TDragRegion::MouseMoved(BPoint where, uint32 code, const BMessage *message)
1589 {
1590 	if (IsTracking()) {
1591 		BScreen screen;
1592 		BRect frame = screen.Frame();
1593 
1594 		float hDivider = frame.Width() / 6;
1595 		hDivider = (hDivider < kMinimumWindowWidth + 10.0f) ? kMinimumWindowWidth + 10.0f : hDivider;
1596 		float miniDivider = frame.top + kMiniHeight + 10.0f;
1597 		float vDivider = frame.Height() / 2;
1598 #ifdef FULL_MODE
1599 		float thirdScreen = frame.Height() / 3;
1600 #endif
1601 		BRect topLeft(frame.left, frame.top, frame.left + hDivider, miniDivider);
1602 		BRect topMiddle(frame.left + hDivider, frame.top, frame.right - hDivider, vDivider);
1603 		BRect topRight(frame.right - hDivider, frame.top, frame.right, miniDivider);
1604 
1605 #ifdef FULL_MODE
1606 		vDivider = miniDivider + thirdScreen;
1607 #endif
1608 		BRect middleLeft(frame.left, miniDivider, frame.left + hDivider, vDivider);
1609 		BRect middleRight(frame.right - hDivider, miniDivider, frame.right, vDivider);
1610 
1611 #ifdef FULL_MODE
1612 		BRect leftSide(frame.left, vDivider, frame.left + hDivider, frame.bottom - thirdScreen);
1613 		BRect rightSide(frame.right - hDivider, vDivider, frame.right, frame.bottom - thirdScreen);
1614 
1615 		vDivider = frame.bottom - thirdScreen;
1616 #endif
1617 		BRect bottomLeft(frame.left, vDivider, frame.left + hDivider, frame.bottom);
1618 		BRect bottomMiddle(frame.left + hDivider, vDivider, frame.right - hDivider, frame.bottom);
1619 		BRect bottomRight(frame.right - hDivider, vDivider, frame.right, frame.bottom);
1620 
1621 		if (where != fPreviousPosition) {
1622 			fPreviousPosition = where;
1623 			ConvertToScreen(&where);
1624 
1625 			// use short circuit evaluation for convenience
1626 			if (SwitchModeForRect(where, topLeft, true, true, true, kMiniState)
1627 				|| SwitchModeForRect(where, topMiddle, false, true, true, kExpandoState)
1628 				|| SwitchModeForRect(where, topRight, true, false, true, kMiniState)
1629 
1630 				|| SwitchModeForRect(where, middleLeft, true, true, true, kExpandoState)
1631 				|| SwitchModeForRect(where, middleRight, true, false, true, kExpandoState)
1632 
1633 #ifdef FULL_MODE
1634 				|| SwitchModeForRect(where, leftSide, true, true, true, kFullState)
1635 				|| SwitchModeForRect(where, rightSide, true, false, true, kFullState)
1636 #endif
1637 				|| SwitchModeForRect(where, bottomLeft, true, true, false, kMiniState)
1638 				|| SwitchModeForRect(where, bottomMiddle, false, true, false, kExpandoState)
1639 				|| SwitchModeForRect(where, bottomRight, true, false, false, kMiniState))
1640 				;
1641 		}
1642 	} else
1643 		BControl::MouseMoved(where, code, message);
1644 }
1645 
1646 
1647 int32
1648 TDragRegion::DragRegionLocation() const
1649 {
1650 	return fDragLocation;
1651 }
1652 
1653 
1654 void
1655 TDragRegion::SetDragRegionLocation(int32 location)
1656 {
1657 	if (location == fDragLocation)
1658 		return;
1659 
1660 	fDragLocation = location;
1661 	Invalidate();
1662 }
1663 
1664