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