Source code: com/virtuosotechnologies/asaph/standardgui/SongBodyEditor.java
1 /*
2 ================================================================================
3
4 FILE: SongBodyEditor.java
5
6 PROJECT:
7
8 Asaph
9
10 CONTENTS:
11
12 The song body editor: block list
13
14 PROGRAMMERS:
15
16 Daniel Azuma (DA) <dazuma@kagi.com>
17
18 COPYRIGHT:
19
20 Copyright (C) 2003 Daniel Azuma (dazuma@kagi.com)
21
22 This program is free software; you can redistribute it and/or
23 modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2
25 of the License, or (at your option) any later version.
26
27 This program is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public
33 License along with this program; if not, write to
34 Free Software Foundation, Inc.
35 59 Temple Place, Suite 330
36 Boston, MA 02111-1307 USA
37
38 ================================================================================
39 */
40
41
42 package com.virtuosotechnologies.asaph.standardgui;
43
44
45 import java.awt.Graphics2D;
46 import java.awt.Rectangle;
47 import java.awt.datatransfer.Transferable;
48 import java.awt.event.MouseEvent;
49 import java.awt.event.KeyEvent;
50 import java.util.List;
51 import java.util.ArrayList;
52 import java.awt.geom.Rectangle2D;
53 import javax.swing.SwingUtilities;
54
55 import com.virtuosotechnologies.asaph.model.Song;
56 import com.virtuosotechnologies.asaph.model.ChordSet;
57 import com.virtuosotechnologies.asaph.model.Variation;
58 import com.virtuosotechnologies.asaph.model.StandardSongBlock;
59 import com.virtuosotechnologies.asaph.model.SongBlock;
60 import com.virtuosotechnologies.asaph.model.SongLine;
61 import com.virtuosotechnologies.asaph.model.notation.Interval;
62 import com.virtuosotechnologies.asaph.modelutils.SongUtils;
63 import com.virtuosotechnologies.asaph.modelutils.DataTransferUtils;
64 import com.virtuosotechnologies.asaph.modelutils.CannotPasteException;
65
66
67 /**
68 * The song body editor
69 */
70 /*package*/ class SongBodyEditor
71 {
72 private Song song_;
73 private SongBodyEditorPane pane_;
74
75 private List blockEditors_;
76 private List blockEditorSizes_;
77 private List blockEditorYs_;
78
79 private BodyEditorSize curSize_;
80
81 private boolean dragging_;
82
83
84 /**
85 * Constructor
86 */
87 /*package*/ SongBodyEditor(
88 SongBodyEditorPane pane,
89 Song song)
90 {
91 pane_ = pane;
92 song_ = song;
93 blockEditors_ = new ArrayList();
94 blockEditorSizes_ = new ArrayList();
95 blockEditorYs_ = new ArrayList();
96 dragging_ = false;
97 }
98
99
100 /**
101 * Get the size of the editor
102 */
103 /*package*/ BodyEditorSize getSize()
104 {
105 return curSize_;
106 }
107
108
109 /**
110 * Rebuild according to the current model and settings
111 */
112 /*package*/ void rebuild(
113 boolean refresh)
114 {
115 blockEditors_.clear();
116 blockEditorSizes_.clear();
117 blockEditorYs_.clear();
118
119 Variation var = pane_.getCurVariation();
120
121 curSize_ = new BodyEditorSize(-EditorConstants.LEFT_MARGIN,
122 EditorConstants.RIGHT_MARGIN+EditorConstants.EMPTY_WIDTH,
123 EditorConstants.BLOCK_SPACING);
124
125 for (SongBlock block = song_.getNextBlock(null, var); block != null;
126 block = song_.getNextBlock(block, var))
127 {
128 SongBlockEditor blockEditor = new SongBlockEditor(pane_, this, block);
129 blockEditor.rebuild(false);
130 blockEditors_.add(blockEditor);
131 blockEditorYs_.add(new Float(curSize_.getBottom()));
132 BodyEditorSize blockSize = blockEditor.getSize();
133 blockEditorSizes_.add(blockSize);
134 curSize_ = curSize_.addSize(
135 blockSize.getLeft()-EditorConstants.LEFT_MARGIN,
136 blockSize.getRight()+EditorConstants.RIGHT_MARGIN,
137 blockSize.getBottom()+EditorConstants.BLOCK_SPACING);
138 }
139
140 pane_.setBodySize(curSize_);
141 }
142
143
144 /**
145 * Get the index of this block
146 */
147 /*package*/ int getBlockIndex(
148 SongBlockEditor blockEditor)
149 {
150 return blockEditors_.indexOf(blockEditor);
151 }
152
153
154 /**
155 * Get the nth block
156 */
157 /*package*/ SongBlockEditor getNthBlock(
158 int n)
159 {
160 return (SongBlockEditor)blockEditors_.get(n);
161 }
162
163
164 /**
165 * Resize for a block
166 */
167 /*package*/ void setBlockSize(
168 SongBlockEditor blockEditor,
169 BodyEditorSize size,
170 boolean refresh)
171 {
172 int index = blockEditors_.indexOf(blockEditor);
173 if (index == -1)
174 {
175 return;
176 }
177 BodyEditorSize oldSize = (BodyEditorSize)blockEditorSizes_.get(index);
178 blockEditorSizes_.set(index, size);
179 float nLeft = curSize_.getLeft();
180 float nRight = curSize_.getRight();
181 float nBottom = curSize_.getBottom();
182 boolean changed = false;
183 if (oldSize.getBottom() != size.getBottom())
184 {
185 float deltaHeight = size.getBottom() - oldSize.getBottom();
186 for (int i=index+1; i<blockEditors_.size(); ++i)
187 {
188 float y = ((Float)blockEditorYs_.get(i)).floatValue();
189 blockEditorYs_.set(i, new Float(y+deltaHeight));
190 }
191 nBottom += deltaHeight;
192 changed = true;
193 }
194 if (oldSize.getLeft() != size.getLeft())
195 {
196 if (size.getLeft() < oldSize.getLeft())
197 {
198 nLeft = Math.min(nLeft, -EditorConstants.LEFT_MARGIN+size.getLeft());
199 }
200 else
201 {
202 nLeft = -EditorConstants.LEFT_MARGIN;
203 for (int i=0; i<blockEditors_.size(); ++i)
204 {
205 BodyEditorSize s = (BodyEditorSize)blockEditorSizes_.get(i);
206 nLeft = Math.min(nLeft, -EditorConstants.LEFT_MARGIN+s.getLeft());
207 }
208 }
209 if (nLeft != curSize_.getLeft())
210 {
211 changed = true;
212 }
213 }
214 if (oldSize.getRight() != size.getRight())
215 {
216 if (size.getRight() > oldSize.getRight())
217 {
218 nRight = Math.max(nRight, EditorConstants.RIGHT_MARGIN+size.getRight());
219 }
220 else
221 {
222 nRight = EditorConstants.RIGHT_MARGIN+EditorConstants.EMPTY_WIDTH;
223 for (int i=0; i<blockEditors_.size(); ++i)
224 {
225 BodyEditorSize s = (BodyEditorSize)blockEditorSizes_.get(i);
226 nRight = Math.max(nRight, EditorConstants.RIGHT_MARGIN+s.getRight());
227 }
228 }
229 if (nRight != curSize_.getRight())
230 {
231 changed = true;
232 }
233 }
234
235 if (changed)
236 {
237 curSize_ = new BodyEditorSize(nLeft, nRight, nBottom);
238 pane_.setBodySize(curSize_);
239 }
240 else if (refresh)
241 {
242 EditorSelection toRefresh = new EditorSelection(null);
243 toRefresh.setBlockSelection(index, index+1, false);
244 pane_.repaintGivenSelection(toRefresh);
245 }
246 }
247
248
249 /**
250 * Draw
251 */
252 /*package*/ void paint(
253 Graphics2D g2d)
254 {
255 EditorSelection selection = pane_.getSelection();
256
257 // Find clipping rectangle
258 Rectangle clipBounds = g2d.getClipBounds();
259 if (clipBounds == null)
260 {
261 clipBounds = new Rectangle(0, 0,
262 (int)(curSize_.getRight()-curSize_.getLeft()+1),
263 (int)(curSize_.getBottom()+1));
264 }
265 else
266 {
267 --clipBounds.x;
268 --clipBounds.y;
269 clipBounds.width += 2;
270 clipBounds.height += 2;
271 }
272
273 // Draw origin line
274 float x = -curSize_.getLeft();
275 g2d.setColor(EditorConstants.ORIGIN_LINE_COLOR);
276 g2d.drawLine((int)x, 0, (int)x, (int)curSize_.getBottom());
277
278 // Draw blocks
279 int num = blockEditors_.size();
280 int selectionTop = selection.getTopBlockPos();
281 int selectionBottom = selection.getBottomBlockPos();
282 for (int i=0; i<num; ++i)
283 {
284 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(i);
285 float y = ((Float)blockEditorYs_.get(i)).floatValue();
286 float bottom = y+((BodyEditorSize)blockEditorSizes_.get(i)).getBottom();
287 if (y < clipBounds.y+clipBounds.height && bottom > clipBounds.y)
288 {
289 blockEditor.paint(g2d, x, y, clipBounds,
290 selectionTop <= i && selectionBottom > i,
291 selection.getContainingBlock() == i);
292 }
293 }
294
295 // Draw caret
296 int sel = selection.getEndBlockPos();
297 if (sel != -1 && pane_.isCaretVisible() && pane_.isFocused())
298 {
299 float y;
300 if (sel == 0)
301 {
302 y = EditorConstants.BLOCK_SPACING*0.5f;
303 }
304 else if (sel == blockEditorYs_.size())
305 {
306 y = ((Float)blockEditorYs_.get(sel-1)).floatValue()+
307 ((BodyEditorSize)blockEditorSizes_.get(sel-1)).getBottom()+
308 EditorConstants.BLOCK_SPACING*0.5f;
309 }
310 else
311 {
312 y = ((Float)blockEditorYs_.get(sel)).floatValue()-
313 EditorConstants.BLOCK_SPACING*0.5f;
314 }
315 g2d.setColor(EditorConstants.BLOCK_CARET_COLOR);
316 g2d.draw(new Rectangle2D.Float(0f, y-0.5f, curSize_.getRight()-curSize_.getLeft(), 1f));
317 }
318 }
319
320
321 /**
322 * Get the update rectangle for the given selection.
323 * Returns a Rectangle2D, or null for no selection.
324 */
325 /*package*/ Rectangle2D getSelectionBounds(
326 EditorSelection selection,
327 boolean blink)
328 {
329 if (!selection.hasSelection())
330 {
331 return null;
332 }
333 else if (selection.isBlockSelection())
334 {
335 int selectionEnd = selection.getEndBlockPos();
336 if (blink || selection.isSinglePointSelection())
337 {
338 float y;
339 if (selectionEnd == 0)
340 {
341 y = 0f;
342 }
343 else if (selectionEnd == blockEditorYs_.size())
344 {
345 y = ((Float)blockEditorYs_.get(selectionEnd-1)).floatValue()+
346 ((BodyEditorSize)blockEditorSizes_.get(selectionEnd-1)).getBottom();
347 }
348 else
349 {
350 y = ((Float)blockEditorYs_.get(selectionEnd)).floatValue()-
351 EditorConstants.BLOCK_SPACING;
352 }
353 return new Rectangle2D.Float(0f, y,
354 curSize_.getRight()-curSize_.getLeft(), EditorConstants.BLOCK_SPACING);
355 }
356 else
357 {
358 int selectionTop = selection.getTopBlockPos();
359 int selectionBottom = selection.getBottomBlockPos();
360 float top = ((Float)blockEditorYs_.get(selectionTop)).floatValue();
361 float bottom = ((Float)blockEditorYs_.get(selectionBottom-1)).floatValue()+
362 ((BodyEditorSize)blockEditorSizes_.get(selectionBottom-1)).getBottom();
363 if (selectionTop == selectionEnd)
364 {
365 top -= EditorConstants.BLOCK_SPACING;
366 }
367 else if (selectionBottom == selectionEnd)
368 {
369 bottom += EditorConstants.BLOCK_SPACING;
370 }
371 return new Rectangle2D.Float(0f, top,
372 curSize_.getRight()-curSize_.getLeft(), bottom-top);
373 }
374 }
375 else
376 {
377 int selectionContainer = selection.getContainingBlock();
378 float x = -curSize_.getLeft();
379 float y = ((Float)blockEditorYs_.get(selectionContainer)).floatValue();
380 return ((SongBlockEditor)blockEditors_.get(selectionContainer)).
381 getSelectionBounds(selection, blink, x, y);
382 }
383 }
384
385
386 /**
387 * Select the top of the block following the current block.
388 * Returns true if successful.
389 * We expect this to be called from the current block because the user is
390 * navigating down using the keyboard.
391 */
392 /*package*/ void selectTopOfNextBlock()
393 {
394 EditorSelection selection = pane_.getSelection();
395 int block = selection.getContainingBlock()+1;
396 if (block < blockEditors_.size())
397 {
398 selection.prepareSelectionInsideBlock(block);
399 ((SongBlockEditor)blockEditors_.get(block)).selectTop();
400 }
401 }
402
403
404 /**
405 * Select the bottom of the block preceding the current block.
406 * Returns true if successful.
407 * We expect this to be called from the current block because the user is
408 * navigating up using the keyboard.
409 */
410 /*package*/ void selectBottomOfPreviousBlock()
411 {
412 EditorSelection selection = pane_.getSelection();
413 int block = selection.getContainingBlock()-1;
414 if (block >= 0)
415 {
416 selection.prepareSelectionInsideBlock(block);
417 ((SongBlockEditor)blockEditors_.get(block)).selectBottom();
418 }
419 }
420
421
422 /**
423 * Move the caret to the last line of the previous block.
424 * We expect this to be called from the current block because the user is
425 * navigating up using the keyboard.
426 */
427 /*package*/ void selectInPreviousLine(
428 float x)
429 {
430 EditorSelection selection = pane_.getSelection();
431 int block = selection.getContainingBlock()-1;
432 while (block >= 0)
433 {
434 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(block);
435 if (blockEditor.hasAtLeastOneLine())
436 {
437 selection.prepareSelectionInsideBlock(block);
438 blockEditor.selectInBottomLine(x);
439 return;
440 }
441 --block;
442 }
443 }
444
445
446 /**
447 * Move the caret to the first line of the next block.
448 * We expect this to be called from the current block because the user is
449 * navigating down using the keyboard.
450 */
451 /*package*/ void selectInNextLine(
452 float x)
453 {
454 EditorSelection selection = pane_.getSelection();
455 int block = selection.getContainingBlock()+1;
456 while (block < blockEditors_.size())
457 {
458 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(block);
459 if (blockEditor.hasAtLeastOneLine())
460 {
461 selection.prepareSelectionInsideBlock(block);
462 blockEditor.selectInTopLine(x);
463 return;
464 }
465 ++block;
466 }
467 }
468
469
470 /**
471 * Handle mouse-down events
472 */
473 /*package*/ void handleMousePressed(
474 MouseEvent ev)
475 {
476 if (SwingUtilities.isLeftMouseButton(ev))
477 {
478 dragging_ = true;
479 EditorSelection selection = pane_.getSelection();
480 int blockIndex = findBlockForPoint(ev.getPoint().y, false);
481 if (blockIndex < 0)
482 {
483 int container = -1-blockIndex;
484 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(container);
485 float x = -curSize_.getLeft();
486 float y = ((Float)blockEditorYs_.get(container)).floatValue();
487 if (blockEditor.handleMousePressed(ev, x, y, container))
488 {
489 return;
490 }
491 float height = ((BodyEditorSize)blockEditorSizes_.get(container)).getBottom();
492 if (ev.getPoint().y > y+height/2)
493 {
494 ++container;
495 }
496 blockIndex = container;
497 }
498 if (ev.isShiftDown() && selection.isBlockSelection())
499 {
500 selection.extendBlockSelection(blockIndex, false);
501 }
502 else
503 {
504 selection.setBlockSelection(blockIndex, blockIndex, false);
505 }
506 }
507 }
508
509
510 /**
511 * Handle mouse-drag events
512 */
513 /*package*/ void handleMouseDragged(
514 MouseEvent ev)
515 {
516 EditorSelection selection = pane_.getSelection();
517 if (dragging_)
518 {
519 if (selection.isBlockSelection())
520 {
521 int blockIndex = findBlockForPoint(ev.getPoint().y, true);
522 selection.extendBlockSelection(blockIndex, false);
523 }
524 else
525 {
526 int container = selection.getContainingBlock();
527 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(container);
528 float x = -curSize_.getLeft();
529 float y = ((Float)blockEditorYs_.get(container)).floatValue();
530 blockEditor.handleMouseDragged(ev, x, y);
531 }
532 }
533 }
534
535
536 /**
537 * Handle mouse-up events
538 */
539 /*package*/ void handleMouseReleased(
540 MouseEvent ev)
541 {
542 if (SwingUtilities.isLeftMouseButton(ev))
543 {
544 dragging_ = false;
545 EditorSelection selection = pane_.getSelection();
546 if (!selection.isBlockSelection())
547 {
548 int container = selection.getContainingBlock();
549 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(container);
550 float x = -curSize_.getLeft();
551 float y = ((Float)blockEditorYs_.get(container)).floatValue();
552 blockEditor.handleMouseReleased(ev, x, y);
553 }
554 }
555 }
556
557
558 /**
559 * Handle key typed events
560 */
561 /*package*/ void handleKey(
562 KeyEvent ev)
563 {
564 EditorSelection selection = pane_.getSelection();
565 if (!selection.hasSelection())
566 {
567 return;
568 }
569 if (selection.isBlockSelection())
570 {
571 if (ev.getID() == KeyEvent.KEY_PRESSED)
572 {
573 switch (ev.getKeyCode())
574 {
575 case KeyEvent.VK_UP:
576 case KeyEvent.VK_KP_UP:
577 {
578 int curEnd = selection.getEndBlockPos();
579 if (ev.isShiftDown())
580 {
581 if (curEnd > 0)
582 {
583 selection.extendBlockSelection(curEnd-1, true);
584 }
585 }
586 else if (selection.isSinglePointSelection())
587 {
588 if (curEnd > 0)
589 {
590 selection.setBlockSelection(curEnd-1, curEnd-1, true);
591 }
592 }
593 else
594 {
595 int pos = selection.getTopBlockPos();
596 selection.setBlockSelection(pos, pos, true);
597 }
598 ev.consume();
599 break;
600 }
601
602 case KeyEvent.VK_DOWN:
603 case KeyEvent.VK_KP_DOWN:
604 {
605 int curEnd = selection.getEndBlockPos();
606 if (ev.isShiftDown())
607 {
608 if (curEnd < blockEditors_.size())
609 {
610 selection.extendBlockSelection(curEnd+1, true);
611 }
612 }
613 else if (selection.isSinglePointSelection())
614 {
615 if (curEnd < blockEditors_.size())
616 {
617 selection.setBlockSelection(curEnd+1, curEnd+1, true);
618 }
619 }
620 else
621 {
622 int pos = selection.getBottomBlockPos();
623 selection.setBlockSelection(pos, pos, true);
624 }
625 ev.consume();
626 break;
627 }
628
629 case KeyEvent.VK_RIGHT:
630 case KeyEvent.VK_KP_RIGHT:
631 {
632 int block = selection.getEndBlockPos();
633 if (block == blockEditors_.size() ||
634 (block == selection.getBottomBlockPos() && !selection.isSinglePointSelection()))
635 {
636 --block;
637 }
638 if (block >= 0)
639 {
640 selection.prepareSelectionInsideBlock(block);
641 ((SongBlockEditor)blockEditors_.get(block)).selectDefault();
642 }
643 ev.consume();
644 break;
645 }
646
647 case KeyEvent.VK_BACK_SPACE:
648 {
649 doDeleteSelection(true);
650 ev.consume();
651 break;
652 }
653
654 case KeyEvent.VK_ENTER:
655 {
656 doEnter();
657 ev.consume();
658 break;
659 }
660
661 case KeyEvent.VK_HOME:
662 case KeyEvent.VK_PAGE_UP:
663 {
664 if (ev.isShiftDown())
665 {
666 selection.extendBlockSelection(0, true);
667 }
668 else
669 {
670 selection.setBlockSelection(0, 0, true);
671 }
672 ev.consume();
673 break;
674 }
675
676 case KeyEvent.VK_END:
677 case KeyEvent.VK_PAGE_DOWN:
678 {
679 if (ev.isShiftDown())
680 {
681 selection.extendBlockSelection(blockEditors_.size(), true);
682 }
683 else
684 {
685 selection.setBlockSelection(blockEditors_.size(),
686 blockEditors_.size(), true);
687 }
688 ev.consume();
689 break;
690 }
691 }
692 }
693 }
694 else
695 {
696 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(selection.getContainingBlock());
697 blockEditor.handleKey(ev, -curSize_.getLeft());
698 }
699 }
700
701
702 /*package*/ class BodyUndoHelper
703 extends UndoHelper
704 {
705 /*package*/ BodyUndoHelper()
706 {
707 super(pane_);
708 }
709
710 protected void rebuildView()
711 {
712 rebuild(true);
713 }
714 }
715
716
717 /*package*/ void doCopy(
718 boolean remove)
719 {
720 EditorSelection selection = pane_.getSelection();
721 if (!selection.hasSelection() || selection.isSinglePointSelection())
722 {
723 return;
724 }
725
726 if (selection.isBlockSelection())
727 {
728 DataTransferUtils dataTransferUtils = pane_.getDataTransferUtils();
729 List blocks = new ArrayList();
730 int top = selection.getTopBlockPos();
731 int bottom = selection.getBottomBlockPos();
732 for (int i=top; i<bottom; ++i)
733 {
734 blocks.add(song_.getNthBlock(i, pane_.getCurVariation()));
735 }
736
737 Transferable transfer;
738 if (remove)
739 {
740 EditorSelection newSelection = selection.createCopy();
741 newSelection.setBlockSelection(top, top, false);
742 UndoHelper helper = new BodyUndoHelper();
743 transfer = dataTransferUtils.createBlockListTransferable(
744 song_, blocks, pane_.getCurChordSet(), pane_.getCurVariation(),
745 true, helper);
746 helper.finish(newSelection);
747 }
748 else
749 {
750 transfer = dataTransferUtils.createBlockListTransferable(
751 song_, blocks, pane_.getCurChordSet(), pane_.getCurVariation(),
752 false, null);
753 }
754 dataTransferUtils.setClipboardContents(transfer);
755 }
756 else
757 {
758 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(
759 selection.getContainingBlock());
760 blockEditor.doCopy(remove);
761 }
762 }
763
764
765 /*package*/ void doPaste()
766 {
767 EditorSelection selection = pane_.getSelection();
768 if (!selection.hasSelection())
769 {
770 return;
771 }
772
773 if (selection.isBlockSelection())
774 {
775 DataTransferUtils dataTransferUtils = pane_.getDataTransferUtils();
776 EditorSelection newSelection = selection.createCopy();
777 int pos = selection.getTopBlockPos();
778 int len = selection.getBottomBlockPos()-pos;
779
780 UndoHelper helper = new BodyUndoHelper();
781
782 Variation var = pane_.getCurVariation();
783
784 SongBlock prevBlock;
785 if (pos == song_.getBlockCount(var))
786 {
787 prevBlock = song_.getPreviousBlock(null, var);
788 }
789 else
790 {
791 prevBlock = song_.getPreviousBlock(song_.getNthBlock(pos, var), var);
792 }
793 for (int i=0; i<len; ++i)
794 {
795 SongBlock block = song_.getNextBlock(prevBlock, var);
796 if (var == null)
797 {
798 song_.removeBlock(block, helper);
799 }
800 else
801 {
802 if (block instanceof StandardSongBlock)
803 {
804 ((StandardSongBlock)block).omitVariation(var, helper);
805 }
806 else //if (block instanceof AddedSongBlock)
807 {
808 song_.removeBlock(block, helper);
809 }
810 }
811 }
812
813 int ocount = song_.getBlockCount(var);
814 try
815 {
816 dataTransferUtils.pasteTransferableAfter(
817 dataTransferUtils.getClipboardContents(), song_, prevBlock,
818 pane_.getCurChordSet(), pane_.getCurVariation(), helper);
819 }
820 catch (CannotPasteException ex)
821 {
822 }
823 pos += (song_.getBlockCount(var)-ocount);
824 newSelection.setBlockSelection(pos, pos, false);
825
826 helper.finish(newSelection);
827 }
828 else
829 {
830 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(
831 selection.getContainingBlock());
832 blockEditor.doPaste();
833 }
834 }
835
836
837 /*package*/ void doInsert()
838 {
839 EditorSelection selection = pane_.getSelection();
840
841 if (selection.isBlockSelection())
842 {
843 EditorSelection newSelection = selection.createCopy();
844 int pos = selection.getEndBlockPos();
845 newSelection.setBlockSelection(pos, pos+1, false);
846
847 UndoHelper helper = new BodyUndoHelper();
848
849 SongBlock nblock;
850 if (pos == 0)
851 {
852 nblock = song_.insertBlockAfter(null, pane_.getCurVariation(), helper);
853 }
854 else
855 {
856 SongBlock block = song_.getNthBlock(pos-1, pane_.getCurVariation());
857 nblock = song_.insertBlockAfter(block, pane_.getCurVariation(), helper);
858 }
859 SongLine nline = nblock.insertLineAfter(null, helper);
860 nline.insertTextStringAfter(null, "", helper);
861
862 helper.finish(newSelection);
863 }
864 else
865 {
866 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(
867 selection.getContainingBlock());
868 blockEditor.doInsert();
869 }
870 }
871
872
873 /*package*/ void doComment(
874 boolean comment)
875 {
876 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(
877 pane_.getSelection().getContainingBlock());
878 blockEditor.doComment(comment);
879 }
880
881
882 /*package*/ void doDeleteSelection(
883 boolean backspace)
884 {
885 EditorSelection selection = pane_.getSelection();
886
887 if (!selection.hasSelection())
888 {
889 return;
890 }
891
892 if (selection.isBlockSelection())
893 {
894 int pos;
895 int len;
896 if (selection.isSinglePointSelection())
897 {
898 if (!backspace)
899 {
900 return;
901 }
902 pos = selection.getStartBlockPos()-1;
903 if (pos < 0)
904 {
905 return;
906 }
907 len = 1;
908 }
909 else
910 {
911 pos = selection.getTopBlockPos();
912 len = selection.getBottomBlockPos()-pos;
913 }
914
915 EditorSelection newSelection = selection.createCopy();
916 newSelection.setBlockSelection(pos, pos, false);
917
918 UndoHelper helper = new BodyUndoHelper();
919
920 Variation var = pane_.getCurVariation();
921 for (int i=0; i<len; ++i)
922 {
923 SongBlock block = song_.getNthBlock(pos, var);
924 if (var == null)
925 {
926 song_.removeBlock(block, helper);
927 }
928 else
929 {
930 if (block instanceof StandardSongBlock)
931 {
932 ((StandardSongBlock)block).omitVariation(var, helper);
933 }
934 else //if (block instanceof AddedSongBlock)
935 {
936 song_.removeBlock(block, helper);
937 }
938 }
939 }
940
941 helper.finish(newSelection);
942 }
943 else
944 {
945 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(
946 selection.getContainingBlock());
947 blockEditor.doDeleteSelection(backspace);
948 }
949 }
950
951
952 /*package*/ boolean canInsertChordMarkingHere()
953 {
954 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(
955 pane_.getSelection().getContainingBlock());
956 return blockEditor.canInsertChordMarkingHere();
957 }
958
959
960 /*package*/ void doShift(
961 int shift)
962 {
963 EditorSelection selection = pane_.getSelection();
964
965 if (!selection.hasSelection())
966 {
967 return;
968 }
969
970 if (selection.isBlockSelection() ||
971 (selection.isLineSelection() && selection.isSinglePointSelection()))
972 {
973 int start;
974 int end;
975 if (selection.isBlockSelection())
976 {
977 start = selection.getTopBlockPos();
978 end = selection.getBottomBlockPos();
979 }
980 else
981 {
982 start = selection.getContainingBlock();
983 end = start+1;
984 }
985 Variation variation = pane_.getCurVariation();
986
987 UndoHelper helper = new BodyUndoHelper();
988
989 for (int i=start; i<end; ++i)
990 {
991 SongBlock block = song_.getNthBlock(i, variation);
992 block.setIndentLevel(block.getIndentLevel()+shift, helper);
993 }
994
995 helper.finish(null);
996 }
997 else
998 {
999 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(
1000 pane_.getSelection().getContainingBlock());
1001 blockEditor.doShift(shift);
1002 }
1003 }
1004
1005
1006 /*package*/ void doRestoreOmitted(
1007 Variation variation)
1008 {
1009 if (variation == null)
1010 {
1011 return;
1012 }
1013
1014 EditorSelection selection = pane_.getSelection();
1015 EditorSelection nselection = selection.createCopy();
1016 nselection.setBlockSelection(0, 0, false); // TODO: be smarter
1017
1018 UndoHelper helper = new BodyUndoHelper();
1019
1020 for (SongBlock block = song_.getNextBlock(null); block != null;
1021 block = song_.getNextBlock(block))
1022 {
1023 if (block instanceof StandardSongBlock)
1024 {
1025 StandardSongBlock standardBlock = (StandardSongBlock)block;
1026 if (standardBlock.getOmittingVariations().contains(variation))
1027 {
1028 standardBlock.unOmitVariation(variation, helper);
1029 }
1030 }
1031 }
1032
1033 helper.finish(nselection);
1034 }
1035
1036
1037 /*package*/ void doTransposeSelection(
1038 ChordSet chordSet,
1039 Interval interval)
1040 {
1041 EditorSelection selection = pane_.getSelection();
1042 SongUtils songUtils = pane_.getSongUtils();
1043 Variation variation = pane_.getCurVariation();
1044
1045 if (selection.isBlockSelection())
1046 {
1047 int start = selection.getTopBlockPos();
1048 int end = selection.getBottomBlockPos();
1049
1050 UndoHelper helper = new BodyUndoHelper();
1051
1052 for (int i=start; i<end; ++i)
1053 {
1054 SongBlock block = song_.getNthBlock(i, variation);
1055 songUtils.transpose(block, chordSet, interval, helper);
1056 }
1057
1058 helper.finish(null);
1059 }
1060 else if (selection.isLineSelection())
1061 {
1062 int blockNum = pane_.getSelection().getContainingBlock();
1063 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(blockNum);
1064 SongBlock block = song_.getNthBlock(blockNum, variation);
1065
1066 int pos = selection.getTopLinePos();
1067 int len = selection.getBottomLinePos()-pos;
1068
1069 UndoHelper helper = blockEditor.new BlockUndoHelper();
1070
1071 SongLine line = block.getNthLine(pos);
1072 for (int i=0; i<len; ++i)
1073 {
1074 songUtils.transpose(line, chordSet, interval, helper);
1075 if (i < len-1)
1076 {
1077 line = block.getNextLine(line);
1078 }
1079 }
1080
1081 helper.finish(null);
1082 }
1083 }
1084
1085
1086 /*package*/ void doSelectAll()
1087 {
1088 EditorSelection selection = pane_.getSelection();
1089
1090 if (!selection.hasSelection() || selection.isBlockSelection())
1091 {
1092 selection.setBlockSelection(0, blockEditors_.size(), false);
1093 }
1094 else
1095 {
1096 SongBlockEditor blockEditor = (SongBlockEditor)blockEditors_.get(
1097 pane_.getSelection().getContainingBlock());
1098 blockEditor.doSelectAll();
1099 }
1100 }
1101
1102
1103 private void doEnter()
1104 {
1105 // TODO
1106 }
1107
1108
1109 /**
1110 * Utility determining where a mouse press happened.
1111 * If the hit is between blocks, the nonnegative position is returned.
1112 * If the hit is on a block, -1-index is returned.
1113 */
1114 private int findBlockForPoint(
1115 float y,
1116 boolean forceBetween)
1117 {
1118 int numBlocks = blockEditorYs_.size();
1119 for (int i=0; i<numBlocks; ++i)
1120 {
1121 float by = ((Float)blockEditorYs_.get(i)).floatValue();
1122 if (y < by)
1123 {
1124 return i;
1125 }
1126 float height = ((BodyEditorSize)blockEditorSizes_.get(i)).getBottom();
1127 if (y < by+height)
1128 {
1129 if (forceBetween)
1130 {
1131 if (y < by+height/2)
1132 {
1133 return i;
1134 }
1135 else
1136 {
1137 return i+1;
1138 }
1139 }
1140 return -1-i;
1141 }
1142 }
1143 return numBlocks;
1144 }
1145}