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