xref: /haiku/src/apps/deskcalc/ExpressionTextView.cpp (revision cfc3fa87da824bdf593eb8b817a83b6376e77935)
1 /*
2  * Copyright 2006 Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 
9 #include "ExpressionTextView.h"
10 
11 #include <new>
12 #include <stdio.h>
13 
14 #include <Beep.h>
15 #include <Window.h>
16 
17 #include "CalcView.h"
18 
19 using std::nothrow;
20 
21 static const int32 kMaxPreviousExpressions = 20;
22 
23 
24 ExpressionTextView::ExpressionTextView(BRect frame, CalcView* calcView)
25 	: InputTextView(frame, "expression text view",
26 					(frame.OffsetToCopy(B_ORIGIN)).InsetByCopy(2, 2),
27 					B_FOLLOW_NONE, B_WILL_DRAW),
28 	  fCalcView(calcView),
29 	  fKeypadLabels(""),
30 	  fPreviousExpressions(20),
31 	  fHistoryPos(0),
32 	  fCurrentExpression("")
33 {
34 	SetStylable(false);
35 	SetDoesUndo(true);
36 	SetColorSpace(B_RGB32);
37 	SetFontAndColor(be_bold_font, B_FONT_ALL);
38 }
39 
40 
41 ExpressionTextView::~ExpressionTextView()
42 {
43 	int32 count = fPreviousExpressions.CountItems();
44 	for (int32 i = 0; i < count; i++)
45 		delete (BString*)fPreviousExpressions.ItemAtFast(i);
46 }
47 
48 
49 void
50 ExpressionTextView::MakeFocus(bool focused)
51 {
52 	if (focused == IsFocus()) {
53 		// stop endless loop when CalcView calls us again
54 		return;
55 	}
56 
57 	// NOTE: order of lines important!
58 	InputTextView::MakeFocus(focused);
59 	fCalcView->MakeFocus(focused);
60 }
61 
62 
63 void
64 ExpressionTextView::KeyDown(const char* bytes, int32 numBytes)
65 {
66 	// handle expression history
67 	if (bytes[0] == B_UP_ARROW) {
68 		PreviousExpression();
69 		return;
70 	}
71 	if (bytes[0] == B_DOWN_ARROW) {
72 		NextExpression();
73 		return;
74 	}
75 	BString current = Text();
76 
77 	// handle in InputTextView, except B_TAB
78 	if (bytes[0] != B_TAB)
79 		InputTextView::KeyDown(bytes, numBytes);
80 
81 	// pass on to CalcView if this was a label on a key
82 	if (fKeypadLabels.FindFirst(bytes[0]) >= 0)
83 		fCalcView->FlashKey(bytes, numBytes);
84 
85 	// as soon as something is typed, we are at the
86 	// end of the expression history
87 	if (current != Text())
88 		fHistoryPos = fPreviousExpressions.CountItems();
89 }
90 
91 
92 void
93 ExpressionTextView::MouseDown(BPoint where)
94 {
95 	uint32 buttons;
96 	Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
97 	if (buttons & B_PRIMARY_MOUSE_BUTTON) {
98 		InputTextView::MouseDown(where);
99 		return;
100 	}
101 	where = ConvertToParent(where);
102 	fCalcView->MouseDown(where);
103 }
104 
105 
106 // #pragma mark -
107 
108 
109 void
110 ExpressionTextView::RevertChanges()
111 {
112 	Clear();
113 }
114 
115 
116 void
117 ExpressionTextView::ApplyChanges()
118 {
119 	AddExpressionToHistory(Text());
120 	fCalcView->Evaluate();
121 }
122 
123 
124 // #pragma mark -
125 
126 
127 void
128 ExpressionTextView::AddKeypadLabel(const char* label)
129 {
130 	fKeypadLabels << label;
131 }
132 
133 
134 void
135 ExpressionTextView::SetExpression(const char* expression)
136 {
137 	SetText(expression);
138 	int32 lastPos = strlen(expression);
139 	Select(lastPos, lastPos);
140 }
141 
142 
143 void
144 ExpressionTextView::BackSpace()
145 {
146 	const char bytes[1] = { B_BACKSPACE };
147 	KeyDown(bytes, 1);
148 }
149 
150 
151 void
152 ExpressionTextView::Clear()
153 {
154 	SetText("");
155 }
156 
157 
158 // #pragma mark -
159 
160 
161 void
162 ExpressionTextView::AddExpressionToHistory(const char* expression)
163 {
164 	// clean out old expressions that are the same as
165 	// the one to be added
166 	int32 count = fPreviousExpressions.CountItems();
167 	for (int32 i = 0; i < count; i++) {
168 		BString* item = (BString*)fPreviousExpressions.ItemAt(i);
169 		if (*item == expression && fPreviousExpressions.RemoveItem(i)) {
170 			delete item;
171 			i--;
172 			count--;
173 		}
174 	}
175 
176 	BString* item = new (nothrow) BString(expression);
177 	if (!item)
178 		return;
179 	if (!fPreviousExpressions.AddItem(item)) {
180 		delete item;
181 		return;
182 	}
183 	while (fPreviousExpressions.CountItems() > kMaxPreviousExpressions)
184 		delete (BString*)fPreviousExpressions.RemoveItem(0L);
185 
186 	fHistoryPos = fPreviousExpressions.CountItems();
187 }
188 
189 
190 void
191 ExpressionTextView::PreviousExpression()
192 {
193 	int32 count = fPreviousExpressions.CountItems();
194 	if (fHistoryPos == count) {
195 		// save current expression
196 		fCurrentExpression = Text();
197 	}
198 
199 	fHistoryPos--;
200 	if (fHistoryPos < 0) {
201 		fHistoryPos = 0;
202 		return;
203 	}
204 
205 	BString* item = (BString*)fPreviousExpressions.ItemAt(fHistoryPos);
206 	if (item)
207 		SetExpression(item->String());
208 }
209 
210 
211 void
212 ExpressionTextView::NextExpression()
213 {
214 	int32 count = fPreviousExpressions.CountItems();
215 
216 	fHistoryPos++;
217 	if (fHistoryPos == count) {
218 		SetExpression(fCurrentExpression.String());
219 		return;
220 	}
221 
222 	if (fHistoryPos > count) {
223 		fHistoryPos = count;
224 		return;
225 	}
226 
227 	BString* item = (BString*)fPreviousExpressions.ItemAt(fHistoryPos);
228 	if (item)
229 		SetExpression(item->String());
230 }
231 
232 
233 // #pragma mark -
234 
235 
236 void
237 ExpressionTextView::LoadSettings(const BMessage* archive)
238 {
239 	const char* oldExpression;
240 	for (int32 i = 0;
241 		 archive->FindString("previous expression", i, &oldExpression) == B_OK;
242 		 i++) {
243 		 AddExpressionToHistory(oldExpression);
244 	}
245 }
246 
247 
248 status_t
249 ExpressionTextView::SaveSettings(BMessage* archive) const
250 {
251 	int32 count = fPreviousExpressions.CountItems();
252 	for (int32 i = 0; i < count; i++) {
253 		BString* item = (BString*)fPreviousExpressions.ItemAtFast(i);
254 		status_t ret = archive->AddString("previous expression", item->String());
255 		if (ret < B_OK)
256 			return ret;
257 	}
258 	return B_OK;
259 }
260 
261 
262