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