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