Exciting new Alt/h help key provided by sge (Georgy) with many thanks!
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / mtimebar.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program 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  * This program 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 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
22 #include "bcsignals.h"
23 #include "clip.h"
24 #include "cplayback.h"
25 #include "cwindow.h"
26 #include "edl.h"
27 #include "edlsession.h"
28 #include "localsession.h"
29 #include "mainclock.h"
30 #include "maincursor.h"
31 #include "mainsession.h"
32 #include "mbuttons.h"
33 #include "mtimebar.h"
34 #include "mwindowgui.h"
35 #include "mwindow.h"
36 #include "patchbay.h"
37 #include "preferences.h"
38 #include "theme.h"
39 #include "trackcanvas.h"
40 #include "tracks.h"
41 #include "transportque.h"
42 #include "zoombar.h"
43
44
45
46 MTimeBar::MTimeBar(MWindow *mwindow,
47         MWindowGUI *gui,
48         int x,
49         int y,
50         int w,
51         int h)
52  : TimeBar(mwindow, gui, x, y, w, h)
53 {
54         this->gui = gui;
55         this->pane = 0;
56 // *** CONTEXT_HELP ***
57         context_help_set_keyword("Time Format section");
58 }
59
60 MTimeBar::MTimeBar(MWindow *mwindow,
61         TimelinePane *pane,
62         int x,
63         int y,
64         int w,
65         int h)
66  : TimeBar(mwindow, mwindow->gui, x, y, w, h)
67 {
68         this->gui = mwindow->gui;
69         this->pane = pane;
70 // *** CONTEXT_HELP ***
71         context_help_set_keyword("Time Format section");
72 }
73
74 void MTimeBar::create_objects()
75 {
76         gui->add_subwindow(menu = new TimeBarPopup(mwindow));
77         menu->create_objects();
78
79         TimeBar::create_objects();
80 }
81
82
83 double MTimeBar::pixel_to_position(int pixel)
84 {
85         return (double)pixel *
86                 mwindow->edl->local_session->zoom_sample /
87                 mwindow->edl->session->sample_rate +
88                 (double)mwindow->edl->local_session->view_start[pane->number] *
89                 mwindow->edl->local_session->zoom_sample /
90                 mwindow->edl->session->sample_rate;
91 }
92
93
94 int64_t MTimeBar::position_to_pixel(double position)
95 {
96         return (int64_t)(position *
97                 mwindow->edl->session->sample_rate /
98                 mwindow->edl->local_session->zoom_sample -
99                 mwindow->edl->local_session->view_start[pane->number]);
100 }
101
102
103 void MTimeBar::stop_transport()
104 {
105         gui->stop_transport("MTimeBar::stop_transport");
106 }
107
108 #define TEXT_MARGIN xS(4)
109 #define TICK_SPACING xS(5)
110 #define LINE_MARGIN yS(3)
111 #define TICK_MARGIN yS(16)
112
113 void MTimeBar::draw_time()
114 {
115
116         char string[BCTEXTLEN];
117         int sample_rate = mwindow->edl->session->sample_rate;
118         double frame_rate = mwindow->edl->session->frame_rate;
119 // Seconds between text markings
120         double text_interval = 3600.0;
121 // Seconds between tick marks
122         double tick_interval = 3600.0;
123
124
125 // Calculate tick mark spacing, number spacing, and starting point based
126 // on zoom, time format, project settings.
127
128 // If the time format is for audio, mark round numbers of samples based on
129 // samplerate.
130 // Fow low zoom, mark tens of samples.
131
132 // If the time format is for video, mark round number of frames based on
133 // framerate.
134 // For low zoom, mark individual frames.
135
136         //int64_t windowspan = mwindow->edl->local_session->zoom_sample * get_w();
137
138         draw_range();
139
140
141
142
143
144 // Number of seconds per pixel
145         double time_per_pixel = (double)mwindow->edl->local_session->zoom_sample /
146                 sample_rate;
147 // Seconds in each frame
148         double frame_seconds = (double)1.0 / frame_rate;
149 // Starting time of view in seconds.
150         double view_start = mwindow->edl->local_session->view_start[pane->number] *
151                 time_per_pixel;
152 // Ending time of view in seconds
153         double view_end = (double)(mwindow->edl->local_session->view_start[pane->number] +
154                 get_w()) * time_per_pixel;
155 // Get minimum distance between text marks
156         int min_pixels1 = get_text_width(MEDIUMFONT,
157                 Units::totext(string,
158                         view_start,
159                         mwindow->edl->session->time_format,
160                         sample_rate,
161                         mwindow->edl->session->frame_rate,
162                         mwindow->edl->session->frames_per_foot)) + TEXT_MARGIN;
163         int min_pixels2 = get_text_width(MEDIUMFONT,
164                 Units::totext(string,
165                         view_end,
166                         mwindow->edl->session->time_format,
167                         sample_rate,
168                         mwindow->edl->session->frame_rate,
169                         mwindow->edl->session->frames_per_foot)) + TEXT_MARGIN;
170         int min_pixels = (int)MAX(min_pixels1, min_pixels2);
171
172
173 // Minimum seconds between text marks
174         double min_time = (double)min_pixels *
175                 mwindow->edl->local_session->zoom_sample /
176                 sample_rate;
177
178
179 // Get first text mark on or before window start
180         int64_t starting_mark = 0;
181
182         int progression = 1;
183
184 // Default text spacing
185         text_interval = 0.5;
186         double prev_text_interval = 1.0;
187
188         while(text_interval >= min_time)
189         {
190                 prev_text_interval = text_interval;
191                 if(progression == 0)
192                 {
193                         text_interval /= 2;
194                         progression++;
195                 }
196                 else
197                 if(progression == 1)
198                 {
199                         text_interval /= 2;
200                         progression++;
201                 }
202                 else
203                 if(progression == 2)
204                 {
205                         text_interval /= 2.5;
206                         progression = 0;
207                 }
208         }
209
210         text_interval = prev_text_interval;
211
212         if(1 >= min_time)
213                 ;
214         else
215         if(2 >= min_time)
216                 text_interval = 2;
217         else
218         if(5 >= min_time)
219                 text_interval = 5;
220         else
221         if(10 >= min_time)
222                 text_interval = 10;
223         else
224         if(15 >= min_time)
225                 text_interval = 15;
226         else
227         if(20 >= min_time)
228                 text_interval = 20;
229         else
230         if(30 >= min_time)
231                 text_interval = 30;
232         else
233         if(60 >= min_time)
234                 text_interval = 60;
235         else
236         if(120 >= min_time)
237                 text_interval = 120;
238         else
239         if(300 >= min_time)
240                 text_interval = 300;
241         else
242         if(600 >= min_time)
243                 text_interval = 600;
244         else
245         if(1200 >= min_time)
246                 text_interval = 1200;
247         else
248         if(1800 >= min_time)
249                 text_interval = 1800;
250         else
251         if(3600 >= min_time)
252                 text_interval = 3600;
253
254 // Set text interval
255         switch(mwindow->edl->session->time_format)
256         {
257                 case TIME_FEET_FRAMES:
258                 {
259                         double foot_seconds = frame_seconds * mwindow->edl->session->frames_per_foot;
260                         if(frame_seconds >= min_time)
261                                 text_interval = frame_seconds;
262                         else
263                         if(foot_seconds / 8.0 > min_time)
264                                 text_interval = frame_seconds * mwindow->edl->session->frames_per_foot / 8.0;
265                         else
266                         if(foot_seconds / 4.0 > min_time)
267                                 text_interval = frame_seconds * mwindow->edl->session->frames_per_foot / 4.0;
268                         else
269                         if(foot_seconds / 2.0 > min_time)
270                                 text_interval = frame_seconds * mwindow->edl->session->frames_per_foot / 2.0;
271                         else
272                         if(foot_seconds > min_time)
273                                 text_interval = frame_seconds * mwindow->edl->session->frames_per_foot;
274                         else
275                         if(foot_seconds * 2 >= min_time)
276                                 text_interval = foot_seconds * 2;
277                         else
278                         if(foot_seconds * 5 >= min_time)
279                                 text_interval = foot_seconds * 5;
280                         else
281                         {
282
283                                 for(int factor = 10, progression = 0; factor <= 100000; )
284                                 {
285                                         if(foot_seconds * factor >= min_time)
286                                         {
287                                                 text_interval = foot_seconds * factor;
288                                                 break;
289                                         }
290
291                                         if(progression == 0)
292                                         {
293                                                 factor = (int)(factor * 2.5);
294                                                 progression++;
295                                         }
296                                         else
297                                         if(progression == 1)
298                                         {
299                                                 factor = (int)(factor * 2);
300                                                 progression++;
301                                         }
302                                         else
303                                         if(progression == 2)
304                                         {
305                                                 factor = (int)(factor * 2);
306                                                 progression = 0;
307                                         }
308                                 }
309
310                         }
311                         break;
312                 }
313
314                 case TIME_FRAMES:
315                 case TIME_TIMECODE:
316                 case TIME_HMSF:
317 // One frame per text mark
318                         if(frame_seconds >= min_time)
319                                 text_interval = frame_seconds;
320                         else
321                         if(frame_seconds * 2 >= min_time)
322                                 text_interval = frame_seconds * 2;
323                         else
324                         if(frame_seconds * 5 >= min_time)
325                                 text_interval = frame_seconds * 5;
326                         else
327                         {
328
329                                 for(int factor = 10, progression = 0; factor <= 100000; )
330                                 {
331                                         if(frame_seconds * factor >= min_time)
332                                         {
333                                                 text_interval = frame_seconds * factor;
334                                                 break;
335                                         }
336
337                                         if(progression == 0)
338                                         {
339                                                 factor = (int)(factor * 2.5);
340                                                 progression++;
341                                         }
342                                         else
343                                         if(progression == 1)
344                                         {
345                                                 factor = (int)(factor * 2);
346                                                 progression++;
347                                         }
348                                         else
349                                         if(progression == 2)
350                                         {
351                                                 factor = (int)(factor * 2);
352                                                 progression = 0;
353                                         }
354                                 }
355
356                         }
357                         break;
358
359                 default:
360                         break;
361         }
362
363 // Sanity
364         while(text_interval < min_time)
365         {
366                 text_interval *= 2;
367         }
368
369 // Set tick interval
370         tick_interval = text_interval;
371         double timecode_offset = 0;
372
373         switch(mwindow->edl->session->time_format)
374         {
375                 case TIME_TIMECODE:
376                         timecode_offset = mwindow->get_timecode_offset(); // fall thru
377                 case TIME_FEET_FRAMES:
378                 case TIME_HMSF:
379                 case TIME_FRAMES:
380                         if(frame_seconds / time_per_pixel > TICK_SPACING)
381                                 tick_interval = frame_seconds;
382                         break;
383         }
384
385 // Get first text mark on or before window start
386         starting_mark = (int64_t)((double)mwindow->edl->local_session->view_start[pane->number] *
387                 time_per_pixel / text_interval);
388
389         double start_position = (double)starting_mark * text_interval;
390         int64_t iteration = 0;
391
392
393 //printf("text_interval=%f\n", text_interval);
394         while(start_position + text_interval * iteration < view_end)
395         {
396                 double position1 = start_position + text_interval * iteration;
397                 int pixel = (int64_t)(position1 / time_per_pixel) -
398                         mwindow->edl->local_session->view_start[pane->number];
399                 int pixel1 = pixel;
400
401                 Units::totext(string,
402                         position1,
403                         mwindow->edl->session->time_format,
404                         sample_rate,
405                         mwindow->edl->session->frame_rate,
406                         mwindow->edl->session->frames_per_foot,
407                         timecode_offset);
408                 set_color(get_resources()->default_text_color);
409                 set_font(MEDIUMFONT);
410
411                 draw_text(pixel + TEXT_MARGIN, get_text_ascent(MEDIUMFONT), string);
412                 draw_line(pixel, LINE_MARGIN, pixel, get_h() - yS(2));
413
414                 double position2 = start_position + text_interval * (iteration + 1);
415                 int pixel2 = (int64_t)(position2 / time_per_pixel) -
416                         mwindow->edl->local_session->view_start[pane->number];
417
418                 for(double tick_position = position1;
419                         tick_position < position2;
420                         tick_position += tick_interval)
421                 {
422                         pixel = (int64_t)(tick_position / time_per_pixel) -
423                                 mwindow->edl->local_session->view_start[pane->number];
424                         if(labs(pixel - pixel1) > 1 &&
425                                 labs(pixel - pixel2) > 1)
426                                 draw_line(pixel, TICK_MARGIN, pixel, get_h() - yS(2));
427                 }
428                 iteration++;
429         }
430
431
432 }
433
434 void MTimeBar::draw_range()
435 {
436         int x1 = 0, x2 = 0;
437         if( mwindow->brender_active && mwindow->preferences->use_brender &&
438             mwindow->edl->tracks->total_playable_vtracks() ) {
439                 double time_per_pixel = (double)mwindow->edl->local_session->zoom_sample /
440                         mwindow->edl->session->sample_rate;
441                 x1 = (int)(mwindow->edl->session->brender_start / time_per_pixel) -
442                         mwindow->edl->local_session->view_start[pane->number];
443                 x2 = (int)(mwindow->session->brender_end / time_per_pixel) -
444                         mwindow->edl->local_session->view_start[pane->number];
445         }
446
447         if(x2 > x1 && x1 < get_w() && x2 > 0) {
448                 draw_top_background(get_parent(), 0, 0, x1, get_h());
449                 draw_3segmenth(x1, 0, x2 - x1, mwindow->theme->get_image("timebar_brender"));
450                 draw_top_background(get_parent(), x2, 0, get_w() - x2, get_h());
451         }
452         else {
453                 draw_top_background(get_parent(), 0, 0, get_w(), get_h());
454         }
455
456 //      int64_t pixel = position_to_pixel(
457 //              mwindow->edl->local_session->get_selectionstart(1));
458 //
459 //      set_color(mwindow->theme->timebar_cursor_colorg);
460 //      draw_line(pixel, 0, pixel, get_h());
461 //printf("MTimeBar::draw_range %f %f\n", mwindow->session->brender_end, time_per_pixel);
462 }
463
464 void MTimeBar::select_label(double position)
465 {
466         stop_transport();
467
468         EDL *edl = mwindow->edl;
469         position = edl->align_to_frame(position, 1);
470
471         if(shift_down())
472         {
473                 if(position > edl->local_session->get_selectionend(1) / 2 +
474                         edl->local_session->get_selectionstart(1) / 2)
475                 {
476
477                         edl->local_session->set_selectionend(position);
478                 }
479                 else
480                 {
481                         edl->local_session->set_selectionstart(position);
482                 }
483         }
484         else
485         {
486                 edl->local_session->set_selectionstart(position);
487                 edl->local_session->set_selectionend(position);
488         }
489
490 // Que the CWindow
491         mwindow->cwindow->update(1, 0, 0, 0, 1);
492
493         gui->hide_cursor(0);
494         gui->draw_cursor(1);
495         gui->flash_canvas(1);
496         gui->zoombar->update();
497         update_highlights();
498         activate_timeline();
499 }
500
501
502 int MTimeBar::resize_event()
503 {
504         reposition_window(mwindow->theme->mtimebar_x,
505                 mwindow->theme->mtimebar_y,
506                 mwindow->theme->mtimebar_w,
507                 mwindow->theme->mtimebar_h);
508         update(0);
509         return 1;
510 }
511
512 int MTimeBar::resize_event(int x, int y, int w, int h)
513 {
514         reposition_window(x,
515                 y,
516                 w,
517                 h);
518         update(0);
519         return 1;
520 }
521
522 // int MTimeBar::test_preview(int buttonpress)
523 // {
524 //      int result = 0;
525 //      return result;
526 // }
527 //
528
529
530 void MTimeBar::handle_mwindow_drag()
531 {
532 //printf("TimeBar::cursor_motion_event %d %d\n", __LINE__, current_operation);
533         int relative_cursor_x = pane->canvas->get_relative_cursor_x();
534         if(relative_cursor_x >= pane->canvas->get_w() ||
535                 relative_cursor_x < 0)
536         {
537                 pane->canvas->start_dragscroll();
538         }
539         else
540         if(relative_cursor_x < pane->canvas->get_w() &&
541                 relative_cursor_x >= 0)
542         {
543                 pane->canvas->stop_dragscroll();
544         }
545
546         update(0);
547 }
548
549 void MTimeBar::update(int flush)
550 {
551         TimeBar::update(flush);
552 }
553
554 void MTimeBar::update_clock(double position)
555 {
556         if(!mwindow->cwindow->playback_engine->is_playing_back)
557                 gui->mainclock->update(position);
558 }
559
560 void MTimeBar::update_cursor()
561 {
562         int rx = get_relative_cursor_x();
563         double position = pixel_to_position(rx);
564
565         position = mwindow->edl->align_to_frame(position, 0);
566         position = MAX(0, position);
567
568         mwindow->select_point(position);
569         update(1);
570 }
571
572 double MTimeBar::test_highlight()
573 {
574 // Don't crash during initialization
575         if(pane->canvas)
576         {
577                 if(mwindow->session->current_operation == NO_OPERATION)
578                 {
579                         if(pane->canvas->is_event_win() &&
580                                 pane->canvas->cursor_inside())
581                         {
582                                 int cursor_x = pane->canvas->get_cursor_x();
583                                 double position = (double)cursor_x *
584                                         (double)mwindow->edl->local_session->zoom_sample /
585                                         (double)mwindow->edl->session->sample_rate +
586                                         (double)mwindow->edl->local_session->view_start[pane->number] *
587                                         (double)mwindow->edl->local_session->zoom_sample /
588                                         (double)mwindow->edl->session->sample_rate;
589                                 pane->canvas->timebar_position = mwindow->edl->align_to_frame(position, 0);
590                         }
591
592 //printf("MTimeBar::test_highlight %d %d %f\n", __LINE__, pane->canvas->cursor_inside(), pane->canvas->timebar_position);
593                         return pane->canvas->timebar_position;
594                 }
595                 else
596                 if(mwindow->session->current_operation == SELECT_REGION ||
597                         mwindow->session->current_operation == DRAG_EDITHANDLE2)
598                 {
599 //printf("MTimeBar::test_highlight %d %f\n", __LINE__, mwindow->gui->canvas->timebar_position);
600                         return pane->canvas->timebar_position;
601                 }
602
603                 return -1;
604         }
605         else
606         {
607                 return -1;
608         }
609 }
610
611 int MTimeBar::repeat_event(int64_t duration)
612 {
613         if(!pane->canvas->drag_scroll) return 0;
614         if(duration != BC_WindowBase::get_resources()->scroll_repeat) return 0;
615
616         int distance = 0;
617         int x_movement = 0;
618         int relative_cursor_x = pane->canvas->get_relative_cursor_x();
619         if(current_operation == TIMEBAR_DRAG)
620         {
621                 if(relative_cursor_x >= pane->canvas->get_w())
622                 {
623                         distance = relative_cursor_x - pane->canvas->get_w();
624                         x_movement = 1;
625                 }
626                 else
627                 if(relative_cursor_x < 0)
628                 {
629                         distance = relative_cursor_x;
630                         x_movement = 1;
631                 }
632
633
634
635                 if(x_movement)
636                 {
637                         update_cursor();
638                         mwindow->samplemovement(
639                                 mwindow->edl->local_session->view_start[pane->number] + distance,
640                                 pane->number);
641                 }
642                 return 1;
643         }
644
645         return 0;
646 }
647
648 int MTimeBar::button_press_event()
649 {
650         int result = 0;
651
652         if(is_event_win() && cursor_above() && get_buttonpress() == 3)
653         {
654                 menu->update();
655                 menu->activate_menu();
656                 result = 1;
657         }
658
659         if(!result) return TimeBar::button_press_event();
660         return result;
661 }
662
663
664 void MTimeBar::activate_timeline()
665 {
666         pane->activate();
667 }
668
669
670 TimeBarPopupItem::TimeBarPopupItem(MWindow *mwindow,
671         TimeBarPopup *menu,
672         const char *text,
673         int value)
674  : BC_MenuItem(text)
675 {
676         this->mwindow = mwindow;
677         this->menu = menu;
678         this->value = value;
679 }
680
681 int TimeBarPopupItem::handle_event()
682 {
683         mwindow->edl->session->time_format = value;
684         mwindow->gui->update(0, NO_DRAW, 1, 0, 0, 1, 0);
685         mwindow->gui->redraw_time_dependancies();
686         return 1;
687 }
688
689
690
691 TimeBarPopup::TimeBarPopup(MWindow *mwindow)
692  : BC_PopupMenu(0, 0, 0, "", 0)
693 {
694         this->mwindow = mwindow;
695 }
696
697 TimeBarPopup::~TimeBarPopup()
698 {
699 }
700
701
702 void TimeBarPopup::create_objects()
703 {
704         add_item(items[0] = new TimeBarPopupItem(mwindow,
705                 this, TIME_HMS_TEXT, TIME_HMS));
706         add_item(items[1] = new TimeBarPopupItem(mwindow,
707                 this, TIME_HMSF_TEXT, TIME_HMSF));
708         add_item(items[2] = new TimeBarPopupItem(mwindow,
709                 this, TIME_TIMECODE_TEXT, TIME_TIMECODE));
710         add_item(items[3] = new TimeBarPopupItem(mwindow,
711                 this, TIME_FRAMES_TEXT, TIME_FRAMES));
712         add_item(items[4] = new TimeBarPopupItem(mwindow,
713                 this, TIME_SAMPLES_TEXT, TIME_SAMPLES));
714         add_item(items[5] = new TimeBarPopupItem(mwindow,
715                 this, TIME_SAMPLES_HEX_TEXT, TIME_SAMPLES_HEX));
716         add_item(items[6] = new TimeBarPopupItem(mwindow,
717                 this, TIME_SECONDS_TEXT, TIME_SECONDS));
718         add_item(items[7] = new TimeBarPopupItem(mwindow,
719                 this, TIME_FEET_FRAMES_TEXT, TIME_FEET_FRAMES));
720 }
721
722 void TimeBarPopup::update()
723 {
724         int time_format = mwindow->edl->session->time_format;
725         for( int i=0; i<TOTAL_TIMEFORMATS; ++i ) {
726                 items[i]->set_checked(items[i]->value == time_format);
727         }
728 }
729
730
731
732
733