1 /*
2 * Copyright (C) 2002 - David W. Durham
3 *
4 * This file is part of ReZound, an audio editing application.
5 *
6 * ReZound is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License,
9 * or (at your option) any later version.
10 *
11 * ReZound is distributed in the hope that it will be useful, but
12 * 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21 #include "FXRezWaveView.h"
22
23 #include "FXWaveScrollArea.h"
24 #include "FXPopupHint.h"
25
26 #include "CSoundFileManager.h"
27 #include "CSoundWindow.h"
28
29 #include "../backend/CActionParameters.h"
30 #include "../backend/Edits/CCueAction.h"
31 #include "../backend/Edits/CSelectionEdit.h"
32
33 #include <string>
34 #include <algorithm>
35
36 #include <istring>
37
38 #include "settings.h"
39
40 // --- declaration of FXWaveRuler -----------------------------------------------
41
42 class FXWaveRuler : public FXComposite
43 {
44 FXDECLARE(FXWaveRuler)
45 public:
46 FXWaveRuler(FXComposite *p,FXRezWaveView *parent,CLoadedSound *_loadedSound);
47 virtual ~FXWaveRuler();
48
49 virtual void create();
50
51 virtual fx_bool canFocus() const;
52
53
54 size_t getClickedCue(FXint x,FXint y);
55
56 // events I get by message
57
58 long onPaint(FXObject *object,FXSelector sel,void *ptr);
59
60 long onPopupMenu(FXObject *object,FXSelector sel,void *ptr);
61
62 long onFindStartPosition(FXObject *object,FXSelector sel,void *ptr);
63 long onFindStopPosition(FXObject *object,FXSelector sel,void *ptr);
64
65 long onSetPositionToCue(FXObject *object,FXSelector sel,void *ptr);
66 long onAddCue(FXObject *object,FXSelector sel,void *ptr);
67 long onRemoveCue(FXObject *object,FXSelector sel,void *ptr);
68 long onEditCue(FXObject *object,FXSelector sel,void *ptr);
69 long onShowCueList(FXObject *object,FXSelector sel,void *ptr);
70
71 long onLeftBtnPress(FXObject *object,FXSelector sel,void *ptr);
72 long onMouseMove(FXObject *object,FXSelector sel,void *ptr);
73 long onLeftBtnRelease(FXObject *object,FXSelector sel,void *ptr);
74
75 long onFocusIn(FXObject* sender,FXSelector sel,void* ptr);
76 long onFocusOut(FXObject* sender,FXSelector sel,void* ptr);
77
78 long onKeyPress(FXObject *object,FXSelector sel,void *ptr);
79
80 // methods get information about and for altering which cue is focused
81 void focusNextCue();
82 void focusPrevCue();
83 void focusFirstCue();
84 void focusLastCue();
85
86 enum
87 {
88 ID_FIND_START_POSITION=FXComposite::ID_LAST,
89 ID_FIND_STOP_POSITION,
90
91 ID_SET_START_POSITION,
92 ID_SET_STOP_POSITION,
93
94 ID_ADD_CUE,
95 ID_ADD_CUE_AT_START_POSITION,
96 ID_ADD_CUE_AT_STOP_POSITION,
97 ID_REMOVE_CUE,
98 ID_EDIT_CUE,
99
100 ID_SHOW_CUE_LIST,
101
102 ID_POPUP_MENU,
103
104 ID_LAST
105 };
106
107 protected:
FXWaveRuler()108 FXWaveRuler() {}
109
110 private:
111 FXRezWaveView *parent;
112
113 CLoadedSound *loadedSound;
114 CSound *sound;
115
116 FXFont *font;
117
118 #define NIL_CUE_INDEX (0xffffffff)
119 size_t cueClicked; // the index of the cue clicked on holding the value between the click event and the menu item event
120 int cueClickedOffset; // used when dragging cues to know how far from the middle a cue was clicked
121 sample_pos_t origCueClickedTime;
122 sample_pos_t origStartPosition; // start position before drag cue start (for undo purposes)
123 sample_pos_t origStopPosition; // stop position before drag cue start (for undo purposes)
124 sample_pos_t addCueTime; // the time in the audio where the mouse was clicked to add a cue if that's what they choose
125
126 size_t focusedCueIndex; // NIL_CUE_INDEX if none focused
127 sample_pos_t focusedCueTime;
128
129 FXPopupHint *dragCueHint;
130
131 bool draggingSelectionToo; // true when a cue to be dragged was on the start or stop position
132
133 CSelectionEditPositionFactory *selectionEditPositionFactory;
134 CMoveCueActionFactory *moveCueActionFactory;
135 CRemoveCueActionFactory *removeCueActionFactory;
136 };
137
138
139
140
141 // --- FXRezWaveView ---------------------------------------------------------
142
143 FXDEFMAP(FXRezWaveView) FXRezWaveViewMap[]=
144 {
145 //Message_Type ID Message_Handler
146 //FXMAPFUNC(SEL_TIMEOUT, FXWindow::ID_AUTOSCROLL, FXRezWaveView::onAutoScroll),
147 };
148
FXIMPLEMENT(FXRezWaveView,FXPacker,FXRezWaveViewMap,ARRAYNUMBER (FXRezWaveViewMap))149 FXIMPLEMENT(FXRezWaveView,FXPacker,FXRezWaveViewMap,ARRAYNUMBER(FXRezWaveViewMap))
150
151 FXRezWaveView::FXRezWaveView(FXComposite* p,CLoadedSound *_loadedSound) :
152 FXPacker(p,FRAME_NONE|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0, 0,0),
153
154 rulerPanel(new FXWaveRuler(this,this,_loadedSound)),
155 waveScrollArea(new FXWaveScrollArea(this,_loadedSound))
156 {
157 enable();
158 }
159
~FXRezWaveView()160 FXRezWaveView::~FXRezWaveView()
161 {
162 }
163
setHorzZoom(double v,FXWaveCanvas::HorzRecenterTypes horzRecenterType)164 void FXRezWaveView::setHorzZoom(double v,FXWaveCanvas::HorzRecenterTypes horzRecenterType)
165 {
166 waveScrollArea->setHorzZoom(v,horzRecenterType);
167 updateRuler();
168 }
169
getHorzZoom() const170 double FXRezWaveView::getHorzZoom() const
171 {
172 return waveScrollArea->getHorzZoom();
173 }
174
setVertZoom(float v)175 void FXRezWaveView::setVertZoom(float v)
176 {
177 waveScrollArea->setVertZoom(v);
178 }
179
getVertZoom() const180 float FXRezWaveView::getVertZoom() const
181 {
182 return waveScrollArea->getVertZoom();
183 }
184
setHorzOffset(const sample_pos_t offset)185 void FXRezWaveView::setHorzOffset(const sample_pos_t offset)
186 {
187 waveScrollArea->setHorzOffset(offset);
188 }
189
getHorzOffset() const190 sample_pos_t FXRezWaveView::getHorzOffset() const
191 {
192 return waveScrollArea->getHorzOffset();
193 }
194
setVertOffset(const int offset)195 void FXRezWaveView::setVertOffset(const int offset)
196 {
197 waveScrollArea->setVertOffset(offset);
198 }
199
getVertOffset() const200 int FXRezWaveView::getVertOffset() const
201 {
202 return waveScrollArea->getVertOffset();
203 }
204
205
drawPlayPosition(sample_pos_t dataPosition,bool justErasing,bool scrollToMakeVisible)206 void FXRezWaveView::drawPlayPosition(sample_pos_t dataPosition,bool justErasing,bool scrollToMakeVisible)
207 {
208 waveScrollArea->drawPlayPosition(dataPosition,justErasing,scrollToMakeVisible);
209 }
210
redraw()211 void FXRezWaveView::redraw()
212 {
213 update();
214 waveScrollArea->redraw();
215 rulerPanel->update();
216 }
217
centerStartPos()218 void FXRezWaveView::centerStartPos()
219 {
220 waveScrollArea->centerStartPos();
221 rulerPanel->update();
222 }
223
centerStopPos()224 void FXRezWaveView::centerStopPos()
225 {
226 waveScrollArea->centerStopPos();
227 rulerPanel->update();
228 }
229
showAmount(double seconds,sample_pos_t pos,int marginPixels)230 void FXRezWaveView::showAmount(double seconds,sample_pos_t pos,int marginPixels)
231 {
232 waveScrollArea->showAmount(seconds,pos,marginPixels);
233 }
234
updateFromSelectionChange(FXWaveCanvas::LastChangedPositions lastChangedPosition)235 void FXRezWaveView::updateFromSelectionChange(FXWaveCanvas::LastChangedPositions lastChangedPosition)
236 {
237 waveScrollArea->updateFromSelectionChange(lastChangedPosition);
238 }
239
updateFromEdit(bool undoing)240 void FXRezWaveView::updateFromEdit(bool undoing)
241 {
242 waveScrollArea->updateFromEdit(undoing);
243 update();
244 rulerPanel->update();
245 }
246
updateRuler()247 void FXRezWaveView::updateRuler()
248 {
249 rulerPanel->update();
250 }
251
updateRulerFromScroll(int deltaX,FXEvent * event)252 void FXRezWaveView::updateRulerFromScroll(int deltaX,FXEvent *event)
253 {
254 FXEvent e(*event);
255 e.last_x+=deltaX;
256 rulerPanel->onMouseMove(NULL,0,&e);
257 }
258
getSamplePosForScreenX(FXint X) const259 sample_pos_t FXRezWaveView::getSamplePosForScreenX(FXint X) const
260 {
261 return waveScrollArea->getSamplePosForScreenX(X);
262 }
263
getWaveSize(int & top,int & height)264 void FXRezWaveView::getWaveSize(int &top,int &height)
265 {
266 top=waveScrollArea->getY();
267 height=waveScrollArea->getHeight()-waveScrollArea->horizontalScrollBar()->getHeight();
268 }
269
270
271
272
273
274
275 // --- FXWaveRuler -------------------------------------------------------
276
277 #include "../backend/CLoadedSound.h"
278 #include "../backend/CSound.h"
279 #include "../backend/CSoundPlayerChannel.h"
280
281 /*
282 * ??? Split this widget into a base class and a derived class so that the base class could be easily
283 * used above other wave renderings the challenge will be eliminating the need for relying on parent
284 * in the base class
285 */
286
287 FXDEFMAP(FXWaveRuler) FXWaveRulerMap[]=
288 {
289 //Message_Type ID Message_Handler
290 FXMAPFUNC(SEL_RIGHTBUTTONPRESS, 0, FXWaveRuler::onPopupMenu),
291
292 FXMAPFUNC(SEL_PAINT, 0, FXWaveRuler::onPaint),
293
294 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_FIND_START_POSITION, FXWaveRuler::onFindStartPosition),
295 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_FIND_STOP_POSITION, FXWaveRuler::onFindStopPosition),
296
297 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_SET_START_POSITION, FXWaveRuler::onSetPositionToCue),
298 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_SET_STOP_POSITION, FXWaveRuler::onSetPositionToCue),
299
300 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_ADD_CUE, FXWaveRuler::onAddCue),
301 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_ADD_CUE_AT_START_POSITION, FXWaveRuler::onAddCue),
302 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_ADD_CUE_AT_STOP_POSITION, FXWaveRuler::onAddCue),
303 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_REMOVE_CUE, FXWaveRuler::onRemoveCue),
304 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_EDIT_CUE, FXWaveRuler::onEditCue),
305
306 FXMAPFUNC(SEL_COMMAND, FXWaveRuler::ID_SHOW_CUE_LIST, FXWaveRuler::onShowCueList),
307
308 // these handle drag, mouse click for focus, and mouse double click for edit cue(s)
309 FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, FXWaveRuler::onLeftBtnPress),
310 FXMAPFUNC(SEL_MOTION, 0, FXWaveRuler::onMouseMove),
311 FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, FXWaveRuler::onLeftBtnRelease),
312
313 FXMAPFUNC(SEL_KEYPRESS, 0, FXWaveRuler::onKeyPress),
314
315
316 FXMAPFUNC(SEL_FOCUSIN, 0, FXWaveRuler::onFocusIn),
317 FXMAPFUNC(SEL_FOCUSOUT, 0, FXWaveRuler::onFocusOut),
318
319 };
320
FXIMPLEMENT(FXWaveRuler,FXComposite,FXWaveRulerMap,ARRAYNUMBER (FXWaveRulerMap))321 FXIMPLEMENT(FXWaveRuler,FXComposite,FXWaveRulerMap,ARRAYNUMBER(FXWaveRulerMap))
322
323
324 FXWaveRuler::FXWaveRuler(FXComposite *p,FXRezWaveView *_parent,CLoadedSound *_loadedSound) :
325 FXComposite(p,FRAME_NONE | LAYOUT_FILL_X | LAYOUT_FIX_HEIGHT, 0,0,0,13+9),
326
327 parent(_parent),
328
329 loadedSound(_loadedSound),
330 sound(_loadedSound->sound),
331
332 font(getApp()->getNormalFont()),
333
334 dragCueHint(new FXPopupHint(p->getApp())),
335
336 selectionEditPositionFactory(NULL),
337 moveCueActionFactory(NULL),
338 removeCueActionFactory(NULL)
339 {
340 enable();
341 flags|=FLAG_SHOWN; // I have to do this, or it will not show up.. like height is 0 or something
342
343 FXFontDesc d;
344 font->getFontDesc(d);
345 d.weight=FONTWEIGHT_LIGHT;
346 d.size=65;
347 font=new FXFont(getApp(),d);
348
349 cueClicked=NIL_CUE_INDEX;
350
351 focusedCueIndex=NIL_CUE_INDEX;
352 focusNextCue(); // to make it focus the first one if there is one
353
354 dragCueHint->create();
355
356 selectionEditPositionFactory=new CSelectionEditPositionFactory;
357 moveCueActionFactory=new CMoveCueActionFactory;
358 removeCueActionFactory=new CRemoveCueActionFactory;
359 }
360
~FXWaveRuler()361 FXWaveRuler::~FXWaveRuler()
362 {
363 delete font;
364 }
365
create()366 void FXWaveRuler::create()
367 {
368 FXComposite::create();
369 font->create();
370 }
371
canFocus() const372 fx_bool FXWaveRuler::canFocus() const
373 {
374 return 1;
375 }
376
377 #define CUE_Y ((height-1)-(1+CUE_RADIUS))
378
onPaint(FXObject * object,FXSelector sel,void * ptr)379 long FXWaveRuler::onPaint(FXObject *object,FXSelector sel,void *ptr)
380 {
381 FXComposite::onPaint(object,sel,ptr);
382
383 FXEvent *ev=(FXEvent*)ptr;
384 FXDCWindow dc(this,ev);
385
386 #define LABEL_TICK_FREQ 80
387 /*
388 * Here, we draw the ruler
389 * We want to always put 1 labled position every LABELED_TICK_FREQ pixels
390 * And draw ruler ticks also
391 */
392
393 FXint lastX=parent->waveScrollArea->getCanvasWidth();
394
395 const FXint left=0;
396 const FXint right=left+lastX;
397
398 FXint s=ev->rect.x;
399 s-=LABEL_TICK_FREQ-1;
400
401 FXint e=s+ev->rect.w;
402 e+=LABEL_TICK_FREQ-1;
403
404 if(e>lastX)
405 e=lastX;
406
407 dc.setForeground(FXRGB(20,20,20));
408 dc.compat_setFont(font);
409 for(FXint x=s;x<=e;x++)
410 {
411 if((x%LABEL_TICK_FREQ)==0)
412 {
413 dc.drawLine(x,0,x,10);
414
415 const string time=sound->getTimePosition(parent->waveScrollArea->getSamplePosForScreenX(x));
416
417 dc.drawText(x+2,12,time.c_str(),time.length());
418 }
419 else if(((x+(LABEL_TICK_FREQ/2))%(LABEL_TICK_FREQ))==0)
420 dc.drawLine(x,0,x,4); // half way tick between labled ticks
421 else if((x%(LABEL_TICK_FREQ/10))==0)
422 dc.drawLine(x,0,x,2); // small tenth ticks
423 }
424
425 // render cues
426 const size_t cueCount=sound->getCueCount();
427 for(size_t t=0;t<cueCount;t++)
428 {
429 // ??? I could figure out the min and max screen-visible time and make sure that is in range before testing every cue
430 FXint cueXPosition=parent->waveScrollArea->getCueScreenX(t);
431
432 if(cueXPosition!=CUE_OFF_SCREEN)
433 {
434 dc.setForeground(FXRGB(0,0,0));
435 const string cueName=sound->getCueName(t);
436 dc.drawText(cueXPosition+CUE_RADIUS+1,height-1,cueName.data(),cueName.size());
437
438 dc.drawArc(cueXPosition-CUE_RADIUS,CUE_Y-CUE_RADIUS,CUE_RADIUS*2,CUE_RADIUS*2,0*64,360*64);
439
440 dc.setForeground(sound->isCueAnchored(t) ? FXRGB(0,0,255) : FXRGB(255,0,0));
441 dc.drawLine(cueXPosition-1,height-4,cueXPosition-1,CUE_Y-1);
442 dc.drawLine(cueXPosition,height-1,cueXPosition,CUE_Y);
443 dc.drawLine(cueXPosition+1,height-4,cueXPosition+1,CUE_Y-1);
444
445 if(hasFocus() && focusedCueIndex!=NIL_CUE_INDEX && focusedCueTime==sound->getCueTime(t))
446 { // draw focus rectangle
447 const int textWidth=font->getTextWidth(cueName.data(),cueName.size());
448 const int textHeight=font->getTextHeight(cueName.data(),cueName.size());
449
450 const int x=cueXPosition-CUE_RADIUS;
451 const int y=min(height-1-textHeight,CUE_Y-CUE_RADIUS);
452 const int w=CUE_RADIUS*2+1+textWidth;
453 const int h=height-y;
454 dc.drawFocusRectangle(x-1,y+1,w+2,h-1);
455 }
456 }
457 }
458
459 return 1;
460 }
461
getClickedCue(FXint x,FXint y)462 size_t FXWaveRuler::getClickedCue(FXint x,FXint y)
463 {
464 const size_t cueCount=sound->getCueCount();
465 size_t cueClicked=NIL_CUE_INDEX;
466 if(cueCount>0)
467 {
468 // go in reverse order so that the more recent one created can be removed first
469 // I mean, if you accidently create one, then want to remove it, and it's on top
470 // of another one then it needs to find that more recent one first
471 for(size_t t=cueCount;t>=1;t--)
472 {
473 FXint X=parent->waveScrollArea->getCueScreenX(t-1);
474 if(X!=CUE_OFF_SCREEN)
475 {
476 FXint Y=CUE_Y;
477
478 // check distance from clicked point to the cue's position
479 if( ((x-X)*(x-X))+((y-Y)*(y-Y)) <= ((CUE_RADIUS+1)*(CUE_RADIUS+1)) )
480 {
481 cueClickedOffset=x-X;
482 cueClicked=t-1;
483 break;
484 }
485 }
486 }
487 }
488 return(cueClicked);
489 }
490
onPopupMenu(FXObject * object,FXSelector sel,void * ptr)491 long FXWaveRuler::onPopupMenu(FXObject *object,FXSelector sel,void *ptr)
492 {
493 if(!underCursor())
494 return(1);
495
496 // ??? if the parent->target is NULL, I should pop up the windows
497
498 FXEvent *event=(FXEvent*)ptr;
499
500 // see if any cue was clicked on
501 cueClicked=getClickedCue(event->win_x,event->win_y); // setting data member for onSetPositionToCue's sake
502
503 if(cueClicked<sound->getCueCount())
504 {
505 FXMenuPane cueMenu(this);
506 // ??? make sure that these get deleted when cueMenu is deleted
507
508 const sample_fpos_t cueTime=sound->getCueTime(cueClicked);
509
510 if(cueTime<loadedSound->channel->getStartPosition())
511 new FXMenuCommand(&cueMenu,_("Set Start Position to Cue"),NULL,this,ID_SET_START_POSITION);
512 else if(cueTime>loadedSound->channel->getStopPosition())
513 new FXMenuCommand(&cueMenu,_("Set Stop Position to Cue"),NULL,this,ID_SET_STOP_POSITION);
514 else
515 {
516 new FXMenuCommand(&cueMenu,_("Set Start Position to Cue"),NULL,this,ID_SET_START_POSITION);
517 new FXMenuCommand(&cueMenu,_("Set Stop Position to Cue"),NULL,this,ID_SET_STOP_POSITION);
518 }
519
520 new FXMenuSeparator(&cueMenu);
521 new FXMenuCommand(&cueMenu,_("&Edit Cue"),NULL,this,ID_EDIT_CUE);
522 new FXMenuCommand(&cueMenu,_("&Remove Cue"),NULL,this,ID_REMOVE_CUE);
523 new FXMenuSeparator(&cueMenu);
524 new FXMenuCommand(&cueMenu,_("Show Cues &List"),NULL,this,ID_SHOW_CUE_LIST);
525
526 cueMenu.create();
527 cueMenu.popup(NULL,event->root_x,event->root_y);
528 getApp()->runModalWhileShown(&cueMenu);
529 }
530 else
531 {
532 addCueTime= parent->waveScrollArea->getSamplePosForScreenX(event->win_x);
533
534 FXMenuPane gotoMenu(this);
535 // ??? make sure that these get deleted when gotoMenu is deleted
536 new FXMenuCommand(&gotoMenu,_("Center Start Position"),NULL,this,ID_FIND_START_POSITION);
537 new FXMenuCommand(&gotoMenu,_("Center Stop Position"),NULL,this,ID_FIND_STOP_POSITION);
538 new FXMenuSeparator(&gotoMenu);
539 new FXMenuCommand(&gotoMenu,_("Add Cue at This Position"),NULL,this,ID_ADD_CUE);
540 new FXMenuCommand(&gotoMenu,_("Add Cue at Start Position"),NULL,this,ID_ADD_CUE_AT_START_POSITION);
541 new FXMenuCommand(&gotoMenu,_("Add Cue at Stop Position"),NULL,this,ID_ADD_CUE_AT_STOP_POSITION);
542 new FXMenuSeparator(&gotoMenu);
543 new FXMenuCommand(&gotoMenu,_("Show Cues &List"),NULL,this,ID_SHOW_CUE_LIST);
544
545 gotoMenu.create();
546 gotoMenu.popup(NULL,event->root_x,event->root_y);
547 getApp()->runModalWhileShown(&gotoMenu);
548 }
549
550 cueClicked=NIL_CUE_INDEX;
551 return 0;
552 }
553
onFindStartPosition(FXObject * object,FXSelector sel,void * ptr)554 long FXWaveRuler::onFindStartPosition(FXObject *object,FXSelector sel,void *ptr)
555 {
556 FXWaveScrollArea *wsa=parent->waveScrollArea;
557 wsa->centerStartPos();
558 return 1;
559 }
560
onFindStopPosition(FXObject * object,FXSelector sel,void * ptr)561 long FXWaveRuler::onFindStopPosition(FXObject *object,FXSelector sel,void *ptr)
562 {
563 FXWaveScrollArea *wsa=parent->waveScrollArea;
564 wsa->centerStopPos();
565 return 1;
566 }
567
onSetPositionToCue(FXObject * object,FXSelector sel,void * ptr)568 long FXWaveRuler::onSetPositionToCue(FXObject *object,FXSelector sel,void *ptr)
569 {
570 switch(FXSELID(sel))
571 {
572 case ID_SET_START_POSITION:
573 loadedSound->channel->setStartPosition((sample_pos_t)sound->getCueTime(cueClicked));
574 break;
575
576 case ID_SET_STOP_POSITION:
577 loadedSound->channel->setStopPosition((sample_pos_t)sound->getCueTime(cueClicked));
578 break;
579 }
580
581 parent->waveScrollArea->updateFromSelectionChange();
582
583 return 1;
584 }
585
onAddCue(FXObject * object,FXSelector sel,void * ptr)586 long FXWaveRuler::onAddCue(FXObject *object,FXSelector sel,void *ptr)
587 {
588 switch(FXSELID(sel))
589 {
590 case ID_ADD_CUE:
591 if(parent->target)
592 parent->target->handle(parent,MKUINT(parent->message,FXRezWaveView::SEL_ADD_CUE),&addCueTime);
593 break;
594 case ID_ADD_CUE_AT_START_POSITION:
595 if(parent->target)
596 parent->target->handle(parent,MKUINT(parent->message,FXRezWaveView::SEL_ADD_CUE_AT_START_POSITION),NULL);
597 break;
598 case ID_ADD_CUE_AT_STOP_POSITION:
599 if(parent->target)
600 parent->target->handle(parent,MKUINT(parent->message,FXRezWaveView::SEL_ADD_CUE_AT_STOP_POSITION),NULL);
601 break;
602 }
603 update();
604
605 return 1;
606 }
607
onRemoveCue(FXObject * object,FXSelector sel,void * ptr)608 long FXWaveRuler::onRemoveCue(FXObject *object,FXSelector sel,void *ptr)
609 {
610 if(parent->target)
611 parent->target->handle(parent,MKUINT(parent->message,FXRezWaveView::SEL_REMOVE_CUE),&cueClicked);
612 update();
613 return 1;
614 }
615
onEditCue(FXObject * object,FXSelector sel,void * ptr)616 long FXWaveRuler::onEditCue(FXObject *object,FXSelector sel,void *ptr)
617 {
618 if(parent->target)
619 parent->target->handle(parent,MKUINT(parent->message,FXRezWaveView::SEL_EDIT_CUE),&cueClicked);
620 update();
621 return 1;
622 }
623
onShowCueList(FXObject * object,FXSelector sel,void * ptr)624 long FXWaveRuler::onShowCueList(FXObject *object,FXSelector sel,void *ptr)
625 {
626 if(parent->target)
627 parent->target->handle(parent,MKUINT(parent->message,FXRezWaveView::SEL_SHOW_CUE_LIST),NULL);
628 update();
629 return 1;
630 }
631
632 /* ??? when I have keyboard event handling for doing something with the focused cue, make it so that if you press esc while dragging a cue that it moves back to the original location */
onLeftBtnPress(FXObject * object,FXSelector sel,void * ptr)633 long FXWaveRuler::onLeftBtnPress(FXObject *object,FXSelector sel,void *ptr)
634 {
635 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
636
637 FXEvent* event=(FXEvent*)ptr;
638 if(event->click_count==1)
639 {
640 cueClicked=getClickedCue(event->win_x,event->win_y); // setting data member
641 if(cueClicked<sound->getCueCount())
642 {
643 origCueClickedTime=sound->getCueTime(cueClicked);
644
645 origStartPosition=loadedSound->channel->getStartPosition();
646 origStopPosition=loadedSound->channel->getStopPosition();
647
648 draggingSelectionToo=
649 !(event->state&SHIFTMASK) &&
650 (origCueClickedTime==loadedSound->channel->getStartPosition() ||
651 origCueClickedTime==loadedSound->channel->getStopPosition());
652
653 dragCueHint->setText(sound->getTimePosition(origCueClickedTime,gSoundFileManager->getSoundWindow(loadedSound)->getZoomDecimalPlaces()).c_str());
654 dragCueHint->show();
655 dragCueHint->autoplace();
656
657 // set focused cue
658 focusedCueIndex=cueClicked;
659 focusedCueTime=sound->getCueTime(focusedCueIndex);
660 update();
661 }
662 }
663 return 0;
664 }
665
onMouseMove(FXObject * object,FXSelector sel,void * ptr)666 long FXWaveRuler::onMouseMove(FXObject *object,FXSelector sel,void *ptr)
667 {
668 FXEvent* event=(FXEvent*)ptr;
669 if(event->state&LEFTBUTTONMASK && cueClicked<sound->getCueCount())
670 { // dragging a cue around
671
672 const string cueName=sound->getCueName(cueClicked);
673 const sample_pos_t cueTime=sound->getCueTime(cueClicked);
674 const FXint cueTextWidth=font->getTextWidth(cueName.data(),cueName.length());
675
676 // last_x is where the cue is on the screen now (possibly after autoscrolling)
677 const FXint last_x=parent->waveScrollArea->getCueScreenX(cueClicked);
678
679 // erase cue at old position
680 update(last_x-CUE_RADIUS-1,0,CUE_RADIUS*2+1+cueTextWidth+2,getHeight());
681
682 // erase vertical cue line at old position
683 if(gDrawVerticalCuePositions)
684 parent->waveScrollArea->redraw(last_x,1);
685
686 if(object==NULL) // simulated event from auto-scrolling
687 {
688 // get where the dragCueHint appears on the wave view canvas
689 FXint hint_win_x,hint_win_y;
690 dragCueHint->getParent()->translateCoordinatesTo(hint_win_x,hint_win_y,this,dragCueHint->getX(),dragCueHint->getY());
691
692 // redraw what was under the dragCueHint (necessary when auto-scrolling)
693 parent->waveScrollArea->redraw(hint_win_x+(last_x-event->win_x),dragCueHint->getWidth()+2); /* +2 I guess because of some border? I dunno exactly, but it fixed the prob; except I did still see the problem once when dragging a cue way off to the side and wiggling it around sometimes going back over the window letting it autoscroll ??? */
694 }
695
696
697 sample_pos_t newTime=parent->waveScrollArea->getSamplePosForScreenX(event->win_x-cueClickedOffset);
698
699 if(draggingSelectionToo)
700 {
701 const sample_pos_t start=loadedSound->channel->getStartPosition();
702 const sample_pos_t stop=loadedSound->channel->getStopPosition();
703
704 if(cueTime==start)
705 { // move start position with cue drag
706 if(newTime>stop)
707 { // dragged cue past stop position
708 loadedSound->channel->setStopPosition(newTime);
709 loadedSound->channel->setStartPosition(stop);
710 parent->updateFromSelectionChange(FXWaveCanvas::lcpStop);
711 }
712 else
713 {
714 loadedSound->channel->setStartPosition(newTime);
715 parent->updateFromSelectionChange(FXWaveCanvas::lcpStart);
716 }
717
718 }
719 else if(cueTime==stop)
720 { // move stop position with cue drag
721 if(newTime<start)
722 { // dragged cue before start position
723 loadedSound->channel->setStartPosition(newTime);
724 loadedSound->channel->setStopPosition(start);
725 parent->updateFromSelectionChange(FXWaveCanvas::lcpStart);
726 }
727 else
728 {
729 loadedSound->channel->setStopPosition(newTime);
730 parent->updateFromSelectionChange(FXWaveCanvas::lcpStop);
731 }
732
733 }
734 }
735
736 // update cue position
737 sound->setCueTime(cueClicked,newTime);
738 if(cueClicked==focusedCueIndex)
739 focusedCueTime=newTime;
740
741 // draw cue at new position
742 update((event->win_x-cueClickedOffset)-CUE_RADIUS-1,0,CUE_RADIUS*2+1+cueTextWidth+2,getHeight());
743
744 // draw vertical cue line at new position
745 if(gDrawVerticalCuePositions)
746 parent->waveScrollArea->redraw(event->win_x-cueClickedOffset,1);
747
748 dragCueHint->setText(sound->getTimePosition(newTime,gSoundFileManager->getSoundWindow(loadedSound)->getZoomDecimalPlaces()).c_str());
749 dragCueHint->autoplace();
750
751 // have to call canvas->repaint() on real mouse moves because if while autoscrolling a real (that is, object!=NULL) mouse move event occurs, then the window may or may not have been blitted leftward or rightward yet and we will erase the vertical cue position at the wrong position (or something like that, I really had a hard time figuring out the problem that shows up if you omit this call to repaint() )
752 if(object && gDrawVerticalCuePositions)
753 parent->waveScrollArea->canvas->repaint();
754
755 if(event->win_x<0 || event->win_x>=width)
756 { // scroll parent window leftwards or rightwards if mouse is beyond the window edges
757 #if REZ_FOX_VERSION>=10125
758 parent->waveScrollArea->startAutoScroll(event);
759 #else
760 parent->waveScrollArea->startAutoScroll(event->win_x,event->win_y);
761 #endif
762 }
763
764 }
765 return 0;
766 }
767
onLeftBtnRelease(FXObject * object,FXSelector sel,void * ptr)768 long FXWaveRuler::onLeftBtnRelease(FXObject *object,FXSelector sel,void *ptr)
769 {
770 FXComposite::handle(object,sel,ptr); // if FXComposite ever starts sending SEL_DOUBLECLICKED events, then the event will happen twice because I'm doing what's below.. detect the version and remove this handler
771
772 FXEvent* event=(FXEvent*)ptr;
773 if(event->click_count==1) // <-- single click
774 {
775 if(cueClicked<sound->getCueCount() && sound->getCueTime(cueClicked)!=origCueClickedTime)
776 { // was dragging a cue around
777 const sample_pos_t newCueTime=sound->getCueTime(cueClicked);
778
779 // set back to the orig position
780 sound->setCueTime(cueClicked,origCueClickedTime);
781
782 // set cue to new position except use an AAction object so it goes on the undo stack
783 CActionParameters actionParameters(NULL);
784 actionParameters.setValue<unsigned>("index",cueClicked);
785 actionParameters.setValue<sample_pos_t>("position",newCueTime);
786 actionParameters.setValue<sample_pos_t>("restoreStartPosition",origStartPosition);
787 actionParameters.setValue<sample_pos_t>("restoreStopPosition",origStopPosition);
788 moveCueActionFactory->performAction(loadedSound,&actionParameters,false);
789 }
790
791 dragCueHint->hide();
792 parent->waveScrollArea->stopAutoScroll();
793 }
794 else if(event->click_count==2) // <-- double click
795 {
796 cueClicked=getClickedCue(event->win_x,event->win_y); // setting data member
797 if(cueClicked<sound->getCueCount())
798 return onEditCue(object,sel,(void *)cueClicked);
799 else
800 { // clicking not on a cue.. change selection to be that which is between the two nearest cues (or to start or end if there isn't any near on that side)
801 size_t index;
802 sample_pos_t startPos=0;
803 sample_pos_t stopPos=sound->getLength()-1;
804 sample_pos_t clickedPos=parent->waveScrollArea->getSamplePosForScreenX(event->win_x);
805
806 if(sound->findPrevCueInTime(clickedPos,index))
807 startPos=sound->getCueTime(index);
808
809 if(sound->findNextCueInTime(clickedPos,index))
810 stopPos=sound->getCueTime(index)-1; // select from the position AT the start cue to one less sample prior to the next cue
811
812 if(stopPos<startPos) // safety
813 stopPos=startPos;
814
815 CActionParameters actionParameters(NULL);
816 selectionEditPositionFactory->selectStart=startPos;
817 selectionEditPositionFactory->selectStop=stopPos;
818 selectionEditPositionFactory->performAction(loadedSound,&actionParameters,false);
819 gSoundFileManager->getActiveWindow()->updateFromEdit(); // would prefer to call this for the very window that owns us, but I suppose the user couldn't have clicked it if it wasn't active
820 }
821 }
822
823 cueClicked=NIL_CUE_INDEX;
824 return 0;
825 }
826
onFocusIn(FXObject * sender,FXSelector sel,void * ptr)827 long FXWaveRuler::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){
828 FXComposite::onFocusIn(sender,sel,ptr);
829 update();
830 return 1;
831 }
832
onFocusOut(FXObject * sender,FXSelector sel,void * ptr)833 long FXWaveRuler::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){
834 FXComposite::onFocusOut(sender,sel,ptr);
835 update();
836 return 1;
837 }
838
839 /* ???
840 * If possible, make plus and minus nudge the cue to the left and right.. add acceleration
841 * if possible and make the undo action pertain the to last time the key was release for
842 * some given time I guess (because I dont want every little nudge to be undoable)
843 */
844
onKeyPress(FXObject * object,FXSelector sel,void * ptr)845 long FXWaveRuler::onKeyPress(FXObject *object,FXSelector sel,void *ptr)
846 {
847 FXEvent* event=(FXEvent*)ptr;
848 if(event->code==KEY_Left || event->code==KEY_KP_Left)
849 {
850 focusPrevCue();
851 }
852 else if(event->code==KEY_Right || event->code==KEY_KP_Right)
853 {
854 focusNextCue();
855 }
856 else if(event->code==KEY_Home || event->code==KEY_KP_Home)
857 {
858 focusFirstCue();
859 }
860 else if(event->code==KEY_End || event->code==KEY_KP_End)
861 {
862 focusLastCue();
863 }
864 else if(event->code==KEY_Delete || event->code==KEY_KP_Delete)
865 { // delete a cue
866 if(focusedCueIndex>=sound->getCueCount())
867 return 0;
868
869 const size_t removeCueIndex=focusedCueIndex;
870
871 focusNextCue();
872
873 CActionParameters actionParameters(NULL);
874 actionParameters.setValue<unsigned>("index",removeCueIndex);
875 removeCueActionFactory->performAction(loadedSound,&actionParameters,false);
876 if(removeCueIndex<focusedCueIndex)
877 focusedCueIndex--; // decrement since we just remove one below it
878
879 if(focusedCueIndex>=sound->getCueCount())
880 focusLastCue(); // find last cue if we just deleted what was the last cue
881
882 if(focusedCueIndex!=NIL_CUE_INDEX)
883 parent->waveScrollArea->centerTime(focusedCueTime);
884 parent->waveScrollArea->redraw();
885 update();
886 return 1; // handled
887 }
888 else if(event->code==KEY_Return)
889 { // edit a cue
890 if(focusedCueIndex>=sound->getCueCount())
891 return 0;
892 else
893 return onEditCue(object,sel,(void *)focusedCueIndex);
894 }
895 else
896 {
897 if(event->code==KEY_Escape && event->state&LEFTBUTTONMASK && cueClicked<sound->getCueCount())
898 { // ESC pressed for cancelling a drag
899 sound->setCueTime(cueClicked,origCueClickedTime);
900 dragCueHint->hide();
901 parent->waveScrollArea->stopAutoScroll();
902 cueClicked=NIL_CUE_INDEX;
903 update();
904 parent->waveScrollArea->redraw();
905 return 1;
906 }
907
908 return 0;
909 }
910
911 if(focusedCueIndex!=NIL_CUE_INDEX)
912 {
913 parent->waveScrollArea->centerTime(focusedCueTime);
914 update();
915 return 1; // handled
916 }
917 else
918 return 0;
919 }
920
focusNextCue()921 void FXWaveRuler::focusNextCue()
922 {
923 if(focusedCueIndex==NIL_CUE_INDEX)
924 {
925 sample_pos_t dummy;
926 if(!sound->findNearestCue(0,focusedCueIndex,dummy))
927 focusedCueIndex=NIL_CUE_INDEX;
928 else
929 focusedCueTime=sound->getCueTime(focusedCueIndex);
930 }
931 else
932 {
933 if(sound->findNextCue(focusedCueTime,focusedCueIndex))
934 focusedCueTime=sound->getCueTime(focusedCueIndex);
935 else
936 focusedCueIndex=NIL_CUE_INDEX;
937 }
938 }
939
focusPrevCue()940 void FXWaveRuler::focusPrevCue()
941 {
942 if(focusedCueIndex==NIL_CUE_INDEX)
943 focusNextCue(); // would implement the same thing here, so just call it
944 else
945 {
946 if(sound->findPrevCue(focusedCueTime,focusedCueIndex))
947 focusedCueTime=sound->getCueTime(focusedCueIndex);
948 else
949 focusedCueIndex=NIL_CUE_INDEX;
950 }
951 }
952
focusFirstCue()953 void FXWaveRuler::focusFirstCue()
954 {
955 sample_pos_t dummy;
956 if(!sound->findNearestCue(0,focusedCueIndex,dummy))
957 focusedCueIndex=NIL_CUE_INDEX;
958 else
959 focusedCueTime=sound->getCueTime(focusedCueIndex);
960 }
961
focusLastCue()962 void FXWaveRuler::focusLastCue()
963 {
964 sample_pos_t dummy;
965 if(!sound->findNearestCue(sound->getLength()-1,focusedCueIndex,dummy))
966 focusedCueIndex=NIL_CUE_INDEX;
967 else
968 focusedCueTime=sound->getCueTime(focusedCueIndex);
969 }
970
971