1/*
2 * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam).
3 * All rights reserved. Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Oliver Tappe <beam@hirschkaefer.de>
7 */
8
9#include "TextViewCompleter.h"
10
11#include <Looper.h>
12#include <TextControl.h>
13#include <stdio.h>
14
15#include "AutoCompleterDefaultImpl.h"
16
17
18// #pragma mark - TextViewWrapper
19
20
21TextViewCompleter::TextViewWrapper::TextViewWrapper(BTextView* textView)
22	:
23	fTextView(textView)
24{
25}
26
27
28void
29TextViewCompleter::TextViewWrapper::GetEditViewState(BString& text,
30	int32* caretPos)
31{
32	if (fTextView && fTextView->LockLooper()) {
33		text = fTextView->Text();
34		if (caretPos) {
35			int32 end;
36			fTextView->GetSelection(caretPos, &end);
37		}
38		fTextView->UnlockLooper();
39	}
40}
41
42
43void
44TextViewCompleter::TextViewWrapper::SetEditViewState(const BString& text,
45	int32 caretPos, int32 selectionLength)
46{
47	if (fTextView && fTextView->LockLooper()) {
48		fTextView->SetText(text.String(), text.Length());
49		fTextView->Select(caretPos, caretPos + selectionLength);
50		fTextView->ScrollToSelection();
51		fTextView->UnlockLooper();
52	}
53}
54
55
56BRect
57TextViewCompleter::TextViewWrapper::GetAdjustmentFrame()
58{
59	BRect frame = fTextView->Bounds();
60	frame = fTextView->ConvertToScreen(frame);
61	frame.InsetBy(0, -3);
62	return frame;
63}
64
65
66// #pragma mark -
67
68
69TextViewCompleter::TextViewCompleter(BTextView* textView, ChoiceModel* model,
70		PatternSelector* patternSelector)
71	:
72	BAutoCompleter(new TextViewWrapper(textView), model,
73		new BDefaultChoiceView(), patternSelector),
74	BMessageFilter(B_KEY_DOWN),
75	fTextView(textView),
76	fModificationsReported(false)
77{
78	fTextView->AddFilter(this);
79}
80
81
82TextViewCompleter::~TextViewCompleter()
83{
84	fTextView->RemoveFilter(this);
85}
86
87
88void
89TextViewCompleter::SetModificationsReported(bool reported)
90{
91	fModificationsReported = reported;
92}
93
94
95void
96TextViewCompleter::TextModified(bool updateChoices)
97{
98	EditViewStateChanged(updateChoices);
99}
100
101
102filter_result
103TextViewCompleter::Filter(BMessage* message, BHandler** target)
104{
105	const char* bytes;
106	int32 modifiers;
107	if ((!target || message->FindString("bytes", &bytes) != B_OK
108			|| message->FindInt32("modifiers", &modifiers) != B_OK)
109		|| (modifiers & (B_CONTROL_KEY | B_COMMAND_KEY | B_OPTION_KEY
110			| B_SHIFT_KEY)) != 0) {
111		return B_DISPATCH_MESSAGE;
112	}
113
114	switch (bytes[0]) {
115		case B_UP_ARROW:
116			SelectPrevious();
117			// Insert the current choice into the text view, so the user can
118			// continue typing. This will not trigger another evaluation, since
119			// we don't invoke EditViewStateChanged().
120			ApplyChoice(false);
121			return B_SKIP_MESSAGE;
122		case B_DOWN_ARROW:
123			SelectNext();
124			// See above.
125			ApplyChoice(false);
126			return B_SKIP_MESSAGE;
127		case B_PAGE_UP:
128		{
129			int32 index = SelectedChoiceIndex() - CountVisibleChoices();
130			index = max_c(index, 0);
131			Select(index);
132			ApplyChoice(false);
133			return B_SKIP_MESSAGE;
134		}
135		case B_PAGE_DOWN:
136		{
137			int32 index = SelectedChoiceIndex() + CountVisibleChoices();
138			index = min_c(index, CountChoices() - 1);
139			Select(index);
140			ApplyChoice(false);
141			return B_SKIP_MESSAGE;
142		}
143
144		case B_ESCAPE:
145			CancelChoice();
146			return B_DISPATCH_MESSAGE;
147		case B_RETURN:
148			if (IsChoiceSelected()) {
149				ApplyChoice();
150				EditViewStateChanged();
151			} else
152				CancelChoice();
153			return B_DISPATCH_MESSAGE;
154		case B_TAB: {
155			// make sure that the choices-view is closed when tabbing out:
156			CancelChoice();
157			return B_DISPATCH_MESSAGE;
158		}
159		default:
160			if (!fModificationsReported) {
161				// dispatch message to textview manually...
162				Looper()->DispatchMessage(message, *target);
163				// ...and propagate the new state to the auto-completer:
164				EditViewStateChanged();
165				return B_SKIP_MESSAGE;
166			}
167			return B_DISPATCH_MESSAGE;
168	}
169}
170