1 /*
2 * tracker/PatternEditorControl.cpp
3 *
4 * Copyright 2009 Peter Barth
5 *
6 * This file is part of Milkytracker.
7 *
8 * Milkytracker is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Milkytracker is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Milkytracker. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 #include "PatternEditorControl.h"
24 #include "GraphicsAbstract.h"
25 #include "Tools.h"
26 #include "Screen.h"
27 #include "Control.h"
28 #include "Font.h"
29 #include "XModule.h"
30 #include "Menu.h"
31 #include "Undo.h"
32 #include "ContextMenu.h"
33 #include "KeyBindings.h"
34 #include "DialogBase.h"
35 #include "PPUIConfig.h"
36
37 #include "TrackerConfig.h"
38
39 #define SCROLLBARWIDTH SCROLLBUTTONSIZE
40
PatternEditorControl(pp_int32 id,PPScreen * parentScreen,EventListenerInterface * eventListener,const PPPoint & location,const PPSize & size,bool border)41 PatternEditorControl::PatternEditorControl(pp_int32 id, PPScreen* parentScreen, EventListenerInterface* eventListener,
42 const PPPoint& location, const PPSize& size, bool border/*= true*/) :
43 PPControl(id, parentScreen, eventListener, location, size),
44 borderColor(&TrackerConfig::colorThemeMain),
45 cursorColor(&TrackerConfig::colorPatternEditorCursorLine),
46 selectionColor(&TrackerConfig::colorPatternEditorSelection),
47 menuPosX(0), menuPosY(0),
48 menuInvokeChannel(-1), lastMenuInvokeChannel(-1),
49 eventKeyDownBindings(NULL),
50 scanCodeBindings(NULL),
51 eventKeyDownBindingsMilkyTracker(NULL), scanCodeBindingsMilkyTracker(NULL), eventKeyDownBindingsFastTracker(NULL), scanCodeBindingsFastTracker(NULL),
52 patternEditor(NULL), module(NULL), pattern(NULL),
53 ppreCursor(NULL),
54 lastAction(RMouseDownActionInvalid), RMouseDownInChannelHeading(-1)
55 {
56 // default color
57 bgColor.r = 0;
58 bgColor.g = 0;
59 bgColor.b = 0;
60
61 vLeftScrollbar = new PPScrollbar(0, parentScreen, this, PPPoint(location.x, location.y), size.height, false);
62 vRightScrollbar = new PPScrollbar(1, parentScreen, this, PPPoint(location.x + size.width - SCROLLBARWIDTH, location.y), size.height, false);
63 hTopScrollbar = new PPScrollbar(2, parentScreen, this, PPPoint(location.x + SCROLLBARWIDTH, location.y), size.width - SCROLLBARWIDTH*2, true);
64 hBottomScrollbar = new PPScrollbar(3, parentScreen, this, PPPoint(location.x + SCROLLBARWIDTH, location.y + size.height - SCROLLBARWIDTH), size.width - SCROLLBARWIDTH*2, true);
65
66 caughtControl = NULL;
67 controlCaughtByLMouseButton = controlCaughtByRMouseButton = false;
68 pattern = NULL;
69
70 startIndex = 0;
71 startPos = 0;
72
73 songPos.orderListIndex = songPos.row = -1;
74
75 startSelection = false;
76
77 // assuming false is zero :)
78 memset(muteChannels, 0, sizeof(muteChannels));
79 memset(recChannels, 0 ,sizeof(recChannels));
80
81 // context menu
82 editMenuControl = new PPContextMenu(4, parentScreen, this, PPPoint(0,0), TrackerConfig::colorThemeMain, false, PPFont::getFont(PPFont::FONT_SYSTEM));
83
84 editMenuControl->addEntry("Mute channel", MenuCommandIDMuteChannel);
85 editMenuControl->addEntry("Solo channel", MenuCommandIDSoloChannel);
86 editMenuControl->addEntry("Unmute all", MenuCommandIDUnmuteAll);
87 editMenuControl->addEntry("\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4", -1);
88 editMenuControl->addEntry("Mark channel", MenuCommandIDSelectChannel);
89 editMenuControl->addEntry("Mark all", MenuCommandIDSelectAll);
90 editMenuControl->addEntry("\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4", -1);
91 editMenuControl->addEntry("Undo", MenuCommandIDUndo);
92 editMenuControl->addEntry("Redo", MenuCommandIDRedo);
93 editMenuControl->addEntry("\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4", -1);
94 editMenuControl->addEntry("Cut", MenuCommandIDCut);
95 editMenuControl->addEntry("Copy", MenuCommandIDCopy);
96 editMenuControl->addEntry("Paste", MenuCommandIDPaste);
97 editMenuControl->addEntry("Porous Paste", MenuCommandIDPorousPaste);
98 editMenuControl->addEntry("\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4", -1);
99 editMenuControl->addEntry("Swap channels", MenuCommandIDSwapChannels);
100
101 editMenuControl->setNotifyParentOnHide(true);
102
103 initKeyBindings();
104
105 #ifdef __LOWRES__
106 setFont(PPFont::getFont(PPFont::FONT_TINY));
107 switchEditMode(EditModeMilkyTracker);
108 #else
109 setFont(PPFont::getFont(PPFont::FONT_SYSTEM));
110 switchEditMode(EditModeFastTracker);
111 #endif
112
113 setRecordMode(false);
114
115 transposeHandlerResponder = new TransposeHandlerResponder(*this);
116 dialog = NULL;
117 }
118
~PatternEditorControl()119 PatternEditorControl::~PatternEditorControl()
120 {
121 if (patternEditor)
122 patternEditor->removeNotificationListener(this);
123
124 delete vLeftScrollbar;
125 delete vRightScrollbar;
126 delete hTopScrollbar;
127 delete hBottomScrollbar;
128
129 delete editMenuControl;
130
131 delete eventKeyDownBindingsMilkyTracker;
132 delete scanCodeBindingsMilkyTracker;
133 delete eventKeyDownBindingsFastTracker;
134 delete scanCodeBindingsFastTracker;
135
136 delete transposeHandlerResponder;
137 delete dialog;
138 }
139
setFont(PPFont * font)140 void PatternEditorControl::setFont(PPFont* font)
141 {
142 this->font = font;
143
144 adjustExtents();
145
146 if (editMenuControl->getSize().width < slotSize)
147 editMenuControl->setFont(font);
148 else
149 editMenuControl->setFont(PPFont::getFont(PPFont::FONT_SYSTEM));
150
151 assureCursorVisible();
152 }
153
setSize(const PPSize & size)154 void PatternEditorControl::setSize(const PPSize& size)
155 {
156 PPControl::setSize(size);
157
158 visibleWidth = size.width - (getRowCountWidth() + 4) - SCROLLBARWIDTH*2;
159 visibleHeight = size.height - (font->getCharHeight() + 4) - SCROLLBARWIDTH*2;
160
161 delete vLeftScrollbar;
162 delete vRightScrollbar;
163 delete hTopScrollbar;
164 delete hBottomScrollbar;
165
166 vLeftScrollbar = new PPScrollbar(0, parentScreen, this, PPPoint(location.x, location.y), size.height, false);
167 vRightScrollbar = new PPScrollbar(1, parentScreen, this, PPPoint(location.x + size.width - SCROLLBARWIDTH, location.y), size.height, false);
168 hTopScrollbar = new PPScrollbar(2, parentScreen, this, PPPoint(location.x + SCROLLBARWIDTH, location.y), size.width - SCROLLBARWIDTH*2, true);
169 hBottomScrollbar = new PPScrollbar(3, parentScreen, this, PPPoint(location.x + SCROLLBARWIDTH, location.y + size.height - SCROLLBARWIDTH), size.width - SCROLLBARWIDTH*2, true);
170
171 adjustScrollBarSizes();
172 assureCursorVisible();
173 }
174
setLocation(const PPPoint & location)175 void PatternEditorControl::setLocation(const PPPoint& location)
176 {
177 PPControl::setLocation(location);
178
179 visibleWidth = size.width - (getRowCountWidth() + 4) - SCROLLBARWIDTH*2;
180 visibleHeight = size.height - (font->getCharHeight() + 4) - SCROLLBARWIDTH*2;
181
182 delete vLeftScrollbar;
183 delete vRightScrollbar;
184 delete hTopScrollbar;
185 delete hBottomScrollbar;
186
187 vLeftScrollbar = new PPScrollbar(0, parentScreen, this, PPPoint(location.x, location.y), size.height, false);
188 vRightScrollbar = new PPScrollbar(1, parentScreen, this, PPPoint(location.x + size.width - SCROLLBARWIDTH, location.y), size.height, false);
189 hTopScrollbar = new PPScrollbar(2, parentScreen, this, PPPoint(location.x + SCROLLBARWIDTH, location.y), size.width - SCROLLBARWIDTH*2, true);
190 hBottomScrollbar = new PPScrollbar(3, parentScreen, this, PPPoint(location.x + SCROLLBARWIDTH, location.y + size.height - SCROLLBARWIDTH), size.width - SCROLLBARWIDTH*2, true);
191
192 adjustScrollBarSizes();
193 assureCursorVisible();
194 }
195
myMod(pp_int32 a,pp_int32 b)196 static inline pp_int32 myMod(pp_int32 a, pp_int32 b)
197 {
198 pp_int32 r = a % b;
199 return r < 0 ? b + r : r;
200 }
201
paint(PPGraphicsAbstract * g)202 void PatternEditorControl::paint(PPGraphicsAbstract* g)
203 {
204 if (!isVisible())
205 return;
206
207 // ;----------------- everything all right?
208 validate();
209
210 // ;----------------- colors
211 PPColor lineColor;
212
213 if (hasFocus || !properties.showFocus)
214 lineColor = *cursorColor;
215 else
216 lineColor.r = lineColor.g = lineColor.b = 64;
217
218 PPColor bColor = *borderColor, dColor = *borderColor, bCursor = lineColor, dCursor = lineColor;
219 // adjust dark color
220 dColor.scaleFixed(32768);
221 // adjust bright color
222 bColor.scaleFixed(87163);
223 // adjust dark color
224 dCursor.scaleFixed(32768);
225 // adjust bright color
226 bCursor.scaleFixed(87163);
227
228 g->setRect(location.x+SCROLLBARWIDTH, location.y+SCROLLBARWIDTH,
229 location.x + size.width - SCROLLBARWIDTH, location.y + size.height - SCROLLBARWIDTH);
230
231 g->setColor(bgColor);
232
233 g->fill();
234
235 g->setFont(font);
236
237 // ;----------------- not going any further with invalid pattern
238 if (pattern == NULL)
239 return;
240
241 // ;----------------- make layout extents
242 adjustExtents();
243
244 char name[32];
245
246 mp_sint32 i,j;
247
248 // ;----------------- selection layout
249 PatternEditorTools::Position selectionStart, selectionEnd;
250 selectionStart = patternEditor->getSelection().start;
251 selectionEnd = patternEditor->getSelection().end;
252
253 PatternEditorTools::flattenSelection(selectionStart, selectionEnd);
254
255 // only entire instrument column is allowed
256 if (selectionStart.inner >= 1 && selectionStart.inner<=2)
257 selectionStart.inner = 1;
258 if (selectionEnd.inner >= 1 && selectionEnd.inner<=2)
259 selectionEnd.inner = 2;
260 // only entire volume column is allowed
261 if (selectionStart.inner >= 3 && selectionStart.inner<=4)
262 selectionStart.inner = 3;
263 if (selectionEnd.inner >= 3 && selectionEnd.inner<=4)
264 selectionEnd.inner = 4;
265
266 PatternEditorTools::Position& cursor = patternEditor->getCursor();
267
268 if (cursor.inner < 0 || cursor.inner >= 8)
269 cursor.inner = 0;
270
271 // ;----------------- some constants
272 const pp_uint32 fontCharWidth3x = font->getCharWidth()*3 + 1;
273 const pp_uint32 fontCharWidth2x = font->getCharWidth()*2 + 1;
274 const pp_uint32 fontCharWidth1x = font->getCharWidth()*1 + 1;
275
276 PatternTools* patternTools = &this->patternTools;
277
278 // ;----------------- Little adjustment for scrolling in center
279 if (properties.scrollMode == ScrollModeToCenter)
280 {
281 if ((size.height - (SCROLLBARWIDTH + ((signed)font->getCharHeight()+4)))/(signed)font->getCharHeight() > (pattern->rows - startIndex + 1) && startIndex > 0)
282 startIndex--;
283 }
284
285 // ;----------------- start painting rows
286 pp_int32 startx = location.x + SCROLLBARWIDTH + getRowCountWidth() + 4;
287
288 pp_int32 previousPatternIndex = currentOrderlistIndex;
289 pp_int32 previousRowIndex = 0;
290
291 pp_int32 nextPatternIndex = currentOrderlistIndex;
292 pp_int32 nextRowIndex = this->pattern->rows-1;
293
294 pp_int32 songPosOrderListIndex = currentOrderlistIndex;
295
296 TXMPattern* pattern = this->pattern;
297
298 // ----------------- colors -----------------
299 PPColor noteColor = TrackerConfig::colorPatternEditorNote;
300 PPColor insColor = TrackerConfig::colorPatternEditorInstrument;
301 PPColor volColor = TrackerConfig::colorPatternEditorVolume;
302 PPColor effColor = TrackerConfig::colorPatternEditorEffect;
303 PPColor opColor = TrackerConfig::colorPatternEditorOperand;
304 PPColor hiLightPrimary = TrackerConfig::colorHighLight_1;
305 PPColor hiLightSecondary = TrackerConfig::colorHighLight_2;
306 PPColor hiLightPrimaryRow = TrackerConfig::colorRowHighLight_1;
307 PPColor hiLightSecondaryRow = TrackerConfig::colorRowHighLight_2;
308
309 PPColor textColor = PPUIConfig::getInstance()->getColor(PPUIConfig::ColorStaticText);
310
311 pp_int32 numVisibleChannels = patternEditor->getNumChannels();
312
313 for (pp_int32 i2 = startIndex;; i2++)
314 {
315 i = i2 < 0 ? startIndex - i2 - 1: i2;
316
317 pp_int32 px = location.x + SCROLLBARWIDTH;
318
319 pp_int32 py = location.y + (i-startIndex) * font->getCharHeight() + SCROLLBARWIDTH + (font->getCharHeight() + 4);
320
321 // rows are already in invisible area => abort
322 if (py >= location.y + size.height)
323 break;
324
325 pp_int32 row = i;
326
327 if (properties.prospective && properties.scrollMode == ScrollModeStayInCenter && currentOrderlistIndex != -1)
328 {
329 if (i < 0)
330 {
331 previousRowIndex--;
332 if (previousRowIndex < 0)
333 {
334 previousPatternIndex--;
335 if (previousPatternIndex >= 0)
336 {
337 pattern = &module->phead[module->header.ord[previousPatternIndex]];
338 previousRowIndex = pattern->rows-1;
339
340 noteColor.set(TrackerConfig::colorThemeMain.r, TrackerConfig::colorThemeMain.g, TrackerConfig::colorThemeMain.b);
341 insColor = volColor = effColor = opColor = noteColor;
342 }
343 else
344 {
345 continue;
346 }
347 }
348
349 songPosOrderListIndex = previousPatternIndex;
350 row = previousRowIndex;
351 }
352 else if (i >= this->pattern->rows)
353 {
354 nextRowIndex++;
355 if (nextRowIndex == pattern->rows && nextPatternIndex < module->header.ordnum)
356 {
357 nextPatternIndex++;
358 if (nextPatternIndex < module->header.ordnum)
359 {
360 pattern = &module->phead[module->header.ord[nextPatternIndex]];
361 nextRowIndex = 0;
362
363 // Outside current range display colors of main theme
364 noteColor.set(TrackerConfig::colorThemeMain.r, TrackerConfig::colorThemeMain.g, TrackerConfig::colorThemeMain.b);
365 insColor = volColor = effColor = opColor = noteColor;
366 }
367 else
368 {
369 continue;
370 }
371 }
372 else if (nextPatternIndex >= module->header.ordnum)
373 {
374 continue;
375 }
376
377 songPosOrderListIndex = nextPatternIndex;
378 row = nextRowIndex;
379 }
380 else
381 {
382 songPosOrderListIndex = currentOrderlistIndex;
383 pattern = this->pattern;
384
385 // inside current range display colors as usual
386 noteColor = TrackerConfig::colorPatternEditorNote;
387 insColor = TrackerConfig::colorPatternEditorInstrument;
388 volColor = TrackerConfig::colorPatternEditorVolume;
389 effColor = TrackerConfig::colorPatternEditorEffect;
390 opColor = TrackerConfig::colorPatternEditorOperand;
391 }
392 }
393 else
394 {
395 if (i2 < 0 || i2 >= pattern->rows)
396 continue;
397
398 row = i;
399 }
400
401 // draw rows
402 if (!(i % properties.highlightSpacingPrimary) && properties.highLightRowPrimary)
403 {
404 g->setColor(hiLightPrimaryRow);
405 for (pp_int32 k = 0; k < (pp_int32)font->getCharHeight(); k++)
406 g->drawHLine(startx - (getRowCountWidth() + 4), startx+visibleWidth, py + k);
407 }
408 else if (!(i % properties.highlightSpacingSecondary) && properties.highLightRowSecondary)
409 {
410 g->setColor(hiLightSecondaryRow);
411 for (pp_int32 k = 0; k < (pp_int32)font->getCharHeight(); k++)
412 g->drawHLine(startx - (getRowCountWidth() + 4), startx+visibleWidth, py + k);
413 }
414
415 // draw position line
416 if ((row == songPos.row && songPosOrderListIndex == songPos.orderListIndex) ||
417 (i >= 0 && i <= pattern->rows - 1 && i == songPos.row && songPos.orderListIndex == -1))
418 {
419 PPColor lineColor(TrackerConfig::colorThemeMain.r>>1, TrackerConfig::colorThemeMain.g>>1, TrackerConfig::colorThemeMain.b>>1);
420 g->setColor(lineColor);
421 for (pp_int32 k = 0; k < (pp_int32)font->getCharHeight(); k++)
422 g->drawHLine(startx - (getRowCountWidth() + 4), startx+visibleWidth, py + k);
423 }
424
425 // draw cursor line
426 if (i == cursor.row)
427 {
428 g->setColor(bCursor);
429 g->drawHLine(startx - (getRowCountWidth() + 4), startx+visibleWidth, py - 1);
430 g->setColor(dCursor);
431 g->drawHLine(startx - (getRowCountWidth() + 4), startx+visibleWidth, py + (pp_int32)font->getCharHeight());
432
433 g->setColor(lineColor);
434 for (pp_int32 k = 0; k < (pp_int32)font->getCharHeight(); k++)
435 g->drawHLine(startx - (getRowCountWidth() + 4), startx+visibleWidth, py + k);
436 }
437
438 // draw rows
439 if (!(i % properties.highlightSpacingPrimary))
440 g->setColor(hiLightPrimary);
441 else if (!(i % properties.highlightSpacingSecondary))
442 g->setColor(hiLightSecondary);
443 else
444 g->setColor(textColor);
445
446 if (properties.hexCount)
447 PatternTools::convertToHex(name, myMod(row, pattern->rows), properties.prospective ? 2 : PatternTools::getHexNumDigits(pattern->rows-1));
448 else
449 PatternTools::convertToDec(name, myMod(row, pattern->rows), properties.prospective ? 3 : PatternTools::getDecNumDigits(pattern->rows-1));
450
451 g->drawString(name, px, py);
452
453 // draw channels
454 for (j = startPos; j < numVisibleChannels; j++)
455 {
456
457 pp_int32 px = (location.x + (j-startPos) * slotSize + SCROLLBARWIDTH) + (getRowCountWidth() + 4);
458
459 // columns are already in invisible area => abort
460 if (px >= location.x + size.width)
461 break;
462
463 pp_int32 py = location.y + SCROLLBARWIDTH;
464
465 if (menuInvokeChannel == j)
466 g->setColor(255-dColor.r, 255-dColor.g, 255-dColor.b);
467 else
468 g->setColor(dColor);
469
470 {
471 PPColor nsdColor = g->getColor(), nsbColor = g->getColor();
472
473 if (menuInvokeChannel != j)
474 {
475 // adjust not so dark color
476 nsdColor.scaleFixed(50000);
477
478 // adjust bright color
479 nsbColor.scaleFixed(80000);
480 }
481 else
482 {
483 // adjust not so dark color
484 nsdColor.scaleFixed(30000);
485
486 // adjust bright color
487 nsbColor.scaleFixed(60000);
488 }
489
490 PPRect rect(px, py, px+slotSize, py + font->getCharHeight()+1);
491 g->fillVerticalShaded(rect, nsbColor, nsdColor, false);
492
493 }
494
495 if (muteChannels[j])
496 {
497 g->setColor(128, 128, 128);
498 }
499 else
500 {
501 if (!(j&1))
502 g->setColor(hiLightPrimary);
503 else
504 g->setColor(textColor);
505
506 if (j == menuInvokeChannel)
507 {
508 PPColor col = g->getColor();
509 col.r = textColor.r - col.r;
510 col.g = textColor.g - col.g;
511 col.b = textColor.b - col.b;
512 col.clamp();
513 g->setColor(col);
514 }
515 }
516
517 sprintf(name, "%i", j+1);
518
519 if (muteChannels[j])
520 strcat(name, " <Mute>");
521
522 g->drawString(name, px + (slotSize>>1)-(((pp_int32)strlen(name)*font->getCharWidth())>>1), py+1);
523 }
524
525 for (j = startPos; j < numVisibleChannels; j++)
526 {
527 pp_int32 px = (j-startPos) * slotSize + startx;
528
529 // columns are already in invisible area => abort
530 if (px >= location.x + size.width)
531 break;
532
533 if (j >= selectionStart.channel && i >= selectionStart.row &&
534 j <= selectionEnd.channel && i <= selectionEnd.row && i < this->pattern->rows)
535 {
536 g->setColor(*selectionColor);
537
538 if ((row == songPos.row && songPosOrderListIndex == songPos.orderListIndex) ||
539 (i >= 0 && i <= pattern->rows - 1 && i == songPos.row && songPos.orderListIndex == -1))
540 {
541 PPColor c = g->getColor();
542 c.r = (TrackerConfig::colorThemeMain.r + c.r)>>1;
543 c.g = (TrackerConfig::colorThemeMain.g + c.g)>>1;
544 c.b = (TrackerConfig::colorThemeMain.b + c.b)>>1;
545 c.clamp();
546 g->setColor(c);
547 }
548
549 if (i == cursor.row)
550 {
551 PPColor c = g->getColor();
552 c.r+=lineColor.r;
553 c.g+=lineColor.g;
554 c.b+=lineColor.b;
555 c.clamp();
556 g->setColor(c);
557 }
558
559 if (selectionStart.channel == selectionEnd.channel && j == selectionStart.channel)
560 {
561 pp_int32 startx = cursorPositions[selectionStart.inner];
562 pp_int32 endx = cursorPositions[selectionEnd.inner] + cursorSizes[selectionEnd.inner];
563 g->fill(PPRect(px + startx, py - (i == cursor.row ? 1 : 0), px + endx, py + font->getCharHeight() + (i == cursor.row ? 1 : 0)));
564 }
565 else if (j == selectionStart.channel)
566 {
567 pp_int32 offset = cursorPositions[selectionStart.inner];
568 g->fill(PPRect(px + offset, py - (i == cursor.row ? 1 : 0), px + slotSize, py + font->getCharHeight() + (i == cursor.row ? 1 : 0)));
569 }
570 else if (j == selectionEnd.channel)
571 {
572 pp_int32 offset = cursorPositions[selectionEnd.inner] + cursorSizes[selectionEnd.inner];
573 g->fill(PPRect(px, py - (i == cursor.row ? 1 : 0), px + offset, py + font->getCharHeight() + (i == cursor.row ? 1 : 0)));
574 }
575 else
576 {
577 g->fill(PPRect(px, py - (i == cursor.row ? 1 : 0), px + slotSize, py + font->getCharHeight() + (i == cursor.row ? 1 : 0)));
578 }
579 }
580
581 // --------------------- draw cursor ---------------------
582 if (j == cursor.channel &&
583 i == cursor.row)
584 {
585 if (hasFocus || !properties.showFocus)
586 g->setColor(TrackerConfig::colorPatternEditorCursor);
587 else
588 g->setColor(PPUIConfig::getInstance()->getColor(PPUIConfig::ColorGrayedOutSelection));
589
590 for (pp_int32 k = cursorPositions[cursor.inner]; k < cursorPositions[cursor.inner]+cursorSizes[cursor.inner]; k++)
591 g->drawVLine(py, py + font->getCharHeight(), px + k);
592
593 PPColor c = g->getColor();
594 PPColor c2 = c;
595 c.scaleFixed(32768);
596 c2.scaleFixed(87163);
597 g->setColor(c2);
598 g->drawHLine(px + cursorPositions[cursor.inner], px + cursorPositions[cursor.inner]+cursorSizes[cursor.inner], py - 1);
599 g->setColor(c);
600 g->drawHLine(px + cursorPositions[cursor.inner], px + cursorPositions[cursor.inner]+cursorSizes[cursor.inner], py + font->getCharHeight());
601 }
602
603 patternTools->setPosition(pattern, j, row);
604
605 PPColor noteCol = noteColor;
606
607 // Show notes in red if outside PT 3 octaves
608 if(properties.ptNoteLimit
609 && ((patternTools->getNote() >= 71 && patternTools->getNote() < patternTools->getNoteOffNote())
610 || patternTools->getNote() < 36))
611 {
612 noteCol.set(0xff,00,00);
613 }
614
615 if (muteChannels[j])
616 {
617 noteCol.scaleFixed(properties.muteFade);
618 }
619
620 g->setColor(noteCol);
621 patternTools->getNoteName(name, patternTools->getNote());
622 g->drawString(name,px, py);
623
624 px += fontCharWidth3x + properties.spacing;
625
626 if (muteChannels[j])
627 {
628 PPColor insCol = insColor;
629 insCol.scaleFixed(properties.muteFade);
630 g->setColor(insCol);
631 }
632 else
633 g->setColor(insColor);
634
635 pp_uint32 i = patternTools->getInstrument();
636
637 if (i)
638 patternTools->convertToHex(name, i, 2);
639 else
640 {
641 name[0] = name[1] = '\xf4';
642 name[2] = 0;
643 }
644
645 if (name[0] == '0')
646 name[0] = '\xf4';
647
648 g->drawString(name,px, py);
649
650 px += fontCharWidth2x + properties.spacing;
651
652 if (muteChannels[j])
653 {
654 PPColor volCol = volColor;
655 volCol.scaleFixed(properties.muteFade);
656 g->setColor(volCol);
657 }
658 else
659 g->setColor(volColor);
660
661 pp_int32 eff, op;
662
663 name[0] = name[1] = '\xf4';
664 name[2] = 0;
665 if (pattern->effnum >= 2)
666 {
667 patternTools->getFirstEffect(eff, op);
668
669 patternTools->convertEffectsToFT2(eff, op);
670
671 pp_int32 volume = patternTools->getVolumeFromEffect(eff, op);
672
673 patternTools->getVolumeName(name, volume);
674 }
675
676 g->drawString(name,px, py);
677
678 px += fontCharWidth2x + properties.spacing;
679
680 if (muteChannels[j])
681 {
682 PPColor effCol = effColor;
683 effCol.scaleFixed(properties.muteFade);
684 g->setColor(effCol);
685 }
686 else
687 g->setColor(effColor);
688
689 if (pattern->effnum == 1)
690 {
691 patternTools->getFirstEffect(eff, op);
692 patternTools->convertEffectsToFT2(eff, op);
693 }
694 else
695 {
696 patternTools->getNextEffect(eff, op);
697 patternTools->convertEffectsToFT2(eff, op);
698 }
699
700 if (eff == 0 && op == 0)
701 {
702 name[0] = properties.zeroEffectCharacter;
703 name[1] = 0;
704 }
705 else
706 {
707 patternTools->getEffectName(name, eff);
708 }
709
710 g->drawString(name,px, py);
711
712 px += fontCharWidth1x;
713
714 if (muteChannels[j])
715 {
716 PPColor opCol = opColor;
717 opCol.scaleFixed(properties.muteFade);
718 g->setColor(opCol);
719 }
720 else
721 g->setColor(opColor);
722
723 if (eff == 0 && op == 0)
724 {
725 name[0] = name[1] = properties.zeroEffectCharacter;
726 name[2] = 0;
727 }
728 else
729 {
730 patternTools->convertToHex(name, op, 2);
731 }
732
733 g->drawString(name,px, py);
734 }
735 }
736
737 for (j = startPos; j < numVisibleChannels; j++)
738 {
739
740 pp_int32 px = (location.x + (j-startPos) * slotSize + SCROLLBARWIDTH) + (getRowCountWidth() + 4);
741
742 // columns are already in invisible area => abort
743 if (px >= location.x + size.width)
744 break;
745
746 px += fontCharWidth3x + properties.spacing;
747 px += fontCharWidth2x*3-1 + properties.spacing*2;
748 px += fontCharWidth1x;
749
750 g->setColor(*borderColor);
751
752 g->drawVLine(location.y, location.y + size.height, px+1);
753
754 g->setColor(bColor);
755
756 g->drawVLine(location.y, location.y + size.height, px);
757
758 g->setColor(dColor);
759
760 g->drawVLine(location.y, location.y + size.height, px+2);
761 }
762
763 // ;----------------- Margin lines
764 // draw margin vertical line
765 g->setColor(*borderColor);
766
767 pp_int32 px = location.x + SCROLLBARWIDTH;
768 px+=getRowCountWidth() + 1;
769 g->drawVLine(location.y, location.y + size.height, px+1);
770
771 g->setColor(bColor);
772 g->drawVLine(location.y, location.y + size.height, px);
773
774 g->setColor(dColor);
775 g->drawVLine(location.y, location.y + size.height, px+2);
776
777 // draw margin horizontal lines
778 for (j = 0; j < visibleWidth / slotSize + 1; j++)
779 {
780 pp_int32 px = (location.x + j * slotSize + SCROLLBARWIDTH) + (getRowCountWidth() + 4) - 1;
781
782 // columns are already in invisible area => abort
783 if (px >= location.x + size.width)
784 break;
785
786 pp_int32 py = location.y + SCROLLBARWIDTH;
787
788 py+=font->getCharHeight() + 1;
789
790 // Did we reach the maximum number of channels already?
791 // no: just draw seperate horizontal line segments between the vertical margin lines
792 if (startPos + j < numVisibleChannels)
793 {
794 g->setColor(*borderColor);
795 g->drawHLine(px, px + slotSize - 1, py+1);
796
797 g->setColor(bColor);
798 g->drawHLine(px + 1, px + slotSize - 2, py);
799
800 g->setColor(dColor);
801 g->drawHLine(px + 1, px + slotSize - 2, py+2);
802 }
803 // yes: draw the horizontal margin line completely to the right and abort loop
804 else
805 {
806 g->setColor(*borderColor);
807 g->drawHLine(px, location.x + size.width, py+1);
808
809 g->setColor(bColor);
810 g->drawHLine(px + 1, location.x + size.width, py);
811
812 g->setColor(dColor);
813 g->drawHLine(px + 1, location.x + size.width, py+2);
814 break;
815 }
816 }
817
818 // --------------------- draw moved selection ---------------------
819
820 if (hasValidSelection() && moveSelection)
821 {
822 pp_int32 moveSelectionRows = moveSelectionFinalPos.row - moveSelectionInitialPos.row;
823 pp_int32 moveSelectionChannels = moveSelectionFinalPos.channel - moveSelectionInitialPos.channel;
824
825 pp_int32 i1 = selectionStart.row + moveSelectionRows;
826 pp_int32 j1 = selectionStart.channel + moveSelectionChannels;
827 pp_int32 i2 = selectionEnd.row + moveSelectionRows;
828 pp_int32 j2 = selectionEnd.channel + moveSelectionChannels;
829
830 if (i2 >= 0 && j2 >= 0 && i1 < pattern->rows && j1 < numVisibleChannels)
831 {
832 i1 = PPTools::clamp(i1, 0, pattern->rows);
833 i2 = PPTools::clamp(i2, 0, pattern->rows);
834 j1 = PPTools::clamp(j1, 0, numVisibleChannels);
835 j2 = PPTools::clamp(j2, 0, numVisibleChannels);
836
837 pp_int32 x1 = (location.x + (j1-startPos) * slotSize + SCROLLBARWIDTH) + cursorPositions[selectionStart.inner] + (getRowCountWidth() + 4);
838 pp_int32 y1 = (location.y + (i1-startIndex) * font->getCharHeight() + SCROLLBARWIDTH) + (font->getCharHeight() + 4);
839
840 pp_int32 x2 = (location.x + (j2-startPos) * slotSize + SCROLLBARWIDTH) + cursorPositions[selectionEnd.inner]+cursorSizes[selectionEnd.inner] + (getRowCountWidth() + 3);
841 pp_int32 y2 = (location.y + (i2-startIndex) * font->getCharHeight() + SCROLLBARWIDTH) + (font->getCharHeight()*2 + 2);
842
843 // use a different color for cloning the selection instead of moving it
844 if (::getKeyModifier() & selectionKeyModifier)
845 g->setColor(hiLightPrimary);
846 else
847 g->setColor(textColor);
848
849 const pp_int32 dashLen = 6;
850
851 // inner dashed lines
852 g->drawHLineDashed(x1, x2, y1, dashLen, 3);
853 g->drawHLineDashed(x1, x2, y2, dashLen, 3+y2-y1);
854 g->drawVLineDashed(y1, y2, x1, dashLen, 3);
855 g->drawVLineDashed(y1, y2+2, x2, dashLen, 3+x2-x1);
856
857 // outer dashed lines
858 g->drawHLineDashed(x1-1, x2+1, y1-1, dashLen, 1);
859 g->drawHLineDashed(x1-1, x2, y2+1, dashLen, 3+y2-y1);
860 g->drawVLineDashed(y1-1, y2+1, x1-1, dashLen, 1);
861 g->drawVLineDashed(y1-1, y2+2, x2+1, dashLen, 3+x2-x1);
862 }
863
864 }
865
866 // draw scrollbars
867 hTopScrollbar->paint(g);
868 hBottomScrollbar->paint(g);
869 vLeftScrollbar->paint(g);
870 vRightScrollbar->paint(g);
871 }
872
attachPatternEditor(PatternEditor * patternEditor)873 void PatternEditorControl::attachPatternEditor(PatternEditor* patternEditor)
874 {
875 if (this->patternEditor)
876 this->patternEditor->removeNotificationListener(this);
877
878 this->patternEditor = patternEditor;
879 patternEditor->addNotificationListener(this);
880
881 this->module = patternEditor->getModule();
882 this->pattern = patternEditor->getPattern();
883
884 adjustScrollBarPositionsAndSizes();
885 }
886
reset()887 void PatternEditorControl::reset()
888 {
889 patternEditor->reset();
890
891 menuInvokeChannel = -1;
892 menuPosX = menuPosY = 0;
893
894 // clear selection
895 startSelection = false;
896 }
897
setNumVisibleChannels(pp_int32 channels)898 void PatternEditorControl::setNumVisibleChannels(pp_int32 channels)
899 {
900 patternEditor->setNumVisibleChannels(channels);
901
902 adjustScrollBarPositionsAndSizes();
903
904 validate();
905 }
906
getRowCountWidth()907 pp_int32 PatternEditorControl::getRowCountWidth()
908 {
909 if (pattern == NULL)
910 return 0;
911
912 if (!properties.prospective)
913 {
914 if (properties.hexCount)
915 return font->getCharWidth()*PatternTools::getHexNumDigits(pattern->rows-1);
916 else
917 return font->getCharWidth()*PatternTools::getDecNumDigits(pattern->rows-1);
918 }
919 else
920 {
921 if (properties.hexCount)
922 return font->getCharWidth()*2;
923 else
924 return font->getCharWidth()*3;
925 }
926 }
927
adjustExtents()928 void PatternEditorControl::adjustExtents()
929 {
930 visibleWidth = size.width - (getRowCountWidth() + 4) - SCROLLBARWIDTH*2;
931 visibleHeight = size.height - (font->getCharHeight() + 4) - SCROLLBARWIDTH*2;
932
933 slotSize = 10*font->getCharWidth() + 3*1 + 4 + 3*properties.spacing;
934
935 cursorPositions[0] = 0;
936 cursorPositions[1] = 3 * font->getCharWidth() + 1 + properties.spacing;
937 cursorPositions[2] = cursorPositions[1] + font->getCharWidth();
938 cursorPositions[3] = cursorPositions[2] + font->getCharWidth() + 1 + properties.spacing;
939 cursorPositions[4] = cursorPositions[3] + font->getCharWidth();
940 cursorPositions[5] = cursorPositions[4] + font->getCharWidth() + 1 + properties.spacing;
941 cursorPositions[6] = cursorPositions[5] + font->getCharWidth() + 1;
942 cursorPositions[7] = cursorPositions[6] + font->getCharWidth();
943 cursorPositions[8] = /*cursorPositions[7] + font->getCharWidth() + 1*/slotSize;
944
945 cursorSizes[0] = 3 * font->getCharWidth(); // note
946 cursorSizes[1] = font->getCharWidth(); // instrument digit 1
947 cursorSizes[2] = font->getCharWidth(); // instrument digit 2
948 cursorSizes[3] = font->getCharWidth(); // volume digit 1
949 cursorSizes[4] = font->getCharWidth(); // volume digit 2
950 cursorSizes[5] = font->getCharWidth(); // effect "digit"
951 cursorSizes[6] = font->getCharWidth(); // operand digit 1
952 cursorSizes[7] = font->getCharWidth(); // operand digit 2
953 }
954
adjustVerticalScrollBarPositions(mp_sint32 startIndex)955 void PatternEditorControl::adjustVerticalScrollBarPositions(mp_sint32 startIndex)
956 {
957 // adjust scrollbar positions
958 if (properties.scrollMode != ScrollModeStayInCenter)
959 {
960 pp_int32 visibleItems = (visibleHeight) / font->getCharHeight();
961 float v = (float)(pattern->rows - visibleItems);
962 vLeftScrollbar->setBarPosition((pp_int32)(startIndex*(65536.0f/v)));
963 vRightScrollbar->setBarPosition((pp_int32)(startIndex*(65536.0f/v)));
964 }
965 else
966 {
967 float v = (float)patternEditor->getCursor().row / (float)(pattern->rows-1);
968 vLeftScrollbar->setBarPosition((pp_int32)(65536.0f*v));
969 vRightScrollbar->setBarPosition((pp_int32)(65536.0f*v));
970 }
971 }
972
adjustHorizontalScrollBarPositions(mp_sint32 startPos)973 void PatternEditorControl::adjustHorizontalScrollBarPositions(mp_sint32 startPos)
974 {
975 pp_int32 visibleItems = (visibleWidth) / slotSize;
976
977 float v = (float)(patternEditor->getNumChannels() - visibleItems);
978
979 hTopScrollbar->setBarPosition((pp_int32)(startPos*(65536.0f/v)));
980 hBottomScrollbar->setBarPosition((pp_int32)(startPos*(65536.0f/v)));
981 }
982
adjustScrollBarSizes()983 void PatternEditorControl::adjustScrollBarSizes()
984 {
985 float s;
986 if (properties.scrollMode != ScrollModeStayInCenter)
987 {
988 s = (float)(visibleHeight) / (float)(pattern->rows*(font->getCharHeight()));
989 }
990 else
991 {
992 //s = (float)(visibleHeight>>1) / (float)((pattern->rows-1)*(font->getCharHeight()));
993
994 s = 1.0f / (float)pattern->rows;
995 if (s > 1.0f)
996 s = 1.0f;
997 }
998
999 vLeftScrollbar->setBarSize((pp_int32)(s*65536.0f), false);
1000 vRightScrollbar->setBarSize((pp_int32)(s*65536.0f), false);
1001
1002 s = (float)(visibleWidth) / (float)(patternEditor->getNumChannels()*slotSize);
1003 hTopScrollbar->setBarSize((pp_int32)(s*65536.0f), false);
1004 hBottomScrollbar->setBarSize((pp_int32)(s*65536.0f), false);
1005 }
1006
adjustScrollBarPositionsAndSizes()1007 void PatternEditorControl::adjustScrollBarPositionsAndSizes()
1008 {
1009 if (pattern == NULL)
1010 return;
1011
1012 adjustScrollBarSizes();
1013
1014 adjustVerticalScrollBarPositions(startIndex);
1015 adjustHorizontalScrollBarPositions(startPos);
1016 }
1017
setScrollbarPositions(mp_sint32 startIndex,mp_sint32 startPos)1018 void PatternEditorControl::setScrollbarPositions(mp_sint32 startIndex, mp_sint32 startPos)
1019 {
1020 adjustHorizontalScrollBarPositions(startPos);
1021 adjustVerticalScrollBarPositions(startIndex);
1022 }
1023
scrollCursorUp()1024 void PatternEditorControl::scrollCursorUp()
1025 {
1026 if (pattern == NULL)
1027 return;
1028
1029 /*cursor.row--;
1030 if (cursor.row < 0)
1031 cursor.row = 0;*/
1032
1033 eventKeyDownBinding_UP();
1034 }
1035
scrollCursorDown()1036 void PatternEditorControl::scrollCursorDown()
1037 {
1038 if (pattern == NULL)
1039 return;
1040
1041 /*cursor.row++;
1042 if (cursor.row > pattern->rows - 1)
1043 cursor.row = pattern->rows - 1;*/
1044
1045 eventKeyDownBinding_DOWN();
1046 }
1047
assureCursorVisible(bool row,bool channel)1048 void PatternEditorControl::assureCursorVisible(bool row/* = true*/, bool channel/* = true*/)
1049 {
1050 if (pattern == NULL)
1051 return;
1052
1053 PatternEditorTools::Position& cursor = patternEditor->getCursor();
1054
1055 if (row)
1056 {
1057
1058 if (cursor.row >= 0)
1059 {
1060
1061 switch (properties.scrollMode)
1062 {
1063 case ScrollModeToEnd:
1064 {
1065 pp_int32 visibleItems = (visibleHeight - font->getCharHeight()) / font->getCharHeight();
1066
1067
1068 if ((startIndex <= cursor.row) &&
1069 ((cursor.row - startIndex) * font->getCharHeight()) <= (visibleHeight - font->getCharHeight()))
1070 {
1071 }
1072 else if (cursor.row > startIndex &&
1073 cursor.row + visibleItems < pattern->rows)
1074 {
1075 //startIndex = cursorPositionRow;
1076 startIndex+=(cursor.row-(startIndex+visibleItems));
1077 }
1078 else if (cursor.row < startIndex &&
1079 cursor.row + visibleItems < pattern->rows)
1080 {
1081 //startIndex = cursorPositionRow;
1082 startIndex+=(cursor.row-startIndex);
1083 }
1084 else
1085 {
1086 startIndex = cursor.row - visibleItems;
1087 if (startIndex < 0)
1088 startIndex = 0;
1089 }
1090 break;
1091 }
1092
1093 case ScrollModeToCenter:
1094 {
1095 pp_int32 mid = (visibleHeight/2) / font->getCharHeight();
1096 startIndex = cursor.row - mid;
1097 if (startIndex < 0)
1098 startIndex = 0;
1099 break;
1100 }
1101
1102 case ScrollModeStayInCenter:
1103 {
1104 pp_int32 mid = (visibleHeight/2) / font->getCharHeight();
1105 startIndex = cursor.row - mid;
1106 break;
1107 }
1108 }
1109 }
1110
1111 adjustVerticalScrollBarPositions(startIndex);
1112 }
1113
1114 if (channel)
1115 {
1116
1117 if (cursor.channel >= 0)
1118 {
1119 pp_int32 visibleChannels = ((visibleWidth) / slotSize) - 1;
1120
1121 pp_int32 cursorPos = (cursor.channel-startPos) * slotSize +
1122 cursorPositions[cursor.inner];
1123
1124 pp_int32 cursorWidth = cursorPositions[cursor.inner+1] -
1125 cursorPositions[cursor.inner];
1126
1127 if ((startPos <= cursor.channel) &&
1128 cursorPos <= visibleWidth - cursorWidth)
1129 {
1130 }
1131 else if (cursor.channel > startPos &&
1132 cursorPos < visibleWidth - cursorWidth)
1133 {
1134 startPos+=(cursor.channel-(startPos+visibleChannels));
1135 }
1136 else if (cursor.channel < startPos &&
1137 cursor.channel + visibleChannels < (signed)patternEditor->getNumChannels())
1138 {
1139 startPos+=(cursor.channel-startPos);
1140 }
1141 else
1142 {
1143 startPos = cursor.channel - visibleChannels;
1144 if (startPos < 0)
1145 startPos = 0;
1146 }
1147
1148 }
1149
1150 adjustHorizontalScrollBarPositions(startPos);
1151 }
1152
1153 }
1154
getNextRecordingChannel(mp_sint32 currentChannel)1155 mp_sint32 PatternEditorControl::getNextRecordingChannel(mp_sint32 currentChannel)
1156 {
1157 if (currentChannel < 0 || currentChannel >= TrackerConfig::MAXCHANNELS)
1158 return -1;
1159
1160 for (pp_int32 i = currentChannel+1; i < currentChannel + 1 + module->header.channum; i++)
1161 {
1162 pp_int32 c = i % module->header.channum;
1163 if (recChannels[c])
1164 return c;
1165 }
1166
1167 return currentChannel;
1168 }
1169
advanceRow(bool assureCursor,bool repaint)1170 void PatternEditorControl::advanceRow(bool assureCursor/* = true*/, bool repaint/* = true*/)
1171 {
1172 if (!properties.rowAdvance)
1173 return;
1174
1175 /*if (cursor.row + rowInsertAdd < pattern->rows - 1)
1176 {
1177 cursor.row+=rowInsertAdd;
1178 }
1179 else
1180 cursor.row = pattern->rows - 1;*/
1181
1182 PatternEditorTools::Position& cursor = patternEditor->getCursor();
1183
1184 cursor.row = (cursor.row + properties.rowInsertAdd)/* % pattern->rows*/;
1185
1186 pp_int32 res = 0;
1187 if (cursor.row > pattern->rows-1)
1188 {
1189 res = notifyUpdate(PatternEditorControl::AdvanceCodeWrappedEnd);
1190 }
1191
1192 if (!res && cursor.row > pattern->rows-1)
1193 {
1194 cursor.row %= pattern->rows;
1195 }
1196
1197 if (assureCursor)
1198 assureCursorVisible();
1199
1200 if (repaint)
1201 parentScreen->paintControl(this);
1202 }
1203
advance()1204 void PatternEditorControl::advance()
1205 {
1206 PatternEditorTools::Position& cursor = patternEditor->getCursor();
1207
1208 // Multichannel editing means:
1209 // If we're in the note column and a note has been entered
1210 // locate cursor to next recording channel
1211 if (properties.multiChannelEdit && cursor.inner == 0)
1212 cursor.channel = getNextRecordingChannel(cursor.channel);
1213
1214 advanceRow(false, false);
1215
1216 assureCursorVisible();
1217 }
1218
setRow(pp_int32 row,bool bAssureCursorVisible)1219 void PatternEditorControl::setRow(pp_int32 row, bool bAssureCursorVisible/* = true*/)
1220 {
1221 if (pattern == NULL)
1222 return;
1223
1224 PatternEditorTools::Position& cursor = patternEditor->getCursor();
1225
1226 if (row >= 0 && row < pattern->rows)
1227 cursor.row = row;
1228
1229 if (bAssureCursorVisible)
1230 assureCursorVisible(true, false);
1231 }
1232
setChannel(pp_int32 chn,pp_int32 posInner)1233 void PatternEditorControl::setChannel(pp_int32 chn, pp_int32 posInner)
1234 {
1235 if (pattern == NULL)
1236 return;
1237
1238 PatternEditorTools::Position& cursor = patternEditor->getCursor();
1239
1240 if (chn >= 0 && chn < pattern->channum)
1241 cursor.channel = chn;
1242
1243 cursor.inner = posInner;
1244
1245 assureCursorVisible(false, true);
1246 }
1247
validate()1248 void PatternEditorControl::validate()
1249 {
1250 if (pattern == NULL)
1251 return;
1252
1253 // validate scrollbar sizes
1254 adjustScrollBarSizes();
1255 /*float s = (float)(visibleHeight) / (float)(pattern->rows*(font->getCharHeight()));
1256
1257 vLeftScrollbar->setBarSize((pp_int32)(s*65536.0f), false);
1258 vRightScrollbar->setBarSize((pp_int32)(s*65536.0f), false);
1259
1260 s = (float)(visibleWidth) / (float)(numVisibleChannels*slotSize);
1261
1262 hTopScrollbar->setBarSize((pp_int32)(s*65536.0f), false);
1263 hBottomScrollbar->setBarSize((pp_int32)(s*65536.0f), false);*/
1264
1265 PatternEditorTools::Position& cursor = patternEditor->getCursor();
1266
1267 // validate cursor position
1268 if (cursor.channel >= patternEditor->getNumChannels())
1269 {
1270 cursor.channel = patternEditor->getNumChannels()-1;
1271 assureCursorVisible(false, true);
1272 }
1273
1274 if (cursor.row >= pattern->rows)
1275 {
1276 cursor.row = pattern->rows-1;
1277 assureCursorVisible(true, false);
1278 }
1279
1280 pp_int32 visibleItems = (visibleHeight - font->getCharHeight()) / font->getCharHeight();
1281
1282 if (properties.scrollMode != ScrollModeStayInCenter)
1283 {
1284 if (startIndex + visibleItems > pattern->rows)
1285 {
1286 startIndex-=(startIndex + visibleItems)-pattern->rows;
1287 }
1288
1289 if (startIndex < 0)
1290 startIndex = 0;
1291 }
1292
1293 pp_int32 visibleChannels = (visibleWidth) / slotSize;
1294 if (startPos + visibleChannels > patternEditor->getNumChannels())
1295 {
1296 startPos-=(startPos + visibleChannels)-patternEditor->getNumChannels();
1297 }
1298
1299 if (startPos < 0)
1300 startPos = 0;
1301
1302 // validate selection
1303 /*flattenSelection();
1304
1305 if ((selectionStart.channel >= 0 && selectionStart.channel > numVisibleChannels-1) ||
1306 (selectionStart.row >= 0 && selectionStart.row > pattern->rows-1))
1307 {
1308 resetSelection();
1309 return;
1310 }
1311
1312 if (selectionEnd.channel >= 0 && selectionEnd.channel > numVisibleChannels-1)
1313 selectionEnd.channel = numVisibleChannels-1;
1314
1315 if (selectionEnd.row >= 0 && selectionEnd.row > pattern->rows-1)
1316 selectionEnd.row = pattern->rows-1;*/
1317 }
1318
hasValidSelection() const1319 bool PatternEditorControl::hasValidSelection() const
1320 {
1321 return patternEditor->hasValidSelection();
1322 }
1323
markChannel(pp_int32 channel,bool invert)1324 void PatternEditorControl::markChannel(pp_int32 channel, bool invert/* = true*/)
1325 {
1326 PatternEditor::Selection currentSelection = patternEditor->getSelection();
1327
1328 if (currentSelection.start.channel == channel &&
1329 currentSelection.start.row == 0 &&
1330 currentSelection.end.channel == channel &&
1331 currentSelection.end.row == pattern->rows - 1 &&
1332 invert)
1333 {
1334 patternEditor->resetSelection();
1335 }
1336 else
1337 {
1338 patternEditor->selectChannel(channel);
1339 }
1340 }
1341
selectAll()1342 void PatternEditorControl::selectAll()
1343 {
1344 patternEditor->selectAll();
1345 }
1346
deselectAll()1347 void PatternEditorControl::deselectAll()
1348 {
1349 patternEditor->resetSelection();
1350 }
1351
executeMenuCommand(pp_int32 commandId)1352 void PatternEditorControl::executeMenuCommand(pp_int32 commandId)
1353 {
1354 switch (commandId)
1355 {
1356 case MenuCommandIDMuteChannel:
1357 {
1358 muteChannels[menuInvokeChannel] = !muteChannels[menuInvokeChannel];
1359 PPEvent e(eValueChanged, &muteChannels, sizeof(muteChannels));
1360 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
1361 break;
1362 }
1363
1364 case MenuCommandIDSoloChannel:
1365 {
1366 for (pp_uint32 i = 0; i < sizeof(muteChannels)/sizeof(pp_uint8); i++)
1367 muteChannels[i] = true;
1368 muteChannels[menuInvokeChannel] = false;
1369 PPEvent e(eValueChanged, &muteChannels, sizeof(muteChannels));
1370 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
1371 break;
1372 }
1373
1374 case MenuCommandIDUnmuteAll:
1375 {
1376 for (pp_uint32 i = 0; i < sizeof(muteChannels)/sizeof(pp_uint8); i++)
1377 muteChannels[i] = false;
1378 PPEvent e(eValueChanged, &muteChannels, sizeof(muteChannels));
1379 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
1380 break;
1381 }
1382
1383 // cut
1384 case MenuCommandIDCut:
1385 eventKeyCharBinding_Cut();
1386 break;
1387
1388 // copy
1389 case MenuCommandIDCopy:
1390 eventKeyCharBinding_Copy();
1391 break;
1392
1393 // paste
1394 case MenuCommandIDPaste:
1395 eventKeyCharBinding_Paste();
1396 break;
1397
1398 // transparent paste
1399 case MenuCommandIDPorousPaste:
1400 eventKeyCharBinding_TransparentPaste();
1401 break;
1402
1403 // mark all
1404 case MenuCommandIDSelectAll:
1405 eventKeyCharBinding_SelectAll();
1406 break;
1407
1408 // mark channel
1409 case MenuCommandIDSelectChannel:
1410 markChannel(menuInvokeChannel);
1411 break;
1412
1413 case MenuCommandIDUndo:
1414 patternEditor->undo();
1415 break;
1416
1417 case MenuCommandIDRedo:
1418 patternEditor->redo();
1419 break;
1420
1421 case MenuCommandIDSwapChannels:
1422 patternEditor->swapChannels(patternEditor->getCursor().channel, menuInvokeChannel);
1423 break;
1424 }
1425
1426 // Hack:
1427 if (parentScreen->getContextMenuControl() == editMenuControl)
1428 {
1429 menuInvokeChannel = -1;
1430 lastMenuInvokeChannel = -1;
1431 }
1432
1433 }
1434
switchEditMode(EditModes mode)1435 void PatternEditorControl::switchEditMode(EditModes mode)
1436 {
1437 switch (mode)
1438 {
1439 case EditModeMilkyTracker:
1440 {
1441 // Assign keyboard bindings
1442 eventKeyDownBindings = eventKeyDownBindingsMilkyTracker;
1443 scanCodeBindings = scanCodeBindingsMilkyTracker;
1444
1445 selectionKeyModifier = KeyModifierSHIFT;
1446 break;
1447 }
1448
1449 case EditModeFastTracker:
1450 {
1451 // Assign keyboard bindings
1452 eventKeyDownBindings = eventKeyDownBindingsFastTracker;
1453 scanCodeBindings = scanCodeBindingsFastTracker;
1454
1455 selectionKeyModifier = KeyModifierALT;
1456 break;
1457 }
1458 }
1459
1460 editMode = mode;
1461 }
1462
unmuteAll()1463 void PatternEditorControl::unmuteAll()
1464 {
1465 memset(muteChannels, 0, sizeof(muteChannels));
1466
1467 PPEvent e(eValueChanged, &muteChannels, sizeof(muteChannels));
1468 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
1469 }
1470
setRecordMode(bool b)1471 void PatternEditorControl::setRecordMode(bool b)
1472 {
1473 cursorColor = b ? &TrackerConfig::colorPatternEditorCursorLineHighLight : &TrackerConfig::colorPatternEditorCursorLine;
1474 }
1475
editorNotification(EditorBase * sender,EditorBase::EditorNotifications notification)1476 void PatternEditorControl::editorNotification(EditorBase* sender, EditorBase::EditorNotifications notification)
1477 {
1478 switch (notification)
1479 {
1480 case PatternEditor::EditorDestruction:
1481 {
1482 patternEditor = NULL;
1483 break;
1484 }
1485
1486 case PatternEditor::NotificationReload:
1487 {
1488 pattern = patternEditor->getPattern();
1489 module = patternEditor->getModule();
1490 adjustScrollBarPositionsAndSizes();
1491 validate();
1492 break;
1493 }
1494
1495 case PatternEditor::NotificationChanges:
1496 {
1497 bool lazyUpdateNotifications = patternEditor->getLazyUpdateNotifications();
1498 bool numRowsChanged = patternEditor->getLastOperationDidChangeRows();
1499 bool cursorChanged = patternEditor->getLastOperationDidChangeCursor();
1500
1501 // if this is a lazy notification it means we're only
1502 // notifying that something has changed, instead of refreshing the screen
1503 // which means that the screen is probably refreshed somewhere else
1504 // and the notification itself comes actually from this control
1505 if (lazyUpdateNotifications)
1506 {
1507 notifyChanges();
1508 return;
1509 }
1510
1511 notifyChanges();
1512
1513 if (numRowsChanged)
1514 adjustScrollBarPositionsAndSizes();
1515
1516 if (cursorChanged)
1517 assureCursorVisible();
1518
1519 validate();
1520
1521 parentScreen->paintControl(this, false);
1522 break;
1523 }
1524
1525 case PatternEditor::NotificationFeedUndoData:
1526 {
1527 undoInfo = UndoInfo(startIndex, startPos);
1528 patternEditor->setUndoUserData(&undoInfo, sizeof(undoInfo));
1529 break;
1530 }
1531
1532 case PatternEditor::NotificationFetchUndoData:
1533 if (sizeof(undoInfo) == patternEditor->getUndoUserDataLen())
1534 {
1535 memcpy(&undoInfo, patternEditor->getUndoUserData(), sizeof(undoInfo));
1536
1537 assureCursorVisible();
1538 setScrollbarPositions(undoInfo.startIndex, undoInfo.startPos);
1539 validate();
1540 parentScreen->paintControl(this, false);
1541 notifyUpdate();
1542 }
1543 break;
1544 default:
1545 break;
1546 }
1547 }
1548
1549