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