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