1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * This file is part of openfx-supportext <https://github.com/devernay/openfx-supportext>,
4 * Copyright (C) 2013-2018 INRIA
5 *
6 * openfx-supportext is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * openfx-supportext is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with openfx-supportext. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
18 * ***** END LICENSE BLOCK ***** */
19
20 /*
21 * OFX generic rectangle interact with 4 corner points + center point and 4 mid-points.
22 * You can use it to define any rectangle in an image resizable by the user.
23 */
24
25 #include "ofxsRectangleInteract.h"
26 #include <cmath>
27
28 #ifdef __APPLE__
29 #include <OpenGL/gl.h>
30 #else
31 #include <GL/gl.h>
32 #endif
33
34 #define POINT_SIZE 5
35 #define POINT_TOLERANCE 6
36 #define CROSS_SIZE 7
37 #define HANDLE_SIZE 6
38
39 using namespace OFX;
40
41 using OFX::RectangleInteract;
42
43 static bool
isNearby(const OfxPointD & p,double x,double y,double tolerance,const OfxPointD & pscale)44 isNearby(const OfxPointD & p,
45 double x,
46 double y,
47 double tolerance,
48 const OfxPointD & pscale)
49 {
50 return std::fabs(p.x - x) <= tolerance * pscale.x && std::fabs(p.y - y) <= tolerance * pscale.y;
51 }
52
53 // round to the closest int, 1/10 int, etc
54 // this make parameter editing easier
55 // pscale is args.pixelScale.x / args.renderScale.x;
56 // pscale10 is the power of 10 below pscale
57 static double
fround(double val,double pscale)58 fround(double val,
59 double pscale)
60 {
61 if (pscale == 0) {
62 return val;
63 }
64 double pscale10 = std::pow( 10., std::floor( std::log10(pscale) ) );
65
66 return pscale10 * std::floor(val / pscale10 + 0.5);
67 }
68
69 static void
drawPoint(const OfxRGBColourD & color,bool draw,double x,double y,RectangleInteract::DrawStateEnum id,RectangleInteract::DrawStateEnum ds,bool keepAR,int l)70 drawPoint(const OfxRGBColourD &color,
71 bool draw,
72 double x,
73 double y,
74 RectangleInteract::DrawStateEnum id,
75 RectangleInteract::DrawStateEnum ds,
76 bool keepAR,
77 int l)
78 {
79 if (draw) {
80 if (ds == id) {
81 if (keepAR) {
82 glColor3f(1.f * l, 0.f * l, 0.f * l);
83 } else {
84 glColor3f(0.f * l, 1.f * l, 0.f * l);
85 }
86 } else {
87 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
88 }
89 glVertex2d(x, y);
90 }
91 }
92
93 bool
draw(const DrawArgs & args)94 RectangleInteract::draw(const DrawArgs &args)
95 {
96 if ( _btmLeft->getIsSecret() || _size->getIsSecret() ||
97 !_btmLeft->getIsEnable() || !_size->getIsEnable() ||
98 ( _enable && !_enable->getValueAtTime(args.time) ) ) {
99 return false;
100 }
101
102 OfxRGBColourD color = { 0.8, 0.8, 0.8 };
103 getSuggestedColour(color);
104 const OfxPointD& pscale = args.pixelScale;
105 GLdouble projection[16];
106 glGetDoublev( GL_PROJECTION_MATRIX, projection);
107 GLint viewport[4];
108 glGetIntegerv(GL_VIEWPORT, viewport);
109 OfxPointD shadow; // how much to translate GL_PROJECTION to get exactly one pixel on screen
110 shadow.x = 2. / (projection[0] * viewport[2]);
111 shadow.y = 2. / (projection[5] * viewport[3]);
112
113 double x1, y1, w, h;
114 if (_mouseState != eMouseStateIdle) {
115 x1 = _btmLeftDragPos.x;
116 y1 = _btmLeftDragPos.y;
117 w = _sizeDrag.x;
118 h = _sizeDrag.y;
119 } else {
120 _btmLeft->getValueAtTime(args.time, x1, y1);
121 _size->getValueAtTime(args.time, w, h);
122 }
123 double x2 = x1 + w;
124 double y2 = y1 + h;
125 double xc = x1 + w / 2;
126 double yc = y1 + h / 2;
127 const bool keepAR = _modifierStateShift > 0;
128 const bool centered = _modifierStateCtrl > 0;
129
130 //glPushAttrib(GL_ALL_ATTRIB_BITS); // caller is responsible for protecting attribs
131 aboutToCheckInteractivity(args.time);
132
133 glDisable(GL_LINE_STIPPLE);
134 glEnable(GL_LINE_SMOOTH);
135 glDisable(GL_POINT_SMOOTH);
136 glEnable(GL_BLEND);
137 glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
138 glLineWidth(1.5f);
139 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
140
141 // Draw everything twice
142 // l = 0: shadow
143 // l = 1: drawing
144 for (int l = 0; l < 2; ++l) {
145 // shadow (uses GL_PROJECTION)
146 glMatrixMode(GL_PROJECTION);
147 int direction = (l == 0) ? 1 : -1;
148 // translate (1,-1) pixels
149 glTranslated(direction * shadow.x, -direction * shadow.y, 0);
150 glMatrixMode(GL_MODELVIEW); // Modelview should be used on Nuke
151
152 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
153
154 glBegin(GL_LINE_LOOP);
155 glVertex2d(x1, y1);
156 glVertex2d(x1, y2);
157 glVertex2d(x2, y2);
158 glVertex2d(x2, y1);
159 glEnd();
160
161 glPointSize(POINT_SIZE);
162 glBegin(GL_POINTS);
163 drawPoint(color, allowBtmLeftInteraction(), x1, y1, eDrawStateHoveringBtmLeft, _drawState, keepAR, l);
164 drawPoint(color, allowMidLeftInteraction(), x1, yc, eDrawStateHoveringMidLeft, _drawState, false, l);
165 drawPoint(color, allowTopLeftInteraction(), x1, y2, eDrawStateHoveringTopLeft, _drawState, keepAR, l);
166 drawPoint(color, allowBtmMidInteraction(), xc, y1, eDrawStateHoveringBtmMid, _drawState, false, l);
167 drawPoint(color, allowCenterInteraction(), xc, yc, eDrawStateHoveringCenter, _drawState, false, l);
168 drawPoint(color, allowTopMidInteraction(), xc, y2, eDrawStateHoveringTopMid, _drawState, false, l);
169 drawPoint(color, allowBtmRightInteraction(), x2, y1, eDrawStateHoveringBtmRight, _drawState, keepAR, l);
170 drawPoint(color, allowMidRightInteraction(), x2, yc, eDrawStateHoveringMidRight, _drawState, false, l);
171 drawPoint(color, allowTopRightInteraction(), x2, y2, eDrawStateHoveringTopRight, _drawState, keepAR, l);
172 glEnd();
173 glPointSize(1);
174
175 ///draw center cross hair
176 glBegin(GL_LINES);
177 if ( (_drawState == eDrawStateHoveringCenter) || ( centered && (_drawState != eDrawStateInactive) ) ) {
178 glColor3f(0.f * l, 1.f * l, 0.f * l);
179 } else if ( !allowCenterInteraction() ) {
180 glColor3f( (float)(color.r / 2) * l, (float)(color.g / 2) * l, (float)(color.b / 2) * l );
181 } else {
182 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
183 }
184 glVertex2d(xc - CROSS_SIZE * pscale.x, yc);
185 glVertex2d(xc + CROSS_SIZE * pscale.x, yc);
186 glVertex2d(xc, yc - CROSS_SIZE * pscale.y);
187 glVertex2d(xc, yc + CROSS_SIZE * pscale.y);
188 glEnd();
189 }
190 //glPopAttrib();
191
192 return true;
193 } // draw
194
195 bool
penMotion(const PenArgs & args)196 RectangleInteract::penMotion(const PenArgs &args)
197 {
198 if ( _btmLeft->getIsSecret() || _size->getIsSecret() ||
199 !_btmLeft->getIsEnable() || !_size->getIsEnable() ||
200 ( _enable && !_enable->getValueAtTime(args.time) ) ) {
201 return false;
202 }
203
204 const OfxPointD& pscale = args.pixelScale;
205 double x1, y1, w, h;
206 if (_mouseState != eMouseStateIdle) {
207 x1 = _btmLeftDragPos.x;
208 y1 = _btmLeftDragPos.y;
209 w = _sizeDrag.x;
210 h = _sizeDrag.y;
211 } else {
212 _btmLeft->getValueAtTime(args.time, x1, y1);
213 _size->getValueAtTime(args.time, w, h);
214 }
215 double x2 = x1 + w;
216 double y2 = y1 + h;
217 double xc = x1 + w / 2;
218 double yc = y1 + h / 2;
219 bool didSomething = false;
220 bool valuesChanged = false;
221 OfxPointD delta;
222 delta.x = args.penPosition.x - _lastMousePos.x;
223 delta.y = args.penPosition.y - _lastMousePos.y;
224
225 bool lastStateWasHovered = _drawState != eDrawStateInactive;
226
227
228 aboutToCheckInteractivity(args.time);
229 // test center first
230 if ( isNearby(args.penPosition, xc, yc, POINT_TOLERANCE, pscale) && allowCenterInteraction() ) {
231 _drawState = eDrawStateHoveringCenter;
232 didSomething = true;
233 } else if ( isNearby(args.penPosition, x1, y1, POINT_TOLERANCE, pscale) && allowBtmLeftInteraction() ) {
234 _drawState = eDrawStateHoveringBtmLeft;
235 didSomething = true;
236 } else if ( isNearby(args.penPosition, x2, y1, POINT_TOLERANCE, pscale) && allowBtmRightInteraction() ) {
237 _drawState = eDrawStateHoveringBtmRight;
238 didSomething = true;
239 } else if ( isNearby(args.penPosition, x1, y2, POINT_TOLERANCE, pscale) && allowTopLeftInteraction() ) {
240 _drawState = eDrawStateHoveringTopLeft;
241 didSomething = true;
242 } else if ( isNearby(args.penPosition, x2, y2, POINT_TOLERANCE, pscale) && allowTopRightInteraction() ) {
243 _drawState = eDrawStateHoveringTopRight;
244 didSomething = true;
245 } else if ( isNearby(args.penPosition, xc, y1, POINT_TOLERANCE, pscale) && allowBtmMidInteraction() ) {
246 _drawState = eDrawStateHoveringBtmMid;
247 didSomething = true;
248 } else if ( isNearby(args.penPosition, xc, y2, POINT_TOLERANCE, pscale) && allowTopMidInteraction() ) {
249 _drawState = eDrawStateHoveringTopMid;
250 didSomething = true;
251 } else if ( isNearby(args.penPosition, x1, yc, POINT_TOLERANCE, pscale) && allowMidLeftInteraction() ) {
252 _drawState = eDrawStateHoveringMidLeft;
253 didSomething = true;
254 } else if ( isNearby(args.penPosition, x2, yc, POINT_TOLERANCE, pscale) && allowMidRightInteraction() ) {
255 _drawState = eDrawStateHoveringMidRight;
256 didSomething = true;
257 } else {
258 _drawState = eDrawStateInactive;
259 }
260
261 const bool keepAR = _modifierStateShift > 0;
262 const bool centered = _modifierStateCtrl > 0;
263 if ( keepAR && (_sizeDrag.x > 0.) && (_sizeDrag.y > 0.) &&
264 ( ( _mouseState == eMouseStateDraggingTopLeft) ||
265 ( _mouseState == eMouseStateDraggingTopRight) ||
266 ( _mouseState == eMouseStateDraggingBtmLeft) ||
267 ( _mouseState == eMouseStateDraggingBtmRight) ) ) {
268 double r2 = _sizeDrag.x * _sizeDrag.x + _sizeDrag.y * _sizeDrag.y;
269 if ( (_mouseState == eMouseStateDraggingTopRight) ||
270 ( _mouseState == eMouseStateDraggingBtmLeft) ) {
271 double dotprod = (delta.x * _sizeDrag.y + delta.y * _sizeDrag.x) / r2;
272 delta.x = _sizeDrag.x * dotprod;
273 delta.y = _sizeDrag.y * dotprod;
274 } else {
275 double dotprod = (delta.x * _sizeDrag.y - delta.y * _sizeDrag.x) / r2;
276 delta.x = _sizeDrag.x * dotprod;
277 delta.y = -_sizeDrag.y * dotprod;
278 }
279 }
280 if (_mouseState == eMouseStateDraggingBtmLeft) {
281 _drawState = eDrawStateHoveringBtmLeft;
282 OfxPointD topRight;
283 topRight.x = _btmLeftDragPos.x + _sizeDrag.x;
284 topRight.y = _btmLeftDragPos.y + _sizeDrag.y;
285 _btmLeftDragPos.x += delta.x;
286 _btmLeftDragPos.y += delta.y;
287 _sizeDrag.x = topRight.x - _btmLeftDragPos.x;
288 _sizeDrag.y = topRight.y - _btmLeftDragPos.y;
289 if (centered) {
290 _sizeDrag.x -= delta.x;
291 _sizeDrag.y -= delta.y;
292 }
293 valuesChanged = true;
294 } else if (_mouseState == eMouseStateDraggingTopLeft) {
295 _drawState = eDrawStateHoveringTopLeft;
296 OfxPointD btmRight;
297 btmRight.x = _btmLeftDragPos.x + _sizeDrag.x;
298 btmRight.y = _btmLeftDragPos.y;
299 _btmLeftDragPos.x += delta.x;
300 _sizeDrag.y += delta.y;
301 _sizeDrag.x = btmRight.x - _btmLeftDragPos.x;
302 if (centered) {
303 _sizeDrag.x -= delta.x;
304 _sizeDrag.y += delta.y;
305 _btmLeftDragPos.y -= delta.y;
306 }
307 valuesChanged = true;
308 } else if (_mouseState == eMouseStateDraggingTopRight) {
309 _drawState = eDrawStateHoveringTopRight;
310 _sizeDrag.x += delta.x;
311 _sizeDrag.y += delta.y;
312 if (centered) {
313 _sizeDrag.x += delta.x;
314 _btmLeftDragPos.x -= delta.x;
315 _sizeDrag.y += delta.y;
316 _btmLeftDragPos.y -= delta.y;
317 }
318 valuesChanged = true;
319 } else if (_mouseState == eMouseStateDraggingBtmRight) {
320 _drawState = eDrawStateHoveringBtmRight;
321 OfxPointD topLeft;
322 topLeft.x = _btmLeftDragPos.x;
323 topLeft.y = _btmLeftDragPos.y + _sizeDrag.y;
324 _sizeDrag.x += delta.x;
325 _btmLeftDragPos.y += delta.y;
326 _sizeDrag.y = topLeft.y - _btmLeftDragPos.y;
327 if (centered) {
328 _sizeDrag.x += delta.x;
329 _btmLeftDragPos.x -= delta.x;
330 _sizeDrag.y -= delta.y;
331 }
332 valuesChanged = true;
333 } else if (_mouseState == eMouseStateDraggingTopMid) {
334 _drawState = eDrawStateHoveringTopMid;
335 _sizeDrag.y += delta.y;
336 if (centered) {
337 _sizeDrag.y += delta.y;
338 _btmLeftDragPos.y -= delta.y;
339 }
340 valuesChanged = true;
341 } else if (_mouseState == eMouseStateDraggingMidRight) {
342 _drawState = eDrawStateHoveringMidRight;
343 _sizeDrag.x += delta.x;
344 if (centered) {
345 _sizeDrag.x += delta.x;
346 _btmLeftDragPos.x -= delta.x;
347 }
348 valuesChanged = true;
349 } else if (_mouseState == eMouseStateDraggingBtmMid) {
350 _drawState = eDrawStateHoveringBtmMid;
351 double top = _btmLeftDragPos.y + _sizeDrag.y;
352 _btmLeftDragPos.y += delta.y;
353 _sizeDrag.y = top - _btmLeftDragPos.y;
354 if (centered) {
355 _sizeDrag.y -= delta.y;
356 }
357 valuesChanged = true;
358 } else if (_mouseState == eMouseStateDraggingMidLeft) {
359 _drawState = eDrawStateHoveringMidLeft;
360 double right = _btmLeftDragPos.x + _sizeDrag.x;
361 _btmLeftDragPos.x += delta.x;
362 _sizeDrag.x = right - _btmLeftDragPos.x;
363 if (centered) {
364 _sizeDrag.x -= delta.x;
365 }
366 valuesChanged = true;
367 } else if (_mouseState == eMouseStateDraggingCenter) {
368 _drawState = eDrawStateHoveringCenter;
369 _btmLeftDragPos.x += delta.x;
370 _btmLeftDragPos.y += delta.y;
371 valuesChanged = true;
372 }
373
374
375 //if size is negative shift bottom left
376 if (_sizeDrag.x < 0) {
377 if (_mouseState == eMouseStateDraggingBtmLeft) {
378 _mouseState = eMouseStateDraggingBtmRight;
379 } else if (_mouseState == eMouseStateDraggingMidLeft) {
380 _mouseState = eMouseStateDraggingMidRight;
381 } else if (_mouseState == eMouseStateDraggingTopLeft) {
382 _mouseState = eMouseStateDraggingTopRight;
383 } else if (_mouseState == eMouseStateDraggingBtmRight) {
384 _mouseState = eMouseStateDraggingBtmLeft;
385 } else if (_mouseState == eMouseStateDraggingMidRight) {
386 _mouseState = eMouseStateDraggingMidLeft;
387 } else if (_mouseState == eMouseStateDraggingTopRight) {
388 _mouseState = eMouseStateDraggingTopLeft;
389 }
390
391 _btmLeftDragPos.x += _sizeDrag.x;
392 _sizeDrag.x = -_sizeDrag.x;
393 valuesChanged = true;
394 }
395 if (_sizeDrag.y < 0) {
396 if (_mouseState == eMouseStateDraggingTopLeft) {
397 _mouseState = eMouseStateDraggingBtmLeft;
398 } else if (_mouseState == eMouseStateDraggingTopMid) {
399 _mouseState = eMouseStateDraggingBtmMid;
400 } else if (_mouseState == eMouseStateDraggingTopRight) {
401 _mouseState = eMouseStateDraggingBtmRight;
402 } else if (_mouseState == eMouseStateDraggingBtmLeft) {
403 _mouseState = eMouseStateDraggingTopLeft;
404 } else if (_mouseState == eMouseStateDraggingBtmMid) {
405 _mouseState = eMouseStateDraggingTopMid;
406 } else if (_mouseState == eMouseStateDraggingBtmRight) {
407 _mouseState = eMouseStateDraggingTopRight;
408 }
409
410 _btmLeftDragPos.y += _sizeDrag.y;
411 _sizeDrag.y = -_sizeDrag.y;
412 valuesChanged = true;
413 }
414
415 ///forbid 0 pixels wide crop rectangles
416 if (_sizeDrag.x < 1) {
417 _sizeDrag.x = 1;
418 valuesChanged = true;
419 }
420 if (_sizeDrag.y < 1) {
421 _sizeDrag.y = 1;
422 valuesChanged = true;
423 }
424
425 ///repaint if we toggled off a hovered handle
426 if (lastStateWasHovered) {
427 didSomething = true;
428 }
429
430 if ( (_mouseState != eMouseStateIdle) && _interactiveDrag && valuesChanged ) {
431 setValue(_btmLeftDragPos, _sizeDrag, args.pixelScale);
432 // no need to redraw overlay since it is slave to the paramaters
433 } else if (didSomething || valuesChanged) {
434 requestRedraw();
435 }
436
437
438 _lastMousePos = args.penPosition;
439
440 return didSomething || valuesChanged;
441 } // penMotion
442
443 bool
penDown(const PenArgs & args)444 RectangleInteract::penDown(const PenArgs &args)
445 {
446 if ( _btmLeft->getIsSecret() || _size->getIsSecret() ||
447 !_btmLeft->getIsEnable() || !_size->getIsEnable() ||
448 ( _enable && !_enable->getValueAtTime(args.time) ) ) {
449 return false;
450 }
451
452 const OfxPointD& pscale = args.pixelScale;
453 double x1, y1, w, h;
454 if (_mouseState != eMouseStateIdle) {
455 x1 = _btmLeftDragPos.x;
456 y1 = _btmLeftDragPos.y;
457 w = _sizeDrag.x;
458 h = _sizeDrag.y;
459 } else {
460 _btmLeft->getValueAtTime(args.time, x1, y1);
461 _size->getValueAtTime(args.time, w, h);
462 if ( _interactive && _interactive->getIsEnable() ) {
463 _interactive->getValueAtTime(args.time, _interactiveDrag);
464 } else {
465 _interactiveDrag = false;
466 }
467 }
468 double x2 = x1 + w;
469 double y2 = y1 + h;
470 double xc = x1 + w / 2;
471 double yc = y1 + h / 2;
472 bool didSomething = false;
473
474 aboutToCheckInteractivity(args.time);
475
476 // test center first
477 if ( isNearby(args.penPosition, xc, yc, POINT_TOLERANCE, pscale) && allowCenterInteraction() ) {
478 _mouseState = eMouseStateDraggingCenter;
479 didSomething = true;
480 } else if ( isNearby(args.penPosition, x1, y1, POINT_TOLERANCE, pscale) && allowBtmLeftInteraction() ) {
481 _mouseState = eMouseStateDraggingBtmLeft;
482 didSomething = true;
483 } else if ( isNearby(args.penPosition, x2, y1, POINT_TOLERANCE, pscale) && allowBtmRightInteraction() ) {
484 _mouseState = eMouseStateDraggingBtmRight;
485 didSomething = true;
486 } else if ( isNearby(args.penPosition, x1, y2, POINT_TOLERANCE, pscale) && allowTopLeftInteraction() ) {
487 _mouseState = eMouseStateDraggingTopLeft;
488 didSomething = true;
489 } else if ( isNearby(args.penPosition, x2, y2, POINT_TOLERANCE, pscale) && allowTopRightInteraction() ) {
490 _mouseState = eMouseStateDraggingTopRight;
491 didSomething = true;
492 } else if ( isNearby(args.penPosition, xc, y1, POINT_TOLERANCE, pscale) && allowBtmMidInteraction() ) {
493 _mouseState = eMouseStateDraggingBtmMid;
494 didSomething = true;
495 } else if ( isNearby(args.penPosition, xc, y2, POINT_TOLERANCE, pscale) && allowTopMidInteraction() ) {
496 _mouseState = eMouseStateDraggingTopMid;
497 didSomething = true;
498 } else if ( isNearby(args.penPosition, x1, yc, POINT_TOLERANCE, pscale) && allowMidLeftInteraction() ) {
499 _mouseState = eMouseStateDraggingMidLeft;
500 didSomething = true;
501 } else if ( isNearby(args.penPosition, x2, yc, POINT_TOLERANCE, pscale) && allowMidRightInteraction() ) {
502 _mouseState = eMouseStateDraggingMidRight;
503 didSomething = true;
504 } else {
505 _mouseState = eMouseStateIdle;
506 }
507
508 _btmLeftDragPos.x = x1;
509 _btmLeftDragPos.y = y1;
510 _sizeDrag.x = w;
511 _sizeDrag.y = h;
512 _lastMousePos = args.penPosition;
513 if (didSomething) {
514 requestRedraw();
515 }
516
517 return didSomething;
518 } // penDown
519
520 bool
penUp(const PenArgs & args)521 RectangleInteract::penUp(const PenArgs &args)
522 {
523 if ( _btmLeft->getIsSecret() || _size->getIsSecret() ||
524 !_btmLeft->getIsEnable() || !_size->getIsEnable() ||
525 ( _enable && !_enable->getValueAtTime(args.time) ) ) {
526 return false;
527 }
528
529 bool didSmthing = false;
530
531 if ( !_interactiveDrag && (_mouseState != eMouseStateIdle) ) {
532 // no need to redraw overlay since it is slave to the paramaters
533 setValue(_btmLeftDragPos, _sizeDrag, args.pixelScale);
534 didSmthing = true;
535 } else if (_mouseState != eMouseStateIdle) {
536 requestRedraw();
537 }
538 _mouseState = eMouseStateIdle;
539
540 return didSmthing;
541 } // penUp
542
543 // keyDown just updates the modifier state
544 bool
keyDown(const KeyArgs & args)545 RectangleInteract::keyDown(const KeyArgs &args)
546 {
547 if ( _btmLeft->getIsSecret() || _size->getIsSecret() ||
548 !_btmLeft->getIsEnable() || !_size->getIsEnable() ||
549 ( _enable && !_enable->getValueAtTime(args.time) ) ) {
550 return false;
551 }
552
553 // Note that on the Mac:
554 // cmd/apple/cloverleaf is kOfxKey_Control_L
555 // ctrl is kOfxKey_Meta_L
556 // alt/option is kOfxKey_Alt_L
557 bool mustRedraw = false;
558
559 // the two control keys may be pressed consecutively, be aware about this
560 if ( (args.keySymbol == kOfxKey_Control_L) || (args.keySymbol == kOfxKey_Control_R) ) {
561 mustRedraw = _modifierStateCtrl == 0;
562 ++_modifierStateCtrl;
563 }
564 if ( (args.keySymbol == kOfxKey_Shift_L) || (args.keySymbol == kOfxKey_Shift_R) ) {
565 mustRedraw = _modifierStateShift == 0;
566 ++_modifierStateShift;
567 }
568 if (mustRedraw) {
569 requestRedraw();
570 }
571 //std::cout << std::hex << args.keySymbol << std::endl;
572
573 // modifiers are not "caught"
574 return false;
575 }
576
577 // keyUp just updates the modifier state
578 bool
keyUp(const KeyArgs & args)579 RectangleInteract::keyUp(const KeyArgs &args)
580 {
581 if ( _btmLeft->getIsSecret() || _size->getIsSecret() ||
582 !_btmLeft->getIsEnable() || !_size->getIsEnable() ||
583 ( _enable && !_enable->getValueAtTime(args.time) ) ) {
584 return false;
585 }
586
587 bool mustRedraw = false;
588
589 if ( (args.keySymbol == kOfxKey_Control_L) || (args.keySymbol == kOfxKey_Control_R) ) {
590 // we may have missed a keypress
591 if (_modifierStateCtrl > 0) {
592 --_modifierStateCtrl;
593 mustRedraw = _modifierStateCtrl == 0;
594 }
595 }
596 if ( (args.keySymbol == kOfxKey_Shift_L) || (args.keySymbol == kOfxKey_Shift_R) ) {
597 if (_modifierStateShift > 0) {
598 --_modifierStateShift;
599 mustRedraw = _modifierStateShift == 0;
600 }
601 }
602 if (mustRedraw) {
603 requestRedraw();
604 }
605
606 // modifiers are not "caught"
607 return false;
608 }
609
610 /** @brief Called when the interact is loses input focus */
611 void
loseFocus(const FocusArgs &)612 RectangleInteract::loseFocus(const FocusArgs & /*args*/)
613 {
614 // reset the modifiers state
615 _modifierStateCtrl = 0;
616 _modifierStateShift = 0;
617 _interactiveDrag = false;
618 }
619
620 OfxPointD
getBtmLeft(OfxTime time) const621 RectangleInteract::getBtmLeft(OfxTime time) const
622 {
623 OfxPointD ret;
624
625 _btmLeft->getValueAtTime(time, ret.x, ret.y);
626
627 return ret;
628 }
629
630 void
setValue(OfxPointD btmLeft,OfxPointD size,const OfxPointD & pscale)631 RectangleInteract::setValue(OfxPointD btmLeft,
632 OfxPointD size,
633 const OfxPointD &pscale)
634 {
635 bool setBtmLeft = false;
636 bool setSize = false;
637 // round newx/y to the closest int, 1/10 int, etc
638 // this make parameter editing easier
639 switch (_mouseState) {
640 case eMouseStateIdle:
641 break;
642 case eMouseStateDraggingTopLeft:
643 btmLeft.x = fround(btmLeft.x, pscale.x);
644 size.x = fround(size.x, pscale.x);
645 size.y = fround(size.y, pscale.y);
646 setBtmLeft = setSize = true;
647 break;
648 case eMouseStateDraggingTopRight:
649 size.x = fround(size.x, pscale.x);
650 size.y = fround(size.y, pscale.y);
651 setSize = true;
652 break;
653 case eMouseStateDraggingBtmLeft:
654 btmLeft.x = fround(btmLeft.x, pscale.x);
655 btmLeft.y = fround(btmLeft.y, pscale.y);
656 size.x = fround(size.x, pscale.x);
657 size.y = fround(size.y, pscale.y);
658 setBtmLeft = setSize = true;
659 break;
660 case eMouseStateDraggingBtmRight:
661 size.x = fround(size.x, pscale.x);
662 size.y = fround(size.y, pscale.y);
663 btmLeft.y = fround(btmLeft.y, pscale.y);
664 setBtmLeft = setSize = true;
665 break;
666 case eMouseStateDraggingCenter:
667 btmLeft.x = fround(btmLeft.x, pscale.x);
668 btmLeft.y = fround(btmLeft.y, pscale.y);
669 setBtmLeft = true;
670 break;
671 case eMouseStateDraggingTopMid:
672 size.y = fround(size.y, pscale.y);
673 setSize = true;
674 break;
675 case eMouseStateDraggingMidRight:
676 size.x = fround(size.x, pscale.x);
677 setSize = true;
678 break;
679 case eMouseStateDraggingBtmMid:
680 btmLeft.y = fround(btmLeft.y, pscale.y);
681 setBtmLeft = true;
682 break;
683 case eMouseStateDraggingMidLeft:
684 btmLeft.x = fround(btmLeft.x, pscale.x);
685 setBtmLeft = true;
686 break;
687 }
688 bool editBlock = setBtmLeft + setSize > 1;
689 if (editBlock) {
690 _effect->beginEditBlock("setRectangle");
691 }
692 _btmLeft->setValue(btmLeft.x, btmLeft.y);
693 _size->setValue(size.x, size.y);
694 if (editBlock) {
695 _effect->endEditBlock();
696 }
697 } // penDown
698