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