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