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