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