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