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