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) + 1.0); 157 min.width = min.height * 3; 158 return BLayoutUtils::ComposeSize(ExplicitMinSize(), min); 159 } 160 161 162 void 163 _BTextInput_::AlignTextRect() 164 { 165 // the label font could require the control to be higher than 166 // necessary for the text view, we compensate this by layouting 167 // the text rect to be in the middle, normally this means there 168 // is one pixel spacing on each side 169 BRect textRect(Bounds()); 170 textRect.left = 0.0; 171 float vInset = max_c(1, floorf((textRect.Height() - LineHeight(0)) / 2.0)); 172 float hInset = 2; 173 174 if (be_control_look) 175 hInset = be_control_look->DefaultLabelSpacing(); 176 177 textRect.InsetBy(hInset, vInset); 178 SetTextRect(textRect); 179 } 180 181 182 void 183 _BTextInput_::SetInitialText() 184 { 185 free(fPreviousText); 186 fPreviousText = NULL; 187 188 if (Text()) 189 fPreviousText = strdup(Text()); 190 } 191 192 193 void 194 _BTextInput_::Paste(BClipboard* clipboard) 195 { 196 BTextView::Paste(clipboard); 197 Invalidate(); 198 } 199 200 201 void 202 _BTextInput_::InsertText(const char* inText, int32 inLength, 203 int32 inOffset, const text_run_array* inRuns) 204 { 205 char* buffer = NULL; 206 207 if (strpbrk(inText, "\r\n") && inLength <= 1024) { 208 buffer = (char*)malloc(inLength); 209 210 if (buffer) { 211 strcpy(buffer, inText); 212 213 for (int32 i = 0; i < inLength; i++) { 214 if (buffer[i] == '\r' || buffer[i] == '\n') 215 buffer[i] = ' '; 216 } 217 } 218 } 219 220 BTextView::InsertText(buffer ? buffer : inText, inLength, inOffset, 221 inRuns); 222 223 TextControl()->InvokeNotify(TextControl()->ModificationMessage(), 224 B_CONTROL_MODIFIED); 225 226 free(buffer); 227 } 228 229 230 void 231 _BTextInput_::DeleteText(int32 fromOffset, int32 toOffset) 232 { 233 BTextView::DeleteText(fromOffset, toOffset); 234 235 TextControl()->InvokeNotify(TextControl()->ModificationMessage(), 236 B_CONTROL_MODIFIED); 237 } 238 239 240 BTextControl* 241 _BTextInput_::TextControl() 242 { 243 BTextControl* textControl = NULL; 244 245 if (Parent()) 246 textControl = dynamic_cast<BTextControl*>(Parent()); 247 248 if (!textControl) 249 debugger("_BTextInput_ should have a BTextControl as parent"); 250 251 return textControl; 252 } 253 254 255 } // namespace BPrivate 256 257