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