/*
 * Copyright 2001-2015, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Frans van Nispen (xlr8@tref.nl)
 *		Marc Flerackers (mflerackers@androme.be)
 */
 
 
#include "TextInput.h"
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include <ControlLook.h>
#include <InterfaceDefs.h>
#include <LayoutUtils.h>
#include <Message.h>
#include <String.h>
#include <TextControl.h>
#include <TextView.h>
#include <Window.h>
 
 
namespace BPrivate {
 
 
_BTextInput_::_BTextInput_(BRect frame, BRect textRect, uint32 resizeMask,
	uint32 flags)
	:
	BTextView(frame, "_input_", textRect, resizeMask, flags),
	fPreviousText(NULL)
{
	MakeResizable(true);
}
 
 
_BTextInput_::_BTextInput_(BMessage* archive)
	:
	BTextView(archive),
	fPreviousText(NULL)
{
	MakeResizable(true);
}
 
 
_BTextInput_::~_BTextInput_()
{
	free(fPreviousText);
}
 
 
BArchivable*
_BTextInput_::Instantiate(BMessage* archive)
{
	if (validate_instantiation(archive, "_BTextInput_"))
		return new _BTextInput_(archive);
 
	return NULL;
}
 
 
status_t
_BTextInput_::Archive(BMessage* data, bool deep) const
{
	return BTextView::Archive(data, true);
}
 
 
void
_BTextInput_::MouseDown(BPoint where)
{
	if (!IsFocus()) {
		MakeFocus(true);
		return;
	}
 
	// only pass through to base class if we already have focus
	BTextView::MouseDown(where);
}
 
 
void
_BTextInput_::FrameResized(float width, float height)
{
	BTextView::FrameResized(width, height);
 
	AlignTextRect();
}
 
 
void
_BTextInput_::KeyDown(const char* bytes, int32 numBytes)
{
	switch (*bytes) {
		case B_ENTER:
		{
			if (!TextControl()->IsEnabled())
				break;
 
			if (fPreviousText == NULL || strcmp(Text(), fPreviousText) != 0) {
				TextControl()->Invoke();
				free(fPreviousText);
				fPreviousText = strdup(Text());
			}
 
			SelectAll();
			break;
		}
 
		case B_TAB:
			BView::KeyDown(bytes, numBytes);
			break;
 
		default:
			BTextView::KeyDown(bytes, numBytes);
			break;
	}
}
 
 
void
_BTextInput_::MakeFocus(bool state)
{
	if (state == IsFocus())
		return;
 
	BTextView::MakeFocus(state);
 
	if (state) {
		SetInitialText();
		SelectAll();
	} else {
		if (strcmp(Text(), fPreviousText) != 0)
			TextControl()->Invoke();
 
		free(fPreviousText);
		fPreviousText = NULL;
	}
 
	if (Window() != NULL) {
		// Invalidate parent to draw or remove the focus mark
		if (BTextControl* parent = dynamic_cast<BTextControl*>(Parent())) {
			BRect frame = Frame();
			frame.InsetBy(-1.0, -1.0);
			parent->Invalidate(frame);
		}
	}
}
 
 
BSize
_BTextInput_::MinSize()
{
	BSize min;
	min.height = ceilf(LineHeight(0) + 2.0);
	// we always add at least one pixel vertical inset top/bottom for
	// the text rect.
	min.width = min.height * 3;
	return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
}
 
 
void
_BTextInput_::AlignTextRect()
{
	// the label font could require the control to be higher than
	// necessary for the text view, we compensate this by layouting
	// the text rect to be in the middle, normally this means there
	// is one pixel spacing on each side
	BRect textRect(Bounds());
	float vInset = max_c(1,
			floorf((textRect.Height() - LineHeight(0)) / 2.0));
	float hInset = 2;
	float textFontWidth = TextRect().right;
 
	switch (Alignment()) {
		case B_ALIGN_LEFT:
			hInset = be_control_look->DefaultLabelSpacing();
			break;
 
		case B_ALIGN_RIGHT:
			hInset  = textRect.right - textFontWidth;
			hInset -= be_control_look->DefaultLabelSpacing();
			break;
 
		case B_ALIGN_CENTER:
			hInset = (textRect.right - textFontWidth) / 2.0;
			break;
 
		default:
			break;
	}
 
	textRect.InsetBy(hInset, vInset);
	SetTextRect(textRect);
}
 
 
void
_BTextInput_::SetInitialText()
{
	free(fPreviousText);
	fPreviousText = NULL;
 
	if (Text() != NULL)
		fPreviousText = strdup(Text());
}
 
 
void
_BTextInput_::Paste(BClipboard* clipboard)
{
	BTextView::Paste(clipboard);
	Invalidate();
}
 
 
void
_BTextInput_::InsertText(const char* inText, int32 inLength,
	int32 inOffset, const text_run_array* inRuns)
{
	// Filter all line breaks, note that inText is not terminated.
	if (inLength == 1) {
		if (*inText == '\n' || *inText == '\r')
			BTextView::InsertText(" ", 1, inOffset, inRuns);
		else
			BTextView::InsertText(inText, 1, inOffset, inRuns);
	} else {
		BString filteredText(inText, inLength);
		filteredText.ReplaceAll('\n', ' ');
		filteredText.ReplaceAll('\r', ' ');
		BTextView::InsertText(filteredText.String(), inLength, inOffset,
			inRuns);
	}
 
	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
		B_CONTROL_MODIFIED);
}
 
 
void
_BTextInput_::DeleteText(int32 fromOffset, int32 toOffset)
{
	BTextView::DeleteText(fromOffset, toOffset);
 
	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
		B_CONTROL_MODIFIED);
}
 
 
BTextControl*
_BTextInput_::TextControl()
{
	BTextControl* textControl = NULL;
	if (Parent() != NULL)
		textControl = dynamic_cast<BTextControl*>(Parent());
 
	if (textControl == NULL)
		debugger("_BTextInput_ should have a BTextControl as parent");
 
	return textControl;
}
 
 
}	// namespace BPrivate
 

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fBool.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fBool.