1 /* 2 * Copyright 2005, Jérôme Duval. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Inspired by SoundCapture from Be newsletter (Media Kit Basics: Consumers and Producers) 6 */ 7 8 #include <stdio.h> 9 #include <string.h> 10 11 #include "TrackSlider.h" 12 #include "icon_button.h" 13 14 TrackSlider::TrackSlider(BRect rect, const char *title, BMessage *msg, uint32 resizeFlags) 15 : BControl(rect, "slider", NULL, msg, resizeFlags, B_WILL_DRAW | B_FRAME_EVENTS), 16 leftBitmap(BRect(BPoint(0,0), kLeftRightTrackSliderSize), B_CMAP8), 17 rightBitmap(BRect(BPoint(0,0), kLeftRightTrackSliderSize), B_CMAP8), 18 leftThumbBitmap(BRect(0, 0, kLeftRightThumbWidth - 1, kLeftRightThumbHeight - 1), B_CMAP8), 19 rightThumbBitmap(BRect(0, 0, kLeftRightThumbWidth - 1, kLeftRightThumbHeight - 1), B_CMAP8), 20 fLeftTime(0), fRightTime(1000000), fMainTime(0), fTotalTime(1000000), 21 fLeftTracking(false), fRightTracking(false), fMainTracking(false) 22 { 23 fFont.SetSize(8.0); 24 fFont.SetFlags(B_DISABLE_ANTIALIASING); 25 26 int32 numFamilies = count_font_families(); 27 for (int32 i = 0; i < numFamilies; i++ ) { 28 font_family family; 29 uint32 flags; 30 if ((get_font_family(i, &family, &flags) == B_OK) 31 && (strcmp(family, "Baskerville") == 0)) { 32 fFont.SetFamilyAndFace(family, B_REGULAR_FACE); 33 break; 34 } 35 } 36 leftBitmap.SetBits(kLeftTrackSliderBits, kLeftRightTrackSliderWidth * kLeftRightTrackSliderHeight, 0, B_CMAP8); 37 rightBitmap.SetBits(kRightTrackSliderBits, kLeftRightTrackSliderWidth * kLeftRightTrackSliderHeight, 0, B_CMAP8); 38 leftThumbBitmap.SetBits(kLeftThumbBits, kLeftRightThumbWidth * kLeftRightThumbHeight, 0, B_CMAP8); 39 rightThumbBitmap.SetBits(kRightThumbBits, kLeftRightThumbWidth * kLeftRightThumbHeight, 0, B_CMAP8); 40 41 fRight = Bounds().right - kLeftRightTrackSliderWidth; 42 if (fTotalTime == 0) { 43 fLeftX = 14; 44 fRightX = fRight; 45 fPositionX = 15; 46 } else { 47 fLeftX = 14 + (fRight - 15) * ((double)fLeftTime / fTotalTime); 48 fRightX = 15 + (fRight - 16) * ((double)fRightTime / fTotalTime); 49 fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime); 50 } 51 } 52 53 54 TrackSlider::~TrackSlider() 55 { 56 57 } 58 59 60 void 61 TrackSlider::AttachedToWindow() 62 { 63 BControl::AttachedToWindow(); 64 SetViewColor(B_TRANSPARENT_COLOR); 65 } 66 67 68 #define SLIDER_BASE 10 69 70 void 71 TrackSlider::Draw(BRect updateRect) 72 { 73 SetHighColor(189,186,189); 74 StrokeLine(BPoint(11,SLIDER_BASE+1), BPoint(fRight,SLIDER_BASE+1)); 75 SetHighColor(0,0,0); 76 StrokeLine(BPoint(11,SLIDER_BASE+2), BPoint(fRight,SLIDER_BASE+2)); 77 SetHighColor(255,255,255); 78 StrokeLine(BPoint(11,SLIDER_BASE+17), BPoint(fRight,SLIDER_BASE+17)); 79 SetHighColor(231,227,231); 80 StrokeLine(BPoint(11,SLIDER_BASE+18), BPoint(fRight,SLIDER_BASE+18)); 81 82 SetDrawingMode(B_OP_OVER); 83 84 SetHighColor(216,216,216); 85 FillRect(BRect(0,1,fPositionX < 18 ? 18 : fPositionX-22,SLIDER_BASE)); 86 FillRect(BRect(fPositionX < 18 ? 65 : fPositionX > fRight - 3 ? fRight : fPositionX+22,0,Bounds().right,SLIDER_BASE)); 87 FillRect(BRect(0,0,Bounds().right,0)); 88 FillRect(BRect(0,SLIDER_BASE+18,Bounds().right,SLIDER_BASE+21)); 89 FillRect(BRect(0,0,10,SLIDER_BASE+21)); 90 FillRect(BRect(Bounds().right - 10,0,Bounds().right,SLIDER_BASE+21)); 91 92 SetLowColor(HighColor()); 93 94 BPoint leftPoint(5,SLIDER_BASE+1); 95 DrawBitmapAsync(&leftBitmap, BRect(BPoint(0,0), kLeftRightTrackSliderSize - BPoint(5,0)), 96 BRect(leftPoint, leftPoint+kLeftRightTrackSliderSize-BPoint(5,0))); 97 BPoint rightPoint(fRight + 1,SLIDER_BASE+1); 98 DrawBitmapAsync(&rightBitmap, BRect(BPoint(5,0), kLeftRightTrackSliderSize), 99 BRect(rightPoint, rightPoint+kLeftRightTrackSliderSize-BPoint(5,0))); 100 101 SetHighColor(153,153,153); 102 FillRect(BRect(11,SLIDER_BASE+3,fLeftX-9,SLIDER_BASE+16)); 103 FillRect(BRect(fRightX+9,SLIDER_BASE+3,fRight,SLIDER_BASE+16)); 104 if (fLeftX>19) { 105 StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+3),BPoint(fLeftX-6,SLIDER_BASE+3)); 106 StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+4),BPoint(fLeftX-7,SLIDER_BASE+4)); 107 StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+5),BPoint(fLeftX-8,SLIDER_BASE+5)); 108 StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+16),BPoint(fLeftX-6,SLIDER_BASE+16)); 109 StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+15),BPoint(fLeftX-7,SLIDER_BASE+15)); 110 StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+14),BPoint(fLeftX-8,SLIDER_BASE+14)); 111 } 112 if (fRightX < fRight - 5) { 113 StrokeLine(BPoint(fRightX+5,SLIDER_BASE+3),BPoint(fRightX+8,SLIDER_BASE+3)); 114 StrokeLine(BPoint(fRightX+7,SLIDER_BASE+4),BPoint(fRightX+8,SLIDER_BASE+4)); 115 StrokeLine(BPoint(fRightX+8,SLIDER_BASE+5),BPoint(fRightX+8,SLIDER_BASE+6)); 116 StrokeLine(BPoint(fRightX+8,SLIDER_BASE+13),BPoint(fRightX+8,SLIDER_BASE+14)); 117 StrokeLine(BPoint(fRightX+5,SLIDER_BASE+16),BPoint(fRightX+8,SLIDER_BASE+16)); 118 StrokeLine(BPoint(fRightX+7,SLIDER_BASE+15),BPoint(fRightX+8,SLIDER_BASE+15)); 119 } 120 SetHighColor(144,186,136); 121 FillRect(BRect(fLeftX+1,SLIDER_BASE+3,fRightX,SLIDER_BASE+4)); 122 FillRect(BRect(fLeftX+1,SLIDER_BASE+5,fLeftX+2,SLIDER_BASE+16)); 123 SetHighColor(171,221,161); 124 FillRect(BRect(fLeftX+3,SLIDER_BASE+5,fRightX,SLIDER_BASE+16)); 125 126 int i = 17; 127 int j = 18; 128 SetHighColor(128,128,128); 129 for (; i<fLeftX-9; i+=6) { 130 StrokeLine(BPoint(i,SLIDER_BASE+7), BPoint(i,SLIDER_BASE+13)); 131 } 132 SetHighColor(179,179,179); 133 for (; j<fLeftX-9; j+=6) { 134 StrokeLine(BPoint(j,SLIDER_BASE+7), BPoint(j,SLIDER_BASE+13)); 135 } 136 137 while (i<=fLeftX) 138 i+=6; 139 while (j<=fLeftX) 140 j+=6; 141 142 SetHighColor(144,186,136); 143 for (; i<=fRightX; i+=6) { 144 StrokeLine(BPoint(i,SLIDER_BASE+7), BPoint(i,SLIDER_BASE+13)); 145 } 146 SetHighColor(189,244,178); 147 for (; j<=fRightX; j+=6) { 148 StrokeLine(BPoint(j,SLIDER_BASE+7), BPoint(j,SLIDER_BASE+13)); 149 } 150 151 while (i<=fRightX+9) 152 i+=6; 153 while (j<=fRightX+9) 154 j+=6; 155 156 SetHighColor(128,128,128); 157 for (; i<=fRight + 1; i+=6) { 158 StrokeLine(BPoint(i,SLIDER_BASE+7), BPoint(i,SLIDER_BASE+13)); 159 } 160 SetHighColor(179,179,179); 161 for (; j<=fRight + 1; j+=6) { 162 StrokeLine(BPoint(j,SLIDER_BASE+7), BPoint(j,SLIDER_BASE+13)); 163 } 164 165 SetLowColor(HighColor()); 166 167 BPoint leftThumbPoint(fLeftX-8,SLIDER_BASE+3); 168 DrawBitmapAsync(&leftThumbBitmap, BRect(BPoint(0,0), kLeftRightThumbSize - BPoint(7,0)), 169 BRect(leftThumbPoint, leftThumbPoint+kLeftRightThumbSize-BPoint(7,0))); 170 171 BPoint rightThumbPoint(fRightX,SLIDER_BASE+3); 172 DrawBitmapAsync(&rightThumbBitmap, BRect(BPoint(6,0), kLeftRightThumbSize), 173 BRect(rightThumbPoint, rightThumbPoint+kLeftRightThumbSize-BPoint(6,0))); 174 175 rgb_color black = {0,0,0}; 176 rgb_color rose = {255,152,152}; 177 rgb_color red = {255,0,0}; 178 rgb_color bordeau = {178,0,0}; 179 rgb_color white = {255,255,255}; 180 181 DrawCounter(fMainTime, fPositionX, fMainTracking); 182 if (fLeftTracking) 183 DrawCounter(fLeftTime, fLeftX, fLeftTracking); 184 else if (fRightTracking) 185 DrawCounter(fRightTime, fRightX, fRightTracking); 186 187 BeginLineArray(30); 188 AddLine(BPoint(fPositionX,SLIDER_BASE+7), BPoint(fPositionX-4,SLIDER_BASE+3), black); 189 AddLine(BPoint(fPositionX-4,SLIDER_BASE+3), BPoint(fPositionX-4,SLIDER_BASE+1), black); 190 AddLine(BPoint(fPositionX-4,SLIDER_BASE+1), BPoint(fPositionX+4,SLIDER_BASE+1), black); 191 AddLine(BPoint(fPositionX+4,SLIDER_BASE+1), BPoint(fPositionX+4,SLIDER_BASE+3), black); 192 AddLine(BPoint(fPositionX+4,SLIDER_BASE+3), BPoint(fPositionX,SLIDER_BASE+7), black); 193 194 195 AddLine(BPoint(fPositionX-3,SLIDER_BASE+2), BPoint(fPositionX+3,SLIDER_BASE+2), rose); 196 AddLine(BPoint(fPositionX-3,SLIDER_BASE+3), BPoint(fPositionX-1,SLIDER_BASE+5), rose); 197 198 AddLine(BPoint(fPositionX-2,SLIDER_BASE+3), BPoint(fPositionX+2,SLIDER_BASE+3), red); 199 AddLine(BPoint(fPositionX-1,SLIDER_BASE+4), BPoint(fPositionX+1,SLIDER_BASE+4), red); 200 AddLine(BPoint(fPositionX,SLIDER_BASE+5), BPoint(fPositionX,SLIDER_BASE+5), red); 201 202 AddLine(BPoint(fPositionX,SLIDER_BASE+6), BPoint(fPositionX+3,SLIDER_BASE+3), bordeau); 203 204 AddLine(BPoint(fPositionX,SLIDER_BASE+12), BPoint(fPositionX-4,SLIDER_BASE+16), black); 205 AddLine(BPoint(fPositionX-4,SLIDER_BASE+16), BPoint(fPositionX-4,SLIDER_BASE+17), black); 206 AddLine(BPoint(fPositionX-4,SLIDER_BASE+17), BPoint(fPositionX+4,SLIDER_BASE+17), black); 207 AddLine(BPoint(fPositionX+4,SLIDER_BASE+17), BPoint(fPositionX+4,SLIDER_BASE+16), black); 208 AddLine(BPoint(fPositionX+4,SLIDER_BASE+16), BPoint(fPositionX,SLIDER_BASE+12), black); 209 AddLine(BPoint(fPositionX-4,SLIDER_BASE+18), BPoint(fPositionX+4,SLIDER_BASE+18), white); 210 211 AddLine(BPoint(fPositionX-3,SLIDER_BASE+16), BPoint(fPositionX,SLIDER_BASE+13), rose); 212 213 AddLine(BPoint(fPositionX-2,SLIDER_BASE+16), BPoint(fPositionX+2,SLIDER_BASE+16), red); 214 AddLine(BPoint(fPositionX-1,SLIDER_BASE+15), BPoint(fPositionX+1,SLIDER_BASE+15), red); 215 AddLine(BPoint(fPositionX,SLIDER_BASE+14), BPoint(fPositionX,SLIDER_BASE+14), red); 216 217 AddLine(BPoint(fPositionX+1,SLIDER_BASE+14), BPoint(fPositionX+3,SLIDER_BASE+16), bordeau); 218 219 EndLineArray(); 220 221 222 223 Flush(); 224 } 225 226 227 void 228 TrackSlider::DrawCounter(bigtime_t timestamp, float position, bool isTracking) 229 { 230 // timecounter 231 232 rgb_color gray = {128,128,128}; 233 rgb_color blue = {0,0,140}; 234 rgb_color blue2 = {146,146,214}; 235 rgb_color white = {255,255,255}; 236 237 char string[12]; 238 TimeToString(timestamp, string); 239 int32 halfwidth = ((int32)fFont.StringWidth(string)) / 2; 240 241 float counterX = position; 242 if (counterX < 39) 243 counterX = 39; 244 if (counterX > fRight - 23) 245 counterX = fRight - 23; 246 247 BeginLineArray(4); 248 if (!isTracking) { 249 AddLine(BPoint(counterX-halfwidth-3,SLIDER_BASE+1), BPoint(counterX+halfwidth+3,SLIDER_BASE+1), gray); 250 AddLine(BPoint(counterX+halfwidth+4,SLIDER_BASE+1), BPoint(counterX+halfwidth+4,SLIDER_BASE-8), gray); 251 AddLine(BPoint(counterX-halfwidth-4,SLIDER_BASE+1), BPoint(counterX-halfwidth-4,SLIDER_BASE-9), white); 252 AddLine(BPoint(counterX-halfwidth-3,SLIDER_BASE-9), BPoint(counterX+halfwidth+4,SLIDER_BASE-9), white); 253 SetHighColor(216,216,216); 254 } else { 255 AddLine(BPoint(counterX-halfwidth-3,SLIDER_BASE+1), BPoint(counterX+halfwidth+3,SLIDER_BASE+1), blue); 256 AddLine(BPoint(counterX+halfwidth+4,SLIDER_BASE+1), BPoint(counterX+halfwidth+4,SLIDER_BASE-9), blue2); 257 AddLine(BPoint(counterX-halfwidth-4,SLIDER_BASE+1), BPoint(counterX-halfwidth-4,SLIDER_BASE-9), blue2); 258 AddLine(BPoint(counterX-halfwidth-3,SLIDER_BASE-9), BPoint(counterX+halfwidth+3,SLIDER_BASE-9), blue2); 259 SetHighColor(48,48,241); 260 } 261 EndLineArray(); 262 FillRect(BRect(counterX-halfwidth-3,SLIDER_BASE-8,counterX+halfwidth+3,SLIDER_BASE)); 263 264 SetDrawingMode(B_OP_COPY); 265 if (isTracking) 266 SetHighColor(255,255,255); 267 else 268 SetHighColor(0,0,0); 269 SetLowColor(ViewColor()); 270 271 SetFont(&fFont); 272 DrawString(string, BPoint(counterX-halfwidth, SLIDER_BASE-1)); 273 274 } 275 276 277 void 278 TrackSlider::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 279 { 280 if (!IsTracking()) 281 return; 282 283 uint32 mouseButtons; 284 BPoint where; 285 GetMouse(&where, &mouseButtons, true); 286 287 // button not pressed, exit 288 if (! (mouseButtons & B_PRIMARY_MOUSE_BUTTON)) { 289 Invoke(); 290 SetTracking(false); 291 } 292 293 UpdatePosition(point); 294 } 295 296 297 void 298 TrackSlider::MouseDown(BPoint point) 299 { 300 if (!Bounds().InsetBySelf(2,2).Contains(point)) 301 return; 302 303 UpdatePosition(point); 304 SetTracking(true); 305 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY| B_LOCK_WINDOW_FOCUS); 306 } 307 308 309 void 310 TrackSlider::MouseUp(BPoint point) 311 { 312 if (!IsTracking()) 313 return; 314 if (Bounds().InsetBySelf(2,2).Contains(point)) { 315 UpdatePosition(point); 316 } 317 318 fLeftTracking = fRightTracking = fMainTracking = false; 319 320 Invoke(); 321 SetTracking(false); 322 Draw(Bounds()); 323 Flush(); 324 } 325 326 327 void 328 TrackSlider::UpdatePosition(BPoint point) 329 { 330 BRect leftRect(fLeftX-9, SLIDER_BASE+3, fLeftX, SLIDER_BASE+16); 331 BRect rightRect(fRightX, SLIDER_BASE+3, fRightX+9, SLIDER_BASE+16); 332 333 if (!(fRightTracking || fMainTracking) && (fLeftTracking || ((point.x < fPositionX-4) && leftRect.Contains(point)))) { 334 if (!IsTracking()) 335 fLastX = point.x - fLeftX; 336 fLeftX = MIN(MAX(point.x - fLastX, 15), fRight); 337 fLeftTime = (bigtime_t)(MAX(MIN((fLeftX - 15) / (fRight - 14),1), 0) * fTotalTime); 338 fLeftTracking = true; 339 340 BMessage msg = *Message(); 341 msg.AddInt64("left", fLeftTime); 342 343 if (fPositionX < fLeftX) { 344 fPositionX = fLeftX + 1; 345 fMainTime = fLeftTime; 346 msg.AddInt64("main", fMainTime); 347 if (fRightX < fPositionX) { 348 fRightX = fPositionX; 349 fRightTime = fMainTime; 350 msg.AddInt64("right", fRightTime); 351 } 352 } 353 354 Invoke(&msg); 355 356 //printf("fLeftPos : %Ld\n", fLeftTime); 357 } else if (!fMainTracking && (fRightTracking || ((point.x > fPositionX+4) && rightRect.Contains(point)))) { 358 if (!IsTracking()) 359 fLastX = point.x - fRightX; 360 fRightX = MIN(MAX(point.x - fLastX, 15), fRight); 361 fRightTime = (bigtime_t)(MAX(MIN((fRightX - 15) / (fRight - 14),1), 0) * fTotalTime); 362 fRightTracking = true; 363 364 BMessage msg = *Message(); 365 msg.AddInt64("right", fRightTime); 366 367 if (fPositionX > fRightX) { 368 fPositionX = fRightX; 369 fMainTime = fRightTime; 370 msg.AddInt64("main", fMainTime); 371 if (fLeftX > fPositionX) { 372 fLeftX = fPositionX - 1; 373 fLeftTime = fMainTime; 374 msg.AddInt64("left", fLeftTime); 375 } 376 } 377 378 Invoke(&msg); 379 380 //printf("fRightPos : %Ld\n", fRightTime); 381 } else { 382 fPositionX = MIN(MAX(point.x, 15), fRight); 383 fMainTime = (bigtime_t)(MAX(MIN((fPositionX - 15) / (fRight - 14),1), 0) * fTotalTime); 384 fMainTracking = true; 385 386 BMessage msg = *Message(); 387 msg.AddInt64("main", fMainTime); 388 389 if (fRightX < fPositionX) { 390 fRightX = fPositionX; 391 fRightTime = fMainTime; 392 msg.AddInt64("right", fRightTime); 393 } else if (fLeftX > fPositionX) { 394 fLeftX = fPositionX - 1; 395 fLeftTime = fMainTime; 396 msg.AddInt64("left", fLeftTime); 397 } 398 399 Invoke(&msg); 400 //printf("fPosition : %Ld\n", fMainTime); 401 } 402 Draw(Bounds()); 403 Flush(); 404 } 405 406 407 void 408 TrackSlider::TimeToString(bigtime_t timestamp, char *string) 409 { 410 uint32 hours = timestamp / 3600000000LL; 411 timestamp -= hours * 3600000000LL; 412 uint32 minutes = timestamp / 60000000LL; 413 timestamp -= minutes * 60000000LL; 414 uint32 seconds = timestamp / 1000000LL; 415 timestamp -= seconds * 1000000LL; 416 uint32 centiseconds = timestamp / 10000LL; 417 sprintf(string, "%02ld:%02ld:%02ld:%02ld", hours, minutes, seconds, centiseconds); 418 419 } 420 421 422 void 423 TrackSlider::SetMainTime(bigtime_t timestamp, bool reset) 424 { 425 fMainTime = timestamp; 426 fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime); 427 if (reset) { 428 fRightTime = fTotalTime; 429 fLeftTime = 0; 430 fLeftX = 14 + (fRight - 15) * ((double)fLeftTime / fTotalTime); 431 fRightX = 15 + (fRight - 16) * ((double)fRightTime / fTotalTime); 432 } 433 Invalidate(); 434 } 435 436 void 437 TrackSlider::SetTotalTime(bigtime_t timestamp, bool reset) 438 { 439 fTotalTime = timestamp; 440 if (reset) { 441 fMainTime = 0; 442 fRightTime = fTotalTime; 443 fLeftTime = 0; 444 } 445 fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime); 446 fLeftX = 14 + (fRight - 15) * ((double)fLeftTime / fTotalTime); 447 fRightX = 15 + (fRight - 16) * ((double)fRightTime / fTotalTime); 448 Invalidate(); 449 } 450 451 452 void 453 TrackSlider::ResetMainTime() 454 { 455 fMainTime = fLeftTime; 456 fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime); 457 Invalidate(); 458 } 459 460 461 void 462 TrackSlider::FrameResized(float width, float height) 463 { 464 fRight = Bounds().right - kLeftRightTrackSliderWidth; 465 fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime); 466 fLeftX = 14 + (fRight - 15) * ((double)fLeftTime / fTotalTime); 467 fRightX = 15 + (fRight - 16) * ((double)fRightTime / fTotalTime); 468 Invalidate(); 469 } 470