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