1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 NoteTrackVRulerControls.cpp
6 
7 Paul Licameli split from TrackPanel.cpp
8 
9 **********************************************************************/
10 
11 
12 
13 #ifdef USE_MIDI
14 #include "NoteTrackVRulerControls.h"
15 
16 #include "NoteTrackVZoomHandle.h"
17 
18 #include "../../../../HitTestResult.h"
19 #include "../../../../NoteTrack.h"
20 #include "../../../../ProjectHistory.h"
21 #include "../../../../RefreshCode.h"
22 #include "../../../../TrackArtist.h"
23 #include "../../../../TrackPanelMouseEvent.h"
24 
25 #include "AColor.h"
26 #include "../../../../TrackPanelDrawingContext.h"
27 #include "../../../../widgets/Ruler.h"
28 
29 #include <wx/dc.h>
30 
31 ///////////////////////////////////////////////////////////////////////////////
~NoteTrackVRulerControls()32 NoteTrackVRulerControls::~NoteTrackVRulerControls()
33 {
34 }
35 
HitTest(const TrackPanelMouseState & st,const AudacityProject * pProject)36 std::vector<UIHandlePtr> NoteTrackVRulerControls::HitTest
37 (const TrackPanelMouseState &st,
38  const AudacityProject *pProject)
39 {
40    std::vector<UIHandlePtr> results;
41    UIHandlePtr result;
42 
43    if ( st.state.GetX() <= st.rect.GetRight() - kGuard ) {
44       auto track = std::static_pointer_cast<NoteTrack>(FindTrack());
45       result = NoteTrackVZoomHandle::HitTest(
46          mVZoomHandle, st.state, track, st.rect);
47       if (result)
48          results.push_back(result);
49    }
50 
51    auto more = TrackVRulerControls::HitTest(st, pProject);
52    std::copy(more.begin(), more.end(), std::back_inserter(results));
53 
54    return results;
55 }
56 
HandleWheelRotation(const TrackPanelMouseEvent & evt,AudacityProject * pProject)57 unsigned NoteTrackVRulerControls::HandleWheelRotation
58 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
59 {
60    using namespace RefreshCode;
61    const wxMouseEvent &event = evt.event;
62 
63    if (!(event.ShiftDown() || event.CmdDown()))
64       return RefreshNone;
65 
66    // Always stop propagation even if the ruler didn't change.  The ruler
67    // is a narrow enough target.
68    evt.event.Skip(false);
69 
70    const auto pTrack = FindTrack();
71    if (!pTrack)
72       return RefreshNone;
73 
74    auto steps = evt.steps;
75    const auto nt = static_cast<NoteTrack*>(pTrack.get());
76 
77    if (event.CmdDown() && !event.ShiftDown()) {
78       if (steps > 0)
79          nt->ZoomIn(evt.rect, evt.event.m_y);
80       else
81          nt->ZoomOut(evt.rect, evt.event.m_y);
82    } else if (!event.CmdDown() && event.ShiftDown()) {
83       // Scroll some fixed number of notes, independent of zoom level or track height:
84       static const int movement = 6; // 6 semitones is half an octave
85       nt->ShiftNoteRange((int) (steps * movement));
86    } else {
87       return RefreshNone;
88    }
89 
90    ProjectHistory::Get( *pProject ).ModifyState(false);
91 
92    return RefreshCell | UpdateVRuler;
93 }
94 
Draw(TrackPanelDrawingContext & context,const wxRect & rect_,unsigned iPass)95 void NoteTrackVRulerControls::Draw(
96    TrackPanelDrawingContext &context,
97    const wxRect &rect_, unsigned iPass )
98 {
99    TrackVRulerControls::Draw( context, rect_, iPass );
100 
101    // Draw on a later pass like other vertical rulers,
102    // although the bevel is done a little differently
103 
104    if ( iPass == TrackArtist::PassControls ) {
105       // The note track draws a vertical keyboard to label pitches
106       auto track = std::static_pointer_cast<NoteTrack>( FindTrack() );
107       if ( !track )
108          return;
109 
110       auto rect = rect_;
111       --rect.width;
112       --rect.height;
113 
114       bool highlight = false;
115 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
116       highlight = rect.Contains(context.lastState.GetPosition());
117 #endif
118 
119       const auto artist = TrackArtist::Get( context );
120       UpdateRuler(rect);
121 
122       auto dc = &context.dc;
123 
124       dc->SetPen(highlight ? AColor::uglyPen : *wxTRANSPARENT_PEN);
125       dc->SetBrush(*wxWHITE_BRUSH);
126       wxRect bev = rect;
127       bev.x++;
128       bev.width--;
129       dc->DrawRectangle(bev);
130 
131       rect.y += 1;
132       rect.height -= 1;
133 
134       NoteTrackDisplayData data{ track.get(), rect };
135 
136       wxPen hilitePen;
137       hilitePen.SetColour(120, 120, 120);
138       wxBrush blackKeyBrush;
139       blackKeyBrush.SetColour(70, 70, 70);
140 
141       dc->SetBrush(blackKeyBrush);
142 
143       int fontSize = 10;
144 #ifdef __WXMSW__
145       fontSize = 8;
146 #endif
147 
148       wxFont labelFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
149       dc->SetFont(labelFont);
150 
151       int octave = 0;
152       int obottom = data.GetOctaveBottom(octave);
153       int marg = data.GetNoteMargin();
154 
155       while (obottom >= rect.y) {
156          dc->SetPen(*wxBLACK_PEN);
157          for (int white = 0; white < 7; white++) {
158             int pos = data.GetWhitePos(white);
159             if (obottom - pos > rect.y + marg + 1 &&
160                 // don't draw too close to margin line -- it's annoying
161                 obottom - pos < rect.y + rect.height - marg - 3)
162                AColor::Line(*dc, rect.x, obottom - pos,
163                             rect.x + rect.width, obottom - pos);
164          }
165          wxRect br = rect;
166          br.height = data.GetPitchHeight(1);
167          br.x++;
168          br.width = 17;
169          for (int black = 0; black < 5; black++) {
170             br.y = obottom - data.GetBlackPos(black);
171             if (br.y > rect.y + marg - 2 && br.y + br.height < rect.y + rect.height - marg) {
172                dc->SetPen(hilitePen);
173                dc->DrawRectangle(br);
174                dc->SetPen(*wxBLACK_PEN);
175                AColor::Line(*dc,
176                             br.x + 1, br.y + br.height - 1,
177                             br.x + br.width - 1, br.y + br.height - 1);
178                AColor::Line(*dc,
179                             br.x + br.width - 1, br.y + 1,
180                             br.x + br.width - 1, br.y + br.height - 1);
181             }
182          }
183 
184          if (octave >= 1 && octave <= 10) {
185             wxString s;
186             // ISO standard: A440 is in the 4th octave, denoted
187             // A4 <- the "4" should be a subscript.
188             s.Printf(wxT("C%d"), octave - 1);
189             wxCoord width, height;
190             dc->GetTextExtent(s, &width, &height);
191             if (obottom - height + 4 > rect.y &&
192                 obottom + 4 < rect.y + rect.height) {
193                dc->SetTextForeground(wxColour(60, 60, 255));
194                dc->DrawText(s, rect.x + rect.width - width,
195                             obottom - height + 2);
196             }
197          }
198          obottom = data.GetOctaveBottom(++octave);
199       }
200       // draw lines delineating the out-of-bounds margins
201       dc->SetPen(*wxBLACK_PEN);
202       // you would think the -1 offset here should be -2 to match the
203       // adjustment to rect.y (see above), but -1 produces correct output
204       AColor::Line(*dc, rect.x, rect.y + marg - 1, rect.x + rect.width, rect.y + marg - 1);
205       // since the margin gives us the bottom of the line,
206       // the extra -1 gets us to the top
207       AColor::Line(*dc, rect.x, rect.y + rect.height - marg - 1,
208                         rect.x + rect.width, rect.y + rect.height - marg - 1);
209 
210    }
211 }
212 
213 
UpdateRuler(const wxRect & rect)214 void NoteTrackVRulerControls::UpdateRuler( const wxRect &rect )
215 {
216    // The note track isn't drawing a ruler at all!
217    // But it needs to!
218 
219    const auto nt = std::static_pointer_cast< NoteTrack >( FindTrack() );
220    if (!nt)
221       return;
222 
223    static Ruler ruler;
224    const auto vruler = &ruler;
225 
226    vruler->SetBounds(rect.x, rect.y, rect.x + 1, rect.y + rect.height-1);
227    vruler->SetOrientation(wxVERTICAL);
228 
229    vruler->GetMaxSize( &nt->vrulerSize.x, &nt->vrulerSize.y );
230 }
231 #endif
232