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