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