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