1 /* 2 * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/> 3 * (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com> 4 * 5 * This file is part of lsp-plugins 6 * Created on: 19 июл. 2017 г. 7 * 8 * lsp-plugins is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * any later version. 12 * 13 * lsp-plugins is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public License 19 * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 22 #include <ui/tk/tk.h> 23 #include <ui/graphics.h> 24 25 namespace lsp 26 { 27 namespace tk 28 { 29 const w_class_t LSPMarker::metadata = { "LSPMarker", &LSPGraphItem::metadata }; 30 LSPMarker(LSPDisplay * dpy)31 LSPMarker::LSPMarker(LSPDisplay *dpy): LSPGraphItem(dpy), 32 sColor(this) 33 { 34 nBasisID = 0; 35 nParallelID = 1; 36 fValue = 0.0f; 37 fLast = 0.0f; 38 fOffset = 0.0f; 39 fAngle = 0.0f; 40 fDX = 1.0f; 41 fDY = 0.0f; 42 fMin = -1.0f; 43 fMax = 1.0f; 44 nWidth = 1; 45 nCenter = 0; 46 nBorder = 0; 47 pClass = &metadata; 48 nXFlags = 0; 49 nMouseX = 0; // debug 50 nMouseY = 0; // debug 51 // nDMouseX = 0; // debug 52 // nDMouseY = 0; // debug 53 nMouseBtn = 0; 54 55 set_smooth(false); 56 } 57 ~LSPMarker()58 LSPMarker::~LSPMarker() 59 { 60 } 61 init()62 status_t LSPMarker::init() 63 { 64 status_t result = LSPGraphItem::init(); 65 if (result != STATUS_OK) 66 return result; 67 68 init_color(C_GRAPH_MARKER, &sColor); 69 70 if (!sSlots.add(LSPSLOT_CHANGE)) 71 return STATUS_NO_MEM; 72 73 return STATUS_OK; 74 } 75 set_basis_id(size_t value)76 void LSPMarker::set_basis_id(size_t value) 77 { 78 if (nBasisID == value) 79 return; 80 nBasisID = value; 81 query_draw(); 82 } 83 set_parallel_id(size_t value)84 void LSPMarker::set_parallel_id(size_t value) 85 { 86 if (nParallelID == value) 87 return; 88 nParallelID = value; 89 query_draw(); 90 } 91 set_value(float value)92 void LSPMarker::set_value(float value) 93 { 94 if (fValue == value) 95 return; 96 fValue = value; 97 query_draw(); 98 } 99 set_offset(float value)100 void LSPMarker::set_offset(float value) 101 { 102 if (fOffset == value) 103 return; 104 fOffset = value; 105 query_draw(); 106 } 107 set_angle(float value)108 void LSPMarker::set_angle(float value) 109 { 110 if (fAngle == value) 111 return; 112 fDX = cosf(value); 113 fDY = sinf(value); 114 fAngle = value; 115 query_draw(); 116 } 117 set_direction(float dx,float dy)118 void LSPMarker::set_direction(float dx, float dy) 119 { 120 fDX = dx; 121 fDY = dy; 122 fAngle = get_angle_2d(0.0f, 0.0f, dx, dy); 123 124 query_draw(); 125 } 126 set_width(size_t value)127 void LSPMarker::set_width(size_t value) 128 { 129 if (nWidth == value) 130 return; 131 nWidth = value; 132 query_draw(); 133 } 134 set_center(size_t value)135 void LSPMarker::set_center(size_t value) 136 { 137 if (nCenter == value) 138 return; 139 nCenter = value; 140 query_draw(); 141 } 142 set_border(ssize_t value)143 void LSPMarker::set_border(ssize_t value) 144 { 145 if (nBorder == value) 146 return; 147 nBorder = value; 148 query_draw(); 149 } 150 set_editable(bool value)151 void LSPMarker::set_editable(bool value) 152 { 153 size_t flags = nXFlags; 154 if (value) 155 nXFlags |= F_EDITABLE; 156 else 157 nXFlags &= ~F_EDITABLE; 158 if (flags != nXFlags) 159 query_draw(); 160 } 161 set_minimum(float value)162 void LSPMarker::set_minimum(float value) 163 { 164 if (fMin == value) 165 return; 166 fMin = value; 167 query_draw(); 168 } 169 set_maximum(float value)170 void LSPMarker::set_maximum(float value) 171 { 172 if (fMax == value) 173 return; 174 fMax = value; 175 query_draw(); 176 } 177 render(ISurface * s,bool force)178 void LSPMarker::render(ISurface *s, bool force) 179 { 180 // Get graph 181 LSPGraph *cv = graph(); 182 if (cv == NULL) 183 return; 184 185 // Prepare palette 186 Color color(sColor); 187 color.scale_lightness(brightness()); 188 189 // Get basis 190 LSPAxis *basis = cv->axis(nBasisID); 191 if (basis == NULL) 192 return; 193 LSPAxis *parallel = cv->axis(nParallelID); 194 if (parallel == NULL) 195 return; 196 197 float x = 0.0f, y = 0.0f; 198 cv->center(nCenter, &x, &y); 199 200 // Translate point and get the coordinates of point that lays on the target line 201 if (!basis->apply(&x, &y, &fValue, 1)) 202 return; 203 if (fOffset != 0.0f) 204 { 205 if (!parallel->apply(&x, &y, &fOffset, 1)) 206 return; 207 } 208 209 // Get equation of the line that contains calculated point 210 float a, b, c; 211 float nx, ny; 212 float a2, b2, c2; 213 214 if (fAngle == 0.0f) 215 { 216 if (!parallel->parallel(x, y, a, b, c)) 217 return; 218 if (nBorder != 0) 219 { 220 parallel->ortogonal_shift(x, y, nBorder, nx, ny); 221 if (!parallel->parallel(nx, ny, a2, b2, c2)) 222 return; 223 } 224 } 225 else 226 { 227 if (!parallel->angle(x, y, fAngle, a, b, c)) 228 return; 229 if (nBorder != 0) 230 { 231 parallel->rotate_shift(x, y, fAngle, nBorder, nx, ny); 232 if (!parallel->angle(x, y, fAngle, a2, b2, c2)) 233 return; 234 } 235 } 236 237 // Draw line 238 bool aa = s->set_antialiasing(bSmooth); 239 Color col(sColor, 0.0f); 240 241 ssize_t l_width = nWidth; 242 if (nXFlags & F_HIGHLIGHT) 243 l_width += 2; 244 245 if (nBorder != 0) 246 { 247 IGradient *g = s->linear_gradient(x, y, nx, ny); 248 if (g != NULL) 249 { 250 g->add_color(0.0f, color, 0.25f + 0.5f * (1.0f - color.alpha())); 251 g->add_color(1.0f, color, 1.0f); 252 253 s->parametric_bar( 254 a, b, c, a2, b2, c2, 255 cv->area_left(), cv->area_right(), cv->area_top(), cv->area_bottom(), 256 g 257 ); 258 s->parametric_line(a, b, c, cv->area_left(), cv->area_right(), cv->area_top(), cv->area_bottom(), l_width, col); 259 // s->parametric_line(a2, b2, c2, cv->area_left(), cv->area_right(), cv->area_top(), cv->area_bottom(), nWidth, col); 260 261 delete g; 262 } 263 } 264 else { 265 s->parametric_line(a, b, c, cv->area_left(), cv->area_right(), cv->area_top(), cv->area_bottom(), l_width, col); 266 } 267 s->set_antialiasing(aa); 268 269 // Debug 270 // if (nXFlags & F_EDITABLE) 271 // { 272 // float a1, b1, c1; 273 // float a2, b2, c2; 274 // float mx = nDMouseX, my = nDMouseY; 275 // float nx, ny; 276 // 277 // if (!parallel->parallel(x, y, a1, b1, c1)) 278 // return; 279 // if (!basis->parallel(mx, my, a2, b2, c2)) 280 // return; 281 // if (!line2d_intersection(a1, b1, c1, a2, b2, c2, nx, ny)) 282 // return; 283 // 284 // s->line(nx, ny, mx, my, 2, col); 285 // } 286 } 287 inside(ssize_t mx,ssize_t my)288 bool LSPMarker::inside(ssize_t mx, ssize_t my) 289 { 290 if (!(nXFlags & F_EDITABLE)) 291 return false; 292 293 // Get graph 294 LSPGraph *cv = graph(); 295 if (cv == NULL) 296 return false; 297 298 mx -= cv->canvas_left(); 299 my -= cv->canvas_top(); 300 301 // nDMouseX = mx; // DEBUG 302 // nDMouseY = my; // DEBUG 303 // query_draw(); // DEBUG 304 305 // Get basis 306 LSPAxis *basis = cv->axis(nBasisID); 307 if (basis == NULL) 308 return false; 309 LSPAxis *parallel = cv->axis(nParallelID); 310 if (parallel == NULL) 311 return false; 312 313 float x = 0.0f, y = 0.0f; 314 cv->center(nCenter, &x, &y); 315 316 // Translate point and get the coordinates of point that lays on the target line 317 if (!basis->apply(&x, &y, &fValue, 1)) 318 return false; 319 if (fOffset != 0.0f) 320 { 321 if (!parallel->apply(&x, &y, &fOffset, 1)) 322 return false; 323 } 324 325 // Get equation of the line that contains calculated point 326 float a1, b1, c1; 327 float a2, b2, c2; 328 float nx, ny; 329 // ssize_t border = (nBorder > 0) ? nBorder : -nBorder; 330 // if (border > 3) 331 // border 332 333 if (!parallel->parallel(x, y, a1, b1, c1)) 334 return false; 335 if (!basis->parallel(mx, my, a2, b2, c2)) 336 return false; 337 if (!line2d_intersection(a1, b1, c1, a2, b2, c2, nx, ny)) 338 return false; 339 340 return distance2d(nx, ny, mx, my) <= 3.0f; 341 } 342 on_mouse_in(const ws_event_t * e)343 status_t LSPMarker::on_mouse_in(const ws_event_t *e) 344 { 345 nXFlags |= F_HIGHLIGHT; 346 query_draw(); 347 348 if (!(nXFlags & F_EDITABLE)) 349 return STATUS_OK; 350 351 LSPGraph *cv = graph(); 352 if (cv == NULL) 353 return STATUS_OK; 354 LSPAxis *basis = cv->axis(nBasisID); 355 if (basis == NULL) 356 return STATUS_OK; 357 358 float x = 0.0f, y = 0.0f; 359 if (!basis->apply(&x, &y, &fValue, 1)) 360 return STATUS_OK; 361 362 if (fabs(x) > fabs(y)) 363 set_cursor(MP_HSIZE); 364 else 365 set_cursor(MP_VSIZE); 366 367 return LSPGraphItem::on_mouse_in(e); 368 } 369 on_mouse_out(const ws_event_t * e)370 status_t LSPMarker::on_mouse_out(const ws_event_t *e) 371 { 372 nXFlags &= ~F_HIGHLIGHT; 373 query_draw(); 374 lsp_trace("this = %p", this); 375 return STATUS_OK; 376 } 377 on_mouse_down(const ws_event_t * e)378 status_t LSPMarker::on_mouse_down(const ws_event_t *e) 379 { 380 if (nMouseBtn == 0) 381 { 382 if (!inside(e->nLeft, e->nTop)) 383 return STATUS_OK; 384 385 if ((e->nCode == MCB_LEFT) || (e->nCode == MCB_RIGHT)) 386 { 387 nMouseX = e->nLeft; 388 nMouseY = e->nTop; 389 390 // LSPGraph *cv = graph(); 391 // nDMouseX = (cv != NULL) ? nMouseX - cv->canvas_left() : 0; 392 // nDMouseY = (cv != NULL) ? nMouseY - cv->canvas_top() : 0; 393 // lsp_trace("dmouse = (%d, %d)", int(nDMouseX), int(nDMouseY)); 394 fLast = fValue; 395 nXFlags |= F_EDITING; 396 if (e->nCode == MCB_RIGHT) 397 nXFlags |= F_FINE_TUNE; 398 } 399 } 400 401 nMouseBtn |= 1 << e->nCode; 402 403 size_t bflag = (nXFlags & F_FINE_TUNE) ? (1 << MCB_RIGHT) : (1 << MCB_LEFT); 404 if (nMouseBtn == bflag) 405 apply_motion(e->nLeft, e->nTop); 406 else 407 apply_motion(nMouseX, nMouseY); 408 409 return STATUS_OK; 410 } 411 on_mouse_up(const ws_event_t * e)412 status_t LSPMarker::on_mouse_up(const ws_event_t *e) 413 { 414 if ((!(nXFlags & F_EDITING)) || (nMouseBtn == 0)) 415 return STATUS_OK; 416 417 size_t button = 1 << e->nCode; 418 size_t bflag = (nXFlags & F_FINE_TUNE) ? (1 << MCB_RIGHT) : (1 << MCB_LEFT); 419 420 nMouseBtn &= ~button; 421 422 if (nMouseBtn != 0) 423 { 424 if (nMouseBtn == bflag) 425 apply_motion(e->nLeft, e->nTop); 426 else 427 apply_motion(nMouseX, nMouseY); 428 } 429 else 430 { 431 if (button == bflag) 432 apply_motion(e->nLeft, e->nTop); 433 else 434 apply_motion(nMouseX, nMouseY); 435 436 nXFlags &= ~F_FINE_TUNE; 437 } 438 439 return STATUS_OK; 440 } 441 on_mouse_move(const ws_event_t * e)442 status_t LSPMarker::on_mouse_move(const ws_event_t *e) 443 { 444 if (nMouseBtn == 0) 445 return STATUS_OK; 446 447 size_t bflag = (nXFlags & F_FINE_TUNE) ? (1 << MCB_RIGHT) : (1 << MCB_LEFT); 448 449 if (nMouseBtn == bflag) 450 apply_motion(e->nLeft, e->nTop); 451 else 452 apply_motion(nMouseX, nMouseY); 453 454 return STATUS_OK; 455 } 456 apply_motion(ssize_t x,ssize_t y)457 void LSPMarker::apply_motion(ssize_t x, ssize_t y) 458 { 459 // Get graph 460 LSPGraph *cv = graph(); 461 if (cv == NULL) 462 return; 463 464 // Ignore canvas coordinates 465 466 // Get axises 467 LSPAxis *basis = cv->axis(nBasisID); 468 if (basis == NULL) 469 return; 470 LSPAxis *parallel = cv->axis(nParallelID); 471 if (parallel == NULL) 472 return; 473 474 // Update the difference relative to the sensitivity 475 lsp_trace("xy=(%d, %d), mxy=(%d, %d)", 476 int(x), int(y), int(nMouseX), int(nMouseY)); 477 478 float rx, ry; 479 if (nXFlags & F_FINE_TUNE) 480 { 481 float dx = x - nMouseX, dy = y - nMouseY; 482 rx = nMouseX - cv->canvas_left() + 0.1f * dx; 483 ry = nMouseY - cv->canvas_top() + 0.1f * dy; 484 } 485 else 486 { 487 rx = x - cv->canvas_left(); 488 ry = y - cv->canvas_top(); 489 } 490 491 lsp_trace("rxy=(%f, %f)", rx, ry); 492 493 // Modify the value according to X coordinate 494 if ((rx != 0.0f) && (ry != 0.0f)) 495 lsp_trace("debug"); 496 497 float old = fValue; 498 if ((nMouseX == x) && (nMouseY == y)) 499 fValue = fLast; 500 else if (basis != NULL) 501 fValue = basis->project(rx, ry); 502 fValue = limit_value(fValue); 503 504 // Query widget for redraw 505 if (fValue != old) 506 sSlots.execute(LSPSLOT_CHANGE, this); 507 query_draw(); 508 } 509 limit_value(float value)510 float LSPMarker::limit_value(float value) 511 { 512 if (fMin < fMax) 513 { 514 if (value < fMin) 515 value = fMin; 516 else if (value > fMax) 517 value = fMax; 518 } 519 else 520 { 521 if (value < fMax) 522 value = fMax; 523 else if (value > fMin) 524 value = fMin; 525 } 526 return value; 527 } 528 } /* namespace tk */ 529 } /* namespace lsp */ 530