1 /* 2 * Copyright 2015 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * John Scipione, jscipione@gmail.com 7 */ 8 9 10 #include <DecimalSpinner.h> 11 12 #include <stdio.h> 13 #include <stdlib.h> 14 15 #include <PropertyInfo.h> 16 #include <TextView.h> 17 18 19 static double 20 roundTo(double value, uint32 n) 21 { 22 return floor(value * pow(10.0, n) + 0.5) / pow(10.0, n); 23 } 24 25 26 static property_info sProperties[] = { 27 { 28 "MaxValue", 29 { B_GET_PROPERTY, 0 }, 30 { B_DIRECT_SPECIFIER, 0 }, 31 "Returns the maximum value of the spinner.", 32 0, 33 { B_DOUBLE_TYPE } 34 }, 35 { 36 "MaxValue", 37 { B_SET_PROPERTY, 0 }, 38 { B_DIRECT_SPECIFIER, 0}, 39 "Sets the maximum value of the spinner.", 40 0, 41 { B_DOUBLE_TYPE } 42 }, 43 44 { 45 "MinValue", 46 { B_GET_PROPERTY, 0 }, 47 { B_DIRECT_SPECIFIER, 0 }, 48 "Returns the minimum value of the spinner.", 49 0, 50 { B_DOUBLE_TYPE } 51 }, 52 { 53 "MinValue", 54 { B_SET_PROPERTY, 0 }, 55 { B_DIRECT_SPECIFIER, 0}, 56 "Sets the minimum value of the spinner.", 57 0, 58 { B_DOUBLE_TYPE } 59 }, 60 61 { 62 "Precision", 63 { B_SET_PROPERTY, 0 }, 64 { B_DIRECT_SPECIFIER, 0}, 65 "Sets the number of decimal places of precision of the spinner.", 66 0, 67 { B_UINT32_TYPE } 68 }, 69 { 70 "Precision", 71 { B_GET_PROPERTY, 0 }, 72 { B_DIRECT_SPECIFIER, 0 }, 73 "Returns the number of decimal places of precision of the spinner.", 74 0, 75 { B_UINT32_TYPE } 76 }, 77 78 { 79 "Step", 80 { B_GET_PROPERTY, 0 }, 81 { B_DIRECT_SPECIFIER, 0 }, 82 "Returns the step size of the spinner.", 83 0, 84 { B_DOUBLE_TYPE } 85 }, 86 { 87 "Step", 88 { B_SET_PROPERTY, 0 }, 89 { B_DIRECT_SPECIFIER, 0}, 90 "Sets the step size of the spinner.", 91 0, 92 { B_DOUBLE_TYPE } 93 }, 94 95 { 96 "Value", 97 { B_GET_PROPERTY, 0 }, 98 { B_DIRECT_SPECIFIER, 0 }, 99 "Returns the value of the spinner.", 100 0, 101 { B_DOUBLE_TYPE } 102 }, 103 { 104 "Value", 105 { B_SET_PROPERTY, 0 }, 106 { B_DIRECT_SPECIFIER, 0}, 107 "Sets the value of the spinner.", 108 0, 109 { B_DOUBLE_TYPE } 110 }, 111 112 { 0 } 113 }; 114 115 116 // #pragma mark - BDecimalSpinner 117 118 119 BDecimalSpinner::BDecimalSpinner(BRect frame, const char* name, 120 const char* label, BMessage* message, uint32 resizingMode, uint32 flags) 121 : 122 BAbstractSpinner(frame, name, label, message, resizingMode, flags) 123 { 124 _InitObject(); 125 } 126 127 128 BDecimalSpinner::BDecimalSpinner(const char* name, const char* label, 129 BMessage* message, uint32 flags) 130 : 131 BAbstractSpinner(name, label, message, flags) 132 { 133 _InitObject(); 134 } 135 136 137 BDecimalSpinner::BDecimalSpinner(BMessage* data) 138 : 139 BAbstractSpinner(data) 140 { 141 _InitObject(); 142 143 if (data->FindDouble("_min", &fMinValue) != B_OK) 144 fMinValue = 0.0; 145 146 if (data->FindDouble("_max", &fMaxValue) != B_OK) 147 fMinValue = 100.0; 148 149 if (data->FindUInt32("_precision", &fPrecision) != B_OK) 150 fPrecision = 2; 151 152 if (data->FindDouble("_step", &fStep) != B_OK) 153 fStep = 1.0; 154 155 if (data->FindDouble("_val", &fValue) != B_OK) 156 fValue = 0.0; 157 } 158 159 160 BDecimalSpinner::~BDecimalSpinner() 161 { 162 } 163 164 165 BArchivable* 166 BDecimalSpinner::Instantiate(BMessage* data) 167 { 168 if (validate_instantiation(data, "DecimalSpinner")) 169 return new BDecimalSpinner(data); 170 171 return NULL; 172 } 173 174 175 status_t 176 BDecimalSpinner::Archive(BMessage* data, bool deep) const 177 { 178 status_t status = BAbstractSpinner::Archive(data, deep); 179 data->AddString("class", "DecimalSpinner"); 180 181 if (status == B_OK) 182 status = data->AddDouble("_min", fMinValue); 183 184 if (status == B_OK) 185 status = data->AddDouble("_max", fMaxValue); 186 187 if (status == B_OK) 188 status = data->AddUInt32("_precision", fPrecision); 189 190 if (status == B_OK) 191 status = data->AddDouble("_step", fStep); 192 193 if (status == B_OK) 194 status = data->AddDouble("_val", fValue); 195 196 return status; 197 } 198 199 200 status_t 201 BDecimalSpinner::GetSupportedSuites(BMessage* message) 202 { 203 message->AddString("suites", "suite/vnd.Haiku-decimal-spinner"); 204 205 BPropertyInfo prop_info(sProperties); 206 message->AddFlat("messages", &prop_info); 207 208 return BView::GetSupportedSuites(message); 209 } 210 211 212 void 213 BDecimalSpinner::AttachedToWindow() 214 { 215 SetValue(fValue); 216 217 BAbstractSpinner::AttachedToWindow(); 218 } 219 220 221 void 222 BDecimalSpinner::Decrement() 223 { 224 SetValue(Value() - Step()); 225 } 226 227 228 void 229 BDecimalSpinner::Increment() 230 { 231 SetValue(Value() + Step()); 232 } 233 234 235 void 236 BDecimalSpinner::SetEnabled(bool enable) 237 { 238 if (IsEnabled() == enable) 239 return; 240 241 SetIncrementEnabled(enable && Value() < fMaxValue); 242 SetDecrementEnabled(enable && Value() > fMinValue); 243 244 BAbstractSpinner::SetEnabled(enable); 245 } 246 247 248 void 249 BDecimalSpinner::SetMinValue(double min) 250 { 251 fMinValue = min; 252 if (fValue < fMinValue) 253 SetValue(fMinValue); 254 } 255 256 257 void 258 BDecimalSpinner::SetMaxValue(double max) 259 { 260 fMaxValue = max; 261 if (fValue > fMaxValue) 262 SetValue(fMaxValue); 263 } 264 265 266 void 267 BDecimalSpinner::Range(double* min, double* max) 268 { 269 *min = fMinValue; 270 *max = fMaxValue; 271 } 272 273 274 void 275 BDecimalSpinner::SetRange(double min, double max) 276 { 277 SetMinValue(min); 278 SetMaxValue(max); 279 } 280 281 282 void 283 BDecimalSpinner::SetValue(int32 value) 284 { 285 SetValue((double)value); 286 } 287 288 289 void 290 BDecimalSpinner::SetValue(double value) 291 { 292 // clip to range 293 if (value < fMinValue) 294 value = fMinValue; 295 else if (value > fMaxValue) 296 value = fMaxValue; 297 298 // update the text view 299 char* format; 300 asprintf(&format, "%%.%" B_PRId32 "f", fPrecision); 301 char* valueString; 302 asprintf(&valueString, format, value); 303 TextView()->SetText(valueString); 304 free(format); 305 free(valueString); 306 307 // update the up and down arrows 308 SetIncrementEnabled(IsEnabled() && value < fMaxValue); 309 SetDecrementEnabled(IsEnabled() && value > fMinValue); 310 311 if (value == fValue) 312 return; 313 314 fValue = value; 315 ValueChanged(); 316 317 Invoke(); 318 Invalidate(); 319 } 320 321 322 void 323 BDecimalSpinner::SetValueFromText() 324 { 325 SetValue(roundTo(atof(TextView()->Text()), Precision())); 326 } 327 328 329 // #pragma mark - BDecimalSpinner private methods 330 331 332 void 333 BDecimalSpinner::_InitObject() 334 { 335 fMinValue = 0.0; 336 fMaxValue = 100.0; 337 fPrecision = 2; 338 fStep = 1.0; 339 fValue = 0.0; 340 341 TextView()->SetAlignment(B_ALIGN_RIGHT); 342 for (uint32 c = 0; c <= 42; c++) 343 TextView()->DisallowChar(c); 344 345 TextView()->DisallowChar('/'); 346 for (uint32 c = 58; c <= 127; c++) 347 TextView()->DisallowChar(c); 348 } 349 350 351 // FBC padding 352 353 void BDecimalSpinner::_ReservedDecimalSpinner20() {} 354 void BDecimalSpinner::_ReservedDecimalSpinner19() {} 355 void BDecimalSpinner::_ReservedDecimalSpinner18() {} 356 void BDecimalSpinner::_ReservedDecimalSpinner17() {} 357 void BDecimalSpinner::_ReservedDecimalSpinner16() {} 358 void BDecimalSpinner::_ReservedDecimalSpinner15() {} 359 void BDecimalSpinner::_ReservedDecimalSpinner14() {} 360 void BDecimalSpinner::_ReservedDecimalSpinner13() {} 361 void BDecimalSpinner::_ReservedDecimalSpinner12() {} 362 void BDecimalSpinner::_ReservedDecimalSpinner11() {} 363 void BDecimalSpinner::_ReservedDecimalSpinner10() {} 364 void BDecimalSpinner::_ReservedDecimalSpinner9() {} 365 void BDecimalSpinner::_ReservedDecimalSpinner8() {} 366 void BDecimalSpinner::_ReservedDecimalSpinner7() {} 367 void BDecimalSpinner::_ReservedDecimalSpinner6() {} 368 void BDecimalSpinner::_ReservedDecimalSpinner5() {} 369 void BDecimalSpinner::_ReservedDecimalSpinner4() {} 370 void BDecimalSpinner::_ReservedDecimalSpinner3() {} 371 void BDecimalSpinner::_ReservedDecimalSpinner2() {} 372 void BDecimalSpinner::_ReservedDecimalSpinner1() {} 373