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