* Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam).
* All rights reserved. Distributed under the terms of the MIT License.
*
* Authors:
* Oliver Tappe <beam@hirschkaefer.de>
*/
#include "AutoCompleterDefaultImpl.h"
#include <ListView.h>
#include <Screen.h>
#include <ScrollView.h>
#include <Window.h>
void
BDefaultPatternSelector::SelectPatternBounds(const BString& text,
int32 caretPos, int32* start, int32* length)
{
if (!start || !length)
return;
*start = 0;
*length = text.Length();
}
BDefaultCompletionStyle::BDefaultCompletionStyle(
BAutoCompleter::EditView* editView,
BAutoCompleter::ChoiceModel* choiceModel,
BAutoCompleter::ChoiceView* choiceView,
BAutoCompleter::PatternSelector* patternSelector)
:
CompletionStyle(editView, choiceModel, choiceView, patternSelector),
fSelectedIndex(-1),
fPatternStartPos(0),
fPatternLength(0),
fIgnoreEditViewStateChanges(false)
{
}
BDefaultCompletionStyle::~BDefaultCompletionStyle()
{
}
bool
BDefaultCompletionStyle::Select(int32 index)
{
if (!fChoiceView || !fChoiceModel || index == fSelectedIndex
|| index < -1 || index >= fChoiceModel->CountChoices()) {
return false;
}
fSelectedIndex = index;
fChoiceView->SelectChoiceAt(index);
return true;
}
bool
BDefaultCompletionStyle::SelectNext(bool wrap)
{
if (!fChoiceModel || fChoiceModel->CountChoices() == 0)
return false;
int32 newIndex = fSelectedIndex + 1;
if (newIndex >= fChoiceModel->CountChoices()) {
if (wrap)
newIndex = 0;
else
newIndex = fSelectedIndex;
}
return Select(newIndex);
}
bool
BDefaultCompletionStyle::SelectPrevious(bool wrap)
{
if (!fChoiceModel || fChoiceModel->CountChoices() == 0)
return false;
int32 newIndex = fSelectedIndex - 1;
if (newIndex < 0) {
if (wrap)
newIndex = fChoiceModel->CountChoices() - 1;
else
newIndex = 0;
}
return Select(newIndex);
}
bool
BDefaultCompletionStyle::IsChoiceSelected() const
{
return fSelectedIndex >= 0;
}
int32
BDefaultCompletionStyle::SelectedChoiceIndex() const
{
return fSelectedIndex;
}
void
BDefaultCompletionStyle::ApplyChoice(bool hideChoices)
{
if (!fChoiceModel || !fChoiceView || !fEditView || fSelectedIndex < 0)
return;
BString completedText(fFullEnteredText);
completedText.Remove(fPatternStartPos, fPatternLength);
const BString& choiceStr = fChoiceModel->ChoiceAt(fSelectedIndex)->Text();
completedText.Insert(choiceStr, fPatternStartPos);
fIgnoreEditViewStateChanges = true;
fFullEnteredText = completedText;
fPatternLength = choiceStr.Length();
fEditView->SetEditViewState(completedText,
fPatternStartPos + choiceStr.Length());
if (hideChoices) {
fChoiceView->HideChoices();
Select(-1);
}
fIgnoreEditViewStateChanges = false;
}
void
BDefaultCompletionStyle::CancelChoice()
{
if (!fChoiceView || !fEditView)
return;
if (fChoiceView->ChoicesAreShown()) {
fIgnoreEditViewStateChanges = true;
fEditView->SetEditViewState(fFullEnteredText,
fPatternStartPos + fPatternLength);
fChoiceView->HideChoices();
Select(-1);
fIgnoreEditViewStateChanges = false;
}
}
void
BDefaultCompletionStyle::EditViewStateChanged(bool updateChoices)
{
if (fIgnoreEditViewStateChanges || !fChoiceModel || !fChoiceView
|| !fEditView) {
return;
}
BString text;
int32 caretPos;
fEditView->GetEditViewState(text, &caretPos);
if (fFullEnteredText == text)
return;
fFullEnteredText = text;
if (!updateChoices)
return;
fPatternSelector->SelectPatternBounds(text, caretPos, &fPatternStartPos,
&fPatternLength);
BString pattern(text.String() + fPatternStartPos, fPatternLength);
fChoiceModel->FetchChoicesFor(pattern);
Select(-1);
if (fChoiceModel->CountChoices() > 1 || (fChoiceModel->CountChoices() == 1
&& pattern.ICompare(fChoiceModel->ChoiceAt(0)->Text())) != 0) {
fChoiceView->ShowChoices(this);
fChoiceView->SelectChoiceAt(fSelectedIndex);
} else
fChoiceView->HideChoices();
}
static const int32 MSG_INVOKED = 'invk';
BDefaultChoiceView::ListView::ListView(
BAutoCompleter::CompletionStyle* completer)
:
BListView(BRect(0, 0, 100, 100), "ChoiceViewList"),
fCompleter(completer)
{
SetEventMask(B_POINTER_EVENTS);
}
void
BDefaultChoiceView::ListView::AttachedToWindow()
{
SetTarget(this);
SetInvocationMessage(new BMessage(MSG_INVOKED));
BListView::AttachedToWindow();
}
void
BDefaultChoiceView::ListView::SelectionChanged()
{
fCompleter->Select(CurrentSelection(0));
}
void
BDefaultChoiceView::ListView::MessageReceived(BMessage* message)
{
switch(message->what) {
case MSG_INVOKED:
fCompleter->ApplyChoice();
break;
default:
BListView::MessageReceived(message);
}
}
void
BDefaultChoiceView::ListView::MouseDown(BPoint point)
{
if (!Window()->Frame().Contains(ConvertToScreen(point)))
Window()->Quit();
else
BListView::MouseDown(point);
}
BDefaultChoiceView::ListItem::ListItem(const BAutoCompleter::Choice* choice)
:
BListItem()
{
fPreText = choice->DisplayText();
if (choice->MatchLen() > 0) {
fPreText.MoveInto(fMatchText, choice->MatchPos(), choice->MatchLen());
fPreText.MoveInto(fPostText, choice->MatchPos(), fPreText.Length());
}
}
void
BDefaultChoiceView::ListItem::DrawItem(BView* owner, BRect frame,
bool complete)
{
rgb_color textColor;
rgb_color nonMatchTextColor;
rgb_color backColor;
rgb_color matchColor;
if (IsSelected()) {
textColor = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR);
backColor = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR);
} else {
textColor = ui_color(B_LIST_ITEM_TEXT_COLOR);
backColor = ui_color(B_LIST_BACKGROUND_COLOR);
}
matchColor = tint_color(backColor, (B_NO_TINT + B_DARKEN_1_TINT) / 2);
if (textColor.red + textColor.green + textColor.blue > 128 * 3)
nonMatchTextColor = tint_color(textColor, 1.2);
else
nonMatchTextColor = tint_color(textColor, 0.75);
BFont font;
font_height fontHeight;
owner->GetFont(&font);
font.GetHeight(&fontHeight);
float xPos = frame.left + 1;
float yPos = frame.top + fontHeight.ascent;
float w;
if (fPreText.Length()) {
w = owner->StringWidth(fPreText.String());
owner->SetLowColor(backColor);
owner->FillRect(BRect(xPos, frame.top, xPos + w - 1, frame.bottom),
B_SOLID_LOW);
owner->SetHighColor(nonMatchTextColor);
owner->DrawString(fPreText.String(), BPoint(xPos, yPos));
xPos += w;
}
if (fMatchText.Length()) {
w = owner->StringWidth(fMatchText.String());
owner->SetLowColor(matchColor);
owner->FillRect(BRect(xPos, frame.top, xPos + w - 1, frame.bottom),
B_SOLID_LOW);
owner->SetHighColor(textColor);
owner->DrawString(fMatchText.String(), BPoint(xPos, yPos));
xPos += w;
}
if (fPostText.Length()) {
w = owner->StringWidth(fPostText.String());
owner->SetLowColor(backColor);
owner->FillRect(BRect(xPos, frame.top, xPos + w - 1, frame.bottom),
B_SOLID_LOW);
owner->SetHighColor(nonMatchTextColor);
owner->DrawString(fPostText.String(), BPoint(xPos, yPos));
}
}
BDefaultChoiceView::BDefaultChoiceView()
:
fWindow(NULL),
fListView(NULL),
fMaxVisibleChoices(8)
{
}
BDefaultChoiceView::~BDefaultChoiceView()
{
HideChoices();
}
void
BDefaultChoiceView::SelectChoiceAt(int32 index)
{
if (fListView && fListView->LockLooper()) {
if (index < 0)
fListView->DeselectAll();
else {
fListView->Select(index);
fListView->ScrollToSelection();
}
fListView->UnlockLooper();
}
}
void
BDefaultChoiceView::ShowChoices(BAutoCompleter::CompletionStyle* completer)
{
if (!completer)
return;
HideChoices();
BAutoCompleter::ChoiceModel* choiceModel = completer->GetChoiceModel();
BAutoCompleter::EditView* editView = completer->GetEditView();
if (!editView || !choiceModel || choiceModel->CountChoices() == 0)
return;
fListView = new ListView(completer);
int32 count = choiceModel->CountChoices();
for(int32 i = 0; i<count; ++i) {
fListView->AddItem(
new ListItem(choiceModel->ChoiceAt(i))
);
}
BScrollView *scrollView = NULL;
if (count > fMaxVisibleChoices) {
scrollView = new BScrollView("", fListView, B_FOLLOW_NONE, 0, false, true, B_NO_BORDER);
}
fWindow = new BWindow(BRect(0, 0, 100, 100), "", B_BORDERED_WINDOW_LOOK,
B_NORMAL_WINDOW_FEEL, B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK
| B_AVOID_FOCUS | B_ASYNCHRONOUS_CONTROLS);
if (scrollView != NULL)
fWindow->AddChild(scrollView);
else
fWindow->AddChild(fListView);
int32 visibleCount = min_c(count, fMaxVisibleChoices);
float listHeight = fListView->ItemFrame(visibleCount - 1).bottom + 1;
BRect pvRect = editView->GetAdjustmentFrame();
BRect listRect = pvRect;
listRect.bottom = listRect.top + listHeight - 1;
BRect screenRect = BScreen().Frame();
if (listRect.bottom + 1 + listHeight <= screenRect.bottom)
listRect.OffsetTo(pvRect.left, pvRect.bottom + 1);
else
listRect.OffsetTo(pvRect.left, pvRect.top - listHeight);
if (scrollView != NULL) {
scrollView->MoveTo(0, -1);
scrollView->ResizeTo(listRect.Width() + 1, listRect.Height() + 2);
fListView->MoveTo(0, 1);
fListView->ResizeTo(listRect.Width() - B_V_SCROLL_BAR_WIDTH, listRect.Height());
} else {
fListView->MoveTo(0, 0);
fListView->ResizeTo(listRect.Width(), listRect.Height());
}
fWindow->MoveTo(listRect.left, listRect.top);
fWindow->ResizeTo(listRect.Width(), listRect.Height());
fWindow->Show();
}
void
BDefaultChoiceView::HideChoices()
{
if (fWindow && fWindow->Lock()) {
fWindow->Quit();
fWindow = NULL;
fListView = NULL;
}
}
bool
BDefaultChoiceView::ChoicesAreShown()
{
return (fWindow != NULL);
}
int32
BDefaultChoiceView::CountVisibleChoices() const
{
if (fListView)
return min_c(fMaxVisibleChoices, fListView->CountItems());
else
return 0;
}
void
BDefaultChoiceView::SetMaxVisibleChoices(int32 choices)
{
if (choices < 1)
choices = 1;
if (choices == fMaxVisibleChoices)
return;
fMaxVisibleChoices = choices;
}
int32
BDefaultChoiceView::MaxVisibleChoices() const
{
return fMaxVisibleChoices;
}