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