1 /* 2 * Copyright 2001-2008, 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 : BTextView(frame, "_input_", textRect, resizeMask, flags), 33 fPreviousText(NULL) 34 { 35 MakeResizable(true); 36 } 37 38 39 _BTextInput_::_BTextInput_(BMessage* archive) 40 : BTextView(archive), 41 fPreviousText(NULL) 42 { 43 MakeResizable(true); 44 } 45 46 47 _BTextInput_::~_BTextInput_() 48 { 49 free(fPreviousText); 50 } 51 52 53 BArchivable* 54 _BTextInput_::Instantiate(BMessage* archive) 55 { 56 if (validate_instantiation(archive, "_BTextInput_")) 57 return new _BTextInput_(archive); 58 59 return NULL; 60 } 61 62 63 status_t 64 _BTextInput_::Archive(BMessage* data, bool deep) const 65 { 66 return BTextView::Archive(data, true); 67 } 68 69 70 void 71 _BTextInput_::MouseDown(BPoint where) 72 { 73 if (!IsFocus()) { 74 MakeFocus(true); 75 return; 76 } 77 78 // only pass through to base class if we already have focus 79 BTextView::MouseDown(where); 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 SelectAll(); 133 } else { 134 if (strcmp(Text(), fPreviousText) != 0) 135 TextControl()->Invoke(); 136 137 free(fPreviousText); 138 fPreviousText = NULL; 139 } 140 141 // if (Window()) { 142 // TODO: why do we have to invalidate here? 143 // I'm leaving this in, but it looks suspicious... :-) 144 // Invalidate(Bounds()); 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 { 181 switch(Alignment()) 182 { 183 case B_ALIGN_LEFT: 184 hInset = be_control_look->DefaultLabelSpacing(); 185 break; 186 187 case B_ALIGN_RIGHT: 188 hInset = textRect.right - textFontWidth; 189 hInset -= be_control_look->DefaultLabelSpacing(); 190 break; 191 192 case B_ALIGN_CENTER: 193 hInset = (textRect.right - textFontWidth)/ 2.0; 194 break; 195 196 default: 197 break; 198 } 199 } 200 201 textRect.InsetBy(hInset, vInset); 202 SetTextRect(textRect); 203 } 204 205 206 void 207 _BTextInput_::SetInitialText() 208 { 209 free(fPreviousText); 210 fPreviousText = NULL; 211 212 if (Text()) 213 fPreviousText = strdup(Text()); 214 } 215 216 217 void 218 _BTextInput_::Paste(BClipboard* clipboard) 219 { 220 BTextView::Paste(clipboard); 221 Invalidate(); 222 } 223 224 225 void 226 _BTextInput_::InsertText(const char* inText, int32 inLength, 227 int32 inOffset, const text_run_array* inRuns) 228 { 229 // Filter all line breaks, note that inText is not terminated. 230 if (inLength == 1) { 231 if (*inText == '\n' || *inText == '\r') 232 BTextView::InsertText(" ", 1, inOffset, inRuns); 233 else 234 BTextView::InsertText(inText, 1, inOffset, inRuns); 235 } else { 236 BString filteredText(inText, inLength); 237 filteredText.ReplaceAll('\n', ' '); 238 filteredText.ReplaceAll('\r', ' '); 239 BTextView::InsertText(filteredText.String(), inLength, inOffset, 240 inRuns); 241 } 242 243 TextControl()->InvokeNotify(TextControl()->ModificationMessage(), 244 B_CONTROL_MODIFIED); 245 } 246 247 248 void 249 _BTextInput_::DeleteText(int32 fromOffset, int32 toOffset) 250 { 251 BTextView::DeleteText(fromOffset, toOffset); 252 253 TextControl()->InvokeNotify(TextControl()->ModificationMessage(), 254 B_CONTROL_MODIFIED); 255 } 256 257 258 BTextControl* 259 _BTextInput_::TextControl() 260 { 261 BTextControl* textControl = NULL; 262 263 if (Parent()) 264 textControl = dynamic_cast<BTextControl*>(Parent()); 265 266 if (!textControl) 267 debugger("_BTextInput_ should have a BTextControl as parent"); 268 269 return textControl; 270 } 271 272 273 } // namespace BPrivate 274 275