xref: /haiku/src/kits/tracker/infowindow/HeaderView.cpp (revision af3dceb2795c29b8fa07aec3865b8a5c976e6552)
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 "HeaderView.h"
37 
38 #include <Alert.h>
39 #include <Application.h>
40 #include <Catalog.h>
41 #include <ControlLook.h>
42 #include <Locale.h>
43 #include <PopUpMenu.h>
44 #include <ScrollView.h>
45 #include <Volume.h>
46 #include <VolumeRoster.h>
47 #include <Window.h>
48 
49 #include "Commands.h"
50 #include "FSUtils.h"
51 #include "GeneralInfoView.h"
52 #include "IconMenuItem.h"
53 #include "Model.h"
54 #include "NavMenu.h"
55 #include "PoseView.h"
56 #include "Tracker.h"
57 
58 
59 #undef B_TRANSLATION_CONTEXT
60 #define B_TRANSLATION_CONTEXT "InfoWindow"
61 
62 
63 // Amount you have to move the mouse before a drag starts
64 const float kDragSlop = 3.0f;
65 
66 
67 HeaderView::HeaderView(Model* model)
68 	:
69 	BView("header", B_WILL_DRAW),
70 	fModel(model),
71 	fIconModel(model),
72 	fTitleEditView(NULL),
73 	fTrackingState(no_track),
74 	fMouseDown(false),
75 	fIsDropTarget(false),
76 	fDoubleClick(false),
77 	fDragging(false)
78 {
79 	const float labelSpacing = be_control_look->DefaultLabelSpacing();
80 	fIconRect = BRect(BPoint(labelSpacing * 3.0f, labelSpacing),
81 		be_control_look->ComposeIconSize(B_LARGE_ICON));
82 	SetExplicitSize(BSize(B_SIZE_UNSET, fIconRect.Width() + 2 * fIconRect.top));
83 
84 	// The title rect
85 	// The magic numbers are used to properly calculate the rect so that
86 	// when the editing text view is displayed, the position of the text
87 	// does not change.
88 	BFont currentFont;
89 	font_height fontMetrics;
90 	GetFont(&currentFont);
91 	currentFont.GetHeight(&fontMetrics);
92 
93 	fTitleRect.left = fIconRect.right + labelSpacing;
94 	fTitleRect.top = 0;
95 	fTitleRect.bottom = fontMetrics.ascent + 1;
96 	fTitleRect.right = min_c(
97 		fTitleRect.left + currentFont.StringWidth(fModel->Name()),
98 		Bounds().Width() - labelSpacing);
99 	// Offset so that it centers with the icon
100 	fTitleRect.OffsetBy(0,
101 		fIconRect.top + ((fIconRect.Height() - fTitleRect.Height()) / 2));
102 	// Make some room for the border for when we are in edit mode
103 	// (Negative numbers increase the size of the rect)
104 	fTitleRect.InsetBy(-1, -2);
105 
106 	// If the model is a symlink, then we deference the model to
107 	// get the targets icon
108 	if (fModel->IsSymLink()) {
109 		Model* resolvedModel = new Model(model->EntryRef(), true, true);
110 		if (resolvedModel->InitCheck() == B_OK)
111 			fIconModel = resolvedModel;
112 		// broken link, just show the symlink
113 		else
114 			delete resolvedModel;
115 	}
116 }
117 
118 
119 HeaderView::~HeaderView()
120 {
121 	if (fIconModel != fModel)
122 		delete fIconModel;
123 }
124 
125 
126 void
127 HeaderView::ModelChanged(Model* model, BMessage* message)
128 {
129 	// Update the icon stuff
130 	if (fIconModel != fModel) {
131 		delete fIconModel;
132 		fIconModel = NULL;
133 	}
134 
135 	fModel = model;
136 	if (fModel->IsSymLink()) {
137 		// if we are looking at a symlink, deference the model and look
138 		// at the target
139 		Model* resolvedModel = new Model(model->EntryRef(), true, true);
140 		if (resolvedModel->InitCheck() == B_OK) {
141 			if (fIconModel != fModel)
142 				delete fIconModel;
143 			fIconModel = resolvedModel;
144 		} else {
145 			fIconModel = model;
146 			delete resolvedModel;
147 		}
148 	}
149 
150 	Invalidate();
151 }
152 
153 
154 void
155 HeaderView::ReLinkTargetModel(Model* model)
156 {
157 	fModel = model;
158 	if (fModel->IsSymLink()) {
159 		Model* resolvedModel = new Model(model->EntryRef(), true, true);
160 		if (resolvedModel->InitCheck() == B_OK) {
161 			if (fIconModel != fModel)
162 				delete fIconModel;
163 			fIconModel = resolvedModel;
164 		} else {
165 			fIconModel = fModel;
166 			delete resolvedModel;
167 		}
168 	}
169 	Invalidate();
170 }
171 
172 
173 void
174 HeaderView::BeginEditingTitle()
175 {
176 	if (fTitleEditView != NULL)
177 		return;
178 
179 	BFont font(be_plain_font);
180 	font.SetSize(font.Size() + 2);
181 	BRect textFrame(fTitleRect);
182 	textFrame.right = Bounds().Width() - 5;
183 	BRect textRect(textFrame);
184 	textRect.OffsetTo(0, 0);
185 	textRect.InsetBy(1, 1);
186 
187 	// Just make it some really large size, since we don't do any line
188 	// wrapping. The text filter will make sure to scroll the cursor
189 	// into position
190 
191 	textRect.right = 2000;
192 	fTitleEditView = new BTextView(textFrame, "text_editor",
193 		textRect, &font, 0, B_FOLLOW_ALL, B_WILL_DRAW);
194 	fTitleEditView->SetText(fModel->Name());
195 	DisallowFilenameKeys(fTitleEditView);
196 
197 	// Reset the width of the text rect
198 	textRect = fTitleEditView->TextRect();
199 	textRect.right = fTitleEditView->LineWidth() + 20;
200 	fTitleEditView->SetTextRect(textRect);
201 	fTitleEditView->SetWordWrap(false);
202 	// Add filter for catching B_RETURN and B_ESCAPE key's
203 	fTitleEditView->AddFilter(
204 		new BMessageFilter(B_KEY_DOWN, HeaderView::TextViewFilter));
205 
206 	BScrollView* scrollView = new BScrollView("BorderView", fTitleEditView,
207 		0, 0, false, false, B_PLAIN_BORDER);
208 	AddChild(scrollView);
209 	fTitleEditView->SelectAll();
210 	fTitleEditView->MakeFocus();
211 
212 	Window()->UpdateIfNeeded();
213 }
214 
215 
216 void
217 HeaderView::FinishEditingTitle(bool commit)
218 {
219 	if (fTitleEditView == NULL)
220 		return;
221 
222 	bool reopen = false;
223 
224 	const char* text = fTitleEditView->Text();
225 	uint32 length = strlen(text);
226 	if (commit && strcmp(text, fModel->Name()) != 0
227 		&& length < B_FILE_NAME_LENGTH) {
228 		BEntry entry(fModel->EntryRef());
229 		BDirectory parent;
230 		if (entry.InitCheck() == B_OK
231 			&& entry.GetParent(&parent) == B_OK) {
232 			if (parent.Contains(text)) {
233 				BAlert* alert = new BAlert("",
234 					B_TRANSLATE("That name is already taken. "
235 					"Please type another one."),
236 					B_TRANSLATE("OK"),
237 					0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
238 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
239 				alert->Go();
240 				reopen = true;
241 			} else {
242 				if (fModel->IsVolume()) {
243 					BVolume	volume(fModel->NodeRef()->device);
244 					if (volume.InitCheck() == B_OK)
245 						volume.SetName(text);
246 				} else
247 					entry.Rename(text);
248 
249 				// Adjust the size of the text rect
250 				BFont currentFont(be_plain_font);
251 				currentFont.SetSize(currentFont.Size() + 2);
252 				fTitleRect.right = min_c(fTitleRect.left
253 						+ currentFont.StringWidth(fTitleEditView->Text()),
254 					Bounds().Width() - 5);
255 			}
256 		}
257 	} else if (length >= B_FILE_NAME_LENGTH) {
258 		BAlert* alert = new BAlert("",
259 			B_TRANSLATE("That name is too long. Please type another one."),
260 			B_TRANSLATE("OK"),
261 			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
262 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
263 		alert->Go();
264 		reopen = true;
265 	}
266 
267 	// Remove view
268 	BView* scrollView = fTitleEditView->Parent();
269 	RemoveChild(scrollView);
270 	delete scrollView;
271 	fTitleEditView = NULL;
272 
273 	if (reopen)
274 		BeginEditingTitle();
275 }
276 
277 
278 void
279 HeaderView::Draw(BRect)
280 {
281 	// Set the low color for anti-aliasing
282 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
283 
284 	// Clear the old contents
285 	SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
286 	FillRect(Bounds());
287 
288 	rgb_color labelColor = ui_color(B_PANEL_TEXT_COLOR);
289 
290 	// Draw the icon, straddling the border
291 	SetDrawingMode(B_OP_OVER);
292 	IconCache::sIconCache->Draw(fIconModel, this, fIconRect.LeftTop(),
293 		kNormalIcon, fIconRect.Size(), true);
294 	SetDrawingMode(B_OP_COPY);
295 
296 	// Font information
297 	font_height fontMetrics;
298 	BFont currentFont;
299 	float lineBase = 0;
300 
301 	// Draw the main title if the user is not currently editing it
302 	if (fTitleEditView == NULL) {
303 		SetFont(be_bold_font);
304 		SetFontSize(be_bold_font->Size());
305 		GetFont(&currentFont);
306 		currentFont.GetHeight(&fontMetrics);
307 		lineBase = fTitleRect.bottom - fontMetrics.descent;
308 		SetHighColor(labelColor);
309 		MovePenTo(BPoint(fIconRect.right + 6, lineBase));
310 
311 		// Recalculate the rect width
312 		fTitleRect.right = min_c(
313 				fTitleRect.left + currentFont.StringWidth(fModel->Name()),
314 			Bounds().Width() - 5);
315 		// Check for possible need of truncation
316 		if (StringWidth(fModel->Name()) > fTitleRect.Width()) {
317 			BString nameString(fModel->Name());
318 			TruncateString(&nameString, B_TRUNCATE_END,
319 				fTitleRect.Width() - 2);
320 			DrawString(nameString.String());
321 		} else
322 			DrawString(fModel->Name());
323 	}
324 
325 }
326 
327 
328 void
329 HeaderView::MakeFocus(bool focus)
330 {
331 	if (!focus && fTitleEditView != NULL)
332 		FinishEditingTitle(true);
333 }
334 
335 
336 void
337 HeaderView::WindowActivated(bool active)
338 {
339 	if (active)
340 		return;
341 
342 	if (fTitleEditView != NULL)
343 		FinishEditingTitle(true);
344 }
345 
346 
347 void
348 HeaderView::MouseDown(BPoint where)
349 {
350 	// Assume this isn't part of a double click
351 	fDoubleClick = false;
352 
353 	BEntry entry;
354 	fModel->GetEntry(&entry);
355 
356 	if (fTitleRect.Contains(where)) {
357 		if (!fModel->HasLocalizedName()
358 			&& ConfirmChangeIfWellKnownDirectory(&entry, kRename, true)) {
359 			BeginEditingTitle();
360 		}
361 	} else if (fTitleEditView) {
362 		FinishEditingTitle(true);
363 	} else if (fIconRect.Contains(where)) {
364 		uint32 buttons;
365 		Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
366 		if (SecondaryMouseButtonDown(modifiers(), buttons)) {
367 			// Show contextual menu
368 			BPopUpMenu* contextMenu
369 				= new BPopUpMenu("FileContext", false, false);
370 			if (contextMenu) {
371 				BuildContextMenu(contextMenu);
372 				contextMenu->SetAsyncAutoDestruct(true);
373 				contextMenu->Go(ConvertToScreen(where), true, true,
374 					ConvertToScreen(fIconRect));
375 			}
376 		} else {
377 			// Check to see if the point is actually on part of the icon,
378 			// versus just in the container rect. The icons are always
379 			// the large version
380 			BPoint offsetPoint;
381 			offsetPoint.x = where.x - fIconRect.left;
382 			offsetPoint.y = where.y - fIconRect.top;
383 			if (IconCache::sIconCache->IconHitTest(offsetPoint, fIconModel,
384 					kNormalIcon, fIconRect.Size())) {
385 				// Can't drag the trash anywhere..
386 				fTrackingState = fModel->IsTrash()
387 					? open_only_track : icon_track;
388 
389 				// Check for possible double click
390 				if (abs((int32)(fClickPoint.x - where.x)) < kDragSlop
391 					&& abs((int32)(fClickPoint.y - where.y)) < kDragSlop) {
392 					int32 clickCount;
393 					Window()->CurrentMessage()->FindInt32("clicks",
394 						&clickCount);
395 
396 					// This checks the* previous* click point
397 					if (clickCount == 2) {
398 						offsetPoint.x = fClickPoint.x - fIconRect.left;
399 						offsetPoint.y = fClickPoint.y - fIconRect.top;
400 						fDoubleClick
401 							= IconCache::sIconCache->IconHitTest(offsetPoint,
402 							fIconModel, kNormalIcon, fIconRect.Size());
403 					}
404 				}
405 			}
406 		}
407 	}
408 
409 	fClickPoint = where;
410 	fMouseDown = true;
411 	SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
412 }
413 
414 
415 void
416 HeaderView::MouseMoved(BPoint where, uint32, const BMessage* dragMessage)
417 {
418 	if (dragMessage != NULL && dragMessage->ReturnAddress() != BMessenger(this)
419 		&& dragMessage->what == B_SIMPLE_DATA
420 		&& BPoseView::CanHandleDragSelection(fModel, dragMessage,
421 			(modifiers() & B_CONTROL_KEY) != 0)) {
422 		// highlight drag target
423 		bool overTarget = fIconRect.Contains(where);
424 		SetDrawingMode(B_OP_OVER);
425 		if (overTarget != fIsDropTarget) {
426 			IconCache::sIconCache->Draw(fIconModel, this, fIconRect.LeftTop(),
427 				overTarget ? kSelectedIcon : kNormalIcon, fIconRect.Size(), true);
428 			fIsDropTarget = overTarget;
429 		}
430 	}
431 
432 	switch (fTrackingState) {
433 		case icon_track:
434 			if (fMouseDown && !fDragging
435 				&& (abs((int32)(where.x - fClickPoint.x)) > kDragSlop
436 					|| abs((int32)(where.y - fClickPoint.y)) > kDragSlop)) {
437 				// Find the required height
438 				BFont font;
439 				GetFont(&font);
440 
441 				float height = CurrentFontHeight()
442 					+ fIconRect.Height() + 8;
443 				BRect rect(0, 0, min_c(fIconRect.Width()
444 						+ font.StringWidth(fModel->Name()) + 4,
445 					fIconRect.Width() * 3), height);
446 				BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true);
447 				dragBitmap->Lock();
448 				BView* view = new BView(dragBitmap->Bounds(), "",
449 					B_FOLLOW_NONE, 0);
450 				dragBitmap->AddChild(view);
451 				view->SetOrigin(0, 0);
452 				BRect clipRect(view->Bounds());
453 				BRegion newClip;
454 				newClip.Set(clipRect);
455 				view->ConstrainClippingRegion(&newClip);
456 
457 				// Transparent draw magic
458 				view->SetHighColor(0, 0, 0, 0);
459 				view->FillRect(view->Bounds());
460 				view->SetDrawingMode(B_OP_ALPHA);
461 				rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
462 				textColor.alpha = 128;
463 					// set transparency by value
464 				view->SetHighColor(textColor);
465 				view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
466 
467 				// Draw the icon
468 				float hIconOffset = (rect.Width() - fIconRect.Width()) / 2;
469 				IconCache::sIconCache->Draw(fIconModel, view,
470 					BPoint(hIconOffset, 0), kNormalIcon, fIconRect.Size(), true);
471 
472 				// See if we need to truncate the string
473 				BString nameString(fModel->Name());
474 				if (view->StringWidth(fModel->Name()) > rect.Width()) {
475 					view->TruncateString(&nameString, B_TRUNCATE_END,
476 						rect.Width() - 5);
477 				}
478 
479 				// Draw the label
480 				font_height fontHeight;
481 				font.GetHeight(&fontHeight);
482 				float leftText = (view->StringWidth(nameString.String())
483 					- fIconRect.Width()) / 2;
484 				view->MovePenTo(BPoint(hIconOffset - leftText + 2,
485 					fIconRect.Height() + (fontHeight.ascent + 2)));
486 				view->DrawString(nameString.String());
487 
488 				view->Sync();
489 				dragBitmap->Unlock();
490 
491 				BMessage dragMessage(B_REFS_RECEIVED);
492 				dragMessage.AddPoint("click_pt", fClickPoint);
493 				BPoint tmpLoc;
494 				uint32 button;
495 				GetMouse(&tmpLoc, &button);
496 				if (button)
497 					dragMessage.AddInt32("buttons", (int32)button);
498 
499 				dragMessage.AddInt32("be:actions",
500 					(modifiers() & B_OPTION_KEY) != 0
501 						? B_COPY_TARGET : B_MOVE_TARGET);
502 				dragMessage.AddRef("refs", fModel->EntryRef());
503 				DragMessage(&dragMessage, dragBitmap, B_OP_ALPHA,
504 					BPoint((fClickPoint.x - fIconRect.left)
505 					+ hIconOffset, fClickPoint.y - fIconRect.top), this);
506 				fDragging = true;
507 			}
508 			break;
509 
510 		case open_only_track :
511 			// Special type of entry that can't be renamed or drag and dropped
512 			// It can only be opened by double clicking on the icon
513 			break;
514 
515 		case no_track:
516 			// No mouse tracking, do nothing
517 			break;
518 	}
519 }
520 
521 
522 void
523 HeaderView::MouseUp(BPoint where)
524 {
525 	if ((fTrackingState == icon_track
526 			|| fTrackingState == open_only_track)
527 		&& fIconRect.Contains(where)) {
528 		// If it was a double click, then tell Tracker to open the item
529 		// The CurrentMessage() here does* not* have a "clicks" field,
530 		// which is why we are tracking the clicks with this temp var
531 		if (fDoubleClick) {
532 			// Double click, launch.
533 			BMessage message(B_REFS_RECEIVED);
534 			message.AddRef("refs", fModel->EntryRef());
535 
536 			// add a messenger to the launch message that will be used to
537 			// dispatch scripting calls from apps to the PoseView
538 			message.AddMessenger("TrackerViewToken", BMessenger(this));
539 			be_app->PostMessage(&message);
540 			fDoubleClick = false;
541 		}
542 	}
543 
544 	// End mouse tracking
545 	fMouseDown = false;
546 	fDragging = false;
547 	fTrackingState = no_track;
548 }
549 
550 
551 void
552 HeaderView::MessageReceived(BMessage* message)
553 {
554 	if (message->WasDropped()
555 		&& message->what == B_SIMPLE_DATA
556 		&& message->ReturnAddress() != BMessenger(this)
557 		&& fIconRect.Contains(ConvertFromScreen(message->DropPoint()))
558 		&& BPoseView::CanHandleDragSelection(fModel, message,
559 			(modifiers() & B_CONTROL_KEY) != 0)) {
560 		BPoseView::HandleDropCommon(message, fModel, 0, this,
561 			message->DropPoint());
562 		Invalidate(fIconRect);
563 		return;
564 	}
565 
566 	BView::MessageReceived(message);
567 }
568 
569 
570 
571 status_t
572 HeaderView::BuildContextMenu(BMenu* parent)
573 {
574 	if (parent == NULL)
575 		return B_BAD_VALUE;
576 
577 	// Add navigation menu if this is not a symlink
578 	// Symlink's to directories are OK however!
579 	BEntry entry(fModel->EntryRef());
580 	entry_ref ref;
581 	entry.GetRef(&ref);
582 	Model model(&entry);
583 	bool navigate = false;
584 	if (model.InitCheck() == B_OK) {
585 		if (model.IsSymLink()) {
586 			// Check if it's to a directory
587 			if (entry.SetTo(model.EntryRef(), true) == B_OK) {
588 				navigate = entry.IsDirectory();
589 				entry.GetRef(&ref);
590 			}
591 		} else if (model.IsDirectory() || model.IsVolume())
592 			navigate = true;
593 	}
594 	ModelMenuItem* navigationItem = NULL;
595 	if (navigate) {
596 		navigationItem = new ModelMenuItem(new Model(model),
597 			new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, Window()));
598 
599 		// setup a navigation menu item which will dynamically load items
600 		// as menu items are traversed
601 		BNavMenu* navMenu = dynamic_cast<BNavMenu*>(navigationItem->Submenu());
602 		if (navMenu != NULL)
603 			navMenu->SetNavDir(&ref);
604 
605 		navigationItem->SetLabel(model.Name());
606 		navigationItem->SetEntry(&entry);
607 
608 		parent->AddItem(navigationItem, 0);
609 		parent->AddItem(new BSeparatorItem(), 1);
610 
611 		BMessage* message = new BMessage(B_REFS_RECEIVED);
612 		message->AddRef("refs", &ref);
613 		navigationItem->SetMessage(message);
614 		navigationItem->SetTarget(be_app);
615 	}
616 
617 	parent->AddItem(new BMenuItem(B_TRANSLATE("Open"),
618 		new BMessage(kOpenSelection), 'O'));
619 
620 	if (!model.IsDesktop() && !model.IsRoot() && !model.IsTrash()
621 		&& !fModel->HasLocalizedName()) {
622 		parent->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
623 			new BMessage(kEditItem), 'E'));
624 		parent->AddSeparatorItem();
625 
626 		if (fModel->IsVolume()) {
627 			BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"),
628 				new BMessage(kUnmountVolume), 'U');
629 			parent->AddItem(item);
630 			// volume model, enable/disable the Unmount item
631 			BVolume boot;
632 			BVolumeRoster().GetBootVolume(&boot);
633 			BVolume volume;
634 			volume.SetTo(fModel->NodeRef()->device);
635 			if (volume == boot)
636 				item->SetEnabled(false);
637 		}
638 	}
639 
640 	if (!model.IsRoot() && !model.IsVolume() && !model.IsTrash())
641 		parent->AddItem(new BMenuItem(B_TRANSLATE("Identify"),
642 			new BMessage(kIdentifyEntry)));
643 
644 	if (model.IsTrash())
645 		parent->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
646 			new BMessage(kEmptyTrash)));
647 
648 	BMenuItem* sizeItem = NULL;
649 	if (model.IsDirectory() && !model.IsVolume() && !model.IsRoot())  {
650 		parent->AddItem(sizeItem
651 				= new BMenuItem(B_TRANSLATE("Recalculate folder size"),
652 			new BMessage(kRecalculateSize)));
653 	}
654 
655 	if (model.IsSymLink()) {
656 		parent->AddItem(sizeItem
657 				= new BMenuItem(B_TRANSLATE("Set new link target"),
658 			new BMessage(kSetLinkTarget)));
659 	}
660 
661 	parent->AddItem(new BSeparatorItem());
662 	parent->AddItem(new BMenuItem(B_TRANSLATE("Permissions"),
663 		new BMessage(kPermissionsSelected), 'P'));
664 
665 	parent->SetFont(be_plain_font);
666 	parent->SetTargetForItems(this);
667 
668 	// Reset the nav menu to be_app
669 	if (navigate)
670 		navigationItem->SetTarget(be_app);
671 	if (sizeItem)
672 		sizeItem->SetTarget(Window());
673 
674 	return B_OK;
675 }
676 
677 
678 filter_result
679 HeaderView::TextViewFilter(BMessage* message, BHandler**,
680 	BMessageFilter* filter)
681 {
682 	uchar key;
683 	HeaderView* attribView = static_cast<HeaderView*>(
684 		static_cast<BWindow*>(filter->Looper())->FindView("header"));
685 
686 	// Adjust the size of the text rect
687 	BRect nuRect(attribView->TextView()->TextRect());
688 	nuRect.right = attribView->TextView()->LineWidth() + 20;
689 	attribView->TextView()->SetTextRect(nuRect);
690 
691 	// Make sure the cursor is in view
692 	attribView->TextView()->ScrollToSelection();
693 	if (message->FindInt8("byte", (int8*)&key) != B_OK)
694 		return B_DISPATCH_MESSAGE;
695 
696 	if (key == B_RETURN || key == B_ESCAPE) {
697 		attribView->FinishEditingTitle(key == B_RETURN);
698 		return B_SKIP_MESSAGE;
699 	}
700 
701 	return B_DISPATCH_MESSAGE;
702 }
703 
704 
705 float
706 HeaderView::CurrentFontHeight()
707 {
708 	BFont font;
709 	GetFont(&font);
710 	font_height fontHeight;
711 	font.GetHeight(&fontHeight);
712 
713 	return fontHeight.ascent + fontHeight.descent + fontHeight.leading + 2;
714 }
715