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