1 /*
2  * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
4  * Copyright (C) 2006-2010 David Robillard <d@drobilla.net>
5  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2013-2017 Tim Mayberry <mojofunk@gmail.com>
7  * Copyright (C) 2013-2018 Robin Gareus <robin@gareus.org>
8  * Copyright (C) 2015-2016 Nick Mainsbridge <mainsbridge@gmail.com>
9  * Copyright (C) 2015-2018 Ben Loftis <ben@harrisonconsoles.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
26 #ifdef WAF_BUILD
27 #include "gtk2ardour-config.h"
28 #endif
29 
30 #include <cstdio> // for sprintf, grrr
31 #include <cmath>
32 #include <inttypes.h>
33 
34 #include <string>
35 
36 #include <gtk/gtkaction.h>
37 
38 #include "canvas/container.h"
39 #include "canvas/canvas.h"
40 #include "canvas/ruler.h"
41 #include "canvas/debug.h"
42 #include "canvas/scroll_group.h"
43 
44 #include "ardour/session.h"
45 #include "ardour/tempo.h"
46 #include "ardour/profile.h"
47 
48 #include "gtkmm2ext/gtk_ui.h"
49 #include "gtkmm2ext/keyboard.h"
50 
51 #include "ardour_ui.h"
52 #include "editor.h"
53 #include "editing.h"
54 #include "actions.h"
55 #include "gui_thread.h"
56 #include "ruler_dialog.h"
57 #include "time_axis_view.h"
58 #include "editor_drag.h"
59 #include "editor_cursors.h"
60 #include "ui_config.h"
61 
62 #include "pbd/i18n.h"
63 
64 using namespace ARDOUR;
65 using namespace PBD;
66 using namespace Gtk;
67 using namespace Editing;
68 
69 /* the order here must match the "metric" enums in editor.h */
70 
71 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
72 {
73     public:
TimecodeMetric(Editor * e)74 	TimecodeMetric (Editor* e) : _editor (e) {}
75 
get_marks(std::vector<ArdourCanvas::Ruler::Mark> & marks,double lower,double upper,int maxchars) const76 	void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
77 		_editor->metric_get_timecode (marks, lower, upper, maxchars);
78 	}
79 
80     private:
81 	Editor* _editor;
82 };
83 
84 class SamplesMetric : public ArdourCanvas::Ruler::Metric
85 {
86     public:
SamplesMetric(Editor * e)87 	SamplesMetric (Editor* e) : _editor (e) {}
88 
get_marks(std::vector<ArdourCanvas::Ruler::Mark> & marks,double lower,double upper,int maxchars) const89 	void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
90 		_editor->metric_get_samples (marks, lower, upper, maxchars);
91 	}
92 
93     private:
94 	Editor* _editor;
95 };
96 
97 class BBTMetric : public ArdourCanvas::Ruler::Metric
98 {
99     public:
BBTMetric(Editor * e)100 	BBTMetric (Editor* e) : _editor (e) {}
101 
get_marks(std::vector<ArdourCanvas::Ruler::Mark> & marks,double lower,double upper,int maxchars) const102 	void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
103 		_editor->metric_get_bbt (marks, lower, upper, maxchars);
104 	}
105 
106     private:
107 	Editor* _editor;
108 };
109 
110 class MinsecMetric : public ArdourCanvas::Ruler::Metric
111 {
112     public:
MinsecMetric(Editor * e)113 	MinsecMetric (Editor* e) : _editor (e) {}
114 
get_marks(std::vector<ArdourCanvas::Ruler::Mark> & marks,double lower,double upper,int maxchars) const115 	void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
116 		_editor->metric_get_minsec (marks, lower, upper, maxchars);
117 	}
118 
119     private:
120 	Editor* _editor;
121 };
122 
123 static ArdourCanvas::Ruler::Metric* _bbt_metric;
124 static ArdourCanvas::Ruler::Metric* _timecode_metric;
125 static ArdourCanvas::Ruler::Metric* _samples_metric;
126 static ArdourCanvas::Ruler::Metric* _minsec_metric;
127 
128 void
initialize_rulers()129 Editor::initialize_rulers ()
130 {
131 	ruler_grabbed_widget = 0;
132 
133 	Pango::FontDescription font (UIConfiguration::instance().get_SmallerFont());
134 	Pango::FontDescription larger_font (UIConfiguration::instance().get_SmallBoldFont());
135 
136 	_timecode_metric = new TimecodeMetric (this);
137 	_bbt_metric = new BBTMetric (this);
138 	_minsec_metric = new MinsecMetric (this);
139 	_samples_metric = new SamplesMetric (this);
140 
141 	timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
142 						  ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
143 	timecode_ruler->set_font_description (font);
144 	CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
145 	timecode_nmarks = 0;
146 
147 	samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
148 						 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
149 	samples_ruler->set_font_description (font);
150 	CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
151 
152 	minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
153 						ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
154 	minsec_ruler->set_font_description (font);
155 	CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
156 	minsec_nmarks = 0;
157 
158 	bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
159 	                                     ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
160 	bbt_ruler->set_font_description (font);
161 	bbt_ruler->set_second_font_description (larger_font);
162 	CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
163 	timecode_nmarks = 0;
164 
165 	using namespace Box_Helpers;
166 	BoxList & lab_children =  time_bars_vbox.children();
167 
168 	lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
169 	lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
170 	lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
171 	lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
172 	lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
173 	lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
174 	lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
175 	lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
176 	lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
177 	lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
178 	lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
179 
180 	/* 1 event handler to bind them all ... */
181 
182 	timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
183 	minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
184 	bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
185 	samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
186 
187 	visible_timebars = 0; /*this will be changed below */
188 }
189 
190 bool
ruler_label_button_release(GdkEventButton * ev)191 Editor::ruler_label_button_release (GdkEventButton* ev)
192 {
193 	if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
194 		if (!ruler_dialog) {
195 			ruler_dialog = new RulerDialog ();
196 		}
197 		ruler_dialog->present ();
198 	}
199 
200 	return true;
201 }
202 
203 void
popup_ruler_menu(samplepos_t where,ItemType t)204 Editor::popup_ruler_menu (samplepos_t where, ItemType t)
205 {
206 	using namespace Menu_Helpers;
207 
208 	if (editor_ruler_menu == 0) {
209 		editor_ruler_menu = new Menu;
210 		editor_ruler_menu->set_name ("ArdourContextMenu");
211 	}
212 
213 	// always build from scratch
214 	MenuList& ruler_items = editor_ruler_menu->items();
215 	editor_ruler_menu->set_name ("ArdourContextMenu");
216 	ruler_items.clear();
217 
218 	switch (t) {
219 	case MarkerBarItem:
220 		ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false)));
221 		ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
222 		ruler_items.push_back (MenuElem (_("Clear all xruns"), sigc::mem_fun(*this, &Editor::clear_xrun_markers)));
223 		ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
224 		break;
225 
226 	case RangeMarkerBarItem:
227 		ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
228 		ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
229 		ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
230 		break;
231 
232 	case TransportMarkerBarItem:
233 		ruler_items.push_back (MenuElem (_("New Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
234 		ruler_items.push_back (MenuElem (_("New Punch range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_punch), where)));
235 		break;
236 
237 	case CdMarkerBarItem:
238 		// TODO
239 		ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
240 		break;
241 
242 	case TempoBarItem:
243 		ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
244 		break;
245 
246 	case MeterBarItem:
247 		ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
248 		break;
249 
250 	case VideoBarItem:
251 		/* proper headings would be nice
252 		 * but AFAICT the only way to get them will be to define a
253 		 * special GTK style for insensitive Elements or subclass MenuItem
254 		 */
255 		//ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
256 		//static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
257 		ruler_items.push_back (CheckMenuElem (_("Large"),  sigc::bind (sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
258 		if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
259 		ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind (sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
260 		if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
261 		ruler_items.push_back (CheckMenuElem (_("Small"),  sigc::bind (sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
262 		if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
263 
264 		ruler_items.push_back (SeparatorElem ());
265 
266 		//ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
267 		//static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
268 		ruler_items.push_back (CheckMenuElem (_("Lock")));
269 		{
270 			Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
271 			vtl_lock->set_active(is_video_timeline_locked());
272 			vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
273 		}
274 
275 		ruler_items.push_back (SeparatorElem ());
276 
277 		//ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
278 		//static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
279 		ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
280 		{
281 			Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
282 			if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
283 				xjadeo_toggle->set_sensitive(false);
284 			}
285 			xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
286 			xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
287 		}
288 		break;
289 
290 	default:
291 		break;
292 	}
293 
294 	if (!ruler_items.empty()) {
295 		editor_ruler_menu->popup (1, gtk_get_current_event_time());
296 	}
297 
298 	no_ruler_shown_update = false;
299 }
300 
301 void
store_ruler_visibility()302 Editor::store_ruler_visibility ()
303 {
304 	XMLNode* node = new XMLNode(X_("RulerVisibility"));
305 
306 	node->set_property (X_("timecode"), ruler_timecode_action->get_active());
307 	node->set_property (X_("bbt"), ruler_bbt_action->get_active());
308 	node->set_property (X_("samples"), ruler_samples_action->get_active());
309 	node->set_property (X_("minsec"), ruler_minsec_action->get_active());
310 	node->set_property (X_("tempo"), ruler_tempo_action->get_active());
311 	node->set_property (X_("meter"), ruler_meter_action->get_active());
312 	node->set_property (X_("marker"), ruler_marker_action->get_active());
313 	node->set_property (X_("rangemarker"), ruler_range_action->get_active());
314 	node->set_property (X_("transportmarker"), ruler_loop_punch_action->get_active());
315 	node->set_property (X_("cdmarker"), ruler_cd_marker_action->get_active());
316 	node->set_property (X_("videotl"), ruler_video_action->get_active());
317 
318 	_session->add_extra_xml (*node);
319 }
320 
321 void
restore_ruler_visibility()322 Editor::restore_ruler_visibility ()
323 {
324 	XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
325 
326 	no_ruler_shown_update = true;
327 
328 	bool yn;
329 	if (node) {
330 		if (node->get_property ("timecode", yn)) {
331 			ruler_timecode_action->set_active (yn);
332 		}
333 		if (node->get_property ("bbt", yn)) {
334 			ruler_bbt_action->set_active (yn);
335 		}
336 		if (node->get_property ("samples", yn)) {
337 			ruler_samples_action->set_active (yn);
338 		}
339 		if (node->get_property ("minsec", yn)) {
340 			ruler_minsec_action->set_active (yn);
341 		}
342 		if (node->get_property ("tempo", yn)) {
343 			ruler_tempo_action->set_active (yn);
344 		}
345 		if (node->get_property ("meter", yn)) {
346 			ruler_meter_action->set_active (yn);
347 		}
348 		if (node->get_property ("marker", yn)) {
349 			ruler_marker_action->set_active (yn);
350 		}
351 		if (node->get_property ("rangemarker", yn)) {
352 			ruler_range_action->set_active (yn);
353 		}
354 		if (node->get_property ("transportmarker", yn)) {
355 			ruler_loop_punch_action->set_active (yn);
356 		}
357 
358 		if (node->get_property ("cdmarker", yn)) {
359 				ruler_cd_marker_action->set_active (yn);
360 		} else {
361 			// this _session doesn't yet know about the cdmarker ruler
362 			// as a benefit to the user who doesn't know the feature exists, show the ruler if
363 			// any cd marks exist
364 			ruler_cd_marker_action->set_active (false);
365 			const Locations::LocationList & locs = _session->locations()->list();
366 			for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
367 				if ((*i)->is_cd_marker()) {
368 					ruler_cd_marker_action->set_active (true);
369 					break;
370 				}
371 			}
372 		}
373 
374 		if (node->get_property ("videotl", yn)) {
375 			ruler_video_action->set_active (yn);
376 		}
377 
378 	}
379 
380 	no_ruler_shown_update = false;
381 	update_ruler_visibility ();
382 }
383 
384 void
update_ruler_visibility()385 Editor::update_ruler_visibility ()
386 {
387 	int visible_timebars = 0;
388 
389 	if (no_ruler_shown_update) {
390 		return;
391 	}
392 
393 	/* the order of the timebars is fixed, so we have to go through each one
394 	 * and adjust its position depending on what is shown.
395 	 *
396 	 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
397 	 * loop/punch, cd markers, location markers
398 	 */
399 
400 	double tbpos = 0.0;
401 	double tbgpos = 0.0;
402 	double old_unit_pos;
403 
404 #ifdef __APPLE__
405 	/* gtk update probs require this (damn) */
406 	meter_label.hide();
407 	tempo_label.hide();
408 	range_mark_label.hide();
409 	transport_mark_label.hide();
410 	cd_mark_label.hide();
411 	mark_label.hide();
412 	videotl_label.hide();
413 #endif
414 
415 	if (ruler_minsec_action->get_active()) {
416 		old_unit_pos = minsec_ruler->position().y;
417 		if (tbpos != old_unit_pos) {
418 			minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
419 		}
420 		minsec_ruler->show();
421 		minsec_label.show();
422 		tbpos += timebar_height;
423 		tbgpos += timebar_height;
424 		visible_timebars++;
425 	} else {
426 		minsec_ruler->hide();
427 		minsec_label.hide();
428 	}
429 
430 	if (ruler_timecode_action->get_active()) {
431 		old_unit_pos = timecode_ruler->position().y;
432 		if (tbpos != old_unit_pos) {
433 			timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
434 		}
435 		timecode_ruler->show();
436 		timecode_label.show();
437 		tbpos += timebar_height;
438 		tbgpos += timebar_height;
439 		visible_timebars++;
440 	} else {
441 		timecode_ruler->hide();
442 		timecode_label.hide();
443 	}
444 
445 	if (ruler_samples_action->get_active()) {
446 		old_unit_pos = samples_ruler->position().y;
447 		if (tbpos != old_unit_pos) {
448 			samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
449 		}
450 		samples_ruler->show();
451 		samples_label.show();
452 		tbpos += timebar_height;
453 		tbgpos += timebar_height;
454 		visible_timebars++;
455 	} else {
456 		samples_ruler->hide();
457 		samples_label.hide();
458 	}
459 
460 	if (ruler_bbt_action->get_active()) {
461 		old_unit_pos = bbt_ruler->position().y;
462 		if (tbpos != old_unit_pos) {
463 			bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
464 		}
465 		bbt_ruler->show();
466 		bbt_label.show();
467 		tbpos += timebar_height;
468 		tbgpos += timebar_height;
469 		visible_timebars++;
470 	} else {
471 		bbt_ruler->hide();
472 		bbt_label.hide();
473 	}
474 
475 	if (ruler_meter_action->get_active()) {
476 		old_unit_pos = meter_group->position().y;
477 		if (tbpos != old_unit_pos) {
478 			meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
479 		}
480 		meter_group->show();
481 		meter_label.show();
482 		tbpos += timebar_height;
483 		tbgpos += timebar_height;
484 		visible_timebars++;
485 	} else {
486 		meter_group->hide();
487 		meter_label.hide();
488 	}
489 
490 	if (ruler_tempo_action->get_active()) {
491 		old_unit_pos = tempo_group->position().y;
492 		if (tbpos != old_unit_pos) {
493 			tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
494 		}
495 		tempo_group->show();
496 		tempo_label.show();
497 		tbpos += timebar_height;
498 		tbgpos += timebar_height;
499 		visible_timebars++;
500 	} else {
501 		tempo_group->hide();
502 		tempo_label.hide();
503 	}
504 
505 	if (ruler_range_action->get_active()) {
506 		old_unit_pos = range_marker_group->position().y;
507 		if (tbpos != old_unit_pos) {
508 			range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
509 		}
510 		range_marker_group->show();
511 		range_mark_label.show();
512 
513 		tbpos += timebar_height;
514 		tbgpos += timebar_height;
515 		visible_timebars++;
516 	} else {
517 		range_marker_group->hide();
518 		range_mark_label.hide();
519 	}
520 
521 	if (ruler_loop_punch_action->get_active()) {
522 		old_unit_pos = transport_marker_group->position().y;
523 		if (tbpos != old_unit_pos) {
524 			transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
525 		}
526 		transport_marker_group->show();
527 		transport_mark_label.show();
528 		tbpos += timebar_height;
529 		tbgpos += timebar_height;
530 		visible_timebars++;
531 	} else {
532 		transport_marker_group->hide();
533 		transport_mark_label.hide();
534 	}
535 
536 	if (ruler_cd_marker_action->get_active()) {
537 		old_unit_pos = cd_marker_group->position().y;
538 		if (tbpos != old_unit_pos) {
539 			cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
540 		}
541 		cd_marker_group->show();
542 		cd_mark_label.show();
543 		tbpos += timebar_height;
544 		tbgpos += timebar_height;
545 		visible_timebars++;
546 		// make sure all cd markers show up in their respective places
547 		update_cd_marker_display();
548 	} else {
549 		cd_marker_group->hide();
550 		cd_mark_label.hide();
551 		// make sure all cd markers show up in their respective places
552 		update_cd_marker_display();
553 	}
554 
555 	if (ruler_marker_action->get_active()) {
556 		old_unit_pos = marker_group->position().y;
557 		if (tbpos != old_unit_pos) {
558 			marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
559 		}
560 		marker_group->show();
561 		mark_label.show();
562 		tbpos += timebar_height;
563 		tbgpos += timebar_height;
564 		visible_timebars++;
565 	} else {
566 		marker_group->hide();
567 		mark_label.hide();
568 	}
569 
570 	if (ruler_video_action->get_active()) {
571 		old_unit_pos = videotl_group->position().y;
572 		if (tbpos != old_unit_pos) {
573 			videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
574 		}
575 		videotl_group->show();
576 		videotl_label.show();
577 		tbpos += timebar_height * videotl_bar_height;
578 		tbgpos += timebar_height * videotl_bar_height;
579 		visible_timebars+=videotl_bar_height;
580 		queue_visual_videotimeline_update();
581 	} else {
582 		videotl_group->hide();
583 		videotl_label.hide();
584 		update_video_timeline(true);
585 	}
586 
587 	time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
588 
589 	/* move hv_scroll_group (trackviews) to the end of the timebars
590 	 */
591 
592 	hv_scroll_group->set_y_position (timebar_height * visible_timebars);
593 
594 	compute_fixed_ruler_scale ();
595 	update_fixed_rulers();
596 	redisplay_grid (false);
597 
598 	/* Changing ruler visibility means that any lines on markers might need updating */
599 	for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
600 		i->second->setup_lines ();
601 	}
602 }
603 
604 void
update_just_timecode()605 Editor::update_just_timecode ()
606 {
607 	ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
608 
609 	if (_session == 0) {
610 		return;
611 	}
612 
613 	samplepos_t rightmost_sample = _leftmost_sample + current_page_samples();
614 
615 	if (ruler_timecode_action->get_active()) {
616 		timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
617 	}
618 }
619 
620 void
compute_fixed_ruler_scale()621 Editor::compute_fixed_ruler_scale ()
622 {
623 	if (_session == 0) {
624 		return;
625 	}
626 
627 	if (ruler_timecode_action->get_active()) {
628 		set_timecode_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
629 	}
630 
631 	if (ruler_minsec_action->get_active()) {
632 		set_minsec_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
633 	}
634 
635 	if (ruler_samples_action->get_active()) {
636 		set_samples_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
637 	}
638 }
639 
640 void
update_fixed_rulers()641 Editor::update_fixed_rulers ()
642 {
643 	samplepos_t rightmost_sample;
644 
645 	if (_session == 0) {
646 		return;
647 	}
648 
649 	compute_fixed_ruler_scale ();
650 
651 	_timecode_metric->units_per_pixel = samples_per_pixel;
652 	_samples_metric->units_per_pixel = samples_per_pixel;
653 	_minsec_metric->units_per_pixel = samples_per_pixel;
654 
655 	rightmost_sample = _leftmost_sample + current_page_samples();
656 
657 	/* these force a redraw, which in turn will force execution of the metric callbacks
658 	   to compute the relevant ticks to display.
659 	*/
660 
661 	if (ruler_timecode_action->get_active()) {
662 		timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
663 	}
664 
665 	if (ruler_samples_action->get_active()) {
666 		samples_ruler->set_range (_leftmost_sample, rightmost_sample);
667 	}
668 
669 	if (ruler_minsec_action->get_active()) {
670 		minsec_ruler->set_range (_leftmost_sample, rightmost_sample);
671 	}
672 }
673 
674 void
update_tempo_based_rulers()675 Editor::update_tempo_based_rulers ()
676 {
677 	if (_session == 0) {
678 		return;
679 	}
680 
681 	_bbt_metric->units_per_pixel = samples_per_pixel;
682 
683 	compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
684 
685 	if (ruler_bbt_action->get_active()) {
686 		bbt_ruler->set_range (_leftmost_sample, _leftmost_sample+current_page_samples());
687 	}
688 }
689 
690 
691 void
set_timecode_ruler_scale(samplepos_t lower,samplepos_t upper)692 Editor::set_timecode_ruler_scale (samplepos_t lower, samplepos_t upper)
693 {
694 	using namespace std;
695 
696 	samplepos_t spacer;
697 	samplepos_t fr;
698 
699 	if (_session == 0) {
700 		return;
701 	}
702 
703 	fr = _session->sample_rate();
704 
705 	if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
706 		lower = lower - spacer;
707 	} else {
708 		lower = 0;
709 	}
710 
711 	upper = upper + spacer;
712 	samplecnt_t const range = upper - lower;
713 
714 	if (range < (2 * _session->samples_per_timecode_frame())) { /* 0 - 2 samples */
715 		timecode_ruler_scale = timecode_show_bits;
716 		timecode_mark_modulo = 20;
717 		timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
718 	} else if (range <= (fr / 4)) { /* 2 samples - 0.250 second */
719 		timecode_ruler_scale = timecode_show_samples;
720 		timecode_mark_modulo = 1;
721 		timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
722 	} else if (range <= (fr / 2)) { /* 0.25-0.5 second */
723 		timecode_ruler_scale = timecode_show_samples;
724 		timecode_mark_modulo = 2;
725 		timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
726 	} else if (range <= fr) { /* 0.5-1 second */
727 		timecode_ruler_scale = timecode_show_samples;
728 		timecode_mark_modulo = 5;
729 		timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
730 	} else if (range <= 2 * fr) { /* 1-2 seconds */
731 		timecode_ruler_scale = timecode_show_samples;
732 		timecode_mark_modulo = 10;
733 		timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
734 	} else if (range <= 8 * fr) { /* 2-8 seconds */
735 		timecode_ruler_scale = timecode_show_seconds;
736 		timecode_mark_modulo = 1;
737 		timecode_nmarks = 2 + (range / fr);
738 	} else if (range <= 16 * fr) { /* 8-16 seconds */
739 		timecode_ruler_scale = timecode_show_seconds;
740 		timecode_mark_modulo = 2;
741 		timecode_nmarks = 2 + (range / fr);
742 	} else if (range <= 30 * fr) { /* 16-30 seconds */
743 		timecode_ruler_scale = timecode_show_seconds;
744 		timecode_mark_modulo = 5;
745 		timecode_nmarks = 2 + (range / fr);
746 	} else if (range <= 60 * fr) { /* 30-60 seconds */
747 		timecode_ruler_scale = timecode_show_seconds;
748 		timecode_mark_modulo = 5;
749 		timecode_nmarks = 2 + (range / fr);
750 	} else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
751 		timecode_ruler_scale = timecode_show_seconds;
752 		timecode_mark_modulo = 15;
753 		timecode_nmarks = 2 + (range / fr);
754 	} else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
755 		timecode_ruler_scale = timecode_show_seconds;
756 		timecode_mark_modulo = 30;
757 		timecode_nmarks = 2 + (range / fr);
758 	} else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
759 		timecode_ruler_scale = timecode_show_minutes;
760 		timecode_mark_modulo = 2;
761 		timecode_nmarks = 2 + 10;
762 	} else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
763 		timecode_ruler_scale = timecode_show_minutes;
764 		timecode_mark_modulo = 5;
765 		timecode_nmarks = 2 + 30;
766 	} else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
767 		timecode_ruler_scale = timecode_show_minutes;
768 		timecode_mark_modulo = 10;
769 		timecode_nmarks = 2 + 60;
770 	} else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
771 		timecode_ruler_scale = timecode_show_minutes;
772 		timecode_mark_modulo = 30;
773 		timecode_nmarks = 2 + (60 * 4);
774 	} else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
775 		timecode_ruler_scale = timecode_show_hours;
776 		timecode_mark_modulo = 1;
777 		timecode_nmarks = 2 + 8;
778 	} else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
779 		timecode_ruler_scale = timecode_show_hours;
780 		timecode_mark_modulo = 1;
781 		timecode_nmarks = 2 + 24;
782 	} else {
783 
784 		const samplecnt_t hours_in_range = range / (60 * 60 * fr);
785 		const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
786 
787 		/* Normally we do not need to know anything about the width of the canvas
788 		   to set the ruler scale, because the caller has already determined
789 		   the width and set lower + upper arguments to this function to match that.
790 
791 		   But in this case, where the range defined by lower and uppper can vary
792 		   substantially (basically anything from 24hrs+ to several billion years)
793 		   trying to decide which tick marks to show does require us to know
794 		   about the available width.
795 		*/
796 
797 		timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
798 		timecode_ruler_scale = timecode_show_many_hours;
799 		timecode_mark_modulo = std::max ((samplecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
800 	}
801 }
802 
803 void
metric_get_timecode(std::vector<ArdourCanvas::Ruler::Mark> & marks,gdouble lower,gdouble,gint)804 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
805 {
806 	samplepos_t pos;
807 	samplecnt_t spacer;
808 	Timecode::Time timecode;
809 	gchar buf[16];
810 	gint n;
811 	ArdourCanvas::Ruler::Mark mark;
812 
813 	if (_session == 0) {
814 		return;
815 	}
816 
817 	if (lower > (spacer = (samplecnt_t)(128 * Editor::get_current_zoom ()))) {
818 		lower = lower - spacer;
819 	} else {
820 		lower = 0;
821 	}
822 
823 	pos = (samplecnt_t) floor (lower);
824 
825 	switch (timecode_ruler_scale) {
826 	case timecode_show_bits:
827 		// Find timecode time of this sample (pos) with subframe accuracy
828 		_session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */);
829 		for (n = 0; n < timecode_nmarks; n++) {
830 			_session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */);
831 			if ((timecode.subframes % timecode_mark_modulo) == 0) {
832 				if (timecode.subframes == 0) {
833 					mark.style = ArdourCanvas::Ruler::Mark::Major;
834 					snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
835 				} else {
836 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
837 					snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
838 				}
839 			} else {
840 				snprintf (buf, sizeof(buf)," ");
841 				mark.style = ArdourCanvas::Ruler::Mark::Micro;
842 			}
843 			mark.label = buf;
844 			mark.position = pos;
845 			marks.push_back (mark);
846 			// Increment subframes by one
847 			Timecode::increment_subframes (timecode, _session->config.get_subframes_per_frame());
848 		}
849 		break;
850 
851 	case timecode_show_samples:
852 		// Find timecode time of this sample (pos)
853 		_session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
854 		// Go to next whole sample down
855 		Timecode::frames_floot (timecode);
856 		for (n = 0; n < timecode_nmarks; n++) {
857 			_session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
858 			if ((timecode.frames % timecode_mark_modulo) == 0) {
859 				if (timecode.frames == 0) {
860 					mark.style = ArdourCanvas::Ruler::Mark::Major;
861 				} else {
862 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
863 				}
864 				mark.position = pos;
865 				snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
866 			} else {
867 				snprintf (buf, sizeof(buf)," ");
868 				mark.style = ArdourCanvas::Ruler::Mark::Micro;
869 				mark.position = pos;
870 			}
871 			mark.label = buf;
872 			marks.push_back (mark);
873 			Timecode::increment (timecode, _session->config.get_subframes_per_frame());
874 		}
875 		break;
876 
877 	case timecode_show_seconds:
878 		// Find timecode time of this sample (pos)
879 		_session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
880 		// Go to next whole second down
881 		Timecode::seconds_floor (timecode);
882 		for (n = 0; n < timecode_nmarks; n++) {
883 			_session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
884 			if ((timecode.seconds % timecode_mark_modulo) == 0) {
885 				if (timecode.seconds == 0) {
886 					mark.style = ArdourCanvas::Ruler::Mark::Major;
887 					mark.position = pos;
888 				} else {
889 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
890 					mark.position = pos;
891 				}
892 				snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
893 			} else {
894 				snprintf (buf, sizeof(buf)," ");
895 				mark.style = ArdourCanvas::Ruler::Mark::Micro;
896 				mark.position = pos;
897 			}
898 			mark.label = buf;
899 			marks.push_back (mark);
900 			Timecode::increment_seconds (timecode, _session->config.get_subframes_per_frame());
901 		}
902 		break;
903 
904 	case timecode_show_minutes:
905 		//Find timecode time of this sample (pos)
906 		_session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
907 		// Go to next whole minute down
908 		Timecode::minutes_floor (timecode);
909 		for (n = 0; n < timecode_nmarks; n++) {
910 			_session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
911 			if ((timecode.minutes % timecode_mark_modulo) == 0) {
912 				if (timecode.minutes == 0) {
913 					mark.style = ArdourCanvas::Ruler::Mark::Major;
914 				} else {
915 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
916 				}
917 				snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
918 			} else {
919 				snprintf (buf, sizeof(buf)," ");
920 				mark.style = ArdourCanvas::Ruler::Mark::Micro;
921 			}
922 			mark.label = buf;
923 			mark.position = pos;
924 			marks.push_back (mark);
925 			Timecode::increment_minutes (timecode, _session->config.get_subframes_per_frame());
926 		}
927 		break;
928 	case timecode_show_hours:
929 		// Find timecode time of this sample (pos)
930 		_session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
931 		// Go to next whole hour down
932 		Timecode::hours_floor (timecode);
933 		for (n = 0; n < timecode_nmarks; n++) {
934 			_session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
935 			if ((timecode.hours % timecode_mark_modulo) == 0) {
936 				mark.style = ArdourCanvas::Ruler::Mark::Major;
937 				snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
938 			} else {
939 				snprintf (buf, sizeof(buf)," ");
940 				mark.style = ArdourCanvas::Ruler::Mark::Micro;
941 			}
942 			mark.label = buf;
943 			mark.position = pos;
944 			marks.push_back (mark);
945 			Timecode::increment_hours (timecode, _session->config.get_subframes_per_frame());
946 		}
947 		break;
948 	case timecode_show_many_hours:
949 		// Find timecode time of this sample (pos)
950 		_session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
951 		// Go to next whole hour down
952 		Timecode::hours_floor (timecode);
953 
954 		for (n = 0; n < timecode_nmarks;) {
955 			_session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
956 			if ((timecode.hours % timecode_mark_modulo) == 0) {
957 				mark.style = ArdourCanvas::Ruler::Mark::Major;
958 				snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
959 				mark.label = buf;
960 				mark.position = pos;
961 				marks.push_back (mark);
962 				++n;
963 			}
964 			/* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
965 			 * and doing it 1 hour at a time is just stupid (and slow).
966 			 */
967 			timecode.hours += timecode_mark_modulo - (timecode.hours % timecode_mark_modulo);
968 		}
969 		break;
970 	}
971 }
972 
973 void
compute_bbt_ruler_scale(samplepos_t lower,samplepos_t upper)974 Editor::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper)
975 {
976 	if (_session == 0) {
977 		return;
978 	}
979 
980 	Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
981 	double floor_lower_beat = floor(std::max (0.0, _session->tempo_map().beat_at_sample (lower)));
982 
983 	if (floor_lower_beat < 0.0) {
984 		floor_lower_beat = 0.0;
985 	}
986 
987 	const samplecnt_t beat_before_lower_pos = _session->tempo_map().sample_at_beat (floor_lower_beat);
988 	const samplecnt_t beat_after_upper_pos = _session->tempo_map().sample_at_beat (floor (std::max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0);
989 
990 	_session->bbt_time (beat_before_lower_pos, lower_beat);
991 	_session->bbt_time (beat_after_upper_pos, upper_beat);
992 	uint32_t beats = 0;
993 
994 	bbt_bar_helper_on = false;
995 	bbt_bars = 0;
996 	bbt_nmarks = 1;
997 
998 	bbt_ruler_scale =  bbt_show_many;
999 
1000 	const double ceil_upper_beat = floor (std::max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0;
1001 	if (ceil_upper_beat == floor_lower_beat) {
1002 		return;
1003 	}
1004 
1005 	bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1006 
1007 	double ruler_line_granularity = UIConfiguration::instance().get_ruler_granularity ();  //in pixels
1008 	ruler_line_granularity = _visible_canvas_width / (ruler_line_granularity*5);  //fudge factor '5' probably related to (4+1 beats)/measure, I think
1009 
1010 	beats = (ceil_upper_beat - floor_lower_beat);
1011 	double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / (float)ruler_line_granularity;
1012 
1013 	/* Only show the bar helper if there aren't many bars on the screen */
1014 	if ((bbt_bars < 2) || (beats < 5)) {
1015 		bbt_bar_helper_on = true;
1016 	}
1017 
1018 	if (beat_density > 2048) {
1019 		bbt_ruler_scale = bbt_show_many;
1020 	} else if (beat_density > 1024) {
1021 		bbt_ruler_scale = bbt_show_64;
1022 	} else if (beat_density > 256) {
1023 		bbt_ruler_scale = bbt_show_16;
1024 	} else if (beat_density > 64) {
1025 		bbt_ruler_scale = bbt_show_4;
1026 	} else if (beat_density > 16) {
1027 		bbt_ruler_scale = bbt_show_1;
1028 	} else if (beat_density > 4) {
1029 		bbt_ruler_scale =  bbt_show_quarters;
1030 	} else  if (beat_density > 2) {
1031 		bbt_ruler_scale =  bbt_show_eighths;
1032 	} else  if (beat_density > 1) {
1033 		bbt_ruler_scale =  bbt_show_sixteenths;
1034 	} else  if (beat_density > 0.5) {
1035 		bbt_ruler_scale =  bbt_show_thirtyseconds;
1036 	} else  if (beat_density > 0.25) {
1037 		bbt_ruler_scale =  bbt_show_sixtyfourths;
1038 	} else {
1039 		bbt_ruler_scale =  bbt_show_onetwentyeighths;
1040 	}
1041 
1042 	/* Now that we know how fine a grid (Ruler) is allowable on this screen, limit it to the coarseness selected by the user */
1043 	/* note: GridType and RulerScale are not the same enums, so it's not a simple mathematical operation */
1044 	int suggested_scale = (int) bbt_ruler_scale;
1045 	int divs = get_grid_music_divisions(_grid_type);
1046 	if (_grid_type == GridTypeBar) {
1047 		suggested_scale = std::min(suggested_scale, (int) bbt_show_1);
1048 	} else if (_grid_type == GridTypeBeat) {
1049 		suggested_scale = std::min(suggested_scale, (int) bbt_show_quarters);
1050 	}  else if ( divs < 4 ) {
1051 		suggested_scale = std::min(suggested_scale, (int) bbt_show_eighths);
1052 	}  else if ( divs < 8 ) {
1053 		suggested_scale = std::min(suggested_scale, (int) bbt_show_sixteenths);
1054 	} else if ( divs < 16 ) {
1055 		suggested_scale = std::min(suggested_scale, (int) bbt_show_thirtyseconds);
1056 	} else if ( divs < 32 ) {
1057 		suggested_scale = std::min(suggested_scale, (int) bbt_show_sixtyfourths);
1058 	} else {
1059 		suggested_scale = std::min(suggested_scale, (int) bbt_show_onetwentyeighths);
1060 	}
1061 
1062 	bbt_ruler_scale = (Editor::BBTRulerScale) suggested_scale;
1063 }
1064 
1065 static void
edit_last_mark_label(std::vector<ArdourCanvas::Ruler::Mark> & marks,const std::string & newlabel)1066 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1067 {
1068 	ArdourCanvas::Ruler::Mark copy = marks.back();
1069 	copy.label = newlabel;
1070 	marks.pop_back ();
1071 	marks.push_back (copy);
1072 }
1073 
1074 void
metric_get_bbt(std::vector<ArdourCanvas::Ruler::Mark> & marks,gdouble lower,gdouble upper,gint)1075 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1076 {
1077 	if (_session == 0) {
1078 		return;
1079 	}
1080 
1081 	std::vector<TempoMap::BBTPoint>::const_iterator i;
1082 
1083 	char buf[64];
1084 	gint  n = 0;
1085 	samplepos_t pos;
1086 	Timecode::BBT_Time next_beat;
1087 	uint32_t beats = 0;
1088 	uint32_t tick = 0;
1089 	uint32_t skip;
1090 	uint32_t t;
1091 	double bbt_position_of_helper;
1092 	bool i_am_accented = false;
1093 	bool helper_active = false;
1094 	ArdourCanvas::Ruler::Mark mark;
1095 
1096 	std::vector<TempoMap::BBTPoint> grid;
1097 
1098 	compute_current_bbt_points (grid, lower, upper);
1099 
1100 	if (distance (grid.begin(), grid.end()) == 0) {
1101 		return;
1102 	}
1103 
1104 	/* we can accent certain lines depending on the user's Grid choice */
1105 	/* for example, even in a 4/4 meter we can draw a grid with triplet-feel */
1106 	/* and in this case you will want the accents on '3s' not '2s' */
1107 	uint32_t bbt_divisor = 2;
1108 	uint32_t bbt_accent_modulo = 2;
1109 	switch (_grid_type) {
1110 	case GridTypeBeatDiv3:
1111 		bbt_divisor = 3;
1112 		bbt_accent_modulo = 3;
1113 		break;
1114 	case GridTypeBeatDiv5:
1115 		bbt_divisor = 5;
1116 		bbt_accent_modulo = 5;
1117 		break;
1118 	case GridTypeBeatDiv6:
1119 		bbt_divisor = 3;
1120 		bbt_accent_modulo = 3;
1121 		break;
1122 	case GridTypeBeatDiv7:
1123 		bbt_divisor = 7;
1124 		bbt_accent_modulo = 7;
1125 		break;
1126 	case GridTypeBeatDiv10:
1127 		bbt_divisor = 5;
1128 		bbt_accent_modulo = 5;
1129 		break;
1130 	case GridTypeBeatDiv12:
1131 		bbt_divisor = 3;
1132 		bbt_accent_modulo = 3;
1133 		break;
1134 	case GridTypeBeatDiv14:
1135 		bbt_divisor = 7;
1136 		bbt_accent_modulo = 7;
1137 		break;
1138 	case GridTypeBeatDiv16:
1139 		bbt_accent_modulo = 4;
1140 		break;
1141 	case GridTypeBeatDiv20:
1142 		bbt_divisor = 5;
1143 		bbt_accent_modulo = 5;
1144 		break;
1145 	case GridTypeBeatDiv24:
1146 		bbt_divisor = 6;
1147 		bbt_accent_modulo = 6;
1148 		break;
1149 	case GridTypeBeatDiv28:
1150 		bbt_divisor = 7;
1151 		bbt_accent_modulo = 7;
1152 		break;
1153 	case GridTypeBeatDiv32:
1154 		bbt_accent_modulo = 8;
1155 		break;
1156 	default:
1157 		bbt_divisor = 2;
1158 		bbt_accent_modulo = 2;
1159 		break;
1160 	}
1161 
1162 	uint32_t bbt_beat_subdivision = 1;
1163 	switch (bbt_ruler_scale) {
1164 	case bbt_show_quarters:
1165 		bbt_beat_subdivision = 1;
1166 		break;
1167 	case bbt_show_eighths:
1168 		bbt_beat_subdivision = 1;
1169 		break;
1170 	case bbt_show_sixteenths:
1171 		bbt_beat_subdivision = 2;
1172 		break;
1173 	case bbt_show_thirtyseconds:
1174 		bbt_beat_subdivision = 4;
1175 		break;
1176 	case bbt_show_sixtyfourths:
1177 		bbt_beat_subdivision = 8;
1178 		break;
1179 	case bbt_show_onetwentyeighths:
1180 		bbt_beat_subdivision = 16;
1181 		break;
1182 	default:
1183 		bbt_beat_subdivision = 1;
1184 		break;
1185 	}
1186 
1187 	bbt_beat_subdivision *= bbt_divisor;
1188 
1189 	switch (bbt_ruler_scale) {
1190 
1191 	case bbt_show_many:
1192 		bbt_nmarks = 1;
1193 		snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars);
1194 		mark.style = ArdourCanvas::Ruler::Mark::Major;
1195 		mark.label = buf;
1196 		mark.position = lower;
1197 		marks.push_back (mark);
1198 		break;
1199 
1200 	case bbt_show_64:
1201 			bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1202 			for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1203 				if ((*i).is_bar()) {
1204 					if ((*i).bar % 64 == 1) {
1205 						if ((*i).bar % 256 == 1) {
1206 							snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1207 							mark.style = ArdourCanvas::Ruler::Mark::Major;
1208 						} else {
1209 							buf[0] = '\0';
1210 							if ((*i).bar % 256 == 129)  {
1211 								mark.style = ArdourCanvas::Ruler::Mark::Minor;
1212 							} else {
1213 								mark.style = ArdourCanvas::Ruler::Mark::Micro;
1214 							}
1215 						}
1216 						mark.label = buf;
1217 						mark.position = (*i).sample;
1218 						marks.push_back (mark);
1219 						++n;
1220 					}
1221 				}
1222 			}
1223 			break;
1224 
1225 	case bbt_show_16:
1226 		bbt_nmarks = (bbt_bars / 16) + 1;
1227 		for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1228 			if ((*i).is_bar()) {
1229 			  if ((*i).bar % 16 == 1) {
1230 				if ((*i).bar % 64 == 1) {
1231 					snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1232 					mark.style = ArdourCanvas::Ruler::Mark::Major;
1233 				} else {
1234 					buf[0] = '\0';
1235 					if ((*i).bar % 64 == 33)  {
1236 						mark.style = ArdourCanvas::Ruler::Mark::Minor;
1237 					} else {
1238 						mark.style = ArdourCanvas::Ruler::Mark::Micro;
1239 					}
1240 				}
1241 				mark.label = buf;
1242 				mark.position = (*i).sample;
1243 				marks.push_back (mark);
1244 				++n;
1245 			  }
1246 			}
1247 		}
1248 	  break;
1249 
1250 	case bbt_show_4:
1251 		bbt_nmarks = (bbt_bars / 4) + 1;
1252 		for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1253 			if ((*i).is_bar()) {
1254 				if ((*i).bar % 4 == 1) {
1255 					if ((*i).bar % 16 == 1) {
1256 						snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1257 						mark.style = ArdourCanvas::Ruler::Mark::Major;
1258 					} else {
1259 						buf[0] = '\0';
1260 						if ((*i).bar % 16 == 9)  {
1261 							mark.style = ArdourCanvas::Ruler::Mark::Minor;
1262 						} else {
1263 							mark.style = ArdourCanvas::Ruler::Mark::Micro;
1264 						}
1265 					}
1266 					mark.label = buf;
1267 					mark.position = (*i).sample;
1268 					marks.push_back (mark);
1269 					++n;
1270 				}
1271 			}
1272 		}
1273 	  break;
1274 
1275 	case bbt_show_1:
1276 		bbt_nmarks = bbt_bars + 2;
1277 		for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1278 			if ((*i).is_bar()) {
1279 				snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bbt().bars);
1280 				mark.style = ArdourCanvas::Ruler::Mark::Major;
1281 				mark.label = buf;
1282 				mark.position = (*i).sample;
1283 				marks.push_back (mark);
1284 				++n;
1285 			}
1286 		}
1287 	break;
1288 
1289 	case bbt_show_quarters:
1290 
1291 		beats = distance (grid.begin(), grid.end());
1292 		bbt_nmarks = beats + 2;
1293 
1294 		mark.label = "";
1295 		mark.position = lower;
1296 		mark.style = ArdourCanvas::Ruler::Mark::Micro;
1297 		marks.push_back (mark);
1298 
1299 		for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1300 
1301 			if ((*i).sample < lower && (bbt_bar_helper_on)) {
1302 				snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1303 				edit_last_mark_label (marks, buf);
1304 			} else {
1305 
1306 				if ((*i).is_bar()) {
1307 					mark.style = ArdourCanvas::Ruler::Mark::Major;
1308 					snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1309 				} else if (((*i).beat % 2 == 1)) {
1310 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
1311 					buf[0] = '\0';
1312 				} else {
1313 					mark.style = ArdourCanvas::Ruler::Mark::Micro;
1314 					buf[0] = '\0';
1315 				}
1316 				mark.label = buf;
1317 				mark.position = (*i).sample;
1318 				marks.push_back (mark);
1319 				n++;
1320 			}
1321 		}
1322 		break;
1323 
1324 	case bbt_show_eighths:
1325 	case bbt_show_sixteenths:
1326 	case bbt_show_thirtyseconds:
1327 	case bbt_show_sixtyfourths:
1328 	case bbt_show_onetwentyeighths:
1329 
1330 		beats = distance (grid.begin(), grid.end());
1331 		bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1332 
1333 		bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1334 
1335 		mark.label = "";
1336 		mark.position = lower;
1337 		mark.style = ArdourCanvas::Ruler::Mark::Micro;
1338 		marks.push_back (mark);
1339 
1340 		for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1341 
1342 			if ((*i).sample < lower && (bbt_bar_helper_on)) {
1343 				snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1344 				edit_last_mark_label (marks, buf);
1345 				helper_active = true;
1346 			} else {
1347 
1348 				if ((*i).is_bar()) {
1349 					mark.style = ArdourCanvas::Ruler::Mark::Major;
1350 					snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1351 				} else {
1352 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
1353 					snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1354 				}
1355 				if (((*i).sample < bbt_position_of_helper) && helper_active) {
1356 					buf[0] = '\0';
1357 				}
1358 				mark.label =  buf;
1359 				mark.position = (*i).sample;
1360 				marks.push_back (mark);
1361 				n++;
1362 			}
1363 
1364 			/* Add the tick marks */
1365 			skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1366 			tick = skip; // the first non-beat tick
1367 
1368 			t = 0;
1369 			while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1370 
1371 				next_beat.beats = (*i).beat;
1372 				next_beat.bars = (*i).bar;
1373 				next_beat.ticks = tick;
1374 				pos = _session->tempo_map().sample_at_bbt (next_beat);
1375 
1376 				if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1377 					i_am_accented = true;
1378 				}
1379 				if (i_am_accented && (pos > bbt_position_of_helper)){
1380 					snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1381 				} else {
1382 					buf[0] = '\0';
1383 				}
1384 
1385 				mark.label = buf;
1386 				mark.position = pos;
1387 
1388 				if ((bbt_beat_subdivision > 4) && i_am_accented) {
1389 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
1390 				} else {
1391 					mark.style = ArdourCanvas::Ruler::Mark::Micro;
1392 				}
1393 				i_am_accented = false;
1394 				marks.push_back (mark);
1395 
1396 				tick += skip;
1397 				++t;
1398 				++n;
1399 			}
1400 		}
1401 
1402 	  break;
1403 
1404 	}
1405 }
1406 
1407 void
set_samples_ruler_scale(samplepos_t lower,samplepos_t upper)1408 Editor::set_samples_ruler_scale (samplepos_t lower, samplepos_t upper)
1409 {
1410 	_samples_ruler_interval = (upper - lower) / 5;
1411 }
1412 
1413 void
metric_get_samples(std::vector<ArdourCanvas::Ruler::Mark> & marks,gdouble lower,gdouble,gint)1414 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1415 {
1416 	samplepos_t pos;
1417 	samplepos_t const ilower = (samplepos_t) floor (lower);
1418 	gchar buf[16];
1419 	gint nmarks;
1420 	gint n;
1421 	ArdourCanvas::Ruler::Mark mark;
1422 
1423 	if (_session == 0) {
1424 		return;
1425 	}
1426 
1427 	nmarks = 5;
1428 	for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1429 		snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1430 		mark.label = buf;
1431 		mark.position = pos;
1432 		mark.style = ArdourCanvas::Ruler::Mark::Major;
1433 		marks.push_back (mark);
1434 	}
1435 }
1436 
1437 static void
sample_to_clock_parts(samplepos_t sample,samplepos_t sample_rate,long * hrs_p,long * mins_p,long * secs_p,long * millisecs_p)1438 sample_to_clock_parts (samplepos_t sample,
1439                        samplepos_t sample_rate,
1440                        long*       hrs_p,
1441                        long*       mins_p,
1442                        long*       secs_p,
1443                        long*       millisecs_p)
1444 {
1445 	samplepos_t left;
1446 	long hrs;
1447 	long mins;
1448 	long secs;
1449 	long millisecs;
1450 
1451 	left = sample;
1452 	hrs = left / (sample_rate * 60 * 60 * 1000);
1453 	left -= hrs * sample_rate * 60 * 60 * 1000;
1454 	mins = left / (sample_rate * 60 * 1000);
1455 	left -= mins * sample_rate * 60 * 1000;
1456 	secs = left / (sample_rate * 1000);
1457 	left -= secs * sample_rate * 1000;
1458 	millisecs = left / sample_rate;
1459 
1460 	*millisecs_p = millisecs;
1461 	*secs_p = secs;
1462 	*mins_p = mins;
1463 	*hrs_p = hrs;
1464 
1465 	return;
1466 }
1467 
1468 void
set_minsec_ruler_scale(samplepos_t lower,samplepos_t upper)1469 Editor::set_minsec_ruler_scale (samplepos_t lower, samplepos_t upper)
1470 {
1471 	samplepos_t fr = _session->sample_rate() * 1000;
1472 	samplepos_t spacer;
1473 
1474 	if (_session == 0) {
1475 		return;
1476 	}
1477 
1478 
1479 	/* to prevent 'flashing' */
1480 	if (lower > (spacer = (samplepos_t)(128 * Editor::get_current_zoom ()))) {
1481 		lower -= spacer;
1482 	} else {
1483 		lower = 0;
1484 	}
1485 	upper += spacer;
1486 	samplecnt_t const range = (upper - lower) * 1000;
1487 
1488 	if (range <= (fr / 10)) { /* 0-0.1 second */
1489 		minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1490 		minsec_ruler_scale = minsec_show_msecs;
1491 		minsec_mark_modulo = 10;
1492 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1493 	} else if (range <= (fr / 2)) { /* 0-0.5 second */
1494 		minsec_mark_interval = fr / 100;  /* show 1/100 seconds */
1495 		minsec_ruler_scale = minsec_show_msecs;
1496 		minsec_mark_modulo = 100;
1497 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1498 	} else if (range <= fr) { /* 0-1 second */
1499 		minsec_mark_interval = fr / 10;  /* show 1/10 seconds */
1500 		minsec_ruler_scale = minsec_show_msecs;
1501 		minsec_mark_modulo = 200;
1502 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1503 	} else if (range <= 2 * fr) { /* 1-2 seconds */
1504 		minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1505 		minsec_ruler_scale = minsec_show_msecs;
1506 		minsec_mark_modulo = 500;
1507 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1508 	} else if (range <= 8 * fr) { /* 2-5 seconds */
1509 		minsec_mark_interval =  fr / 5; /* show 2 seconds */
1510 		minsec_ruler_scale = minsec_show_msecs;
1511 		minsec_mark_modulo = 1000;
1512 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1513 	} else if (range <= 16 * fr) { /* 8-16 seconds */
1514 		minsec_mark_interval =  fr; /* show 1 seconds */
1515 		minsec_ruler_scale = minsec_show_seconds;
1516 		minsec_mark_modulo = 2;
1517 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1518 	} else if (range <= 30 * fr) { /* 10-30 seconds */
1519 		minsec_mark_interval =  fr; /* show 1 seconds */
1520 		minsec_ruler_scale = minsec_show_seconds;
1521 		minsec_mark_modulo = 5;
1522 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1523 	} else if (range <= 60 * fr) { /* 30-60 seconds */
1524 		minsec_mark_interval = fr; /* show 1 seconds */
1525 		minsec_ruler_scale = minsec_show_seconds;
1526 		minsec_mark_modulo = 5;
1527 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1528 	} else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1529 		minsec_mark_interval = 5 * fr; /* show 5 seconds */
1530 		minsec_ruler_scale = minsec_show_seconds;
1531 		minsec_mark_modulo = 3;
1532 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1533 	} else if (range <= 4 * 60 * fr) { /* 4 minutes */
1534 		minsec_mark_interval = 5 * fr; /* show 10 seconds */
1535 		minsec_ruler_scale = minsec_show_seconds;
1536 		minsec_mark_modulo = 30;
1537 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1538 	} else if (range <= 10 * 60 * fr) { /* 10 minutes */
1539 		minsec_mark_interval = 30 * fr; /* show 30 seconds */
1540 		minsec_ruler_scale = minsec_show_seconds;
1541 		minsec_mark_modulo = 120;
1542 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1543 	} else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1544 		minsec_mark_interval =  60 * fr; /* show 1 minute */
1545 		minsec_ruler_scale = minsec_show_minutes;
1546 		minsec_mark_modulo = 5;
1547 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1548 	} else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1549 		minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1550 		minsec_ruler_scale = minsec_show_minutes;
1551 		minsec_mark_modulo = 10;
1552 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1553 	} else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1554 		minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1555 		minsec_ruler_scale = minsec_show_minutes;
1556 		minsec_mark_modulo = 30;
1557 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1558 	} else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1559 		minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1560 		minsec_ruler_scale = minsec_show_minutes;
1561 		minsec_mark_modulo = 60;
1562 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1563 	} else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1564 		minsec_mark_interval =  60 * 60 * fr; /* show 60 minutes */
1565 		minsec_ruler_scale = minsec_show_hours;
1566 		minsec_mark_modulo = 2;
1567 		minsec_nmarks = 2 + (range / minsec_mark_interval);
1568 	} else {
1569 
1570 		const samplecnt_t hours_in_range = range / (60 * 60 * fr);
1571 		const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1572 
1573 		/* Normally we do not need to know anything about the width of the canvas
1574 		   to set the ruler scale, because the caller has already determined
1575 		   the width and set lower + upper arguments to this function to match that.
1576 
1577 		   But in this case, where the range defined by lower and uppper can vary
1578 		   substantially (anything from 24hrs+ to several billion years)
1579 		   trying to decide which tick marks to show does require us to know
1580 		   about the available width.
1581 		*/
1582 
1583 		minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1584 		minsec_mark_modulo = std::max ((samplecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1585 		minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1586 		minsec_ruler_scale = minsec_show_many_hours;
1587 	}
1588 }
1589 
1590 void
metric_get_minsec(std::vector<ArdourCanvas::Ruler::Mark> & marks,gdouble lower,gdouble upper,gint)1591 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1592 {
1593 	samplepos_t pos;
1594 	samplepos_t spacer;
1595 	long hrs, mins, secs, millisecs;
1596 	gchar buf[16];
1597 	gint n;
1598 	ArdourCanvas::Ruler::Mark mark;
1599 
1600 	if (_session == 0) {
1601 		return;
1602 	}
1603 
1604 	/* to prevent 'flashing' */
1605 	if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
1606 		lower = lower - spacer;
1607 	} else {
1608 		lower = 0;
1609 	}
1610 
1611 	if (minsec_mark_interval == 0) {  //we got here too early; divide-by-zero imminent
1612 		return;
1613 	}
1614 
1615 	pos = (((1000 * (samplepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1616 
1617 	switch (minsec_ruler_scale) {
1618 
1619 	case minsec_show_msecs:
1620 		for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1621 			sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1622 			if (millisecs % minsec_mark_modulo == 0) {
1623 				if (millisecs == 0) {
1624 					mark.style = ArdourCanvas::Ruler::Mark::Major;
1625 				} else {
1626 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
1627 				}
1628 				snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1629 			} else {
1630 				buf[0] = '\0';
1631 				mark.style = ArdourCanvas::Ruler::Mark::Micro;
1632 			}
1633 			mark.label = buf;
1634 			mark.position = pos/1000.0;
1635 			marks.push_back (mark);
1636 		}
1637 		break;
1638 
1639 	case minsec_show_seconds:
1640 		for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1641 			sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1642 			if (secs % minsec_mark_modulo == 0) {
1643 				if (secs == 0) {
1644 					mark.style = ArdourCanvas::Ruler::Mark::Major;
1645 				} else {
1646 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
1647 				}
1648 				snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1649 			} else {
1650 				buf[0] = '\0';
1651 				mark.style = ArdourCanvas::Ruler::Mark::Micro;
1652 			}
1653 			mark.label = buf;
1654 			mark.position = pos/1000.0;
1655 			marks.push_back (mark);
1656 		}
1657 		break;
1658 
1659 	case minsec_show_minutes:
1660 		for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1661 			sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1662 			if (mins % minsec_mark_modulo == 0) {
1663 				if (mins == 0) {
1664 					mark.style = ArdourCanvas::Ruler::Mark::Major;
1665 				} else {
1666 					mark.style = ArdourCanvas::Ruler::Mark::Minor;
1667 				}
1668 				snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1669 			} else {
1670 				buf[0] = '\0';
1671 				mark.style = ArdourCanvas::Ruler::Mark::Micro;
1672 			}
1673 			mark.label = buf;
1674 			mark.position = pos/1000.0;
1675 			marks.push_back (mark);
1676 		}
1677 		break;
1678 
1679 	case minsec_show_hours:
1680 		 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1681 			sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1682 			if (hrs % minsec_mark_modulo == 0) {
1683 				mark.style = ArdourCanvas::Ruler::Mark::Major;
1684 				snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1685 			} else {
1686 				buf[0] = '\0';
1687 				mark.style = ArdourCanvas::Ruler::Mark::Micro;
1688 			}
1689 			mark.label = buf;
1690 			mark.position = pos/1000.0;
1691 			marks.push_back (mark);
1692 		 }
1693 		 break;
1694 
1695 	case minsec_show_many_hours:
1696 		for (n = 0; n < minsec_nmarks;) {
1697 			sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1698 			if (hrs % minsec_mark_modulo == 0) {
1699 				mark.style = ArdourCanvas::Ruler::Mark::Major;
1700 				snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1701 				mark.label = buf;
1702 				mark.position = pos/1000.0;
1703 				marks.push_back (mark);
1704 				++n;
1705 			}
1706 			pos += minsec_mark_interval;
1707 		}
1708 		break;
1709 	}
1710 }
1711