1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //  scoreedit.cpp
5 //  (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net)
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; version 2 of
10 //  the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 //=========================================================
22 
23 
24 #include <QLayout>
25 #include <QSizeGrip>
26 #include <QLabel>
27 #include <QPushButton>
28 #include <QToolTip>
29 #include <QMenu>
30 #include <QMenuBar>
31 #include <QApplication>
32 #include <QClipboard>
33 #include <QDir>
34 #include <QKeySequence>
35 #include <QMimeData>
36 #include <QScrollArea>
37 #include <QSettings>
38 #include <QImage>
39 #include <QInputDialog>
40 #include <QMessageBox>
41 #include <QTimer>
42 #include <QPainterPath>
43 
44 #include <stdio.h>
45 #include "muse_math.h"
46 
47 #include <iostream>
48 #include <sstream>
49 using namespace std;
50 
51 #include "app.h"
52 #include "xml.h"
53 #include "sig.h"
54 #include "scoreedit.h"
55 #include "ttoolbar.h"
56 #include "tb1.h"
57 #include "globals.h"
58 #include "gconfig.h"
59 #include "icons.h"
60 #include "audio.h"
61 #include "functions.h"
62 #include "helper.h"
63 #include "cmd.h"
64 #include "song.h"
65 #include "shortcuts.h"
66 #include "menutitleitem.h"
67 
68 // Forwards from header:
69 #include <QCloseEvent>
70 #include <QResizeEvent>
71 #include <QKeyEvent>
72 #include <QPainter>
73 #include <QPixmap>
74 #include <QScrollBar>
75 #include <QComboBox>
76 #include <QAction>
77 #include <QActionGroup>
78 #include <QGridLayout>
79 #include <QToolButton>
80 #include "part.h"
81 #include "mtscale_flo.h"
82 #include "steprec.h"
83 #include "spinbox.h"
84 #include "tools.h"
85 
86 using MusEGlobal::debugMsg;
87 using MusEGlobal::heavyDebugMsg;
88 using MusECore::UndoOp;
89 using MusECore::Undo;
90 
91 namespace MusEGui {
92 
93 string IntToStr(int i);
94 QString IntToQStr(int i);
95 
96 
97 #define SPLIT_NOTE 60
98 
99 
100 
101 //PIXELS_PER_NOTEPOS must be greater or equal to 3*NOTE_XLEN + 2*NOTE_SHIFT
102 //because if tick 0 is at x=0: the notes can be shifted by NOTE_SHIFT.
103 //additionally, they can be moved by NOTE_XLEN (collision avoiding)
104 //then, they have their own width, which is NOTE_XLEN/2 into the x>0-area
105 //the same thing applies to the x<0-area
106 
107 //  OOO
108 //   |
109 //   ^actual calculated x, without shifting or moving
110 //  ^ and
111 //    ^   : moved note (by XLEN)
112 // additionally, a shift is possible
113 // total_width = shift + move + note_xlen + move + shift, where move==note_xlen
114 // total_width = 2*shift + 3*note_xlen
115 // if total_width is greater than px_per_notepos, there will be collisions!
116 
117 
118 
119 
120 
121 //do NOT put parentheses around this! and always right-multiply it,
122 //that is: foo * PAGESTEP, never PAGESTEP * foo!
123 #define PAGESTEP 3/4
124 
125 
126 #define SCROLL_MARGIN 10
127 #define SCROLL_SPEED 5
128 //SCROLL_SPEED is in (scroll_pixels per second) per mouse-move-pixel
129 #define SCROLL_SPEED_MAX 500
130 //SCROLL_SPEED_MAX is in scroll_pixels_per_second
131 
132 
133 
134 #define AKKOLADE_WIDTH 8
135 #define AKKOLADE_LEFTMARGIN 0
136 #define AKKOLADE_RIGHTMARGIN 2
137 #define STAFF_DISTANCE (10*YLEN)
138 #define GRANDSTAFF_DISTANCE (8*YLEN)
139 #define NOTE_YDIST 20
140 //NOTE_YDIST is the number of pixels which are between two notes
141 //which exceed their staves' y-boundaries, so that these boundaries
142 //must be expanded.
143 
144 
145 
146 #define MAX_QUANT_POWER 5
147 
148 
149 
create_random_string(int len=8)150 QString create_random_string(int len=8)
151 {
152     string result;
153 
154     for (int i=0;i<len;i++)
155         result+=char((rand() % 26) + 'A');
156 
157     return QString(result.c_str());
158 }
159 
160 
161 
162 
163 
164 
165 QPixmap *pix_whole, *pix_half, *pix_quarter; // arrays [NUM_MYCOLORS]
166 QPixmap *pix_dot, *pix_b, *pix_sharp, *pix_noacc; // arrays [NUM_MYCOLORS]
167 QPixmap *pix_r1, *pix_r2, *pix_r4, *pix_r8, *pix_r16, *pix_r32; // pointers
168 QPixmap *pix_flag_up, *pix_flag_down; // arrays [4]
169 QPixmap *pix_num; // array [10]
170 QPixmap *pix_clef_violin, *pix_clef_bass; //pointers
171 bool pixmaps_initalized=false;
172 QColor* mycolors; // array [NUM_MYCOLORS]
173 
174 
175 static const int scoreTools = PointerTool | PencilTool | RubberTool;
176 
177 set<QString> ScoreEdit::names;
178 
179 int ScoreCanvas::_quant_power2_init=3;
180 int ScoreCanvas::_pixels_per_whole_init=300;
181 int ScoreCanvas::note_velo_init=64;
182 int ScoreCanvas::note_velo_off_init=64;
183 int ScoreCanvas::new_len_init=0;
184 ScoreCanvas::coloring_mode_t ScoreCanvas::coloring_mode_init=COLOR_MODE_BLACK;
185 bool ScoreCanvas::preamble_contains_timesig_init=true;
186 bool ScoreCanvas::preamble_contains_keysig_init=true;
187 
188 
189 //---------------------------------------------------------
190 //   ScoreEdit
191 //---------------------------------------------------------
192 
ScoreEdit(QWidget * parent,const char * name,unsigned initPos)193 ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos)
194    : TopWin(TopWin::SCORE, parent, name)
195 {
196     //setAttribute(Qt::WA_DeleteOnClose);
197     setFocusPolicy(Qt::NoFocus);
198 
199     mainw    = new QWidget(this);
200 
201     mainGrid = new QGridLayout();
202     mainw->setLayout(mainGrid);
203 
204     mainGrid->setContentsMargins(0, 0, 0, 0);
205     mainGrid->setSpacing(0);
206     setCentralWidget(mainw);
207 
208 
209     apply_velo=false;
210 
211 
212     score_canvas=new ScoreCanvas(this, mainw);
213     xscroll = new QScrollBar(Qt::Horizontal, mainw);
214     yscroll = new QScrollBar(Qt::Vertical, mainw);
215     time_bar = new MusEGui::MTScaleFlo(score_canvas, mainw);
216 
217     connect(xscroll, SIGNAL(valueChanged(int)), score_canvas,   SLOT(x_scroll_event(int)));
218     connect(score_canvas, SIGNAL(xscroll_changed(int)), xscroll,   SLOT(setValue(int)));
219 
220     connect(yscroll, SIGNAL(valueChanged(int)), score_canvas,   SLOT(y_scroll_event(int)));
221     connect(score_canvas, SIGNAL(yscroll_changed(int)), yscroll,   SLOT(setValue(int)));
222 
223     connect(score_canvas, SIGNAL(canvas_width_changed(int)), SLOT(canvas_width_changed(int)));
224     connect(score_canvas, SIGNAL(viewport_width_changed(int)), SLOT(viewport_width_changed(int)));
225     connect(score_canvas, SIGNAL(canvas_height_changed(int)), SLOT(canvas_height_changed(int)));
226     connect(score_canvas, SIGNAL(viewport_height_changed(int)), SLOT(viewport_height_changed(int)));
227 
228     connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), score_canvas, SLOT(song_changed(MusECore::SongChangedStruct_t)));
229 
230     connect(xscroll, SIGNAL(valueChanged(int)), time_bar, SLOT(set_xpos(int)));
231     connect(score_canvas, SIGNAL(pos_add_changed()), time_bar, SLOT(pos_add_changed()));
232     connect(score_canvas, SIGNAL(preamble_width_changed(int)), time_bar, SLOT(set_xoffset(int)));
233 
234     mainGrid->addWidget(time_bar, 0,0);
235     mainGrid->addWidget(score_canvas, 1,0);
236     mainGrid->addWidget(xscroll,2,0);
237     mainGrid->addWidget(yscroll,1,1);
238 
239     xscroll->setMinimum(0);
240     yscroll->setMinimum(0);
241     xscroll->setValue(0);
242     yscroll->setValue(0);
243 
244 
245     // Toolbars ---------------------------------------------------------
246 
247       // NOTICE: Please ensure that any tool bar object names here match the names assigned
248       //          to identical or similar toolbars in class MusE or other TopWin classes.
249       //         This allows MusE::setCurrentMenuSharingTopwin() to do some magic
250       //          to retain the original toolbar layout. If it finds an existing
251       //          toolbar with the same object name, it /replaces/ it using insertToolBar(),
252       //          instead of /appending/ with addToolBar().
253 
254     addToolBarBreak();
255 
256     edit_tools = new MusEGui::EditToolBar(this, scoreTools);
257     addToolBar(edit_tools);
258     edit_tools->set(MusEGui::PointerTool);
259     score_canvas->set_tool(MusEGui::PointerTool);
260     connect(edit_tools, SIGNAL(toolChanged(int)), score_canvas,   SLOT(set_tool(int)));
261     connect(MusEGlobal::muse, &MusE::configChanged, edit_tools, &EditToolBar::configChanged);
262 
263     QToolBar* steprec_tools=addToolBar(tr("Step recording tools"));
264     steprec_tools->setObjectName("Score tools");
265     srec  = new QToolButton();
266     srec->setToolTip(tr("Step record"));
267     srec->setIcon(*steprecSVGIcon);
268     srec->setCheckable(true);
269     srec->setFocusPolicy(Qt::NoFocus);
270     steprec_tools->addWidget(srec);
271     connect(srec, SIGNAL(toggled(bool)), score_canvas, SLOT(set_steprec(bool)));
272 
273     QToolBar* quant_toolbar = addToolBar(tr("Quantisation settings"));
274     quant_toolbar->setObjectName("Score quantisation toolbar");
275     quant_toolbar->addWidget(new QLabel(tr("Quantisation"), quant_toolbar));
276     quant_combobox = new QComboBox(this);
277     quant_combobox->addItem("2");  // if you add or remove items from
278     quant_combobox->addItem("4");  // here, also change all code regarding
279     quant_combobox->addItem("8");  // _quant_power2 and _quant_power2_init
280     quant_combobox->addItem("16"); // and MAX_QUANT_POWER (must be log2(largest_value))
281     quant_combobox->addItem("32");
282     quant_combobox->setFocusPolicy(Qt::TabFocus);
283     quant_combobox->setCurrentIndex(score_canvas->quant_power2()-1);
284     // the above is intendedly executed BEFORE connecting. otherwise this would
285     // destroy pixels_per_whole_init!
286     //connect(quant_combobox, SIGNAL(currentIndexChanged(int)), score_canvas, SLOT(set_quant(int)));
287     connect(quant_combobox, SIGNAL(activated(int)), SLOT(quant_combobox_changed(int)));      // Tim
288     quant_toolbar->addWidget(quant_combobox);
289 
290 
291     QLabel* label = new QLabel(tr("Pixels per whole"));
292     label->setIndent(3);
293     quant_toolbar->addWidget(label);
294     px_per_whole_spinbox = new SpinBox(this);
295     px_per_whole_spinbox->setFocusPolicy(Qt::StrongFocus);
296     px_per_whole_spinbox->setRange(10, 1200);
297     px_per_whole_spinbox->setSingleStep(50);
298     connect(px_per_whole_spinbox, SIGNAL(valueChanged(int)), score_canvas, SLOT(set_pixels_per_whole(int)));
299     connect(score_canvas, SIGNAL(pixels_per_whole_changed(int)), px_per_whole_spinbox, SLOT(setValue(int)));
300     connect(px_per_whole_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas()));
301     connect(px_per_whole_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas()));
302     quant_toolbar->addWidget(px_per_whole_spinbox);
303     px_per_whole_spinbox->setValue(ScoreCanvas::_pixels_per_whole_init);
304 
305     addToolBarBreak();
306 
307     QToolBar* note_settings_toolbar = addToolBar(tr("Note settings"));
308     //don't change that name, or you will lose toolbar settings
309     note_settings_toolbar->setObjectName("New note settings");
310     note_settings_toolbar->addWidget(new QLabel(tr("Note length"), note_settings_toolbar));
311     len_actions=new QActionGroup(this);
312     n1_action = note_settings_toolbar->addAction("1");
313     n2_action = note_settings_toolbar->addAction("2");
314     n4_action = note_settings_toolbar->addAction("4");
315     n8_action = note_settings_toolbar->addAction("8");
316     n16_action = note_settings_toolbar->addAction("16");
317     n32_action = note_settings_toolbar->addAction("32");
318     nlast_action = note_settings_toolbar->addAction(tr("last"));
319     connect(n1_action,    &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_1); } );
320     connect(n2_action,    &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_2); } );
321     connect(n4_action,    &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_4); } );
322     connect(n8_action,    &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_8); } );
323     connect(n16_action,   &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_16); } );
324     connect(n32_action,   &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_32); } );
325     connect(nlast_action, &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_LAST); } );
326     n1_action->setCheckable(true);
327     n2_action->setCheckable(true);
328     n4_action->setCheckable(true);
329     n8_action->setCheckable(true);
330     n16_action->setCheckable(true);
331     n32_action->setCheckable(true);
332     nlast_action->setCheckable(true);
333     len_actions->addAction(n1_action);
334     len_actions->addAction(n2_action);
335     len_actions->addAction(n4_action);
336     len_actions->addAction(n8_action);
337     len_actions->addAction(n16_action);
338     len_actions->addAction(n32_action);
339     len_actions->addAction(nlast_action);
340 
341     switch (ScoreCanvas::new_len_init)
342     {
343         case 0: nlast_action->setChecked(true); menu_command(CMD_NOTELEN_LAST); break;
344         case 1: n1_action->setChecked(true); menu_command(CMD_NOTELEN_1); break;
345         case 2: n2_action->setChecked(true); menu_command(CMD_NOTELEN_2); break;
346         case 4: n4_action->setChecked(true); menu_command(CMD_NOTELEN_4); break;
347         case 8: n8_action->setChecked(true); menu_command(CMD_NOTELEN_8); break;
348         case 16: n16_action->setChecked(true); menu_command(CMD_NOTELEN_16); break;
349         case 32: n32_action->setChecked(true); menu_command(CMD_NOTELEN_32); break;
350         default:
351             cerr << "ERROR: THIS SHOULD NEVER HAPPEN. newLen is invalid in ScoreEdit::ScoreEdit.\n" <<
352                             "       (newLen="<<ScoreCanvas::new_len_init<<"; the only valid values are 0,1,2,4,8,16 and 32)\n" <<
353                             "       however, don't worry, this is no major problem, using 0 instead" << endl;
354             nlast_action->setChecked(true);
355             menu_command(CMD_NOTELEN_LAST);
356     }
357 
358     label = new QLabel(tr("Velocity"));
359     label->setIndent(3);
360     note_settings_toolbar->addWidget(label);
361     velo_spinbox = new SpinBox(this);
362     velo_spinbox->setRange(0, 127);
363     velo_spinbox->setSingleStep(1);
364     velo_spinbox->setToolTip(tr("Apply to selected notes, or new notes if none is selected"));
365     //we do not use the valueChanged signal, because that would generate
366     //many many undos when using the spin buttons.
367     connect(velo_spinbox, SIGNAL(editingFinished()), SLOT(velo_box_changed()));
368     connect(this,SIGNAL(velo_changed(int)), score_canvas, SLOT(set_velo(int)));
369     connect(velo_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas()));
370     connect(velo_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas()));
371     note_settings_toolbar->addWidget(velo_spinbox);
372     velo_spinbox->setValue(ScoreCanvas::note_velo_init);
373 
374     label = new QLabel(tr("Off-Velocity"));
375     label->setIndent(3);
376     note_settings_toolbar->addWidget(label);
377     velo_off_spinbox = new SpinBox(this);
378     velo_off_spinbox->setRange(0, 127);
379     velo_off_spinbox->setSingleStep(1);
380     velo_off_spinbox->setToolTip(tr("Apply to selected notes, or new notes if none is selected"));
381     //we do not use the valueChanged signal, because that would generate
382     //many many undos when using the spin buttons.
383     connect(velo_off_spinbox, SIGNAL(editingFinished()), SLOT(velo_off_box_changed()));
384     connect(this,SIGNAL(velo_off_changed(int)), score_canvas, SLOT(set_velo_off(int)));
385     connect(velo_off_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas()));
386     connect(velo_off_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas()));
387     note_settings_toolbar->addWidget(velo_off_spinbox);
388     velo_off_spinbox->setValue(ScoreCanvas::note_velo_off_init);
389 
390 
391     QMenu* edit_menu = menuBar()->addMenu(tr("&Edit"));
392 
393         edit_menu->addActions(MusEGlobal::undoRedo->actions());
394         edit_menu->addSeparator();
395 
396         cut_action = edit_menu->addAction(*cutSVGIcon, tr("C&ut"));
397         connect(cut_action, &QAction::triggered, [this]() { menu_command(CMD_CUT); } );
398 
399         copy_action = edit_menu->addAction(*copySVGIcon, tr("&Copy"));
400         connect(copy_action, &QAction::triggered, [this]() { menu_command(CMD_COPY); } );
401 
402         copy_range_action = edit_menu->addAction(*copyRangeSVGIcon, tr("Copy Events in Range"));
403         connect(copy_range_action, &QAction::triggered, [this]() { menu_command(CMD_COPY_RANGE); } );
404 
405         paste_action = edit_menu->addAction(*pasteSVGIcon, tr("&Paste"));
406         connect(paste_action, &QAction::triggered, [this]() { menu_command(CMD_PASTE); } );
407 
408         paste_dialog_action = edit_menu->addAction(*pasteDialogSVGIcon, tr("Paste (With Dialog)..."));
409         connect(paste_dialog_action, &QAction::triggered, [this]() { menu_command(CMD_PASTE_DIALOG); } );
410 
411         edit_menu->addSeparator();
412 
413         del_action = edit_menu->addAction(*deleteSVGIcon, tr("Delete &Events"));
414         connect(del_action, &QAction::triggered, [this]() { menu_command(CMD_DEL); } );
415 
416         edit_menu->addSeparator();
417 
418         QMenu* select_menu = edit_menu->addMenu(tr("&Select"));
419 
420             select_all_action = select_menu->addAction(*selectAllSVGIcon, tr("Select &All"));
421             connect(select_all_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_ALL); } );
422 
423             select_none_action = select_menu->addAction(*deselectAllSVGIcon, tr("&Deselect All"));
424             connect(select_none_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_NONE); } );
425 
426             select_invert_action = select_menu->addAction(*selectInvertSVGIcon, tr("Invert &Selection"));
427             connect(select_invert_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_INVERT); } );
428 
429             select_menu->addSeparator();
430 
431             select_iloop_action = select_menu->addAction(*selectInsideLoopSVGIcon, tr("&Inside Loop"));
432             connect(select_iloop_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_ILOOP); } );
433 
434             select_oloop_action = select_menu->addAction(*selectOutsideLoopSVGIcon, tr("&Outside Loop"));
435             connect(select_oloop_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_OLOOP); } );
436 
437 
438     QMenu* functions_menu = menuBar()->addMenu(tr("Fu&nctions"));
439 
440         func_quantize_action = functions_menu->addAction(*quantizeSVGIcon, tr("&Quantize"));
441         func_velocity_action = functions_menu->addAction(tr("Change Note &Velocity"));
442         func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo"));
443         func_move_action = functions_menu->addAction(tr("Move Notes"));
444         func_del_overlaps_action = functions_menu->addAction(tr("Delete Overlaps"));
445         func_erase_action = functions_menu->addAction(tr("Erase Events"));
446 
447         functions_menu->addSeparator();
448 
449         func_notelen_action = functions_menu->addAction(tr("Change Note &Length"));
450         func_fixed_len_action = functions_menu->addAction(tr("Set Fixed Length"));
451         func_transpose_action = functions_menu->addAction(tr("Transpose"));
452         func_legato_action = functions_menu->addAction(tr("Legato"));
453 
454         connect(func_quantize_action,     &QAction::triggered, [this]() { menu_command(CMD_QUANTIZE); } );
455         connect(func_notelen_action,      &QAction::triggered, [this]() { menu_command(CMD_NOTELEN); } );
456         connect(func_velocity_action,     &QAction::triggered, [this]() { menu_command(CMD_VELOCITY); } );
457         connect(func_cresc_action,        &QAction::triggered, [this]() { menu_command(CMD_CRESCENDO); } );
458         connect(func_transpose_action,    &QAction::triggered, [this]() { menu_command(CMD_TRANSPOSE); } );
459         connect(func_erase_action,        &QAction::triggered, [this]() { menu_command(CMD_ERASE); } );
460         connect(func_move_action,         &QAction::triggered, [this]() { menu_command(CMD_MOVE); } );
461         connect(func_fixed_len_action,    &QAction::triggered, [this]() { menu_command(CMD_FIXED_LEN); } );
462         connect(func_del_overlaps_action, &QAction::triggered, [this]() { menu_command(CMD_DELETE_OVERLAPS); } );
463         connect(func_legato_action,       &QAction::triggered, [this]() { menu_command(CMD_LEGATO); } );
464 
465     QMenu* settings_menu = menuBar()->addMenu(tr("&Display"));
466     settings_menu->menuAction()->setStatusTip(tr("Display menu: Display options specific to current editor."));
467 
468     settings_menu->addAction(subwinAction);
469 //    settings_menu->addAction(shareAction);
470     settings_menu->addAction(fullscreenAction);
471 
472     settings_menu->addSeparator();
473 
474     color_menu = settings_menu->addMenu(tr("Note Head &Colors"));
475             color_actions = new QActionGroup(this);
476             color_actions->setObjectName("CheckmarkOnly");
477             color_black_action = color_menu->addAction(tr("&Default"));
478             color_velo_action =  color_menu->addAction(tr("&Velocity"));
479             color_part_action =  color_menu->addAction(tr("&Part"));
480             color_black_action->setCheckable(true);
481             color_velo_action->setCheckable(true);
482             color_part_action->setCheckable(true);
483             color_actions->addAction(color_black_action);
484             color_actions->addAction(color_velo_action);
485             color_actions->addAction(color_part_action);
486             connect(color_black_action, &QAction::triggered, [this]() { menu_command(CMD_COLOR_BLACK); } );
487             connect(color_velo_action,  &QAction::triggered, [this]() { menu_command(CMD_COLOR_VELO); } );
488             connect(color_part_action,  &QAction::triggered, [this]() { menu_command(CMD_COLOR_PART); } );
489 
490             switch (ScoreCanvas::coloring_mode_init)
491             {
492                 case 0: color_black_action->setChecked(true); menu_command(CMD_COLOR_BLACK); break;
493                 case 1: color_velo_action->setChecked(true); menu_command(CMD_COLOR_VELO); break;
494                 case 2: color_part_action->setChecked(true); menu_command(CMD_COLOR_PART); break;
495                 default:
496                     cerr << "ERROR: THIS SHOULD NEVER HAPPEN. noteColor is invalid in ScoreEdit::ScoreEdit.\n" <<
497                                     "       (noteColor="<<ScoreCanvas::coloring_mode_init<<"; the only valid values are 0,1 and 2)\n" <<
498                                     "       however, don't worry, this is no major problem, using 0 instead" << endl;
499                     color_black_action->setChecked(true);
500                     menu_command(CMD_COLOR_BLACK);
501             }
502 
503         QMenu* preamble_menu = settings_menu->addMenu(tr("Set Up &Preamble"));
504         preamble_menu->setObjectName("CheckmarkOnly");
505 
506             preamble_keysig_action = preamble_menu->addAction(tr("Display &Key Signature"));
507             preamble_timesig_action =  preamble_menu->addAction(tr("Display &Time Signature"));
508             connect(preamble_keysig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_keysig_slot(bool)));
509             connect(preamble_timesig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_timesig_slot(bool)));
510 
511             preamble_keysig_action->setCheckable(true);
512             preamble_timesig_action->setCheckable(true);
513 
514             preamble_keysig_action->setChecked(ScoreCanvas::preamble_contains_keysig_init);
515             preamble_timesig_action->setChecked(ScoreCanvas::preamble_contains_timesig_init);
516 
517         QAction* set_name_action = settings_menu->addAction(*dummySVGIcon, tr("Set Score &Name..."));
518         connect(set_name_action, &QAction::triggered, [this]() { menu_command(CMD_SET_NAME); } );
519 
520     config_changed();  // set configuration values, initialize shortcuts
521     connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(config_changed()));
522 
523     QClipboard* cb = QApplication::clipboard();
524     connect(cb, SIGNAL(dataChanged()), SLOT(clipboard_changed()));
525 
526     clipboard_changed();
527     selection_changed();
528 
529     connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(song_changed(MusECore::SongChangedStruct_t)));
530     connect(MusEGlobal::song, SIGNAL(newPartsCreated(const std::map< const MusECore::Part*, std::set<const MusECore::Part*> >&)), score_canvas, SLOT(add_new_parts(const std::map< const MusECore::Part*, std::set<const MusECore::Part*> >&)));
531 
532     score_canvas->fully_recalculate();
533     score_canvas->goto_tick(initPos,true);
534     score_canvas->setFocus();
535 
536     if (name!=nullptr)
537         set_name(name, false, true);
538     else
539         init_name();
540 
541 
542     apply_velo=true;
543 
544     initTopwinState();
545     finalizeInit();
546 }
547 
config_changed()548 void ScoreEdit::config_changed()
549 {
550     if(score_canvas)
551       score_canvas->config_changed();
552 
553     init_shortcuts();
554 }
555 
init_shortcuts()556 void ScoreEdit::init_shortcuts()
557 {
558     cut_action->setShortcut(shortcuts[SHRT_CUT].key);
559     copy_action->setShortcut(shortcuts[SHRT_COPY].key);
560     copy_range_action->setShortcut(shortcuts[SHRT_COPY_RANGE].key);
561     paste_action->setShortcut(shortcuts[SHRT_PASTE].key);
562     paste_dialog_action->setShortcut(shortcuts[SHRT_PASTE_DIALOG].key);
563     del_action->setShortcut(shortcuts[SHRT_DELETE].key);
564 
565     select_all_action->setShortcut(shortcuts[SHRT_SELECT_ALL].key);
566     select_none_action->setShortcut(shortcuts[SHRT_SELECT_NONE].key);
567     select_invert_action->setShortcut(shortcuts[SHRT_SELECT_INVERT].key);
568     select_iloop_action->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key);
569     select_oloop_action->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key);
570 
571 //    color_menu->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key);
572 
573     func_quantize_action->setShortcut(shortcuts[SHRT_QUANTIZE].key);
574     func_notelen_action->setShortcut(shortcuts[SHRT_MODIFY_GATE_TIME].key);
575     func_velocity_action->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key);
576     func_transpose_action->setShortcut(shortcuts[SHRT_TRANSPOSE].key);
577     func_erase_action->setShortcut(shortcuts[SHRT_ERASE_EVENT].key);
578     func_move_action->setShortcut(shortcuts[SHRT_NOTE_SHIFT].key);
579     func_fixed_len_action->setShortcut(shortcuts[SHRT_FIXED_LEN].key);
580     func_del_overlaps_action->setShortcut(shortcuts[SHRT_DELETE_OVERLAPS].key);
581 }
582 
583 
add_parts(MusECore::PartList * pl,bool all_in_one)584 void ScoreEdit::add_parts(MusECore::PartList* pl, bool all_in_one)
585 {
586     score_canvas->add_staves(pl, all_in_one);
587 }
588 
589 //---------------------------------------------------------
590 //   itemsAreSelected
591 //---------------------------------------------------------
592 
itemsAreSelected() const593 bool ScoreEdit::itemsAreSelected() const
594 {
595   if(score_canvas)
596     return score_canvas->itemsAreSelected();
597   return false;
598 }
599 
600 
setEditTool(int tool)601 void ScoreEdit::setEditTool(int tool)
602 {
603     edit_tools->set(tool);
604 }
605 
606 
607 //---------------------------------------------------------
608 //   tagItems
609 //---------------------------------------------------------
610 
tagItems(MusECore::TagEventList * tag_list,const MusECore::EventTagOptionsStruct & options) const611 void ScoreEdit::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const
612 {
613   if(score_canvas)
614     score_canvas->tagItems(tag_list, options);
615 }
616 
init_name()617 void ScoreEdit::init_name()
618 {
619     int no=1;
620     QString temp;
621 
622     while (true)
623     {
624         temp="Score "+IntToQStr(no);
625         if (set_name(temp, false, false))
626             break;
627         else
628             no++;
629     }
630 }
631 
set_name(QString newname,bool emit_signal,bool emergency_name)632 bool ScoreEdit::set_name(QString newname, bool emit_signal, bool emergency_name)
633 {
634     if (names.find(newname)==names.end())
635     {
636         names.erase(name);
637         names.insert(newname);
638 
639         name=newname;
640 
641         isMdiWin() ? setWindowTitle(name) : setWindowTitle("MusE: " + name);
642 
643         if (emit_signal)
644             emit name_changed();
645 
646         return true;
647     }
648     else
649     {
650         if (emergency_name)
651         {
652             while (set_name(create_random_string(), emit_signal, false) == false) ;
653             return true;
654         }
655         else
656             return false;
657     }
658 }
659 
660 //---------------------------------------------------------
661 //   ~ScoreEdit
662 //---------------------------------------------------------
663 
~ScoreEdit()664 ScoreEdit::~ScoreEdit()
665 {
666     names.erase(name);
667 }
668 
focusCanvas()669 void ScoreEdit::focusCanvas()
670 {
671     if(MusEGlobal::config.smartFocus)
672     {
673       score_canvas->setFocus();
674       score_canvas->activateWindow();
675     }
676 }
677 
velo_box_changed()678 void ScoreEdit::velo_box_changed()
679 {
680     emit velo_changed(velo_spinbox->value());
681 }
682 
velo_off_box_changed()683 void ScoreEdit::velo_off_box_changed()
684 {
685     emit velo_off_changed(velo_off_spinbox->value());
686 }
687 
quant_combobox_changed(int idx)688 void ScoreEdit::quant_combobox_changed(int idx)
689 {
690     score_canvas->set_quant(idx);
691     focusCanvas();
692 }
693 
song_changed(MusECore::SongChangedStruct_t flags)694 void ScoreEdit::song_changed(MusECore::SongChangedStruct_t flags)
695 {
696     if(_isDeleting)  // Ignore while while deleting to prevent crash.
697         return;
698 
699     if (flags & (SC_SELECTION | SC_EVENT_MODIFIED | SC_EVENT_REMOVED))
700     {
701         map<const MusECore::Event*,
702           const MusECore::Part*> selection=get_events(score_canvas->get_all_parts(),1, MusECore::AllEventsRelevant);
703         if (!selection.empty()) {
704             int velo=-1;
705             int velo_off=-1;
706             for (map<const MusECore::Event*, const MusECore::Part*>::iterator it=selection.begin(); it!=selection.end(); it++)
707                 if (it->first->type()==MusECore::Note)
708                 {
709                     if (velo==-1) velo=it->first->velo();
710                     else if ((velo>=0) && (velo!=it->first->velo())) velo=-2;
711 
712                     if (velo_off==-1) velo_off=it->first->veloOff();
713                     else if ((velo_off>=0) && (velo_off!=it->first->veloOff())) velo_off=-2;
714                 }
715 
716             if (velo>=0) velo_spinbox->setValue(velo);
717             if (velo_off>=0) velo_off_spinbox->setValue(velo_off);
718         }
719 
720         selection_changed();
721     }
722 }
723 
canvas_width_changed(int width)724 void ScoreEdit::canvas_width_changed(int width)
725 {
726     xscroll->setMaximum(width);
727 }
viewport_width_changed(int width)728 void ScoreEdit::viewport_width_changed(int width)
729 {
730     xscroll->setPageStep(width * PAGESTEP);
731 }
732 
canvas_height_changed(int height)733 void ScoreEdit::canvas_height_changed(int height)
734 {
735     int val=height - score_canvas->viewport_height();
736     if (val<=0) val=0;
737 
738     yscroll->setMaximum(val);
739 
740     if (val==0)
741         yscroll->hide();
742     else
743         yscroll->show();
744 }
viewport_height_changed(int height)745 void ScoreEdit::viewport_height_changed(int height)
746 {
747     int val=score_canvas->canvas_height() - height;
748     // FINDMICHJETZT canvas_height() is uninitialized!
749     if (val<0) val=0;
750     yscroll->setPageStep(height * PAGESTEP);
751     yscroll->setMaximum(val);
752 
753     if (val==0)
754         yscroll->hide();
755     else
756         yscroll->show();
757 }
758 
closeEvent(QCloseEvent * e)759 void ScoreEdit::closeEvent(QCloseEvent* e)
760 {
761     _isDeleting = true;  // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored.
762     names.erase(name);
763 
764     storeSettings();
765 
766     emit isDeleting(static_cast<TopWin*>(this));
767     e->accept();
768 }
769 
storeSettings()770 void ScoreEdit::storeSettings() {
771 
772     QSettings settings;
773     //settings.setValue("ScoreEdit/geometry", saveGeometry());
774     settings.setValue("ScoreEdit/windowState", saveState());
775 }
776 
menu_command(int cmd)777 void ScoreEdit::menu_command(int cmd)
778 {
779     MusECore::TagEventList tag_list;
780 
781     const FunctionDialogElements_t fn_element_dflt =
782       FunctionAllEventsButton |
783       FunctionSelectedEventsButton |
784       FunctionLoopedButton |
785       FunctionSelectedLoopedButton;
786       // | FunctionAllPartsButton |
787       // FunctionSelectedPartsButton
788 
789     switch (cmd)
790     {
791         case CMD_SET_NAME:
792         {
793             bool ok;
794             QString newname = QInputDialog::getText(this, tr("Set Score Name"),
795                                                 tr("Enter the new score title"), QLineEdit::Normal,
796                                                 name, &ok);
797             if (ok)
798             {
799                 if (!set_name(newname))
800                     QMessageBox::warning(this, tr("Error"), tr("Changing score title failed:\nthe selected title is not unique"));
801             }
802         }
803         break;
804 
805         case CMD_SELECT_ALL: select_all(score_canvas->get_all_parts()); break;
806         case CMD_SELECT_NONE: select_none(score_canvas->get_all_parts()); break;
807         case CMD_SELECT_INVERT: select_invert(score_canvas->get_all_parts()); break;
808         case CMD_SELECT_ILOOP: select_in_loop(score_canvas->get_all_parts()); break;
809         case CMD_SELECT_OLOOP: select_not_in_loop(score_canvas->get_all_parts()); break;
810 
811         case CMD_CUT:
812               tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts));
813               MusECore::cut_items(&tag_list);
814             break;
815 
816         case CMD_COPY:
817               tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts));
818               MusECore::copy_items(&tag_list);
819               break;
820         case CMD_COPY_RANGE:
821               //tagItems(!itemsAreSelected(), false, true, MusEGlobal::song->lPos(), MusEGlobal::song->rPos());
822               // For now, unlike the other editors, just tag all parts.
823               tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
824                       !itemsAreSelected(), true, true, MusEGlobal::song->lPos(), MusEGlobal::song->rPos()));
825               MusECore::copy_items(&tag_list);
826               break;
827         case CMD_PASTE:
828             menu_command(CMD_SELECT_NONE);
829             MusECore::paste_items(score_canvas->get_all_parts(), 3072, MusECore::FunctionOptionsStruct(
830                                   MusECore::FunctionEraseItemsDefault | MusECore::FunctionPasteNeverNewPart));
831             break;
832         case CMD_PASTE_DIALOG:
833             menu_command(CMD_SELECT_NONE);
834             MusECore::paste_items(score_canvas->get_all_parts(), score_canvas->get_selected_part());
835             break;
836         case CMD_QUANTIZE:
837         {
838           FunctionDialogReturnQuantize ret =
839             quantize_items_dialog(FunctionDialogMode(fn_element_dflt));
840           if(ret._valid)
841           {
842             tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
843                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
844             MusECore::quantize_items(&tag_list, ret._raster_index,
845                                     ret._quant_len,
846                                     ret._strength,
847                                     ret._swing,
848                                     ret._threshold);
849           }
850         break;
851         }
852 
853         case CMD_VELOCITY:
854               {
855               FunctionDialogReturnVelocity ret =
856                 velocity_items_dialog(FunctionDialogMode(fn_element_dflt));
857               if(ret._valid)
858               {
859                 tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
860                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
861                 MusECore::modify_velocity_items(&tag_list, ret._rateVal, ret._offsetVal);
862               }
863               break;
864               }
865         case CMD_CRESCENDO:
866               {
867               FunctionDialogReturnCrescendo ret =
868                 crescendo_items_dialog(FunctionDialogMode(
869                   FunctionLoopedButton |
870                   FunctionSelectedLoopedButton
871                   // | FunctionAllPartsButton |
872                   // FunctionSelectedPartsButton
873                 ));
874               if(ret._valid)
875               {
876                 tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
877                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
878                 MusECore::crescendo_items(&tag_list, ret._start_val, ret._end_val, ret._absolute);
879               }
880               break;
881               }
882         case CMD_NOTELEN:
883               {
884               FunctionDialogReturnGateTime ret =
885                 gatetime_items_dialog(FunctionDialogMode(fn_element_dflt));
886               if(ret._valid)
887               {
888                 tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
889                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
890                 MusECore::modify_notelen_items(&tag_list, ret._rateVal, ret._offsetVal);
891               }
892               break;
893               }
894         case CMD_TRANSPOSE:
895               {
896               FunctionDialogReturnTranspose ret =
897                 transpose_items_dialog(FunctionDialogMode(fn_element_dflt));
898               if(ret._valid)
899               {
900                 tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
901                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
902                 MusECore::transpose_items(&tag_list, ret._amount);
903               }
904               break;
905               }
906         case CMD_ERASE:
907             {
908               FunctionDialogReturnErase ret =
909                 erase_items_dialog(FunctionDialogMode(fn_element_dflt));
910               if(ret._valid)
911               {
912                 tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
913                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
914                 MusECore::erase_items(&tag_list, ret._veloThreshold, ret._veloThresUsed, ret._lenThreshold, ret._lenThresUsed);
915               }
916             }
917             break;
918 
919         case CMD_DEL:
920               tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts));
921               MusECore::erase_items(&tag_list);
922             break;
923 
924         case CMD_MOVE:
925               {
926               FunctionDialogReturnMove ret =
927                 move_items_dialog(FunctionDialogMode(fn_element_dflt));
928               if(ret._valid)
929               {
930                 tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
931                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
932                 MusECore::move_items(&tag_list, ret._amount);
933               }
934               break;
935               }
936         case CMD_FIXED_LEN:
937               {
938               FunctionDialogReturnSetLen ret =
939                 setlen_items_dialog(FunctionDialogMode(fn_element_dflt));
940               if(ret._valid)
941               {
942                 tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
943                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
944                 MusECore::set_notelen_items(&tag_list, ret._len);
945               }
946               break;
947               }
948         case CMD_DELETE_OVERLAPS:
949               {
950               FunctionDialogReturnDelOverlaps ret =
951                 deloverlaps_items_dialog(FunctionDialogMode(fn_element_dflt));
952               if(ret._valid)
953               {
954                 tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
955                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
956                 MusECore::delete_overlaps_items(&tag_list);
957               }
958               break;
959               }
960         case CMD_LEGATO:
961               {
962               FunctionDialogReturnLegato ret =
963                 legato_items_dialog(FunctionDialogMode(fn_element_dflt));
964               if(ret._valid)
965               {
966                 tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(
967                       ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1));
968                 MusECore::legato_items(&tag_list, ret._min_len, !ret._allow_shortening);
969               }
970               break;
971               }
972 
973         default:
974             score_canvas->menu_command(cmd);
975     }
976 }
977 
clipboard_changed()978 void ScoreEdit::clipboard_changed()
979 {
980     paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists")));
981     paste_dialog_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists")));
982 }
983 
selection_changed()984 void ScoreEdit::selection_changed()
985 {
986     bool flag = !get_events(score_canvas->get_all_parts(),1).empty();
987     cut_action->setEnabled(flag);
988     copy_action->setEnabled(flag);
989     del_action->setEnabled(flag);
990 }
991 
992 
993 //duplicated from songfile.cpp's MusE::readPart(); the only differences:
994 //"none" is supported and tag_name is settable
read_part(MusECore::Xml & xml,QString tag_name="part")995 MusECore::Part* read_part(MusECore::Xml& xml, QString tag_name="part")
996 {
997     MusECore::Part* part = 0;
998 
999     for (;;)
1000     {
1001         MusECore::Xml::Token token = xml.parse();
1002         const QString& tag = xml.s1();
1003 
1004         switch (token)
1005         {
1006             case MusECore::Xml::Error:
1007             case MusECore::Xml::End:
1008                 return part;
1009 
1010             case MusECore::Xml::Text:
1011                 {
1012                     int trackIdx, partIdx;
1013                     if (tag=="none")
1014                         part=NULL;
1015                     else
1016                     {
1017                         sscanf(tag.toLatin1().constData(), "%d:%d", &trackIdx, &partIdx);
1018                         if (debugMsg) cout << "read_part: trackIdx="<<trackIdx<<", partIdx="<<partIdx;
1019                         MusECore::Track* track = MusEGlobal::song->tracks()->index(trackIdx);
1020                         if (track)
1021                             part = track->parts()->find(partIdx);
1022                         if (debugMsg) cout << ", track="<<track<<", part="<<part<<endl;
1023                     }
1024                 }
1025                 break;
1026 
1027             case MusECore::Xml::TagStart:
1028                 xml.unknown("read_part");
1029                 break;
1030 
1031             case MusECore::Xml::TagEnd:
1032                 if (tag == tag_name)
1033                     return part;
1034 
1035             default:
1036                 break;
1037         }
1038     }
1039 }
1040 
1041 
1042 
read_status(MusECore::Xml & xml)1043 void staff_t::read_status(MusECore::Xml& xml)
1044 {
1045     for (;;)
1046     {
1047         MusECore::Xml::Token token = xml.parse();
1048         if (token == MusECore::Xml::Error || token == MusECore::Xml::End)
1049             break;
1050         const QString& tag = xml.s1();
1051         switch (token)
1052         {
1053             case MusECore::Xml::TagStart:
1054                 if (tag == "type")
1055                     type = staff_type_t(xml.parseInt());
1056                 else if (tag == "clef")
1057                     clef = clef_t(xml.parseInt());
1058                 else if (tag == "part")
1059                 {
1060                     MusECore::Part* part=read_part(xml);
1061                     if (part)
1062                         parts.insert(part);
1063                     else
1064                         cerr << "ERROR: THIS SHOULD NEVER HAPPEN: part is NULL while reading from xml" << endl;
1065                 }
1066                 else
1067                     xml.unknown("staff");
1068                 break;
1069 
1070             case MusECore::Xml::TagEnd:
1071                 if (tag == "staff")
1072                     goto staff_readstatus_end;
1073 
1074             default:
1075                 break;
1076         }
1077     }
1078 
1079     staff_readstatus_end:
1080     update_part_indices();
1081 }
1082 
1083 
write_status(int level,MusECore::Xml & xml) const1084 void staff_t::write_status(int level, MusECore::Xml& xml) const
1085 {
1086     xml.tag(level++, "staff");
1087     xml.intTag(level, "type", type);
1088     xml.intTag(level, "clef", clef);
1089     for (set<const MusECore::Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
1090     {
1091         MusECore::Track* track = (*part)->track();
1092         int trkIdx   = MusEGlobal::song->tracks()->index(track);
1093         int partIdx  = track->parts()->index(*part);
1094 
1095         if((trkIdx == -1) || (partIdx == -1))
1096             cerr << "ERROR: staff_t::write_status: trkIdx:"<<trkIdx<<", partIdx:"<<partIdx<<endl;
1097 
1098         xml.put(level, "<part>%d:%d</part>", trkIdx, partIdx);
1099     }
1100     xml.tag(level, "/staff");
1101 }
1102 
1103 //---------------------------------------------------------
1104 //   writeStatus
1105 //---------------------------------------------------------
1106 
writeStatus(int level,MusECore::Xml & xml) const1107 void ScoreEdit::writeStatus(int level, MusECore::Xml& xml) const
1108 {
1109     xml.tag(level++, "scoreedit");
1110     TopWin::writeStatus(level, xml);
1111 
1112     xml.strTag(level, "name", name);
1113     xml.intTag(level, "tool", edit_tools->curTool());
1114     xml.intTag(level, "steprec", srec->isChecked());
1115     xml.intTag(level, "quantPower", score_canvas->quant_power2());
1116     xml.intTag(level, "pxPerWhole", score_canvas->pixels_per_whole());
1117     xml.intTag(level, "newNoteVelo", velo_spinbox->value());
1118     xml.intTag(level, "newNoteVeloOff", velo_off_spinbox->value());
1119     xml.intTag(level, "lastLen", score_canvas->get_last_len());
1120 
1121     int len=0;
1122     if (n1_action->isChecked())
1123         len=1;
1124     else if (n2_action->isChecked())
1125         len=2;
1126     else if (n4_action->isChecked())
1127         len=4;
1128     else if (n8_action->isChecked())
1129         len=8;
1130     else if (n16_action->isChecked())
1131         len=16;
1132     else if (n32_action->isChecked())
1133         len=32;
1134     else if (nlast_action->isChecked())
1135         len=0; // means "last"
1136 
1137     xml.intTag(level, "newLen", len);
1138 
1139     int color=0;
1140     if (color_black_action->isChecked())
1141         color=0;
1142     else if (color_velo_action->isChecked())
1143         color=1;
1144     else if (color_part_action->isChecked())
1145         color=2;
1146 
1147     xml.intTag(level, "noteColor", color);
1148 
1149     xml.intTag(level, "xscroll", xscroll->value());
1150     xml.intTag(level, "yscroll", yscroll->value());
1151     xml.intTag(level, "preambleContainsKeysig", preamble_keysig_action->isChecked());
1152     xml.intTag(level, "preambleContainsTimesig", preamble_timesig_action->isChecked());
1153 
1154     const MusECore::Part* selected_part=score_canvas->get_selected_part();
1155     if (selected_part==NULL)
1156     {
1157         xml.put(level, "<selectedPart>none</selectedPart>");
1158     }
1159     else
1160     {
1161         MusECore::Track* track = selected_part->track();
1162         int trkIdx   = MusEGlobal::song->tracks()->index(track);
1163         int partIdx  = track->parts()->index(selected_part);
1164 
1165         if((trkIdx == -1) || (partIdx == -1))
1166             cerr << "ERROR: ScoreEdit::write_status: trkIdx:"<<trkIdx<<", partIdx:"<<partIdx<<endl;
1167 
1168         xml.put(level, "<selectedPart>%d:%d</selectedPart>", trkIdx, partIdx);
1169     }
1170 
1171 
1172     score_canvas->write_staves(level,xml);
1173 
1174     xml.tag(level, "/scoreedit");
1175 }
1176 
write_staves(int level,MusECore::Xml & xml) const1177 void ScoreCanvas::write_staves(int level, MusECore::Xml& xml) const
1178 {
1179     for (list<staff_t>::const_iterator staff=staves.begin(); staff!=staves.end(); staff++)
1180         staff->write_status(level, xml);
1181 }
1182 
1183 
1184 //---------------------------------------------------------
1185 //   readStatus
1186 //---------------------------------------------------------
1187 
readStatus(MusECore::Xml & xml)1188 void ScoreEdit::readStatus(MusECore::Xml& xml)
1189 {
1190     // never "return;" inside that function.
1191     // instead, goto end_of_scoreedit_read_status;
1192     // (there is a command which must be executed!)
1193 
1194     bool apply_velo_temp=apply_velo;
1195     apply_velo=false;
1196 
1197     for (;;)
1198     {
1199         MusECore::Xml::Token token = xml.parse();
1200         if (token == MusECore::Xml::Error || token == MusECore::Xml::End)
1201             break;
1202 
1203         const QString& tag = xml.s1();
1204         switch (token)
1205         {
1206             case MusECore::Xml::TagStart:
1207                 if (tag == "name")
1208                     set_name(xml.parse1());
1209                 else if (tag == "tool")
1210                     edit_tools->set(xml.parseInt());
1211                 else if (tag == "steprec")
1212                     srec->setChecked(xml.parseInt());
1213                 else if (tag == "quantPower")
1214                     quant_combobox->setCurrentIndex(xml.parseInt()-1);
1215                 else if (tag == "pxPerWhole")
1216                     px_per_whole_spinbox->setValue(xml.parseInt());
1217                 else if (tag == "newNoteVelo")
1218                     velo_spinbox->setValue(xml.parseInt());
1219                 else if (tag == "newNoteVeloOff")
1220                     velo_off_spinbox->setValue(xml.parseInt());
1221                 else if (tag == "lastLen")
1222                     score_canvas->set_last_len(xml.parseInt());
1223                 else if (tag == "newLen")
1224                 {
1225                     int val=xml.parseInt();
1226                     switch (val)
1227                     {
1228                         case 0: nlast_action->setChecked(true); break;
1229                         case 1: n1_action->setChecked(true); break;
1230                         case 2: n2_action->setChecked(true); break;
1231                         case 4: n4_action->setChecked(true); break;
1232                         case 8: n8_action->setChecked(true); break;
1233                         case 16: n16_action->setChecked(true); break;
1234                         case 32: n32_action->setChecked(true); break;
1235                         default:
1236                             cerr << "ERROR: THIS SHOULD NEVER HAPPEN. newLen is invalid in ScoreEdit::readStatus.\n" <<
1237                                     "       (newLen="<<val<<"; the only valid values are 0,1,2,4,8,16 and 32)\n" <<
1238                                     "       however, don't worry, this is no major problem, using 0 instead" << endl;
1239                             nlast_action->setChecked(true);
1240                     }
1241                 }
1242                 else if (tag == "noteColor")
1243                 {
1244                     int val=xml.parseInt();
1245                     switch (val)
1246                     {
1247                         case 0: color_black_action->setChecked(true); break;
1248                         case 1: color_velo_action->setChecked(true); break;
1249                         case 2: color_part_action->setChecked(true); break;
1250                         default:
1251                             cerr << "ERROR: THIS SHOULD NEVER HAPPEN. noteColor is invalid in ScoreEdit::readStatus.\n" <<
1252                                     "       (noteColor="<<val<<"; the only valid values are 0,1 and 2)\n" <<
1253                                     "       however, don't worry, this is no major problem, using 0 instead" << endl;
1254                             color_black_action->setChecked(true);
1255                     }
1256                 }
1257                 else if (tag == "xscroll")
1258                     xscroll->setValue(xml.parseInt());
1259                 else if (tag == "yscroll")
1260                     yscroll->setValue(xml.parseInt());
1261                 else if (tag == "preambleContainsKeysig")
1262                     preamble_keysig_action->setChecked(xml.parseInt());
1263                 else if (tag == "preambleContainsTimesig")
1264                     preamble_timesig_action->setChecked(xml.parseInt());
1265                 else if (tag == "topwin")
1266                     TopWin::readStatus(xml);
1267                 else if (tag == "selectedPart")
1268                     score_canvas->set_selected_part(read_part(xml, "selectedPart"));
1269                 else if (tag == "staff")
1270                 {
1271                     staff_t staff(score_canvas);
1272                     staff.read_status(xml);
1273                     score_canvas->push_back_staff(staff);
1274                 }
1275                 else
1276                     xml.unknown("ScoreEdit");
1277                 break;
1278 
1279             case MusECore::Xml::TagEnd:
1280                 if (tag == "scoreedit")
1281                     goto end_of_scoreedit_read_status;
1282 
1283             default:
1284                 break;
1285         }
1286     }
1287 
1288 end_of_scoreedit_read_status:
1289 
1290     apply_velo=apply_velo_temp;
1291 }
1292 
read_configuration(MusECore::Xml & xml)1293 void ScoreEdit::read_configuration(MusECore::Xml& xml)
1294 {
1295     for (;;)
1296     {
1297         MusECore::Xml::Token token = xml.parse();
1298         if (token == MusECore::Xml::Error || token == MusECore::Xml::End)
1299             break;
1300 
1301         const QString& tag = xml.s1();
1302         switch (token)
1303         {
1304             case MusECore::Xml::TagStart:
1305                 if (tag=="quantPowerInit")
1306                     ScoreCanvas::_quant_power2_init=xml.parseInt();
1307                 else if (tag=="pxPerWholeInit")
1308                     ScoreCanvas::_pixels_per_whole_init=xml.parseInt();
1309                 else if (tag=="newNoteVeloInit")
1310                     ScoreCanvas::note_velo_init=xml.parseInt();
1311                 else if (tag=="newNoteVeloOffInit")
1312                     ScoreCanvas::note_velo_off_init=xml.parseInt();
1313                 else if (tag=="newLenInit")
1314                     ScoreCanvas::new_len_init=xml.parseInt();
1315                 else if (tag=="noteColorInit")
1316                     ScoreCanvas::coloring_mode_init=(ScoreCanvas::coloring_mode_t)xml.parseInt();
1317                 else if (tag=="preambleContainsKeysig")
1318                     ScoreCanvas::preamble_contains_keysig_init=xml.parseInt();
1319                 else if (tag=="preambleContainsTimesig")
1320                     ScoreCanvas::preamble_contains_timesig_init=xml.parseInt();
1321                 else if (tag == "topwin")
1322                     TopWin::readConfiguration(SCORE, xml);
1323                 else
1324                     xml.unknown("ScoreEdit");
1325                 break;
1326 
1327             case MusECore::Xml::TagEnd:
1328                 if (tag == "scoreedit")
1329                     return;
1330 
1331             default:
1332                 break;
1333         }
1334     }
1335 }
1336 
1337 
write_configuration(int level,MusECore::Xml & xml)1338 void ScoreEdit::write_configuration(int level, MusECore::Xml& xml)
1339 {
1340     xml.tag(level++, "scoreedit");
1341 
1342     xml.intTag(level, "quantPowerInit", ScoreCanvas::_quant_power2_init);
1343     xml.intTag(level, "pxPerWholeInit", ScoreCanvas::_pixels_per_whole_init);
1344     xml.intTag(level, "newNoteVeloInit", ScoreCanvas::note_velo_init);
1345     xml.intTag(level, "newNoteVeloOffInit", ScoreCanvas::note_velo_off_init);
1346     xml.intTag(level, "newLenInit", ScoreCanvas::new_len_init);
1347     xml.intTag(level, "noteColorInit", ScoreCanvas::coloring_mode_init);
1348     xml.intTag(level, "preambleContainsKeysig", ScoreCanvas::preamble_contains_keysig_init);
1349     xml.intTag(level, "preambleContainsTimesig", ScoreCanvas::preamble_contains_timesig_init);
1350 
1351     TopWin::writeConfiguration(SCORE, level, xml);
1352 
1353     xml.etag(level, "scoreedit");
1354 }
1355 
1356 
1357 
1358 //---------------------------------------------------------
1359 //   itemsAreSelected
1360 //---------------------------------------------------------
1361 
itemsAreSelected() const1362 bool ScoreCanvas::itemsAreSelected() const
1363 {
1364   FloItem fi;
1365   for(list<staff_t>::const_iterator is = staves.begin(); is != staves.end(); ++is)
1366   {
1367     const staff_t& staff = *is;
1368     const ScoreItemList& sil = staff.itemlist;
1369     for(ScoreItemList::const_iterator isi = sil.begin(); isi != sil.end(); ++isi)
1370     {
1371       const set<FloItem, floComp>& si = isi->second;
1372       for(set<FloItem, floComp>::const_iterator efi = si.begin(); efi != si.end(); ++efi)
1373       {
1374         fi = *efi;
1375         if(fi.source_event && (*fi.source_event).selected())
1376           return true;
1377       }
1378     }
1379   }
1380   return false;
1381 }
1382 
1383 //---------------------------------------------------------
1384 //   tagItems
1385 //---------------------------------------------------------
1386 
tagItems(MusECore::TagEventList * tag_list,const MusECore::EventTagOptionsStruct & options) const1387 void ScoreCanvas::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const
1388 {
1389   const bool tagSelected = options._flags & MusECore::TagSelected;
1390   //const bool tagMoving   = options._flags & MusECore::TagMoving;
1391   const bool tagAllItems = options._flags & MusECore::TagAllItems;
1392   const bool tagAllParts = options._flags & MusECore::TagAllParts;
1393   const bool range       = options._flags & MusECore::TagRange;
1394   const MusECore::Pos& p0 = options._p0;
1395   const MusECore::Pos& p1 = options._p1;
1396 
1397   // Here we can choose between using tagAllParts for parts or staves.
1398   // If we choose parts, it's kind of difficult for the user to tell which part is selected.
1399   // Whereas, if we choose staves, it's much easier to see that a staff is currently selected.
1400   // TODO: Possibly add 'staves' radio buttons to the function dialogs instead.
1401   const bool use_staves_for_tag_all_parts = true;
1402   // For now we don't use the tagAllParts flag.
1403   const bool use_tag_all_parts = false;
1404 
1405   const MusECore::Part* part;
1406   MusECore::Pos pos;
1407   FloItem fi;
1408   for(list<staff_t>::const_iterator is = staves.begin(); is != staves.end(); ++is)
1409   {
1410     const staff_t& staff = *is;
1411 
1412     if(use_tag_all_parts && use_staves_for_tag_all_parts && !tagAllParts && (&staff != &(*current_staff)))
1413       continue;
1414 
1415     const ScoreItemList& sil = staff.itemlist;
1416     for(ScoreItemList::const_iterator isi = sil.begin(); isi != sil.end(); ++isi)
1417     {
1418       const set<FloItem, floComp>& si = isi->second;
1419       for(set<FloItem, floComp>::const_iterator efi = si.begin(); efi != si.end(); ++efi)
1420       {
1421         fi = *efi;
1422 
1423         // Make sure the event and part pointers are valid.
1424         if(!fi.source_event || !fi.source_part)
1425           continue;
1426 
1427         const MusECore::Event& e = *fi.source_event;
1428 
1429         part = fi.source_part;
1430 
1431         if(use_tag_all_parts && !use_staves_for_tag_all_parts && !tagAllParts && part != selected_part)
1432           continue;
1433 
1434         if(range)
1435         {
1436           // Don't forget to add the part's position.
1437           pos = e.pos() + *part;
1438           // p1 should be considered outside (one past) the very last position in the range.
1439           if(pos < p0 || pos >= p1)
1440             continue;
1441         }
1442 
1443         if(tagAllItems
1444            || (tagSelected && e.selected())
1445            //|| (tagMoving && e.isMoving()) //TODO
1446           )
1447         {
1448           tag_list->add(part, e);
1449         }
1450       }
1451     }
1452   }
1453 }
1454 
1455 
add_staves(MusECore::PartList * pl,bool all_in_one)1456 void ScoreCanvas::add_staves(MusECore::PartList* pl, bool all_in_one)
1457 {
1458     if (!pl->empty())
1459     {
1460         staff_t staff(this);
1461 
1462         if (all_in_one)
1463         {
1464             clefTypes clef=((MusECore::MidiTrack*)pl->begin()->second->track())->getClef();
1465 
1466             staff.parts.clear();
1467             for (MusECore::ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++)
1468             {
1469                 if (((MusECore::MidiTrack*)part_it->second->track())->getClef() != clef)
1470                     clef=grandStaff;
1471 
1472                 staff.parts.insert(part_it->second);
1473             }
1474             staff.cleanup_parts();
1475             staff.update_part_indices();
1476 
1477             switch (clef)
1478             {
1479                 case trebleClef:
1480                     staff.type=NORMAL;
1481                     staff.clef=VIOLIN;
1482                     staves.push_back(staff);
1483                     break;
1484 
1485                 case bassClef:
1486                     staff.type=NORMAL;
1487                     staff.clef=BASS;
1488                     staves.push_back(staff);
1489                     break;
1490 
1491                 case grandStaff:
1492                     staff.type=GRAND_TOP;
1493                     staff.clef=VIOLIN;
1494                     staves.push_back(staff);
1495 
1496                     staff.type=GRAND_BOTTOM;
1497                     staff.clef=BASS;
1498                     staves.push_back(staff);
1499                     break;
1500             }
1501         }
1502         else
1503         {
1504             set<MusECore::Track*> tracks;
1505             for (MusECore::ciPart it=pl->begin(); it!=pl->end(); it++)
1506                 tracks.insert(it->second->track());
1507 
1508             MusECore::TrackList* tracklist = MusEGlobal::song->tracks();
1509             // this loop is used for inserting track-staves in the
1510             // correct order. simply iterating through tracks's contents
1511             // would sort after the pointer values, i.e. randomly
1512             for (MusECore::ciTrack track_it=tracklist->begin(); track_it!=tracklist->end(); track_it++)
1513                 if (tracks.find(*track_it)!=tracks.end())
1514                 {
1515                     staff.parts.clear();
1516                     for (MusECore::ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++)
1517                         if (part_it->second->track() == *track_it)
1518                             staff.parts.insert(part_it->second);
1519                     staff.cleanup_parts();
1520                     staff.update_part_indices();
1521 
1522                     switch (((MusECore::MidiTrack*)(*track_it))->getClef())
1523                     {
1524                         case trebleClef:
1525                             staff.type=NORMAL;
1526                             staff.clef=VIOLIN;
1527                             staves.push_back(staff);
1528                             break;
1529 
1530                         case bassClef:
1531                             staff.type=NORMAL;
1532                             staff.clef=BASS;
1533                             staves.push_back(staff);
1534                             break;
1535 
1536                         case grandStaff:
1537                             staff.type=GRAND_TOP;
1538                             staff.clef=VIOLIN;
1539                             staves.push_back(staff);
1540 
1541                             staff.type=GRAND_BOTTOM;
1542                             staff.clef=BASS;
1543                             staves.push_back(staff);
1544                             break;
1545                     }
1546                 }
1547         }
1548 
1549         cleanup_staves();
1550         fully_recalculate();
1551         recalc_staff_pos();
1552     }
1553 }
1554 
1555 
ScoreCanvas(ScoreEdit * pr,QWidget * parent_widget)1556 ScoreCanvas::ScoreCanvas(ScoreEdit* pr, QWidget* parent_widget) : View(parent_widget, 1, 1)
1557 {
1558     parent      = pr;
1559     setFocusPolicy(Qt::StrongFocus);
1560     setBg(MusEGlobal::config.midiCanvasBg);
1561 
1562     setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
1563 
1564     srec=false;
1565     for (int i=0;i<128;i++) held_notes[i]=false;
1566     steprec=new MusECore::StepRec(held_notes);
1567     connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), SLOT(midi_note(int,int)));
1568 
1569     x_pos=0;
1570     x_left=0;
1571     y_pos=0;
1572     have_lasso=false;
1573     inserting=false;
1574     dragging=false;
1575     drag_cursor_changed=false;
1576     mouse_erases_notes=false;
1577     mouse_inserts_notes=true;
1578     undo_started=false;
1579 
1580     selected_part=nullptr;
1581     dragged_event_part=nullptr;
1582 
1583     last_len=384;
1584     new_len=-1; // will be initialized with new_len_init by ScoreEdit::ScoreEdit();
1585 
1586     _quant_power2=_quant_power2_init; // ScoreEdit relies on this to be done!
1587     _pixels_per_whole = _pixels_per_whole_init;
1588 
1589     note_velo=note_velo_init;
1590     note_velo_off=note_velo_off_init;
1591 
1592     dragging_staff=false;
1593 
1594 
1595     coloring_mode=coloring_mode_init;
1596     preamble_contains_keysig=preamble_contains_keysig_init;
1597     preamble_contains_timesig=preamble_contains_timesig_init;
1598 
1599 
1600     x_scroll_speed=0;
1601     x_scroll_pos=0;
1602     y_scroll_speed=0;
1603     y_scroll_pos=0;
1604     connect (MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat_timer_event()));
1605 
1606     connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(pos_changed(int,unsigned,bool)));
1607     connect(MusEGlobal::song, SIGNAL(playChanged(bool)), SLOT(play_changed(bool)));
1608 
1609     staff_menu=new QMenu(this);
1610 
1611     staffmode_treble_action = staff_menu->addAction(tr("Treble"));
1612     connect(staffmode_treble_action, SIGNAL(triggered()), SLOT(staffmode_treble_slot()));
1613 
1614     staffmode_bass_action = staff_menu->addAction(tr("Bass"));
1615     connect(staffmode_bass_action, SIGNAL(triggered()), SLOT(staffmode_bass_slot()));
1616 
1617     staffmode_both_action = staff_menu->addAction(tr("Grand Staff"));
1618     connect(staffmode_both_action, SIGNAL(triggered()), SLOT(staffmode_both_slot()));
1619 
1620     remove_staff_action = staff_menu->addAction(tr("Remove staff"));
1621     connect(remove_staff_action, SIGNAL(triggered()), SLOT(remove_staff_slot()));
1622 
1623     unsetCursor();
1624 
1625     ensurePolished();
1626     init_pixmaps();
1627 }
1628 
~ScoreCanvas()1629 ScoreCanvas::~ScoreCanvas()
1630 {
1631     delete steprec;
1632 }
1633 
staffmode_treble_slot()1634 void ScoreCanvas::staffmode_treble_slot()
1635 {
1636     set_staffmode(current_staff, MODE_TREBLE);
1637 }
1638 
staffmode_bass_slot()1639 void ScoreCanvas::staffmode_bass_slot()
1640 {
1641     set_staffmode(current_staff, MODE_BASS);
1642 }
1643 
staffmode_both_slot()1644 void ScoreCanvas::staffmode_both_slot()
1645 {
1646     set_staffmode(current_staff, MODE_BOTH);
1647 }
1648 
remove_staff_slot()1649 void ScoreCanvas::remove_staff_slot()
1650 {
1651     remove_staff(current_staff);
1652 }
1653 
set_staffmode(list<staff_t>::iterator it,staff_mode_t mode)1654 void ScoreCanvas::set_staffmode(list<staff_t>::iterator it, staff_mode_t mode)
1655 {
1656     if (it->type == GRAND_BOTTOM)
1657     {
1658         it--;
1659         if (it->type!=GRAND_TOP)
1660             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
1661     }
1662 
1663     if (it->type==GRAND_TOP)
1664     {
1665         list<staff_t>::iterator tmp=it;
1666         tmp++;
1667         if (tmp->type!=GRAND_BOTTOM)
1668             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl;
1669         staves.erase(tmp);
1670     }
1671 
1672     switch (mode)
1673     {
1674         case MODE_TREBLE:
1675             it->type=NORMAL;
1676             it->clef=VIOLIN;
1677             break;
1678 
1679         case MODE_BASS:
1680             it->type=NORMAL;
1681             it->clef=BASS;
1682             break;
1683 
1684         case MODE_BOTH:
1685             it->type=GRAND_BOTTOM;
1686             it->clef=BASS;
1687 
1688             staves.insert(it, staff_t(this, GRAND_TOP, VIOLIN, it->parts));
1689             break;
1690 
1691         default:
1692             cerr << "ERROR: ILLEGAL FUNCTION CALL: invalid mode in set_staffmode" << endl;
1693     }
1694 
1695     fully_recalculate();
1696     recalc_staff_pos();
1697 }
1698 
remove_staff(list<staff_t>::iterator it)1699 void ScoreCanvas::remove_staff(list<staff_t>::iterator it)
1700 {
1701     if (it->type == GRAND_BOTTOM)
1702     {
1703         it--;
1704         if (it->type!=GRAND_TOP)
1705             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
1706     }
1707 
1708     if (it->type == NORMAL)
1709     {
1710         staves.erase(it);
1711     }
1712     else if (it->type == GRAND_TOP)
1713     {
1714         staves.erase(it++);
1715         if (it->type!=GRAND_BOTTOM)
1716             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl;
1717         staves.erase(it);
1718     }
1719 
1720     maybe_close_if_empty();
1721     fully_recalculate();
1722     recalc_staff_pos();
1723 }
1724 
merge_staves(list<staff_t>::iterator dest,list<staff_t>::iterator src)1725 void ScoreCanvas::merge_staves(list<staff_t>::iterator dest, list<staff_t>::iterator src)
1726 {
1727     if (dest->type == GRAND_BOTTOM)
1728     {
1729         dest--;
1730         if (dest->type!=GRAND_TOP)
1731             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
1732     }
1733 
1734     if (src->type == GRAND_BOTTOM)
1735     {
1736         src--;
1737         if (src->type!=GRAND_TOP)
1738             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
1739     }
1740 
1741     if (dest==src) //dragged to itself?
1742         return;
1743 
1744 
1745     dest->parts.insert(src->parts.begin(), src->parts.end());
1746 
1747     if (dest->type == GRAND_TOP)
1748     {
1749         dest++;
1750         if (dest->type != GRAND_BOTTOM)
1751             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl;
1752         dest->parts.insert(src->parts.begin(), src->parts.end());
1753     }
1754 
1755     dest->update_part_indices();
1756 
1757     remove_staff(src);
1758 
1759     fully_recalculate();
1760     recalc_staff_pos();
1761 }
1762 
move_staff_above(list<staff_t>::iterator dest,list<staff_t>::iterator src)1763 void ScoreCanvas::move_staff_above(list<staff_t>::iterator dest, list<staff_t>::iterator src)
1764 {
1765     if (dest->type == GRAND_BOTTOM)
1766     {
1767         dest--;
1768         if (dest->type!=GRAND_TOP)
1769             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
1770     }
1771 
1772     if (src->type == GRAND_BOTTOM)
1773     {
1774         src--;
1775         if (src->type!=GRAND_TOP)
1776             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
1777     }
1778 
1779     if (dest==src) //dragged to itself?
1780         return;
1781 
1782 
1783     list<staff_t>::iterator src_end=src;
1784     src_end++; //point _after_ src
1785     if (src->type==GRAND_TOP) //if this is a grand staff, we need to splice two list-entries
1786         src_end++;
1787 
1788     staves.splice(dest, staves, src, src_end);
1789 
1790     fully_recalculate();
1791     recalc_staff_pos();
1792 }
1793 
move_staff_below(list<staff_t>::iterator dest,list<staff_t>::iterator src)1794 void ScoreCanvas::move_staff_below(list<staff_t>::iterator dest, list<staff_t>::iterator src)
1795 {
1796     if (dest->type == GRAND_TOP)
1797     {
1798         dest++;
1799         if (dest->type!=GRAND_BOTTOM)
1800             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl;
1801     }
1802     dest++; //now dest points past the destination staff.
1803             //if dest was a grand staff, it now points past the bottom staff
1804 
1805     move_staff_above(dest, src);
1806 }
1807 
get_all_parts()1808 set<const MusECore::Part*> ScoreCanvas::get_all_parts()
1809 {
1810     set<const MusECore::Part*> result;
1811 
1812     for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
1813         result.insert(it->parts.begin(), it->parts.end());
1814 
1815     return result;
1816 }
1817 
fully_recalculate()1818 void ScoreCanvas::fully_recalculate()
1819 {
1820     song_changed(SC_EVENT_MODIFIED);
1821 }
1822 
song_changed(MusECore::SongChangedStruct_t flags)1823 void ScoreCanvas::song_changed(MusECore::SongChangedStruct_t flags)
1824 {
1825     if(parent && parent->deleting())  // Ignore while while deleting to prevent crash.
1826         return;
1827 
1828     if (flags & (SC_PART_MODIFIED | SC_PART_REMOVED | SC_PART_INSERTED | SC_TRACK_REMOVED))
1829     {
1830         update_parts();
1831 
1832         if (flags & (SC_PART_REMOVED | SC_TRACK_REMOVED))
1833         {
1834             for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
1835                 it->cleanup_parts();
1836 
1837             cleanup_staves();
1838 
1839             for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
1840                 it->recalculate();
1841 
1842             recalc_staff_pos();
1843 
1844             redraw();
1845         }
1846     }
1847 
1848     if (flags & (SC_PART_MODIFIED |
1849                  SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED |
1850                  SC_SIG  | SC_KEY) )
1851     {
1852         calc_pos_add_list();
1853 
1854         for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
1855             it->recalculate();
1856 
1857         recalc_staff_pos();
1858 
1859         redraw();
1860         emit canvas_width_changed(canvas_width());
1861     }
1862 
1863     if (flags & SC_SELECTION)
1864     {
1865         redraw();
1866     }
1867 }
1868 
canvas_width()1869 int ScoreCanvas::canvas_width()
1870 {
1871     //return tick_to_x(staves.begin()->itemlist.rbegin()->first);
1872     return tick_to_x(SONG_LENGTH);
1873 }
1874 
canvas_height()1875 int ScoreCanvas::canvas_height()
1876 {
1877     return staves.empty() ? 0 : staves.rbegin()->y_bottom;
1878 }
1879 
viewport_width()1880 int ScoreCanvas::viewport_width()
1881 {
1882     return (width() - x_left);
1883 }
1884 
viewport_height()1885 int ScoreCanvas::viewport_height()
1886 {
1887     return height();
1888 }
1889 
IntToStr(int i)1890 string IntToStr(int i)
1891 {
1892     ostringstream s;
1893     s<<i;
1894     return s.str();
1895 }
1896 
IntToQStr(int i)1897 QString IntToQStr(int i)
1898 {
1899     return QString(IntToStr(i).c_str());
1900 }
1901 
color_image(QImage & img,const QColor & color)1902 void color_image(QImage& img, const QColor& color)
1903 {
1904     uchar* ptr=img.bits();
1905     int bytes=img.bytesPerLine() * img.height();   // By Tim. For older Qt versions. Tested OK on Qt4.5.
1906     int r,g,b;
1907     color.getRgb(&r,&g,&b);
1908 
1909     for (int i=0; i<bytes/4; i++)
1910     {
1911         QRgb* rgb=((QRgb*)ptr);
1912         (*rgb) = qRgba(r,g,b,qAlpha(*rgb));
1913 
1914         ptr+=4;
1915     }
1916 }
1917 
load_colored_pixmaps(QString file,QPixmap * array,bool all_colors=false)1918 void load_colored_pixmaps(QString file, QPixmap* array, bool all_colors = false)
1919 {
1920     QImage img(file);
1921 
1922     if (all_colors) {
1923         for (int color_index=0;color_index<NUM_MYCOLORS; color_index++)
1924         {
1925             color_image(img, mycolors[color_index]);
1926             array[color_index]=QPixmap::fromImage(img);
1927         }
1928     } else {
1929         color_image(img, mycolors[DEFAULT]);
1930         array[0] = QPixmap::fromImage(img);
1931     }
1932 }
1933 
1934 
1935 
init_pixmaps()1936 void ScoreCanvas::init_pixmaps()
1937 {
1938     if (!pixmaps_initalized)
1939     {
1940         if (debugMsg) cout << "initalizing colors..." << endl;
1941 
1942         mycolors = new QColor[NUM_MYCOLORS];
1943 
1944         mycolors[DEFAULT] = palette().color(QPalette::WindowText);
1945         for (int i = 0; i < NUM_PARTCOLORS; i++)
1946             mycolors[i] = MusEGlobal::config.partColors[i];
1947         mycolors[HIGHLIGHTED_PIXMAP] = Qt::red;
1948         mycolors[SELECTED_PIXMAP] = QColor(255,160,0);
1949 
1950         for (int i=0; i<64; i++)
1951             mycolors[i+VELO_PIXMAP_BEGIN]=QColor(i*4,0,0xff);
1952         for (int i=64; i<128; i++)
1953             mycolors[i+VELO_PIXMAP_BEGIN]=QColor(0xff,0,(127-i)*4);
1954 
1955 
1956         if (debugMsg) cout << "loading pixmaps..." << endl;
1957 
1958         pix_whole=new QPixmap[NUM_MYCOLORS];
1959         pix_half=new QPixmap[NUM_MYCOLORS];
1960         pix_quarter=new QPixmap[NUM_MYCOLORS];
1961         pix_dot=new QPixmap[NUM_MYCOLORS];
1962         pix_b=new QPixmap[NUM_MYCOLORS];
1963         pix_sharp=new QPixmap[NUM_MYCOLORS];
1964         pix_noacc=new QPixmap[NUM_MYCOLORS];
1965         pix_num=new QPixmap[10];
1966 
1967         pix_r1=new QPixmap;
1968         pix_r2=new QPixmap;
1969         pix_r4=new QPixmap;
1970         pix_r8=new QPixmap;
1971         pix_r16=new QPixmap;
1972         pix_r32=new QPixmap;
1973 
1974         pix_clef_violin=new QPixmap;
1975         pix_clef_bass=new QPixmap;
1976 
1977         pix_flag_up=new QPixmap[4];
1978         pix_flag_down=new QPixmap[4];
1979 
1980 
1981         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/whole.png", pix_whole, true);
1982         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/half.png", pix_half, true);
1983         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/quarter.png", pix_quarter, true);
1984         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/dot.png", pix_dot, true);
1985         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/acc_none.png", pix_noacc, true);
1986         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/acc_sharp.png", pix_sharp, true);
1987         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/acc_b.png", pix_b, true);
1988 
1989         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/rest1.png", pix_r1);
1990         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/rest2.png", pix_r2);
1991         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/rest4.png", pix_r4);
1992         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/rest8.png", pix_r8);
1993         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/rest16.png", pix_r16);
1994         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/rest32.png", pix_r32);
1995 
1996         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/flags8u.png", &pix_flag_up[0]);
1997         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/flags16u.png", &pix_flag_up[1]);
1998         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/flags32u.png", &pix_flag_up[2]);
1999         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/flags64u.png", &pix_flag_up[3]);
2000         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/flags8d.png", &pix_flag_down[0]);
2001         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/flags16d.png", &pix_flag_down[1]);
2002         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/flags32d.png", &pix_flag_down[2]);
2003         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/flags64d.png", &pix_flag_down[3]);
2004 
2005         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/clef_violin_big.png", pix_clef_violin);
2006         load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/clef_bass_big.png", pix_clef_bass);
2007 
2008         for (int i = 0; i < 10; i++)
2009             load_colored_pixmaps(MusEGlobal::museGlobalShare + "/scoreglyphs/" + IntToQStr(i) + ".png", &pix_num[i]);
2010 
2011 
2012         pixmaps_initalized=true;
2013 
2014         if (debugMsg) cout << "done" << endl;
2015     }
2016 }
2017 
2018 
2019 
modulo(int a,int b)2020 int modulo(int a, int b) // similar to a % b
2021 {
2022     return (((a%b)+b)%b);
2023 }
2024 
divide_floor(int a,int b)2025 int divide_floor(int a, int b) // similar to a / b
2026 {
2027     return int(floor(float(a)/float(b)));
2028 }
2029 
2030 #define DEFAULT_REST_HEIGHT 6
2031 
2032 
operator <(const note_pos_t & a,const note_pos_t & b)2033 bool operator< (const note_pos_t& a, const note_pos_t& b)
2034 {
2035     if (a.height<b.height) return true;
2036     if (a.height>b.height) return false;
2037     return a.vorzeichen<b.vorzeichen;
2038 }
2039 
2040 
2041 
flo_quantize(int tick,int quant_ticks)2042 int flo_quantize(int tick, int quant_ticks)
2043 {
2044     return MusEGlobal::sigmap.raster(tick, quant_ticks);
2045 }
2046 
flo_quantize_floor(int tick,int quant_ticks)2047 int flo_quantize_floor(int tick, int quant_ticks)
2048 {
2049     return MusEGlobal::sigmap.raster1(tick, quant_ticks);
2050 }
2051 
2052 
2053 /* builds the event list used by the score editor.
2054  * that list contains only note-on and -off, time-sig- and
2055  * key-change events.
2056  * it stores them sorted by their time (quantized); if more
2057  * events with the same time occur, the NOTE-OFFs are
2058  * put before the NOTE-ONs
2059  * it only operates on parts which were selected in the
2060  * arranger when the score viewer was started
2061  *
2062  * this abstracts the rest of the renderer from muse's internal
2063  * data structures, making this easy to port to another application
2064  */
create_appropriate_eventlist()2065 void staff_t::create_appropriate_eventlist()
2066 {
2067     using MusEGlobal::sigmap;
2068     using MusECore::iSigEvent;
2069 
2070     eventlist.clear();
2071 
2072     // phase one: fill the list -----------------------------------------
2073 
2074     //insert note on events
2075     for (set<const MusECore::Part*>::const_iterator part_it=parts.begin(); part_it!=parts.end(); part_it++)
2076     {
2077         const MusECore::Part* part=*part_it;
2078 
2079         for (MusECore::ciEvent it=part->events().begin(); it!=part->events().end(); it++)
2080         {
2081             const MusECore::Event& event=it->second;
2082 
2083             if ( ( event.isNote() && !event.isNoteOff() &&
2084                    // (event.endTick() <= part->lenTick()) ) &&
2085                    (event.tick() <= part->lenTick()) ) && // changed to accord to prcanvas.cpp and others (flo93)
2086                  ( ((type==GRAND_TOP) && (event.pitch() >= SPLIT_NOTE)) ||
2087                    ((type==GRAND_BOTTOM) && (event.pitch() < SPLIT_NOTE)) ||
2088                    (type==NORMAL) )                          )
2089             {
2090                 unsigned begin, end;
2091                 begin=flo_quantize(event.tick()+part->tick(), parent->quant_ticks());
2092                 end=flo_quantize(event.endTick()+part->tick(), parent->quant_ticks());
2093                 if (end==begin)
2094                 {
2095                     if (heavyDebugMsg) cout << "note len would be quantized to zero. using minimal possible length" << endl;
2096                     end=begin+parent->quant_ticks();
2097                 }
2098 
2099                 if (heavyDebugMsg) cout << "inserting note on at "<<begin<<" with pitch="<<event.pitch()<<" and len="<<end-begin<<endl;
2100                 eventlist.insert(pair<unsigned, FloEvent>(begin, FloEvent(begin,event.pitch(), event.velo(),end-begin,FloEvent::NOTE_ON,part,&it->second)));
2101             }
2102             //else ignore it
2103         }
2104     }
2105 
2106     //insert bars and time signatures
2107     for (iSigEvent it=MusEGlobal::sigmap.begin(); it!=MusEGlobal::sigmap.end(); it++)
2108     {
2109         unsigned from=it->second->tick;
2110         unsigned to=it->first;
2111         unsigned ticks_per_measure=MusEGlobal::sigmap.ticksMeasure(it->second->tick);
2112 
2113         if (to > unsigned(SONG_LENGTH))
2114             to=SONG_LENGTH;
2115 
2116         if (heavyDebugMsg) cout << "new signature from tick "<<from<<" to " << to << ": "<<it->second->sig.z<<"/"<<it->second->sig.n<<"; ticks per measure = "<<ticks_per_measure<<endl;
2117         eventlist.insert(pair<unsigned, FloEvent>(from,  FloEvent(from, FloEvent::TIME_SIG, it->second->sig.z, it->second->sig.n) ) );
2118         for (unsigned t=from; t<to; t+=ticks_per_measure)
2119             eventlist.insert(pair<unsigned, FloEvent>(t,  FloEvent(t,0,0,ticks_per_measure,FloEvent::BAR) ) );
2120     }
2121 
2122     //insert key changes
2123     for (MusECore::iKeyEvent it=MusEGlobal::keymap.begin(); it!=MusEGlobal::keymap.end(); it++)
2124         eventlist.insert(pair<unsigned, FloEvent>(
2125           it->second.tick, FloEvent(it->second.tick,FloEvent::KEY_CHANGE, it->second.key, it->second.minor ) ) );
2126 
2127     // phase two: deal with overlapping notes ---------------------------
2128     ScoreEventList::iterator it, it2;
2129 
2130     //iterate through all note_on - events
2131     for (it=eventlist.begin(); it!=eventlist.end(); it++) {
2132         if (it->second.type==FloEvent::NOTE_ON)
2133         {
2134             unsigned end_tick=it->first + it->second.len;
2135 
2136             //iterate though all (relevant) later note_ons which are
2137             //at the same pitch. if there's a collision, shorten it's len
2138             for (it2=it, it2++; it2!=eventlist.end() && it2->first < end_tick; it2++)
2139                 if ((it2->second.type==FloEvent::NOTE_ON) && (it2->second.pitch == it->second.pitch))
2140                     it->second.len=it2->first - it->first;
2141         }
2142     }
2143 
2144 
2145         // phase three: eliminate zero-length-notes -------------------------
2146         for (it=eventlist.begin(); it!=eventlist.end();) {
2147             if ((it->second.type==FloEvent::NOTE_ON) && (it->second.len<=0))
2148                 eventlist.erase(it++);
2149             else
2150                 it++;
2151         }
2152 }
2153 
2154 
is_sharp_key(MusECore::key_enum t)2155 bool is_sharp_key(MusECore::key_enum t)
2156 {
2157     return ((t>=MusECore::KEY_SHARP_BEGIN) && (t<=MusECore::KEY_SHARP_END));
2158 }
is_b_key(MusECore::key_enum t)2159 bool is_b_key(MusECore::key_enum t)
2160 {
2161     return ((t>=MusECore::KEY_B_BEGIN) && (t<=MusECore::KEY_B_END));
2162 }
2163 
n_accidentials(MusECore::key_enum t)2164 int n_accidentials(MusECore::key_enum t)
2165 {
2166     if (is_sharp_key(t))
2167         return t-MusECore::KEY_SHARP_BEGIN-1;
2168     else
2169         return t-MusECore::KEY_B_BEGIN-1;
2170 }
2171 
2172 
2173 //note needs to be 0..11
2174 //always assumes violin clef
2175 //only for internal use
note_pos_(int note,MusECore::key_enum key)2176 note_pos_t note_pos_(int note, MusECore::key_enum key)
2177 {
2178     note_pos_t result;
2179                //C CIS D DIS E F FIS G GIS A AIS H
2180     int foo[12]={0,-1, 1,-1, 2,3,-1, 4,-1, 5, -1,6};
2181 
2182     if ((note<0) || (note>=12))
2183         cerr << "ERROR: ILLEGAL FUNCTION CALL (note_pos, note out of range)" << endl;
2184 
2185     if (foo[note]!=-1)
2186     {
2187         result.height=foo[note];
2188         result.vorzeichen=NONE;
2189     }
2190     else
2191     {
2192         if (is_sharp_key(key))
2193         {
2194             result.height=foo[note-1];
2195             result.vorzeichen=SHARP;
2196         }
2197         else // if is_b_key
2198         {
2199             result.height=foo[note+1];
2200             result.vorzeichen=B;
2201         }
2202     }
2203 
2204     // Special cases for GES / FIS keys
2205     if (key==MusECore::KEY_GES)
2206     {
2207         // convert a H to a Ces
2208         if (note==11)
2209         {
2210             result.height=12;
2211             result.vorzeichen=B;
2212         }
2213     }
2214     else if (key==MusECore::KEY_FIS)
2215     {
2216         // convert a F to an Eis
2217         if (note==5)
2218         {
2219             result.height=2;
2220             result.vorzeichen=SHARP;
2221         }
2222     }
2223 
2224     return result;
2225 }
2226 
2227 
2228 //  V   --------------------------  <-- height=10
2229 //  I C --------------------------  <-- height=8
2230 //  O L --------------------------  <-- height=6
2231 //  L E --------------------------  <-- height=4
2232 //  I F --------------------------  <-- height=2
2233 //  N    --o--                      <-- this is C4. height=0
2234 
2235 // the "spaces" in between the lines have odd numbers.
2236 // that is, the space between line 2 and 4 is numbered 3.
2237 
2238 // these numbers do not change when clef changes. line 2
2239 // is always the "bottom line" of the system.
2240 // in violin clef, line 2 is E4
2241 // in bass clef, line 2 is G2
2242 
note_pos(unsigned note,MusECore::key_enum key,clef_t clef)2243 note_pos_t note_pos (unsigned note, MusECore::key_enum key, clef_t clef)
2244 {
2245     int octave=(note/12)-1; //integer division. note is unsigned
2246     note=note%12;
2247 
2248     //now octave contains the octave the note is in
2249     //(A4 is the 440Hz tone. C4 is the "low C" in the violin clef
2250     //and the "high C" in the bass clef.
2251     //note contains 0 for C, 1 for Cis, ..., 11 for H (or B if you're not german)
2252 
2253     note_pos_t pos=note_pos_(note,key);
2254 
2255     switch (clef) //CLEF_MARKER
2256     {
2257         case VIOLIN:
2258             pos.height=pos.height + (octave-4)*7;
2259             break;
2260 
2261         case BASS:
2262             pos.height=pos.height + (octave-3)*7 + 5;
2263             break;
2264     }
2265 
2266     return pos;
2267 }
2268 
2269 
calc_len(int l,int d)2270 int calc_len(int l, int d)
2271 {
2272     // l=0,1,2 -> whole, half, quarter (think of 2^0, 2^1, 2^2)
2273     // d=number of dots
2274 
2275     int tmp=0;
2276     for (int i=0;i<=d;i++)
2277         tmp+=TICKS_PER_WHOLE / (1 << (l+i));
2278 
2279     return tmp;
2280 }
2281 
2282 
calc_measure_len(const list<int> & nums,int denom)2283 int calc_measure_len(const list<int>& nums, int denom)
2284 {
2285     int sum=0;
2286 
2287     for (list<int>::const_iterator it=nums.begin(); it!=nums.end(); it++)
2288         sum+=*it;
2289 
2290     return 64* sum/denom;
2291 }
2292 
create_emphasize_list(const list<int> & nums,int denom)2293 vector<int> create_emphasize_list(const list<int>& nums, int denom)
2294 {
2295     if (heavyDebugMsg)
2296     {
2297         cout << "creating emphasize list for ";
2298         for (list<int>::const_iterator it=nums.begin(); it!=nums.end(); it++)
2299             cout << *it << " ";
2300         cout << "/ "<<denom;
2301     }
2302 
2303     //        |----- 8th -----|
2304     int foo[]={4,7,6,7,5,7,6,7}; //if 64 changes, this also must change
2305     int pos=0;
2306     int len=calc_measure_len(nums, denom);
2307 
2308     vector<int> result(len);
2309 
2310     for (int i=0;i<len;i++)
2311         result[i]=foo[i%8];
2312 
2313     for (list<int>::const_iterator it=nums.begin(); it!=nums.end(); it++)
2314     {
2315         result[pos]=1;
2316         for (int i=1;i<*it;i++)
2317             result[pos + i*64/denom]=2;
2318         pos+= *it * 64 / denom;
2319     }
2320 
2321     result[0]=0;
2322 
2323     if (heavyDebugMsg)
2324     {
2325         for (int i=0;i<len;i++)
2326         {
2327             if (i%8==0)
2328                 cout << endl<<i<<":\t";
2329             cout << result[i]<<" ";
2330         }
2331         cout << endl;
2332     }
2333 
2334     return result;
2335 }
2336 
create_emphasize_list(int num,int denom)2337 vector<int> create_emphasize_list(int num, int denom)
2338 {
2339     list<int> nums;
2340 
2341     if (num%3 ==0)
2342     {
2343         for (int i=0;i<num/3;i++)
2344             nums.push_back(3);
2345     }
2346     else if (num%2 ==0)
2347     {
2348         for (int i=0;i<num/2;i++)
2349             nums.push_back(2);
2350     }
2351     else // num is odd
2352     {
2353         for (int i=0;i<(num-3)/2;i++)
2354             nums.push_back(2);
2355 
2356         nums.push_back(3);
2357     }
2358 
2359     return create_emphasize_list(nums, denom);
2360 }
2361 
2362 //quant_power2 must be in log(len), that is
2363 //whole, half, quarter, eighth = 0,1,2,3
2364 //NOT:  1,2,4,8! (think of 2^foo)
2365 //len is in ticks
parse_note_len(int len_ticks,int begin_tick,vector<int> & foo,bool allow_dots,bool allow_normal)2366 list<note_len_t> parse_note_len(int len_ticks, int begin_tick, vector<int>& foo, bool allow_dots, bool allow_normal)
2367 {
2368     list<note_len_t> retval;
2369 
2370     if (len_ticks<0)
2371         cerr << "ERROR: ILLEGAL FUNCTION CALL in parse_note_len: len_ticks < 0" << endl;
2372     if (begin_tick<0)
2373         cerr << "ERROR: ILLEGAL FUNCTION CALL in parse_note_len: begin_tick < 0" << endl;
2374 
2375     if (allow_normal)
2376     {
2377         int dot_max = allow_dots ? MAX_QUANT_POWER : 0;
2378 
2379         for (int i=0;i<=MAX_QUANT_POWER;i++)
2380             for (int j=0;j<=dot_max-i;j++)
2381                 if (calc_len(i,j) == len_ticks)
2382                 {
2383                     retval.push_back(note_len_t (i,j));
2384                     return retval;
2385                 }
2386     }
2387 
2388     //if !allow_normal or if the above failed
2389 
2390     int begin=begin_tick * 64 / TICKS_PER_WHOLE;
2391     int len=len_ticks * 64 / TICKS_PER_WHOLE;
2392 
2393     unsigned pos=begin;
2394     int len_done=0;
2395 
2396     while (len_done<len)
2397     {
2398         int len_now=0;
2399         int last_number=foo[pos];
2400 
2401         do {pos++;len_done++;len_now++;} while (! ((pos==foo.size()) || (foo[pos]<=last_number) || (len_done==len)) );
2402 
2403         len_now=len_now*TICKS_PER_WHOLE/64;
2404 
2405         if (heavyDebugMsg) cout << "add " << len_now << " ticks" << endl;
2406         if (allow_dots)
2407         {
2408             for (int i=0;i<=MAX_QUANT_POWER;i++)
2409                 for (int j=0;j<=MAX_QUANT_POWER-i;j++)
2410                     if (calc_len(i,j) == len_now)
2411                     {
2412                         retval.push_back(note_len_t (i,j));
2413                         len_now=0;
2414                     }
2415         }
2416 
2417         if (len_now) //the above failed or allow_dots=false
2418         {
2419             for (int i=0; i<=MAX_QUANT_POWER; i++)
2420             {
2421                 int tmp=calc_len(i,0);
2422                 if (tmp <= len_now)
2423                 {
2424                     retval.push_back(note_len_t(i));
2425                     len_now-=tmp;
2426                     if (len_now==0) break;
2427                 }
2428             }
2429         }
2430 
2431         if (len_now!=0)
2432             cerr << "ERROR: THIS SHOULD NEVER HAPPEN. wasn't able to split note len properly; len_now="<<len_now << endl;
2433 
2434         if (pos==foo.size()) //we cross measure boundaries?
2435             pos=0;
2436     }
2437 
2438 
2439     return retval;
2440 }
2441 
2442 
2443 #define YLEN 10
2444 #define NOTE_SHIFT 3
2445 
2446 #define REST_AUSWEICH_X 10
2447 #define DOT_XDIST 6
2448 #define DOT_XBEGIN 10
2449 #define DOT_XBEGIN_REST 10
2450 
2451 #define NUMBER_HEIGHT (pix_num[0].height())
2452 
2453 // kann 0 oder 1 sein:
2454 // bei notenkollisionen mit ungerader anzahl von kollidierenden
2455 // wird immer so ausgewichen, dass möglichst wenige ausweichen müssen
2456 // wenn die anzahl aber gerade ist, gibt es keine "bessere" lösung
2457 // in dem fall werden immer die geraden (0) bzw. ungeraden (1)
2458 // ausweichen.
2459 // ROUGH TRANSLATION:
2460 // can be 0 or 1:
2461 // when there are note head collisions with an odd number of colliding
2462 // heads there's an unique solution for "stepping aside", so that
2463 // fewer notes must "step aside". but when the number of colliding
2464 // heads is even, there is no "better" solution. this constant
2465 // specifies whether the "odd" (1) or the "even" (0) heads will move.
2466 #define AUSWEICHEN_BEVORZUGT 0
2467 
2468 #define STEM_LEN 30
2469 
2470 #define DOTTED_RESTS true
2471 #define UNSPLIT_RESTS false
2472 
2473 #define AUX_LINE_LEN 1.5
2474 
2475 #define ACCIDENTIAL_DIST 11
2476 #define KEYCHANGE_ACC_DIST 9
2477 #define KEYCHANGE_ACC_LEFTDIST 9
2478 #define KEYCHANGE_ACC_RIGHTDIST 0
2479 
2480 
2481 #define no_notepos note_pos_t()
2482 
2483 #define TIE_DIST 5
2484 #define TIE_HEIGHT 6
2485 #define TIE_THICKNESS 3
2486 
draw_tie(QPainter & p,int x1,int x4,int yo,bool up,QColor color)2487 void ScoreCanvas::draw_tie (QPainter& p, int x1, int x4, int yo, bool up, QColor color)
2488 {
2489     QPainterPath path;
2490 
2491     int y1, y2, y3;
2492 
2493     if (up)
2494     {
2495         y1 = yo - TIE_DIST;
2496         y2 = y1 - TIE_HEIGHT;
2497         y3=y2-TIE_THICKNESS;
2498     }
2499     else
2500     {
2501         y1 = yo + TIE_DIST;
2502         y2 = y1 + TIE_HEIGHT;
2503         y3=y2+TIE_THICKNESS;
2504     }
2505 
2506     int x2 = x1 + (x4-x1)/4;
2507     int x3 = x4 - (x4-x1)/4;
2508 
2509     path.moveTo(x1,y1);
2510     path.cubicTo( x2,y2  ,  x3,y2  ,  x4,y1 );
2511     path.cubicTo( x3,y3  ,  x2,y3  ,  x1,y1 );
2512 
2513     p.setPen(color);
2514     p.setBrush(color);
2515 
2516     p.drawPath(path);
2517 }
2518 
draw_akkolade(QPainter & p,int x,int y_)2519 void ScoreCanvas::draw_akkolade (QPainter& p, int x, int y_)
2520 {
2521     QPainterPath path;
2522 
2523     qreal h = (2*2*YLEN+GRANDSTAFF_DISTANCE) /2.0  +3; //this is only the half height
2524     qreal w         = AKKOLADE_WIDTH;
2525     int y = y_ -h;
2526 
2527     const double X1 =  2.0 * w;
2528     const double X2 = -0.7096 * w;
2529     const double X3 = -1.234 * w;
2530     const double X4 =  1.734 * w;
2531 
2532     path.moveTo(x+ 0, y+ h);
2533     path.cubicTo(x+ X1,  y+ h + h * .3359, x+ X2,  y+ h + h * .5089, x+ w, y+ 2 * h);
2534     path.cubicTo(x+ X3,  y+ h + h * .5025, x+ X4,  y+ h + h * .2413, x+ 0, y+ h);
2535     path.cubicTo(x+ X1,  y+ h - h * .3359, x+ X2,  y+ h - h * .5089, x+ w, y+ 0);
2536     path.cubicTo(x+ X3,  y+ h - h * .5025, x+ X4,  y+ h - h * .2413, x+ 0, y+ h);
2537 
2538     p.drawPath(path);
2539 }
2540 
draw_accidentials(QPainter & p,int x,int y_offset,const list<int> & acc_list,const QPixmap & pix)2541 void ScoreCanvas::draw_accidentials(QPainter& p, int x, int y_offset, const list<int>& acc_list, const QPixmap& pix)
2542 {
2543     int n_acc_drawn=0;
2544 
2545     for (list<int>::const_iterator acc_it=acc_list.begin(); acc_it!=acc_list.end(); acc_it++)
2546     {
2547         int y_coord=2*YLEN  -  ( *acc_it -2)*YLEN/2;
2548         draw_pixmap(p,x + n_acc_drawn*KEYCHANGE_ACC_DIST,y_offset + y_coord,pix);
2549         n_acc_drawn++;
2550     }
2551 }
2552 
create_itemlist()2553 void staff_t::create_itemlist()
2554 {
2555     MusECore::key_enum tmp_key=MusECore::KEY_C;
2556     int lastevent=0;
2557     int next_measure=-1;
2558     int last_measure=-1;
2559     vector<int> emphasize_list=create_emphasize_list(4,4); //actually unnecessary, for safety
2560 
2561     itemlist.clear();
2562 
2563     for (ScoreEventList::iterator it=eventlist.begin(); it!=eventlist.end(); it++)
2564     {
2565         int t, pitch, len, velo, actual_tick;
2566         FloEvent::typeEnum type;
2567         t=it->first;
2568         pitch=it->second.pitch;
2569         velo=it->second.vel;
2570         len=it->second.len;
2571         type=it->second.type;
2572         actual_tick=it->second.tick;
2573         if (actual_tick==-1) actual_tick=t;
2574 
2575         note_pos_t notepos=note_pos(pitch,tmp_key,clef);
2576 
2577         if (heavyDebugMsg)
2578         {
2579             printf("FLO: t=%i\ttype=%i\tpitch=%i\tvel=%i\tlen=%i\n",it->first, it->second.type, it->second.pitch, it->second.vel, it->second.len);
2580             cout << "\tline="<<notepos.height<<"\tvorzeichen="<<notepos.vorzeichen << endl;
2581         }
2582 
2583         if (type==FloEvent::BAR)
2584         {
2585             if (last_measure!=-1) //i.e.: "this is NOT the first bar"
2586             {
2587                 if (lastevent==last_measure) //there was no note?
2588                 {
2589                     unsigned tmppos=(last_measure+t-parent->quant_ticks())/2;
2590                     if (heavyDebugMsg) cout << "\tend-of-measure: this was an empty measure. inserting rest in between at t="<<tmppos << endl;
2591                     itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,0,0) );
2592                     itemlist[t].insert( FloItem(FloItem::REST_END,notepos,0,0) );
2593                 }
2594                 else
2595                 {
2596                     // if necessary, insert rest at between last note and end-of-measure
2597                     int rest=t-lastevent;
2598                     if (rest)
2599                     {
2600                         if (heavyDebugMsg) printf("\tend-of-measure: set rest at %i with len %i\n",lastevent,rest);
2601 
2602                         list<note_len_t> lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS);
2603                         unsigned tmppos=lastevent;
2604                         for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
2605                         {
2606                             if (heavyDebugMsg) cout << "\t\tpartial rest with len="<<x->len<<", dots="<<x->dots<<endl;
2607                             itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,x->len,x->dots) );
2608                             tmppos+=calc_len(x->len,x->dots);
2609                             itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) );
2610                         }
2611                     }
2612                 }
2613             }
2614 
2615             lastevent=t;
2616             last_measure=t;
2617             next_measure=t+len;
2618 
2619             itemlist[t].insert( FloItem(FloItem::BAR,no_notepos,0,0) );
2620         }
2621         else if (type==FloEvent::NOTE_ON)
2622         {
2623             int rest=t-lastevent;
2624             if (rest)
2625             {
2626                 if (heavyDebugMsg) printf("\tset rest at %i with len %i\n",lastevent,rest);
2627                 // no need to check if the rest crosses measure boundaries;
2628                 // it can't.
2629 
2630                 list<note_len_t> lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS);
2631                 unsigned tmppos=lastevent;
2632                 for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
2633                 {
2634                     if (heavyDebugMsg) cout << "\t\tpartial rest with len="<<x->len<<", dots="<<x->dots<<endl;
2635                     itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,x->len,x->dots) );
2636                     tmppos+=calc_len(x->len,x->dots);
2637                     itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) );
2638                 }
2639             }
2640 
2641 
2642 
2643             if (heavyDebugMsg) printf("\tset note at %i with len=%i\n", t, len);
2644 
2645             int tmplen;
2646             bool tied_note;
2647 
2648             // if the note exceeds the current measure, split it.
2649             if (t+len>next_measure)
2650             {
2651                 tmplen=next_measure-t;
2652                 tied_note=true;
2653 
2654                 //append the "remainder" of the note to our EventList, so that
2655                 //it gets processed again when entering the new measure
2656                 int newlen=len-tmplen;
2657                 eventlist.insert(pair<unsigned, FloEvent>(next_measure, FloEvent(actual_tick,pitch, velo,0,FloEvent::NOTE_OFF, it->second.source_part, it->second.source_event)));
2658                 eventlist.insert(pair<unsigned, FloEvent>(next_measure, FloEvent(actual_tick,pitch, velo,newlen,FloEvent::NOTE_ON, it->second.source_part, it->second.source_event)));
2659 
2660                 if (heavyDebugMsg) cout << "\t\tnote was split to length "<<tmplen<<" + " << newlen<<endl;
2661             }
2662             else
2663             {
2664                 tmplen=len;
2665                 tied_note=false;
2666 
2667                 if (heavyDebugMsg) cout << "\t\tinserting NOTE OFF at "<<t+len<<endl;
2668                 eventlist.insert(pair<unsigned, FloEvent>(t+len,   FloEvent(t+len,pitch, velo,0,FloEvent::NOTE_OFF,it->second.source_part, it->second.source_event)));
2669             }
2670 
2671             list<note_len_t> lens=parse_note_len(tmplen,t-last_measure,emphasize_list,true,true);
2672             unsigned tmppos=t;
2673             int n_lens=lens.size();
2674             int count=0;
2675             for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
2676             {
2677                 if (heavyDebugMsg) cout << "\t\tpartial note with len="<<x->len<<", dots="<<x->dots<<endl;
2678                 count++;
2679 
2680                 bool tie;
2681 
2682                 if (count<n_lens)
2683                     tie=true;      // all notes except the last are always tied
2684                 else
2685                     tie=tied_note; // only the last respects tied_note
2686 
2687                 itemlist[tmppos].insert( FloItem(FloItem::NOTE,notepos,x->len,x->dots, tie, actual_tick, it->second.source_part, it->second.source_event) );
2688                 tmppos+=calc_len(x->len,x->dots);
2689                 itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,notepos,0,0) );
2690             }
2691         }
2692         else if (type==FloEvent::NOTE_OFF)
2693         {
2694             lastevent=t;
2695         }
2696         else if (type==FloEvent::TIME_SIG)
2697         {
2698             if (heavyDebugMsg) cout << "inserting TIME SIGNATURE "<<it->second.num<<"/"<<it->second.denom<<" at "<<t<<endl;
2699             itemlist[t].insert( FloItem(FloItem::TIME_SIG, it->second.num, it->second.denom) );
2700 
2701             emphasize_list=create_emphasize_list(it->second.num, it->second.denom);
2702         }
2703         else if (type==FloEvent::KEY_CHANGE)
2704         {
2705             if (heavyDebugMsg) cout << "inserting KEY CHANGE ("<<it->second.key<<") at "<<t<<endl;
2706             itemlist[t].insert( FloItem(FloItem::KEY_CHANGE, it->second.key, it->second.minor) );
2707             tmp_key=it->second.key;
2708         }
2709     }
2710 }
2711 
process_itemlist()2712 void staff_t::process_itemlist()
2713 {
2714     map<int,int> occupied;
2715     int last_measure=0;
2716     vector<int> emphasize_list=create_emphasize_list(4,4); //unnecessary, only for safety
2717 
2718     //iterate through all times with items
2719     for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++)
2720     {
2721         set<FloItem, floComp>& curr_items=it2->second;
2722 
2723         if (heavyDebugMsg) cout << "at t="<<it2->first<<endl;
2724 
2725         // phase 0: keep track of active notes, rests -------------------
2726         //          (and occupied lines) and the last measure
2727         //          and the current time signature
2728         for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
2729         {
2730             if ((it->type==FloItem::NOTE) || (it->type==FloItem::REST))
2731                 occupied[it->pos.height]++;
2732             else if ((it->type==FloItem::NOTE_END) || (it->type==FloItem::REST_END))
2733                 occupied[it->pos.height]--;
2734             else if (it->type==FloItem::BAR)
2735                 last_measure=it2->first;
2736             else if (it->type==FloItem::TIME_SIG)
2737                 emphasize_list=create_emphasize_list(it->num, it->denom);
2738         }
2739 
2740         if (heavyDebugMsg)
2741         {
2742             cout << "occupied: ";
2743             for (map<int,int>::iterator i=occupied.begin(); i!=occupied.end(); i++)
2744                 if (i->second) cout << i->first << "("<<i->second<<")   ";
2745             cout << endl;
2746         }
2747 
2748 
2749 
2750 
2751 
2752         // phase 1: group rests together -----------------------------------
2753         int n_groups=0;
2754         bool dont_group=false;
2755 
2756         //iterate through all rests R at that time
2757         //  iterate through all rests X at that time below R
2758         //    if something is between X and R ("barrier"), stop
2759         //    else: group them together
2760         for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end();)
2761         {
2762             //only operate on rests; ignore rests which are created by this code
2763             //(can be seen on already_grouped)
2764             if ((it->type==FloItem::REST) && (it->already_grouped==false))
2765             {
2766                 if (heavyDebugMsg) cout << "trying to group" << endl;
2767 
2768                 int lastheight;
2769                 int height_cumulative=0;
2770                 int counter=0;
2771 
2772                 lastheight=it->pos.height;
2773 
2774                 set<FloItem, floComp>::iterator tmp;
2775                 for (tmp=it; tmp!=curr_items.end();)
2776                 {
2777                     if (heavyDebugMsg) cout << "checking if we can proceed with an item at height="<<tmp->pos.height<<endl;
2778 
2779                     for (int i=lastheight+1; i<=tmp->pos.height-1; i++)
2780                         if (occupied[i]!=0)
2781                         {
2782                             if (heavyDebugMsg) cout << "we can NOT, because occ["<<i<<"] != 0" << endl;
2783                             //stop grouping that rest
2784                             goto get_out_here;
2785                         }
2786 
2787                     lastheight=tmp->pos.height;
2788 
2789                     // the current item is a rest with equal len? cool!
2790                     if (tmp->type==FloItem::REST && tmp->len==it->len && tmp->dots==it->dots)
2791                     {
2792                         // füge diese pause zur gruppe dazu und entferne sie von diesem set hier
2793                         // entfernen aber nur, wenn sie nicht it, also die erste pause ist, die brauchen wir noch!
2794                         if (heavyDebugMsg) cout << "\tgrouping rest at height="<<tmp->pos.height<<endl;
2795                         height_cumulative+=tmp->pos.height;
2796                         counter++;
2797                         if (tmp!=it)
2798                             curr_items.erase(tmp++);
2799                         else
2800                             tmp++;
2801                     }
2802                     else //it's something else? well, we can stop grouping that rest then
2803                     {
2804                         if (heavyDebugMsg) cout << "we can NOT, because that item is not a rest" << endl;
2805                         //stop grouping that rest
2806                         goto get_out_here;
2807                     }
2808                 }
2809                 if (heavyDebugMsg) cout << "no items to proceed on left, continuing" << endl;
2810                 get_out_here:
2811 
2812                 n_groups++;
2813 
2814                 // entferne it vom set und
2815                 // füge eine pause mit dem "mittelwert" ein.
2816                 // occupied und die "_END"-events bleiben unberührt
2817 
2818                 FloItem temp=*it;
2819                 temp.already_grouped=true;
2820 
2821                 // have we grouped all available rests into one single?
2822                 if ( (n_groups==1) && (tmp==curr_items.end()) && !dont_group)
2823                 {
2824                     if (heavyDebugMsg) cout << "wow, we were able to group all rests into one single" << endl;
2825                     if (temp.len==0) //the whole rest is shifted one line (one space and one line)
2826                         temp.pos.height=DEFAULT_REST_HEIGHT+2;
2827                     else
2828                         temp.pos.height=DEFAULT_REST_HEIGHT;
2829                 }
2830                 else
2831                 {
2832                     if (heavyDebugMsg) cout << "creating group #"<<n_groups<<endl;
2833                     temp.pos.height=nearbyint((float)height_cumulative/counter);
2834                 }
2835 
2836                 // do NOT first insert, then erase, because if temp.height ==
2837                 // it->height, the set considers temp and it equal (it doesn't
2838                 // take already_grouped into account)
2839                 // the result of this: insert does nothing, and erase erases
2840                 // the item. effect: you don't have the rest at all
2841                 curr_items.erase(it++);
2842 
2843                 if (heavyDebugMsg) cout << "replacing all grouped rests with a rest at height="<<temp.pos.height<<endl;
2844 
2845                 curr_items.insert(temp);
2846             }
2847             else
2848             {
2849                 if (it->type==FloItem::NOTE)
2850                     dont_group=true;
2851 
2852                 it++;
2853             }
2854         }
2855 
2856 
2857 
2858 
2859 
2860         // phase 2: avoid collisions of items ------------------------------
2861         set<FloItem, floComp>::iterator lastit, groupbegin, invalid;
2862         invalid=curr_items.end();
2863         lastit=invalid;
2864         groupbegin=invalid;
2865   int count = 0;
2866 
2867         //TODO: is "grouping" notes and rests together okay?
2868         //      or is it better to ignore rests when grouping?
2869         for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) {
2870             if ( (it->type==FloItem::NOTE) || (it->type==FloItem::REST) )
2871             {
2872                 if (lastit != invalid)
2873                 {
2874                     if (it->pos.height == lastit->pos.height+1) // they would collide?
2875                     {
2876                         if (groupbegin==invalid) // we have no group atm?
2877                         {
2878                             groupbegin=lastit;     // start a new group
2879                             count=1; // because lastit has to be taken into account.
2880                                      // for "it", there's a count++ later
2881                         }
2882 
2883                         // the following will work even on start-new-group,
2884                         // because lastit will be "untouched", and that's why
2885                         // still be initialized to "false"
2886                         it->ausweich=!lastit->ausweich;
2887 
2888                         count++;
2889                     }
2890                     else
2891                     {
2892                         if (groupbegin!=invalid) //this is the first item which
2893                         {												 //doesn't belong to the previous group any more
2894                             if (count%2 == 0) //count is even?
2895                                 if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT)
2896                                     for (set<FloItem, floComp>::iterator tmp=groupbegin; tmp!=it; tmp++)
2897                                         tmp->ausweich=!tmp->ausweich;
2898 
2899                             groupbegin=invalid;
2900                         }
2901                         // else: everything is ok :)
2902                     }
2903                 }
2904 
2905                 lastit=it;
2906             }
2907         }
2908 
2909             // this could be the case if the last processed item before end()
2910             // still belonged to a group. finalize this last group as well:
2911             if (groupbegin!=invalid)
2912             {
2913                 if (count%2 == 0) //count is even?
2914                     if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT)
2915                         for (set<FloItem, floComp>::iterator tmp=groupbegin; tmp!=curr_items.end(); tmp++)
2916                             tmp->ausweich=!tmp->ausweich;
2917             }
2918             // else: everything is ok :)
2919 
2920 
2921 
2922 
2923 
2924         // phase 3: group notes by their length and ------------------------
2925         //          find out appropriate stem directions
2926 group_them_again:
2927         map<int, cumulative_t> lengths;
2928         bool has_whole=false;
2929 
2930         // find out which note lengths are present at that time
2931         for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
2932             if (it->type==FloItem::NOTE)
2933                 lengths[it->len].add(it->pos.height);
2934 
2935         if (heavyDebugMsg)
2936         {
2937             cout << "note lengths at that time are:";
2938             for (map<int, cumulative_t>::iterator it=lengths.begin(); it!=lengths.end(); it++)
2939                 cout << it->first << "("<< it->second.mean() <<")  ";
2940             cout << endl;
2941         }
2942 
2943         if (lengths.erase(0)) // in case "0" is in the set, erase it
2944             has_whole=true;     // but remember there were whole notes
2945 
2946         if (lengths.size()==0)
2947         {
2948             if (heavyDebugMsg) cout << "no notes other than wholes, or no notes at all. we can relax" << endl;
2949         }
2950         else if (lengths.size()==1)
2951         {
2952             pair<const int, cumulative_t>& group=*(lengths.begin());
2953             stem_t stem;
2954             int shift=0;
2955             if (heavyDebugMsg) cout << "only one non-whole note group (len="<<group.first<<") at height="<<group.second.mean()<< endl;
2956 
2957             if (group.second.mean()>=6)
2958             {
2959                 stem=DOWNWARDS;
2960                 if (has_whole)
2961                     shift=-1;
2962             }
2963             else
2964             {
2965                 stem=UPWARDS;
2966                 if (has_whole)
2967                     shift=1;
2968             }
2969 
2970             // for each note in that group
2971             for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
2972                 if ( (it->type==FloItem::NOTE) && (it->len==group.first) )
2973                 {
2974                     it->stem=stem;
2975                     it->shift=shift;
2976                 }
2977         }
2978         else if (lengths.size()==2)
2979         {
2980             map<int, cumulative_t>::iterator it=lengths.begin();
2981             pair<const int, cumulative_t>& group1=*it;
2982             it++;
2983             pair<const int, cumulative_t>& group2=*it;
2984             stem_t stem1, stem2;
2985             int shift1=0, shift2=0;
2986             if (heavyDebugMsg) cout << "two non-whole note group: len="<<group1.first<<" at height="<<group1.second.mean()<<"  and len="<<group2.first<<" at height="<<group2.second.mean()<< endl;
2987 
2988             if (group1.second.mean()<group2.second.mean())
2989             {
2990                 stem1=DOWNWARDS;
2991                 stem2=UPWARDS;
2992                 shift1=-1;
2993                 if (has_whole)
2994                     shift2=1;
2995             }
2996             else
2997             {
2998                 stem1=UPWARDS;
2999                 stem2=DOWNWARDS;
3000                 shift2=-1;
3001                 if (has_whole)
3002                     shift1=1;
3003             }
3004 
3005             // for each note in group1
3006             for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
3007                 if ( (it->type==FloItem::NOTE) && (it->len==group1.first) )
3008                 {
3009                     it->stem=stem1;
3010                     it->shift=shift1;
3011                 }
3012 
3013             // for each note in group2
3014             for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
3015                 if ( (it->type==FloItem::NOTE) && (it->len==group2.first) )
3016                 {
3017                     it->stem=stem2;
3018                     it->shift=shift2;
3019                 }
3020         }
3021         else //more than 2 groups
3022         {
3023             //at this time, there are no iterators pointing to curr_items.
3024             //this means, we can erase and insert safely into curr_items here.
3025 
3026             //group1 contains the longer notes, group2 the shorter
3027 
3028             int group1_n=lengths.size()/2; //round down
3029             int group2_n=lengths.size()-group1_n;
3030 
3031             int group1_len, group2_len;
3032             int group1_len_ticks, group2_len_ticks;
3033 
3034 
3035             map<int, cumulative_t>::iterator lit=lengths.begin();
3036             for (int i=0;i<group1_n-1;i++) lit++; //go to the group1_n-th entry
3037             group1_len=lit->first;
3038             for (int i=0;i<group2_n;i++) lit++;  //go to the (group1_n+group2_n)-th entry (i.e., the last before end() )
3039             group2_len=lit->first;
3040 
3041             group1_len_ticks=calc_len(group1_len,0);
3042             group2_len_ticks=calc_len(group2_len,0);
3043 
3044             if (heavyDebugMsg) cout << "we have "<<lengths.size()<<" groups. putting the "<<group1_n<<" longest and the "<<group2_n<<"shortest groups together"<<endl <<
3045                                        "\tgroup1 will have len="<<group1_len<<" ("<<group1_len_ticks<<" ticks), group2 will have len="<<group2_len<<" ("<<group2_len_ticks<<" ticks)"<<endl;
3046 
3047             for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end();)
3048                 if (it->type==FloItem::NOTE)
3049                 {
3050                     //if *it belongs to group1 and has not already its destination length
3051                     if (heavyDebugMsg) cout << "\tprocessing note-item with len="<<it->len<<endl;
3052                     if (it->len<group1_len)
3053                     {
3054                         if (heavyDebugMsg) cout << "\t\thas to be changed to fit into group 1" << endl;
3055                         FloItem tmp=*it;
3056                         curr_items.erase(it++);
3057 
3058                         int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group1_len_ticks;
3059                         bool tied_note=tmp.tied;
3060 
3061 
3062                         //shorten the current item to it's group's length
3063                         tmp.len=group1_len;
3064                         tmp.dots=0;
3065                         tmp.tied=true;
3066                         curr_items.insert(tmp);
3067 
3068                         //create items for the remaining lengths (and a note_END for the just created shortened note)
3069                         int t=it2->first+group1_len_ticks;
3070 
3071                         itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) );
3072 
3073                         list<note_len_t> lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true);
3074                         unsigned tmppos=t;
3075                         int n_lens=lens.size();
3076                         int count=0;
3077                         for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
3078                         {
3079                             if (heavyDebugMsg) cout << "\t\twhile regrouping: partial note with len="<<x->len<<", dots="<<x->dots<<endl;
3080                             count++;
3081 
3082                             bool tie;
3083 
3084                             if (count<n_lens)
3085                                 tie=true;      // all notes except the last are always tied
3086                             else
3087                                 tie=tied_note; // only the last respects tied_note
3088 
3089                             itemlist[tmppos].insert( FloItem(FloItem::NOTE, tmp.pos,x->len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) );
3090                             tmppos+=calc_len(x->len,x->dots);
3091                             itemlist[tmppos].insert( FloItem(FloItem::NOTE_END, tmp.pos,0,0) );
3092                         }
3093 
3094                     }
3095                     //else if *it belongs to group2 and has not already its destination length
3096                     else if ((it->len<group2_len) && (it->len>group1_len))
3097                     {
3098                         if (heavyDebugMsg) cout << "\t\thas to be changed to fit into group 2" << endl;
3099 
3100                         FloItem tmp=*it;
3101                         curr_items.erase(it++);
3102 
3103                         int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group2_len_ticks;
3104                         bool tied_note=tmp.tied;
3105 
3106 
3107                         //shorten the current item to it's group's length
3108                         tmp.len=group2_len;
3109                         tmp.dots=0;
3110                         tmp.tied=true;
3111                         curr_items.insert(tmp);
3112 
3113                         //create items for the remaining lengths (and a note_END for the just created shortened note)
3114                         int t=it2->first+group2_len_ticks;
3115 
3116                         itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) );
3117 
3118                         list<note_len_t> lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true);
3119                         unsigned tmppos=t;
3120                         int n_lens=lens.size();
3121                         int count=0;
3122                         for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
3123                         {
3124                             if (heavyDebugMsg) cout << "\t\twhile regrouping: partial note with len="<<x->len<<", dots="<<x->dots<<endl;
3125                             count++;
3126 
3127                             bool tie;
3128 
3129                             if (count<n_lens)
3130                                 tie=true;      // all notes except the last are always tied
3131                             else
3132                                 tie=tied_note; // only the last respects tied_note
3133 
3134                             itemlist[tmppos].insert( FloItem(FloItem::NOTE,tmp.pos,x->len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) );
3135                             tmppos+=calc_len(x->len,x->dots);
3136                             itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) );
3137                         }
3138 
3139                     }
3140                     else //nothing to do?
3141                     {
3142                         if (heavyDebugMsg) cout << "\t\tnothing to do" << endl;
3143                         it++;
3144                     }
3145                 }
3146                 else
3147                     it++;
3148 
3149             goto group_them_again; //do it again
3150         }
3151 
3152     }
3153 }
3154 
3155 //draw a pixmap centered
draw_pixmap(QPainter & p,int x,int y,const QPixmap & pm)3156 void ScoreCanvas::draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm)
3157 {
3158     if (heavyDebugMsg) cout << "drawing pixmap with size="<<pm.width()<<"/"<<pm.height()<<" at "<<x<<"/"<<y<<endl;
3159     p.drawPixmap(x-pm.width()/2,y-pm.height()/2,pm);
3160 }
3161 
bbox_center(int x,int y,const QSize & size)3162 QRect bbox_center(int x, int y, const QSize& size)
3163 {
3164     //why x-foo/2+foo? because due to integer divisions,
3165     // x-foo/2+foo can be smaller than x+foo/2!
3166     return QRect(x-size.width()/2,y-size.height()/2,size.width(),size.height());
3167 }
3168 
bbox() const3169 QRect FloItem::bbox() const
3170 {
3171     return bbox_center(x,y,pix->size());
3172 }
3173 
draw_note_lines(QPainter & p,int y,bool reserve_akkolade_space)3174 void ScoreCanvas::draw_note_lines(QPainter& p, int y, bool reserve_akkolade_space)
3175 {
3176     int xbegin = reserve_akkolade_space ? AKKOLADE_LEFTMARGIN+AKKOLADE_WIDTH+AKKOLADE_RIGHTMARGIN : 0;
3177     int xend=width();
3178     // FINDMICHJETZT y is uninitialized!
3179 
3180     p.setPen(mycolors[DEFAULT]);
3181 
3182     for (int i=0;i<5;i++)
3183         p.drawLine(xbegin, y + i*YLEN - 2*YLEN, xend, y + i*YLEN - 2*YLEN);
3184 }
3185 
3186 
calc_item_pos()3187 void staff_t::calc_item_pos()
3188 {
3189     //this has to be KEY_C or KEY_C_B and nothing else,
3190     //because only with these two keys the next (initial)
3191     //key signature is properly drawn.
3192     MusECore::key_enum curr_key=MusECore::KEY_C;
3193 
3194     int pos_add=0;
3195 
3196     max_y_coord=0;
3197     min_y_coord=0;
3198 
3199     for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++)
3200     {
3201         for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++)
3202         {
3203             it->x=it2->first * parent->pixels_per_whole()/TICKS_PER_WHOLE  +pos_add;
3204             //if this changes, also change the line(s) with YLEN (but not all). don't change it.
3205             it->y=2*YLEN  -  (it->pos.height-2)*YLEN/2;
3206 
3207             if (it->type==FloItem::NOTE)
3208             {
3209                 if (it->y > max_y_coord) max_y_coord=it->y;
3210                 if (it->y < min_y_coord) min_y_coord=it->y;
3211 
3212                 it->x+=parent->note_x_indent() + it->shift*NOTE_SHIFT;
3213 
3214                 switch (it->len)
3215                 {
3216                     case 0: it->pix=pix_whole; break;
3217                     case 1: it->pix=pix_half; break;
3218                     default: it->pix=pix_quarter; break;
3219                 }
3220 
3221                 it->stem_x=it->x;
3222 
3223                 if (it->ausweich)
3224                 {
3225                     if ((it->stem==UPWARDS) || (it->len==0))
3226                         it->x += it->pix->width()-1; //AUSWEICH_X
3227                     else
3228                         it->x -= it->pix->width()-1; //AUSWEICH_X
3229                 }
3230 
3231                 //if there's a tie, try to find the tie's destination and set is_tie_dest
3232                 if (it->tied)
3233                 {
3234                     set<FloItem, floComp>::iterator dest;
3235                     set<FloItem, floComp>& desttime = itemlist[it2->first+calc_len(it->len,it->dots)];
3236                     for (dest=desttime.begin(); dest!=desttime.end();dest++)
3237                         if ((dest->type==FloItem::NOTE) && (dest->pos==it->pos))
3238                         {
3239                             dest->is_tie_dest=true;
3240                             dest->tie_from_x=it->x;
3241                             break;
3242                         }
3243 
3244                     if (dest==desttime.end())
3245                         cerr << "ERROR: THIS SHOULD NEVER HAPPEN: did not find destination note for tie!" << endl;
3246                 }
3247             }
3248             else if (it->type==FloItem::REST)
3249             {
3250                 switch (it->len)
3251                 {
3252                     case 0: it->pix=pix_r1; break;
3253                     case 1: it->pix=pix_r2; break;
3254                     case 2: it->pix=pix_r4; break;
3255                     case 3: it->pix=pix_r8; break;
3256                     case 4: it->pix=pix_r16; break;
3257                     case 5: it->pix=pix_r32; break;
3258                 }
3259 
3260                 it->x+=parent->note_x_indent() + (it->ausweich ? REST_AUSWEICH_X : 0); //AUSWEICH_X
3261             }
3262             else if (it->type==FloItem::BAR)
3263             {
3264                 //nothing to do :)
3265             }
3266             else if (it->type==FloItem::TIME_SIG)
3267             {
3268                 int add=calc_timesig_width(it->num, it->denom);
3269                 pos_add+=add;
3270             }
3271             else if (it->type==FloItem::KEY_CHANGE)
3272             {
3273                 MusECore::key_enum new_key=it->key;
3274 
3275                 list<int> aufloes_list=calc_accidentials(curr_key, clef, new_key);
3276                 list<int> new_acc_list=calc_accidentials(new_key, clef);
3277 
3278                 int n_acc_drawn=aufloes_list.size() + new_acc_list.size();
3279                 pos_add+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST;
3280 
3281                 curr_key=new_key;
3282             }
3283         }
3284     }
3285 
3286     max_y_coord+= (pix_quarter->height()/2 +NOTE_YDIST/2);
3287     min_y_coord-= (pix_quarter->height()/2 +NOTE_YDIST/2);
3288 }
3289 
calc_pos_add_list()3290 void ScoreCanvas::calc_pos_add_list()
3291 {
3292     using MusEGlobal::sigmap;
3293     using MusECore::iSigEvent;
3294 
3295 
3296     pos_add_list.clear();
3297 
3298     //process time signatures
3299     for (iSigEvent it=MusEGlobal::sigmap.begin(); it!=MusEGlobal::sigmap.end(); it++)
3300         pos_add_list[it->second->tick]+=calc_timesig_width(it->second->sig.z, it->second->sig.n);
3301 
3302 
3303     //process key changes
3304 
3305     //this has to be KEY_C or KEY_C_B and nothing else,
3306     //because only with these two keys the next (initial)
3307     //key signature is properly calculated.
3308     MusECore::key_enum curr_key=MusECore::KEY_C;
3309 
3310 
3311     for (MusECore::iKeyEvent it=MusEGlobal::keymap.begin(); it!=MusEGlobal::keymap.end(); it++)
3312     {
3313         MusECore::key_enum new_key=it->second.key;
3314         list<int> aufloes_list=calc_accidentials(curr_key, VIOLIN, new_key); //clef argument is unnecessary
3315         list<int> new_acc_list=calc_accidentials(new_key, VIOLIN);           //in this case
3316         int n_acc_drawn=aufloes_list.size() + new_acc_list.size();
3317         pos_add_list[it->second.tick]+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST;
3318 
3319         curr_key=new_key;
3320     }
3321 
3322     emit pos_add_changed();
3323 }
3324 
draw_items(QPainter & p,int y,staff_t & staff,int x1,int x2)3325 void ScoreCanvas::draw_items(QPainter& p, int y, staff_t& staff, int x1, int x2)
3326 {
3327     int from_tick, to_tick;
3328     ScoreItemList::iterator from_it, to_it;
3329 
3330     //drawing too much isn't bad. drawing too few is.
3331 
3332     from_tick=x_to_tick(x1);
3333     from_it=staff.itemlist.lower_bound(from_tick);
3334     //from_it now contains the first time which is fully drawn
3335     //however, the previous beat could still be relevant, when it's
3336     //partly drawn. so we decrement from_it
3337     if (from_it!=staff.itemlist.begin()) from_it--;
3338 
3339     //decrement until we're at a time with a bar
3340     //otherwise, drawing accidentials will be broken
3341     while (from_it!=staff.itemlist.begin() && from_it->second.find(FloItem(FloItem::BAR))==from_it->second.end())
3342         from_it--;
3343 
3344 
3345     to_tick=x_to_tick(x2);
3346     to_it=staff.itemlist.upper_bound(to_tick);
3347     //to_it now contains the first time which is not drawn at all any more
3348     //however, a tie from 1:04 to 2:01 is stored in 2:01, not in 1:04,
3349     //so for drawing ties, we need to increment to_it, so that the
3350     //"first time not drawn at all any more" is the last which gets
3351     //actually drawn.
3352     if (to_it!=staff.itemlist.end()) to_it++; //do one tick more than necessary. this will draw ties
3353 
3354     draw_items(p,y, staff, from_it, to_it);
3355 }
3356 
draw_items(QPainter & p,int y,staff_t & staff)3357 void ScoreCanvas::draw_items(QPainter& p, int y, staff_t& staff)
3358 {
3359     draw_items(p,y, staff,x_pos,x_pos+width()-x_left);
3360 }
3361 
draw_items(QPainter & p,int y_offset,staff_t & staff,ScoreItemList::iterator from_it,ScoreItemList::iterator to_it)3362 void ScoreCanvas::draw_items(QPainter& p, int y_offset, staff_t& staff, ScoreItemList::iterator from_it, ScoreItemList::iterator to_it)
3363 {
3364     // init accidentials properly
3365     vorzeichen_t curr_accidential[7];
3366     vorzeichen_t default_accidential[7];
3367     MusECore::key_enum curr_key;
3368 
3369     curr_key=key_at_tick(from_it->first).key;
3370     list<int> new_acc_list=calc_accidentials(curr_key, staff.clef);
3371     vorzeichen_t new_accidential = is_sharp_key(curr_key) ? SHARP : B;
3372 
3373     for (int i=0;i<7;i++)
3374         curr_accidential[i]=default_accidential[i]=NONE;
3375 
3376     for (list<int>::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++)
3377         default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential;
3378 
3379 
3380 
3381     for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++)
3382     {
3383         if (heavyDebugMsg) cout << "at t="<<it2->first << endl;
3384 
3385         int upstem_y1 = -1, upstem_y2=-1, upstem_x=-1, upflag=-1;
3386         int downstem_y1 = -1, downstem_y2=-1, downstem_x=-1, downflag=-1;
3387 
3388         for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++)
3389         {
3390             if (it->type==FloItem::NOTE)
3391             {
3392                 if (heavyDebugMsg)
3393                 {
3394                     cout << "\tNOTE at line"<<it->pos.height<<" with acc.="<<it->pos.vorzeichen<<", len="<<pow(2,it->len);
3395                     for (int i=0;i<it->dots;i++) cout << ".";
3396                     cout << " , stem=";
3397                     if (it->stem==UPWARDS)
3398                         cout << "UPWARDS";
3399                     else
3400                         cout << "DOWNWARDS";
3401 
3402                     cout << " , shift="<<it->shift<<", ausweich="<<it->ausweich<<", ";
3403                     if (!it->tied)	cout << "un";
3404                     cout << "tied, is_tie_dest="<<it->is_tie_dest<<endl;
3405                 }
3406 
3407                 if (it->len!=0) //only for non-whole notes the stems are relevant!
3408                 {
3409                     if (it->stem==UPWARDS)
3410                     {
3411                         if (upstem_y1 == -1)
3412                             upstem_y1=it->y;
3413 
3414                         upstem_y2=it->y;
3415 
3416 
3417                         if ((upflag!=-1) && (upflag!=it->len))
3418                             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: upflag != this->flag" << endl;
3419                         upflag=it->len;
3420 
3421                         if ((upstem_x!=-1) && (upstem_x!=it->stem_x ))
3422                             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: upstem_x != x_result" << endl;
3423                         upstem_x=it->stem_x;
3424                     }
3425                     else
3426                     {
3427                         if (downstem_y1 == -1)
3428                             downstem_y1=it->y;
3429 
3430                         downstem_y2=it->y;
3431 
3432 
3433                         if ((downflag!=-1) && (downflag!=it->len))
3434                             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: downflag != this->flag" << endl;
3435                         downflag=it->len;
3436 
3437                         if ((downstem_x!=-1) && (downstem_x!=it->stem_x))
3438                             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: downstem_x != x_result" << endl;
3439                         downstem_x=it->stem_x; //important: before the below calculation!
3440                     }
3441                 }
3442 
3443 
3444                 if (it->pos.height <= 0) //we need auxiliary lines on the bottom?
3445                 {
3446                     p.setPen(mycolors[DEFAULT]);
3447                     for (int i=0; i>=it->pos.height; i-=2)
3448                         p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN  -  (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN  -  (i-2)*YLEN/2);
3449                 }
3450                 else if (it->pos.height >= 12) //we need auxiliary lines on the top?
3451                 {
3452                     p.setPen(mycolors[DEFAULT]);
3453                     for (int i=12; i<=it->pos.height; i+=2)
3454                         p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN  -  (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN  -  (i-2)*YLEN/2);
3455                 }
3456 
3457                 it->is_active= ( (MusEGlobal::song->cpos() >= it->source_event->tick() + it->source_part->tick()) &&
3458                                        (MusEGlobal::song->cpos() < it->source_event->endTick() + it->source_part->tick()) );
3459 
3460 
3461                 int color_index;
3462                 switch (coloring_mode)
3463                 {
3464                     case COLOR_MODE_BLACK:
3465                         color_index=DEFAULT;
3466                         break;
3467 
3468                     case COLOR_MODE_PART:
3469                         color_index=it->source_part->colorIndex();
3470                         break;
3471 
3472                     case COLOR_MODE_VELO:
3473                         color_index=VELO_PIXMAP_BEGIN + it->source_event->velo();
3474                         break;
3475 
3476                     default:
3477                         cerr << "ERROR: THIS CANNOT HAPPEN: coloring_mode (="<<coloring_mode<<") is invalid! defaulting to black." << endl;
3478                         color_index=DEFAULT;
3479                 }
3480 
3481                 if (it->source_event->selected())
3482                     color_index=SELECTED_PIXMAP;
3483 
3484                 if (MusEGlobal::audio->isPlaying() && it->is_active)
3485                     color_index=HIGHLIGHTED_PIXMAP;
3486 
3487 
3488                 draw_pixmap(p,it->x -x_pos+x_left,y_offset + it->y,it->pix[color_index]);
3489 
3490                 //draw dots
3491 
3492                 int x_dot=DOT_XBEGIN;
3493                 int y_dot;
3494                 if (modulo(it->pos.height, 2) == 0) //note is on a line?
3495                     y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space
3496                 else //note is between two lines?
3497                     y_dot=YLEN * 0.1;
3498 
3499                 if (it->stem==DOWNWARDS)
3500                     y_dot=-y_dot;
3501                 //else y_dot=y_dot;
3502 
3503                 for (int i=0;i<it->dots;i++)
3504                 {
3505                     draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[color_index]);
3506                     x_dot+=DOT_XDIST;
3507                 }
3508 
3509 
3510 
3511                 //draw accidentials
3512                 if (it->pos.vorzeichen != curr_accidential[modulo(it->pos.height,7)])
3513                 {
3514                     QPixmap* acc_pix;
3515                     switch (it->pos.vorzeichen)
3516                     {
3517                         case NONE: acc_pix=pix_noacc; break;
3518                         case SHARP: acc_pix=pix_sharp; break;
3519                         case B: acc_pix=pix_b; break;
3520                         default: cerr << "ERROR: THIS CANNOT HAPPEN: it->pos.vorzeichen (="<<it->pos.vorzeichen<<") is invalid! defaulting to NONE." << endl;
3521                                  acc_pix=pix_noacc; break;
3522                     }
3523                     draw_pixmap(p,it->x-ACCIDENTIAL_DIST -x_pos+x_left,y_offset + it->y, acc_pix[color_index]);
3524 
3525                     curr_accidential[modulo(it->pos.height,7)]=it->pos.vorzeichen;
3526                 }
3527 
3528 
3529                 //if needed, draw tie
3530                 if (it->is_tie_dest)
3531                 {
3532                     if (heavyDebugMsg) cout << "drawing tie" << endl;
3533                     draw_tie(p,it->tie_from_x-x_pos+x_left,it->x -x_pos+x_left,y_offset + it->y, (it->len==0) ? true : (it->stem==DOWNWARDS) , mycolors[DEFAULT]);
3534                     // in english: "if it's a whole note, tie is upwards (true). if not, tie is upwards if
3535                     //              stem is downwards and vice versa"
3536                 }
3537             }
3538             else if (it->type==FloItem::REST)
3539             {
3540                 if (heavyDebugMsg)
3541                 {
3542                     cout << "\tREST at line"<<it->pos.height<<" with len="<<pow(2,it->len);
3543                     for (int i=0;i<it->dots;i++) cout << ".";
3544                     cout << " , ausweich="<<it->ausweich<<endl;
3545                 }
3546 
3547                 draw_pixmap(p,it->x -x_pos+x_left,y_offset + it->y,*it->pix);
3548 
3549 
3550                 //draw dots
3551 
3552                 int x_dot=DOT_XBEGIN_REST;
3553                 int y_dot;
3554                 if (modulo(it->pos.height, 2) == 0) //rest is on a line?
3555                     y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space
3556                 else //note is between two lines?
3557                     y_dot=YLEN * 0.1;
3558 
3559                 if (it->len!=0) // all rests except the whole are treated as
3560                     y_dot=-y_dot; // if they had a downwards stem
3561 
3562                 for (int i=0;i<it->dots;i++)
3563                 {
3564                     draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[DEFAULT]);
3565                     x_dot+=DOT_XDIST;
3566                 }
3567             }
3568             else if (it->type==FloItem::BAR)
3569             {
3570                 if (heavyDebugMsg) cout << "\tBAR" << endl;
3571 
3572                 p.setPen(mycolors[DEFAULT]);
3573                 p.drawLine(it->x -x_pos+x_left,y_offset  -2*YLEN,it->x -x_pos+x_left,y_offset +2*YLEN);
3574 
3575                 for (int i=0;i<7;i++)
3576                     curr_accidential[i]=default_accidential[i];
3577             }
3578             else if (it->type==FloItem::TIME_SIG)
3579             {
3580                 if (heavyDebugMsg) cout << "\tTIME SIGNATURE: "<<it->num<<"/"<<it->denom<<endl;
3581 
3582                 draw_timesig(p,  it->x - x_pos+x_left, y_offset, it->num, it->denom);
3583             }
3584             else if (it->type==FloItem::KEY_CHANGE)
3585             {
3586                 MusECore::key_enum new_key=it->key;
3587                 if (heavyDebugMsg) cout << "\tKEY CHANGE: from "<<curr_key<<" to "<<new_key<<endl;
3588 
3589                 const bool is_minor = it->minor;
3590                 const int str_y_coord = -4 * YLEN + 2 /* margin */;
3591                 const QString kstr = MusECore::KeyEvent::keyToString(new_key, is_minor);
3592                 const int x_1 = -x_pos + x_left;
3593                 const int kstrx = x_1 + it->x + KEYCHANGE_ACC_LEFTDIST;
3594                 // Don't bother drawing the string if it would begin just before the left edge
3595                 //  because the preamble will already change to show the new key.
3596                 if(!preamble_contains_timesig || kstrx >= x_1)
3597                   p.drawText(kstrx, y_offset + str_y_coord, kstr);
3598 
3599                 list<int> aufloes_list=calc_accidentials(curr_key, staff.clef, new_key);
3600                 list<int> new_acc_list=calc_accidentials(new_key, staff.clef);
3601 
3602                 // cancel accidentials from curr_key
3603                 draw_accidentials(p, it->x + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, aufloes_list, pix_noacc[DEFAULT]);
3604 
3605                 // draw all accidentials from new_key
3606                 QPixmap* pix = is_sharp_key(new_key) ? &pix_sharp[DEFAULT] : &pix_b[DEFAULT];
3607                 vorzeichen_t new_accidential = is_sharp_key(new_key) ? SHARP : B;
3608 
3609                 draw_accidentials(p, it->x + aufloes_list.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, new_acc_list, *pix);
3610 
3611                 for (int i=0;i<7;i++)
3612                     curr_accidential[i]=default_accidential[i]=NONE;
3613 
3614                 for (list<int>::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++)
3615                     default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential;
3616 
3617                 curr_key=new_key;
3618             }
3619         }
3620 
3621         p.setPen(mycolors[DEFAULT]);
3622         //note: y1 is bottom, y2 is top!
3623         if (upstem_x!=-1)
3624         {
3625             upstem_x=upstem_x-pix_quarter[0].width()/2 +pix_quarter[0].width() -1;
3626             p.drawLine(upstem_x -x_pos+x_left, y_offset + upstem_y1, upstem_x -x_pos+x_left, y_offset + upstem_y2-STEM_LEN);
3627 
3628             if (upflag>=3) //if the note needs a flag
3629                 p.drawPixmap(upstem_x -x_pos+x_left,y_offset + upstem_y2-STEM_LEN,pix_flag_up[upflag-3]);
3630         }
3631         if (downstem_x!=-1)
3632         {
3633             downstem_x=downstem_x-pix_quarter[0].width()/2;
3634             p.drawLine(downstem_x -x_pos+x_left, y_offset + downstem_y1+STEM_LEN, downstem_x -x_pos+x_left, y_offset + downstem_y2);
3635 
3636             if (downflag>=3) //if the note needs a flag
3637                 p.drawPixmap(downstem_x -x_pos+x_left,y_offset + downstem_y1+STEM_LEN-pix_flag_down[downflag-3].height(),pix_flag_down[downflag-3]);
3638         }
3639     }
3640 }
3641 
need_redraw_for_hilighting()3642 bool ScoreCanvas::need_redraw_for_hilighting()
3643 {
3644     for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
3645         if (need_redraw_for_hilighting(it->itemlist)) return true;
3646 
3647     return false;
3648 }
3649 
need_redraw_for_hilighting(ScoreItemList & itemlist)3650 bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist)
3651 {
3652     return need_redraw_for_hilighting(itemlist, x_pos,x_pos+width()-x_left);
3653 }
3654 
need_redraw_for_hilighting(ScoreItemList & itemlist,int x1,int x2)3655 bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist, int x1, int x2)
3656 {
3657     int from_tick, to_tick;
3658     ScoreItemList::iterator from_it, to_it;
3659 
3660     from_tick=x_to_tick(x1);
3661     from_it=itemlist.lower_bound(from_tick);
3662     //from_it now contains the first time which is fully drawn
3663     //however, the previous beat could still be relevant, when it's
3664     //partly drawn. so we decrement from_it
3665     if (from_it!=itemlist.begin()) from_it--;
3666 
3667     to_tick=x_to_tick(x2);
3668     to_it=itemlist.upper_bound(to_tick);
3669     //to_it now contains the first time which is not drawn at all any more
3670 
3671     return need_redraw_for_hilighting(from_it, to_it);
3672 }
3673 
need_redraw_for_hilighting(ScoreItemList::iterator from_it,ScoreItemList::iterator to_it)3674 bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList::iterator from_it, ScoreItemList::iterator to_it)
3675 {
3676     //if we aren't playing, there will never be a need for redrawing due to highlighting things
3677     if (MusEGlobal::audio->isPlaying()==false)
3678         return false;
3679 
3680     for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++)
3681         for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++)
3682             if (it->type==FloItem::NOTE)
3683             {
3684                 bool is_active= ( (MusEGlobal::song->cpos() >= it->source_event->tick() + it->source_part->tick()) &&
3685                                   (MusEGlobal::song->cpos() < it->source_event->endTick() + it->source_part->tick()) );
3686                 if (it->is_active != is_active)
3687                     return true;
3688             }
3689 
3690     return false;
3691 }
3692 
clef_height(clef_t clef)3693 int clef_height(clef_t clef)
3694 {
3695     switch (clef) //CLEF_MARKER
3696     {
3697         case VIOLIN: return 4;
3698         case BASS: return 8;
3699         default:
3700             cerr << "ERROR: ILLEGAL FUNCTION CALL in clef_height()" << endl;
3701             return 6;
3702     }
3703 }
3704 
3705 #define TIMESIG_LEFTMARGIN 5
3706 #define TIMESIG_RIGHTMARGIN 5
3707 #define DIGIT_YDIST 9
3708 #define DIGIT_WIDTH 12
3709 
3710 #define CLEF_LEFTMARGIN 5
3711 #define CLEF_RIGHTMARGIN 5
3712 
draw_preamble(QPainter & p,int y_offset,clef_t clef,bool reserve_akkolade_space,bool with_akkolade)3713 void ScoreCanvas::draw_preamble(QPainter& p, int y_offset, clef_t clef, bool reserve_akkolade_space, bool with_akkolade)
3714 {
3715     int x_left_old=x_left;
3716     int tick=x_to_tick(x_pos);
3717 
3718     // maybe draw akkolade ----------------------------------------------
3719     if (reserve_akkolade_space)
3720     {
3721         if (with_akkolade) {
3722             p.setBrush(mycolors[DEFAULT]);
3723             draw_akkolade(p, AKKOLADE_LEFTMARGIN, y_offset+GRANDSTAFF_DISTANCE/2);
3724         }
3725 
3726         x_left= AKKOLADE_LEFTMARGIN + AKKOLADE_WIDTH + AKKOLADE_RIGHTMARGIN;
3727     }
3728     else
3729         x_left=0;
3730 
3731 
3732     // draw clef --------------------------------------------------------
3733     QPixmap* pix_clef= (clef==BASS) ? pix_clef_bass : pix_clef_violin;
3734     int y_coord=2*YLEN  -  ( clef_height(clef) -2)*YLEN/2;
3735 
3736     draw_pixmap(p,x_left + CLEF_LEFTMARGIN + pix_clef->width()/2,y_offset + y_coord,*pix_clef);
3737 
3738     x_left+= CLEF_LEFTMARGIN + pix_clef->width() + CLEF_RIGHTMARGIN;
3739 
3740 
3741     // draw accidentials ------------------------------------------------
3742     if (preamble_contains_keysig)
3743     {
3744         MusECore::KeyEvent key=key_at_tick(tick);
3745         QPixmap* pix_acc=is_sharp_key(key.key) ? &pix_sharp[DEFAULT] : &pix_b[DEFAULT];
3746         list<int> acclist=calc_accidentials(key.key,clef);
3747 
3748         const int str_y_coord = -4 * YLEN + 2 /* margin */;
3749         const QString kstr = key.keyString();
3750 // Width() is obsolete. Qt >= 5.11 use horizontalAdvance().
3751 #if QT_VERSION >= 0x050b00
3752         const int ksw = fontMetrics().horizontalAdvance(kstr);
3753 #else
3754         const int ksw = fontMetrics().width(kstr);
3755 #endif
3756         int kstrx = x_left - ksw / 2;
3757         if(kstrx < 0)
3758           kstrx = 0;
3759         p.drawText(kstrx, y_offset + str_y_coord, kstr);
3760         x_left+=KEYCHANGE_ACC_LEFTDIST;
3761 
3762         draw_accidentials(p,x_left, y_offset, acclist ,*pix_acc);
3763 
3764         x_left+=acclist.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_RIGHTDIST;
3765     }
3766 
3767 
3768     // draw time signature ----------------------------------------------
3769     if (preamble_contains_timesig)
3770     {
3771         x_left+=TIMESIG_LEFTMARGIN;
3772 
3773         timesig_t timesig=timesig_at_tick(tick);
3774 
3775         draw_timesig(p, x_left, y_offset, timesig.num, timesig.denom);
3776 
3777         x_left+=calc_timesig_width(timesig.num, timesig.denom)+TIMESIG_RIGHTMARGIN;
3778     }
3779 
3780     // draw bar ---------------------------------------------------------
3781     p.setPen(mycolors[DEFAULT]);
3782     p.drawLine(x_left,y_offset  -2*YLEN,x_left,y_offset +2*YLEN);
3783 
3784 
3785     if (x_left_old!=x_left)
3786     {
3787         emit viewport_width_changed(viewport_width());
3788         emit preamble_width_changed(x_left);
3789     }
3790 }
3791 
3792 
draw_timesig(QPainter & p,int x,int y_offset,int num,int denom)3793 void ScoreCanvas::draw_timesig(QPainter& p, int x, int y_offset, int num, int denom)
3794 {
3795     int num_width=calc_number_width(num);
3796     int denom_width=calc_number_width(denom);
3797     int width=((num_width > denom_width) ? num_width : denom_width);
3798     int num_indent=(width-num_width)/2 + TIMESIG_LEFTMARGIN;
3799     int denom_indent=(width-denom_width)/2 + TIMESIG_LEFTMARGIN;
3800 
3801     draw_number(p, x+num_indent, y_offset -DIGIT_YDIST, num);
3802     draw_number(p, x+denom_indent, y_offset +DIGIT_YDIST, denom);
3803 }
3804 
calc_timesig_width(int num,int denom)3805 int calc_timesig_width(int num, int denom)
3806 {
3807     int num_width=calc_number_width(num);
3808     int denom_width=calc_number_width(denom);
3809     int width=((num_width > denom_width) ? num_width : denom_width);
3810     return width+TIMESIG_LEFTMARGIN+TIMESIG_RIGHTMARGIN;
3811 }
3812 
calc_number_width(int n)3813 int calc_number_width(int n)
3814 {
3815     string str=IntToStr(n);
3816     return (str.length()*DIGIT_WIDTH);
3817 }
3818 
draw_number(QPainter & p,int x,int y,int n)3819 void ScoreCanvas::draw_number(QPainter& p, int x, int y, int n)
3820 {
3821     string str=IntToStr(n);
3822     int curr_x=x+DIGIT_WIDTH/2;
3823 
3824     for (size_t i=0;i<str.length(); i++)
3825     {
3826         draw_pixmap(p, curr_x, y, pix_num[str[i]-'0']);
3827         curr_x+=DIGIT_WIDTH;
3828     }
3829 }
3830 
3831 
draw(QPainter & p,const QRect &,const QRegion &)3832 void ScoreCanvas::draw(QPainter& p, const QRect&, const QRegion&)
3833 {
3834     if (debugMsg) cout <<"now in ScoreCanvas::draw"<<endl;
3835 
3836 
3837     p.setPen(mycolors[DEFAULT]);
3838 
3839     bool reserve_akkolade_space=false;
3840     for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
3841         if (it->type==GRAND_TOP)
3842         {
3843             reserve_akkolade_space=true;
3844             break;
3845         }
3846 
3847     for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
3848     {
3849         //TODO: maybe only draw visible staves?
3850         draw_note_lines(p,it->y_draw - y_pos, reserve_akkolade_space);
3851         draw_preamble(p,it->y_draw - y_pos, it->clef, reserve_akkolade_space, (it->type==GRAND_TOP));
3852         p.setClipRect(x_left+1,0,p.device()->width(),p.device()->height());
3853         draw_items(p,it->y_draw - y_pos, *it);
3854         p.setClipping(false);
3855     }
3856 
3857     if (have_lasso)
3858     {
3859         p.setPen(Qt::blue);
3860         p.setBrush(Qt::NoBrush);
3861         p.drawRect(lasso);
3862     }
3863 
3864     if (debugMsg) cout << "drawing done." << endl;
3865 }
3866 
3867 
calc_accidentials(MusECore::key_enum key,clef_t clef,MusECore::key_enum next_key)3868 list<int> calc_accidentials(MusECore::key_enum key, clef_t clef, MusECore::key_enum next_key)
3869 {
3870     list<int> result;
3871 
3872     int violin_sharp_pos[]={10,7,11,8,5,9,6}; //CLEF_MARKER
3873     int violin_b_pos[]={6,9,5,8,4,7,3};
3874     int bass_sharp_pos[]={8,5,9,6,3,7,4};
3875     int bass_b_pos[]={4,7,3,6,2,5,1};
3876 
3877  int* accidential_pos = NULL;
3878 
3879     switch (clef)
3880     {
3881         case VIOLIN: accidential_pos = is_sharp_key(key) ? violin_sharp_pos : violin_b_pos; break;
3882         case BASS: accidential_pos = is_sharp_key(key) ? bass_sharp_pos : bass_b_pos; break;
3883     }
3884 
3885     int begin=0;
3886 
3887     if (is_sharp_key(key)==is_sharp_key(next_key)) //same kind of key (both b or both #)?
3888         begin=n_accidentials(next_key);
3889     else
3890         begin=0;
3891 
3892 
3893     int end=n_accidentials(key);
3894 
3895     for (int i=begin; i<end; i++)
3896         result.push_back(accidential_pos[i]);
3897 
3898     return result;
3899 }
3900 
3901 
3902 
3903 
tick_to_x(int t)3904 int ScoreCanvas::tick_to_x(int t)
3905 {
3906     int x=t*pixels_per_whole()/TICKS_PER_WHOLE;
3907 
3908     for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<=t; it++)
3909         x+=it->second;
3910 
3911     return x;
3912 }
3913 
delta_tick_to_delta_x(int t)3914 int ScoreCanvas::delta_tick_to_delta_x(int t)
3915 {
3916     return t*pixels_per_whole()/TICKS_PER_WHOLE;
3917 }
3918 
calc_posadd(int t)3919 int ScoreCanvas::calc_posadd(int t)
3920 {
3921     int result=0;
3922 
3923     for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<t; it++)
3924         result+=it->second;
3925 
3926     return result;
3927 }
3928 
3929 //doesn't round mathematically correct, but i don't think this
3930 //will be a problem, because a tick is pretty small
x_to_tick(int x)3931 int ScoreCanvas::x_to_tick(int x)
3932 {
3933     int t=TICKS_PER_WHOLE * x/pixels_per_whole();
3934     int min_t=0;
3935 
3936     for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<t; it++)
3937     {
3938         min_t=it->first;
3939         x-=it->second;
3940         t=TICKS_PER_WHOLE * x/pixels_per_whole();
3941     }
3942 
3943     return t > min_t ? t : min_t;
3944 }
3945 
key_at_tick(int t_)3946 MusECore::KeyEvent ScoreCanvas::key_at_tick(int t_)
3947 {
3948     unsigned int t= (t_>=0) ? t_ : 0;
3949 
3950     return MusEGlobal::keymap.keyAtTick(t);
3951 }
3952 
timesig_at_tick(int t_)3953 timesig_t ScoreCanvas::timesig_at_tick(int t_)
3954 {
3955     timesig_t tmp;
3956     unsigned int t= (t_>=0) ? t_ : 0;
3957 
3958     MusEGlobal::sigmap.timesig(t, tmp.num, tmp.denom);
3959 
3960     return tmp;
3961 }
3962 
height_to_pitch(int h,clef_t clef)3963 int ScoreCanvas::height_to_pitch(int h, clef_t clef)
3964 {
3965     int foo[]={0,2,4,5,7,9,11};
3966 
3967     switch(clef) //CLEF_MARKER
3968     {
3969         case VIOLIN:	return foo[modulo(h,7)] + ( divide_floor(h,7)*12 ) + 60;
3970         case BASS:		return foo[modulo((h-5),7)] + ( divide_floor(h-5,7)*12 ) + 48;
3971         default:
3972             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: unknown clef in height_to_pitch" << endl;
3973             return 60;
3974     }
3975 }
3976 
height_to_pitch(int h,clef_t clef,MusECore::key_enum key)3977 int ScoreCanvas::height_to_pitch(int h, clef_t clef, MusECore::key_enum key)
3978 {
3979     int add=0;
3980 
3981     list<int> accs=calc_accidentials(key,clef);
3982 
3983     for (list<int>::iterator it=accs.begin(); it!=accs.end(); it++)
3984     {
3985         if (modulo(*it,7) == modulo(h,7))
3986         {
3987             add=is_sharp_key(key) ? 1 : -1;
3988             break;
3989         }
3990     }
3991 
3992     return height_to_pitch(h,clef)+add;
3993 }
3994 
y_to_height(int y)3995 int ScoreCanvas::y_to_height(int y)
3996 {
3997     return int(nearbyint(float(2*YLEN  - y)*2.0/YLEN))+2 ;
3998 }
3999 
y_to_pitch(int y,int t,clef_t clef)4000 int ScoreCanvas::y_to_pitch(int y, int t, clef_t clef)
4001 {
4002     return height_to_pitch(y_to_height(y), clef, key_at_tick(t).key);
4003 }
4004 
4005 
4006 #define DRAG_INIT_DISTANCE 5
4007 
mousePressEvent(QMouseEvent * event)4008 void ScoreCanvas::mousePressEvent (QMouseEvent* event)
4009 {
4010     keystate=event->modifiers();
4011     bool ctrl=keystate & Qt::ControlModifier;
4012 
4013     // always round DOWN.
4014     // because the "area" of a beat goes from "beat_begin" to "nextbeat_begin-1",
4015     // but notes are drawn in the middle of that area!
4016 
4017     list<staff_t>::iterator staff_it=staff_at_y(event->y() + y_pos);
4018 
4019     int y=event->y() + y_pos - staff_it->y_draw;
4020     int x=event->x()+x_pos-x_left;
4021     int tick=flo_quantize_floor(x_to_tick(x), quant_ticks());
4022 
4023     if (staff_it!=staves.end())
4024     {
4025         if (event->x() <= x_left) //clicked in the preamble?
4026         {
4027             if (event->button() == Qt::RightButton) //right-click?
4028             {
4029                 current_staff=staff_it;
4030                 staff_menu->popup(event->globalPos());
4031             }
4032             else if (event->button() == Qt::MidButton) //middle click?
4033             {
4034                 remove_staff(staff_it);
4035             }
4036             else if (event->button() == Qt::LeftButton) //left click?
4037             {
4038                 current_staff=staff_it;
4039                 setCursor(Qt::SizeAllCursor);
4040                 dragging_staff=true;
4041             }
4042         }
4043         else
4044         {
4045             ScoreItemList& itemlist=staff_it->itemlist;
4046 
4047             if (debugMsg) cout << "mousePressEvent at "<<x<<"/"<<y<<"; tick="<<tick<<endl;
4048             set<FloItem, floComp>::iterator set_it;
4049             for (set_it=itemlist[tick].begin(); set_it!=itemlist[tick].end(); set_it++)
4050                 if (set_it->type==FloItem::NOTE)
4051                     if (set_it->bbox().contains(x,y))
4052                         break;
4053 
4054             if (set_it!=itemlist[tick].end()) //we found something?
4055             {
4056                 mouse_down_pos=event->pos();
4057                 mouse_operation=NO_OP;
4058 
4059                 int t=tick;
4060 
4061                 set<FloItem, floComp>::iterator found;
4062                 do
4063                 {
4064                     found=itemlist[t].find(FloItem(FloItem::NOTE, set_it->pos));
4065                     if (found == itemlist[t].end())
4066                     {
4067                         cerr << "ERROR: THIS SHOULD NEVER HAPPEN: could not find the note's tie-destination" << endl;
4068                         break;
4069                     }
4070                     else
4071                     {
4072                         t+=calc_len(found->len, found->dots);
4073                     }
4074                 } while (found->tied);
4075 
4076                 int total_begin=set_it->begin_tick;
4077                 int total_end=t;
4078 
4079                 int this_begin=tick;
4080                 int this_end=this_begin+calc_len(set_it->len, set_it->dots);
4081 
4082                 set_selected_part(set_it->source_part);
4083 
4084                 //that's the only note corresponding to the event?
4085                 if (this_begin==total_begin && this_end==total_end)
4086                 {
4087                     if (x < set_it->x)
4088                         mouse_x_drag_operation=BEGIN;
4089                     else
4090                         mouse_x_drag_operation=LENGTH;
4091                 }
4092                 //that's NOT the only note?
4093                 else
4094                 {
4095                     if (this_begin==total_begin)
4096                         mouse_x_drag_operation=BEGIN;
4097                     else if (this_end==total_end)
4098                         mouse_x_drag_operation=LENGTH;
4099                     else
4100                         mouse_x_drag_operation=NO_OP;
4101                 }
4102 
4103                 if (debugMsg)
4104                     cout << "you clicked at a note with begin at "<<set_it->begin_tick<<" and end at "<<t<<endl
4105                              << "x-drag-operation will be "<<mouse_x_drag_operation<<endl
4106                              << "pointer to part is "<<set_it->source_part << endl;
4107 
4108                 if (set_it->source_part == NULL) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: set_it->source_part is NULL!" << endl;
4109 
4110 
4111 
4112                 clicked_event_ptr=set_it->source_event;
4113                 dragged_event=*set_it->source_event;
4114                 original_dragged_event=dragged_event.clone();
4115                 set_dragged_event_part(set_it->source_part);
4116 
4117                 if ((mouse_erases_notes) || (event->button()==Qt::MidButton)) //erase?
4118                 {
4119                     MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent,dragged_event, dragged_event_part,  false, false));
4120                 }
4121                 else if (event->button()==Qt::LeftButton) //edit?
4122                 {
4123                     setMouseTracking(true);
4124                     dragging=true;
4125                     drag_cursor_changed=false;
4126                 }
4127             }
4128             else //we found nothing?
4129                 if (event->button()==Qt::LeftButton)
4130                 {
4131                     if (mouse_inserts_notes)
4132                     {
4133                         const MusECore::Part* curr_part = NULL;
4134                         set<const MusECore::Part*> possible_dests=staff_it->parts_at_tick(tick);
4135 
4136                         if (!possible_dests.empty())
4137                         {
4138                             if (possible_dests.size()==1)
4139                                 curr_part=*possible_dests.begin();
4140                             else
4141                             {
4142                                 if (possible_dests.find(selected_part)!=possible_dests.end())
4143                                     curr_part=selected_part;
4144                                 else
4145                                     QMessageBox::information(this, tr("Ambiguous part"), tr("There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part."));
4146                             }
4147                         }
4148                         else
4149                             QMessageBox::information(this, tr("No part"), tr("There are no parts you could add the note to."));
4150 
4151                         if (curr_part!=NULL)
4152                         {
4153                             signed int relative_tick=(signed) tick - curr_part->tick();
4154                             if (relative_tick<0)
4155                                 cerr << "ERROR: THIS SHOULD NEVER HAPPEN: relative_tick is negative!" << endl;
4156 
4157                             if (!ctrl)
4158                                 deselect_all();
4159 
4160                             MusECore::Event newevent(MusECore::Note);
4161                             newevent.setPitch(y_to_pitch(y,tick, staff_it->clef));
4162                             newevent.setVelo(note_velo);
4163                             newevent.setVeloOff(note_velo_off);
4164                             newevent.setTick(relative_tick);
4165                             newevent.setLenTick((new_len>0)?new_len:last_len);
4166                             newevent.setSelected(true); // No need to select clones, AddEvent operation below will take care of that.
4167 
4168                             if (flo_quantize(newevent.lenTick(), quant_ticks()) <= 0)
4169                             {
4170                                 newevent.setLenTick(quant_ticks());
4171                                 if (debugMsg) cout << "inserted note's length would be invisible after quantisation (too short)." << endl <<
4172                                                                             "       setting it to " << newevent.lenTick() << endl;
4173                             }
4174 
4175                             if (newevent.endTick() > curr_part->lenTick())
4176                             {
4177                                 if (debugMsg) cout << "clipping inserted note from len="<<newevent.endTick()<<" to len="<<(curr_part->lenTick() - newevent.tick())<<endl;
4178                                 newevent.setLenTick(curr_part->lenTick() - newevent.tick());
4179                             }
4180 
4181                             MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddEvent, newevent, curr_part,false, false));
4182 
4183                             set_dragged_event_part(curr_part);
4184                             dragged_event=newevent;
4185                             original_dragged_event=dragged_event.clone();
4186 
4187                             mouse_down_pos=event->pos();
4188                             mouse_operation=NO_OP;
4189                             mouse_x_drag_operation=LENGTH;
4190 
4191                             fully_recalculate();
4192 
4193                             setMouseTracking(true);
4194                             dragging=true;
4195                             inserting=true;
4196                             drag_cursor_changed=true;
4197                             setCursor(Qt::SizeAllCursor);
4198 
4199                             MusEGlobal::song->update(SC_SELECTION);
4200                         }
4201                     }
4202                     else // !mouse_inserts_notes. open a lasso
4203                     {
4204                         have_lasso=true;
4205                         lasso_start=event->pos();
4206                         lasso=QRect(lasso_start, lasso_start);
4207 
4208                         setMouseTracking(true);
4209                     }
4210 
4211                 } else if (event->button() == Qt::RightButton)
4212                     callContextMenu();
4213         }
4214 
4215     } else if (event->button() == Qt::RightButton)
4216         callContextMenu();
4217 }
4218 
mouseReleaseEvent(QMouseEvent * event)4219 void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event)
4220 {
4221     keystate=event->modifiers();
4222     bool ctrl=keystate & Qt::ControlModifier;
4223 
4224     if (dragging && event->button()==Qt::LeftButton)
4225     {
4226         if (mouse_operation==LENGTH)
4227         {
4228             if (flo_quantize(dragged_event.lenTick(), quant_ticks()) <= 0)
4229             {
4230                 if (debugMsg) cout << "new length <= 0, erasing item" << endl;
4231                 if (undo_started) MusEGlobal::song->undo();
4232                 MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent,dragged_event, dragged_event_part, false, false));
4233             }
4234             else
4235             {
4236                 last_len=flo_quantize(dragged_event.lenTick(), quant_ticks());
4237             }
4238         }
4239 
4240 
4241         if (mouse_operation==NO_OP && !inserting)
4242         {
4243             if (event->button()==Qt::LeftButton)
4244                 if (!ctrl)
4245                     deselect_all();
4246 
4247             MusEGlobal::song->applyOperation(
4248                 UndoOp(UndoOp::SelectEvent, *clicked_event_ptr, selected_part,
4249                        !clicked_event_ptr->selected(), clicked_event_ptr->selected()),
4250               MusECore::Song::OperationExecuteUpdate);
4251         }
4252 
4253         setMouseTracking(false);
4254         setCursor(active_tool_cursor);
4255         inserting=false;
4256         dragging=false;
4257         drag_cursor_changed=false;
4258         undo_started=false;
4259 
4260         x_scroll_speed=0; x_scroll_pos=0;
4261     }
4262 
4263     if (dragging_staff && event->button()==Qt::LeftButton)
4264     {
4265         int y=event->y()+y_pos;
4266         list<staff_t>::iterator mouse_staff=staff_at_y(y);
4267 
4268         if (mouse_staff!=staves.end())
4269         {
4270             if ( ((mouse_staff->type==NORMAL) && (y >= mouse_staff->y_draw-2*YLEN) && (y <= mouse_staff->y_draw+2*YLEN)) ||
4271                  ((mouse_staff->type==GRAND_TOP) && (y >= mouse_staff->y_draw-2*YLEN)) ||
4272                  ((mouse_staff->type==GRAND_BOTTOM) && (y <= mouse_staff->y_draw+2*YLEN)) )
4273                 merge_staves(mouse_staff, current_staff);
4274             else if (y >= mouse_staff->y_draw+2*YLEN) //will never happen when mouse_staff->type==GRAND_TOP
4275                 move_staff_below(mouse_staff, current_staff);
4276             else if (y <= mouse_staff->y_draw-2*YLEN) //will never happen when mouse_staff->type==GRAND_BOTTOM
4277                 move_staff_above(mouse_staff, current_staff);
4278         }
4279 
4280         dragging_staff=false;
4281         setCursor(active_tool_cursor);
4282 
4283         y_scroll_speed=0; y_scroll_pos=0;
4284     }
4285 
4286     if (have_lasso && event->button()==Qt::LeftButton)
4287     {
4288         if (!ctrl)
4289             deselect_all();
4290 
4291         set<const MusECore::Event*> already_processed;
4292 
4293         for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
4294             it->apply_lasso(lasso.translated(x_pos-x_left, y_pos - it->y_draw), already_processed);
4295 
4296         MusEGlobal::song->update(SC_SELECTION);
4297 
4298         have_lasso=false;
4299         redraw();
4300     }
4301 }
4302 
4303 #define PITCH_DELTA 5
4304 
4305 
mouseMoveEvent(QMouseEvent * event)4306 void ScoreCanvas::mouseMoveEvent (QMouseEvent* event)
4307 {
4308     keystate=event->modifiers();
4309     bool ctrl=keystate & Qt::ControlModifier;
4310 
4311     if (dragging)
4312     {
4313         int dx=event->x()-mouse_down_pos.x();
4314         int dy=event->y()-mouse_down_pos.y();
4315 
4316         int x=event->x()+x_pos-x_left;
4317 
4318         int tick=flo_quantize_floor(x_to_tick(x), quant_ticks());
4319 
4320 
4321         if ((drag_cursor_changed==false) && ((dx!=0) || (dy!=0)))
4322         {
4323             setCursor(Qt::SizeAllCursor);
4324             drag_cursor_changed=true;
4325         }
4326 
4327         if (mouse_operation==NO_OP)
4328         {
4329             if ((abs(dx)>DRAG_INIT_DISTANCE) && (mouse_x_drag_operation!=NO_OP))
4330             {
4331                 if (debugMsg) cout << "mouse-operation is now "<<mouse_x_drag_operation<<endl;
4332                 mouse_operation=mouse_x_drag_operation;
4333                 setCursor(Qt::SizeHorCursor);
4334             }
4335             else if (abs(dy)>DRAG_INIT_DISTANCE)
4336             {
4337                 if (debugMsg) cout << "mouse-operation is now PITCH" << endl;
4338                 mouse_operation=PITCH;
4339                 setCursor(Qt::SizeVerCursor);
4340             }
4341 
4342             if (mouse_operation!=NO_OP)
4343             {
4344                 if (!inserting && clicked_event_ptr->selected()==false)
4345                 {
4346                     if (!ctrl)
4347                         deselect_all();
4348 
4349                     MusEGlobal::song->applyOperation(
4350                         UndoOp(UndoOp::SelectEvent, *clicked_event_ptr, selected_part,
4351                                true, clicked_event_ptr->selected()),
4352                       MusECore::Song::OperationExecuteUpdate);
4353                 }
4354 
4355                 old_pitch=-1;
4356                 old_dest_tick=INT_MAX;
4357                 old_len=-1;
4358             }
4359         }
4360 
4361         int new_pitch;
4362 
4363         switch (mouse_operation)
4364         {
4365             case NO_OP:
4366                 break;
4367 
4368             case PITCH:
4369                 if (heavyDebugMsg) cout << "trying to change pitch, delta="<<-nearbyint((float)dy/PITCH_DELTA)<<endl;
4370                 new_pitch=original_dragged_event.pitch() - nearbyint((float)dy/PITCH_DELTA);
4371 
4372                 if (new_pitch < 0) new_pitch=0;
4373                 if (new_pitch > 127) new_pitch=127;
4374 
4375                 if (new_pitch != old_pitch)
4376                 {
4377                     if (debugMsg) cout << "changing pitch, delta="<<new_pitch-original_dragged_event.pitch()<<endl;
4378                     if (undo_started) MusEGlobal::song->undo();
4379                     undo_started=transpose_notes(part_to_set(dragged_event_part),1, new_pitch-original_dragged_event.pitch());
4380                     old_pitch=new_pitch;
4381                 }
4382 
4383                 break;
4384 
4385             case BEGIN:
4386                 {
4387                     signed relative_tick=tick-signed(dragged_event_part->tick());
4388                     unsigned dest_tick;
4389 
4390                     if (relative_tick >= 0)
4391                         dest_tick=relative_tick;
4392                     else
4393                     {
4394                         dest_tick=0;
4395                         if (debugMsg) cout << "not moving note before begin of part; setting it directly to the begin" << endl;
4396                     }
4397 
4398                     if (dest_tick != old_dest_tick)
4399                     {
4400                         if (undo_started) MusEGlobal::song->undo(); //FINDMICH EXTEND
4401                         undo_started=move_notes(part_to_set(dragged_event_part),1, (signed)dest_tick-original_dragged_event.tick());
4402                         old_dest_tick=dest_tick;
4403                     }
4404                 }
4405 
4406                 break;
4407 
4408             case LENGTH:
4409                 tick+=quant_ticks();
4410                 if (dragged_event.tick()+old_len + dragged_event_part->tick() != unsigned(tick))
4411                 {
4412                     MusECore::Event tmp=dragged_event.clone();
4413                     signed relative_tick=tick-signed(dragged_event_part->tick());
4414                     signed new_len=relative_tick-dragged_event.tick();
4415 
4416                     if (new_len>=0)
4417                         tmp.setLenTick(new_len);
4418                     else
4419                     {
4420                         tmp.setLenTick(0);
4421                         if (debugMsg) cout << "not setting len to a negative value. using 0 instead" << endl;
4422                     }
4423 
4424                     unsigned newpartlen=dragged_event_part->lenTick();
4425                     if (tmp.endTick() > dragged_event_part->lenTick())
4426                     {
4427                         if (dragged_event_part->hasHiddenEvents() & MusECore::Part::RightEventsHidden) // do not allow autoexpand
4428                         {
4429                             tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick());
4430                             if (debugMsg) cout << "resized note would exceed its part; limiting length to " << tmp.lenTick() << endl;
4431                         }
4432                         else
4433                         {
4434                             newpartlen=tmp.endTick();
4435                             if (debugMsg) cout << "resized note would exceeds its part; expanding the part..." << endl;
4436                         }
4437                     }
4438 
4439                     if (undo_started) MusEGlobal::song->undo();
4440                     MusECore::Undo operations;
4441                     operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, tmp, dragged_event, dragged_event_part, false, false));
4442                     if (newpartlen != dragged_event_part->lenTick())
4443                         schedule_resize_all_same_len_clone_parts(dragged_event_part, newpartlen, operations);
4444                     undo_started=MusEGlobal::song->applyOperationGroup(operations);
4445 
4446                     old_len=new_len;
4447                 }
4448 
4449                 break;
4450         }
4451 
4452 
4453         if ((mouse_operation==LENGTH) || (mouse_operation==BEGIN)) //x-scrolling enabled?
4454         {
4455             int win_x=event->x();
4456 
4457             if (win_x < x_left + SCROLL_MARGIN)
4458             {
4459                 x_scroll_speed=(win_x - (x_left + SCROLL_MARGIN)) * SCROLL_SPEED;
4460                 if (x_scroll_speed < -SCROLL_SPEED_MAX) x_scroll_speed=-SCROLL_SPEED_MAX;
4461             }
4462             else if (win_x > width() - SCROLL_MARGIN)
4463             {
4464                 x_scroll_speed=(win_x - (width() - SCROLL_MARGIN)) * SCROLL_SPEED;
4465                 if (x_scroll_speed > SCROLL_SPEED_MAX) x_scroll_speed=SCROLL_SPEED_MAX;
4466             }
4467             else
4468                 x_scroll_speed=0;
4469         }
4470         else
4471         {
4472             x_scroll_speed=0;
4473         }
4474     }
4475 
4476     if (dragging_staff) //y-scrolling enabled?
4477     {
4478         int win_y=event->y();
4479 
4480         if (win_y < SCROLL_MARGIN)
4481         {
4482             y_scroll_speed=(win_y - SCROLL_MARGIN) * SCROLL_SPEED;
4483             if (y_scroll_speed < -SCROLL_SPEED_MAX) y_scroll_speed=-SCROLL_SPEED_MAX;
4484         }
4485         else if (win_y > height() - SCROLL_MARGIN)
4486         {
4487             y_scroll_speed=(win_y - (height() - SCROLL_MARGIN)) * SCROLL_SPEED;
4488             if (y_scroll_speed > SCROLL_SPEED_MAX) y_scroll_speed=SCROLL_SPEED_MAX;
4489         }
4490         else
4491             y_scroll_speed=0;
4492     }
4493     else
4494     {
4495         y_scroll_speed=0;
4496     }
4497 
4498     if (have_lasso)
4499     {
4500         lasso=QRect(lasso_start, event->pos()).normalized();
4501         redraw();
4502     }
4503 }
4504 
heartbeat_timer_event()4505 void ScoreCanvas::heartbeat_timer_event()
4506 {
4507     if (x_scroll_speed)
4508     {
4509         int old_xpos=x_pos;
4510 
4511         x_scroll_pos+=x_scroll_speed*MusEGlobal::heartBeatTimer->interval()/1000.0;
4512         int tmp=int(x_scroll_pos);
4513         if (tmp!=0)
4514         x_pos+=tmp;
4515         x_scroll_pos-=tmp;
4516 
4517         if (x_pos<0) x_pos=0;
4518         if (x_pos>canvas_width()) x_pos=canvas_width();
4519 
4520         if (old_xpos!=x_pos) emit xscroll_changed(x_pos);
4521     }
4522 
4523     if (y_scroll_speed)
4524     {
4525         int old_ypos=y_pos;
4526 
4527         y_scroll_pos+=y_scroll_speed*MusEGlobal::heartBeatTimer->interval()/1000.0;
4528         int tmp=int(y_scroll_pos);
4529         if (tmp!=0)
4530         y_pos+=tmp;
4531         y_scroll_pos-=tmp;
4532 
4533         if (y_pos<0) y_pos=0;
4534         if (y_pos>canvas_height()) y_pos=canvas_height();
4535 
4536         if (old_ypos!=y_pos) emit yscroll_changed(y_pos);
4537     }
4538 }
4539 
x_scroll_event(int x)4540 void ScoreCanvas::x_scroll_event(int x)
4541 {
4542     if (debugMsg) cout << "SCROLL EVENT: x="<<x<<endl;
4543     x_pos=x;
4544     redraw();
4545 }
4546 
y_scroll_event(int y)4547 void ScoreCanvas::y_scroll_event(int y)
4548 {
4549     if (debugMsg) cout << "SCROLL EVENT: y="<<y<<endl;
4550     y_pos=y;
4551     redraw();
4552 }
4553 
4554 
4555 //if force is true, it will always happen something
4556 //if force is false, it will only happen something, if
4557 //tick isn't visible at all. if it's visible, but not at
4558 //the position goto would set it to, nothing happens
goto_tick(int tick,bool force)4559 void ScoreCanvas::goto_tick(int tick, bool force)
4560 {
4561     if (!force)
4562     {
4563         if (tick < x_to_tick(x_pos))
4564         {
4565             x_pos=tick_to_x(tick) - x_left;
4566             if (x_pos<0) x_pos=0;
4567             if (x_pos>canvas_width()) x_pos=canvas_width();
4568 
4569             emit xscroll_changed(x_pos);
4570         }
4571         else if (tick > x_to_tick(x_pos+viewport_width()*PAGESTEP))
4572         {
4573             x_pos=tick_to_x(tick);
4574             if (x_pos<0) x_pos=0;
4575             if (x_pos>canvas_width()) x_pos=canvas_width();
4576 
4577             emit xscroll_changed(x_pos);
4578         }
4579     }
4580     else
4581     {
4582         x_pos=tick_to_x(tick)-viewport_width()/2;
4583         if (x_pos<0) x_pos=0;
4584         if (x_pos>canvas_width()) x_pos=canvas_width();
4585 
4586         emit xscroll_changed(x_pos);
4587     }
4588 }
4589 
4590 //---------------------------------------------------------
4591 //   resizeEvent
4592 //---------------------------------------------------------
4593 
resizeEvent(QResizeEvent * ev)4594 void ScoreCanvas::resizeEvent(QResizeEvent* ev)
4595 {
4596     QWidget::resizeEvent(ev);
4597 
4598     emit viewport_width_changed( viewport_width()  );
4599     emit viewport_height_changed( viewport_height()  );
4600 }
4601 
pos_changed(int index,unsigned tick,bool scroll)4602 void ScoreCanvas::pos_changed(int index, unsigned tick, bool scroll)
4603 {
4604     if (index==0)
4605     {
4606         if (scroll) //potential need to scroll?
4607         {
4608             switch (MusEGlobal::song->follow())
4609             {
4610                 case MusECore::Song::NO: break;
4611                 case MusECore::Song::JUMP:  goto_tick(tick,false);  break;
4612                 case MusECore::Song::CONTINUOUS:  goto_tick(tick,true);  break;
4613             }
4614         }
4615 
4616         if (need_redraw_for_hilighting())
4617             redraw();
4618     }
4619 }
4620 
4621 
recalc_staff_pos()4622 void ScoreCanvas::recalc_staff_pos()
4623 {
4624     int y=0;
4625 
4626     for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
4627     {
4628         it->y_top=y;
4629         switch (it->type)
4630         {
4631             case NORMAL:
4632                 it->y_draw = it->y_top + STAFF_DISTANCE/2;
4633                 if (it->min_y_coord < -STAFF_DISTANCE/2)
4634                     it->y_draw+= (-it->min_y_coord - STAFF_DISTANCE/2);
4635 
4636                 it->y_bottom = it->y_draw + STAFF_DISTANCE/2;
4637                 if (it->max_y_coord > STAFF_DISTANCE/2)
4638                     it->y_bottom+= (it->max_y_coord - STAFF_DISTANCE/2);
4639 
4640                 break;
4641 
4642             case GRAND_TOP:
4643                 it->y_draw = it->y_top + STAFF_DISTANCE/2;
4644                 if (it->min_y_coord < -STAFF_DISTANCE/2)
4645                     it->y_draw+= (-it->min_y_coord - STAFF_DISTANCE/2);
4646 
4647                 it->y_bottom = it->y_draw + GRANDSTAFF_DISTANCE/2;
4648 
4649                 break;
4650 
4651             case GRAND_BOTTOM:
4652                 it->y_draw = it->y_top + GRANDSTAFF_DISTANCE/2;
4653 
4654                 it->y_bottom = it->y_draw + STAFF_DISTANCE/2;
4655                 if (it->max_y_coord > STAFF_DISTANCE/2)
4656                     it->y_bottom+= (it->max_y_coord - STAFF_DISTANCE/2);
4657 
4658                 break;
4659 
4660             default:
4661                 cerr << "ERROR: THIS SHOULD NEVER HAPPEN: invalid staff type!" << endl;
4662         }
4663         y=it->y_bottom;
4664     }
4665 
4666     emit canvas_height_changed( canvas_height() );
4667 }
4668 
staff_at_y(int y)4669 list<staff_t>::iterator ScoreCanvas::staff_at_y(int y)
4670 {
4671     for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
4672         if ((y >= it->y_top) && (y < it->y_bottom))
4673             return it;
4674 
4675     return staves.end();
4676 }
4677 
play_changed(bool)4678 void ScoreCanvas::play_changed(bool)
4679 {
4680     redraw();
4681 }
4682 
config_changed()4683 void ScoreCanvas::config_changed()
4684 {
4685     if (MusEGlobal::config.canvasBgPixmap.isEmpty()) {
4686           setBg(MusEGlobal::config.midiCanvasBg);
4687           setBg(QPixmap());
4688     }
4689     else {
4690           setBg(QPixmap(MusEGlobal::config.canvasBgPixmap));
4691     }
4692     redraw();
4693 }
4694 
set_tool(int tool)4695 void ScoreCanvas::set_tool(int tool)
4696 {
4697     switch (tool)
4698     {
4699     case MusEGui::PointerTool:
4700         setCursor(QCursor(Qt::ArrowCursor));
4701         mouse_erases_notes=false;
4702         mouse_inserts_notes=false;
4703         break;
4704     case MusEGui::RubberTool:
4705         setCursor(*deleteCursor);
4706         mouse_erases_notes=true;
4707         mouse_inserts_notes=false;
4708         break;
4709     case MusEGui::PencilTool:
4710         setCursor(*pencilCursor);
4711         mouse_erases_notes=false;
4712         mouse_inserts_notes=true;
4713         break;
4714     default:
4715         cerr << "ERROR: THIS SHOULD NEVER HAPPEN: set_tool called with unknown tool ("<<tool<<")"<<endl;
4716     }
4717 
4718     active_tool_cursor = cursor();
4719     active_tool = tool;
4720 }
4721 
menu_command(int cmd)4722 void ScoreCanvas::menu_command(int cmd)
4723 {
4724     switch (cmd)
4725     {
4726         case CMD_COLOR_BLACK:  coloring_mode_init=coloring_mode=COLOR_MODE_BLACK; redraw(); break;
4727         case CMD_COLOR_PART:   coloring_mode_init=coloring_mode=COLOR_MODE_PART;  redraw(); break;
4728         case CMD_COLOR_VELO:   coloring_mode_init=coloring_mode=COLOR_MODE_VELO;  redraw(); break;
4729         case CMD_NOTELEN_1:    new_len_init= 1; new_len=TICKS_PER_WHOLE/ 1; break;
4730         case CMD_NOTELEN_2:    new_len_init= 2; new_len=TICKS_PER_WHOLE/ 2; break;
4731         case CMD_NOTELEN_4:    new_len_init= 4; new_len=TICKS_PER_WHOLE/ 4; break;
4732         case CMD_NOTELEN_8:    new_len_init= 8; new_len=TICKS_PER_WHOLE/ 8; break;
4733         case CMD_NOTELEN_16:   new_len_init=16; new_len=TICKS_PER_WHOLE/16; break;
4734         case CMD_NOTELEN_32:   new_len_init=32; new_len=TICKS_PER_WHOLE/32; break;
4735         case CMD_NOTELEN_LAST: new_len_init= 0; new_len=-1; break;
4736 
4737         default:
4738             cerr << "ERROR: ILLEGAL FUNCTION CALL: ScoreCanvas::menu_command called with unknown command ("<<cmd<<")"<<endl;
4739     }
4740 }
4741 
preamble_keysig_slot(bool state)4742 void ScoreCanvas::preamble_keysig_slot(bool state)
4743 {
4744     preamble_contains_keysig=state;
4745     preamble_contains_keysig_init=state;
4746     redraw();
4747 }
preamble_timesig_slot(bool state)4748 void ScoreCanvas::preamble_timesig_slot(bool state)
4749 {
4750     preamble_contains_timesig=state;
4751     preamble_contains_timesig_init=state;
4752     redraw();
4753 }
4754 
set_quant(int val)4755 void ScoreCanvas::set_quant(int val)
4756 {
4757     if ((val>=0) && (val<5))
4758     {
4759         int old_len=quant_len();
4760 
4761         _quant_power2=val+1;
4762         _quant_power2_init=_quant_power2;
4763 
4764         set_pixels_per_whole(pixels_per_whole() * quant_len() / old_len );
4765 
4766         fully_recalculate();
4767     }
4768     else
4769     {
4770         cerr << "ERROR: ILLEGAL FUNCTION CALL: set_quant called with invalid value of "<<val<<endl;
4771     }
4772 }
4773 
set_pixels_per_whole(int val)4774 void ScoreCanvas::set_pixels_per_whole(int val)
4775 {
4776     if (debugMsg) cout << "setting px per whole to " << val << endl;
4777 
4778  int tick = 0;
4779     int old_xpos=x_pos;
4780     if (x_pos!=0) tick=x_to_tick(x_pos);
4781     // the above saves us from a division by zero when initalizing
4782     // ScoreCanvas; then x_pos will be 0 and x_to_tick (causing the
4783     // division by zero) won't be called. also, when x_pos=0, and the
4784     // above would not be done, after that function, x_pos will be
4785     // not zero, but at the position of the first note (which isn't
4786     // zero!)
4787 
4788     _pixels_per_whole=val;
4789     _pixels_per_whole_init=val;
4790 
4791     for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
4792         it->calc_item_pos();
4793 
4794     emit pixels_per_whole_changed(val);
4795 
4796     if (old_xpos!=0)
4797     {
4798         x_pos=tick_to_x(tick);
4799         if (debugMsg) cout << "x_pos was not zero, readjusting to " << x_pos << endl;
4800         emit xscroll_changed(x_pos);
4801     }
4802 
4803     redraw();
4804 }
4805 
cleanup_staves()4806 void ScoreCanvas::cleanup_staves()
4807 {
4808     for (list<staff_t>::iterator it=staves.begin(); it!=staves.end();)
4809     {
4810         if (it->parts.empty())
4811             staves.erase(it++);
4812         else
4813             it++;
4814     }
4815 
4816     maybe_close_if_empty();
4817 }
4818 
maybe_close_if_empty()4819 void ScoreCanvas::maybe_close_if_empty()
4820 {
4821     if (staves.empty())
4822     {
4823         if (!parent->close())
4824             cerr << "ERROR: THIS SHOULD NEVER HAPPEN: tried to close, but event hasn't been accepted!" << endl;
4825     }
4826 }
4827 
set_velo(int velo)4828 void ScoreCanvas::set_velo(int velo)
4829 {
4830     note_velo=velo;
4831     note_velo_init=velo;
4832 
4833     if (parent->get_apply_velo())
4834     {
4835         MusECore::TagEventList tag_list;
4836         tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts));
4837         MusECore::modify_velocity_items(&tag_list, 0, velo);
4838     }
4839 }
4840 
set_velo_off(int velo)4841 void ScoreCanvas::set_velo_off(int velo)
4842 {
4843     note_velo_off=velo;
4844     note_velo_off_init=velo;
4845 
4846     if (parent->get_apply_velo())
4847     {
4848         MusECore::TagEventList tag_list;
4849         tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts));
4850         MusECore::modify_off_velocity_items(&tag_list, 0, velo);
4851     }
4852 }
4853 
deselect_all()4854 void ScoreCanvas::deselect_all()
4855 {
4856     set<const MusECore::Part*> all_parts=get_all_parts();
4857 
4858     Undo operations;
4859     operations.combobreaker=true;
4860 
4861     for (set<const MusECore::Part*>::iterator part=all_parts.begin(); part!=all_parts.end(); part++)
4862         for (MusECore::ciEvent event=(*part)->events().begin(); event!=(*part)->events().end(); event++)
4863             operations.push_back(UndoOp(UndoOp::SelectEvent, event->second, *part, false, event->second.selected()));
4864 
4865     MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate);
4866 }
4867 
cleanup_parts()4868 bool staff_t::cleanup_parts()
4869 {
4870     bool did_something=false;
4871 
4872     for (set<const MusECore::Part*>::iterator it=parts.begin(); it!=parts.end();)
4873     {
4874         bool valid=false;
4875 
4876         for (MusECore::iTrack track=MusEGlobal::song->tracks()->begin(); track!=MusEGlobal::song->tracks()->end(); track++)
4877             if ((*track)->type() == MusECore::Track::MIDI)
4878             {
4879                 MusECore::PartList* pl=(*track)->parts();
4880                 for (MusECore::iPart part=pl->begin(); part!=pl->end(); part++)
4881                     if (*it == part->second)
4882                     {
4883                         valid=true;
4884                         goto get_out_here2;
4885                     }
4886             }
4887 
4888         get_out_here2:
4889         if (!valid)
4890         {
4891             parts.erase(it++);
4892 
4893             did_something=true;
4894         }
4895         else
4896             it++;
4897     }
4898 
4899     if (did_something) update_part_indices();
4900     return did_something;
4901 }
4902 
parts_at_tick(unsigned tick)4903 set<const MusECore::Part*> staff_t::parts_at_tick(unsigned tick)
4904 {
4905     set<const MusECore::Part*> result;
4906 
4907     for (set<const MusECore::Part*>::iterator it=parts.begin(); it!=parts.end(); it++)
4908         if ((tick >= (*it)->tick()) && (tick<=(*it)->endTick()))
4909             result.insert(*it);
4910 
4911     return result;
4912 }
4913 
apply_lasso(QRect rect,set<const MusECore::Event * > & already_processed)4914 void staff_t::apply_lasso(QRect rect, set<const MusECore::Event*>& already_processed)
4915 {
4916     Undo operations;
4917     for (ScoreItemList::iterator it=itemlist.begin(); it!=itemlist.end(); it++)
4918         for (set<FloItem>::iterator it2=it->second.begin(); it2!=it->second.end(); it2++)
4919             if (it2->type==FloItem::NOTE)
4920             {
4921                 if (rect.contains(it2->x, it2->y))
4922                     if (already_processed.find(it2->source_event)==already_processed.end())
4923                     {
4924                         operations.push_back(UndoOp(UndoOp::SelectEvent,*it2->source_event,it2->source_part,
4925                                                        !it2->source_event->selected(),it2->source_event->selected()));
4926                         // Here we have a choice of whether to allow undoing of selections.
4927                         // Disabled for now, it's too tedious in use. Possibly make the choice user settable.
4928 //                         operations.push_back(UndoOp(UndoOp::SelectEvent,*it2->source_event,it2->source_part,
4929 //                                                     !it2->source_event->selected(),it2->source_event->selected(), false));
4930                         already_processed.insert(it2->source_event);
4931                     }
4932             }
4933     MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate);
4934 }
4935 
set_steprec(bool flag)4936 void ScoreCanvas::set_steprec(bool flag)
4937 {
4938     srec=flag;
4939 }
4940 
midi_note(int pitch,int velo)4941 void ScoreCanvas::midi_note(int pitch, int velo)
4942 {
4943     if (velo)
4944         held_notes[pitch]=true;
4945     else
4946         held_notes[pitch]=false;
4947 
4948     if ( srec && selected_part && !MusEGlobal::audio->isPlaying() && velo )
4949         steprec->record(selected_part,pitch,quant_ticks(),quant_ticks(),velo,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier);
4950 }
4951 
4952 
4953 
update_parts()4954 void ScoreCanvas::update_parts()
4955 {
4956     if (selected_part!=NULL) //if it's null, let it be null
4957         selected_part=MusECore::partFromSerialNumber(selected_part_index);
4958 
4959     if (dragged_event_part!=NULL) //same thing here
4960         dragged_event_part=MusECore::partFromSerialNumber(dragged_event_part_index);
4961 
4962     for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++)
4963         it->update_parts();
4964 }
4965 
update_parts()4966 void staff_t::update_parts()
4967 {
4968     parts.clear();
4969 
4970     for (set<int>::iterator it=part_indices.begin(); it!=part_indices.end(); it++)
4971         parts.insert(MusECore::partFromSerialNumber(*it));
4972 }
4973 
update_part_indices()4974 void staff_t::update_part_indices()
4975 {
4976     part_indices.clear();
4977 
4978     for (set<const MusECore::Part*>::iterator it=parts.begin(); it!=parts.end(); it++)
4979         part_indices.insert((*it)->sn());
4980 }
4981 
4982 
keyPressEvent(QKeyEvent * event)4983 void ScoreEdit::keyPressEvent(QKeyEvent* event)
4984 {
4985     int key = event->key();
4986 
4987     if (key == Qt::Key_Escape)
4988     {
4989         close();
4990         return;
4991     }
4992     else if (key == shortcuts[SHRT_TOOL_POINTER].key)
4993     {
4994         edit_tools->set(MusEGui::PointerTool);
4995         return;
4996     }
4997     else if (key == shortcuts[SHRT_TOOL_PENCIL].key)
4998     {
4999         edit_tools->set(MusEGui::PencilTool);
5000         return;
5001     }
5002     else if (key == shortcuts[SHRT_TOOL_RUBBER].key)
5003     {
5004         edit_tools->set(MusEGui::RubberTool);
5005         return;
5006     }
5007     else if (key == shortcuts[SHRT_EVENT_COLOR].key)
5008     {
5009         if (score_canvas->coloring_mode_init == score_canvas->COLOR_MODE_BLACK)
5010             color_velo_action->trigger();
5011         else if (score_canvas->coloring_mode_init == score_canvas->COLOR_MODE_VELO)
5012             color_part_action->trigger();
5013         else
5014             color_black_action->trigger();
5015         return;
5016     }
5017     else //Default:
5018     {
5019         event->ignore();
5020         return;
5021     }
5022 }
5023 
5024 
add_new_parts(const std::map<const MusECore::Part *,std::set<const MusECore::Part * >> & param)5025 void ScoreCanvas::add_new_parts(const std::map< const MusECore::Part*, std::set<const MusECore::Part*> >& param)
5026 {
5027     for (list<staff_t>::iterator staff=staves.begin(); staff!=staves.end(); staff++)
5028     {
5029         for (std::map< const MusECore::Part*, set<const MusECore::Part*> >::const_iterator it = param.begin(); it!=param.end(); it++)
5030             if (staff->parts.find(it->first)!=staff->parts.end())
5031                 staff->parts.insert(it->second.begin(), it->second.end());
5032 
5033         //staff->cleanup_parts(); // don't cleanup here, because at this point, the parts might exist only
5034                                   // in the operation group. cleanup could remove them immediately
5035         staff->update_part_indices();
5036     }
5037 
5038     fully_recalculate();
5039 }
5040 
5041 
toolContextMenu()5042 QMenu* ScoreCanvas::toolContextMenu()
5043 {
5044     QMenu* r_menu = new QMenu(this);
5045     QAction* act0 = nullptr;
5046 
5047     r_menu->addAction(new MenuTitleItem(tr("Tools"), r_menu));
5048 
5049     for (unsigned i = 0; i < static_cast<unsigned>(EditToolBar::toolList.size()); ++i) {
5050         if ((scoreTools & (1 << i)) == 0)
5051             continue;
5052         QAction* act = r_menu->addAction(QIcon(**EditToolBar::toolList[i].icon), tr(EditToolBar::toolList[i].tip));
5053 
5054         if (MusEGui::EditToolBar::toolShortcuts.contains(1 << i)) {
5055             act->setShortcut(MusEGui::shortcuts[MusEGui::EditToolBar::toolShortcuts[1 << i]].key);
5056         }
5057 
5058         act->setData(scoreTools & (1 << i));
5059         act->setCheckable(true);
5060         act->setChecked((1 << i) == active_tool);
5061         if (!act0)
5062             act0 = act;
5063     }
5064 
5065     r_menu->setActiveAction(act0);
5066     return r_menu;
5067 }
5068 
callContextMenu()5069 void ScoreCanvas::callContextMenu()
5070 {
5071     QMenu * cm = toolContextMenu();
5072     if (cm) {
5073         QAction *act = cm->exec(QCursor::pos());
5074         if (act && act->data().isValid()) {
5075             int tool = act->data().toInt();
5076             parent->setEditTool(tool);
5077         }
5078         delete cm;
5079     }
5080 }
5081 
5082 
5083 
5084 } // namespace MusEGui
5085 
5086 //the following assertions are made:
5087 //  pix_quarter.width() == pix_half.width()
5088 
5089 
5090 // pix->width()-1 + 1/2*pix->width() + SHIFT + ADD_SPACE
5091 // 10-1+5+3+3=20 <- um so viel wird der taktstrich verschoben
5092 // um das doppelte (20*2=40) werden die kleinsten schläge gegeneinander versetzt
5093 
5094 
5095 
5096 
5097 //note: recalculating event- and itemlists "from zero"
5098 //      could happen in realtime, as it is pretty fast.
5099 //      however, this adds unnecessary cpu usage.
5100 //      it is NO problem to recalc the stuff "from zero"
5101 //      every time something changes.
5102 
5103 
5104 /* BUGS and potential bugs
5105  *   o tied notes don't work properly when there's a key-change in
5106  *     between, for example, when a cis is tied to a des [ will not fix ]
5107  *         (reason: this actually never happens if dealing with a sane piece)
5108  * > o when changing toolbarstate when sharing and immediately after that
5109  *     changing "share" status, the changed state isn't stored
5110  *     (could be solved by storing the current window when quitting/saving whatever)
5111  *   ? pasting in editors sometimes fails oO? ( ERROR: reading eventlist
5112  *     from clipboard failed. ignoring this one... ) [ not reproducible ]
5113  *   o test drum controllers
5114  *   o test old- and new drumtrack recording, steprecording
5115  *   o velo-controller doesn't work in new-style drum tracks
5116  *
5117  * CURRENT TODO
5118  *   o better and visual means for fading etc
5119  *   o rename E- and A-note to I- and O-
5120  *   o storing <no_toplevels /> into a template file seems to ignore
5121  *     the arranger's "MDI-ness", sets is at subwin all the time!
5122  *
5123  *   o add controller editor "like search-and-replace":
5124  *     acts on all specified type's events, and edits the value:
5125  *      - apply some MIN or MAX on it
5126  *      - scale it
5127  *      - offset it
5128  *      - only act on values in a certain value range
5129  *      - maybe do curve-mapping
5130  *   o remove the silly song type!
5131  *   o remove the song length spinbox
5132  *   o logical/physical device mapping:
5133  *     song only contains logical information: not some "synth port",
5134  *       but a "synth name", like "Yamaha DX7" or "TR-808".
5135  *     the global config contains physical information, like
5136  *       "'Yamaha DX7' is on 'USB Midi Bridge, Output two'",
5137  *       "'TR-808' is on 'jack-midi port foo, connected to bar'"
5138  *     if loading a song with unknown synth name, ask the user.
5139  *   o prerecord feature.
5140  *   o part templates
5141  *
5142  *   o find and fix FINDMICHJETZT
5143  *   o fix valgrind problems (the two "FINDMICHJETZT" lines in scoreedit.cpp)
5144  *
5145  * IMPORTANT TODO
5146  *   o allow steprec-insert-rest-note to be set to "off" / "unused"
5147  *   o all places where i added doubleclick-edits: only react on left-click double clicks!
5148  *   o support "new style" reordering with old style drum tracks as well
5149  *     (not swapping but inserting!)
5150  *
5151  *   o canvas editor: create clone via "alt+drag" moves window instead
5152  *   o controller view in score editor
5153  *   o solo button
5154  *   o do partial recalculating; recalculating can take pretty long
5155  *     (0,5 sec) when displaying a whole song in scores
5156  *   o transpose etc. must also transpose key-pressure events
5157  *   o transpose: support in-key-transpose
5158  *   o thin out: remove unneeded ctrl messages
5159  *   o support edge-scrolling when opening a lasso
5160  *   o add "dotted quarter" quantize option (for 6/8 beat)
5161  *   o ticks-to-quarter spinboxes
5162  *   o mirror most menus to an additional right-click context menu to avoid the long mouse pointer
5163  *     journey to the menu bar. try to find a way which does not involve duplicate code!
5164  *   o implement borland-style maximize: free windows do not cover the main menu, even when maximized
5165  *   o smart range selection: if range markers have been used recently (that is, a dialog with
5166  *     "range" setting, or they've been modified), default to "in range" or "selected in range"
5167  *
5168  *   o rename stuff with F2 key
5169  *
5170  *   o shrink a part from its beginning as well! watch out for clones!
5171  *
5172  * less important stuff
5173  *   o allow "fixating" toolbars?
5174  *   o quantize-templates (everything is forced into a specified
5175  *                         rhythm)
5176  *   o part-templates (you specify some notes and a control-chord;
5177  *                     the notes are set according to the chord then)
5178  *   o add functions like set velo, mod/set velo-off
5179  *   o use bars instead of flags over groups of 8ths / 16ths etc
5180  *   o support different keys in different tracks at the same time
5181  *       calc_pos_add_list and calc_item_pos will be affected by this
5182  *       calc_pos_add_list must be called before calc_item_pos then,
5183  *       and calc_item_pos must respect the pos_add_list instead of
5184  *       keeping its own pos_add variable (which is only an optimisation)
5185  *
5186  * really unimportant nice-to-haves
5187  *   o support in-song clef-changes
5188  *   o draw measure numbers
5189  *   o use timesig_t in all timesig-stuff
5190  *   o refuse to resize so that width gets smaller or equal than x_left
5191  *   o draw a margin around notes which are in a bright color
5192  *   o support drum tracks in the score editor (x-note-heads etc.)
5193  *   o drum list: scroll while dragging: probably unnecessary with the "reorder list" function
5194  *
5195  *
5196  * stuff for the other muse developers
5197  *   o process accurate timesignatures from muse's list (has to be implemented first in muse)
5198  *      ( (2+2+3)/4 or (3+2+2)/4 instead of 7/4 )
5199  */
5200 
5201 
5202 /* R O A D M A P
5203  * =============
5204  *
5205  *   1. finish the score editor, without transposing instruments and
5206  *      with only a global keymap
5207  *
5208  * 			REASON: a score editor with few functions is better than
5209  *              no score editor at all
5210  *
5211  *
5212  *   2. support transposing by octave-steps
5213  *
5214  *      REASON: the main problem with transposing is, that the
5215  *              editor needs different key signatures and needs
5216  *              to align them against each other. this problem
5217  *              doesn't exist when only transposing by octaves
5218  *
5219  *
5220  *   3. support transposing instruments, but only one
5221  *      transposing-setting per score window. that is, you won't be
5222  *      able to display your (C-)strings in the same window as your
5223  *      B-trumpet. this will be very easy to implement
5224  *
5225  *      REASON: the above problem still exists, but is circumvented
5226  *              by simply not having to align them against each other
5227  *              (because they're in different windows)
5228  *
5229  *
5230  *   4. support different transposing instruments in the same score
5231  *      window. this will be some hassle, because we need to align
5232  *      the scores properly. for example, when the C-violin has
5233  *      C-major (no accidentials), then the B-trumpet need some
5234  *      accidentials. we now must align the staves so that the
5235  *      "note-after-keychange"s of both staves are again at the
5236  *      same x-position
5237  *
5238  *      REASON: some solution for that problem must be written.
5239  *              this is a large step, which atm isn't very important
5240  *
5241  *
5242  *   5. support different keys per track. this wouldn't be that
5243  *      hard, when 4) is already done; because we then already have
5244  *      the "align it properly" functionality, and can use it
5245  *
5246  * 			REASON: this is only a nice-to-have, which can however be
5247  *              easily implemented when 4) is done
5248  */
5249