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