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