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