xref: /haiku/src/kits/tracker/ContainerWindow.cpp (revision 5f4f984a94d150153bcb00a2ed780d0437859543)
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 trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 
36 #include "ContainerWindow.h"
37 
38 #include <Alert.h>
39 #include <Application.h>
40 #include <AppFileInfo.h>
41 #include <Catalog.h>
42 #include <ControlLook.h>
43 #include <Debug.h>
44 #include <Directory.h>
45 #include <Entry.h>
46 #include <FindDirectory.h>
47 #include <GridView.h>
48 #include <GroupLayout.h>
49 #include <Keymap.h>
50 #include <Locale.h>
51 #include <MenuItem.h>
52 #include <MenuBar.h>
53 #include <NodeMonitor.h>
54 #include <Path.h>
55 #include <PopUpMenu.h>
56 #include <Roster.h>
57 #include <Screen.h>
58 #include <UnicodeChar.h>
59 #include <Volume.h>
60 #include <VolumeRoster.h>
61 #include <WindowPrivate.h>
62 
63 #include <fs_attr.h>
64 #include <image.h>
65 #include <strings.h>
66 #include <stdlib.h>
67 
68 #include <algorithm>
69 #include <memory>
70 
71 #include "Attributes.h"
72 #include "AttributeStream.h"
73 #include "AutoLock.h"
74 #include "BackgroundImage.h"
75 #include "Commands.h"
76 #include "CountView.h"
77 #include "DeskWindow.h"
78 #include "FavoritesMenu.h"
79 #include "FindPanel.h"
80 #include "FSClipboard.h"
81 #include "FSUndoRedo.h"
82 #include "FSUtils.h"
83 #include "IconMenuItem.h"
84 #include "OpenWithWindow.h"
85 #include "MimeTypes.h"
86 #include "MountMenu.h"
87 #include "Navigator.h"
88 #include "NavMenu.h"
89 #include "PoseView.h"
90 #include "QueryContainerWindow.h"
91 #include "SelectionWindow.h"
92 #include "TitleView.h"
93 #include "Tracker.h"
94 #include "TrackerSettings.h"
95 #include "Thread.h"
96 #include "TemplatesMenu.h"
97 
98 
99 #undef B_TRANSLATION_CONTEXT
100 #define B_TRANSLATION_CONTEXT "ContainerWindow"
101 
102 
103 #ifdef _IMPEXP_BE
104 _IMPEXP_BE
105 #endif
106 void do_minimize_team(BRect zoomRect, team_id team, bool zoom);
107 
108 
109 // Amount you have to move the mouse before a drag starts
110 const float kDragSlop = 3.0f;
111 
112 
113 namespace BPrivate {
114 
115 class DraggableContainerIcon : public BView {
116 	public:
117 		DraggableContainerIcon();
118 
119 		virtual void MouseDown(BPoint where);
120 		virtual void MouseUp(BPoint);
121 		virtual void MouseMoved(BPoint point, uint32, const BMessage*);
122 		virtual void Draw(BRect updateRect);
123 
124 	private:
125 		uint32	fDragButton;
126 		BPoint	fClickPoint;
127 		bool	fDragStarted;
128 };
129 
130 }	// namespace BPrivate
131 
132 
133 struct AddOneAddonParams {
134 	BObjectList<BMenuItem>* primaryList;
135 	BObjectList<BMenuItem>* secondaryList;
136 };
137 
138 struct StaggerOneParams {
139 	bool rectFromParent;
140 };
141 
142 
143 const int32 kWindowStaggerBy = 17;
144 
145 
146 BRect BContainerWindow::sNewWindRect(85, 50, 548, 280);
147 
148 LockingList<AddonShortcut>* BContainerWindow::fAddonsList
149 	= new LockingList<struct AddonShortcut>(10, true);
150 
151 
152 namespace BPrivate {
153 
154 filter_result
155 ActivateWindowFilter(BMessage*, BHandler** target, BMessageFilter*)
156 {
157 	BView* view = dynamic_cast<BView*>(*target);
158 
159 	// activate the window if no PoseView or DraggableContainerIcon had been
160 	// pressed (those will activate the window themselves, if necessary)
161 	if (view != NULL
162 		&& dynamic_cast<BPoseView*>(view) == NULL
163 		&& dynamic_cast<DraggableContainerIcon*>(view) == NULL
164 		&& view->Window() != NULL) {
165 		view->Window()->Activate(true);
166 	}
167 
168 	return B_DISPATCH_MESSAGE;
169 }
170 
171 
172 int
173 CompareLabels(const BMenuItem* item1, const BMenuItem* item2)
174 {
175 	return strcasecmp(item1->Label(), item2->Label());
176 }
177 
178 }	// namespace BPrivate
179 
180 
181 static bool
182 AddOneAddon(const Model* model, const char* name, uint32 shortcut,
183 	uint32 modifiers, bool primary, void* context)
184 {
185 	AddOneAddonParams* params = (AddOneAddonParams*)context;
186 
187 	BMessage* message = new BMessage(kLoadAddOn);
188 	message->AddRef("refs", model->EntryRef());
189 
190 	ModelMenuItem* item = new ModelMenuItem(model, name, message,
191 		(char)shortcut, modifiers);
192 
193 	if (primary)
194 		params->primaryList->AddItem(item);
195 	else
196 		params->secondaryList->AddItem(item);
197 
198 	return false;
199 }
200 
201 
202 static int32
203 AddOnThread(BMessage* refsMessage, entry_ref addonRef, entry_ref directoryRef)
204 {
205 	std::auto_ptr<BMessage> refsMessagePtr(refsMessage);
206 
207 	BEntry entry(&addonRef);
208 	BPath path;
209 	status_t result = entry.InitCheck();
210 	if (result == B_OK)
211 		result = entry.GetPath(&path);
212 
213 	if (result == B_OK) {
214 		image_id addonImage = load_add_on(path.Path());
215 		if (addonImage >= 0) {
216 			void (*processRefs)(entry_ref, BMessage*, void*);
217 			result = get_image_symbol(addonImage, "process_refs", 2,
218 				(void**)&processRefs);
219 
220 			if (result >= 0) {
221 				// call add-on code
222 				(*processRefs)(directoryRef, refsMessagePtr.get(), NULL);
223 
224 				unload_add_on(addonImage);
225 				return B_OK;
226 			} else
227 				PRINT(("couldn't find process_refs\n"));
228 
229 			unload_add_on(addonImage);
230 		} else
231 			result = addonImage;
232 	}
233 
234 	BString buffer(B_TRANSLATE("Error %error loading Add-On %name."));
235 	buffer.ReplaceFirst("%error", strerror(result));
236 	buffer.ReplaceFirst("%name", addonRef.name);
237 
238 	BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
239 		0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
240 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
241 	alert->Go();
242 
243 	return result;
244 }
245 
246 
247 static bool
248 NodeHasSavedState(const BNode* node)
249 {
250 	attr_info info;
251 	return node->GetAttrInfo(kAttrWindowFrame, &info) == B_OK;
252 }
253 
254 
255 static bool
256 OffsetFrameOne(const char* DEBUG_ONLY(name), uint32, off_t, void* castToRect,
257 	void* castToParams)
258 {
259 	ASSERT(strcmp(name, kAttrWindowFrame) == 0);
260 	StaggerOneParams* params = (StaggerOneParams*)castToParams;
261 
262 	if (!params->rectFromParent)
263 		return false;
264 
265 	if (!castToRect)
266 		return false;
267 
268 	((BRect*)castToRect)->OffsetBy(kWindowStaggerBy, kWindowStaggerBy);
269 
270 	return true;
271 }
272 
273 
274 static void
275 AddMimeTypeString(BStringList& list, Model* model)
276 {
277 	if (model == NULL)
278 		return;
279 
280 	const char* modelMimeType = model->MimeType();
281 	if (modelMimeType == NULL || *modelMimeType == '\0')
282 		return;
283 
284 	BString type = BString(modelMimeType);
285 	if (list.HasString(type, true))
286 		return;
287 
288 	list.Add(type);
289 }
290 
291 
292 //	#pragma mark - DraggableContainerIcon
293 
294 
295 DraggableContainerIcon::DraggableContainerIcon()
296 	:
297 	BView("DraggableContainerIcon", B_WILL_DRAW),
298 	fDragButton(0),
299 	fDragStarted(false)
300 {
301 }
302 
303 
304 void
305 DraggableContainerIcon::MouseDown(BPoint where)
306 {
307 	// we only like container windows
308 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
309 	ThrowOnAssert(window != NULL);
310 
311 	// we don't like the Trash icon (because it cannot be moved)
312 	if (window->IsTrash() || window->IsPrintersDir())
313 		return;
314 
315 	uint32 buttons;
316 	window->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
317 
318 	if (IconCache::sIconCache->IconHitTest(where, window->TargetModel(),
319 			kNormalIcon, B_MINI_ICON)) {
320 		// The click hit the icon, initiate a drag
321 		fDragButton = buttons
322 			& (B_PRIMARY_MOUSE_BUTTON | B_SECONDARY_MOUSE_BUTTON);
323 		fDragStarted = false;
324 		fClickPoint = where;
325 	} else
326 		fDragButton = 0;
327 
328 	if (!fDragButton)
329 		Window()->Activate(true);
330 }
331 
332 
333 void
334 DraggableContainerIcon::MouseUp(BPoint)
335 {
336 	if (!fDragStarted)
337 		Window()->Activate(true);
338 
339 	fDragButton = 0;
340 	fDragStarted = false;
341 }
342 
343 
344 void
345 DraggableContainerIcon::MouseMoved(BPoint where, uint32, const BMessage*)
346 {
347 	if (fDragButton == 0 || fDragStarted
348 		|| (abs((int32)(where.x - fClickPoint.x)) <= kDragSlop
349 			&& abs((int32)(where.y - fClickPoint.y)) <= kDragSlop))
350 		return;
351 
352 	BContainerWindow* window = static_cast<BContainerWindow*>(Window());
353 		// we can only get here in a BContainerWindow
354 	Model* model = window->TargetModel();
355 
356 	// Find the required height
357 	BFont font;
358 	GetFont(&font);
359 
360 	font_height fontHeight;
361 	font.GetHeight(&fontHeight);
362 	float height = ceilf(fontHeight.ascent + fontHeight.descent
363 		+ fontHeight.leading + 2 + Bounds().Height() + 8);
364 
365 	BRect rect(0, 0, std::max(Bounds().Width(),
366 		font.StringWidth(model->Name()) + 4), height);
367 	BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true);
368 
369 	dragBitmap->Lock();
370 	BView* view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
371 	dragBitmap->AddChild(view);
372 	view->SetOrigin(0, 0);
373 	BRect clipRect(view->Bounds());
374 	BRegion newClip;
375 	newClip.Set(clipRect);
376 	view->ConstrainClippingRegion(&newClip);
377 
378 	// Transparent draw magic
379 	view->SetHighColor(0, 0, 0, 0);
380 	view->FillRect(view->Bounds());
381 	view->SetDrawingMode(B_OP_ALPHA);
382 
383 	rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
384 	textColor.alpha = 128;
385 		// set the level of transparency by value
386 	view->SetHighColor(textColor);
387 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
388 
389 	// Draw the icon
390 	float hIconOffset = (rect.Width() - Bounds().Width()) / 2;
391 	IconCache::sIconCache->Draw(model, view, BPoint(hIconOffset, 0),
392 		kNormalIcon, B_MINI_ICON, true);
393 
394 	// See if we need to truncate the string
395 	BString nameString = model->Name();
396 	if (view->StringWidth(model->Name()) > rect.Width())
397 		view->TruncateString(&nameString, B_TRUNCATE_END, rect.Width() - 5);
398 
399 	// Draw the label
400 	float leftText = (view->StringWidth(nameString.String())
401 		- Bounds().Width()) / 2;
402 	view->MovePenTo(BPoint(hIconOffset - leftText + 2, Bounds().Height()
403 		+ (fontHeight.ascent + 2)));
404 	view->DrawString(nameString.String());
405 
406 	view->Sync();
407 	dragBitmap->Unlock();
408 
409 	BMessage message(B_SIMPLE_DATA);
410 	message.AddRef("refs", model->EntryRef());
411 	message.AddPoint("click_pt", fClickPoint);
412 
413 	BPoint tmpLoc;
414 	uint32 button;
415 	GetMouse(&tmpLoc, &button);
416 	if (button)
417 		message.AddInt32("buttons", (int32)button);
418 
419 	if ((button & B_PRIMARY_MOUSE_BUTTON) != 0) {
420 		// add an action specifier to the message, so that it is not copied
421 		message.AddInt32("be:actions", (modifiers() & B_OPTION_KEY) != 0
422 			? B_COPY_TARGET : B_MOVE_TARGET);
423 	}
424 
425 	fDragStarted = true;
426 	fDragButton = 0;
427 
428 	DragMessage(&message, dragBitmap, B_OP_ALPHA,
429 		BPoint(fClickPoint.x + hIconOffset, fClickPoint.y), this);
430 }
431 
432 
433 void
434 DraggableContainerIcon::Draw(BRect updateRect)
435 {
436 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
437 	ThrowOnAssert(window != NULL);
438 
439 	BRect rect(Bounds());
440 	rgb_color base = ui_color(B_MENU_BACKGROUND_COLOR);
441 	be_control_look->DrawBorder(this, rect, updateRect, base, B_PLAIN_BORDER,
442 		0, BControlLook::B_BOTTOM_BORDER);
443 	be_control_look->DrawMenuBarBackground(this, rect, updateRect, base, 0,
444 		BControlLook::B_ALL_BORDERS & ~BControlLook::B_LEFT_BORDER);
445 
446 	// Draw the icon, straddling the border
447 	SetDrawingMode(B_OP_ALPHA);
448 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
449 	float iconOffsetX = (Bounds().Width() - B_MINI_ICON) / 2;
450 	float iconOffsetY = (Bounds().Height() - B_MINI_ICON) / 2;
451 	IconCache::sIconCache->Draw(window->TargetModel(), this,
452 		BPoint(iconOffsetX, iconOffsetY), kNormalIcon, B_MINI_ICON, true);
453 }
454 
455 
456 //	#pragma mark - BContainerWindow
457 
458 
459 BContainerWindow::BContainerWindow(LockingList<BWindow>* list,
460 	uint32 containerWindowFlags, window_look look, window_feel feel,
461 	uint32 flags, uint32 workspace, bool useLayouts, bool isDeskWindow)
462 	:
463 	BWindow(InitialWindowRect(feel), "TrackerWindow", look, feel, flags,
464 		workspace),
465 	fUseLayouts(useLayouts),
466 	fMenuContainer(NULL),
467 	fPoseContainer(NULL),
468 	fBorderedView(NULL),
469 	fVScrollBarContainer(NULL),
470 	fCountContainer(NULL),
471 	fFileContextMenu(NULL),
472 	fWindowContextMenu(NULL),
473 	fDropContextMenu(NULL),
474 	fVolumeContextMenu(NULL),
475 	fTrashContextMenu(NULL),
476 	fDragContextMenu(NULL),
477 	fMoveToItem(NULL),
478 	fCopyToItem(NULL),
479 	fCreateLinkItem(NULL),
480 	fOpenWithItem(NULL),
481 	fNavigationItem(NULL),
482 	fMenuBar(NULL),
483 	fDraggableIcon(NULL),
484 	fNavigator(NULL),
485 	fPoseView(NULL),
486 	fWindowList(list),
487 	fAttrMenu(NULL),
488 	fWindowMenu(NULL),
489 	fFileMenu(NULL),
490 	fArrangeByMenu(NULL),
491 	fSelectionWindow(NULL),
492 	fTaskLoop(NULL),
493 	fIsTrash(false),
494 	fInTrash(false),
495 	fIsPrinters(false),
496 	fIsDesktop(isDeskWindow),
497 	fContainerWindowFlags(containerWindowFlags),
498 	fBackgroundImage(NULL),
499 	fSavedZoomRect(0, 0, -1, -1),
500 	fContextMenu(NULL),
501 	fDragMessage(NULL),
502 	fCachedTypesList(NULL),
503 	fStateNeedsSaving(false),
504 	fSaveStateIsEnabled(true),
505 	fIsWatchingPath(false)
506 {
507 	InitIconPreloader();
508 
509 	if (list != NULL) {
510 		ASSERT(list->IsLocked());
511 		list->AddItem(this);
512 	}
513 
514 	if (useLayouts) {
515 		SetFlags(Flags() | B_AUTO_UPDATE_SIZE_LIMITS);
516 
517 		fRootLayout = new BGroupLayout(B_VERTICAL, 0);
518 		fRootLayout->SetInsets(0);
519 		SetLayout(fRootLayout);
520 		fRootLayout->Owner()->AdoptSystemColors();
521 
522 		if (!fIsDesktop) {
523 			fMenuContainer = new BGroupView(B_HORIZONTAL, 0);
524 			fRootLayout->AddView(fMenuContainer);
525 
526 			fPoseContainer = new BGridView(0.0, 0.0);
527 			fRootLayout->AddView(fPoseContainer);
528 
529 			fBorderedView = new BorderedView;
530 			fPoseContainer->GridLayout()->AddView(fBorderedView, 0, 1);
531 
532 			fCountContainer = new BGroupView(B_HORIZONTAL, 0);
533 			fPoseContainer->GridLayout()->AddView(fCountContainer, 0, 2);
534 		}
535 	}
536 
537 	AddCommonFilter(new BMessageFilter(B_MOUSE_DOWN, ActivateWindowFilter));
538 
539 	Run();
540 
541 	// watch out for settings changes
542 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
543 	if (tracker != NULL && tracker->Lock()) {
544 		tracker->StartWatching(this, kWindowsShowFullPathChanged);
545 		tracker->StartWatching(this, kSingleWindowBrowseChanged);
546 		tracker->StartWatching(this, kShowNavigatorChanged);
547 		tracker->StartWatching(this, kDontMoveFilesToTrashChanged);
548 		tracker->Unlock();
549 	}
550 
551 	// ToDo: remove me once we have undo/redo menu items
552 	// (that is, move them to AddShortcuts())
553 	AddShortcut('Z', B_COMMAND_KEY, new BMessage(B_UNDO), this);
554 	AddShortcut('Z', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO), this);
555 }
556 
557 
558 BContainerWindow::~BContainerWindow()
559 {
560 	ASSERT(IsLocked());
561 
562 	// stop the watchers
563 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
564 	if (tracker != NULL && tracker->Lock()) {
565 		tracker->StopWatching(this, kWindowsShowFullPathChanged);
566 		tracker->StopWatching(this, kSingleWindowBrowseChanged);
567 		tracker->StopWatching(this, kShowNavigatorChanged);
568 		tracker->StopWatching(this, kDontMoveFilesToTrashChanged);
569 		tracker->Unlock();
570 	}
571 
572 	delete fTaskLoop;
573 	delete fBackgroundImage;
574 	delete fDragMessage;
575 	delete fCachedTypesList;
576 
577 	if (fSelectionWindow != NULL && fSelectionWindow->Lock())
578 		fSelectionWindow->Quit();
579 }
580 
581 
582 BRect
583 BContainerWindow::InitialWindowRect(window_feel feel)
584 {
585 	if (feel != kDesktopWindowFeel)
586 		return sNewWindRect;
587 
588 	// do not offset desktop window
589 	BRect result = sNewWindRect;
590 	result.OffsetTo(0, 0);
591 	return result;
592 }
593 
594 
595 void
596 BContainerWindow::Minimize(bool minimize)
597 {
598 	if (minimize && (modifiers() & B_OPTION_KEY) != 0)
599 		do_minimize_team(BRect(0, 0, 0, 0), be_app->Team(), true);
600 	else
601 		_inherited::Minimize(minimize);
602 }
603 
604 
605 bool
606 BContainerWindow::QuitRequested()
607 {
608 	// this is a response to the DeskBar sending us a B_QUIT, when it really
609 	// means to say close all your windows. It might be better to have it
610 	// send a kCloseAllWindows message and have windowless apps stay running,
611 	// which is what we will do for the Tracker
612 	if (CurrentMessage() != NULL
613 		&& ((CurrentMessage()->FindInt32("modifiers") & B_CONTROL_KEY)) != 0) {
614 		be_app->PostMessage(kCloseAllWindows);
615 	}
616 
617 	Hide();
618 		// this will close the window instantly, even if
619 		// the file system is very busy right now
620 	return true;
621 }
622 
623 
624 void
625 BContainerWindow::Quit()
626 {
627 	// get rid of context menus
628 	if (fNavigationItem) {
629 		BMenu* menu = fNavigationItem->Menu();
630 		if (menu != NULL)
631 			menu->RemoveItem(fNavigationItem);
632 
633 		delete fNavigationItem;
634 		fNavigationItem = NULL;
635 	}
636 
637 	if (fOpenWithItem != NULL && fOpenWithItem->Menu() == NULL) {
638 		delete fOpenWithItem;
639 		fOpenWithItem = NULL;
640 	}
641 
642 	if (fMoveToItem != NULL && fMoveToItem->Menu() == NULL) {
643 		delete fMoveToItem;
644 		fMoveToItem = NULL;
645 	}
646 
647 	if (fCopyToItem != NULL && fCopyToItem->Menu() == NULL) {
648 		delete fCopyToItem;
649 		fCopyToItem = NULL;
650 	}
651 
652 	if (fCreateLinkItem != NULL && fCreateLinkItem->Menu() == NULL) {
653 		delete fCreateLinkItem;
654 		fCreateLinkItem = NULL;
655 	}
656 
657 	if (fAttrMenu != NULL && fAttrMenu->Supermenu() == NULL) {
658 		delete fAttrMenu;
659 		fAttrMenu = NULL;
660 	}
661 
662 	delete fFileContextMenu;
663 	fFileContextMenu = NULL;
664 
665 	delete fWindowContextMenu;
666 	fWindowContextMenu = NULL;
667 
668 	delete fDropContextMenu;
669 	fDropContextMenu = NULL;
670 
671 	delete fVolumeContextMenu;
672 	fVolumeContextMenu = NULL;
673 
674 	delete fDragContextMenu;
675 	fDragContextMenu = NULL;
676 
677 	int32 windowCount = 0;
678 
679 	// This is a deadlock code sequence - need to change this
680 	// to acquire the window list while this container window is unlocked
681 	if (fWindowList != NULL) {
682 		AutoLock<LockingList<BWindow> > lock(fWindowList);
683 		if (lock.IsLocked()) {
684 			fWindowList->RemoveItem(this, false);
685 			windowCount = fWindowList->CountItems();
686 		}
687 	}
688 
689 	if (StateNeedsSaving())
690 		SaveState();
691 
692 	if (fWindowList != NULL && windowCount == 0)
693 		be_app->PostMessage(B_QUIT_REQUESTED);
694 
695 	_inherited::Quit();
696 }
697 
698 
699 BPoseView*
700 BContainerWindow::NewPoseView(Model* model, uint32 viewMode)
701 {
702 	return new BPoseView(model, viewMode);
703 }
704 
705 
706 void
707 BContainerWindow::UpdateIfTrash(Model* model)
708 {
709 	BEntry entry(model->EntryRef());
710 
711 	if (entry.InitCheck() == B_OK) {
712 		fIsTrash = model->IsTrash();
713 		fInTrash = FSInTrashDir(model->EntryRef());
714 		fIsPrinters = FSIsPrintersDir(&entry);
715 	}
716 }
717 
718 
719 void
720 BContainerWindow::CreatePoseView(Model* model)
721 {
722 	UpdateIfTrash(model);
723 
724 	fPoseView = NewPoseView(model, kListMode);
725 	fBorderedView->GroupLayout()->AddView(fPoseView);
726 	fBorderedView->GroupLayout()->SetInsets(1, 0, 1, 1);
727 	fBorderedView->EnableBorderHighlight(false);
728 
729 	TrackerSettings settings;
730 	if (settings.SingleWindowBrowse() && model->IsDirectory()
731 		&& !fPoseView->IsFilePanel()) {
732 		fNavigator = new BNavigator(model);
733 		fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
734 		if (!settings.ShowNavigator())
735 			fNavigator->Hide();
736 	}
737 
738 	SetPathWatchingEnabled(settings.ShowNavigator()
739 		|| settings.ShowFullPathInTitleBar());
740 }
741 
742 
743 void
744 BContainerWindow::AddContextMenus()
745 {
746 	// create context sensitive menus
747 	fFileContextMenu = new BPopUpMenu("FileContext", false, false);
748 	fFileContextMenu->SetFont(be_plain_font);
749 	AddFileContextMenus(fFileContextMenu);
750 
751 	fVolumeContextMenu = new BPopUpMenu("VolumeContext", false, false);
752 	fVolumeContextMenu->SetFont(be_plain_font);
753 	AddVolumeContextMenus(fVolumeContextMenu);
754 
755 	fWindowContextMenu = new BPopUpMenu("WindowContext", false, false);
756 	fWindowContextMenu->SetFont(be_plain_font);
757 	AddWindowContextMenus(fWindowContextMenu);
758 
759 	fDropContextMenu = new BPopUpMenu("DropContext", false, false);
760 	fDropContextMenu->SetFont(be_plain_font);
761 	AddDropContextMenus(fDropContextMenu);
762 
763 	fDragContextMenu = new BSlowContextMenu("DragContext");
764 		// will get added and built dynamically in ShowContextMenu
765 
766 	fTrashContextMenu = new BPopUpMenu("TrashContext", false, false);
767 	fTrashContextMenu->SetFont(be_plain_font);
768 	AddTrashContextMenus(fTrashContextMenu);
769 }
770 
771 
772 void
773 BContainerWindow::RepopulateMenus()
774 {
775 	// Avoid these menus to be destroyed:
776 	if (fMoveToItem && fMoveToItem->Menu())
777 		fMoveToItem->Menu()->RemoveItem(fMoveToItem);
778 
779 	if (fCopyToItem && fCopyToItem->Menu())
780 		fCopyToItem->Menu()->RemoveItem(fCopyToItem);
781 
782 	if (fCreateLinkItem && fCreateLinkItem->Menu())
783 		fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem);
784 
785 	if (fOpenWithItem && fOpenWithItem->Menu()) {
786 		fOpenWithItem->Menu()->RemoveItem(fOpenWithItem);
787 		delete fOpenWithItem;
788 		fOpenWithItem = NULL;
789 	}
790 
791 	if (fNavigationItem) {
792 		BMenu* menu = fNavigationItem->Menu();
793 		if (menu) {
794 			menu->RemoveItem(fNavigationItem);
795 			BMenuItem* item = menu->RemoveItem((int32)0);
796 			ASSERT(item != fNavigationItem);
797 			delete item;
798 		}
799 	}
800 
801 	delete fFileContextMenu;
802 	fFileContextMenu = new BPopUpMenu("FileContext", false, false);
803 	fFileContextMenu->SetFont(be_plain_font);
804 	AddFileContextMenus(fFileContextMenu);
805 
806 	delete fWindowContextMenu;
807 	fWindowContextMenu = new BPopUpMenu("WindowContext", false, false);
808 	fWindowContextMenu->SetFont(be_plain_font);
809 	AddWindowContextMenus(fWindowContextMenu);
810 
811 	if (fMenuBar != NULL) {
812 		fMenuBar->RemoveItem(fFileMenu);
813 		delete fFileMenu;
814 		fFileMenu = new BMenu(B_TRANSLATE("File"));
815 		AddFileMenu(fFileMenu);
816 		fMenuBar->AddItem(fFileMenu);
817 
818 		fMenuBar->RemoveItem(fWindowMenu);
819 		delete fWindowMenu;
820 		fWindowMenu = new BMenu(B_TRANSLATE("Window"));
821 		fMenuBar->AddItem(fWindowMenu);
822 		AddWindowMenu(fWindowMenu);
823 
824 		// just create the attribute, decide to add it later
825 		fMenuBar->RemoveItem(fAttrMenu);
826 		delete fAttrMenu;
827 		fAttrMenu = new BMenu(B_TRANSLATE("Attributes"));
828 		NewAttributeMenu(fAttrMenu);
829 		if (PoseView()->ViewMode() == kListMode)
830 			ShowAttributeMenu();
831 
832 		PopulateArrangeByMenu(fArrangeByMenu);
833 
834 		int32 selectCount = PoseView()->SelectionList()->CountItems();
835 
836 		SetupOpenWithMenu(fFileMenu);
837 		SetupMoveCopyMenus(selectCount ? PoseView()->SelectionList()
838 				->FirstItem()->TargetModel()->EntryRef() : NULL,
839 			fFileMenu);
840 	}
841 }
842 
843 
844 void
845 BContainerWindow::Init(const BMessage* message)
846 {
847 	BEntry entry;
848 
849 	ASSERT(fPoseView != NULL);
850 	if (fPoseView == NULL)
851 		return;
852 
853 	// deal with new unconfigured folders
854 	if (NeedsDefaultStateSetup())
855 		SetUpDefaultState();
856 
857 	if (ShouldAddScrollBars())
858 		fPoseView->AddScrollBars();
859 
860 	fMoveToItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Move to"),
861 		kMoveSelectionTo, this));
862 	fCopyToItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Copy to"),
863 		kCopySelectionTo, this));
864 	fCreateLinkItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Create link"),
865 		kCreateLink, this), new BMessage(kCreateLink));
866 
867 	TrackerSettings settings;
868 
869 	if (ShouldAddMenus()) {
870 		fMenuBar = new BMenuBar("MenuBar");
871 		fMenuContainer->GroupLayout()->AddView(fMenuBar);
872 		AddMenus();
873 
874 		if (!TargetModel()->IsRoot() && !IsTrash())
875 			_AddFolderIcon();
876 	} else {
877 		// add equivalents of the menu shortcuts to the menuless
878 		// desktop window
879 		AddShortcuts();
880 	}
881 
882 	AddContextMenus();
883 	AddShortcut('T', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kDelete),
884 		PoseView());
885 	AddShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kCleanupAll),
886 		PoseView());
887 	AddShortcut('Q', B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY
888 		| B_CONTROL_KEY, new BMessage(kQuitTracker));
889 
890 	AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(kOpenSelection),
891 		PoseView());
892 
893 	SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
894 
895 #if DEBUG
896 	// add some debugging shortcuts
897 	AddShortcut('D', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dbug'),
898 		PoseView());
899 	AddShortcut('C', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpcc'),
900 		PoseView());
901 	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpfl'),
902 		PoseView());
903 	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY,
904 		new BMessage('dpfL'), PoseView());
905 #endif
906 
907 	BKeymap keymap;
908 	keymap.SetToCurrent();
909 	BObjectList<const char> unmodified(3, true);
910 	if (keymap.GetModifiedCharacters("+", B_SHIFT_KEY, 0, &unmodified)
911 			== B_OK) {
912 		int32 count = unmodified.CountItems();
913 		for (int32 i = 0; i < count; i++) {
914 			uint32 key = BUnicodeChar::FromUTF8(unmodified.ItemAt(i));
915 			if (!HasShortcut(key, 0)) {
916 				// Add semantic zoom in shortcut, bug #6692
917 				BMessage* increaseSize = new BMessage(kIconMode);
918 					increaseSize->AddInt32("scale", 1);
919 				AddShortcut(key, B_COMMAND_KEY, increaseSize, PoseView());
920 			}
921 		}
922 	}
923 	unmodified.MakeEmpty();
924 
925 	if (message != NULL)
926 		RestoreState(*message);
927 	else
928 		RestoreState();
929 
930 	if (ShouldAddMenus() && PoseView()->ViewMode() == kListMode) {
931 		// for now only show attributes in list view
932 		// eventually enable attribute menu to allow users to select
933 		// using different attributes as titles in icon view modes
934 		ShowAttributeMenu();
935 	}
936 	MarkAttributeMenu(fAttrMenu);
937 	CheckScreenIntersect();
938 
939 	if (fBackgroundImage != NULL && !fIsDesktop
940 		&& PoseView()->ViewMode() != kListMode) {
941 		fBackgroundImage->Show(PoseView(), current_workspace());
942 	}
943 
944 	Show();
945 
946 	// done showing, turn the B_NO_WORKSPACE_ACTIVATION flag off;
947 	// it was on to prevent workspace jerking during boot
948 	SetFlags(Flags() & ~B_NO_WORKSPACE_ACTIVATION);
949 }
950 
951 void
952 BContainerWindow::InitLayout()
953 {
954 	fBorderedView->GroupLayout()->AddView(0, fPoseView->TitleView());
955 
956 	BLayoutItem* item = fCountContainer->GroupLayout()->AddView(
957 		fPoseView->CountView());
958 	item->SetExplicitMinSize(BSize(kCountViewWidth, B_H_SCROLL_BAR_HEIGHT));
959 	item->SetExplicitMaxSize(BSize(kCountViewWidth, B_SIZE_UNSET));
960 
961 	// Eliminate the extra borders
962 	fMenuContainer->GroupLayout()->SetInsets(0, 0, -1, 0);
963 	fPoseContainer->GridLayout()->SetInsets(-1, 0, -1, -1);
964 	fCountContainer->GroupLayout()->SetInsets(0, -1, 0, 0);
965 
966 	if (fPoseView->VScrollBar() != NULL) {
967 		fVScrollBarContainer = new BGroupView(B_VERTICAL, 0);
968 		fVScrollBarContainer->GroupLayout()->AddView(fPoseView->VScrollBar());
969 		fVScrollBarContainer->GroupLayout()->SetInsets(-1, -1, 0, 0);
970 		fPoseContainer->GridLayout()->AddView(fVScrollBarContainer, 1, 1);
971 	}
972 	if (fPoseView->HScrollBar() != NULL) {
973 		BGroupView* hScrollBarContainer = new BGroupView(B_VERTICAL, 0);
974 		hScrollBarContainer->GroupLayout()->AddView(fPoseView->HScrollBar());
975 		hScrollBarContainer->GroupLayout()->SetInsets(0, -1, 0, -1);
976 		fCountContainer->GroupLayout()->AddView(hScrollBarContainer);
977 	}
978 }
979 
980 void
981 BContainerWindow::RestoreState()
982 {
983 	UpdateTitle();
984 
985 	WindowStateNodeOpener opener(this, false);
986 	RestoreWindowState(opener.StreamNode());
987 	fPoseView->Init(opener.StreamNode());
988 
989 	RestoreStateCommon();
990 }
991 
992 
993 void
994 BContainerWindow::RestoreState(const BMessage &message)
995 {
996 	UpdateTitle();
997 
998 	RestoreWindowState(message);
999 	fPoseView->Init(message);
1000 
1001 	RestoreStateCommon();
1002 }
1003 
1004 
1005 void
1006 BContainerWindow::RestoreStateCommon()
1007 {
1008 	if (!fIsDesktop && fUseLayouts)
1009 		InitLayout();
1010 
1011 	if (BootedInSafeMode())
1012 		// don't pick up backgrounds in safe mode
1013 		return;
1014 
1015 	WindowStateNodeOpener opener(this, false);
1016 
1017 	if (!TargetModel()->IsRoot() && opener.Node() != NULL) {
1018 		// don't pick up background image for root disks
1019 		// to do this, would have to have a unique attribute for the
1020 		// disks window that doesn't collide with the desktop
1021 		// for R4 this was not done to make things simpler
1022 		// the default image will still work though
1023 		fBackgroundImage = BackgroundImage::GetBackgroundImage(
1024 			opener.Node(), fIsDesktop);
1025 			// look for background image info in the window's node
1026 	}
1027 
1028 	BNode defaultingNode;
1029 	if (fBackgroundImage == NULL && !fIsDesktop
1030 		&& DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode)) {
1031 		// look for background image info in the source for defaults
1032 		fBackgroundImage = BackgroundImage::GetBackgroundImage(&defaultingNode,
1033 			fIsDesktop);
1034 	}
1035 }
1036 
1037 
1038 void
1039 BContainerWindow::UpdateTitle()
1040 {
1041 	// set title to full path, if necessary
1042 	if (TrackerSettings().ShowFullPathInTitleBar()) {
1043 		// use the Entry's full path
1044 		BPath path;
1045 		TargetModel()->GetPath(&path);
1046 		SetTitle(path.Path());
1047 	} else {
1048 		// use the default look
1049 		SetTitle(TargetModel()->Name());
1050 	}
1051 
1052 	if (Navigator() != NULL) {
1053 		Navigator()->UpdateLocation(PoseView()->TargetModel(),
1054 			kActionUpdatePath);
1055 	}
1056 }
1057 
1058 
1059 void
1060 BContainerWindow::UpdateBackgroundImage()
1061 {
1062 	if (BootedInSafeMode())
1063 		return;
1064 
1065 	WindowStateNodeOpener opener(this, false);
1066 
1067 	if (!TargetModel()->IsRoot() && opener.Node() != NULL) {
1068 		fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage,
1069 			opener.Node(), fIsDesktop, PoseView());
1070 	}
1071 
1072 		// look for background image info in the window's node
1073 	BNode defaultingNode;
1074 	if (!fBackgroundImage && !fIsDesktop
1075 		&& DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode)) {
1076 		// look for background image info in the source for defaults
1077 		fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage,
1078 			&defaultingNode, fIsDesktop, PoseView());
1079 	}
1080 }
1081 
1082 
1083 void
1084 BContainerWindow::FrameResized(float, float)
1085 {
1086 	if (PoseView() != NULL && !fIsDesktop) {
1087 		BRect extent = PoseView()->Extent();
1088 		float offsetX = extent.left - PoseView()->Bounds().left;
1089 		float offsetY = extent.top - PoseView()->Bounds().top;
1090 
1091 		// scroll when the size augmented, there is a negative offset
1092 		// and we have resized over the bottom right corner of the extent
1093 		BPoint scroll(B_ORIGIN);
1094 		if (offsetX < 0 && PoseView()->Bounds().right > extent.right
1095 			&& Bounds().Width() > fPreviousBounds.Width()) {
1096 			scroll.x = std::max(fPreviousBounds.Width() - Bounds().Width(),
1097 				offsetX);
1098 		}
1099 
1100 		if (offsetY < 0 && PoseView()->Bounds().bottom > extent.bottom
1101 			&& Bounds().Height() > fPreviousBounds.Height()) {
1102 			scroll.y = std::max(fPreviousBounds.Height() - Bounds().Height(),
1103 				offsetY);
1104 		}
1105 
1106 		if (scroll != B_ORIGIN)
1107 			PoseView()->ScrollBy(scroll.x, scroll.y);
1108 
1109 		PoseView()->UpdateScrollRange();
1110 		PoseView()->ResetPosePlacementHint();
1111 	}
1112 
1113 	fPreviousBounds = Bounds();
1114 	fStateNeedsSaving = true;
1115 }
1116 
1117 
1118 void
1119 BContainerWindow::FrameMoved(BPoint)
1120 {
1121 	fStateNeedsSaving = true;
1122 }
1123 
1124 
1125 void
1126 BContainerWindow::WorkspacesChanged(uint32, uint32)
1127 {
1128 	fStateNeedsSaving = true;
1129 }
1130 
1131 
1132 void
1133 BContainerWindow::ViewModeChanged(uint32 oldMode, uint32 newMode)
1134 {
1135 	if (fBackgroundImage == NULL)
1136 		return;
1137 
1138 	if (newMode == kListMode)
1139 		fBackgroundImage->Remove();
1140 	else if (oldMode == kListMode)
1141 		fBackgroundImage->Show(PoseView(), current_workspace());
1142 }
1143 
1144 
1145 void
1146 BContainerWindow::CheckScreenIntersect()
1147 {
1148 	BScreen screen(this);
1149 	BRect screenFrame(screen.Frame());
1150 	BRect frame(Frame());
1151 
1152 	if (sNewWindRect.bottom > screenFrame.bottom)
1153 		sNewWindRect.OffsetTo(85, 50);
1154 
1155 	if (sNewWindRect.right > screenFrame.right)
1156 		sNewWindRect.OffsetTo(85, 50);
1157 
1158 	if (!frame.Intersects(screenFrame))
1159 		MoveTo(sNewWindRect.LeftTop());
1160 }
1161 
1162 
1163 void
1164 BContainerWindow::SaveState(bool hide)
1165 {
1166 	if (SaveStateIsEnabled()) {
1167 		WindowStateNodeOpener opener(this, true);
1168 		if (opener.StreamNode() != NULL)
1169 			SaveWindowState(opener.StreamNode());
1170 
1171 		if (hide)
1172 			Hide();
1173 
1174 		if (opener.StreamNode())
1175 			fPoseView->SaveState(opener.StreamNode());
1176 
1177 		fStateNeedsSaving = false;
1178 	}
1179 }
1180 
1181 
1182 void
1183 BContainerWindow::SaveState(BMessage& message) const
1184 {
1185 	if (SaveStateIsEnabled()) {
1186 		SaveWindowState(message);
1187 		fPoseView->SaveState(message);
1188 	}
1189 }
1190 
1191 
1192 bool
1193 BContainerWindow::StateNeedsSaving() const
1194 {
1195 	return fPoseView != NULL && (fStateNeedsSaving || fPoseView->StateNeedsSaving());
1196 }
1197 
1198 
1199 status_t
1200 BContainerWindow::GetLayoutState(BNode* node, BMessage* message)
1201 {
1202 	if (node == NULL || message == NULL)
1203 		return B_BAD_VALUE;
1204 
1205 	status_t result = node->InitCheck();
1206 	if (result != B_OK)
1207 		return result;
1208 
1209 	// ToDo: get rid of this, use AttrStream instead
1210 	node->RewindAttrs();
1211 	char attrName[256];
1212 	while (node->GetNextAttrName(attrName) == B_OK) {
1213 		attr_info info;
1214 		if (node->GetAttrInfo(attrName, &info) != B_OK)
1215 			continue;
1216 
1217 		// filter out attributes that are not related to window position
1218 		// and column resizing
1219 		// more can be added as needed
1220 		if (strcmp(attrName, kAttrWindowFrame) != 0
1221 			&& strcmp(attrName, kAttrColumns) != 0
1222 			&& strcmp(attrName, kAttrViewState) != 0
1223 			&& strcmp(attrName, kAttrColumnsForeign) != 0
1224 			&& strcmp(attrName, kAttrViewStateForeign) != 0) {
1225 			continue;
1226 		}
1227 
1228 		char* buffer = new char[info.size];
1229 		if (node->ReadAttr(attrName, info.type, 0, buffer,
1230 				(size_t)info.size) == info.size) {
1231 			message->AddData(attrName, info.type, buffer, (ssize_t)info.size);
1232 		}
1233 		delete[] buffer;
1234 	}
1235 
1236 	return B_OK;
1237 }
1238 
1239 
1240 status_t
1241 BContainerWindow::SetLayoutState(BNode* node, const BMessage* message)
1242 {
1243 	status_t result = node->InitCheck();
1244 	if (result != B_OK)
1245 		return result;
1246 
1247 	for (int32 globalIndex = 0; ;) {
1248 #if B_BEOS_VERSION_DANO
1249  		const char* name;
1250 #else
1251 		char* name;
1252 #endif
1253 		type_code type;
1254 		int32 count;
1255 		status_t result = message->GetInfo(B_ANY_TYPE, globalIndex, &name,
1256 			&type, &count);
1257 		if (result != B_OK)
1258 			break;
1259 
1260 		for (int32 index = 0; index < count; index++) {
1261 			const void* buffer;
1262 			ssize_t size;
1263 			result = message->FindData(name, type, index, &buffer, &size);
1264 			if (result != B_OK) {
1265 				PRINT(("error reading %s \n", name));
1266 				return result;
1267 			}
1268 
1269 			if (node->WriteAttr(name, type, 0, buffer,
1270 					(size_t)size) != size) {
1271 				PRINT(("error writing %s \n", name));
1272 				return result;
1273 			}
1274 			globalIndex++;
1275 		}
1276 	}
1277 
1278 	return B_OK;
1279 }
1280 
1281 
1282 bool
1283 BContainerWindow::ShouldAddMenus() const
1284 {
1285 	return true;
1286 }
1287 
1288 
1289 bool
1290 BContainerWindow::ShouldAddScrollBars() const
1291 {
1292 	return true;
1293 }
1294 
1295 
1296 Model*
1297 BContainerWindow::TargetModel() const
1298 {
1299 	return fPoseView->TargetModel();
1300 }
1301 
1302 
1303 void
1304 BContainerWindow::SelectionChanged()
1305 {
1306 }
1307 
1308 
1309 void
1310 BContainerWindow::Zoom(BPoint, float, float)
1311 {
1312 	BRect oldZoomRect(fSavedZoomRect);
1313 	fSavedZoomRect = Frame();
1314 	ResizeToFit();
1315 
1316 	if (fSavedZoomRect == Frame() && oldZoomRect.IsValid())
1317 		ResizeTo(oldZoomRect.Width(), oldZoomRect.Height());
1318 }
1319 
1320 
1321 void
1322 BContainerWindow::ResizeToFit()
1323 {
1324 	BScreen screen(this);
1325 	BRect screenFrame(screen.Frame());
1326 
1327 	screenFrame.InsetBy(5, 5);
1328 	BMessage decoratorSettings;
1329 	GetDecoratorSettings(&decoratorSettings);
1330 
1331 	float tabHeight = 15;
1332 	BRect tabRect;
1333 	if (decoratorSettings.FindRect("tab frame", &tabRect) == B_OK)
1334 		tabHeight = tabRect.Height();
1335 	screenFrame.top += tabHeight;
1336 
1337 	BRect frame(Frame());
1338 
1339 	float widthDiff = frame.Width() - PoseView()->Frame().Width();
1340 	float heightDiff = frame.Height() - PoseView()->Frame().Height();
1341 
1342 	// move frame left top on screen
1343 	BPoint leftTop(frame.LeftTop());
1344 	leftTop.ConstrainTo(screenFrame);
1345 	frame.OffsetTo(leftTop);
1346 
1347 	// resize to extent size
1348 	BRect extent(PoseView()->Extent());
1349 	frame.right = frame.left + extent.Width() + widthDiff;
1350 	frame.bottom = frame.top + extent.Height() + heightDiff;
1351 
1352 	// make sure entire window fits on screen
1353 	frame = frame & screenFrame;
1354 
1355 	ResizeTo(frame.Width(), frame.Height());
1356 	MoveTo(frame.LeftTop());
1357 	PoseView()->DisableScrollBars();
1358 
1359 	// scroll if there is an offset
1360 	PoseView()->ScrollBy(
1361 		extent.left - PoseView()->Bounds().left,
1362 		extent.top - PoseView()->Bounds().top);
1363 
1364 	PoseView()->UpdateScrollRange();
1365 	PoseView()->EnableScrollBars();
1366 }
1367 
1368 
1369 void
1370 BContainerWindow::MessageReceived(BMessage* message)
1371 {
1372 	switch (message->what) {
1373 		case B_CUT:
1374 		case B_COPY:
1375 		case B_PASTE:
1376 		case kCutMoreSelectionToClipboard:
1377 		case kCopyMoreSelectionToClipboard:
1378 		case kPasteLinksFromClipboard:
1379 		{
1380 			BView* view = CurrentFocus();
1381 			if (dynamic_cast<BTextView*>(view) == NULL) {
1382 				// The selected item is not a BTextView, so forward the
1383 				// message to the PoseView.
1384 				if (fPoseView != NULL)
1385 					fPoseView->MessageReceived(message);
1386 			} else {
1387 				// Since we catch the generic clipboard shortcuts in a way that
1388 				// means the BTextView will never get them, we must
1389 				// manually forward them ourselves.
1390 				//
1391 				// However, we have to take care to not forward the custom
1392 				// clipboard messages, else we would wind up in infinite
1393 				// recursion.
1394 				if (message->what == B_CUT || message->what == B_COPY
1395 						|| message->what == B_PASTE) {
1396 					view->MessageReceived(message);
1397 				}
1398 			}
1399 			break;
1400 		}
1401 
1402 		case B_UNDO: {
1403 			BView* view = CurrentFocus();
1404 			if (dynamic_cast<BTextView*>(view) == NULL) {
1405 				FSUndo();
1406 			} else {
1407 				view->MessageReceived(message);
1408 			}
1409 			break;
1410 		}
1411 
1412 		case B_REDO: {
1413 			BView* view = CurrentFocus();
1414 			if (dynamic_cast<BTextView*>(view) == NULL) {
1415 				FSRedo();
1416 			} else {
1417 				view->MessageReceived(message);
1418 			}
1419 			break;
1420 		}
1421 
1422 		case kNewFolder:
1423 			PostMessage(message, PoseView());
1424 			break;
1425 
1426 		case kRestoreState:
1427 			if (message->HasMessage("state")) {
1428 				BMessage state;
1429 				message->FindMessage("state", &state);
1430 				Init(&state);
1431 			} else
1432 				Init();
1433 			break;
1434 
1435 		case kResizeToFit:
1436 			ResizeToFit();
1437 			break;
1438 
1439 		case kLoadAddOn:
1440 			LoadAddOn(message);
1441 			break;
1442 
1443 		case kCopySelectionTo:
1444 		{
1445 			entry_ref ref;
1446 			if (message->FindRef("refs", &ref) != B_OK)
1447 				break;
1448 
1449 			BRoster().AddToRecentFolders(&ref);
1450 
1451 			Model model(&ref);
1452 			if (model.InitCheck() != B_OK)
1453 				break;
1454 
1455 			if (*model.NodeRef() == *TargetModel()->NodeRef())
1456 				PoseView()->DuplicateSelection();
1457 			else
1458 				PoseView()->MoveSelectionInto(&model, this, true);
1459 			break;
1460 		}
1461 
1462 		case kMoveSelectionTo:
1463 		{
1464 			entry_ref ref;
1465 			if (message->FindRef("refs", &ref) != B_OK)
1466 				break;
1467 
1468 			BRoster().AddToRecentFolders(&ref);
1469 
1470 			Model model(&ref);
1471 			if (model.InitCheck() != B_OK)
1472 				break;
1473 
1474 			PoseView()->MoveSelectionInto(&model, this, false, true);
1475 			break;
1476 		}
1477 
1478 		case kCreateLink:
1479 		case kCreateRelativeLink:
1480 		{
1481 			entry_ref ref;
1482 			if (message->FindRef("refs", &ref) == B_OK) {
1483 				BRoster().AddToRecentFolders(&ref);
1484 
1485 				Model model(&ref);
1486 				if (model.InitCheck() != B_OK)
1487 					break;
1488 
1489 				PoseView()->MoveSelectionInto(&model, this, false, false,
1490 					message->what == kCreateLink,
1491 					message->what == kCreateRelativeLink);
1492 			} else if (!TargetModel()->IsQuery()
1493 				&& !TargetModel()->IsVirtualDirectory()) {
1494 				// no destination specified, create link in same dir as item
1495 				PoseView()->MoveSelectionInto(TargetModel(), this, false, false,
1496 					message->what == kCreateLink,
1497 					message->what == kCreateRelativeLink);
1498 			}
1499 			break;
1500 		}
1501 
1502 		case kShowSelectionWindow:
1503 			ShowSelectionWindow();
1504 			break;
1505 
1506 		case kSelectMatchingEntries:
1507 			PoseView()->SelectMatchingEntries(message);
1508 			break;
1509 
1510 		case kFindButton:
1511 			(new FindWindow())->Show();
1512 			break;
1513 
1514 		case kQuitTracker:
1515 			be_app->PostMessage(B_QUIT_REQUESTED);
1516 			break;
1517 
1518 		case kRestoreBackgroundImage:
1519 			UpdateBackgroundImage();
1520 			break;
1521 
1522 		case kSwitchDirectory:
1523 		{
1524 			entry_ref ref;
1525 			if (message->FindRef("refs", &ref) == B_OK) {
1526 				BEntry entry;
1527 				if (entry.SetTo(&ref) == B_OK) {
1528 					if (StateNeedsSaving())
1529 						SaveState(false);
1530 
1531 					bool wasInTrash = IsTrash() || InTrash();
1532 					bool isRoot = PoseView()->TargetModel()->IsRoot();
1533 
1534 					// Switch dir and apply new state
1535 					WindowStateNodeOpener opener(this, false);
1536 					opener.SetTo(&entry, false);
1537 
1538 					// Update PoseView
1539 					PoseView()->SwitchDir(&ref, opener.StreamNode());
1540 
1541 					fIsTrash = FSIsTrashDir(&entry);
1542 					fInTrash = FSInTrashDir(&ref);
1543 
1544 					if (wasInTrash ^ (IsTrash() || InTrash())
1545 						|| isRoot != PoseView()->TargetModel()->IsRoot())
1546 						RepopulateMenus();
1547 
1548 					if (Navigator() != NULL) {
1549 						// update Navigation bar
1550 						int32 action = kActionSet;
1551 						if (message->FindInt32("action", &action) != B_OK) {
1552 							// Design problem? Why does FindInt32 touch
1553 							// 'action' at all if he can't find it??
1554 							action = kActionSet;
1555 						}
1556 						Navigator()->UpdateLocation(PoseView()->TargetModel(),
1557 							action);
1558 					}
1559 
1560 					TrackerSettings settings;
1561 					if (settings.ShowNavigator()
1562 						|| settings.ShowFullPathInTitleBar()) {
1563 						SetPathWatchingEnabled(true);
1564 					}
1565 					SetSingleWindowBrowseShortcuts(
1566 						settings.SingleWindowBrowse());
1567 
1568 					// Update draggable folder icon
1569 					if (fMenuBar != NULL) {
1570 						if (!TargetModel()->IsRoot() && !IsTrash()) {
1571 							// Folder icon should be visible, but in single
1572 							// window navigation, it might not be.
1573 							if (fDraggableIcon != NULL) {
1574 								IconCache::sIconCache->IconChanged(
1575 									TargetModel());
1576 								fDraggableIcon->Invalidate();
1577 							} else
1578 								_AddFolderIcon();
1579 						} else if (fDraggableIcon != NULL)
1580 							fDraggableIcon->RemoveSelf();
1581 					}
1582 
1583 					// Update window title
1584 					UpdateTitle();
1585 				}
1586 			}
1587 			break;
1588 		}
1589 
1590 		case B_REFS_RECEIVED:
1591 			if (Dragging()) {
1592 				// ref in this message is the target,
1593 				// the end point of the drag
1594 
1595 				entry_ref ref;
1596 				if (message->FindRef("refs", &ref) == B_OK) {
1597 					fWaitingForRefs = false;
1598 					BEntry entry(&ref, true);
1599 					// don't copy to printers dir
1600 					if (!FSIsPrintersDir(&entry)) {
1601 						if (entry.InitCheck() == B_OK
1602 							&& entry.IsDirectory()) {
1603 							Model targetModel(&entry, true, false);
1604 							BPoint dropPoint;
1605 							uint32 buttons;
1606 							PoseView()->GetMouse(&dropPoint, &buttons, true);
1607 							PoseView()->HandleDropCommon(fDragMessage,
1608 								&targetModel, NULL, PoseView(), dropPoint);
1609 						}
1610 					}
1611 				}
1612 				DragStop();
1613 			}
1614 			break;
1615 
1616 		case B_OBSERVER_NOTICE_CHANGE:
1617 		{
1618 			int32 observerWhat;
1619 			if (message->FindInt32("be:observe_change_what", &observerWhat)
1620 					== B_OK) {
1621 				TrackerSettings settings;
1622 				switch (observerWhat) {
1623 					case kWindowsShowFullPathChanged:
1624 						UpdateTitle();
1625 						if (!IsPathWatchingEnabled()
1626 							&& settings.ShowFullPathInTitleBar()) {
1627 							SetPathWatchingEnabled(true);
1628 						}
1629 						if (IsPathWatchingEnabled()
1630 							&& !(settings.ShowNavigator()
1631 								|| settings.ShowFullPathInTitleBar())) {
1632 							SetPathWatchingEnabled(false);
1633 						}
1634 						break;
1635 
1636 					case kSingleWindowBrowseChanged:
1637 						if (settings.SingleWindowBrowse()
1638 							&& !Navigator()
1639 							&& TargetModel()->IsDirectory()
1640 							&& !PoseView()->IsFilePanel()
1641 							&& !PoseView()->IsDesktopWindow()) {
1642 							fNavigator = new BNavigator(TargetModel());
1643 							fPoseContainer->GridLayout()->AddView(fNavigator,
1644 								0, 0, 2);
1645 							fNavigator->Hide();
1646 							SetPathWatchingEnabled(settings.ShowNavigator()
1647 								|| settings.ShowFullPathInTitleBar());
1648 						}
1649 
1650 						if (!settings.SingleWindowBrowse()
1651 							&& !fIsDesktop && TargetModel()->IsDesktop()) {
1652 							// Close the "Desktop" window, but not the Desktop
1653 							this->Quit();
1654 						}
1655 
1656 						SetSingleWindowBrowseShortcuts(
1657 							settings.SingleWindowBrowse());
1658 						break;
1659 
1660 					case kShowNavigatorChanged:
1661 						ShowNavigator(settings.ShowNavigator());
1662 						if (!IsPathWatchingEnabled()
1663 							&& settings.ShowNavigator()) {
1664 							SetPathWatchingEnabled(true);
1665 						}
1666 						if (IsPathWatchingEnabled()
1667 							&& !(settings.ShowNavigator()
1668 								|| settings.ShowFullPathInTitleBar())) {
1669 							SetPathWatchingEnabled(false);
1670 						}
1671 						SetSingleWindowBrowseShortcuts(
1672 							settings.SingleWindowBrowse());
1673 						break;
1674 
1675 					case kDontMoveFilesToTrashChanged:
1676 					{
1677 						bool dontMoveToTrash
1678 							= settings.DontMoveFilesToTrash();
1679 
1680 						BMenuItem* item
1681 							= fFileContextMenu->FindItem(kMoveToTrash);
1682 						if (item != NULL) {
1683 							item->SetLabel(dontMoveToTrash
1684 								? B_TRANSLATE("Delete")
1685 								: B_TRANSLATE("Move to Trash"));
1686 						}
1687 						// Deskbar doesn't have a menu bar, so check if
1688 						// there is fMenuBar
1689 						if (fMenuBar && fFileMenu) {
1690 							item = fFileMenu->FindItem(kMoveToTrash);
1691 							if (item != NULL) {
1692 								item->SetLabel(dontMoveToTrash
1693 									? B_TRANSLATE("Delete")
1694 									: B_TRANSLATE("Move to Trash"));
1695 							}
1696 						}
1697 						UpdateIfNeeded();
1698 						break;
1699 					}
1700 
1701 					default:
1702 						_inherited::MessageReceived(message);
1703 						break;
1704 				}
1705 			}
1706 			break;
1707 		}
1708 
1709 		case B_NODE_MONITOR:
1710 			UpdateTitle();
1711 			break;
1712 
1713 		default:
1714 			_inherited::MessageReceived(message);
1715 			break;
1716 	}
1717 }
1718 
1719 
1720 void
1721 BContainerWindow::SetCutItem(BMenu* menu)
1722 {
1723 	BMenuItem* item;
1724 	if ((item = menu->FindItem(B_CUT)) == NULL
1725 		&& (item = menu->FindItem(kCutMoreSelectionToClipboard)) == NULL)
1726 		return;
1727 
1728 	item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0
1729 		|| PoseView() != CurrentFocus());
1730 
1731 	if (modifiers() & B_SHIFT_KEY) {
1732 		item->SetLabel(B_TRANSLATE("Cut more"));
1733 		item->SetShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY);
1734 		item->SetMessage(new BMessage(kCutMoreSelectionToClipboard));
1735 	} else {
1736 		item->SetLabel(B_TRANSLATE("Cut"));
1737 		item->SetShortcut('X', B_COMMAND_KEY);
1738 		item->SetMessage(new BMessage(B_CUT));
1739 	}
1740 }
1741 
1742 
1743 void
1744 BContainerWindow::SetCopyItem(BMenu* menu)
1745 {
1746 	BMenuItem* item;
1747 	if ((item = menu->FindItem(B_COPY)) == NULL
1748 		&& (item = menu->FindItem(kCopyMoreSelectionToClipboard)) == NULL) {
1749 		return;
1750 	}
1751 
1752 	item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0
1753 		|| PoseView() != CurrentFocus());
1754 
1755 	if (modifiers() & B_SHIFT_KEY) {
1756 		item->SetLabel(B_TRANSLATE("Copy more"));
1757 		item->SetShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY);
1758 		item->SetMessage(new BMessage(kCopyMoreSelectionToClipboard));
1759 	} else {
1760 		item->SetLabel(B_TRANSLATE("Copy"));
1761 		item->SetShortcut('C', B_COMMAND_KEY);
1762 		item->SetMessage(new BMessage(B_COPY));
1763 	}
1764 }
1765 
1766 
1767 void
1768 BContainerWindow::SetPasteItem(BMenu* menu)
1769 {
1770 	BMenuItem* item;
1771 	if ((item = menu->FindItem(B_PASTE)) == NULL
1772 		&& (item = menu->FindItem(kPasteLinksFromClipboard)) == NULL) {
1773 		return;
1774 	}
1775 
1776 	item->SetEnabled(FSClipboardHasRefs() || PoseView() != CurrentFocus());
1777 
1778 	if (modifiers() & B_SHIFT_KEY) {
1779 		item->SetLabel(B_TRANSLATE("Paste links"));
1780 		item->SetShortcut('V', B_COMMAND_KEY | B_SHIFT_KEY);
1781 		item->SetMessage(new BMessage(kPasteLinksFromClipboard));
1782 	} else {
1783 		item->SetLabel(B_TRANSLATE("Paste"));
1784 		item->SetShortcut('V', B_COMMAND_KEY);
1785 		item->SetMessage(new BMessage(B_PASTE));
1786 	}
1787 }
1788 
1789 
1790 void
1791 BContainerWindow::SetArrangeMenu(BMenu* menu)
1792 {
1793 	BMenuItem* item;
1794 	if ((item = menu->FindItem(kCleanup)) == NULL
1795 		&& (item = menu->FindItem(kCleanupAll)) == NULL) {
1796 		return;
1797 	}
1798 
1799 	item->Menu()->SetEnabled(PoseView()->CountItems() > 0
1800 		&& (PoseView()->ViewMode() != kListMode));
1801 
1802 	BMenu* arrangeMenu;
1803 
1804 	if (modifiers() & B_SHIFT_KEY) {
1805 		item->SetLabel(B_TRANSLATE("Clean up all"));
1806 		item->SetShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY);
1807 		item->SetMessage(new BMessage(kCleanupAll));
1808 		arrangeMenu = item->Menu();
1809 	} else {
1810 		item->SetLabel(B_TRANSLATE("Clean up"));
1811 		item->SetShortcut('K', B_COMMAND_KEY);
1812 		item->SetMessage(new BMessage(kCleanup));
1813 		arrangeMenu = item->Menu();
1814 	}
1815 
1816 	MarkArrangeByMenu(arrangeMenu);
1817 }
1818 
1819 
1820 void
1821 BContainerWindow::SetCloseItem(BMenu* menu)
1822 {
1823 	BMenuItem* item;
1824 	if ((item = menu->FindItem(B_QUIT_REQUESTED)) == NULL
1825 		&& (item = menu->FindItem(kCloseAllWindows)) == NULL) {
1826 		return;
1827 	}
1828 
1829 	if (modifiers() & B_SHIFT_KEY) {
1830 		item->SetLabel(B_TRANSLATE("Close all"));
1831 		item->SetShortcut('W', B_COMMAND_KEY | B_SHIFT_KEY);
1832 		item->SetTarget(be_app);
1833 		item->SetMessage(new BMessage(kCloseAllWindows));
1834 	} else {
1835 		item->SetLabel(B_TRANSLATE("Close"));
1836 		item->SetShortcut('W', B_COMMAND_KEY);
1837 		item->SetTarget(this);
1838 		item->SetMessage(new BMessage(B_QUIT_REQUESTED));
1839 	}
1840 }
1841 
1842 
1843 bool
1844 BContainerWindow::IsShowing(const node_ref* node) const
1845 {
1846 	return PoseView()->Represents(node);
1847 }
1848 
1849 
1850 bool
1851 BContainerWindow::IsShowing(const entry_ref* entry) const
1852 {
1853 	return PoseView()->Represents(entry);
1854 }
1855 
1856 
1857 void
1858 BContainerWindow::AddMenus()
1859 {
1860 	fFileMenu = new BMenu(B_TRANSLATE("File"));
1861 	AddFileMenu(fFileMenu);
1862 	fMenuBar->AddItem(fFileMenu);
1863 	fWindowMenu = new BMenu(B_TRANSLATE("Window"));
1864 	fMenuBar->AddItem(fWindowMenu);
1865 	AddWindowMenu(fWindowMenu);
1866 	// just create the attribute, decide to add it later
1867 	fAttrMenu = new BMenu(B_TRANSLATE("Attributes"));
1868 	NewAttributeMenu(fAttrMenu);
1869 	PopulateArrangeByMenu(fArrangeByMenu);
1870 }
1871 
1872 
1873 void
1874 BContainerWindow::AddFileMenu(BMenu* menu)
1875 {
1876 	if (!PoseView()->IsFilePanel()) {
1877 		menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
1878 			new BMessage(kFindButton), 'F'));
1879 	}
1880 
1881 	if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory()
1882 		&& !IsTrash() && !IsPrintersDir() && !TargetModel()->IsRoot()) {
1883 		if (!PoseView()->IsFilePanel()) {
1884 			TemplatesMenu* templatesMenu = new TemplatesMenu(PoseView(),
1885 				B_TRANSLATE("New"));
1886 			menu->AddItem(templatesMenu);
1887 			templatesMenu->SetTargetForItems(PoseView());
1888 		} else {
1889 			menu->AddItem(new BMenuItem(B_TRANSLATE("New folder"),
1890 				new BMessage(kNewFolder), 'N'));
1891 		}
1892 	}
1893 	menu->AddSeparatorItem();
1894 
1895 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
1896 		new BMessage(kOpenSelection), 'O'));
1897 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
1898 		new BMessage(kGetInfo), 'I'));
1899 	menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
1900 		new BMessage(kEditItem), 'E'));
1901 
1902 	if (IsTrash() || InTrash()) {
1903 		menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"),
1904 			new BMessage(kRestoreFromTrash)));
1905 		if (IsTrash()) {
1906 			// add as first item in menu
1907 			menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
1908 				new BMessage(kEmptyTrash)), 0);
1909 			menu->AddItem(new BSeparatorItem(), 1);
1910 		}
1911 	} else if (IsPrintersDir()) {
1912 		menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS),
1913 			new BMessage(kAddPrinter), 'N'), 0);
1914 		menu->AddItem(new BSeparatorItem(), 1);
1915 		menu->AddItem(new BMenuItem(B_TRANSLATE("Make active printer"),
1916 			new BMessage(kMakeActivePrinter)));
1917 	} else if (TargetModel()->IsRoot()) {
1918 		BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"),
1919 			new BMessage(kUnmountVolume), 'U');
1920 		item->SetEnabled(false);
1921 		menu->AddItem(item);
1922 		menu->AddItem(new BMenuItem(
1923 			B_TRANSLATE("Mount settings" B_UTF8_ELLIPSIS),
1924 			new BMessage(kRunAutomounterSettings)));
1925 	} else {
1926 		menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"),
1927 			new BMessage(kDuplicateSelection), 'D'));
1928 		menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash()
1929 			? B_TRANSLATE("Delete") : B_TRANSLATE("Move to Trash"),
1930 			new BMessage(kMoveToTrash), 'T'));
1931 		menu->AddSeparatorItem();
1932 
1933 		// The "Move To", "Copy To", "Create Link" menus are inserted
1934 		// at this place, have a look at:
1935 		// BContainerWindow::SetupMoveCopyMenus()
1936 	}
1937 
1938 	BMenuItem* cutItem = NULL;
1939 	BMenuItem* copyItem = NULL;
1940 	BMenuItem* pasteItem = NULL;
1941 	if (!IsPrintersDir()) {
1942 		menu->AddSeparatorItem();
1943 
1944 		if (!TargetModel()->IsRoot()) {
1945 			cutItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Cut"),
1946 				new BMessage(B_CUT), 'X');
1947 			menu->AddItem(cutItem);
1948 			copyItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Copy"),
1949 				new BMessage(B_COPY), 'C');
1950 			menu->AddItem(copyItem);
1951 			pasteItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Paste"),
1952 				new BMessage(B_PASTE), 'V');
1953 			menu->AddItem(pasteItem);
1954 			menu->AddSeparatorItem();
1955 			menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"),
1956 				new BMessage(kIdentifyEntry)));
1957 		}
1958 		BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
1959 		addOnMenuItem->SetFont(be_plain_font);
1960 		menu->AddItem(addOnMenuItem);
1961 	}
1962 
1963 	menu->SetTargetForItems(PoseView());
1964 	if (cutItem != NULL)
1965 		cutItem->SetTarget(this);
1966 
1967 	if (copyItem != NULL)
1968 		copyItem->SetTarget(this);
1969 
1970 	if (pasteItem != NULL)
1971 		pasteItem->SetTarget(this);
1972 }
1973 
1974 
1975 void
1976 BContainerWindow::AddWindowMenu(BMenu* menu)
1977 {
1978 	BMenuItem* item;
1979 
1980 	BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view"));
1981 
1982 	BMessage* message = new BMessage(kIconMode);
1983 	message->AddInt32("size", 32);
1984 	item = new BMenuItem(B_TRANSLATE("32 x 32"), message);
1985 	item->SetTarget(PoseView());
1986 	iconSizeMenu->AddItem(item);
1987 
1988 	message = new BMessage(kIconMode);
1989 	message->AddInt32("size", 40);
1990 	item = new BMenuItem(B_TRANSLATE("40 x 40"), message);
1991 	item->SetTarget(PoseView());
1992 	iconSizeMenu->AddItem(item);
1993 
1994 	message = new BMessage(kIconMode);
1995 	message->AddInt32("size", 48);
1996 	item = new BMenuItem(B_TRANSLATE("48 x 48"), message);
1997 	item->SetTarget(PoseView());
1998 	iconSizeMenu->AddItem(item);
1999 
2000 	message = new BMessage(kIconMode);
2001 	message->AddInt32("size", 64);
2002 	item = new BMenuItem(B_TRANSLATE("64 x 64"), message);
2003 	item->SetTarget(PoseView());
2004 	iconSizeMenu->AddItem(item);
2005 
2006 	message = new BMessage(kIconMode);
2007 	message->AddInt32("size", 96);
2008 	item = new BMenuItem(B_TRANSLATE("96 x 96"), message);
2009 	item->SetTarget(PoseView());
2010 	iconSizeMenu->AddItem(item);
2011 
2012 	message = new BMessage(kIconMode);
2013 	message->AddInt32("size", 128);
2014 	item = new BMenuItem(B_TRANSLATE("128 x 128"), message);
2015 	item->SetTarget(PoseView());
2016 	iconSizeMenu->AddItem(item);
2017 
2018 	iconSizeMenu->AddSeparatorItem();
2019 
2020 	message = new BMessage(kIconMode);
2021 	message->AddInt32("scale", 0);
2022 	item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-');
2023 	item->SetTarget(PoseView());
2024 	iconSizeMenu->AddItem(item);
2025 
2026 	message = new BMessage(kIconMode);
2027 	message->AddInt32("scale", 1);
2028 	item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+');
2029 	item->SetTarget(PoseView());
2030 	iconSizeMenu->AddItem(item);
2031 
2032 	// A sub menu where the super item can be invoked.
2033 	menu->AddItem(iconSizeMenu);
2034 	iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY);
2035 	iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode));
2036 	iconSizeMenu->Superitem()->SetTarget(PoseView());
2037 
2038 	item = new BMenuItem(B_TRANSLATE("Mini icon view"),
2039 		new BMessage(kMiniIconMode), '2');
2040 	item->SetTarget(PoseView());
2041 	menu->AddItem(item);
2042 
2043 	BMenu* listViewMenu = new BMenu(B_TRANSLATE("List view"));
2044 
2045 	message = new BMessage(kListMode);
2046 	message->AddInt32("icon_size", B_MINI_ICON);
2047 	item = new BMenuItem(listViewMenu, message);
2048 	item->SetShortcut('3', B_COMMAND_KEY);
2049 	item->SetTarget(PoseView());
2050 	menu->AddItem(item);
2051 
2052 	message = new BMessage(kListMode);
2053 	message->AddInt32("icon_size", B_MINI_ICON);
2054 	item = new BMenuItem(B_TRANSLATE("Mini"), message);
2055 	item->SetTarget(PoseView());
2056 	listViewMenu->AddItem(item);
2057 
2058 	message = new BMessage(kListMode);
2059 	message->AddInt32("icon_size", B_LARGE_ICON);
2060 	item = new BMenuItem(B_TRANSLATE("Large"), message);
2061 	item->SetTarget(PoseView());
2062 	listViewMenu->AddItem(item);
2063 
2064 	listViewMenu->SetTargetForItems(PoseView());
2065 
2066 	menu->AddSeparatorItem();
2067 
2068 	item = new BMenuItem(B_TRANSLATE("Resize to fit"),
2069 		new BMessage(kResizeToFit), 'Y');
2070 	item->SetTarget(this);
2071 	menu->AddItem(item);
2072 
2073 	fArrangeByMenu = new BMenu(B_TRANSLATE("Arrange by"));
2074 	menu->AddItem(fArrangeByMenu);
2075 
2076 	item = new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
2077 		new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY);
2078 	item->SetTarget(PoseView());
2079 	menu->AddItem(item);
2080 
2081 	item = new BMenuItem(B_TRANSLATE("Select all"),
2082 		new BMessage(B_SELECT_ALL), 'A');
2083 	item->SetTarget(PoseView());
2084 	menu->AddItem(item);
2085 
2086 	item = new BMenuItem(B_TRANSLATE("Invert selection"),
2087 		new BMessage(kInvertSelection), 'S');
2088 	item->SetTarget(PoseView());
2089 	menu->AddItem(item);
2090 
2091 	if (!IsTrash()) {
2092 		item = new BMenuItem(B_TRANSLATE("Open parent"),
2093 			new BMessage(kOpenParentDir), B_UP_ARROW);
2094 		item->SetTarget(PoseView());
2095 		menu->AddItem(item);
2096 	}
2097 
2098 	item = new BMenuItem(B_TRANSLATE("Close"),
2099 		new BMessage(B_QUIT_REQUESTED), 'W');
2100 	item->SetTarget(this);
2101 	menu->AddItem(item);
2102 
2103 	item = new BMenuItem(B_TRANSLATE("Close all in workspace"),
2104 		new BMessage(kCloseAllInWorkspace), 'Q');
2105 	item->SetTarget(be_app);
2106 	menu->AddItem(item);
2107 
2108 	menu->AddSeparatorItem();
2109 
2110 	item = new BMenuItem(B_TRANSLATE("Preferences" B_UTF8_ELLIPSIS),
2111 		new BMessage(kShowSettingsWindow));
2112 	item->SetTarget(be_app);
2113 	menu->AddItem(item);
2114 }
2115 
2116 
2117 void
2118 BContainerWindow::AddShortcuts()
2119 {
2120 	// add equivalents of the menu shortcuts to the menuless desktop window
2121 	ASSERT(!IsTrash());
2122 	ASSERT(!PoseView()->IsFilePanel());
2123 	ASSERT(!TargetModel()->IsQuery());
2124 	ASSERT(!TargetModel()->IsVirtualDirectory());
2125 
2126 	AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY,
2127 		new BMessage(kCutMoreSelectionToClipboard), this);
2128 	AddShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY,
2129 		new BMessage(kCopyMoreSelectionToClipboard), this);
2130 	AddShortcut('F', B_COMMAND_KEY,
2131 		new BMessage(kFindButton), PoseView());
2132 	AddShortcut('N', B_COMMAND_KEY,
2133 		new BMessage(kNewFolder), PoseView());
2134 	AddShortcut('O', B_COMMAND_KEY,
2135 		new BMessage(kOpenSelection), PoseView());
2136 	AddShortcut('I', B_COMMAND_KEY,
2137 		new BMessage(kGetInfo), PoseView());
2138 	AddShortcut('E', B_COMMAND_KEY,
2139 		new BMessage(kEditItem), PoseView());
2140 	AddShortcut('D', B_COMMAND_KEY,
2141 		new BMessage(kDuplicateSelection), PoseView());
2142 	AddShortcut('T', B_COMMAND_KEY,
2143 		new BMessage(kMoveToTrash), PoseView());
2144 	AddShortcut('K', B_COMMAND_KEY,
2145 		new BMessage(kCleanup), PoseView());
2146 	AddShortcut('A', B_COMMAND_KEY,
2147 		new BMessage(B_SELECT_ALL), PoseView());
2148 	AddShortcut('S', B_COMMAND_KEY,
2149 		new BMessage(kInvertSelection), PoseView());
2150 	AddShortcut('A', B_COMMAND_KEY | B_SHIFT_KEY,
2151 		new BMessage(kShowSelectionWindow), PoseView());
2152 	AddShortcut('G', B_COMMAND_KEY,
2153 		new BMessage(kEditQuery), PoseView());
2154 		// it is ok to add a global Edit query shortcut here, PoseView will
2155 		// filter out cases where selected pose is not a query
2156 	AddShortcut('U', B_COMMAND_KEY,
2157 		new BMessage(kUnmountVolume), PoseView());
2158 	AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
2159 		new BMessage(kOpenParentDir), PoseView());
2160 	AddShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY,
2161 		new BMessage(kOpenSelectionWith), PoseView());
2162 
2163 	BMessage* decreaseSize = new BMessage(kIconMode);
2164 	decreaseSize->AddInt32("scale", 0);
2165 	AddShortcut('-', B_COMMAND_KEY, decreaseSize, PoseView());
2166 
2167 	BMessage* increaseSize = new BMessage(kIconMode);
2168 	increaseSize->AddInt32("scale", 1);
2169 	AddShortcut('+', B_COMMAND_KEY, increaseSize, PoseView());
2170 }
2171 
2172 
2173 void
2174 BContainerWindow::MenusBeginning()
2175 {
2176 	if (fMenuBar == NULL)
2177 		return;
2178 
2179 	if (CurrentMessage() != NULL && CurrentMessage()->what == B_MOUSE_DOWN) {
2180 		// don't commit active pose if only a keyboard shortcut is
2181 		// invoked - this would prevent Cut/Copy/Paste from working
2182 		fPoseView->CommitActivePose();
2183 	}
2184 
2185 	// File menu
2186 	int32 selectCount = PoseView()->SelectionList()->CountItems();
2187 
2188 	SetupOpenWithMenu(fFileMenu);
2189 	SetupMoveCopyMenus(selectCount
2190 		? PoseView()->SelectionList()->FirstItem()->TargetModel()->EntryRef()
2191 		: NULL, fFileMenu);
2192 
2193 	if (TargetModel()->IsRoot()) {
2194 		BVolume boot;
2195 		BVolumeRoster().GetBootVolume(&boot);
2196 
2197 		bool ejectableVolumeSelected = false;
2198 
2199 		int32 count = PoseView()->SelectionList()->CountItems();
2200 		for (int32 index = 0; index < count; index++) {
2201 			Model* model
2202 				= PoseView()->SelectionList()->ItemAt(index)->TargetModel();
2203 			if (model->IsVolume()) {
2204 				BVolume volume;
2205 				volume.SetTo(model->NodeRef()->device);
2206 				if (volume != boot) {
2207 					ejectableVolumeSelected = true;
2208 					break;
2209 				}
2210 			}
2211 		}
2212 		BMenuItem* item = fMenuBar->FindItem(kUnmountVolume);
2213 		if (item != NULL)
2214 			item->SetEnabled(ejectableVolumeSelected);
2215 	}
2216 
2217 	UpdateMenu(fMenuBar, kMenuBarContext);
2218 
2219 	AddMimeTypesToMenu(fAttrMenu);
2220 
2221 	if (IsPrintersDir()) {
2222 		EnableNamedMenuItem(fFileMenu, B_TRANSLATE("Make active printer"),
2223 			selectCount == 1);
2224 	}
2225 }
2226 
2227 
2228 void
2229 BContainerWindow::MenusEnded()
2230 {
2231 	// when we're done we want to clear nav menus for next time
2232 	DeleteSubmenu(fNavigationItem);
2233 	DeleteSubmenu(fMoveToItem);
2234 	DeleteSubmenu(fCopyToItem);
2235 	DeleteSubmenu(fCreateLinkItem);
2236 	DeleteSubmenu(fOpenWithItem);
2237 }
2238 
2239 
2240 void
2241 BContainerWindow::SetupNavigationMenu(const entry_ref* ref, BMenu* parent)
2242 {
2243 	// start by removing nav item (and separator) from old menu
2244 	if (fNavigationItem != NULL) {
2245 		BMenu* menu = fNavigationItem->Menu();
2246 		if (menu != NULL) {
2247 			menu->RemoveItem(fNavigationItem);
2248 			BMenuItem* item = menu->RemoveItem((int32)0);
2249 			ASSERT(item != fNavigationItem);
2250 			delete item;
2251 		}
2252 	}
2253 
2254 	// if we weren't passed a ref then we're navigating this window
2255 	if (ref == NULL)
2256 		ref = TargetModel()->EntryRef();
2257 
2258 	BEntry entry;
2259 	if (entry.SetTo(ref) != B_OK)
2260 		return;
2261 
2262 	// only navigate directories and queries (check for symlink here)
2263 	Model model(&entry);
2264 	entry_ref resolvedRef;
2265 
2266 	if (model.InitCheck() != B_OK
2267 		|| (!model.IsContainer() && !model.IsSymLink())) {
2268 		return;
2269 	}
2270 
2271 	if (model.IsSymLink()) {
2272 		if (entry.SetTo(model.EntryRef(), true) != B_OK)
2273 			return;
2274 
2275 		Model resolvedModel(&entry);
2276 		if (resolvedModel.InitCheck() != B_OK || !resolvedModel.IsContainer())
2277 			return;
2278 
2279 		entry.GetRef(&resolvedRef);
2280 		ref = &resolvedRef;
2281 	}
2282 
2283 	if (fNavigationItem == NULL) {
2284 		fNavigationItem = new ModelMenuItem(&model,
2285 			new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, this));
2286 	}
2287 
2288 	// setup a navigation menu item which will dynamically load items
2289 	// as menu items are traversed
2290 	BNavMenu* navMenu = dynamic_cast<BNavMenu*>(fNavigationItem->Submenu());
2291 	navMenu->SetNavDir(ref);
2292 	fNavigationItem->SetLabel(model.Name());
2293 	fNavigationItem->SetEntry(&entry);
2294 
2295 	parent->AddItem(fNavigationItem, 0);
2296 	parent->AddItem(new BSeparatorItem(), 1);
2297 
2298 	BMessage* message = new BMessage(B_REFS_RECEIVED);
2299 	message->AddRef("refs", ref);
2300 	fNavigationItem->SetMessage(message);
2301 	fNavigationItem->SetTarget(be_app);
2302 
2303 	if (!Dragging())
2304 		parent->SetTrackingHook(NULL, NULL);
2305 }
2306 
2307 
2308 void
2309 BContainerWindow::SetUpEditQueryItem(BMenu* menu)
2310 {
2311 	ASSERT(menu);
2312 	// File menu
2313 	int32 selectCount = PoseView()->SelectionList()->CountItems();
2314 
2315 	// add Edit query if appropriate
2316 	bool queryInSelection = false;
2317 	if (selectCount && selectCount < 100) {
2318 		// only do this for a limited number of selected poses
2319 
2320 		// if any queries selected, add an edit query menu item
2321 		for (int32 index = 0; index < selectCount; index++) {
2322 			BPose* pose = PoseView()->SelectionList()->ItemAt(index);
2323 			Model model(pose->TargetModel()->EntryRef(), true);
2324 			if (model.InitCheck() != B_OK)
2325 				continue;
2326 
2327 			if (model.IsQuery() || model.IsQueryTemplate()) {
2328 				queryInSelection = true;
2329 				break;
2330 			}
2331 		}
2332 	}
2333 
2334 	bool poseViewIsQuery = TargetModel()->IsQuery();
2335 		// if the view is a query pose view, add edit query menu item
2336 
2337 	BMenuItem* item = menu->FindItem(kEditQuery);
2338 	if (!poseViewIsQuery && !queryInSelection && item != NULL)
2339 		item->Menu()->RemoveItem(item);
2340 	else if ((poseViewIsQuery || queryInSelection) && item == NULL) {
2341 		// add edit query item after Open
2342 		item = menu->FindItem(kOpenSelection);
2343 		if (item) {
2344 			int32 itemIndex = item->Menu()->IndexOf(item);
2345 			BMenuItem* query = new BMenuItem(B_TRANSLATE("Edit query"),
2346 				new BMessage(kEditQuery), 'G');
2347 			item->Menu()->AddItem(query, itemIndex + 1);
2348 			query->SetTarget(PoseView());
2349 		}
2350 	}
2351 }
2352 
2353 
2354 void
2355 BContainerWindow::SetupOpenWithMenu(BMenu* parent)
2356 {
2357 	// start by removing nav item (and separator) from old menu
2358 	if (fOpenWithItem) {
2359 		BMenu* menu = fOpenWithItem->Menu();
2360 		if (menu != NULL)
2361 			menu->RemoveItem(fOpenWithItem);
2362 
2363 		delete fOpenWithItem;
2364 		fOpenWithItem = 0;
2365 	}
2366 
2367 	if (PoseView()->SelectionList()->CountItems() == 0) {
2368 		// no selection, nothing to open
2369 		return;
2370 	}
2371 
2372 	if (TargetModel()->IsRoot()) {
2373 		// don't add ourselves if we are root
2374 		return;
2375 	}
2376 
2377 	// ToDo:
2378 	// check if only item in selection list is the root
2379 	// and do not add if true
2380 
2381 	// add after "Open"
2382 	BMenuItem* item = parent->FindItem(kOpenSelection);
2383 
2384 	int32 count = PoseView()->SelectionList()->CountItems();
2385 	if (count == 0)
2386 		return;
2387 
2388 	// build a list of all refs to open
2389 	BMessage message(B_REFS_RECEIVED);
2390 	for (int32 index = 0; index < count; index++) {
2391 		BPose* pose = PoseView()->SelectionList()->ItemAt(index);
2392 		message.AddRef("refs", pose->TargetModel()->EntryRef());
2393 	}
2394 
2395 	// add Tracker token so that refs received recipients can script us
2396 	message.AddMessenger("TrackerViewToken", BMessenger(PoseView()));
2397 
2398 	int32 index = item->Menu()->IndexOf(item);
2399 	fOpenWithItem = new BMenuItem(
2400 		new OpenWithMenu(B_TRANSLATE("Open with" B_UTF8_ELLIPSIS),
2401 			&message, this, be_app), new BMessage(kOpenSelectionWith));
2402 	fOpenWithItem->SetTarget(PoseView());
2403 	fOpenWithItem->SetShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY);
2404 
2405 	item->Menu()->AddItem(fOpenWithItem, index + 1);
2406 }
2407 
2408 
2409 void
2410 BContainerWindow::PopulateMoveCopyNavMenu(BNavMenu* navMenu, uint32 what,
2411 	const entry_ref* ref, bool addLocalOnly)
2412 {
2413 	BVolume volume;
2414 	BVolumeRoster volumeRoster;
2415 	BDirectory directory;
2416 	BEntry entry;
2417 	BPath path;
2418 	Model model;
2419 	dev_t device = ref->device;
2420 
2421 	int32 volumeCount = 0;
2422 
2423 	navMenu->RemoveItems(0, navMenu->CountItems(), true);
2424 
2425 	// count persistent writable volumes
2426 	volumeRoster.Rewind();
2427 	while (volumeRoster.GetNextVolume(&volume) == B_OK)
2428 		if (!volume.IsReadOnly() && volume.IsPersistent())
2429 			volumeCount++;
2430 
2431 	// add the current folder
2432 	if (entry.SetTo(ref) == B_OK
2433 		&& entry.GetParent(&entry) == B_OK
2434 		&& model.SetTo(&entry) == B_OK) {
2435 		BNavMenu* menu = new BNavMenu(B_TRANSLATE("Current folder"), what,
2436 			this);
2437 		menu->SetNavDir(model.EntryRef());
2438 		menu->SetShowParent(true);
2439 
2440 		BMenuItem* item = new SpecialModelMenuItem(&model,menu);
2441 		item->SetMessage(new BMessage((uint32)what));
2442 
2443 		navMenu->AddItem(item);
2444 	}
2445 
2446 	// add the recent folder menu
2447 	// the "Tracker" settings directory is only used to get its icon
2448 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
2449 		path.Append("Tracker");
2450 		if (entry.SetTo(path.Path()) == B_OK
2451 			&& model.SetTo(&entry) == B_OK) {
2452 			BMenu* menu = new RecentsMenu(B_TRANSLATE("Recent folders"),
2453 				kRecentFolders, what, this);
2454 
2455 			BMenuItem* item = new SpecialModelMenuItem(&model,menu);
2456 			item->SetMessage(new BMessage((uint32)what));
2457 
2458 			navMenu->AddItem(item);
2459 		}
2460 	}
2461 
2462 	// add Desktop
2463 	FSGetBootDeskDir(&directory);
2464 	if (directory.InitCheck() == B_OK
2465 		&& directory.GetEntry(&entry) == B_OK
2466 		&& model.SetTo(&entry) == B_OK)
2467 		navMenu->AddNavDir(&model, what, this, true);
2468 			// ask NavMenu to populate submenu for us
2469 
2470 	// add the home dir
2471 	if (find_directory(B_USER_DIRECTORY, &path) == B_OK
2472 		&& entry.SetTo(path.Path()) == B_OK
2473 		&& model.SetTo(&entry) == B_OK)
2474 		navMenu->AddNavDir(&model, what, this, true);
2475 
2476 	navMenu->AddSeparatorItem();
2477 
2478 	// either add all mounted volumes (for copy), or all the top-level
2479 	// directories from the same device (for move)
2480 	// ToDo: can be changed if cross-device moves are implemented
2481 
2482 	if (addLocalOnly || volumeCount < 2) {
2483 		// add volume this item lives on
2484 		if (volume.SetTo(device) == B_OK
2485 			&& volume.GetRootDirectory(&directory) == B_OK
2486 			&& directory.GetEntry(&entry) == B_OK
2487 			&& model.SetTo(&entry) == B_OK) {
2488 			navMenu->AddNavDir(&model, what, this, false);
2489 				// do not have submenu populated
2490 
2491 			navMenu->SetNavDir(model.EntryRef());
2492 		}
2493 	} else {
2494 		// add all persistent writable volumes
2495 		volumeRoster.Rewind();
2496 		while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2497 			if (volume.IsReadOnly() || !volume.IsPersistent())
2498 				continue;
2499 
2500 			// add root dir
2501 			if (volume.GetRootDirectory(&directory) == B_OK
2502 				&& directory.GetEntry(&entry) == B_OK
2503 				&& model.SetTo(&entry) == B_OK) {
2504 				navMenu->AddNavDir(&model, what, this, true);
2505 					// ask NavMenu to populate submenu for us
2506 			}
2507 		}
2508 	}
2509 }
2510 
2511 
2512 void
2513 BContainerWindow::SetupMoveCopyMenus(const entry_ref* item_ref, BMenu* parent)
2514 {
2515 	if (IsTrash() || InTrash() || IsPrintersDir() || !fMoveToItem
2516 		|| !fCopyToItem || !fCreateLinkItem || TargetModel()->IsRoot()) {
2517 		return;
2518 	}
2519 
2520 	// Grab the modifiers state since we use it twice
2521 	uint32 modifierKeys = modifiers();
2522 
2523 	// re-parent items to this menu since they're shared
2524 	int32 index;
2525 	BMenuItem* trash = parent->FindItem(kMoveToTrash);
2526 	if (trash)
2527 		index = parent->IndexOf(trash) + 2;
2528 	else
2529 		index = 0;
2530 
2531 	if (fMoveToItem->Menu() != parent) {
2532 		if (fMoveToItem->Menu())
2533 			fMoveToItem->Menu()->RemoveItem(fMoveToItem);
2534 
2535 		parent->AddItem(fMoveToItem, index++);
2536 	}
2537 
2538 	if (fCopyToItem->Menu() != parent) {
2539 		if (fCopyToItem->Menu())
2540 			fCopyToItem->Menu()->RemoveItem(fCopyToItem);
2541 
2542 		parent->AddItem(fCopyToItem, index++);
2543 	}
2544 
2545 	if (fCreateLinkItem->Menu() != parent) {
2546 		if (fCreateLinkItem->Menu())
2547 			fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem);
2548 
2549 		parent->AddItem(fCreateLinkItem, index);
2550 	}
2551 
2552 	// Set the "Create Link" item label here so it
2553 	// appears correctly when menus are disabled, too.
2554 	if (modifierKeys & B_SHIFT_KEY)
2555 		fCreateLinkItem->SetLabel(B_TRANSLATE("Create relative link"));
2556 	else
2557 		fCreateLinkItem->SetLabel(B_TRANSLATE("Create link"));
2558 
2559 	// only enable once the menus are built
2560 	fMoveToItem->SetEnabled(false);
2561 	fCopyToItem->SetEnabled(false);
2562 	fCreateLinkItem->SetEnabled(false);
2563 
2564 	// get ref for item which is selected
2565 	BEntry entry;
2566 	if (entry.SetTo(item_ref) != B_OK)
2567 		return;
2568 
2569 	Model tempModel(&entry);
2570 	if (tempModel.InitCheck() != B_OK)
2571 		return;
2572 
2573 	if (tempModel.IsRoot() || tempModel.IsVolume())
2574 		return;
2575 
2576 	// configure "Move to" menu item
2577 	PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fMoveToItem->Submenu()),
2578 		kMoveSelectionTo, item_ref, true);
2579 
2580 	// configure "Copy to" menu item
2581 	// add all mounted volumes (except the one this item lives on)
2582 	PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fCopyToItem->Submenu()),
2583 		kCopySelectionTo, item_ref, false);
2584 
2585 	// Set "Create Link" menu item message and
2586 	// add all mounted volumes (except the one this item lives on)
2587 	if (modifierKeys & B_SHIFT_KEY) {
2588 		fCreateLinkItem->SetMessage(new BMessage(kCreateRelativeLink));
2589 		PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>
2590 				(fCreateLinkItem->Submenu()),
2591 			kCreateRelativeLink, item_ref, false);
2592 	} else {
2593 		fCreateLinkItem->SetMessage(new BMessage(kCreateLink));
2594 		PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>
2595 			(fCreateLinkItem->Submenu()),
2596 		kCreateLink, item_ref, false);
2597 	}
2598 
2599 	fMoveToItem->SetEnabled(true);
2600 	fCopyToItem->SetEnabled(true);
2601 	fCreateLinkItem->SetEnabled(true);
2602 
2603 	// Set the "Identify" item label
2604 	BMenuItem* identifyItem = parent->FindItem(kIdentifyEntry);
2605 	if (identifyItem != NULL) {
2606 		if (modifierKeys & B_SHIFT_KEY) {
2607 			identifyItem->SetLabel(B_TRANSLATE("Force identify"));
2608 			identifyItem->Message()->ReplaceBool("force", true);
2609 		} else {
2610 			identifyItem->SetLabel(B_TRANSLATE("Identify"));
2611 			identifyItem->Message()->ReplaceBool("force", false);
2612 		}
2613 	}
2614 }
2615 
2616 
2617 uint32
2618 BContainerWindow::ShowDropContextMenu(BPoint loc)
2619 {
2620 	BPoint global(loc);
2621 
2622 	PoseView()->ConvertToScreen(&global);
2623 	PoseView()->CommitActivePose();
2624 
2625 	// Change the "Create Link" item - allow user to
2626 	// create relative links with the Shift key down.
2627 	BMenuItem* item = fDropContextMenu->FindItem(kCreateLink);
2628 	if (item == NULL)
2629 		item = fDropContextMenu->FindItem(kCreateRelativeLink);
2630 	if (item && (modifiers() & B_SHIFT_KEY)) {
2631 		item->SetLabel(B_TRANSLATE("Create relative link here"));
2632 		item->SetMessage(new BMessage(kCreateRelativeLink));
2633 	} else if (item) {
2634 		item->SetLabel(B_TRANSLATE("Create link here"));
2635 		item->SetMessage(new BMessage(kCreateLink));
2636 	}
2637 
2638 	item = fDropContextMenu->Go(global, true, true);
2639 	if (item)
2640 		return item->Command();
2641 
2642 	return 0;
2643 }
2644 
2645 
2646 void
2647 BContainerWindow::ShowContextMenu(BPoint loc, const entry_ref* ref, BView*)
2648 {
2649 	ASSERT(IsLocked());
2650 	BPoint global(loc);
2651 	PoseView()->ConvertToScreen(&global);
2652 	PoseView()->CommitActivePose();
2653 
2654 	if (ref != NULL) {
2655 		// clicked on a pose, show file or volume context menu
2656 		Model model(ref);
2657 
2658 		if (model.IsTrash()) {
2659 			if (fTrashContextMenu->Window() || Dragging())
2660 				return;
2661 
2662 			DeleteSubmenu(fNavigationItem);
2663 
2664 			// selected item was trash, show the trash context menu instead
2665 
2666 			EnableNamedMenuItem(fTrashContextMenu, kEmptyTrash,
2667 				static_cast<TTracker*>(be_app)->TrashFull());
2668 
2669 			SetupNavigationMenu(ref, fTrashContextMenu);
2670 			fTrashContextMenu->Go(global, true, true, true);
2671 		} else {
2672 			bool showAsVolume = false;
2673 			bool filePanel = PoseView()->IsFilePanel();
2674 
2675 			if (Dragging()) {
2676 				fContextMenu = NULL;
2677 
2678 				BEntry entry;
2679 				model.GetEntry(&entry);
2680 
2681 				// only show for directories (directory, volume, root)
2682 				//
2683 				// don't show a popup for the trash or printers
2684 				// trash is handled in DeskWindow
2685 				//
2686 				// since this menu is opened asynchronously
2687 				// we need to make sure we don't open it more
2688 				// than once, the IsShowing flag is set in
2689 				// SlowContextPopup::AttachedToWindow and
2690 				// reset in DetachedFromWindow
2691 				// see the notes in SlowContextPopup::AttachedToWindow
2692 
2693 				if (!FSIsPrintersDir(&entry)
2694 					&& !fDragContextMenu->IsShowing()) {
2695 					//printf("ShowContextMenu - target is %s %i\n",
2696 					//	ref->name, IsShowing(ref));
2697 					fDragContextMenu->ClearMenu();
2698 
2699 					// in case the ref is a symlink, resolve it
2700 					// only pop open for directories
2701 					BEntry resolvedEntry(ref, true);
2702 					if (!resolvedEntry.IsDirectory())
2703 						return;
2704 
2705 					entry_ref resolvedRef;
2706 					resolvedEntry.GetRef(&resolvedRef);
2707 
2708 					// use the resolved ref for the menu
2709 					fDragContextMenu->SetNavDir(&resolvedRef);
2710 					fDragContextMenu->SetTypesList(fCachedTypesList);
2711 					fDragContextMenu->SetTarget(BMessenger(this));
2712 					BPoseView* poseView = PoseView();
2713 					if (poseView != NULL) {
2714 						BMessenger target(poseView);
2715 						fDragContextMenu->InitTrackingHook(
2716 							&BPoseView::MenuTrackingHook, &target,
2717 							fDragMessage);
2718 					}
2719 
2720 					// this is now asynchronous so that we don't
2721 					// deadlock in Window::Quit,
2722 					fDragContextMenu->Go(global, true, false, true);
2723 				}
2724 
2725 				return;
2726 			} else if (TargetModel()->IsRoot() || model.IsVolume()) {
2727 				fContextMenu = fVolumeContextMenu;
2728 				showAsVolume = true;
2729 			} else
2730 				fContextMenu = fFileContextMenu;
2731 
2732 			// clean up items from last context menu
2733 
2734 			if (fContextMenu != NULL) {
2735 				if (fContextMenu->Window())
2736 					return;
2737 				else
2738 					MenusEnded();
2739 
2740 				if (model.InitCheck() == B_OK) { // ??? Do I need this ???
2741 					if (showAsVolume) {
2742 						// non-volume enable/disable copy, move, identify
2743 						EnableNamedMenuItem(fContextMenu, kDuplicateSelection,
2744 							false);
2745 						EnableNamedMenuItem(fContextMenu, kMoveToTrash, false);
2746 						EnableNamedMenuItem(fContextMenu, kIdentifyEntry,
2747 							false);
2748 
2749 						// volume model, enable/disable the Unmount item
2750 						bool ejectableVolumeSelected = false;
2751 
2752 						BVolume boot;
2753 						BVolumeRoster().GetBootVolume(&boot);
2754 						BVolume volume;
2755 						volume.SetTo(model.NodeRef()->device);
2756 						if (volume != boot)
2757 							ejectableVolumeSelected = true;
2758 
2759 						EnableNamedMenuItem(fContextMenu,
2760 							B_TRANSLATE("Unmount"),	ejectableVolumeSelected);
2761 					}
2762 				}
2763 
2764 				SetupNavigationMenu(ref, fContextMenu);
2765 				if (!showAsVolume && !filePanel) {
2766 					SetupMoveCopyMenus(ref, fContextMenu);
2767 					SetupOpenWithMenu(fContextMenu);
2768 				}
2769 
2770 				UpdateMenu(fContextMenu, kPosePopUpContext);
2771 
2772 				fContextMenu->Go(global, true, true, true);
2773 			}
2774 		}
2775 	} else if (fWindowContextMenu != NULL) {
2776 		if (fWindowContextMenu->Window())
2777 			return;
2778 
2779 		// Repopulate desktop menu if IsDesktop
2780 		if (fIsDesktop)
2781 			RepopulateMenus();
2782 
2783 		MenusEnded();
2784 
2785 		// clicked on a window, show window context menu
2786 
2787 		SetupNavigationMenu(ref, fWindowContextMenu);
2788 		UpdateMenu(fWindowContextMenu, kWindowPopUpContext);
2789 
2790 		fWindowContextMenu->Go(global, true, true, true);
2791 	}
2792 
2793 	fContextMenu = NULL;
2794 }
2795 
2796 
2797 void
2798 BContainerWindow::AddFileContextMenus(BMenu* menu)
2799 {
2800 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2801 		new BMessage(kOpenSelection), 'O'));
2802 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2803 		new BMessage(kGetInfo), 'I'));
2804 	menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
2805 		new BMessage(kEditItem), 'E'));
2806 
2807 	if (!IsTrash() && !InTrash() && !IsPrintersDir()) {
2808 		menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"),
2809 			new BMessage(kDuplicateSelection), 'D'));
2810 	}
2811 
2812 	if (!IsTrash() && !InTrash()) {
2813 		menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash()
2814 			? B_TRANSLATE("Delete")	: B_TRANSLATE("Move to Trash"),
2815 			new BMessage(kMoveToTrash), 'T'));
2816 		if (!IsPrintersDir()) {
2817 			// add separator for copy to/move to items (navigation items)
2818 			menu->AddSeparatorItem();
2819 		}
2820 	} else {
2821 		menu->AddItem(new BMenuItem(B_TRANSLATE("Delete"),
2822 			new BMessage(kDelete), 0));
2823 		menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"),
2824 			new BMessage(kRestoreFromTrash), 0));
2825 	}
2826 
2827 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2828 	menu->AddSeparatorItem();
2829 	BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"),
2830 		new BMessage(B_CUT), 'X');
2831 	menu->AddItem(cutItem);
2832 	BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"),
2833 		new BMessage(B_COPY), 'C');
2834 	menu->AddItem(copyItem);
2835 #endif
2836 
2837 	menu->AddSeparatorItem();
2838 	BMessage* message = new BMessage(kIdentifyEntry);
2839 	message->AddBool("force", false);
2840 	menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"), message));
2841 	BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
2842 	addOnMenuItem->SetFont(be_plain_font);
2843 	menu->AddItem(addOnMenuItem);
2844 
2845 	// set targets as needed
2846 	menu->SetTargetForItems(PoseView());
2847 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2848 	cutItem->SetTarget(this);
2849 	copyItem->SetTarget(this);
2850 #endif
2851 }
2852 
2853 
2854 void
2855 BContainerWindow::AddVolumeContextMenus(BMenu* menu)
2856 {
2857 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2858 		new BMessage(kOpenSelection), 'O'));
2859 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2860 		new BMessage(kGetInfo), 'I'));
2861 	menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
2862 		new BMessage(kEditItem), 'E'));
2863 
2864 	menu->AddSeparatorItem();
2865 	menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
2866 
2867 	BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"),
2868 		new BMessage(kUnmountVolume), 'U');
2869 	item->SetEnabled(false);
2870 	menu->AddItem(item);
2871 
2872 	menu->AddSeparatorItem();
2873 	menu->AddItem(new BMenu(B_TRANSLATE("Add-ons")));
2874 
2875 	menu->SetTargetForItems(PoseView());
2876 }
2877 
2878 
2879 void
2880 BContainerWindow::AddWindowContextMenus(BMenu* menu)
2881 {
2882 	// create context sensitive menu for empty area of window
2883 	// since we check view mode before display, this should be a radio
2884 	// mode menu
2885 
2886 	Model* targetModel = TargetModel();
2887 	ASSERT(targetModel != NULL);
2888 
2889 	bool needSeparator = true;
2890 	if (IsTrash()) {
2891 		menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
2892 			new BMessage(kEmptyTrash)));
2893 	} else if (IsPrintersDir()) {
2894 		menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS),
2895 			new BMessage(kAddPrinter), 'N'));
2896 	} else if (InTrash() || targetModel->IsRoot()) {
2897 		needSeparator = false;
2898 	} else {
2899 		TemplatesMenu* templatesMenu = new TemplatesMenu(PoseView(),
2900 			B_TRANSLATE("New"));
2901 		menu->AddItem(templatesMenu);
2902 		templatesMenu->SetTargetForItems(PoseView());
2903 		templatesMenu->SetFont(be_plain_font);
2904 	}
2905 
2906 	if (needSeparator)
2907 		menu->AddSeparatorItem();
2908 
2909 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2910 	BMenuItem* pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V');
2911 	menu->AddItem(pasteItem);
2912 	menu->AddSeparatorItem();
2913 #endif
2914 
2915 	BMenu* arrangeBy = new BMenu(B_TRANSLATE("Arrange by"));
2916 	PopulateArrangeByMenu(arrangeBy);
2917 	menu->AddItem(arrangeBy);
2918 
2919 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
2920 		new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY));
2921 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
2922 		new BMessage(B_SELECT_ALL), 'A'));
2923 	if (!IsTrash()) {
2924 		menu->AddItem(new BMenuItem(B_TRANSLATE("Open parent"),
2925 			new BMessage(kOpenParentDir), B_UP_ARROW));
2926 	}
2927 
2928 	if (targetModel->IsRoot()) {
2929 		menu->AddSeparatorItem();
2930 		menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
2931 	}
2932 
2933 	menu->AddSeparatorItem();
2934 	BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
2935 	addOnMenuItem->SetFont(be_plain_font);
2936 	menu->AddItem(addOnMenuItem);
2937 
2938 #if DEBUG
2939 	menu->AddSeparatorItem();
2940 	BMenuItem* testing = new BMenuItem("Test icon cache",
2941 		new BMessage(kTestIconCache));
2942 	menu->AddItem(testing);
2943 #endif
2944 
2945 	// target items as needed
2946 	menu->SetTargetForItems(PoseView());
2947 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2948 	pasteItem->SetTarget(this);
2949 #endif
2950 }
2951 
2952 
2953 void
2954 BContainerWindow::AddDropContextMenus(BMenu* menu)
2955 {
2956 	menu->AddItem(new BMenuItem(B_TRANSLATE("Create link here"),
2957 		new BMessage(kCreateLink)));
2958 	menu->AddItem(new BMenuItem(B_TRANSLATE("Move here"),
2959 		new BMessage(kMoveSelectionTo)));
2960 	menu->AddItem(new BMenuItem(B_TRANSLATE("Copy here"),
2961 		new BMessage(kCopySelectionTo)));
2962 	menu->AddSeparatorItem();
2963 	menu->AddItem(new BMenuItem(B_TRANSLATE("Cancel"),
2964 		new BMessage(kCancelButton)));
2965 }
2966 
2967 
2968 void
2969 BContainerWindow::AddTrashContextMenus(BMenu* menu)
2970 {
2971 	// setup special trash context menu
2972 	menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
2973 		new BMessage(kEmptyTrash)));
2974 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2975 		new BMessage(kOpenSelection), 'O'));
2976 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2977 		new BMessage(kGetInfo), 'I'));
2978 	menu->SetTargetForItems(PoseView());
2979 }
2980 
2981 
2982 void
2983 BContainerWindow::EachAddon(bool (*eachAddon)(const Model*, const char*,
2984 	uint32 shortcut, uint32 modifiers, bool primary, void* context),
2985 	void* passThru, BStringList& mimeTypes)
2986 {
2987 	AutoLock<LockingList<AddonShortcut> > lock(fAddonsList);
2988 	if (lock.IsLocked()) {
2989 		for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) {
2990 			struct AddonShortcut* item = fAddonsList->ItemAt(i);
2991 			bool primary = false;
2992 
2993 			if (mimeTypes.CountStrings() > 0) {
2994 				BFile file(item->model->EntryRef(), B_READ_ONLY);
2995 				if (file.InitCheck() == B_OK) {
2996 					BAppFileInfo info(&file);
2997 					if (info.InitCheck() == B_OK) {
2998 						bool secondary = true;
2999 
3000 						// does this add-on has types set at all?
3001 						BMessage message;
3002 						if (info.GetSupportedTypes(&message) == B_OK) {
3003 							type_code typeCode;
3004 							int32 count;
3005 							if (message.GetInfo("types", &typeCode,
3006 									&count) == B_OK) {
3007 								secondary = false;
3008 							}
3009 						}
3010 
3011 						// check all supported types if it has some set
3012 						if (!secondary) {
3013 							for (int32 i = mimeTypes.CountStrings();
3014 									!primary && i-- > 0;) {
3015 								BString type = mimeTypes.StringAt(i);
3016 								if (info.IsSupportedType(type.String())) {
3017 									BMimeType mimeType(type.String());
3018 									if (info.Supports(&mimeType))
3019 										primary = true;
3020 									else
3021 										secondary = true;
3022 								}
3023 							}
3024 						}
3025 
3026 						if (!secondary && !primary)
3027 							continue;
3028 					}
3029 				}
3030 			}
3031 			((eachAddon)(item->model, item->model->Name(), item->key,
3032 				item->modifiers, primary, passThru));
3033 		}
3034 	}
3035 }
3036 
3037 
3038 void
3039 BContainerWindow::BuildMimeTypeList(BStringList& mimeTypes)
3040 {
3041 	int32 count = PoseView()->SelectionList()->CountItems();
3042 	if (count <= 0) {
3043 		// just add the type of the current directory
3044 		AddMimeTypeString(mimeTypes, TargetModel());
3045 	} else {
3046 		_UpdateSelectionMIMEInfo();
3047 		for (int32 index = 0; index < count; index++) {
3048 			BPose* pose = PoseView()->SelectionList()->ItemAt(index);
3049 			AddMimeTypeString(mimeTypes, pose->TargetModel());
3050 			// If it's a symlink, resolves it and add the Target's MimeType
3051 			if (pose->TargetModel()->IsSymLink()) {
3052 				Model* resolved = new Model(
3053 					pose->TargetModel()->EntryRef(), true, true);
3054 				if (resolved->InitCheck() == B_OK)
3055 					AddMimeTypeString(mimeTypes, resolved);
3056 
3057 				delete resolved;
3058 			}
3059 		}
3060 	}
3061 }
3062 
3063 
3064 void
3065 BContainerWindow::BuildAddOnMenu(BMenu* menu)
3066 {
3067 	BMenuItem* item = menu->FindItem(B_TRANSLATE("Add-ons"));
3068 	if (menu->IndexOf(item) == 0) {
3069 		// the folder of the context menu seems to be named "Add-Ons"
3070 		// so we just take the last menu item, which is correct if not
3071 		// build with debug option
3072 		item = menu->ItemAt(menu->CountItems() - 1);
3073 	}
3074 	if (item == NULL)
3075 		return;
3076 
3077 	BFont font;
3078 	menu->GetFont(&font);
3079 
3080 	menu = item->Submenu();
3081 	if (menu == NULL)
3082 		return;
3083 
3084 	menu->SetFont(&font);
3085 
3086 	// found the addons menu, empty it first
3087 	for (;;) {
3088 		item = menu->RemoveItem((int32)0);
3089 		if (!item)
3090 			break;
3091 		delete item;
3092 	}
3093 
3094 	BObjectList<BMenuItem> primaryList;
3095 	BObjectList<BMenuItem> secondaryList;
3096 	BStringList mimeTypes(10);
3097 	BuildMimeTypeList(mimeTypes);
3098 
3099 	AddOneAddonParams params;
3100 	params.primaryList = &primaryList;
3101 	params.secondaryList = &secondaryList;
3102 
3103 	// build a list of the MIME types of the selected items
3104 
3105 	EachAddon(AddOneAddon, &params, mimeTypes);
3106 
3107 	primaryList.SortItems(CompareLabels);
3108 	secondaryList.SortItems(CompareLabels);
3109 
3110 	int32 count = primaryList.CountItems();
3111 	for (int32 index = 0; index < count; index++)
3112 		menu->AddItem(primaryList.ItemAt(index));
3113 
3114 	if (count > 0)
3115 		menu->AddSeparatorItem();
3116 
3117 	count = secondaryList.CountItems();
3118 	for (int32 index = 0; index < count; index++)
3119 		menu->AddItem(secondaryList.ItemAt(index));
3120 
3121 	menu->SetTargetForItems(this);
3122 }
3123 
3124 
3125 void
3126 BContainerWindow::UpdateMenu(BMenu* menu, UpdateMenuContext context)
3127 {
3128 	const int32 selectCount = PoseView()->SelectionList()->CountItems();
3129 	const int32 count = PoseView()->CountItems();
3130 
3131 	if (context == kMenuBarContext) {
3132 		EnableNamedMenuItem(menu, kOpenSelection, selectCount > 0);
3133 		EnableNamedMenuItem(menu, kGetInfo, selectCount > 0);
3134 		EnableNamedMenuItem(menu, kIdentifyEntry, selectCount > 0);
3135 		EnableNamedMenuItem(menu, kMoveToTrash, selectCount > 0);
3136 		EnableNamedMenuItem(menu, kRestoreFromTrash, selectCount > 0);
3137 		EnableNamedMenuItem(menu, kDelete, selectCount > 0);
3138 		EnableNamedMenuItem(menu, kDuplicateSelection, selectCount > 0);
3139 	}
3140 
3141 	Model* selectedModel = NULL;
3142 	if (selectCount == 1) {
3143 		selectedModel = PoseView()->SelectionList()->FirstItem()->
3144 			TargetModel();
3145 	}
3146 
3147 	if (context == kMenuBarContext || context == kPosePopUpContext) {
3148 		SetUpEditQueryItem(menu);
3149 		EnableNamedMenuItem(menu, kEditItem, selectCount == 1
3150 			&& (context == kPosePopUpContext || !PoseView()->ActivePose())
3151 			&& selectedModel != NULL
3152 			&& !selectedModel->IsDesktop()
3153 			&& !selectedModel->IsRoot()
3154 			&& !selectedModel->IsTrash()
3155 			&& !selectedModel->HasLocalizedName());
3156 		SetCutItem(menu);
3157 		SetCopyItem(menu);
3158 		SetPasteItem(menu);
3159 	}
3160 
3161 	if (context == kMenuBarContext || context == kWindowPopUpContext) {
3162 		uint32 viewMode = PoseView()->ViewMode();
3163 
3164 		BMenu* iconSizeMenu = NULL;
3165 		if (BMenuItem* item = menu->FindItem(kIconMode))
3166 			iconSizeMenu = item->Submenu();
3167 
3168 		if (iconSizeMenu != NULL) {
3169 			if (viewMode == kIconMode) {
3170 				int32 iconSize = PoseView()->IconSizeInt();
3171 				BMenuItem* item = iconSizeMenu->ItemAt(0);
3172 				for (int32 i = 0; (item = iconSizeMenu->ItemAt(i)) != NULL;
3173 						i++) {
3174 					BMessage* message = item->Message();
3175 					if (message == NULL) {
3176 						item->SetMarked(false);
3177 						continue;
3178 					}
3179 					int32 size;
3180 					if (message->FindInt32("size", &size) != B_OK)
3181 						size = -1;
3182 					item->SetMarked(iconSize == size);
3183 				}
3184 			} else {
3185 				BMenuItem* item;
3186 				for (int32 i = 0; (item = iconSizeMenu->ItemAt(i)) != NULL; i++)
3187 					item->SetMarked(false);
3188 			}
3189 		}
3190 
3191 		BMenu* listSizeMenu = NULL;
3192 		if (BMenuItem* item = menu->FindItem(kListMode))
3193 			listSizeMenu = item->Submenu();
3194 
3195 		if (listSizeMenu != NULL) {
3196 			if (viewMode == kListMode) {
3197 				int32 iconSize = PoseView()->IconSizeInt();
3198 				BMenuItem* item = listSizeMenu->ItemAt(0);
3199 				for (int32 i = 0; (item = listSizeMenu->ItemAt(i)) != NULL;
3200 						i++) {
3201 					BMessage* message = item->Message();
3202 					if (message == NULL) {
3203 						item->SetMarked(false);
3204 						continue;
3205 					}
3206 					int32 size;
3207 					if (message->FindInt32("icon_size", &size) != B_OK)
3208 						size = -1;
3209 					item->SetMarked(iconSize == size);
3210 				}
3211 			} else {
3212 				BMenuItem* item;
3213 				for (int32 i = 0; (item = listSizeMenu->ItemAt(i)) != NULL; i++)
3214 					item->SetMarked(false);
3215 			}
3216 		}
3217 
3218 		MarkNamedMenuItem(menu, kIconMode, viewMode == kIconMode);
3219 		MarkNamedMenuItem(menu, kListMode, viewMode == kListMode);
3220 		MarkNamedMenuItem(menu, kMiniIconMode, viewMode == kMiniIconMode);
3221 
3222 		SetCloseItem(menu);
3223 		SetArrangeMenu(menu);
3224 		SetPasteItem(menu);
3225 
3226 		BEntry entry(TargetModel()->EntryRef());
3227 		BDirectory parent;
3228 		entry_ref ref;
3229 		BEntry root("/");
3230 
3231 		bool parentIsRoot = (entry.GetParent(&parent) == B_OK
3232 			&& parent.GetEntry(&entry) == B_OK
3233 			&& entry.GetRef(&ref) == B_OK
3234 			&& entry == root);
3235 
3236 		EnableNamedMenuItem(menu, kOpenParentDir, !TargetModel()->IsDesktop()
3237 			&& !TargetModel()->IsRoot()
3238 			&& (!parentIsRoot
3239 				|| TrackerSettings().SingleWindowBrowse()
3240 				|| TrackerSettings().ShowDisksIcon()
3241 				|| (modifiers() & B_CONTROL_KEY) != 0));
3242 
3243 		EnableNamedMenuItem(menu, kEmptyTrash, count > 0);
3244 		EnableNamedMenuItem(menu, B_SELECT_ALL, count > 0);
3245 
3246 		BMenuItem* item = menu->FindItem(B_TRANSLATE("New"));
3247 		if (item != NULL) {
3248 			TemplatesMenu* templatesMenu = dynamic_cast<TemplatesMenu*>(
3249 				item->Submenu());
3250 			if (templatesMenu != NULL)
3251 				templatesMenu->UpdateMenuState();
3252 		}
3253 	}
3254 
3255 	BuildAddOnMenu(menu);
3256 }
3257 
3258 
3259 void
3260 BContainerWindow::LoadAddOn(BMessage* message)
3261 {
3262 	UpdateIfNeeded();
3263 
3264 	entry_ref addonRef;
3265 	status_t result = message->FindRef("refs", &addonRef);
3266 	if (result != B_OK) {
3267 		BString buffer(B_TRANSLATE("Error %error loading add-On %name."));
3268 		buffer.ReplaceFirst("%error", strerror(result));
3269 		buffer.ReplaceFirst("%name", addonRef.name);
3270 
3271 		BAlert* alert = new BAlert("", buffer.String(),	B_TRANSLATE("Cancel"),
3272 			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3273 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3274 		alert->Go();
3275 		return;
3276 	}
3277 
3278 	// add selected refs to message
3279 	BMessage* refs = new BMessage(B_REFS_RECEIVED);
3280 	BObjectList<BPose>* selectionList = PoseView()->SelectionList();
3281 
3282 	int32 index = 0;
3283 	BPose* pose;
3284 	while ((pose = selectionList->ItemAt(index++)) != NULL)
3285 		refs->AddRef("refs", pose->TargetModel()->EntryRef());
3286 
3287 	refs->AddMessenger("TrackerViewToken", BMessenger(PoseView()));
3288 
3289 	LaunchInNewThread("Add-on", B_NORMAL_PRIORITY, &AddOnThread, refs,
3290 		addonRef, *TargetModel()->EntryRef());
3291 }
3292 
3293 
3294 void
3295 BContainerWindow::_UpdateSelectionMIMEInfo()
3296 {
3297 	BPose* pose;
3298 	int32 index = 0;
3299 	while ((pose = PoseView()->SelectionList()->ItemAt(index++)) != NULL) {
3300 		BString mimeType(pose->TargetModel()->MimeType());
3301 		if (!mimeType.Length() || mimeType.ICompare(B_FILE_MIMETYPE) == 0) {
3302 			pose->TargetModel()->Mimeset(true);
3303 			if (pose->TargetModel()->IsSymLink()) {
3304 				Model* resolved = new Model(pose->TargetModel()->EntryRef(),
3305 					true, true);
3306 				if (resolved->InitCheck() == B_OK) {
3307 					mimeType.SetTo(resolved->MimeType());
3308 					if (!mimeType.Length()
3309 						|| mimeType.ICompare(B_FILE_MIMETYPE) == 0) {
3310 						resolved->Mimeset(true);
3311 					}
3312 				}
3313 				delete resolved;
3314 			}
3315 		}
3316 	}
3317 }
3318 
3319 
3320 void
3321 BContainerWindow::_AddFolderIcon()
3322 {
3323 	if (fMenuBar == NULL) {
3324 		// We don't want to add the icon if there's no menubar
3325 		return;
3326 	}
3327 
3328 	float iconSize = fMenuBar->Bounds().Height() - 2;
3329 	if (iconSize < 16)
3330 		iconSize = 16;
3331 
3332 	fDraggableIcon = new(std::nothrow) DraggableContainerIcon();
3333 	if (fDraggableIcon != NULL) {
3334 		BLayoutItem* item = fMenuContainer->GroupLayout()->AddView(
3335 			fDraggableIcon);
3336 		item->SetExplicitMinSize(BSize(iconSize + 5, iconSize));
3337 		item->SetExplicitMaxSize(BSize(iconSize + 5, item->MaxSize().Height()));
3338 
3339 		fMenuBar->SetBorders(
3340 			BControlLook::B_ALL_BORDERS & ~BControlLook::B_RIGHT_BORDER);
3341 	}
3342 }
3343 
3344 
3345 BMenuItem*
3346 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name,
3347 	int32 type, float width, int32 align, bool editable, bool statField)
3348 {
3349 	return NewAttributeMenuItem(label, name, type, NULL, width, align,
3350 		editable, statField);
3351 }
3352 
3353 
3354 BMenuItem*
3355 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name,
3356 	int32 type, const char* displayAs, float width, int32 align,
3357 	bool editable, bool statField)
3358 {
3359 	BMessage* message = new BMessage(kAttributeItem);
3360 	message->AddString("attr_name", name);
3361 	message->AddInt32("attr_type", type);
3362 	message->AddInt32("attr_hash", (int32)AttrHashString(name, (uint32)type));
3363 	message->AddFloat("attr_width", width);
3364 	message->AddInt32("attr_align", align);
3365 	if (displayAs != NULL)
3366 		message->AddString("attr_display_as", displayAs);
3367 	message->AddBool("attr_editable", editable);
3368 	message->AddBool("attr_statfield", statField);
3369 
3370 	BMenuItem* menuItem = new BMenuItem(label, message);
3371 	menuItem->SetTarget(PoseView());
3372 
3373 	return menuItem;
3374 }
3375 
3376 
3377 void
3378 BContainerWindow::NewAttributeMenu(BMenu* menu)
3379 {
3380 	ASSERT(PoseView());
3381 
3382 	BMenuItem* item;
3383 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy layout"),
3384 		new BMessage(kCopyAttributes)));
3385 	item->SetTarget(PoseView());
3386 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Paste layout"),
3387 		new BMessage(kPasteAttributes)));
3388 	item->SetTarget(PoseView());
3389 	menu->AddSeparatorItem();
3390 
3391 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Name"),
3392 		kAttrStatName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true));
3393 
3394 	if (gLocalizedNamePreferred) {
3395 		menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Real name"),
3396 			kAttrRealName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true));
3397 	}
3398 
3399 	menu->AddItem(NewAttributeMenuItem (B_TRANSLATE("Size"), kAttrStatSize,
3400 		B_OFF_T_TYPE, 80, B_ALIGN_RIGHT, false, true));
3401 
3402 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Modified"),
3403 		kAttrStatModified, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true));
3404 
3405 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Created"),
3406 		kAttrStatCreated, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true));
3407 
3408 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Kind"),
3409 		kAttrMIMEType, B_MIME_STRING_TYPE, 145, B_ALIGN_LEFT, false, false));
3410 
3411 	if (IsTrash() || InTrash()) {
3412 		menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Original name"),
3413 			kAttrOriginalPath, B_STRING_TYPE, 225, B_ALIGN_LEFT, false,
3414 			false));
3415 	} else {
3416 		menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Location"), kAttrPath,
3417 			B_STRING_TYPE, 225, B_ALIGN_LEFT, false, false));
3418 	}
3419 
3420 #ifdef OWNER_GROUP_ATTRIBUTES
3421 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Owner"), kAttrStatOwner,
3422 		B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true));
3423 
3424 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Group"), kAttrStatGroup,
3425 		B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true));
3426 #endif
3427 
3428 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Permissions"),
3429 		kAttrStatMode, B_STRING_TYPE, 80, B_ALIGN_LEFT, false, true));
3430 }
3431 
3432 
3433 void
3434 BContainerWindow::ShowAttributeMenu()
3435 {
3436 	ASSERT(fAttrMenu);
3437 	fMenuBar->AddItem(fAttrMenu);
3438 }
3439 
3440 
3441 void
3442 BContainerWindow::HideAttributeMenu()
3443 {
3444 	ASSERT(fAttrMenu);
3445 	fMenuBar->RemoveItem(fAttrMenu);
3446 }
3447 
3448 
3449 void
3450 BContainerWindow::MarkAttributeMenu()
3451 {
3452 	MarkAttributeMenu(fAttrMenu);
3453 }
3454 
3455 
3456 void
3457 BContainerWindow::MarkAttributeMenu(BMenu* menu)
3458 {
3459 	if (!menu)
3460 		return;
3461 
3462 	int32 count = menu->CountItems();
3463 	for (int32 index = 0; index < count; index++) {
3464 		BMenuItem* item = menu->ItemAt(index);
3465 		int32 attrHash;
3466 		if (item->Message()) {
3467 			if (item->Message()->FindInt32("attr_hash", &attrHash) == B_OK)
3468 				item->SetMarked(PoseView()->ColumnFor((uint32)attrHash) != 0);
3469 			else
3470 				item->SetMarked(false);
3471 		}
3472 
3473 		BMenu* submenu = item->Submenu();
3474 		if (submenu) {
3475 			int32 count2 = submenu->CountItems();
3476 			for (int32 subindex = 0; subindex < count2; subindex++) {
3477 				item = submenu->ItemAt(subindex);
3478 				if (item->Message()) {
3479 					if (item->Message()->FindInt32("attr_hash", &attrHash)
3480 						== B_OK) {
3481 						item->SetMarked(PoseView()->ColumnFor((uint32)attrHash)
3482 							!= 0);
3483 					} else
3484 						item->SetMarked(false);
3485 				}
3486 			}
3487 		}
3488 	}
3489 }
3490 
3491 
3492 void
3493 BContainerWindow::MarkArrangeByMenu(BMenu* menu)
3494 {
3495 	if (!menu)
3496 		return;
3497 
3498 	int32 count = menu->CountItems();
3499 	for (int32 index = 0; index < count; index++) {
3500 		BMenuItem* item = menu->ItemAt(index);
3501 		if (item->Message()) {
3502 			uint32 attrHash;
3503 			if (item->Message()->FindInt32("attr_hash",
3504 					(int32*)&attrHash) == B_OK) {
3505 				item->SetMarked(PoseView()->PrimarySort() == attrHash);
3506 			} else if (item->Command() == kArrangeReverseOrder)
3507 				item->SetMarked(PoseView()->ReverseSort());
3508 		}
3509 	}
3510 }
3511 
3512 
3513 void
3514 BContainerWindow::AddMimeTypesToMenu()
3515 {
3516 	AddMimeTypesToMenu(fAttrMenu);
3517 }
3518 
3519 
3520 // Adds a menu for a specific MIME type if it doesn't exist already.
3521 // Returns the menu, if it existed or not.
3522 BMenu*
3523 BContainerWindow::AddMimeMenu(const BMimeType& mimeType, bool isSuperType,
3524 	BMenu* menu, int32 start)
3525 {
3526 	AutoLock<BLooper> _(menu->Looper());
3527 
3528 	if (!mimeType.IsValid())
3529 		return NULL;
3530 
3531 	// Check if we already have an entry for this MIME type in the menu.
3532 	for (int32 i = start; BMenuItem* item = menu->ItemAt(i); i++) {
3533 		BMessage* message = item->Message();
3534 		if (message == NULL)
3535 			continue;
3536 
3537 		const char* type;
3538 		if (message->FindString("mimetype", &type) == B_OK
3539 			&& !strcmp(mimeType.Type(), type)) {
3540 			return item->Submenu();
3541 		}
3542 	}
3543 
3544 	BMessage attrInfo;
3545 	char description[B_MIME_TYPE_LENGTH];
3546 	const char* label = mimeType.Type();
3547 
3548 	if (!mimeType.IsInstalled())
3549 		return NULL;
3550 
3551 	// only add things to menu which have "user-visible" data
3552 	if (mimeType.GetAttrInfo(&attrInfo) != B_OK)
3553 		return NULL;
3554 
3555 	if (mimeType.GetShortDescription(description) == B_OK && description[0])
3556 		label = description;
3557 
3558 	// go through each field in meta mime and add it to a menu
3559 	BMenu* mimeMenu = NULL;
3560 	if (isSuperType) {
3561 		// If it is a supertype, we create the menu anyway as it may have
3562 		// submenus later on.
3563 		mimeMenu = new BMenu(label);
3564 		BFont font;
3565 		menu->GetFont(&font);
3566 		mimeMenu->SetFont(&font);
3567 	}
3568 
3569 	int32 index = -1;
3570 	const char* publicName;
3571 	while (attrInfo.FindString("attr:public_name", ++index, &publicName)
3572 			== B_OK) {
3573 		if (!attrInfo.FindBool("attr:viewable", index)) {
3574 			// don't add if attribute not viewable
3575 			continue;
3576 		}
3577 
3578 		int32 type;
3579 		int32 align;
3580 		int32 width;
3581 		bool editable;
3582 		const char* attrName;
3583 		if (attrInfo.FindString("attr:name", index, &attrName) != B_OK
3584 			|| attrInfo.FindInt32("attr:type", index, &type) != B_OK
3585 			|| attrInfo.FindBool("attr:editable", index, &editable) != B_OK
3586 			|| attrInfo.FindInt32("attr:width", index, &width) != B_OK
3587 			|| attrInfo.FindInt32("attr:alignment", index, &align) != B_OK)
3588 			continue;
3589 
3590 		BString displayAs;
3591 		attrInfo.FindString("attr:display_as", index, &displayAs);
3592 
3593 		if (mimeMenu == NULL) {
3594 			// do a lazy allocation of the menu
3595 			mimeMenu = new BMenu(label);
3596 			BFont font;
3597 			menu->GetFont(&font);
3598 			mimeMenu->SetFont(&font);
3599 		}
3600 		mimeMenu->AddItem(NewAttributeMenuItem(publicName, attrName, type,
3601 			displayAs.String(), width, align, editable, false));
3602 	}
3603 
3604 	if (mimeMenu == NULL)
3605 		return NULL;
3606 
3607 	BMessage* message = new BMessage(kMIMETypeItem);
3608 	message->AddString("mimetype", mimeType.Type());
3609 	menu->AddItem(new IconMenuItem(mimeMenu, message, mimeType.Type()));
3610 
3611 	return mimeMenu;
3612 }
3613 
3614 
3615 void
3616 BContainerWindow::AddMimeTypesToMenu(BMenu* menu)
3617 {
3618 	if (!menu)
3619 		return;
3620 
3621 	// Remove old mime type menus
3622 	int32 start = menu->CountItems();
3623 	while (start > 0 && menu->ItemAt(start - 1)->Submenu() != NULL) {
3624 		delete menu->RemoveItem(start - 1);
3625 		start--;
3626 	}
3627 
3628 	// Add a separator item if there is none yet
3629 	if (start > 0
3630 		&& dynamic_cast<BSeparatorItem*>(menu->ItemAt(start - 1)) == NULL)
3631 		menu->AddSeparatorItem();
3632 
3633 	// Add MIME type in case we're a default query type window
3634 	BPath path;
3635 	if (TargetModel() != NULL) {
3636 		TargetModel()->GetPath(&path);
3637 		if (path.InitCheck() == B_OK
3638 			&& strstr(path.Path(), "/" kQueryTemplates "/") != NULL) {
3639 			// demangle MIME type name
3640 			BString name(TargetModel()->Name());
3641 			name.ReplaceFirst('_', '/');
3642 
3643 			PoseView()->AddMimeType(name.String());
3644 		}
3645 	}
3646 
3647 	// Add MIME type menus
3648 
3649 	int32 typeCount = PoseView()->CountMimeTypes();
3650 
3651 	for (int32 index = 0; index < typeCount; index++) {
3652 		BMimeType mimeType(PoseView()->MimeTypeAt(index));
3653 		if (mimeType.InitCheck() == B_OK) {
3654 			BMimeType superType;
3655 			mimeType.GetSupertype(&superType);
3656 			if (superType.InitCheck() == B_OK) {
3657 				BMenu* superMenu = AddMimeMenu(superType, true, menu, start);
3658 				if (superMenu != NULL) {
3659 					// We have a supertype menu.
3660 					AddMimeMenu(mimeType, false, superMenu, 0);
3661 				}
3662 			}
3663 		}
3664 	}
3665 
3666 	// remove empty super menus, promote sub-types if needed
3667 
3668 	for (int32 index = 0; index < typeCount; index++) {
3669 		BMimeType mimeType(PoseView()->MimeTypeAt(index));
3670 		BMimeType superType;
3671 		mimeType.GetSupertype(&superType);
3672 
3673 		BMenu* superMenu = AddMimeMenu(superType, true, menu, start);
3674 		if (superMenu == NULL)
3675 			continue;
3676 
3677 		int32 itemsFound = 0;
3678 		int32 menusFound = 0;
3679 		for (int32 i = 0; BMenuItem* item = superMenu->ItemAt(i); i++) {
3680 			if (item->Submenu() != NULL)
3681 				menusFound++;
3682 			else
3683 				itemsFound++;
3684 		}
3685 
3686 		if (itemsFound == 0) {
3687 			if (menusFound != 0) {
3688 				// promote types to the top level
3689 				while (BMenuItem* item = superMenu->RemoveItem((int32)0)) {
3690 					menu->AddItem(item);
3691 				}
3692 			}
3693 
3694 			menu->RemoveItem(superMenu->Superitem());
3695 			delete superMenu->Superitem();
3696 		}
3697 	}
3698 
3699 	// remove separator if it's the only item in menu
3700 	BMenuItem* item = menu->ItemAt(menu->CountItems() - 1);
3701 	if (dynamic_cast<BSeparatorItem*>(item) != NULL) {
3702 		menu->RemoveItem(item);
3703 		delete item;
3704 	}
3705 
3706 	MarkAttributeMenu(menu);
3707 }
3708 
3709 
3710 BHandler*
3711 BContainerWindow::ResolveSpecifier(BMessage* message, int32 index,
3712 	BMessage* specifier, int32 form, const char* property)
3713 {
3714 	if (strcmp(property, "Poses") == 0) {
3715 //		PRINT(("BContainerWindow::ResolveSpecifier %s\n", property));
3716 		message->PopSpecifier();
3717 		return PoseView();
3718 	}
3719 
3720 	return _inherited::ResolveSpecifier(message, index, specifier,
3721 		form, property);
3722 }
3723 
3724 
3725 PiggybackTaskLoop*
3726 BContainerWindow::DelayedTaskLoop()
3727 {
3728 	if (!fTaskLoop)
3729 		fTaskLoop = new PiggybackTaskLoop;
3730 
3731 	return fTaskLoop;
3732 }
3733 
3734 
3735 bool
3736 BContainerWindow::NeedsDefaultStateSetup()
3737 {
3738 	if (TargetModel() == NULL)
3739 		return false;
3740 
3741 	if (TargetModel()->IsRoot()) {
3742 		// don't try to set up anything if we are root
3743 		return false;
3744 	}
3745 
3746 	WindowStateNodeOpener opener(this, false);
3747 	if (opener.StreamNode() == NULL) {
3748 		// can't read state, give up
3749 		return false;
3750 	}
3751 
3752 	return !NodeHasSavedState(opener.Node());
3753 }
3754 
3755 
3756 bool
3757 BContainerWindow::DefaultStateSourceNode(const char* name, BNode* result,
3758 	bool createNew, bool createFolder)
3759 {
3760 	//PRINT(("looking for default state in tracker settings dir\n"));
3761 	BPath settingsPath;
3762 	if (FSFindTrackerSettingsDir(&settingsPath) != B_OK)
3763 		return false;
3764 
3765 	BDirectory dir(settingsPath.Path());
3766 
3767 	BPath path(settingsPath);
3768 	path.Append(name);
3769 	if (!BEntry(path.Path()).Exists()) {
3770 		if (!createNew)
3771 			return false;
3772 
3773 		BPath tmpPath(settingsPath);
3774 		for (;;) {
3775 			// deal with several levels of folders
3776 			const char* nextSlash = strchr(name, '/');
3777 			if (!nextSlash)
3778 				break;
3779 
3780 			BString tmp;
3781 			tmp.SetTo(name, nextSlash - name);
3782 			tmpPath.Append(tmp.String());
3783 
3784 			mkdir(tmpPath.Path(), 0777);
3785 
3786 			name = nextSlash + 1;
3787 			if (!name[0]) {
3788 				// can't deal with a slash at end
3789 				return false;
3790 			}
3791 		}
3792 
3793 		if (createFolder) {
3794 			if (mkdir(path.Path(), 0777) < 0)
3795 				return false;
3796 		} else {
3797 			BFile file;
3798 			if (dir.CreateFile(name, &file) != B_OK)
3799 				return false;
3800 		}
3801 	}
3802 
3803 	//PRINT(("using default state from %s\n", path.Path()));
3804 	result->SetTo(path.Path());
3805 	return result->InitCheck() == B_OK;
3806 }
3807 
3808 
3809 void
3810 BContainerWindow::SetUpDefaultState()
3811 {
3812 	BNode defaultingNode;
3813 		// this is where we'll ulitimately get the state from
3814 	bool gotDefaultingNode = 0;
3815 	bool shouldStagger = false;
3816 
3817 	ASSERT(TargetModel() != NULL);
3818 
3819 	PRINT(("folder %s does not have any saved state\n", TargetModel()->Name()));
3820 
3821 	WindowStateNodeOpener opener(this, true);
3822 		// this is our destination node, whatever it is for this window
3823 	if (opener.StreamNode() == NULL)
3824 		return;
3825 
3826 	if (!TargetModel()->IsRoot()) {
3827 		BDirectory deskDir;
3828 		FSGetDeskDir(&deskDir);
3829 
3830 		// try copying state from our parent directory, unless it is the
3831 		// desktop folder
3832 		BEntry entry(TargetModel()->EntryRef());
3833 		BNode parent;
3834 		if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK
3835 			&& parent != deskDir) {
3836 			PRINT(("looking at parent for state\n"));
3837 			if (NodeHasSavedState(&parent)) {
3838 				PRINT(("got state from parent\n"));
3839 				defaultingNode = parent;
3840 				gotDefaultingNode = true;
3841 				// when getting state from parent, stagger the window
3842 				shouldStagger = true;
3843 			}
3844 		}
3845 	}
3846 
3847 	if (!gotDefaultingNode
3848 		// parent didn't have any state, use the template directory from
3849 		// tracker settings folder for what our state should be
3850 		// For simplicity we are not picking up the most recent
3851 		// changes that didn't get committed if home is still open in
3852 		// a window, that's probably not a problem; would be OK if state
3853 		// got committed after every change
3854 		&& !DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode,
3855 			true)) {
3856 		return;
3857 	}
3858 
3859 	if (fIsDesktop) {
3860 		// don't copy over the attributes if we are the Desktop
3861 		return;
3862 	}
3863 
3864 	// copy over the attributes
3865 
3866 	// set up a filter of the attributes we want copied
3867 	const char* allowAttrs[] = {
3868 		kAttrWindowFrame,
3869 		kAttrWindowWorkspace,
3870 		kAttrViewState,
3871 		kAttrViewStateForeign,
3872 		kAttrColumns,
3873 		kAttrColumnsForeign,
3874 		0
3875 	};
3876 
3877 	// copy over attributes that apply; transform them properly, stripping
3878 	// parts that do not apply, adding a window stagger, etc.
3879 
3880 	StaggerOneParams params;
3881 	params.rectFromParent = shouldStagger;
3882 	SelectiveAttributeTransformer frameOffsetter(kAttrWindowFrame,
3883 		OffsetFrameOne, &params);
3884 	SelectiveAttributeTransformer scrollOriginCleaner(kAttrViewState,
3885 		ClearViewOriginOne, &params);
3886 
3887 	// do it
3888 	AttributeStreamMemoryNode memoryNode;
3889 	NamesToAcceptAttrFilter filter(allowAttrs);
3890 	AttributeStreamFileNode fileNode(&defaultingNode);
3891 
3892 	*opener.StreamNode() << scrollOriginCleaner << frameOffsetter
3893 		<< memoryNode << filter << fileNode;
3894 }
3895 
3896 
3897 void
3898 BContainerWindow::RestoreWindowState(AttributeStreamNode* node)
3899 {
3900 	if (node == NULL || fIsDesktop) {
3901 		// don't restore any window state if we are the Desktop
3902 		return;
3903 	}
3904 
3905 	const char* rectAttributeName;
3906 	const char* workspaceAttributeName;
3907 	if (TargetModel()->IsRoot()) {
3908 		rectAttributeName = kAttrDisksFrame;
3909 		workspaceAttributeName = kAttrDisksWorkspace;
3910 	} else {
3911 		rectAttributeName = kAttrWindowFrame;
3912 		workspaceAttributeName = kAttrWindowWorkspace;
3913 	}
3914 
3915 	BRect frame(Frame());
3916 	if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame)
3917 			== sizeof(BRect)) {
3918 		MoveTo(frame.LeftTop());
3919 		ResizeTo(frame.Width(), frame.Height());
3920 	} else
3921 		sNewWindRect.OffsetBy(kWindowStaggerBy, kWindowStaggerBy);
3922 
3923 	fPreviousBounds = Bounds();
3924 
3925 	uint32 workspace;
3926 	if (((fContainerWindowFlags & kRestoreWorkspace) != 0)
3927 		&& node->Read(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32),
3928 			&workspace) == sizeof(uint32))
3929 		SetWorkspaces(workspace);
3930 
3931 	if ((fContainerWindowFlags & kIsHidden) != 0)
3932 		Minimize(true);
3933 
3934 	// restore window decor settings
3935 	int32 size = node->Contains(kAttrWindowDecor, B_RAW_TYPE);
3936 	if (size > 0) {
3937 		char buffer[size];
3938 		if (((fContainerWindowFlags & kRestoreDecor) != 0)
3939 			&& node->Read(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer)
3940 				== size) {
3941 			BMessage decorSettings;
3942 			if (decorSettings.Unflatten(buffer) == B_OK)
3943 				SetDecoratorSettings(decorSettings);
3944 		}
3945 	}
3946 }
3947 
3948 
3949 void
3950 BContainerWindow::RestoreWindowState(const BMessage& message)
3951 {
3952 	if (fIsDesktop) {
3953 		// don't restore any window state if we are the Desktop
3954 		return;
3955 	}
3956 
3957 	const char* rectAttributeName;
3958 	const char* workspaceAttributeName;
3959 	if (TargetModel()->IsRoot()) {
3960 		rectAttributeName = kAttrDisksFrame;
3961 		workspaceAttributeName = kAttrDisksWorkspace;
3962 	} else {
3963 		rectAttributeName = kAttrWindowFrame;
3964 		workspaceAttributeName = kAttrWindowWorkspace;
3965 	}
3966 
3967 	BRect frame(Frame());
3968 	if (message.FindRect(rectAttributeName, &frame) == B_OK) {
3969 		MoveTo(frame.LeftTop());
3970 		ResizeTo(frame.Width(), frame.Height());
3971 	} else
3972 		sNewWindRect.OffsetBy(kWindowStaggerBy, kWindowStaggerBy);
3973 
3974 	uint32 workspace;
3975 	if ((fContainerWindowFlags & kRestoreWorkspace)
3976 		&& message.FindInt32(workspaceAttributeName,
3977 			(int32*)&workspace) == B_OK) {
3978 		SetWorkspaces(workspace);
3979 	}
3980 
3981 	if (fContainerWindowFlags & kIsHidden)
3982 		Minimize(true);
3983 
3984 	// restore window decor settings
3985 	BMessage decorSettings;
3986 	if ((fContainerWindowFlags & kRestoreDecor)
3987 		&& message.FindMessage(kAttrWindowDecor, &decorSettings) == B_OK) {
3988 		SetDecoratorSettings(decorSettings);
3989 	}
3990 }
3991 
3992 
3993 void
3994 BContainerWindow::SaveWindowState(AttributeStreamNode* node)
3995 {
3996 	if (fIsDesktop) {
3997 		// don't save window state if we are the Desktop
3998 		return;
3999 	}
4000 
4001 	ASSERT(node != NULL);
4002 
4003 	const char* rectAttributeName;
4004 	const char* workspaceAttributeName;
4005 	if (TargetModel() != NULL && TargetModel()->IsRoot()) {
4006 		rectAttributeName = kAttrDisksFrame;
4007 		workspaceAttributeName = kAttrDisksWorkspace;
4008 	} else {
4009 		rectAttributeName = kAttrWindowFrame;
4010 		workspaceAttributeName = kAttrWindowWorkspace;
4011 	}
4012 
4013 	// node is null if it already got deleted
4014 	BRect frame(Frame());
4015 	node->Write(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame);
4016 
4017 	uint32 workspaces = Workspaces();
4018 	node->Write(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32),
4019 		&workspaces);
4020 
4021 	BMessage decorSettings;
4022 	if (GetDecoratorSettings(&decorSettings) == B_OK) {
4023 		int32 size = decorSettings.FlattenedSize();
4024 		char buffer[size];
4025 		if (decorSettings.Flatten(buffer, size) == B_OK) {
4026 			node->Write(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer);
4027 		}
4028 	}
4029 }
4030 
4031 
4032 void
4033 BContainerWindow::SaveWindowState(BMessage& message) const
4034 {
4035 	const char* rectAttributeName;
4036 	const char* workspaceAttributeName;
4037 
4038 	if (TargetModel() != NULL && TargetModel()->IsRoot()) {
4039 		rectAttributeName = kAttrDisksFrame;
4040 		workspaceAttributeName = kAttrDisksWorkspace;
4041 	} else {
4042 		rectAttributeName = kAttrWindowFrame;
4043 		workspaceAttributeName = kAttrWindowWorkspace;
4044 	}
4045 
4046 	// node is null if it already got deleted
4047 	BRect frame(Frame());
4048 	message.AddRect(rectAttributeName, frame);
4049 	message.AddInt32(workspaceAttributeName, (int32)Workspaces());
4050 
4051 	BMessage decorSettings;
4052 	if (GetDecoratorSettings(&decorSettings) == B_OK) {
4053 		message.AddMessage(kAttrWindowDecor, &decorSettings);
4054 	}
4055 }
4056 
4057 
4058 status_t
4059 BContainerWindow::DragStart(const BMessage* dragMessage)
4060 {
4061 	if (dragMessage == NULL)
4062 		return B_ERROR;
4063 
4064 	// if already dragging, or
4065 	// if all the refs match
4066 	if (Dragging()
4067 		&& SpringLoadedFolderCompareMessages(dragMessage, fDragMessage)) {
4068 		return B_OK;
4069 	}
4070 
4071 	// cache the current drag message
4072 	// build a list of the mimetypes in the message
4073 	SpringLoadedFolderCacheDragData(dragMessage, &fDragMessage,
4074 		&fCachedTypesList);
4075 
4076 	fWaitingForRefs = true;
4077 
4078 	return B_OK;
4079 }
4080 
4081 
4082 void
4083 BContainerWindow::DragStop()
4084 {
4085 	delete fDragMessage;
4086 	fDragMessage = NULL;
4087 
4088 	delete fCachedTypesList;
4089 	fCachedTypesList = NULL;
4090 
4091 	fWaitingForRefs = false;
4092 }
4093 
4094 
4095 void
4096 BContainerWindow::ShowSelectionWindow()
4097 {
4098 	if (fSelectionWindow == NULL) {
4099 		fSelectionWindow = new SelectionWindow(this);
4100 		fSelectionWindow->Show();
4101 	} else if (fSelectionWindow->Lock()) {
4102 		// The window is already there, just bring it close
4103 		fSelectionWindow->MoveCloseToMouse();
4104 		if (fSelectionWindow->IsHidden())
4105 			fSelectionWindow->Show();
4106 
4107 		fSelectionWindow->Unlock();
4108 	}
4109 }
4110 
4111 
4112 void
4113 BContainerWindow::ShowNavigator(bool show)
4114 {
4115 	if (PoseView()->IsDesktopWindow() || !TargetModel()->IsDirectory()
4116 		|| fPoseView->IsFilePanel()) {
4117 		return;
4118 	}
4119 
4120 	if (show) {
4121 		if (Navigator() && !Navigator()->IsHidden())
4122 			return;
4123 
4124 		if (Navigator() == NULL) {
4125 			fNavigator = new BNavigator(TargetModel());
4126 			fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
4127 		}
4128 
4129 		if (Navigator()->IsHidden())
4130 			Navigator()->Show();
4131 
4132 		if (PoseView()->VScrollBar())
4133 			PoseView()->UpdateScrollRange();
4134 	} else {
4135 		if (!Navigator() || Navigator()->IsHidden())
4136 			return;
4137 
4138 		if (PoseView()->VScrollBar())
4139 			PoseView()->UpdateScrollRange();
4140 
4141 		fNavigator->Hide();
4142 	}
4143 }
4144 
4145 
4146 void
4147 BContainerWindow::SetSingleWindowBrowseShortcuts(bool enabled)
4148 {
4149 	if (PoseView()->IsDesktopWindow())
4150 		return;
4151 
4152 	if (enabled) {
4153 		if (!Navigator())
4154 			return;
4155 
4156 		RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
4157 		RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY);
4158 		RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
4159 
4160 		AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY,
4161 			new BMessage(kNavigatorCommandBackward), Navigator());
4162 		AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY,
4163 			new BMessage(kNavigatorCommandForward), Navigator());
4164 		AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
4165 			new BMessage(kNavigatorCommandUp), Navigator());
4166 
4167 		AddShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4168 			new BMessage(kNavigatorCommandBackward), Navigator());
4169 		AddShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4170 			new BMessage(kNavigatorCommandForward), Navigator());
4171 		AddShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4172 			new BMessage(kNavigatorCommandUp), Navigator());
4173 		AddShortcut(B_DOWN_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4174 			new BMessage(kOpenSelection), PoseView());
4175 		AddShortcut('L', B_COMMAND_KEY,
4176 			new BMessage(kNavigatorCommandSetFocus), Navigator());
4177 
4178 	} else {
4179 		RemoveShortcut(B_LEFT_ARROW, B_COMMAND_KEY);
4180 		RemoveShortcut(B_RIGHT_ARROW, B_COMMAND_KEY);
4181 		RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY);
4182 			// This is added again, below, with a new meaning.
4183 
4184 		RemoveShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
4185 		RemoveShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
4186 		RemoveShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
4187 		RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
4188 			// This also changes meaning, added again below.
4189 
4190 		AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
4191 			new BMessage(kOpenSelection), PoseView());
4192 		AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
4193 			new BMessage(kOpenParentDir), PoseView());
4194 			// We change the meaning from kNavigatorCommandUp
4195 			// to kOpenParentDir.
4196 		AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
4197 			new BMessage(kOpenParentDir), PoseView());
4198 			// command + option results in closing the parent window
4199 		RemoveShortcut('L', B_COMMAND_KEY);
4200 	}
4201 }
4202 
4203 
4204 void
4205 BContainerWindow::SetPathWatchingEnabled(bool enable)
4206 {
4207 	if (IsPathWatchingEnabled()) {
4208 		stop_watching(this);
4209 		fIsWatchingPath = false;
4210 	}
4211 
4212 	if (enable) {
4213 		if (TargetModel() != NULL) {
4214 			BEntry entry;
4215 
4216 			TargetModel()->GetEntry(&entry);
4217 			status_t err;
4218 			do {
4219 				err = entry.GetParent(&entry);
4220 				if (err != B_OK)
4221 					break;
4222 
4223 				char name[B_FILE_NAME_LENGTH];
4224 				entry.GetName(name);
4225 				if (strcmp(name, "/") == 0)
4226 					break;
4227 
4228 				node_ref ref;
4229 				entry.GetNodeRef(&ref);
4230 				watch_node(&ref, B_WATCH_NAME, this);
4231 			} while (err == B_OK);
4232 
4233 			fIsWatchingPath = err == B_OK;
4234 		} else
4235 			fIsWatchingPath = false;
4236 	}
4237 }
4238 
4239 
4240 void
4241 BContainerWindow::PulseTaskLoop()
4242 {
4243 	if (fTaskLoop)
4244 		fTaskLoop->PulseMe();
4245 }
4246 
4247 
4248 void
4249 BContainerWindow::PopulateArrangeByMenu(BMenu* menu)
4250 {
4251 	if (!fAttrMenu || !menu)
4252 		return;
4253 	// empty fArrangeByMenu...
4254 	BMenuItem* item;
4255 	while ((item = menu->RemoveItem((int32)0)) != NULL)
4256 		delete item;
4257 
4258 	int32 itemCount = fAttrMenu->CountItems();
4259 	for (int32 i = 0; i < itemCount; i++) {
4260 		item = fAttrMenu->ItemAt(i);
4261 		if (item->Command() == kAttributeItem) {
4262 			BMessage* message = new BMessage(*(item->Message()));
4263 			message->what = kArrangeBy;
4264 			BMenuItem* newItem = new BMenuItem(item->Label(), message);
4265 			newItem->SetTarget(PoseView());
4266 			menu->AddItem(newItem);
4267 		}
4268 	}
4269 
4270 	menu->AddSeparatorItem();
4271 
4272 	item = new BMenuItem(B_TRANSLATE("Reverse order"),
4273 		new BMessage(kArrangeReverseOrder));
4274 
4275 	item->SetTarget(PoseView());
4276 	menu->AddItem(item);
4277 	menu->AddSeparatorItem();
4278 
4279 
4280 	item = new BMenuItem(B_TRANSLATE("Clean up"), new BMessage(kCleanup),
4281 		'K');
4282 	item->SetTarget(PoseView());
4283 	menu->AddItem(item);
4284 }
4285 
4286 
4287 //	#pragma mark - WindowStateNodeOpener
4288 
4289 
4290 WindowStateNodeOpener::WindowStateNodeOpener(BContainerWindow* window,
4291 	bool forWriting)
4292 	:
4293 	fModelOpener(NULL),
4294 	fNode(NULL),
4295 	fStreamNode(NULL)
4296 {
4297 	if (window->TargetModel() && window->TargetModel()->IsRoot()) {
4298 		BDirectory dir;
4299 		if (FSGetDeskDir(&dir) == B_OK) {
4300 			fNode = new BDirectory(dir);
4301 			fStreamNode = new AttributeStreamFileNode(fNode);
4302 		}
4303 	} else if (window->TargetModel()){
4304 		fModelOpener = new ModelNodeLazyOpener(window->TargetModel(),
4305 			forWriting, false);
4306 		if (fModelOpener->IsOpen(forWriting)) {
4307 			fStreamNode = new AttributeStreamFileNode(
4308 				fModelOpener->TargetModel()->Node());
4309 		}
4310 	}
4311 }
4312 
4313 WindowStateNodeOpener::~WindowStateNodeOpener()
4314 {
4315 	delete fModelOpener;
4316 	delete fNode;
4317 	delete fStreamNode;
4318 }
4319 
4320 
4321 void
4322 WindowStateNodeOpener::SetTo(const BDirectory* node)
4323 {
4324 	delete fModelOpener;
4325 	delete fNode;
4326 	delete fStreamNode;
4327 
4328 	fModelOpener = NULL;
4329 	fNode = new BDirectory(*node);
4330 	fStreamNode = new AttributeStreamFileNode(fNode);
4331 }
4332 
4333 
4334 void
4335 WindowStateNodeOpener::SetTo(const BEntry* entry, bool forWriting)
4336 {
4337 	delete fModelOpener;
4338 	delete fNode;
4339 	delete fStreamNode;
4340 
4341 	fModelOpener = NULL;
4342 	fNode = new BFile(entry, (uint32)(forWriting ? O_RDWR : O_RDONLY));
4343 	fStreamNode = new AttributeStreamFileNode(fNode);
4344 }
4345 
4346 
4347 void
4348 WindowStateNodeOpener::SetTo(Model* model, bool forWriting)
4349 {
4350 	delete fModelOpener;
4351 	delete fNode;
4352 	delete fStreamNode;
4353 
4354 	fNode = NULL;
4355 	fStreamNode = NULL;
4356 	fModelOpener = new ModelNodeLazyOpener(model, forWriting, false);
4357 	if (fModelOpener->IsOpen(forWriting)) {
4358 		fStreamNode = new AttributeStreamFileNode(
4359 			fModelOpener->TargetModel()->Node());
4360 	}
4361 }
4362 
4363 
4364 AttributeStreamNode*
4365 WindowStateNodeOpener::StreamNode() const
4366 {
4367 	return fStreamNode;
4368 }
4369 
4370 
4371 BNode*
4372 WindowStateNodeOpener::Node() const
4373 {
4374 	if (!fStreamNode)
4375 		return NULL;
4376 
4377 	if (fNode)
4378 		return fNode;
4379 
4380 	return fModelOpener->TargetModel()->Node();
4381 }
4382 
4383 
4384 //	#pragma mark - BorderedView
4385 
4386 
4387 BorderedView::BorderedView()
4388 	:
4389 	BGroupView(B_VERTICAL, 0),
4390 	fEnableBorderHighlight(true)
4391 {
4392 	GroupLayout()->SetInsets(1);
4393 }
4394 
4395 
4396 void
4397 BorderedView::WindowActivated(bool active)
4398 {
4399 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
4400 	if (window == NULL)
4401 		return;
4402 
4403 	if (window->PoseView()->IsFocus())
4404 		PoseViewFocused(active); // Update border color
4405 }
4406 
4407 
4408 void BorderedView::EnableBorderHighlight(bool enable)
4409 {
4410 	fEnableBorderHighlight = enable;
4411 	PoseViewFocused(false);
4412 }
4413 
4414 
4415 void
4416 BorderedView::PoseViewFocused(bool focused)
4417 {
4418 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
4419 	if (window == NULL)
4420 		return;
4421 
4422 	color_which base = B_DOCUMENT_BACKGROUND_COLOR;
4423 	float tint = B_DARKEN_2_TINT;
4424 	if (focused && window->IsActive() && fEnableBorderHighlight) {
4425 		base = B_KEYBOARD_NAVIGATION_COLOR;
4426 		tint = B_NO_TINT;
4427 	}
4428 
4429 	BScrollBar* hScrollBar = window->PoseView()->HScrollBar();
4430 	if (hScrollBar != NULL)
4431 		hScrollBar->SetBorderHighlighted(focused);
4432 
4433 	BScrollBar* vScrollBar = window->PoseView()->VScrollBar();
4434 	if (vScrollBar != NULL)
4435 		vScrollBar->SetBorderHighlighted(focused);
4436 
4437 	SetViewUIColor(base, tint);
4438 	Invalidate();
4439 }
4440 
4441 
4442 void
4443 BorderedView::Pulse()
4444 {
4445 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
4446 	if (window != NULL)
4447 		window->PulseTaskLoop();
4448 }
4449