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