xref: /haiku/src/kits/tracker/infowindow/HeaderView.cpp (revision 958b83c3ed45e0e599e7dc0bc7f5841d4d9c03e5)
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
337 				= new BPopUpMenu("FileContext", false, false);
338 			if (contextMenu) {
339 				BuildContextMenu(contextMenu);
340 				contextMenu->SetAsyncAutoDestruct(true);
341 				contextMenu->Go(ConvertToScreen(where), true, true,
342 					ConvertToScreen(fIconRect));
343 			}
344 		} else {
345 			// Check to see if the point is actually on part of the icon,
346 			// versus just in the container rect. The icons are always
347 			// the large version
348 			BPoint offsetPoint;
349 			offsetPoint.x = where.x - fIconRect.left;
350 			offsetPoint.y = where.y - fIconRect.top;
351 			if (IconCache::sIconCache->IconHitTest(offsetPoint, fIconModel,
352 					kNormalIcon, fIconRect.Size())) {
353 				// Can't drag the trash anywhere..
354 				fTrackingState = fModel->IsTrash()
355 					? open_only_track : icon_track;
356 
357 				// Check for possible double click
358 				if (abs((int32)(fClickPoint.x - where.x)) < kDragSlop
359 					&& abs((int32)(fClickPoint.y - where.y)) < kDragSlop) {
360 					int32 clickCount;
361 					Window()->CurrentMessage()->FindInt32("clicks",
362 						&clickCount);
363 
364 					// This checks the* previous* click point
365 					if (clickCount == 2) {
366 						offsetPoint.x = fClickPoint.x - fIconRect.left;
367 						offsetPoint.y = fClickPoint.y - fIconRect.top;
368 						fDoubleClick
369 							= IconCache::sIconCache->IconHitTest(offsetPoint,
370 							fIconModel, kNormalIcon, fIconRect.Size());
371 					}
372 				}
373 			}
374 		}
375 	}
376 
377 	fClickPoint = where;
378 	fMouseDown = true;
379 	SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
380 }
381 
382 
383 void
384 HeaderView::MouseMoved(BPoint where, uint32, const BMessage* dragMessage)
385 {
386 	if (dragMessage != NULL && dragMessage->ReturnAddress() != BMessenger(this)
387 		&& dragMessage->what == B_SIMPLE_DATA
388 		&& BPoseView::CanHandleDragSelection(fModel, dragMessage,
389 			(modifiers() & B_CONTROL_KEY) != 0)) {
390 		// highlight drag target
391 		bool overTarget = fIconRect.Contains(where);
392 		SetDrawingMode(B_OP_OVER);
393 		if (overTarget != fIsDropTarget) {
394 			IconCache::sIconCache->Draw(fIconModel, this, fIconRect.LeftTop(),
395 				overTarget ? kSelectedIcon : kNormalIcon, fIconRect.Size(), true);
396 			fIsDropTarget = overTarget;
397 		}
398 	}
399 
400 	switch (fTrackingState) {
401 		case icon_track:
402 			if (fMouseDown && !fDragging
403 				&& (abs((int32)(where.x - fClickPoint.x)) > kDragSlop
404 					|| abs((int32)(where.y - fClickPoint.y)) > kDragSlop)) {
405 				// Find the required height
406 				BFont font;
407 				GetFont(&font);
408 
409 				float height = CurrentFontHeight()
410 					+ fIconRect.Height() + 8;
411 				BRect rect(0, 0, std::min(fIconRect.Width()
412 						+ font.StringWidth(fModel->Name()) + 4,
413 					fIconRect.Width() * 3), height);
414 				BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true);
415 				dragBitmap->Lock();
416 				BView* view = new BView(dragBitmap->Bounds(), "",
417 					B_FOLLOW_NONE, 0);
418 				dragBitmap->AddChild(view);
419 				view->SetOrigin(0, 0);
420 				BRect clipRect(view->Bounds());
421 				BRegion newClip;
422 				newClip.Set(clipRect);
423 				view->ConstrainClippingRegion(&newClip);
424 
425 				// Transparent draw magic
426 				view->SetHighColor(0, 0, 0, 0);
427 				view->FillRect(view->Bounds());
428 				view->SetDrawingMode(B_OP_ALPHA);
429 				rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
430 				textColor.alpha = 128;
431 					// set transparency by value
432 				view->SetHighColor(textColor);
433 				view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
434 
435 				// Draw the icon
436 				float hIconOffset = (rect.Width() - fIconRect.Width()) / 2;
437 				IconCache::sIconCache->Draw(fIconModel, view,
438 					BPoint(hIconOffset, 0), kNormalIcon, fIconRect.Size(), true);
439 
440 				// See if we need to truncate the string
441 				BString nameString(fModel->Name());
442 				if (view->StringWidth(fModel->Name()) > rect.Width()) {
443 					view->TruncateString(&nameString, B_TRUNCATE_END,
444 						rect.Width() - 5);
445 				}
446 
447 				// Draw the label
448 				font_height fontHeight;
449 				font.GetHeight(&fontHeight);
450 				float leftText = (view->StringWidth(nameString.String())
451 					- fIconRect.Width()) / 2;
452 				view->MovePenTo(BPoint(hIconOffset - leftText + 2,
453 					fIconRect.Height() + (fontHeight.ascent + 2)));
454 				view->DrawString(nameString.String());
455 
456 				view->Sync();
457 				dragBitmap->Unlock();
458 
459 				BMessage dragMessage(B_REFS_RECEIVED);
460 				dragMessage.AddPoint("click_pt", fClickPoint);
461 				BPoint tmpLoc;
462 				uint32 button;
463 				GetMouse(&tmpLoc, &button);
464 				if (button)
465 					dragMessage.AddInt32("buttons", (int32)button);
466 
467 				dragMessage.AddInt32("be:actions",
468 					(modifiers() & B_OPTION_KEY) != 0
469 						? B_COPY_TARGET : B_MOVE_TARGET);
470 				dragMessage.AddRef("refs", fModel->EntryRef());
471 				DragMessage(&dragMessage, dragBitmap, B_OP_ALPHA,
472 					BPoint((fClickPoint.x - fIconRect.left)
473 					+ hIconOffset, fClickPoint.y - fIconRect.top), this);
474 				fDragging = true;
475 			}
476 			break;
477 
478 		case open_only_track :
479 			// Special type of entry that can't be renamed or drag and dropped
480 			// It can only be opened by double clicking on the icon
481 			break;
482 
483 		case no_track:
484 			// No mouse tracking, do nothing
485 			break;
486 	}
487 }
488 
489 
490 void
491 HeaderView::MouseUp(BPoint where)
492 {
493 	if ((fTrackingState == icon_track
494 			|| fTrackingState == open_only_track)
495 		&& fIconRect.Contains(where)) {
496 		// If it was a double click, then tell Tracker to open the item
497 		// The CurrentMessage() here does* not* have a "clicks" field,
498 		// which is why we are tracking the clicks with this temp var
499 		if (fDoubleClick) {
500 			// Double click, launch.
501 			BMessage message(B_REFS_RECEIVED);
502 			message.AddRef("refs", fModel->EntryRef());
503 
504 			// add a messenger to the launch message that will be used to
505 			// dispatch scripting calls from apps to the PoseView
506 			message.AddMessenger("TrackerViewToken", BMessenger(this));
507 			be_app->PostMessage(&message);
508 			fDoubleClick = false;
509 		}
510 	}
511 
512 	// End mouse tracking
513 	fMouseDown = false;
514 	fDragging = false;
515 	fTrackingState = no_track;
516 }
517 
518 
519 void
520 HeaderView::MessageReceived(BMessage* message)
521 {
522 	if (message->WasDropped()
523 		&& message->what == B_SIMPLE_DATA
524 		&& message->ReturnAddress() != BMessenger(this)
525 		&& fIconRect.Contains(ConvertFromScreen(message->DropPoint()))
526 		&& BPoseView::CanHandleDragSelection(fModel, message,
527 			(modifiers() & B_CONTROL_KEY) != 0)) {
528 		BPoseView::HandleDropCommon(message, fModel, 0, this,
529 			message->DropPoint());
530 		Invalidate(fIconRect);
531 		return;
532 	}
533 
534 	BView::MessageReceived(message);
535 }
536 
537 
538 
539 status_t
540 HeaderView::BuildContextMenu(BMenu* parent)
541 {
542 	if (parent == NULL)
543 		return B_BAD_VALUE;
544 
545 	// Add navigation menu if this is not a symlink
546 	// Symlink's to directories are OK however!
547 	BEntry entry(fModel->EntryRef());
548 	entry_ref ref;
549 	entry.GetRef(&ref);
550 	Model model(&entry);
551 	bool navigate = false;
552 	if (model.InitCheck() == B_OK) {
553 		if (model.IsSymLink()) {
554 			// Check if it's to a directory
555 			if (entry.SetTo(model.EntryRef(), true) == B_OK) {
556 				navigate = entry.IsDirectory();
557 				entry.GetRef(&ref);
558 			}
559 		} else if (model.IsDirectory() || model.IsVolume())
560 			navigate = true;
561 	}
562 	ModelMenuItem* navigationItem = NULL;
563 	if (navigate) {
564 		navigationItem = new ModelMenuItem(new Model(model),
565 			new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, Window()));
566 
567 		// setup a navigation menu item which will dynamically load items
568 		// as menu items are traversed
569 		BNavMenu* navMenu = dynamic_cast<BNavMenu*>(navigationItem->Submenu());
570 		if (navMenu != NULL)
571 			navMenu->SetNavDir(&ref);
572 
573 		navigationItem->SetLabel(model.Name());
574 		navigationItem->SetEntry(&entry);
575 
576 		parent->AddItem(navigationItem, 0);
577 		parent->AddItem(new BSeparatorItem(), 1);
578 
579 		BMessage* message = new BMessage(B_REFS_RECEIVED);
580 		message->AddRef("refs", &ref);
581 		navigationItem->SetMessage(message);
582 		navigationItem->SetTarget(be_app);
583 	}
584 
585 	parent->AddItem(new BMenuItem(B_TRANSLATE("Open"),
586 		new BMessage(kOpenSelection), 'O'));
587 
588 	if (!model.IsDesktop() && !model.IsRoot() && !model.IsTrash()) {
589 		parent->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
590 			new BMessage(kEditItem), 'E'));
591 		parent->AddSeparatorItem();
592 
593 		if (fModel->IsVolume()) {
594 			BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"),
595 				new BMessage(kUnmountVolume), 'U');
596 			parent->AddItem(item);
597 			// volume model, enable/disable the Unmount item
598 			BVolume boot;
599 			BVolumeRoster().GetBootVolume(&boot);
600 			BVolume volume;
601 			volume.SetTo(fModel->NodeRef()->device);
602 			if (volume == boot)
603 				item->SetEnabled(false);
604 		}
605 	}
606 
607 	if (!model.IsRoot() && !model.IsVolume() && !model.IsTrash())
608 		parent->AddItem(new BMenuItem(B_TRANSLATE("Identify"),
609 			new BMessage(kIdentifyEntry)));
610 
611 	if (model.IsTrash())
612 		parent->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
613 			new BMessage(kEmptyTrash)));
614 
615 	BMenuItem* sizeItem = NULL;
616 	if (model.IsDirectory() && !model.IsVolume() && !model.IsRoot())  {
617 		parent->AddItem(sizeItem
618 				= new BMenuItem(B_TRANSLATE("Recalculate folder size"),
619 			new BMessage(kRecalculateSize)));
620 	}
621 
622 	if (model.IsSymLink()) {
623 		parent->AddItem(sizeItem
624 				= new BMenuItem(B_TRANSLATE("Set new link target"),
625 			new BMessage(kSetLinkTarget)));
626 	}
627 
628 	parent->AddItem(new BSeparatorItem());
629 	parent->AddItem(new BMenuItem(B_TRANSLATE("Permissions"),
630 		new BMessage(kPermissionsSelected), 'P'));
631 
632 	parent->SetFont(be_plain_font);
633 	parent->SetTargetForItems(this);
634 
635 	// Reset the nav menu to be_app
636 	if (navigate)
637 		navigationItem->SetTarget(be_app);
638 	if (sizeItem)
639 		sizeItem->SetTarget(Window());
640 
641 	return B_OK;
642 }
643 
644 
645 filter_result
646 HeaderView::TextViewFilter(BMessage* message, BHandler**,
647 	BMessageFilter* filter)
648 {
649 	uchar key;
650 	HeaderView* attribView = static_cast<HeaderView*>(
651 		static_cast<BWindow*>(filter->Looper())->FindView("header"));
652 
653 	// Adjust the size of the text rect
654 	BRect nuRect(attribView->TextView()->TextRect());
655 	nuRect.right = attribView->TextView()->LineWidth() + 20;
656 	attribView->TextView()->SetTextRect(nuRect);
657 
658 	// Make sure the cursor is in view
659 	attribView->TextView()->ScrollToSelection();
660 	if (message->FindInt8("byte", (int8*)&key) != B_OK)
661 		return B_DISPATCH_MESSAGE;
662 
663 	if (key == B_RETURN || key == B_ESCAPE) {
664 		attribView->FinishEditingTitle(key == B_RETURN);
665 		return B_SKIP_MESSAGE;
666 	}
667 
668 	return B_DISPATCH_MESSAGE;
669 }
670 
671 
672 float
673 HeaderView::CurrentFontHeight()
674 {
675 	BFont font;
676 	GetFont(&font);
677 	font_height fontHeight;
678 	font.GetHeight(&fontHeight);
679 
680 	return fontHeight.ascent + fontHeight.descent + fontHeight.leading + 2;
681 }
682