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