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