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