2 * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam).
3 * All rights reserved. Distributed under the terms of the MIT License.
6 * Oliver Tappe <beam@hirschkaefer.de>
9 #include "AutoCompleterDefaultImpl.h"
13 #include <ScrollView.h>
17 // #pragma mark - BDefaultPatternSelector
21 BDefaultPatternSelector::SelectPatternBounds(const BString
& text
,
22 int32 caretPos
, int32
* start
, int32
* length
)
24 if (!start
|| !length
)
27 *length
= text
.Length();
31 // #pragma mark - BDefaultCompletionStyle
34 BDefaultCompletionStyle::BDefaultCompletionStyle(
35 BAutoCompleter::EditView
* editView
,
36 BAutoCompleter::ChoiceModel
* choiceModel
,
37 BAutoCompleter::ChoiceView
* choiceView
,
38 BAutoCompleter::PatternSelector
* patternSelector
)
40 CompletionStyle(editView
, choiceModel
, choiceView
, patternSelector
),
44 fIgnoreEditViewStateChanges(false)
49 BDefaultCompletionStyle::~BDefaultCompletionStyle()
55 BDefaultCompletionStyle::Select(int32 index
)
57 if (!fChoiceView
|| !fChoiceModel
|| index
== fSelectedIndex
58 || index
< -1 || index
>= fChoiceModel
->CountChoices()) {
62 fSelectedIndex
= index
;
63 fChoiceView
->SelectChoiceAt(index
);
69 BDefaultCompletionStyle::SelectNext(bool wrap
)
71 if (!fChoiceModel
|| fChoiceModel
->CountChoices() == 0)
74 int32 newIndex
= fSelectedIndex
+ 1;
75 if (newIndex
>= fChoiceModel
->CountChoices()) {
79 newIndex
= fSelectedIndex
;
81 return Select(newIndex
);
86 BDefaultCompletionStyle::SelectPrevious(bool wrap
)
88 if (!fChoiceModel
|| fChoiceModel
->CountChoices() == 0)
91 int32 newIndex
= fSelectedIndex
- 1;
94 newIndex
= fChoiceModel
->CountChoices() - 1;
98 return Select(newIndex
);
103 BDefaultCompletionStyle::IsChoiceSelected() const
105 return fSelectedIndex
>= 0;
110 BDefaultCompletionStyle::SelectedChoiceIndex() const
112 return fSelectedIndex
;
117 BDefaultCompletionStyle::ApplyChoice(bool hideChoices
)
119 if (!fChoiceModel
|| !fChoiceView
|| !fEditView
|| fSelectedIndex
< 0)
122 BString
completedText(fFullEnteredText
);
123 completedText
.Remove(fPatternStartPos
, fPatternLength
);
124 const BString
& choiceStr
= fChoiceModel
->ChoiceAt(fSelectedIndex
)->Text();
125 completedText
.Insert(choiceStr
, fPatternStartPos
);
127 fIgnoreEditViewStateChanges
= true;
129 fFullEnteredText
= completedText
;
130 fPatternLength
= choiceStr
.Length();
131 fEditView
->SetEditViewState(completedText
,
132 fPatternStartPos
+ choiceStr
.Length());
135 fChoiceView
->HideChoices();
139 fIgnoreEditViewStateChanges
= false;
144 BDefaultCompletionStyle::CancelChoice()
146 if (!fChoiceView
|| !fEditView
)
148 if (fChoiceView
->ChoicesAreShown()) {
149 fIgnoreEditViewStateChanges
= true;
151 fEditView
->SetEditViewState(fFullEnteredText
,
152 fPatternStartPos
+ fPatternLength
);
153 fChoiceView
->HideChoices();
156 fIgnoreEditViewStateChanges
= false;
161 BDefaultCompletionStyle::EditViewStateChanged(bool updateChoices
)
163 if (fIgnoreEditViewStateChanges
|| !fChoiceModel
|| !fChoiceView
170 fEditView
->GetEditViewState(text
, &caretPos
);
171 if (fFullEnteredText
== text
)
174 fFullEnteredText
= text
;
179 fPatternSelector
->SelectPatternBounds(text
, caretPos
, &fPatternStartPos
,
181 BString
pattern(text
.String() + fPatternStartPos
, fPatternLength
);
182 fChoiceModel
->FetchChoicesFor(pattern
);
185 // show a single choice only if it doesn't match the pattern exactly:
186 if (fChoiceModel
->CountChoices() > 1 || (fChoiceModel
->CountChoices() == 1
187 && pattern
.ICompare(fChoiceModel
->ChoiceAt(0)->Text())) != 0) {
188 fChoiceView
->ShowChoices(this);
189 fChoiceView
->SelectChoiceAt(fSelectedIndex
);
191 fChoiceView
->HideChoices();
195 // #pragma mark - BDefaultChoiceView::ListView
198 static const int32 MSG_INVOKED
= 'invk';
201 BDefaultChoiceView::ListView::ListView(
202 BAutoCompleter::CompletionStyle
* completer
)
204 BListView(BRect(0, 0, 100, 100), "ChoiceViewList"),
205 fCompleter(completer
)
207 // we need to check if user clicks outside of window-bounds:
208 SetEventMask(B_POINTER_EVENTS
);
213 BDefaultChoiceView::ListView::AttachedToWindow()
216 SetInvocationMessage(new BMessage(MSG_INVOKED
));
217 BListView::AttachedToWindow();
222 BDefaultChoiceView::ListView::SelectionChanged()
224 fCompleter
->Select(CurrentSelection(0));
229 BDefaultChoiceView::ListView::MessageReceived(BMessage
* message
)
231 switch(message
->what
) {
233 fCompleter
->ApplyChoice();
236 BListView::MessageReceived(message
);
242 BDefaultChoiceView::ListView::MouseDown(BPoint point
)
244 if (!Window()->Frame().Contains(ConvertToScreen(point
)))
245 // click outside of window, so we close it:
248 BListView::MouseDown(point
);
252 // #pragma mark - BDefaultChoiceView::ListItem
255 BDefaultChoiceView::ListItem::ListItem(const BAutoCompleter::Choice
* choice
)
259 fPreText
= choice
->DisplayText();
260 if (choice
->MatchLen() > 0) {
261 fPreText
.MoveInto(fMatchText
, choice
->MatchPos(), choice
->MatchLen());
262 fPreText
.MoveInto(fPostText
, choice
->MatchPos(), fPreText
.Length());
268 BDefaultChoiceView::ListItem::DrawItem(BView
* owner
, BRect frame
,
272 rgb_color nonMatchTextColor
;
274 rgb_color matchColor
;
276 textColor
= ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR
);
277 backColor
= ui_color(B_LIST_SELECTED_BACKGROUND_COLOR
);
279 textColor
= ui_color(B_LIST_ITEM_TEXT_COLOR
);
280 backColor
= ui_color(B_LIST_BACKGROUND_COLOR
);
282 matchColor
= tint_color(backColor
, (B_NO_TINT
+ B_DARKEN_1_TINT
) / 2);
283 if (textColor
.red
+ textColor
.green
+ textColor
.blue
> 128 * 3)
284 nonMatchTextColor
= tint_color(textColor
, 1.2);
286 nonMatchTextColor
= tint_color(textColor
, 0.75);
288 font_height fontHeight
;
289 owner
->GetFont(&font
);
290 font
.GetHeight(&fontHeight
);
291 float xPos
= frame
.left
+ 1;
292 float yPos
= frame
.top
+ fontHeight
.ascent
;
294 if (fPreText
.Length()) {
295 w
= owner
->StringWidth(fPreText
.String());
296 owner
->SetLowColor(backColor
);
297 owner
->FillRect(BRect(xPos
, frame
.top
, xPos
+ w
- 1, frame
.bottom
),
299 owner
->SetHighColor(nonMatchTextColor
);
300 owner
->DrawString(fPreText
.String(), BPoint(xPos
, yPos
));
303 if (fMatchText
.Length()) {
304 w
= owner
->StringWidth(fMatchText
.String());
305 owner
->SetLowColor(matchColor
);
306 owner
->FillRect(BRect(xPos
, frame
.top
, xPos
+ w
- 1, frame
.bottom
),
308 owner
->SetHighColor(textColor
);
309 owner
->DrawString(fMatchText
.String(), BPoint(xPos
, yPos
));
312 if (fPostText
.Length()) {
313 w
= owner
->StringWidth(fPostText
.String());
314 owner
->SetLowColor(backColor
);
315 owner
->FillRect(BRect(xPos
, frame
.top
, xPos
+ w
- 1, frame
.bottom
),
317 owner
->SetHighColor(nonMatchTextColor
);
318 owner
->DrawString(fPostText
.String(), BPoint(xPos
, yPos
));
323 // #pragma mark - BDefaultChoiceView
326 BDefaultChoiceView::BDefaultChoiceView()
330 fMaxVisibleChoices(8)
336 BDefaultChoiceView::~BDefaultChoiceView()
343 BDefaultChoiceView::SelectChoiceAt(int32 index
)
345 if (fListView
&& fListView
->LockLooper()) {
347 fListView
->DeselectAll();
349 fListView
->Select(index
);
350 fListView
->ScrollToSelection();
352 fListView
->UnlockLooper();
358 BDefaultChoiceView::ShowChoices(BAutoCompleter::CompletionStyle
* completer
)
365 BAutoCompleter::ChoiceModel
* choiceModel
= completer
->GetChoiceModel();
366 BAutoCompleter::EditView
* editView
= completer
->GetEditView();
368 if (!editView
|| !choiceModel
|| choiceModel
->CountChoices() == 0)
371 fListView
= new ListView(completer
);
372 int32 count
= choiceModel
->CountChoices();
373 for(int32 i
= 0; i
<count
; ++i
) {
375 new ListItem(choiceModel
->ChoiceAt(i
))
379 BScrollView
*scrollView
= NULL
;
380 if (count
> fMaxVisibleChoices
) {
381 scrollView
= new BScrollView("", fListView
, B_FOLLOW_NONE
, 0, false, true, B_NO_BORDER
);
384 fWindow
= new BWindow(BRect(0, 0, 100, 100), "", B_BORDERED_WINDOW_LOOK
,
385 B_NORMAL_WINDOW_FEEL
, B_NOT_MOVABLE
| B_WILL_ACCEPT_FIRST_CLICK
386 | B_AVOID_FOCUS
| B_ASYNCHRONOUS_CONTROLS
);
387 if (scrollView
!= NULL
)
388 fWindow
->AddChild(scrollView
);
390 fWindow
->AddChild(fListView
);
392 int32 visibleCount
= min_c(count
, fMaxVisibleChoices
);
393 float listHeight
= fListView
->ItemFrame(visibleCount
- 1).bottom
+ 1;
395 BRect pvRect
= editView
->GetAdjustmentFrame();
396 BRect listRect
= pvRect
;
397 listRect
.bottom
= listRect
.top
+ listHeight
- 1;
398 BRect screenRect
= BScreen().Frame();
399 if (listRect
.bottom
+ 1 + listHeight
<= screenRect
.bottom
)
400 listRect
.OffsetTo(pvRect
.left
, pvRect
.bottom
+ 1);
402 listRect
.OffsetTo(pvRect
.left
, pvRect
.top
- listHeight
);
404 if (scrollView
!= NULL
) {
405 // Moving here to cut off the scrollbar top
406 scrollView
->MoveTo(0, -1);
407 // Adding the 1 and 2 to cut-off the scroll-bar top, right and bottom
408 scrollView
->ResizeTo(listRect
.Width() + 1, listRect
.Height() + 2);
409 // Move here to compensate for the above
410 fListView
->MoveTo(0, 1);
411 fListView
->ResizeTo(listRect
.Width() - B_V_SCROLL_BAR_WIDTH
, listRect
.Height());
413 fListView
->MoveTo(0, 0);
414 fListView
->ResizeTo(listRect
.Width(), listRect
.Height());
416 fWindow
->MoveTo(listRect
.left
, listRect
.top
);
417 fWindow
->ResizeTo(listRect
.Width(), listRect
.Height());
423 BDefaultChoiceView::HideChoices()
425 if (fWindow
&& fWindow
->Lock()) {
434 BDefaultChoiceView::ChoicesAreShown()
436 return (fWindow
!= NULL
);
441 BDefaultChoiceView::CountVisibleChoices() const
444 return min_c(fMaxVisibleChoices
, fListView
->CountItems());
451 BDefaultChoiceView::SetMaxVisibleChoices(int32 choices
)
455 if (choices
== fMaxVisibleChoices
)
458 fMaxVisibleChoices
= choices
;
460 // TODO: Update live?
465 BDefaultChoiceView::MaxVisibleChoices() const
467 return fMaxVisibleChoices
;