xref: /haiku/src/kits/tracker/FilePanelPriv.cpp (revision b3de82492af3b6412ffaf7eb87fd6e1995755685)
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 "Attributes.h"
37 #include "AttributeStream.h"
38 #include "AutoLock.h"
39 #include "Commands.h"
40 #include "DesktopPoseView.h"
41 #include "DirMenu.h"
42 #include "FavoritesMenu.h"
43 #include "FilePanelPriv.h"
44 #include "FSUtils.h"
45 #include "FSClipboard.h"
46 #include "IconMenuItem.h"
47 #include "MimeTypes.h"
48 #include "NavMenu.h"
49 #include "PoseView.h"
50 #include "Tracker.h"
51 #include "tracker_private.h"
52 
53 #include <Alert.h>
54 #include <Application.h>
55 #include <Button.h>
56 #include <Debug.h>
57 #include <Directory.h>
58 #include <FindDirectory.h>
59 #include <MenuBar.h>
60 #include <MenuField.h>
61 #include <MenuItem.h>
62 #include <MessageFilter.h>
63 #include <NodeInfo.h>
64 #include <NodeMonitor.h>
65 #include <Path.h>
66 #include <Roster.h>
67 #include <SymLink.h>
68 #include <ScrollView.h>
69 #include <String.h>
70 #include <StopWatch.h>
71 #include <TextControl.h>
72 #include <TextView.h>
73 #include <Volume.h>
74 #include <VolumeRoster.h>
75 
76 #include <string.h>
77 
78 
79 const char *kDefaultFilePanelTemplate = "FilePanelSettings";
80 
81 
82 static uint32
83 GetLinkFlavor(const Model *model, bool resolve = true)
84 {
85 	if (model && model->IsSymLink()) {
86 		if (!resolve)
87 			return B_SYMLINK_NODE;
88 		model = model->LinkTo();
89 	}
90 	if (!model)
91 		return 0;
92 
93 	if (model->IsDirectory())
94 		return B_DIRECTORY_NODE;
95 
96 	return B_FILE_NODE;
97 }
98 
99 
100 static filter_result
101 key_down_filter(BMessage *message, BHandler **, BMessageFilter *filter)
102 {
103 	TFilePanel *panel = dynamic_cast<TFilePanel *>(filter->Looper());
104 	ASSERT(panel);
105 	BPoseView *view = panel->PoseView();
106 
107 	if (panel->TrackingMenu())
108 		return B_DISPATCH_MESSAGE;
109 
110 	uchar key;
111 	if (message->FindInt8("byte", (int8 *)&key) != B_OK)
112 		return B_DISPATCH_MESSAGE;
113 
114 	int32 modifier = 0;
115 	message->FindInt32("modifiers", &modifier);
116 	if (!modifier && key == B_ESCAPE) {
117 		if (view->ActivePose())
118 			view->CommitActivePose(false);
119 		else
120 			filter->Looper()->PostMessage(kCancelButton);
121 		return B_SKIP_MESSAGE;
122 	}
123 
124 	if (key == B_RETURN && view->ActivePose()) {
125 		view->CommitActivePose();
126 		return B_SKIP_MESSAGE;
127 	}
128 
129 	return B_DISPATCH_MESSAGE;
130 }
131 
132 
133 //	#pragma mark -
134 
135 
136 TFilePanel::TFilePanel(file_panel_mode mode, BMessenger *target,
137 		const BEntry *startDir, uint32 nodeFlavors, bool multipleSelection,
138 		BMessage *message, BRefFilter *filter, uint32 containerWindowFlags,
139 		window_look look, window_feel feel, bool hideWhenDone)
140 	: BContainerWindow(0, containerWindowFlags, look, feel, 0, B_CURRENT_WORKSPACE),
141 	fDirMenu(NULL),
142 	fDirMenuField(NULL),
143 	fTextControl(NULL),
144 	fClientObject(NULL),
145 	fSelectionIterator(0),
146 	fMessage(NULL),
147 	fHideWhenDone(hideWhenDone),
148 	fIsTrackingMenu(false)
149 {
150 	InitIconPreloader();
151 
152 	fIsSavePanel = (mode == B_SAVE_PANEL);
153 
154 	BRect windRect(85, 50, 510, 296);
155 	MoveTo(windRect.LeftTop());
156 	ResizeTo(windRect.Width(), windRect.Height());
157 
158 	fNodeFlavors = (nodeFlavors == 0) ? B_FILE_NODE : nodeFlavors;
159 
160 	if (target)
161 		fTarget = *target;
162 	else
163 		fTarget = BMessenger(be_app);
164 
165 	if (message)
166 		SetMessage(message);
167 	else if (fIsSavePanel)
168 		fMessage = new BMessage(B_SAVE_REQUESTED);
169 	else
170 		fMessage = new BMessage(B_REFS_RECEIVED);
171 
172 	// check for legal starting directory
173 	Model *model = new Model();
174 	bool useRoot = true;
175 
176 	if (startDir) {
177 		if (model->SetTo(startDir) == B_OK && model->IsDirectory())
178 			useRoot = false;
179 		else {
180 			delete model;
181 			model = new Model();
182 		}
183 	}
184 
185 	if (useRoot) {
186 		BPath path;
187 		if (find_directory(B_USER_DIRECTORY, &path) == B_OK) {
188 			BEntry entry(path.Path(), true);
189 			if (entry.InitCheck() == B_OK && model->SetTo(&entry) == B_OK)
190 				useRoot = false;
191 		}
192 	}
193 
194 	if (useRoot) {
195 		BVolume volume;
196 		BDirectory root;
197 		BVolumeRoster volumeRoster;
198 		volumeRoster.GetBootVolume(&volume);
199 		volume.GetRootDirectory(&root);
200 
201 		BEntry entry;
202 		root.GetEntry(&entry);
203 		model->SetTo(&entry);
204 	}
205 
206 	fTaskLoop = new PiggybackTaskLoop;
207 
208 	AutoLock<BWindow> lock(this);
209 	CreatePoseView(model);
210 	fPoseView->SetRefFilter(filter);
211 	if (!fIsSavePanel)
212 		fPoseView->SetMultipleSelection(multipleSelection);
213 
214 	fPoseView->SetFlags(fPoseView->Flags() | B_NAVIGABLE);
215 	fPoseView->SetPoseEditing(false);
216 	AddCommonFilter(new BMessageFilter(B_KEY_DOWN, key_down_filter));
217 	AddCommonFilter(new BMessageFilter(B_SIMPLE_DATA, TFilePanel::MessageDropFilter));
218 	AddCommonFilter(new BMessageFilter(B_NODE_MONITOR, TFilePanel::FSFilter));
219 
220 	// inter-application observing
221 	BMessenger tracker(kTrackerSignature);
222 	BHandler::StartWatching(tracker, kDesktopFilePanelRootChanged);
223 
224 	Init();
225 }
226 
227 
228 TFilePanel::~TFilePanel()
229 {
230 	BMessenger tracker(kTrackerSignature);
231 	BHandler::StopWatching(tracker, kDesktopFilePanelRootChanged);
232 
233 	delete fMessage;
234 }
235 
236 
237 filter_result
238 TFilePanel::MessageDropFilter(BMessage *message, BHandler **, BMessageFilter *filter)
239 {
240 	TFilePanel *panel = dynamic_cast<TFilePanel *>(filter->Looper());
241 	if (panel == NULL || !message->WasDropped())
242 		return B_SKIP_MESSAGE;
243 
244 	uint32 type;
245 	int32 count;
246 	if (message->GetInfo("refs", &type, &count) != B_OK)
247 		return B_SKIP_MESSAGE;
248 
249 	if (count != 1)
250 		return B_SKIP_MESSAGE;
251 
252 	entry_ref ref;
253 	if (message->FindRef("refs", &ref) != B_OK)
254 		return B_SKIP_MESSAGE;
255 
256 	BEntry entry(&ref);
257 	if (entry.InitCheck() != B_OK)
258 		return B_SKIP_MESSAGE;
259 
260 	// if the entry is a symlink
261 	// resolve it and see if it is a directory
262 	// pass it on if it is
263 	if (entry.IsSymLink()) {
264 		entry_ref resolvedRef;
265 
266 		entry.GetRef(&resolvedRef);
267 		BEntry resolvedEntry(&resolvedRef, true);
268 
269 		if (resolvedEntry.IsDirectory()) {
270 			// both entry and ref need to be the correct locations
271 			// for the last setto
272 			resolvedEntry.GetRef(&ref);
273 			entry.SetTo(&ref);
274 		}
275 	}
276 
277 	// if not a directory, set to the parent, and select the child
278 	if (!entry.IsDirectory()) {
279 		node_ref child;
280 		if (entry.GetNodeRef(&child) != B_OK)
281 			return B_SKIP_MESSAGE;
282 
283 		BPath path(&entry);
284 
285 		if (entry.GetParent(&entry) != B_OK)
286 			return B_SKIP_MESSAGE;
287 
288 		entry.GetRef(&ref);
289 
290 		panel->fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
291 			(&TFilePanel::SelectChildInParent, panel,
292 			const_cast<const entry_ref *>(&ref),
293 			const_cast<const node_ref *>(&child)),
294 			ref == *panel->TargetModel()->EntryRef() ? 0 : 100000, 200000, 5000000);
295 				// if the target directory is already current, we won't
296 				// delay the initial selection try
297 
298 		// also set the save name to the dragged in entry
299 		if (panel->IsSavePanel())
300 			panel->SetSaveText(path.Leaf());
301 	}
302 
303 	panel->SetTo(&ref);
304 
305 	return B_SKIP_MESSAGE;
306 }
307 
308 
309 filter_result
310 TFilePanel::FSFilter(BMessage *message, BHandler **, BMessageFilter *filter)
311 {
312 	switch (message->FindInt32("opcode")) {
313 		case B_ENTRY_MOVED:
314 			{
315 				node_ref itemNode;
316 				node_ref dirNode;
317 				TFilePanel *panel = dynamic_cast<TFilePanel *>(filter->Looper());
318 
319 				message->FindInt32("device", &dirNode.device);
320 				itemNode.device = dirNode.device;
321 				message->FindInt64("to directory", (int64 *)&dirNode.node);
322 				message->FindInt64("node", (int64 *)&itemNode.node);
323 				const char *name;
324 				if (message->FindString("name", &name) != B_OK)
325 					break;
326 
327 				// if current directory moved, update entry ref and menu
328 				// but not wind title
329 				if (*(panel->TargetModel()->NodeRef()) == itemNode) {
330 					panel->TargetModel()->UpdateEntryRef(&dirNode, name);
331 					panel->SetTo(panel->TargetModel()->EntryRef());
332 					return B_SKIP_MESSAGE;
333 				}
334 				break;
335 			}
336 		case B_ENTRY_REMOVED:
337 			{
338 				node_ref itemNode;
339 				TFilePanel *panel = dynamic_cast<TFilePanel *>(filter->Looper());
340 				message->FindInt32("device", &itemNode.device);
341 				message->FindInt64("node", (int64 *)&itemNode.node);
342 
343 				// if folder we're watching is deleted, switch to root
344 				// or Desktop
345 				if (*(panel->TargetModel()->NodeRef()) == itemNode) {
346 					BVolumeRoster volumeRoster;
347 					BVolume volume;
348 					volumeRoster.GetBootVolume(&volume);
349 
350 					BDirectory root;
351 					volume.GetRootDirectory(&root);
352 
353 					BEntry entry;
354 					entry_ref ref;
355 					root.GetEntry(&entry);
356 					entry.GetRef(&ref);
357 
358 					panel->SwitchDirToDesktopIfNeeded(ref);
359 
360 					panel->SetTo(&ref);
361 					return B_SKIP_MESSAGE;
362 				}
363 			}
364 			break;
365 	}
366 	return B_DISPATCH_MESSAGE;
367 }
368 
369 
370 void
371 TFilePanel::DispatchMessage(BMessage *message, BHandler *handler)
372 {
373 	_inherited::DispatchMessage(message, handler);
374 	if (message->what == B_KEY_DOWN || message->what == B_MOUSE_DOWN)
375 		AdjustButton();
376 }
377 
378 
379 BFilePanelPoseView *
380 TFilePanel::PoseView() const
381 {
382 	ASSERT(dynamic_cast<BFilePanelPoseView *>(fPoseView));
383 	return static_cast<BFilePanelPoseView *>(fPoseView);
384 }
385 
386 
387 bool
388 TFilePanel::QuitRequested()
389 {
390 	// If we have a client object then this window will simply hide
391 	// itself, to be closed later when the client object itself is
392 	// destroyed. If we have no client then we must have been started
393 	// from the "easy" functions which simply instantiate a TFilePanel
394 	// and expect it to go away by itself
395 
396 	if (fClientObject) {
397 		Hide();
398 		if (fClientObject)
399 			fClientObject->WasHidden();
400 
401 		BMessage message(*fMessage);
402 		message.what = B_CANCEL;
403 		message.AddInt32("old_what", (int32)fMessage->what);
404 		message.AddPointer("source", fClientObject);
405 		fTarget.SendMessage(&message);
406 		return false;
407 	}
408 
409 	return _inherited::QuitRequested();
410 }
411 
412 
413 BRefFilter *
414 TFilePanel::Filter() const
415 {
416 	return fPoseView->RefFilter();
417 }
418 
419 
420 void
421 TFilePanel::SetTarget(BMessenger target)
422 {
423 	fTarget = target;
424 }
425 
426 
427 void
428 TFilePanel::SetMessage(BMessage *message)
429 {
430 	delete fMessage;
431 	fMessage = new BMessage(*message);
432 }
433 
434 
435 void
436 TFilePanel::SetRefFilter(BRefFilter *filter)
437 {
438 	if (!filter)
439 		return;
440 
441 	fPoseView->SetRefFilter(filter);
442 	fPoseView->CommitActivePose();
443 	fPoseView->Refresh();
444 	FavoritesMenu *menu = dynamic_cast<FavoritesMenu *>(
445 		fMenuBar->FindItem("Favorites")->Submenu());
446 	if (menu)
447 		menu->SetRefFilter(filter);
448 }
449 
450 
451 void
452 TFilePanel::SetTo(const entry_ref *ref)
453 {
454 	if (!ref)
455 		return;
456 
457 	entry_ref setToRef(*ref);
458 
459 	bool isDesktop = SwitchDirToDesktopIfNeeded(setToRef);
460 
461 	BEntry entry(&setToRef);
462 	if (entry.InitCheck() != B_OK || !entry.IsDirectory())
463 		return;
464 
465 	SwitchDirMenuTo(&setToRef);
466 
467 	PoseView()->SetIsDesktop(isDesktop);
468 	fPoseView->SwitchDir(&setToRef);
469 
470 	AddShortcut('H', B_COMMAND_KEY, new BMessage(kSwitchToHome));
471 		// our shortcut got possibly removed because the home
472 		// menu item got removed - we shouldn't really have to do
473 		// this - this is a workaround for a kit bug.
474 }
475 
476 
477 void
478 TFilePanel::Rewind()
479 {
480 	fSelectionIterator = 0;
481 }
482 
483 
484 void
485 TFilePanel::SetClientObject(BFilePanel *panel)
486 {
487 	fClientObject = panel;
488 }
489 
490 
491 void
492 TFilePanel::AdjustButton()
493 {
494 	// adjust button state
495 	BButton *button = dynamic_cast<BButton *>(FindView("default button"));
496 	if (!button)
497 		return;
498 
499 	BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("text view"));
500 	BObjectList<BPose> *selectionList = fPoseView->SelectionList();
501 	const char *buttonText = fButtonText.String();
502 	bool enabled = false;
503 
504 	if (fIsSavePanel && textControl) {
505 		enabled = textControl->Text()[0] != '\0';
506 		if (fPoseView->IsFocus()) {
507 			fPoseView->ShowSelection(true);
508 			if (selectionList->CountItems() == 1) {
509 				Model *model = selectionList->FirstItem()->TargetModel();
510 				if (model->ResolveIfLink()->IsDirectory()) {
511 					enabled = true;
512 					buttonText = "Open";
513 				} else {
514 					// insert the name of the selected model into the text field
515 					textControl->SetText(model->Name());
516 					textControl->MakeFocus(true);
517 				}
518 			}
519 		} else
520 			fPoseView->ShowSelection(false);
521 	} else {
522 		int32 count = selectionList->CountItems();
523 		if (count) {
524 			enabled = true;
525 
526 			// go through selection list looking at content
527 			for (int32 index = 0; index < count; index++) {
528 				Model *model = selectionList->ItemAt(index)->TargetModel();
529 
530 				uint32 modelFlavor = GetLinkFlavor(model, false);
531 				uint32 linkFlavor = GetLinkFlavor(model, true);
532 
533 				// if only one item is selected and we're not in dir
534 				// selection mode then we don't disable button ever
535 				if ((modelFlavor == B_DIRECTORY_NODE
536 						|| linkFlavor == B_DIRECTORY_NODE)
537 					&& count == 1)
538 				  break;
539 
540 				if ((fNodeFlavors & modelFlavor) == 0
541 					&& (fNodeFlavors & linkFlavor) == 0) {
542 		    		enabled = false;
543 					break;
544 				}
545 			}
546 		}
547 	}
548 
549 	button->SetLabel(buttonText);
550 	button->SetEnabled(enabled);
551 }
552 
553 
554 void
555 TFilePanel::SelectionChanged()
556 {
557 	AdjustButton();
558 
559 	if (fClientObject)
560 		fClientObject->SelectionChanged();
561 }
562 
563 
564 status_t
565 TFilePanel::GetNextEntryRef(entry_ref *ref)
566 {
567 	if (!ref)
568 		return B_ERROR;
569 
570 	BPose *pose = fPoseView->SelectionList()->ItemAt(fSelectionIterator++);
571 	if (!pose)
572 		return B_ERROR;
573 
574 	*ref = *pose->TargetModel()->EntryRef();
575 	return B_OK;
576 }
577 
578 
579 BPoseView *
580 TFilePanel::NewPoseView(Model *model, BRect rect, uint32)
581 {
582 	return new BFilePanelPoseView(model, rect);
583 }
584 
585 
586 void
587 TFilePanel::Init(const BMessage *)
588 {
589 	BRect windRect(Bounds());
590 	AddChild(fBackView = new BackgroundView(windRect));
591 
592 	// add poseview menu bar
593 	fMenuBar = new BMenuBar(BRect(0, 0, windRect.Width(), 1), "MenuBar");
594 	fMenuBar->SetBorder(B_BORDER_FRAME);
595 	fBackView->AddChild(fMenuBar);
596 
597 	AddMenus();
598 	AddContextMenus();
599 
600 	FavoritesMenu *favorites = new FavoritesMenu("Favorites",
601 		new BMessage(kSwitchDirectory), new BMessage(B_REFS_RECEIVED),
602 		BMessenger(this), IsSavePanel(), fPoseView->RefFilter());
603 	favorites->AddItem(new BMenuItem("Add current folder",
604 		new BMessage(kAddCurrentDir)));
605 	favorites->AddItem(new BMenuItem("Edit favorites"B_UTF8_ELLIPSIS,
606 		new BMessage(kEditFavorites)));
607 
608 	fMenuBar->AddItem(favorites);
609 
610 	// configure menus
611 	BMenuItem *item = fMenuBar->FindItem("Window");
612 	if (item) {
613 		fMenuBar->RemoveItem(item);
614 		delete item;
615 	}
616 
617 	item = fMenuBar->FindItem("File");
618 	if (item) {
619 		BMenu *menu = item->Submenu();
620 		if (menu) {
621 			item = menu->FindItem(kOpenSelection);
622 			if (item && menu->RemoveItem(item))
623 				delete item;
624 
625 			item = menu->FindItem(kDuplicateSelection);
626 			if (item && menu->RemoveItem(item))
627 				delete item;
628 
629 			// remove add-ons menu, identifier menu, separator
630 			item = menu->FindItem(kAddOnsMenuName);
631 			if (item) {
632 				int32 index = menu->IndexOf(item);
633 				delete menu->RemoveItem(index);
634 				delete menu->RemoveItem(--index);
635 				delete menu->RemoveItem(--index);
636 			}
637 
638 			// remove separator
639 			item = menu->FindItem(B_CUT);
640 			if (item) {
641 				item = menu->ItemAt(menu->IndexOf(item)-1);
642 				if (item && menu->RemoveItem(item))
643 					delete item;
644 			}
645 		}
646 	}
647 
648 	// add directory menu and menufield
649 	fDirMenu = new BDirMenu(0, kSwitchDirectory, "refs");
650 
651 	font_height ht;
652 	be_plain_font->GetHeight(&ht);
653 	float f_height = ht.ascent + ht.descent + ht.leading;
654 
655 	BRect rect;
656 	rect.top = fMenuBar->Bounds().Height() + 8;
657 	rect.left = windRect.left + 8;
658 	rect.right = rect.left + 300;
659 	rect.bottom = rect.top + (f_height > 22 ? f_height : 22);
660 
661 	fDirMenuField = new BMenuField(rect, "DirMenuField", "", fDirMenu);
662 	fDirMenuField->MenuBar()->SetFont(be_plain_font);
663 	fDirMenuField->SetDivider(0);
664 
665 	fDirMenuField->MenuBar()->RemoveItem((int32)0);
666 	fDirMenu->SetMenuBar(fDirMenuField->MenuBar());
667 		// the above is a weird call from BDirMenu
668 		// ToDo: clean up
669 
670 	BEntry entry(TargetModel()->EntryRef());
671 	if (entry.InitCheck() == B_OK)
672 		fDirMenu->Populate(&entry, 0, true, true, false, true);
673 	else
674 		fDirMenu->Populate(0, 0, true, true, false, true);
675 
676 	fBackView->AddChild(fDirMenuField);
677 
678 	// add file name text view
679 	if (fIsSavePanel) {
680 		BRect rect(windRect);
681 		rect.top = rect.bottom - 35;
682 		rect.left = 8;
683 		rect.right = rect.left + 170;
684 		rect.bottom = rect.top + 13;
685 
686 		fTextControl = new BTextControl(rect, "text view",  "save text", "", NULL,
687 			B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
688 		DisallowMetaKeys(fTextControl->TextView());
689 		DisallowFilenameKeys(fTextControl->TextView());
690 		fBackView->AddChild(fTextControl);
691 		fTextControl->SetDivider(0.0f);
692 		fTextControl->TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1);
693 
694 		fButtonText = "Save";
695 	} else
696 		fButtonText = "Open";
697 
698 	rect = windRect;
699 	rect.OffsetTo(10, fDirMenuField->Frame().bottom + 10);
700 	rect.bottom = windRect.bottom - 60;
701 	rect.right -= B_V_SCROLL_BAR_WIDTH + 20;
702 
703 	// re-parent the poseview to our backview
704 	// ToDo:
705 	// This is terrible, fix it up
706 	PoseView()->RemoveSelf();
707 	if (fIsSavePanel)
708 		fBackView->AddChild(PoseView(), fTextControl);
709 	else
710 		fBackView->AddChild(PoseView());
711 
712 	PoseView()->MoveTo(rect.LeftTop());
713 	PoseView()->ResizeTo(rect.Width(), rect.Height());
714 	PoseView()->AddScrollBars();
715 	PoseView()->SetDragEnabled(false);
716 	PoseView()->SetDropEnabled(false);
717 	PoseView()->SetSelectionHandler(this);
718 	PoseView()->SetSelectionChangedHook(true);
719 	PoseView()->DisableSaveLocation();
720 	PoseView()->VScrollBar()->MoveBy(0, -1);
721 	PoseView()->VScrollBar()->ResizeBy(0, 1);
722 
723 
724 	AddShortcut('W', B_COMMAND_KEY, new BMessage(kCancelButton));
725 	AddShortcut('H', B_COMMAND_KEY, new BMessage(kSwitchToHome));
726 	AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(kOpenDir));
727 	AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY, new BMessage(kOpenDir));
728 	AddShortcut(B_UP_ARROW, B_COMMAND_KEY, new BMessage(kOpenParentDir));
729 	AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY, new BMessage(kOpenParentDir));
730 
731 	// New code to make buttons font sensitive
732 	rect = windRect;
733 	rect.top = rect.bottom - 35;
734 	rect.bottom -= 10;
735 	rect.right -= 25;
736 	float default_width = be_plain_font->StringWidth(fButtonText.String()) + 20;
737 	rect.left = (default_width > 75) ? (rect.right - default_width) : (rect.right - 75);
738 
739 	BButton *default_button = new BButton(rect, "default button", fButtonText.String(),
740 		new BMessage(kDefaultButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
741 	fBackView->AddChild(default_button);
742 
743 	rect.right = rect.left -= 10;
744 	float cancel_width = be_plain_font->StringWidth("Cancel") + 20;
745 	rect.left = (cancel_width > 75) ? (rect.right - cancel_width) : (rect.right - 75);
746 
747 	BButton *cancel_button = new BButton(rect, "cancel button", "Cancel",
748 		new BMessage(kCancelButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
749 	fBackView->AddChild(cancel_button);
750 
751 	if (!fIsSavePanel)
752 		default_button->SetEnabled(false);
753 
754 	default_button->MakeDefault(true);
755 
756 	RestoreState();
757 
758 	PoseView()->ScrollTo(B_ORIGIN);
759 	PoseView()->UpdateScrollRange();
760 	PoseView()->ScrollTo(B_ORIGIN);
761 
762 	if (fTextControl) {
763 		fTextControl->MakeFocus();
764 		fTextControl->TextView()->SelectAll();
765 	} else
766 		PoseView()->MakeFocus();
767 
768 	app_info info;
769 	BString title;
770 	if (be_app->GetAppInfo(&info) == B_OK)
771 		title << info.ref.name << ": ";
772 
773 	title << fButtonText;	// Open or Save
774 
775 	SetTitle(title.String());
776 
777 	SetSizeLimits(370, 10000, 200, 10000);
778 }
779 
780 
781 void
782 TFilePanel::RestoreState()
783 {
784 	BNode defaultingNode;
785 	if (DefaultStateSourceNode(kDefaultFilePanelTemplate, &defaultingNode, false)) {
786 		AttributeStreamFileNode streamNodeSource(&defaultingNode);
787 		RestoreWindowState(&streamNodeSource);
788 		PoseView()->Init(&streamNodeSource);
789 	} else {
790 		RestoreWindowState(NULL);
791 		PoseView()->Init(NULL);
792 	}
793 }
794 
795 
796 void
797 TFilePanel::SaveState(bool)
798 {
799 	BNode defaultingNode;
800 	if (DefaultStateSourceNode(kDefaultFilePanelTemplate, &defaultingNode,
801 		true, false)) {
802 		AttributeStreamFileNode streamNodeDestination(&defaultingNode);
803 		SaveWindowState(&streamNodeDestination);
804 		PoseView()->SaveState(&streamNodeDestination);
805 	}
806 }
807 
808 
809 void
810 TFilePanel::SaveState(BMessage &message) const
811 {
812 	_inherited::SaveState(message);
813 }
814 
815 
816 void
817 TFilePanel::RestoreWindowState(AttributeStreamNode *node)
818 {
819 	SetSizeLimits(360, 10000, 200, 10000);
820 	if (!node)
821 		return;
822 
823 	const char *rectAttributeName = kAttrWindowFrame;
824 	BRect frame(Frame());
825 	if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame)
826 		== sizeof(BRect)) {
827 		MoveTo(frame.LeftTop());
828 		ResizeTo(frame.Width(), frame.Height());
829 	}
830 }
831 
832 
833 void
834 TFilePanel::RestoreState(const BMessage &message)
835 {
836 	_inherited::RestoreState(message);
837 }
838 
839 
840 void
841 TFilePanel::RestoreWindowState(const BMessage &message)
842 {
843 	_inherited::RestoreWindowState(message);
844 }
845 
846 
847 void
848 TFilePanel::AddFileContextMenus(BMenu *menu)
849 {
850 	menu->AddItem(new BMenuItem("Get info", new BMessage(kGetInfo), 'I'));
851 	menu->AddItem(new BMenuItem("Edit name", new BMessage(kEditItem), 'E'));
852 	menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash() ?
853 			"Delete" : "Move to Trash",
854 			new BMessage(kMoveToTrash), 'T'));
855 	menu->AddSeparatorItem();
856 	menu->AddItem(new BMenuItem("Cut", new BMessage(B_CUT), 'X'));
857 	menu->AddItem(new BMenuItem("Copy", new BMessage(B_COPY), 'C'));
858 //	menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V'));
859 
860 	menu->SetTargetForItems(PoseView());
861 }
862 
863 
864 void
865 TFilePanel::AddVolumeContextMenus(BMenu *menu)
866 {
867 	menu->AddItem(new BMenuItem("Open", new BMessage(kOpenSelection), 'O'));
868 	menu->AddItem(new BMenuItem("Get info", new BMessage(kGetInfo), 'I'));
869 	menu->AddItem(new BMenuItem("Edit name", new BMessage(kEditItem), 'E'));
870 	menu->AddSeparatorItem();
871 	menu->AddItem(new BMenuItem("Cut", new BMessage(B_CUT), 'X'));
872 	menu->AddItem(new BMenuItem("Copy", new BMessage(B_COPY), 'C'));
873 //	menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V'));
874 
875 	menu->SetTargetForItems(PoseView());
876 }
877 
878 
879 void
880 TFilePanel::AddWindowContextMenus(BMenu *menu)
881 {
882 	BMenuItem *item = new BMenuItem("New folder", new BMessage(kNewFolder), 'N');
883 	item->SetTarget(PoseView());
884 	menu->AddItem(item);
885 	menu->AddSeparatorItem();
886 
887 	item = new BMenuItem("Paste", new BMessage(B_PASTE), 'V');
888 	item->SetTarget(PoseView());
889 	menu->AddItem(item);
890 	menu->AddSeparatorItem();
891 
892 	item = new BMenuItem("Select"B_UTF8_ELLIPSIS, new BMessage(kShowSelectionWindow),
893 		'A', B_SHIFT_KEY);
894 	item->SetTarget(PoseView());
895 	menu->AddItem(item);
896 
897 	item = new BMenuItem("Select all", new BMessage(B_SELECT_ALL), 'A');
898 	item->SetTarget(PoseView());
899 	menu->AddItem(item);
900 
901 	item = new BMenuItem("Invert selection", new BMessage(kInvertSelection), 'S');
902 	item->SetTarget(PoseView());
903 	menu->AddItem(item);
904 
905 	item = new BMenuItem("Go to parent", new BMessage(kOpenParentDir), B_UP_ARROW);
906 	item->SetTarget(this);
907 	menu->AddItem(item);
908 }
909 
910 
911 void
912 TFilePanel::AddDropContextMenus(BMenu *)
913 {
914 }
915 
916 
917 void
918 TFilePanel::MenusBeginning()
919 {
920 	int32 count = PoseView()->SelectionList()->CountItems();
921 
922 	EnableNamedMenuItem(fMenuBar, kNewFolder, !TargetModel()->IsRoot());
923 	EnableNamedMenuItem(fMenuBar, kMoveToTrash, !TargetModel()->IsRoot() && count);
924 	EnableNamedMenuItem(fMenuBar, kGetInfo, count != 0);
925 	EnableNamedMenuItem(fMenuBar, kEditItem, count == 1);
926 
927 	SetCutItem(fMenuBar);
928 	SetCopyItem(fMenuBar);
929 	SetPasteItem(fMenuBar);
930 
931 	fIsTrackingMenu = true;
932 }
933 
934 
935 void
936 TFilePanel::MenusEnded()
937 {
938 	fIsTrackingMenu = false;
939 }
940 
941 
942 void
943 TFilePanel::ShowContextMenu(BPoint point, const entry_ref *ref, BView *view)
944 {
945 	EnableNamedMenuItem(fWindowContextMenu, kNewFolder, !TargetModel()->IsRoot());
946 	EnableNamedMenuItem(fWindowContextMenu, kOpenParentDir, !TargetModel()->IsRoot());
947 	EnableNamedMenuItem(fWindowContextMenu, kMoveToTrash, !TargetModel()->IsRoot());
948 
949 	_inherited::ShowContextMenu(point, ref, view);
950 }
951 
952 
953 void
954 TFilePanel::SetupNavigationMenu(const entry_ref *, BMenu *)
955 {
956 	// do nothing here so nav menu doesn't get added
957 }
958 
959 
960 void
961 TFilePanel::SetButtonLabel(file_panel_button selector, const char *text)
962 {
963 	switch (selector) {
964 		case B_CANCEL_BUTTON:
965 			{
966 				BButton *button = dynamic_cast<BButton *>(FindView("cancel button"));
967 				if (!button)
968 					break;
969 
970 				float old_width = button->StringWidth(button->Label());
971 				button->SetLabel(text);
972 				float delta = old_width - button->StringWidth(text);
973 				if (delta) {
974 					button->MoveBy(delta, 0);
975 					button->ResizeBy(-delta, 0);
976 				}
977 			}
978 			break;
979 
980 		case B_DEFAULT_BUTTON:
981 			{
982 				fButtonText = text;
983 				float delta = 0;
984 				BButton *button = dynamic_cast<BButton *>(FindView("default button"));
985 				if (button) {
986 					float old_width = button->StringWidth(button->Label());
987 					button->SetLabel(text);
988 					delta = old_width - button->StringWidth(text);
989 					if (delta) {
990 						button->MoveBy(delta, 0);
991 						button->ResizeBy(-delta, 0);
992 					}
993 				}
994 
995 				// now must move cancel button
996 				button = dynamic_cast<BButton *>(FindView("cancel button"));
997 				if (button)
998 					button->MoveBy(delta, 0);
999 			}
1000 			break;
1001 	}
1002 }
1003 
1004 
1005 void
1006 TFilePanel::SetSaveText(const char *text)
1007 {
1008 	if (!text)
1009 		return;
1010 
1011 	BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("text view"));
1012 	textControl->SetText(text);
1013 	textControl->TextView()->SelectAll();
1014 }
1015 
1016 
1017 void
1018 TFilePanel::MessageReceived(BMessage *message)
1019 {
1020 	entry_ref ref;
1021 
1022 	switch (message->what) {
1023 		case B_REFS_RECEIVED:
1024 			// item was double clicked in file panel (PoseView)
1025 			if (message->FindRef("refs", &ref) == B_OK) {
1026 				BEntry entry(&ref, true);
1027 				if (entry.InitCheck() == B_OK) {
1028 					// Double-click on dir or link-to-dir ALWAYS opens the dir.
1029 					// If more than one dir is selected, the
1030 					// first is entered.
1031 					if (entry.IsDirectory()) {
1032 						entry.GetRef(&ref);
1033 						bool isDesktop = SwitchDirToDesktopIfNeeded(ref);
1034 
1035 						PoseView()->SetIsDesktop(isDesktop);
1036 						entry.SetTo(&ref);
1037 						PoseView()->SwitchDir(&ref);
1038 						SwitchDirMenuTo(&ref);
1039 					} else {
1040 						// Otherwise, we have a file or a link to a file.
1041 						// AdjustButton has already tested the flavor;
1042 						// all we have to do is see if the button is enabled.
1043 						BButton *button = dynamic_cast<BButton *>(FindView("default button"));
1044 						if (!button)
1045 							break;
1046 
1047 						if (IsSavePanel()) {
1048 							int32 count = 0;
1049 							type_code type;
1050 							message->GetInfo("refs", &type, &count);
1051 
1052 							// Don't allow saves of multiple files
1053 							if (count > 1) {
1054 								ShowCenteredAlert("Sorry, saving more than one item is not allowed.",
1055 									"Cancel");
1056 							} else {
1057 								// if we are a savepanel, set up the filepanel correctly
1058 								// then pass control so we follow the same path as if the user
1059 								// clicked the save button
1060 
1061 								// set the 'name' fld to the current ref's name
1062 								// notify the panel that the default button should be enabled
1063 								SetSaveText(ref.name);
1064 								SelectionChanged();
1065 
1066 								HandleSaveButton();
1067 							}
1068 							break;
1069 						}
1070 
1071 					  	// send handler a message and close
1072 						BMessage openMessage(*fMessage);
1073 						for (int32 index = 0; ; index++) {
1074 					  		if (message->FindRef("refs", index, &ref) != B_OK)
1075 								break;
1076 							openMessage.AddRef("refs", &ref);
1077 					  	}
1078 						OpenSelectionCommon(&openMessage);
1079 					}
1080 				}
1081 			}
1082 			break;
1083 
1084 		case kSwitchDirectory:
1085 		{
1086 			entry_ref ref;
1087 			// this comes from dir menu or nav menu, so switch directories
1088 			if (message->FindRef("refs", &ref) == B_OK) {
1089 				BEntry entry(&ref, true);
1090 				if (entry.GetRef(&ref) == B_OK)
1091 					SetTo(&ref);
1092 			}
1093 			break;
1094 		}
1095 
1096 		case kSwitchToHome:
1097 		{
1098 			BPath homePath;
1099 			entry_ref ref;
1100 			if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK
1101 				|| get_ref_for_path(homePath.Path(), &ref) != B_OK)
1102 				break;
1103 
1104 			SetTo(&ref);
1105 			break;
1106 		}
1107 
1108 		case kAddCurrentDir:
1109 		{
1110 			BPath path;
1111 			if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
1112 				break;
1113 
1114 			path.Append(kGoDirectory);
1115 			BDirectory goDirectory(path.Path());
1116 
1117 			if (goDirectory.InitCheck() == B_OK) {
1118 				BEntry entry(TargetModel()->EntryRef());
1119 				entry.GetPath(&path);
1120 
1121 				BSymLink link;
1122 				goDirectory.CreateSymLink(TargetModel()->Name(), path.Path(), &link);
1123 			}
1124 			break;
1125 		}
1126 
1127 		case kEditFavorites:
1128 		{
1129 			BPath path;
1130 			if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
1131 				break;
1132 
1133 			path.Append(kGoDirectory);
1134 			BMessenger msgr(kTrackerSignature);
1135 			if (msgr.IsValid()) {
1136 				BMessage message(B_REFS_RECEIVED);
1137 				entry_ref ref;
1138 				if (get_ref_for_path(path.Path(), &ref) == B_OK) {
1139 					message.AddRef("refs", &ref);
1140 					msgr.SendMessage(&message);
1141 				}
1142 			}
1143 			break;
1144 		}
1145 
1146 		case kCancelButton:
1147 			PostMessage(B_QUIT_REQUESTED);
1148 			break;
1149 
1150 		case kOpenDir:
1151 			OpenDirectory();
1152 			break;
1153 
1154 		case kOpenParentDir:
1155 			OpenParent();
1156 			break;
1157 
1158 		case kDefaultButton:
1159 			if (fIsSavePanel) {
1160 				if (PoseView()->IsFocus()
1161 					&& PoseView()->SelectionList()->CountItems() == 1) {
1162 					Model *model = (PoseView()->SelectionList()->FirstItem())->TargetModel();
1163 					if (model->ResolveIfLink()->IsDirectory()) {
1164 						PoseView()->CommitActivePose();
1165 						PoseView()->OpenSelection();
1166 						break;
1167 					}
1168 				}
1169 
1170 				HandleSaveButton();
1171 			} else
1172 				HandleOpenButton();
1173 			break;
1174 
1175 		case B_OBSERVER_NOTICE_CHANGE:
1176 		{
1177 			int32 observerWhat;
1178 			if (message->FindInt32("be:observe_change_what", &observerWhat) == B_OK) {
1179 				switch (observerWhat) {
1180 					case kDesktopFilePanelRootChanged:
1181 					{
1182 						bool desktopIsRoot = true;
1183 						if (message->FindBool("DesktopFilePanelRoot", &desktopIsRoot) == B_OK)
1184 							TrackerSettings().SetDesktopFilePanelRoot(desktopIsRoot);
1185 						SetTo(TargetModel()->EntryRef());
1186 						break;
1187 					}
1188 				}
1189 			}
1190 			break;
1191 		}
1192 
1193 		default:
1194 			_inherited::MessageReceived(message);
1195 	}
1196 }
1197 
1198 
1199 void
1200 TFilePanel::OpenDirectory()
1201 {
1202 	BObjectList<BPose> *list = PoseView()->SelectionList();
1203 	if (list->CountItems() != 1)
1204 		return;
1205 
1206 	Model *model = list->FirstItem()->TargetModel();
1207 	if (model->ResolveIfLink()->IsDirectory()) {
1208 		BMessage message(B_REFS_RECEIVED);
1209 		message.AddRef("refs", model->EntryRef());
1210 		PostMessage(&message);
1211 	}
1212 }
1213 
1214 
1215 void
1216 TFilePanel::OpenParent()
1217 {
1218 	if (!CanOpenParent())
1219 		return;
1220 
1221 	BEntry parentEntry;
1222 	BDirectory dir;
1223 
1224 	Model oldModel(*PoseView()->TargetModel());
1225 	BEntry entry(oldModel.EntryRef());
1226 
1227 	if (entry.InitCheck() == B_OK
1228 		&& entry.GetParent(&dir) == B_OK
1229 		&& dir.GetEntry(&parentEntry) == B_OK
1230 		&& entry != parentEntry) {
1231 
1232 		entry_ref ref;
1233 		parentEntry.GetRef(&ref);
1234 
1235 		PoseView()->SetIsDesktop(SwitchDirToDesktopIfNeeded(ref));
1236 		PoseView()->SwitchDir(&ref);
1237 		SwitchDirMenuTo(&ref);
1238 
1239 		// make sure the child get's selected in the new view once it
1240 		// shows up
1241 		fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1242 			(&TFilePanel::SelectChildInParent, this,
1243 			const_cast<const entry_ref *>(&ref),
1244 			oldModel.NodeRef()), 100000, 200000, 5000000);
1245 	}
1246 }
1247 
1248 
1249 bool
1250 TFilePanel::CanOpenParent() const
1251 {
1252 	if (TrackerSettings().DesktopFilePanelRoot()) {
1253 		// don't allow opening Desktop folder's parent
1254 		BEntry entry(TargetModel()->EntryRef());
1255 		if (FSIsDeskDir(&entry, TargetModel()->NodeRef()->device))
1256 			return false;
1257 	}
1258 
1259 	// block on "/"
1260 	BEntry root("/");
1261 	node_ref rootRef;
1262 	root.GetNodeRef(&rootRef);
1263 
1264 	return rootRef != *TargetModel()->NodeRef();
1265 }
1266 
1267 
1268 bool
1269 TFilePanel::SwitchDirToDesktopIfNeeded(entry_ref &ref)
1270 {
1271 	// support showing Desktop as root of everything
1272 	// This call implements the worm hole that maps Desktop as
1273 	// a root above the disks
1274 	TrackerSettings settings;
1275 	if (!settings.DesktopFilePanelRoot())
1276 		// Tracker isn't set up that way, just let Disks show
1277 		return false;
1278 
1279 	BEntry entry(&ref);
1280 	BEntry root("/");
1281 
1282 	BDirectory desktopDir;
1283 	BVolume	bootVol;
1284 	BVolumeRoster().GetBootVolume(&bootVol);
1285 	FSGetDeskDir(&desktopDir, bootVol.Device());
1286 
1287 	if ((bootVol.Device() != ref.device && FSIsDeskDir(&entry, ref.device))
1288 		// navigated into non-boot desktop, switch to boot desktop
1289 		|| (entry == root && !settings.ShowDisksIcon())) {
1290 		// hit "/" level, map to desktop
1291 
1292 		desktopDir.GetEntry(&entry);
1293 		entry.GetRef(&ref);
1294 		return true;
1295 	}
1296 	return FSIsDeskDir(&entry, ref.device);
1297 }
1298 
1299 
1300 bool
1301 TFilePanel::SelectChildInParent(const entry_ref *, const node_ref *child)
1302 {
1303 	AutoLock<TFilePanel> lock(this);
1304 
1305 	if (!IsLocked())
1306 		return false;
1307 
1308 	int32 index;
1309 	BPose *pose = PoseView()->FindPose(child, &index);
1310 	if (!pose)
1311 		return false;
1312 
1313 	PoseView()->UpdateScrollRange();
1314 		// ToDo: Scroll range should be updated by now, for some
1315 		//	reason sometimes it is not right, force it here
1316 	PoseView()->SelectPose(pose, index, true);
1317 	return true;
1318 }
1319 
1320 
1321 int32
1322 TFilePanel::ShowCenteredAlert(const char *text, const char *button1,
1323 	const char *button2, const char *button3)
1324 {
1325 	BAlert *alert = new BAlert("", text, button1, button2, button3,
1326 		B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1327 	alert->MoveTo(Frame().left + 10, Frame().top + 10);
1328 
1329 #if 0
1330 	if (button1 != NULL && !strncmp(button1, "Cancel", 7))
1331 		alert->SetShortcut(0, B_ESCAPE);
1332 	else if (button2 != NULL && !strncmp(button2, "Cancel", 7))
1333 		alert->SetShortcut(1, B_ESCAPE);
1334 	else if (button3 != NULL && !strncmp(button3, "Cancel", 7))
1335 		alert->SetShortcut(2, B_ESCAPE);
1336 #endif
1337 
1338 	return alert->Go();
1339 }
1340 
1341 
1342 void
1343 TFilePanel::HandleSaveButton()
1344 {
1345 	BDirectory dir;
1346 
1347 	if (TargetModel()->IsRoot()) {
1348 		ShowCenteredAlert("Sorry, you can't save things at the root of "
1349 			"your system.", "Cancel");
1350 		return;
1351 	}
1352 
1353 	// check for some illegal file names
1354 	if (strcmp(fTextControl->Text(), ".") == 0
1355 		|| strcmp(fTextControl->Text(), "..") == 0) {
1356 		ShowCenteredAlert("The specified name is illegal. Please choose "
1357 			"another name.", "Cancel");
1358 		fTextControl->TextView()->SelectAll();
1359 		return;
1360 	}
1361 
1362 	if (dir.SetTo(TargetModel()->EntryRef()) != B_OK) {
1363 		ShowCenteredAlert("There was a problem trying to save in the folder "
1364 			"you specified. Please try another one.", "Cancel");
1365 		return;
1366 	}
1367 
1368 	if (dir.Contains(fTextControl->Text())) {
1369 		if (dir.Contains(fTextControl->Text(), B_DIRECTORY_NODE)) {
1370 			ShowCenteredAlert("The specified name is already used as the name "
1371 				"of a folder. Please choose another name.", "Cancel");
1372 			fTextControl->TextView()->SelectAll();
1373 			return;
1374 		} else {
1375 			// if this was invoked by a dbl click, it is an explicit replacement
1376 			// of the file.
1377 			BString str;
1378 			str << "The file \"" << fTextControl->Text() << "\" already exists in the "
1379 				"specified folder. Do you want to replace it?";
1380 
1381 			if (ShowCenteredAlert(str.String(), "Cancel", "Replace") == 0) {
1382 				// user canceled
1383 				fTextControl->TextView()->SelectAll();
1384 				return;
1385 			}
1386 			// user selected "Replace" - let app deal with it
1387 		}
1388 	}
1389 
1390 	BMessage message(*fMessage);
1391 	message.AddRef("directory", TargetModel()->EntryRef());
1392 	message.AddString("name", fTextControl->Text());
1393 
1394 	if (fClientObject)
1395 		fClientObject->SendMessage(&fTarget, &message);
1396 	else
1397 		fTarget.SendMessage(&message);
1398 
1399 	// close window if we're dealing with standard message
1400 	if (fHideWhenDone)
1401 		PostMessage(B_QUIT_REQUESTED);
1402 }
1403 
1404 
1405 void
1406 TFilePanel::OpenSelectionCommon(BMessage *openMessage)
1407 {
1408 	if (!openMessage->HasRef("refs"))
1409 		return;
1410 
1411 	for (int32 index = 0; ; index++) {
1412 		entry_ref ref;
1413 		if (openMessage->FindRef("refs", index, &ref) != B_OK)
1414 			break;
1415 
1416 		BEntry entry(&ref, true);
1417 		if (entry.InitCheck() == B_OK) {
1418 			if (entry.IsDirectory())
1419 				BRoster().AddToRecentFolders(&ref);
1420 			else
1421 				BRoster().AddToRecentDocuments(&ref);
1422 		}
1423 	}
1424 
1425 	BRoster().AddToRecentFolders(TargetModel()->EntryRef());
1426 
1427 	if (fClientObject)
1428 		fClientObject->SendMessage(&fTarget, openMessage);
1429 	else
1430 		fTarget.SendMessage(openMessage);
1431 
1432 	// close window if we're dealing with standard message
1433 	if (fHideWhenDone)
1434 		PostMessage(B_QUIT_REQUESTED);
1435 }
1436 
1437 
1438 void
1439 TFilePanel::HandleOpenButton()
1440 {
1441 	PoseView()->CommitActivePose();
1442 	BObjectList<BPose> *selection = PoseView()->SelectionList();
1443 
1444 	// if we have only one directory and we're not opening dirs, enter.
1445 	if ((fNodeFlavors & B_DIRECTORY_NODE) == 0
1446 		&& selection->CountItems() == 1) {
1447 		Model *model = selection->FirstItem()->TargetModel();
1448 
1449 		if (model->IsDirectory()
1450 			|| (model->IsSymLink() && !(fNodeFlavors & B_SYMLINK_NODE)
1451 				&& model->ResolveIfLink()->IsDirectory())) {
1452 
1453 			BMessage message(B_REFS_RECEIVED);
1454 			message.AddRef("refs", model->EntryRef());
1455 			PostMessage(&message);
1456 			return;
1457 		}
1458 	}
1459 
1460 	// don't do anything unless there are items selected
1461     // message->fMessage->message from here to end
1462 	if (selection->CountItems()) {
1463 		BMessage message(*fMessage);
1464 		// go through selection and add appropriate items
1465 		for (int32 index = 0; index < selection->CountItems(); index++) {
1466 			Model *model = selection->ItemAt(index)->TargetModel();
1467 
1468 			if (((fNodeFlavors & B_DIRECTORY_NODE) != 0
1469 					&& model->ResolveIfLink()->IsDirectory())
1470 				|| ((fNodeFlavors & B_SYMLINK_NODE) != 0 && model->IsSymLink())
1471 				|| ((fNodeFlavors & B_FILE_NODE) != 0 && model->ResolveIfLink()->IsFile()))
1472 				message.AddRef("refs", model->EntryRef());
1473 		}
1474 
1475 		OpenSelectionCommon(&message);
1476 	}
1477 }
1478 
1479 
1480 void
1481 TFilePanel::SwitchDirMenuTo(const entry_ref *ref)
1482 {
1483 	BEntry entry(ref);
1484 	for (int32 index = fDirMenu->CountItems() - 1; index >= 0; index--)
1485 		delete fDirMenu->RemoveItem(index);
1486 
1487 	fDirMenuField->MenuBar()->RemoveItem((int32)0);
1488 	fDirMenu->Populate(&entry, 0, true, true, false, true);
1489 
1490 	ModelMenuItem *item = dynamic_cast<ModelMenuItem *>(
1491 		fDirMenuField->MenuBar()->ItemAt(0));
1492 	ASSERT(item);
1493 	item->SetEntry(&entry);
1494 }
1495 
1496 
1497 void
1498 TFilePanel::WindowActivated(bool active)
1499 {
1500 	// force focus to update properly
1501 	fBackView->Invalidate();
1502 	_inherited::WindowActivated(active);
1503 }
1504 
1505 
1506 //	#pragma mark -
1507 
1508 
1509 BFilePanelPoseView::BFilePanelPoseView(Model *model, BRect frame, uint32 resizeMask)
1510 	: BPoseView(model, frame, kListMode, resizeMask)
1511 {
1512 	BEntry entry;
1513 	model->GetEntry(&entry);
1514 	fIsDesktop = FSIsDeskDir(&entry);
1515 }
1516 
1517 
1518 void
1519 BFilePanelPoseView::StartWatching()
1520 {
1521 	TTracker::WatchNode(0, B_WATCH_MOUNT, this);
1522 
1523 	// inter-application observing
1524 	BMessenger tracker(kTrackerSignature);
1525 	BHandler::StartWatching(tracker, kVolumesOnDesktopChanged);
1526 }
1527 
1528 
1529 void
1530 BFilePanelPoseView::StopWatching()
1531 {
1532 	stop_watching(this);
1533 
1534 	// inter-application observing
1535 	BMessenger tracker(kTrackerSignature);
1536 	BHandler::StopWatching(tracker, kVolumesOnDesktopChanged);
1537 }
1538 
1539 
1540 bool
1541 BFilePanelPoseView::FSNotification(const BMessage *message)
1542 {
1543 	if (IsDesktopView()) {
1544 		// Pretty much copied straight from DesktopPoseView.  Would be better
1545 		// if the code could be shared somehow.
1546 		switch (message->FindInt32("opcode")) {
1547 			case B_DEVICE_MOUNTED:
1548 			{
1549 				dev_t device;
1550 				if (message->FindInt32("new device", &device) != B_OK)
1551 					break;
1552 
1553 				ASSERT(TargetModel());
1554 				TrackerSettings settings;
1555 
1556 				BVolume volume(device);
1557 				if (volume.InitCheck() != B_OK)
1558 					break;
1559 
1560 				if (settings.MountVolumesOntoDesktop()
1561 					&& (!volume.IsShared() || settings.MountSharedVolumesOntoDesktop())) {
1562 					// place an icon for the volume onto the desktop
1563 					CreateVolumePose(&volume, true);
1564 				}
1565 			}
1566 			break;
1567 		}
1568 	}
1569 	return _inherited::FSNotification(message);
1570 }
1571 
1572 
1573 void
1574 BFilePanelPoseView::RestoreState(AttributeStreamNode *node)
1575 {
1576 	_inherited::RestoreState(node);
1577 	fViewState->SetViewMode(kListMode);
1578 }
1579 
1580 
1581 void
1582 BFilePanelPoseView::RestoreState(const BMessage &message)
1583 {
1584 	_inherited::RestoreState(message);
1585 }
1586 
1587 
1588 void
1589 BFilePanelPoseView::SavePoseLocations(BRect *)
1590 {
1591 }
1592 
1593 
1594 EntryListBase *
1595 BFilePanelPoseView::InitDirentIterator(const entry_ref *ref)
1596 {
1597 	if (IsDesktopView())
1598 		return DesktopPoseView::InitDesktopDirentIterator(this, ref);
1599 
1600 	return _inherited::InitDirentIterator(ref);
1601 }
1602 
1603 
1604 bool
1605 BFilePanelPoseView::ShouldShowPose(const Model *model, const PoseInfo *poseInfo)
1606 {
1607 	if (IsDesktopView() && !ShouldShowDesktopPose(TargetModel()->NodeRef()->device,
1608 		model, poseInfo))
1609 		return false;
1610 
1611 	return _inherited::ShouldShowPose(model, poseInfo);
1612 }
1613 
1614 
1615 void
1616 BFilePanelPoseView::SetIsDesktop(bool on)
1617 {
1618 	fIsDesktop = on;
1619 }
1620 
1621 
1622 bool
1623 BFilePanelPoseView::IsDesktopView() const
1624 {
1625 	return fIsDesktop;
1626 }
1627 
1628 
1629 void
1630 BFilePanelPoseView::ShowVolumes(bool visible, bool showShared)
1631 {
1632 	if (IsDesktopView()) {
1633 		if (!visible)
1634 			RemoveRootPoses();
1635 		else
1636 			AddRootPoses(true, showShared);
1637 	}
1638 
1639 
1640 	TFilePanel *filepanel = dynamic_cast<TFilePanel*>(Window());
1641 	if (filepanel)
1642 		filepanel->SetTo(TargetModel()->EntryRef());
1643 }
1644 
1645 
1646 void
1647 BFilePanelPoseView::AdaptToVolumeChange(BMessage *message)
1648 {
1649 	bool showDisksIcon;
1650 	bool mountVolumesOnDesktop;
1651 	bool mountSharedVolumesOntoDesktop;
1652 
1653 	message->FindBool("ShowDisksIcon", &showDisksIcon);
1654 	message->FindBool("MountVolumesOntoDesktop", &mountVolumesOnDesktop);
1655 	message->FindBool("MountSharedVolumesOntoDesktop", &mountSharedVolumesOntoDesktop);
1656 
1657 	BEntry entry("/");
1658 	Model model(&entry);
1659 	if (model.InitCheck() == B_OK) {
1660 		BMessage monitorMsg;
1661 		monitorMsg.what = B_NODE_MONITOR;
1662 
1663 		if (showDisksIcon)
1664 			monitorMsg.AddInt32("opcode", B_ENTRY_CREATED);
1665 		else
1666 			monitorMsg.AddInt32("opcode", B_ENTRY_REMOVED);
1667 
1668 		monitorMsg.AddInt32("device", model.NodeRef()->device);
1669 		monitorMsg.AddInt64("node", model.NodeRef()->node);
1670 		monitorMsg.AddInt64("directory", model.EntryRef()->directory);
1671 		monitorMsg.AddString("name", model.EntryRef()->name);
1672 		TrackerSettings().SetShowDisksIcon(showDisksIcon);
1673 		if (Window())
1674 			Window()->PostMessage(&monitorMsg, this);
1675 	}
1676 
1677 	ShowVolumes(mountVolumesOnDesktop, mountSharedVolumesOntoDesktop);
1678 }
1679 
1680 
1681 void
1682 BFilePanelPoseView::AdaptToDesktopIntegrationChange(BMessage *message)
1683 {
1684 	bool mountVolumesOnDesktop = true;
1685 	bool mountSharedVolumesOntoDesktop = true;
1686 
1687 	message->FindBool("MountVolumesOntoDesktop", &mountVolumesOnDesktop);
1688 	message->FindBool("MountSharedVolumesOntoDesktop", &mountSharedVolumesOntoDesktop);
1689 
1690 	ShowVolumes(false, mountSharedVolumesOntoDesktop);
1691 	ShowVolumes(mountVolumesOnDesktop, mountSharedVolumesOntoDesktop);
1692 }
1693 
1694