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