1 /*  cdrdao - write audio CD-Rs in disc-at-once mode
2  *
3  *  Copyright (C) 1998-2002  Andreas Mueller <andreas@daneb.de>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include <stdio.h>
21 #include <limits.h>
22 #include <math.h>
23 #include <assert.h>
24 #include <iostream>
25 
26 #include <gtkmm.h>
27 #include <gnome.h>
28 
29 #include "TocEdit.h"
30 #include "SampleDisplay.h"
31 #include "SampleManager.h"
32 #include "TrackManager.h"
33 
34 #include "Toc.h"
35 #include "util.h"
36 #include "log.h"
37 
38 
39 /* XPM data for track marker */
40 #define TRACK_MARKER_XPM_WIDTH 9
41 #define TRACK_MARKER_XPM_HEIGHT 12
42 static const gchar *TRACK_MARKER_XPM_DATA[] = {
43   "9 12 3 1",
44   "       c None",
45   "X      c #000000000000",
46   ".      c #FFFFFFFFFFFF",
47   "         ",
48   " XXXXXXX ",
49   " XXXXXXX ",
50   " XXXXXXX ",
51   " XXXXXXX ",
52   " XXXXXXX ",
53   " XXXXXXX ",
54   "  XXXXX  ",
55   "   XXX   ",
56   "    X    ",
57   "    X    ",
58   "    X    "};
59 
60 /* XPM data for index marker */
61 static const gchar *INDEX_MARKER_XPM_DATA[] = {
62   "9 12 3 1",
63   "       c None",
64   "X      c #000000000000",
65   ".      c #FFFFFFFFFFFF",
66   "         ",
67   " XXXXXXX ",
68   " X.....X ",
69   " X.....X ",
70   " X.....X ",
71   " X.....X ",
72   " X.....X ",
73   "  X...X  ",
74   "   X.X   ",
75   "    X    ",
76   "    X    ",
77   "    X    "};
78 
79 /* XPM data for extend track marker */
80 static const gchar *TRACK_EXTEND_XPM_DATA[] = {
81   "9 12 3 1",
82   "       c None",
83   "X      c #000000000000",
84   ".      c #FFFFFFFFFFFF",
85   "......XX.",
86   ".....XXX.",
87   "....XXXX.",
88   "...XXXXX.",
89   "..XXXXXX.",
90   ".XXXXXXX.",
91   "..XXXXXX.",
92   "...XXXXX.",
93   "....XXXX.",
94   ".....XXX.",
95   "......XX.",
96   "........."};
97 
98 /* XPM data for extend track marker */
99 static const gchar *INDEX_EXTEND_XPM_DATA[] = {
100   "9 12 3 1",
101   "       c None",
102   "X      c #000000000000",
103   ".      c #FFFFFFFFFFFF",
104   "......XX.",
105   ".....X.X.",
106   "....X..X.",
107   "...X...X.",
108   "..X....X.",
109   ".X.....X.",
110   "..X....X.",
111   "...X...X.",
112   "....X..X.",
113   ".....X.X.",
114   "......XX.",
115   "........."};
116 
117 
SampleDisplay()118 SampleDisplay::SampleDisplay():
119 	pixmap_(NULL),
120 	trackMarkerPixmap_(NULL),
121 	indexMarkerPixmap_(NULL),
122 	trackMarkerSelectedPixmap_(NULL),
123 	indexMarkerSelectedPixmap_(NULL),
124 	trackExtendPixmap_(NULL),
125 	indexExtendPixmap_(NULL),
126 	drawGc_(NULL)
127 
128 {
129   adjustment_ = new Gtk::Adjustment(0.0, 0.0, 1.0);
130   adjustment_->signal_value_changed().connect(mem_fun(*this,
131                                                    &SampleDisplay::scrollTo));
132 
133   trackManager_ = NULL;
134 
135   width_ = height_ = chanHeight_ = lcenter_ = rcenter_ = 0;
136   timeLineHeight_ = timeLineY_ = 0;
137   timeTickWidth_ = 0;
138   timeTickSep_ = 20;
139   sampleStartX_ = sampleEndX_ = sampleWidthX_ = 0;
140   minSample_ = maxSample_ = resolution_ = 0;
141   tocEdit_ = NULL;
142 
143   chanSep_ = 10;
144 
145   cursorControlExtern_ = false;
146   cursorDrawn_ = false;
147   cursorX_ = 0;
148 
149   markerSet_ = false;
150   selectionSet_ = false;
151   regionSet_ = false;
152   dragMode_ = DRAG_NONE;
153 
154   pickedTrackMarker_ = NULL;
155   selectedTrack_ = 0;
156   selectedIndex_ = 0;
157 
158   signal_expose_event().connect(mem_fun(*this,
159                                      &SampleDisplay::handleExposeEvent));
160   signal_configure_event().
161     connect(mem_fun(*this, &SampleDisplay::handleConfigureEvent));
162   signal_motion_notify_event().
163     connect(mem_fun(*this, &SampleDisplay::handleMotionNotifyEvent));
164   signal_button_press_event().
165     connect(mem_fun(*this, &SampleDisplay::handleButtonPressEvent));
166   signal_button_release_event().
167     connect(mem_fun(*this,	&SampleDisplay::handleButtonReleaseEvent));
168   signal_enter_notify_event().
169     connect(mem_fun(*this, &SampleDisplay::handleEnterEvent));
170   signal_leave_notify_event().
171     connect(mem_fun(*this, &SampleDisplay::handleLeaveEvent));
172 
173   set_events(Gdk::EXPOSURE_MASK
174              | Gdk::LEAVE_NOTIFY_MASK
175              | Gdk::ENTER_NOTIFY_MASK
176              | Gdk::BUTTON_PRESS_MASK
177              | Gdk::BUTTON_RELEASE_MASK
178              | Gdk::POINTER_MOTION_MASK
179              | Gdk::POINTER_MOTION_HINT_MASK);
180 }
181 
setTocEdit(TocEdit * t)182 void SampleDisplay::setTocEdit(TocEdit *t)
183 {
184   tocEdit_ = t;
185 
186   Toc *toc = tocEdit_->toc();
187 
188   markerSet_ = false;
189   selectionSet_ = false;
190   regionSet_ = false;
191 
192   minSample_ = 0;
193 
194   if (toc->length().samples() > 0) {
195     maxSample_ = toc->length().samples() - 1;
196   }
197   else {
198     maxSample_ = 0;
199   }
200 }
201 
updateToc(unsigned long smin,unsigned long smax)202 void SampleDisplay::updateToc(unsigned long smin, unsigned long smax)
203 {
204   if (tocEdit_ == NULL)
205     return;
206 
207   Toc *toc = tocEdit_->toc();
208 
209   if (smin <= smax) {
210     minSample_ = smin;
211     maxSample_ = smax;
212   }
213 
214   if (toc->length().samples() == 0) {
215     minSample_ = maxSample_ = 0;
216   }
217   else {
218     if (maxSample_ >= toc->length().samples()) {
219       // adjust 'maxSample_' to reduced length
220       unsigned long len = maxSample_ - minSample_;
221 
222       maxSample_ = toc->length().samples() - 1;
223       if (maxSample_ > len) {
224 	minSample_ = maxSample_ - len;
225       }
226       else {
227 	minSample_ = 0;
228       }
229     }
230   }
231 
232   setView(minSample_, maxSample_);
233 }
234 
setView(unsigned long start,unsigned long end)235 void SampleDisplay::setView(unsigned long start, unsigned long end)
236 {
237   if (tocEdit_ == NULL)
238     return;
239 
240   Toc *toc = tocEdit_->toc();
241 
242   if (end < start)
243     end = start;
244 
245   unsigned long len = end - start + 1;
246 
247   if (len < 3 && toc != NULL) {
248     end = start + 2;
249 
250     if (end >= toc->length().samples()) {
251       if (toc->length().samples() != 0)
252 	end = toc->length().samples() - 1;
253       else
254 	end = 0;
255 
256       if (end <= 2)
257 	start = 0;
258       else
259 	start = end - 2;
260     }
261 
262     len = (end == 0 ? 0 : 3);
263   }
264 
265   minSample_ = start;
266   maxSample_ = end;
267 
268   updateSamples();
269   redraw(0, 0, width_, height_, 0);
270 
271   GtkAdjustment *adjust = adjustment_->gobj();
272   if (toc == NULL) {
273     adjust->lower = 0.0;
274     adjust->upper = 1.0;
275     adjust->value = 0.0;
276     adjust->page_size = 0.0;
277   }
278   else {
279     adjust->lower = 0.0;
280     adjust->upper = toc->length().samples();
281     adjust->value = minSample_;
282 
283     adjust->step_increment = len / 4;
284     if (adjust->step_increment == 0.0)
285       adjust->step_increment = 1.0;
286 
287     adjust->page_increment = len / 1.1;
288     adjust->page_size = len;
289   }
290   adjustment_->changed();
291 
292 }
293 
getView(unsigned long * start,unsigned long * end)294 void SampleDisplay::getView(unsigned long *start, unsigned long *end)
295 {
296   *start = minSample_;
297   *end = maxSample_;
298 }
299 
getSelection(unsigned long * start,unsigned long * end)300 bool SampleDisplay::getSelection(unsigned long *start, unsigned long *end)
301 {
302   if (selectionSet_) {
303     *start = selectionStartSample_;
304     *end = selectionEndSample_;
305     return true;
306   }
307 
308   return false;
309 }
310 
getMarker(unsigned long * sample)311 int SampleDisplay::getMarker(unsigned long *sample)
312 {
313   if (markerSet_) {
314     *sample = markerSample_;
315     return 1;
316   }
317 
318   return 0;
319 }
320 
setSelectedTrackMarker(int trackNr,int indexNr)321 void SampleDisplay::setSelectedTrackMarker(int trackNr, int indexNr)
322 {
323   selectedTrack_ = trackNr;
324   selectedIndex_ = indexNr;
325 }
326 
setRegion(unsigned long start,unsigned long end)327 void SampleDisplay::setRegion(unsigned long start, unsigned long end)
328 {
329   if (tocEdit_ == NULL)
330     return;
331 
332   Toc *toc = tocEdit_->toc();
333 
334   if (end <= start || end >= toc->length().samples()) {
335     regionSet_ = false;
336   }
337   else {
338     regionStartSample_ = start;
339     regionEndSample_ = end;
340     regionSet_ = true;
341   }
342 
343   setView(minSample_, maxSample_);
344 }
345 
clearRegion()346 void SampleDisplay::clearRegion()
347 {
348   bool wasSet = regionSet_;
349   regionSet_ = false;
350   if (wasSet) {
351     setView(minSample_, maxSample_);
352   }
353 }
354 
getRegion(unsigned long * start,unsigned long * end)355 int SampleDisplay::getRegion(unsigned long *start, unsigned long *end)
356 {
357   if (regionSet_) {
358     *start = regionStartSample_;
359     *end = regionEndSample_;
360     return 1;
361   }
362 
363   return 0;
364 }
365 
setCursor(int ctrl,unsigned long sample)366 void SampleDisplay::setCursor(int ctrl, unsigned long sample)
367 {
368   if (ctrl == 0) {
369     cursorControlExtern_ = false;
370   }
371   else {
372     cursorControlExtern_ = true;
373 
374     gint x = sample2pixel(sample);
375     if (x >= 0)
376       drawCursor(x);
377     else
378       undrawCursor();
379   }
380 }
381 
getColor(const char * colorName,Gdk::Color * color)382 void SampleDisplay::getColor(const char *colorName, Gdk::Color *color)
383 {
384   if (!color->parse(colorName) || !get_colormap()->alloc_color(*color)) {
385     log_message(-1, _("Cannot allocate color \"%s\""), colorName);
386     *color = get_style()->get_black();
387   }
388 }
389 
scrollTo()390 void SampleDisplay::scrollTo()
391 {
392   unsigned long minSample, maxSample;
393 
394   if (tocEdit_ == NULL)
395     return;
396 
397   Toc *toc = tocEdit_->toc();
398   GtkAdjustment *adjust = adjustment_->gobj();
399 
400   if (adjust->page_size == 0.0)
401     return;
402 
403   minSample = (unsigned long)adjust->value;
404   maxSample = (unsigned long)(adjust->value + adjust->page_size) - 1;
405 
406   if (maxSample >= toc->length().samples()) {
407     maxSample = toc->length().samples() - 1;
408     if (maxSample <= (unsigned long)(adjust->page_size - 1))
409       minSample = 0;
410     else
411       minSample = maxSample - (unsigned long)(adjust->page_size - 1);
412   }
413 
414   viewModified(minSample, maxSample);
415 }
416 
pixel2sample(gint x)417 unsigned long SampleDisplay::pixel2sample(gint x)
418 {
419   if (tocEdit_ == NULL)
420     return 0;
421 
422   Toc *toc = tocEdit_->toc();
423   unsigned long sample;
424 
425   if (toc->length().lba() == 0)
426     return 0;
427 
428   assert(x >= sampleStartX_ && x <= sampleEndX_);
429 
430   x -= sampleStartX_;
431 
432   double res = maxSample_ - minSample_;
433   res /= sampleWidthX_ - 1;
434 
435   sample = (unsigned long)(minSample_ + res * x + 0.5);
436 
437   unsigned long round = 75 * 588; // 1 second
438   unsigned long rest;
439 
440   if (res >=  2 * round) {
441     if ((rest = sample % round) != 0)
442       sample += round - rest;
443   }
444   else {
445     round = 588; // 1 block
446     if (res >= 2 * round) {
447       if ((rest = sample % round) != 0)
448 	sample += round - rest;
449     }
450   }
451 
452   if (sample > maxSample_)
453     sample = maxSample_;
454 
455   return sample;
456 }
457 
sample2pixel(unsigned long sample)458 gint SampleDisplay::sample2pixel(unsigned long sample)
459 {
460   if (sample < minSample_ || sample > maxSample_)
461     return -1;
462 
463   unsigned long len = maxSample_ - minSample_;
464   double val = sample - minSample_;
465 
466   val *= sampleWidthX_ - 1;
467   val /= len;
468 
469   return (gint)(sampleStartX_ + val + 0.5);
470 }
471 
handleConfigureEvent(GdkEventConfigure * event)472 bool SampleDisplay::handleConfigureEvent(GdkEventConfigure *event)
473 {
474   Glib::RefPtr<Pango::Context> context = get_pango_context();
475   Pango::FontMetrics metrics = context->get_metrics(get_style()->get_font());
476 
477   if (!drawGc_) {
478     Glib::RefPtr<Gdk::Bitmap> mask;
479     // mask = Gdk::Bitmap::create(NULL, get_width(), get_height());
480     Glib::RefPtr<const Gdk::Drawable> window(get_window());
481 
482     drawGc_ = Gdk::GC::create(get_window());
483 
484     getColor("darkslateblue", &sampleColor_);
485     getColor("red3", &middleLineColor_);
486     getColor("gold2", &cursorColor_);
487     getColor("red", &markerColor_);
488     getColor("#ffc0e0", &selectionBackgroundColor_);
489 
490     timeLineHeight_ = ((metrics.get_ascent() + metrics.get_descent())
491                        / Pango::SCALE);
492     trackLineHeight_ = ((metrics.get_ascent() + metrics.get_descent())
493                         / Pango::SCALE);
494 
495     trackMarkerPixmap_ =
496         Gdk::Pixmap::create_from_xpm(window, mask,
497                                      get_style()->get_white(),
498                                      TRACK_MARKER_XPM_DATA);
499     indexMarkerPixmap_ =
500         Gdk::Pixmap::create_from_xpm(window, mask,
501                                      get_style()->get_white(),
502                                      INDEX_MARKER_XPM_DATA);
503     trackMarkerSelectedPixmap_ =
504         Gdk::Pixmap::create_from_xpm(window, mask,
505                                        markerColor_,
506                                        TRACK_MARKER_XPM_DATA);
507     indexMarkerSelectedPixmap_ =
508         Gdk::Pixmap::create_from_xpm(window, mask,
509                                      markerColor_,
510                                      INDEX_MARKER_XPM_DATA);
511     trackExtendPixmap_ =
512         Gdk::Pixmap::create_from_xpm(window, mask,
513                                      get_style()->get_white(),
514                                      TRACK_EXTEND_XPM_DATA);
515     indexExtendPixmap_ =
516         Gdk::Pixmap::create_from_xpm(window, mask,
517                                      get_style()->get_white(),
518                                      INDEX_EXTEND_XPM_DATA);
519 
520     trackMarkerWidth_ = ((metrics.get_approximate_digit_width() /
521                           Pango::SCALE) * 5) + TRACK_MARKER_XPM_WIDTH + 2;
522     trackManager_ = new TrackManager(TRACK_MARKER_XPM_WIDTH);
523   }
524 
525   width_ = get_width();
526   height_ = get_height();
527 
528   // Don't even try to do anything smart if we haven't received a
529   // reasonable window size yet. This will keep pixmap_ to NULL. This
530   // is important because during startup we don't control how the
531   // configure_event are timed wrt to gcdmaster bringup.
532   if (width_ <= 1 || height_ <= 1)
533     return true;
534 
535   chanHeight_ = (height_ - timeLineHeight_ - trackLineHeight_ - 2) / 2;
536 
537   lcenter_ = chanHeight_ / 2 + trackLineHeight_;
538   rcenter_ = lcenter_ + timeLineHeight_ + chanHeight_;
539 
540   trackLineY_ = trackLineHeight_ - 1;
541 
542   timeLineY_ = chanHeight_ + timeLineHeight_ + trackLineHeight_;
543   timeTickWidth_ = ((metrics.get_approximate_digit_width() /
544                      Pango::SCALE) * 13) + 3;
545 
546   sampleStartX_ = 10;
547   sampleEndX_ = width_ - 10;
548   sampleWidthX_ = sampleEndX_ - sampleStartX_ + 1;
549 
550   pixmap_ = Gdk::Pixmap::create(get_window(), get_width(), get_height(), -1);
551 
552   if (width_ > 100 && height_ > 100)
553     updateSamples();
554 
555   return true;
556 }
557 
558 
handleExposeEvent(GdkEventExpose * event)559 bool SampleDisplay::handleExposeEvent (GdkEventExpose *event)
560 {
561   redraw(event->area.x, event->area.y, event->area.width, event->area.height,
562 	 0);
563 
564   return false;
565 }
566 
handleButtonPressEvent(GdkEventButton * event)567 bool SampleDisplay::handleButtonPressEvent(GdkEventButton *event)
568 {
569   gint x = (gint)event->x;
570   gint y = (gint)event->y;
571 
572   dragMode_ = DRAG_NONE;
573 
574   // e.g. if audio is playing
575   if (cursorControlExtern_)
576     return true;
577 
578   if (event->button == 1 && x >= sampleStartX_ && x <= sampleEndX_) {
579     if (y > trackLineY_) {
580       dragMode_ = DRAG_SAMPLE_MARKER;
581       dragStart_ = dragEnd_ = x;
582     }
583     else {
584       if ((pickedTrackMarker_ = trackManager_->pick(x - sampleStartX_ + 4,
585 						    &dragStopMin_,
586 						    &dragStopMax_)) != NULL) {
587 	dragMode_ = DRAG_TRACK_MARKER;
588 	dragStart_ = dragEnd_ = x;
589 	dragLastX_ = -1;
590 	dragStopMin_ += sampleStartX_;
591 	dragStopMax_ += sampleStartX_;
592       }
593     }
594 
595   }
596 
597   return true;
598 }
599 
handleButtonReleaseEvent(GdkEventButton * event)600 bool SampleDisplay::handleButtonReleaseEvent(GdkEventButton *event)
601 {
602   gint x = (gint)event->x;
603 
604   if (cursorControlExtern_)
605     return false;
606 
607   if (x < sampleStartX_) {
608     x = sampleStartX_;
609   }
610   else if (x > sampleEndX_) {
611     x = sampleEndX_;
612   }
613 
614   if (event->button == 1 && dragMode_ != DRAG_NONE) {
615     if (dragMode_ == DRAG_SAMPLE_MARKER) {
616       if (dragStart_ - x >= -5 && dragStart_ - x <= 5) {
617         selectionSet_ = false;
618         selectionCleared();
619 	markerSet(pixel2sample(dragStart_));
620       }
621       else {
622 	selectionSet_ = true;
623 	if (x > dragStart_) {
624 	  selectionStartSample_ = pixel2sample(dragStart_);
625 	  selectionEndSample_ = pixel2sample(x);
626 	  selectionStart_ = dragStart_;
627 	  selectionEnd_ = x;
628 	}
629 	else {
630 	  selectionEndSample_ = pixel2sample(dragStart_);
631 	  selectionStartSample_ = pixel2sample(x);
632 	  selectionEnd_ = dragStart_;
633 	  selectionStart_ = x;
634 	}
635 	selectionSet(selectionStartSample_, selectionEndSample_);
636       }
637     }
638     else if (dragMode_ == DRAG_TRACK_MARKER) {
639       if (dragStart_ - x >= -5 && dragStart_ - x <= 5) {
640 	trackManager_->select(pickedTrackMarker_);
641 	selectedTrack_ = pickedTrackMarker_->trackNr;
642 	selectedIndex_ = pickedTrackMarker_->indexNr;
643 	drawTrackLine();
644 	trackMarkSelected(pickedTrackMarker_->track, selectedTrack_,
645 			  selectedIndex_);
646       }
647       else {
648 	selectedTrack_ = pickedTrackMarker_->trackNr;
649 	selectedIndex_ = pickedTrackMarker_->indexNr;
650 	trackMarkMoved(pickedTrackMarker_->track, selectedTrack_,
651 		       selectedIndex_, pixel2sample(x));
652       }
653       pickedTrackMarker_ = NULL;
654     }
655 
656     dragMode_ = DRAG_NONE;
657     drawCursor(x);
658     redraw(0, 0, width_, height_, 0);
659   }
660 
661   return true;
662 }
663 
handleMotionNotifyEvent(GdkEventMotion * event)664 bool SampleDisplay::handleMotionNotifyEvent (GdkEventMotion *event)
665 {
666   gint x, y;
667   GdkModifierType state;
668 
669   if (cursorControlExtern_)
670     return TRUE;
671 
672   if (event->is_hint)
673     gdk_window_get_pointer (event->window, &x, &y, &state);
674   else {
675     x = (gint)event->x;
676     y = (gint)event->y;
677     state = (GdkModifierType) event->state;
678   }
679 
680 
681   if (dragMode_ == DRAG_SAMPLE_MARKER) {
682     gint dw = 0;
683     gint dx = 0;
684 
685     if (x < sampleStartX_)
686       x = sampleStartX_;
687     else if (x > sampleEndX_)
688       x = sampleEndX_;
689 
690     if (selectionEnd_ > dragStart_) {
691       if (x < selectionEnd_) {
692 	redraw(x + 1, 0, selectionEnd_ - x, height_, 0x01);
693 	if (x < dragStart_) {
694 	  dw = dragStart_ - x + 1;
695 	  dx = x;
696 	}
697       }
698       else {
699 	dw = x - selectionEnd_;
700 	dx = selectionEnd_ + 1;
701       }
702     }
703     else if (selectionEnd_ < dragStart_) {
704       if (x > selectionEnd_) {
705 	redraw(selectionEnd_, 0, x - selectionEnd_, height_, 0x01);
706 	if (x > dragStart_) {
707 	  dw = x - dragStart_ + 1;
708 	  dx = dragStart_;
709 	}
710       }
711       else {
712 	dw = selectionEnd_ - x;
713 	dx = x;
714       }
715     }
716 
717     if (dw != 0) {
718       drawGc_->set_foreground(cursorColor_);
719       drawGc_->set_function(Gdk::XOR);
720       get_window()->draw_rectangle(drawGc_, TRUE, dx, 0, dw, height_ - 1);
721       drawGc_->set_function(Gdk::COPY);
722     }
723 
724     selectionEnd_ = x;
725   }
726   else if (dragMode_ == DRAG_TRACK_MARKER) {
727     if (x < dragStopMin_)
728       x = dragStopMin_;
729 
730     if (x > dragStopMax_)
731       x = dragStopMax_;
732 
733     if (dragLastX_ > 0) {
734       drawTrackMarker(2, dragLastX_, 0, 0, 0, 0);
735     }
736     drawTrackMarker(1, x, pickedTrackMarker_->trackNr,
737 		    pickedTrackMarker_->indexNr, 0, 0);
738     dragLastX_ = x;
739     drawCursor(x);
740   }
741   else {
742     drawCursor(x);
743   }
744 
745   return TRUE;
746 }
747 
handleEnterEvent(GdkEventCrossing * event)748 bool SampleDisplay::handleEnterEvent(GdkEventCrossing *event)
749 {
750   if (cursorControlExtern_)
751     return true;
752 
753   drawCursor((gint)event->x);
754   return true;
755 }
756 
handleLeaveEvent(GdkEventCrossing * event)757 bool SampleDisplay::handleLeaveEvent(GdkEventCrossing *event)
758 {
759   if (cursorControlExtern_)
760     return true;
761 
762   undrawCursor();
763   return true;
764 }
765 
766 // drawMask: 0x01: do not draw cursor
767 //           0x02: do not draw marker
redraw(gint x,gint y,gint width,gint height,int drawMask)768 void SampleDisplay::redraw(gint x, gint y, gint width, gint height,
769 			   int drawMask)
770 {
771   if (!pixmap_)
772     return;
773 
774   get_window()->draw_drawable(drawGc_, pixmap_, x, y, x, y, width, height);
775 
776   if ((drawMask & 0x02) == 0)
777     drawMarker();
778 
779   if ((drawMask & 0x01) == 0 && cursorDrawn_) {
780     cursorDrawn_ = false;
781     drawCursor(cursorX_);
782   }
783 }
784 
drawMarker()785 void SampleDisplay::drawMarker()
786 {
787   if (markerSet_) {
788     drawGc_->set_foreground(markerColor_);
789 
790     markerX_ = sample2pixel(markerSample_);
791     if (markerX_ >= 0)
792       get_window()->draw_line(drawGc_, markerX_, trackLineY_,
793 			     markerX_, height_ - 1);
794   }
795 }
796 
setMarker(unsigned long sample)797 void SampleDisplay::setMarker(unsigned long sample)
798 {
799   if (markerSet_)
800     redraw(markerX_, 0, 1, height_, 0x02);
801 
802   markerSample_ = sample;
803   markerSet_ = true;
804   drawMarker();
805 }
806 
clearMarker()807 void SampleDisplay::clearMarker()
808 {
809   if (markerSet_)
810     redraw(markerX_, 0, 1, height_, 0x02);
811 
812   markerSet_ = false;
813 }
814 
815 
updateSamples()816 void SampleDisplay::updateSamples()
817 {
818   if (tocEdit_ == NULL)
819     return;
820 
821   Toc *toc = tocEdit_->toc();
822 
823   if (!pixmap_)
824     return;
825 
826   gint halfHeight = chanHeight_ / 2;
827 
828   drawGc_->set_foreground(get_style()->get_white());
829   pixmap_->draw_rectangle(drawGc_, TRUE, 0, 0, width_, height_);
830 
831   long res = (maxSample_ - minSample_ + 1)/sampleWidthX_;
832   long bres = res / tocEdit_->sampleManager()->blocking();
833   gint i;
834   double pos;
835   long j;
836   unsigned long s;
837   short lnegsum, lpossum, rnegsum, rpossum;
838   gint regionStart = -1;
839   gint regionEnd = -1;
840   int regionActive = 0;
841 
842   if (regionSet_) {
843     if (regionStartSample_ <= maxSample_ &&
844 	regionEndSample_ >= minSample_) {
845 
846       if (regionStartSample_ > minSample_)
847 	regionStart = sample2pixel(regionStartSample_);
848       else
849 	regionStart = sampleStartX_;
850 
851       if (regionEndSample_ < maxSample_)
852 	regionEnd = sample2pixel(regionEndSample_);
853       else
854 	regionEnd = sampleEndX_;
855     }
856 
857     if (regionStart >= 0 && regionEnd >= regionStart) {
858       drawGc_->set_foreground(selectionBackgroundColor_);
859       pixmap_->draw_rectangle(drawGc_, TRUE,
860 			      regionStart, lcenter_ - halfHeight,
861 			      regionEnd - regionStart + 1, chanHeight_);
862       pixmap_->draw_rectangle(drawGc_, TRUE,
863 			      regionStart, rcenter_ - halfHeight,
864 			      regionEnd - regionStart + 1, chanHeight_);
865     }
866   }
867 
868   drawGc_->set_foreground(sampleColor_);
869 
870   if (bres > 0) {
871     for (s = minSample_, i = sampleStartX_;
872 	 s < maxSample_ && i <= sampleEndX_;
873 	 s += res, i++) {
874       lnegsum = lpossum = rnegsum = rpossum = 0;
875 
876       if (regionStart != -1 && i >= regionStart && regionActive == 0) {
877 	regionActive = 1;
878 	drawGc_->set_foreground(markerColor_);
879       }
880       else if (regionActive == 1 && i > regionEnd) {
881 	regionActive = 2;
882 	drawGc_->set_foreground(sampleColor_);
883       }
884 
885       tocEdit_->sampleManager()->getPeak(s, s + res, &lnegsum, &lpossum,
886 					 &rnegsum, &rpossum);
887 
888       pos = double(lnegsum) * halfHeight;
889       pos /= SHRT_MAX;
890       if (pos != 0)
891 	pixmap_->draw_line(drawGc_, i, lcenter_, i, lcenter_ - (gint)pos);
892 
893       pos = double(lpossum) * halfHeight;
894       pos /= SHRT_MAX;
895       if (pos != 0)
896 	pixmap_->draw_line(drawGc_, i, lcenter_, i, lcenter_ - (gint)pos);
897 
898       pos = double(rnegsum) * halfHeight;
899       pos /= SHRT_MAX;
900       if (pos != 0)
901 	pixmap_->draw_line(drawGc_, i, rcenter_, i, rcenter_ - (gint)pos);
902 
903       pos = double(rpossum) * halfHeight;
904       pos /= SHRT_MAX;
905       if (pos != 0)
906 	pixmap_->draw_line(drawGc_, i, rcenter_, i, rcenter_ - (gint)pos);
907     }
908   }
909   else if (maxSample_ > 0 && res >= 1) {
910 
911     TocReader reader(toc);
912 
913     if (reader.openData() == 0) {
914       Sample *sampleBuf = new Sample[tocEdit_->sampleManager()->blocking()];
915       double dres = double(maxSample_ - minSample_ + 1)/double(sampleWidthX_);
916       double ds;
917 
918       for (ds = minSample_, i = sampleStartX_;
919 	   ds < maxSample_ && i <= sampleEndX_;
920 	   ds += dres, i++) {
921 
922 	lnegsum = lpossum = rnegsum = rpossum = 0;
923 
924 	if (reader.seekSample((long)ds) == 0 &&
925 	    reader.readSamples(sampleBuf, res) == res) {
926 	  for (j = 0; j < res; j++) {
927 	    if (sampleBuf[j].left() < lnegsum)
928 	      lnegsum = sampleBuf[j].left();
929 
930 	    if (sampleBuf[j].left() > lpossum)
931 	      lpossum = sampleBuf[j].left();
932 
933 	    if (sampleBuf[j].right() < rnegsum)
934 	      rnegsum = sampleBuf[j].right();
935 
936 	    if (sampleBuf[j].right() > rpossum)
937 	      rpossum = sampleBuf[j].right();
938 	  }
939 	}
940 
941 	if (regionStart != -1 && i >= regionStart && regionActive == 0) {
942 	  regionActive = 1;
943 	  drawGc_->set_foreground(markerColor_);
944 	}
945 	else if (regionActive == 1 && i > regionEnd) {
946 	  regionActive = 2;
947 	  drawGc_->set_foreground(sampleColor_);
948 	}
949 
950 	pos = double(lnegsum) * halfHeight;
951 	pos /= SHRT_MAX;
952 	if (pos != 0)
953 	  pixmap_->draw_line(drawGc_, i, lcenter_, i, lcenter_ - (gint)pos);
954 
955 	pos = double(lpossum) * halfHeight;
956 	pos /= SHRT_MAX;
957 	if (pos != 0)
958 	  pixmap_->draw_line(drawGc_, i, lcenter_, i, lcenter_ - (gint)pos);
959 
960 	pos = double(rnegsum) * halfHeight;
961 	pos /= SHRT_MAX;
962 	if (pos != 0)
963 	  pixmap_->draw_line(drawGc_, i, rcenter_, i, rcenter_ - (gint)pos);
964 
965 	pos = double(rpossum) * halfHeight;
966 	pos /= SHRT_MAX;
967 	if (pos != 0)
968 	  pixmap_->draw_line(drawGc_, i, rcenter_, i, rcenter_ - (gint)pos);
969       }
970 
971       delete[] sampleBuf;
972       reader.closeData();
973     }
974   }
975   else if (toc != NULL && maxSample_ > minSample_ + 1) {
976 
977     TocReader reader(toc);
978 
979     if (reader.openData() == 0) {
980       long len = maxSample_ - minSample_ + 1;
981       Sample *sampleBuf = new Sample[len];
982 
983       double pres = double(sampleWidthX_ - 1) / double(len - 1);
984       double di;
985       gint pos1;
986       gint lastPosLeft, lastPosRight;
987 
988       if (reader.seekSample(minSample_) == 0 &&
989 	  reader.readSamples(sampleBuf, len) == len) {
990 
991 	for (j = 1, di = sampleStartX_ + pres;
992 	     j < len && di < sampleEndX_ + 1; j++, di += pres) {
993 	  if (regionStart != -1 && regionActive == 0 &&
994 	      minSample_ + j - 1 >= regionStartSample_ &&
995 	      minSample_ + j <= regionEndSample_) {
996 	    regionActive = 1;
997 	    drawGc_->set_foreground(markerColor_);
998 	  }
999 	  else if (regionActive == 1 && minSample_ + j > regionEndSample_) {
1000 	    regionActive = 2;
1001 	    drawGc_->set_foreground(sampleColor_);
1002 	  }
1003 
1004 	  pos = sampleBuf[j - 1].left() * halfHeight;
1005 	  pos /= SHRT_MAX;
1006 
1007 	  pos1 = sampleBuf[j].left() * halfHeight;
1008 	  pos1 /= SHRT_MAX;
1009 	  lastPosLeft = pos1;
1010 
1011 	  if (pos != 0 || pos1 != 0)
1012 	    pixmap_->draw_line(drawGc_, long(di - pres), lcenter_ - (gint)pos,
1013 			       long(di), lcenter_ - pos1);
1014 
1015 	  pos = sampleBuf[j - 1].right() * halfHeight;
1016 	  pos /= SHRT_MAX;
1017 
1018 	  pos1 = sampleBuf[j].right() * halfHeight;
1019 	  pos1 /= SHRT_MAX;
1020 	  lastPosRight = pos1;
1021 
1022 	  if (pos != 0 || pos1 != 0)
1023 	    pixmap_->draw_line(drawGc_, long(di - pres), rcenter_ - (gint)pos,
1024 			       long(di), rcenter_ - pos1);
1025 
1026 	}
1027 
1028 	if (&pixmap_ == 0)
1029 	  std::cout << "null !!" << std::endl;
1030 
1031 	if (0 && (gint)di < sampleEndX_) {
1032 	  pos = sampleBuf[len -1].left() * halfHeight;
1033 	  pos /= SHRT_MAX;
1034 	  if (pos != 0 || lastPosLeft != 0)
1035 	    pixmap_->draw_line(drawGc_, long(di), lcenter_ - lastPosLeft,
1036 			       sampleEndX_, lcenter_ - (gint)pos);
1037 
1038 	  pos = sampleBuf[len - 1].right() * halfHeight;
1039 	  pos /= SHRT_MAX;
1040 	  if (pos != 0 || lastPosRight != 0)
1041 	    pixmap_->draw_line(drawGc_, long(di), rcenter_ - lastPosRight,
1042 			       sampleEndX_, rcenter_ - (gint)pos);
1043 	}
1044       }
1045       delete[] sampleBuf;
1046     }
1047 
1048   }
1049 
1050 
1051   drawGc_->set_foreground(middleLineColor_);
1052 
1053   pixmap_->draw_line(drawGc_, sampleStartX_, lcenter_,	sampleEndX_, lcenter_);
1054   pixmap_->draw_line(drawGc_, sampleStartX_, rcenter_, sampleEndX_, rcenter_);
1055 
1056   drawGc_->set_foreground(get_style()->get_black());
1057 
1058   pixmap_->draw_line(drawGc_, sampleStartX_ - 1, lcenter_ - halfHeight,
1059 		     sampleEndX_ + 1, lcenter_ - halfHeight);
1060   pixmap_->draw_line(drawGc_, sampleStartX_ - 1, lcenter_ + halfHeight,
1061 		     sampleEndX_ + 1, lcenter_ + halfHeight);
1062   pixmap_->draw_line(drawGc_, sampleStartX_ - 1, lcenter_ - halfHeight,
1063 		sampleStartX_ - 1, lcenter_ + halfHeight);
1064   pixmap_->draw_line(drawGc_, sampleEndX_ + 1, lcenter_ - halfHeight,
1065 		     sampleEndX_ + 1, lcenter_ + halfHeight);
1066 
1067   pixmap_->draw_line(drawGc_, sampleStartX_ - 1, rcenter_ - halfHeight,
1068 		     sampleEndX_ + 1, rcenter_ - halfHeight);
1069   pixmap_->draw_line(drawGc_, sampleStartX_ - 1, rcenter_ + halfHeight,
1070 		     sampleEndX_ + 1, rcenter_ + halfHeight);
1071   pixmap_->draw_line(drawGc_, sampleStartX_ - 1, rcenter_ + halfHeight,
1072 		     sampleStartX_ - 1, rcenter_ - halfHeight);
1073   pixmap_->draw_line(drawGc_, sampleEndX_ + 1, rcenter_ + halfHeight,
1074 		     sampleEndX_ + 1, rcenter_ - halfHeight);
1075 
1076   drawTimeLine();
1077 
1078   trackManager_->update(toc, minSample_, maxSample_, sampleWidthX_);
1079   if (selectedTrack_ > 0) {
1080     trackManager_->select(selectedTrack_, selectedIndex_);
1081   }
1082   drawTrackLine();
1083 }
1084 
drawCursor(gint x)1085 void SampleDisplay::drawCursor(gint x)
1086 {
1087   if (!pixmap_)
1088     return;
1089 
1090   if (x < sampleStartX_ || x > sampleEndX_)
1091     return;
1092 
1093   if (cursorDrawn_ && cursorX_ != x) {
1094     redraw(cursorX_, 0, 1, height_, 0x01);
1095   }
1096 
1097   if (!cursorDrawn_ || cursorX_ != x) {
1098     drawGc_->set_foreground(cursorColor_);
1099     get_window()->draw_line(drawGc_, x, trackLineY_, x, height_ - 1);
1100   }
1101 
1102   cursorDrawn_ = true;
1103   cursorX_ = x;
1104 
1105   if (cursorControlExtern_ == false)
1106     cursorMoved(pixel2sample(x));
1107 }
1108 
undrawCursor()1109 void SampleDisplay::undrawCursor()
1110 {
1111   if (cursorDrawn_) {
1112     redraw(cursorX_, 0, 1, height_, 0x01);
1113     cursorDrawn_ = false;
1114   }
1115 }
1116 
drawTimeTick(gint x,gint y,unsigned long sample)1117 void SampleDisplay::drawTimeTick(gint x, gint y, unsigned long sample)
1118 {
1119   char buf[50];
1120 
1121   if (!pixmap_)
1122     return;
1123 
1124   unsigned long min = sample / (60 * 44100);
1125   sample %= 60 * 44100;
1126 
1127   unsigned long sec = sample / 44100;
1128   sample %= 44100;
1129 
1130   unsigned long frame = sample / 588;
1131   sample %= 588;
1132 
1133   sprintf(buf, "%lu:%02lu:%02lu.%03lu", min, sec, frame, sample);
1134 
1135   drawGc_->set_foreground(get_style()->get_black());
1136 
1137   pixmap_->draw_line(drawGc_, x, y - timeLineHeight_, x, y);
1138 
1139   Glib::RefPtr<Pango::Layout> playout =
1140     Pango::Layout::create(get_pango_context());
1141   playout->set_text(buf);
1142   pixmap_->draw_layout(drawGc_, x + 3, y - timeLineHeight_ + 1, playout);
1143 }
1144 
drawTimeLine()1145 void SampleDisplay::drawTimeLine()
1146 {
1147   if (tocEdit_ == NULL)
1148     return;
1149 
1150   Toc *toc = tocEdit_->toc();
1151 
1152   if (toc->length().lba() == 0)
1153     return;
1154 
1155   gint sep = timeTickWidth_ + timeTickSep_;
1156   unsigned long maxNofTicks = (sampleWidthX_ + timeTickSep_) / sep;
1157   gint x;
1158 
1159   unsigned long len = maxSample_ - minSample_ + 1;
1160   unsigned long dt;
1161   unsigned long dtx;
1162   unsigned long startSample;
1163   unsigned long s;
1164 
1165   if ((s = len / (60 * 44100)) > 1) {
1166     dt = 60 * 44100;
1167   }
1168   else if ((s = len / 44100) > 1) {
1169     dt = 44100;
1170   }
1171   else if ((s = len / 588) > 1) {
1172     dt = 588;
1173   }
1174   else {
1175     dt = 1;
1176     s = len;
1177   }
1178 
1179   if (s > maxNofTicks) {
1180     dtx = s / maxNofTicks;
1181     if (s % maxNofTicks != 0)
1182       dtx++;
1183     dtx *= dt;
1184   }
1185   else {
1186     dtx = dt;
1187   }
1188 
1189   if (dt > 1) {
1190     if (minSample_ % dt == 0) {
1191       startSample = minSample_;
1192     }
1193     else {
1194       startSample = minSample_ / dt;
1195       startSample = startSample * dt + dt;
1196     }
1197   }
1198   else {
1199     startSample = minSample_;
1200   }
1201 
1202   for (s = startSample; s < maxSample_; s += dtx) {
1203     x = sample2pixel(s);
1204 
1205     if (x + timeTickWidth_ <= sampleEndX_)
1206       drawTimeTick(x, timeLineY_, s);
1207   }
1208 }
1209 
1210 // Draws track marker.
1211 // mode: 0: draw on 'pixmap_'
1212 //       1: draw on window
1213 //       2: redraw region at given position
drawTrackMarker(int mode,gint x,int trackNr,int indexNr,int selected,int extend)1214 void SampleDisplay::drawTrackMarker(int mode, gint x, int trackNr,
1215 				    int indexNr, int selected, int extend)
1216 {
1217   if (mode < 2) {
1218     char buf[20];
1219 
1220     sprintf(buf, "%d.%d", trackNr, indexNr);
1221 
1222     Glib::RefPtr<Gdk::Pixmap> marker;
1223 
1224     if (extend) {
1225       marker = (indexNr == 1 ? trackExtendPixmap_ : indexExtendPixmap_);
1226     }
1227     else {
1228       if (selected)
1229 	marker = (indexNr == 1 ? trackMarkerSelectedPixmap_ :
1230                   indexMarkerSelectedPixmap_);
1231       else
1232 	marker = (indexNr == 1 ? trackMarkerPixmap_ : indexMarkerPixmap_);
1233     }
1234 
1235     Glib::RefPtr<Gdk::Window> win = get_window();
1236     Glib::RefPtr<Gdk::Drawable> dr = win;
1237 
1238     if (mode == 0)
1239       dr = pixmap_;
1240 
1241     if (mode == 0) {
1242       if (selected)
1243 	drawGc_->set_foreground(markerColor_);
1244       else
1245 	drawGc_->set_foreground(get_style()->get_white());
1246 
1247       dr->draw_rectangle(drawGc_, TRUE,  x-4, trackLineY_ - trackLineHeight_,
1248 			trackMarkerWidth_, trackLineHeight_);
1249     }
1250 
1251     drawGc_->set_foreground(get_style()->get_black());
1252 
1253     dr->draw_drawable(drawGc_, marker, 0, 0,
1254 		   x - 4, trackLineY_ - TRACK_MARKER_XPM_HEIGHT,
1255 		   TRACK_MARKER_XPM_WIDTH, TRACK_MARKER_XPM_HEIGHT);
1256 
1257     Glib::RefPtr<Pango::Layout> playout =
1258       Pango::Layout::create(get_pango_context());
1259     playout->set_text(buf);
1260     dr->draw_layout(drawGc_,
1261 		    x + TRACK_MARKER_XPM_WIDTH / 2 + 2,
1262                     trackLineY_ - trackLineHeight_ + 2,
1263                     playout);
1264   }
1265   else {
1266     redraw(x - 4, trackLineY_ - trackLineHeight_, trackMarkerWidth_,
1267 	   trackLineHeight_, 0x03);
1268   }
1269 }
1270 
drawTrackLine()1271 void SampleDisplay::drawTrackLine()
1272 {
1273   const TrackManager::Entry *run;
1274   const TrackManager::Entry *selected = NULL;
1275 
1276   for (run = trackManager_->first(); run != NULL;
1277        run = trackManager_->next()) {
1278     if (run->selected != 0 && run->extend == 0) {
1279       selected = run;
1280     }
1281     else if (run->indexNr != 1 || run->extend != 0) {
1282       drawTrackMarker(0, sampleStartX_ + run->xpos, run->trackNr, run->indexNr,
1283 		      0, run->extend);
1284     }
1285   }
1286 
1287   for (run = trackManager_->first(); run != NULL;
1288        run = trackManager_->next()) {
1289     if (run->indexNr == 1 && run->selected == 0 && run->extend == 0) {
1290       drawTrackMarker(0, sampleStartX_ + run->xpos, run->trackNr, run->indexNr,
1291 		      0, run->extend);
1292     }
1293   }
1294 
1295   if (selected != NULL)
1296     drawTrackMarker(0, sampleStartX_ + selected->xpos, selected->trackNr,
1297 		    selected->indexNr, 1, 0);
1298 }
1299 
updateTrackMarks()1300 void SampleDisplay::updateTrackMarks()
1301 {
1302   if (tocEdit_ == NULL)
1303     return;
1304 
1305   Toc *toc = tocEdit_->toc();
1306 
1307   drawGc_->set_foreground(get_style()->get_white());
1308   pixmap_->draw_rectangle(drawGc_, TRUE,
1309 			  sampleStartX_ - 4, trackLineY_ - trackLineHeight_,
1310 			  width_ - sampleStartX_, trackLineHeight_);
1311 
1312   trackManager_->update(toc, minSample_, maxSample_, sampleWidthX_);
1313   if (selectedTrack_ > 0) {
1314     trackManager_->select(selectedTrack_, selectedIndex_);
1315   }
1316   drawTrackLine();
1317 
1318   redraw(0, 0, width_, height_, 0x03);
1319 }
1320