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