1 /* 2 * Copyright 2001-2015, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Frans van Nispen (xlr8@tref.nl) 7 * Marc Flerackers (mflerackers@androme.be) 8 */ 9 10 11 #include "TextInput.h" 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include <ControlLook.h> 18 #include <InterfaceDefs.h> 19 #include <LayoutUtils.h> 20 #include <Message.h> 21 #include <String.h> 22 #include <TextControl.h> 23 #include <TextView.h> 24 #include <Window.h> 25 26 27 namespace BPrivate { 28 29 30 _BTextInput_::_BTextInput_(BRect frame, BRect textRect, uint32 resizeMask, 31 uint32 flags) 32 : 33 BTextView(frame, "_input_", textRect, resizeMask, flags), 34 fPreviousText(NULL) 35 { 36 MakeResizable(true); 37 } 38 39 40 _BTextInput_::_BTextInput_(BMessage* archive) 41 : 42 BTextView(archive), 43 fPreviousText(NULL) 44 { 45 MakeResizable(true); 46 } 47 48 49 _BTextInput_::~_BTextInput_() 50 { 51 free(fPreviousText); 52 } 53 54 55 BArchivable* 56 _BTextInput_::Instantiate(BMessage* archive) 57 { 58 if (validate_instantiation(archive, "_BTextInput_")) 59 return new _BTextInput_(archive); 60 61 return NULL; 62 } 63 64 65 status_t 66 _BTextInput_::Archive(BMessage* data, bool deep) const 67 { 68 return BTextView::Archive(data, true); 69 } 70 71 72 void 73 _BTextInput_::MouseDown(BPoint where) 74 { 75 if (!IsFocus()) { 76 MakeFocus(true); 77 return; 78 } 79 80 // only pass through to base class if we already have focus 81 BTextView::MouseDown(where); 82 } 83 84 85 void 86 _BTextInput_::FrameResized(float width, float height) 87 { 88 BTextView::FrameResized(width, height); 89 90 AlignTextRect(); 91 } 92 93 94 void 95 _BTextInput_::KeyDown(const char* bytes, int32 numBytes) 96 { 97 switch (*bytes) { 98 case B_ENTER: 99 { 100 if (!TextControl()->IsEnabled()) 101 break; 102 103 if (fPreviousText == NULL || strcmp(Text(), fPreviousText) != 0) { 104 TextControl()->Invoke(); 105 free(fPreviousText); 106 fPreviousText = strdup(Text()); 107 } 108 109 SelectAll(); 110 break; 111 } 112 113 case B_TAB: 114 BView::KeyDown(bytes, numBytes); 115 break; 116 117 default: 118 BTextView::KeyDown(bytes, numBytes); 119 break; 120 } 121 } 122 123 124 void 125 _BTextInput_::MakeFocus(bool state) 126 { 127 if (state == IsFocus()) 128 return; 129 130 BTextView::MakeFocus(state); 131 132 if (state) { 133 SetInitialText(); 134 SelectAll(); 135 } else { 136 if (strcmp(Text(), fPreviousText) != 0) 137 TextControl()->Invoke(); 138 139 free(fPreviousText); 140 fPreviousText = NULL; 141 } 142 143 if (Window() != NULL) { 144 // Invalidate parent to draw or remove the focus mark 145 if (BTextControl* parent = dynamic_cast<BTextControl*>(Parent())) { 146 BRect frame = Frame(); 147 frame.InsetBy(-1.0, -1.0); 148 parent->Invalidate(frame); 149 } 150 } 151 } 152 153 154 BSize 155 _BTextInput_::MinSize() 156 { 157 BSize min; 158 min.height = ceilf(LineHeight(0) + 2.0); 159 // we always add at least one pixel vertical inset top/bottom for 160 // the text rect. 161 min.width = min.height * 3; 162 return BLayoutUtils::ComposeSize(ExplicitMinSize(), min); 163 } 164 165 166 void 167 _BTextInput_::AlignTextRect() 168 { 169 // the label font could require the control to be higher than 170 // necessary for the text view, we compensate this by layouting 171 // the text rect to be in the middle, normally this means there 172 // is one pixel spacing on each side 173 BRect textRect(Bounds()); 174 float vInset = max_c(1, 175 floorf((textRect.Height() - LineHeight(0)) / 2.0)); 176 float hInset = 2; 177 float textFontWidth = TextRect().right; 178 179 if (be_control_look != NULL) { 180 switch (Alignment()) { 181 case B_ALIGN_LEFT: 182 hInset = be_control_look->DefaultLabelSpacing(); 183 break; 184 185 case B_ALIGN_RIGHT: 186 hInset = textRect.right - textFontWidth; 187 hInset -= be_control_look->DefaultLabelSpacing(); 188 break; 189 190 case B_ALIGN_CENTER: 191 hInset = (textRect.right - textFontWidth) / 2.0; 192 break; 193 194 default: 195 break; 196 } 197 } 198 199 textRect.InsetBy(hInset, vInset); 200 SetTextRect(textRect); 201 } 202 203 204 void 205 _BTextInput_::SetInitialText() 206 { 207 free(fPreviousText); 208 fPreviousText = NULL; 209 210 if (Text() != NULL) 211 fPreviousText = strdup(Text()); 212 } 213 214 215 void 216 _BTextInput_::Paste(BClipboard* clipboard) 217 { 218 BTextView::Paste(clipboard); 219 Invalidate(); 220 } 221 222 223 void 224 _BTextInput_::InsertText(const char* inText, int32 inLength, 225 int32 inOffset, const text_run_array* inRuns) 226 { 227 // Filter all line breaks, note that inText is not terminated. 228 if (inLength == 1) { 229 if (*inText == '\n' || *inText == '\r') 230 BTextView::InsertText(" ", 1, inOffset, inRuns); 231 else 232 BTextView::InsertText(inText, 1, inOffset, inRuns); 233 } else { 234 BString filteredText(inText, inLength); 235 filteredText.ReplaceAll('\n', ' '); 236 filteredText.ReplaceAll('\r', ' '); 237 BTextView::InsertText(filteredText.String(), inLength, inOffset, 238 inRuns); 239 } 240 241 TextControl()->InvokeNotify(TextControl()->ModificationMessage(), 242 B_CONTROL_MODIFIED); 243 } 244 245 246 void 247 _BTextInput_::DeleteText(int32 fromOffset, int32 toOffset) 248 { 249 BTextView::DeleteText(fromOffset, toOffset); 250 251 TextControl()->InvokeNotify(TextControl()->ModificationMessage(), 252 B_CONTROL_MODIFIED); 253 } 254 255 256 BTextControl* 257 _BTextInput_::TextControl() 258 { 259 BTextControl* textControl = NULL; 260 if (Parent() != NULL) 261 textControl = dynamic_cast<BTextControl*>(Parent()); 262 263 if (textControl == NULL) 264 debugger("_BTextInput_ should have a BTextControl as parent"); 265 266 return textControl; 267 } 268 269 270 } // namespace BPrivate 271 272