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