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