Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/Classes/PianoRollView.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 199 - (show annotations) (download)
Mon Apr 4 14:50:52 2022 UTC (2 years, 1 month ago) by toshinagata1964
File size: 37372 byte(s)
Vertical grids are shown in strip chart view
1 /*
2 PianoRollView.m
3 */
4 /*
5 Copyright (c) 2000-2017 Toshi Nagata. All rights reserved.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation version 2 of the License.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 */
16
17 #import "PianoRollView.h"
18 #import "GraphicWindowController.h"
19 #import "StripChartView.h"
20 #import "MyDocument.h"
21 #import "MyMIDISequence.h"
22 #import "MDObjects.h"
23 #import "NSEventAdditions.h"
24 #import "NSCursorAdditions.h"
25
26 #include <math.h> /* for floor(), ceil() */
27
28 @implementation PianoRollView
29
30 - (id)initWithFrame: (NSRect)rect
31 {
32 self = [super initWithFrame: rect];
33 if (self) {
34 autoScaleOnResizing = NO;
35 }
36 return self;
37 }
38
39 - (void)dealloc
40 {
41 [super dealloc];
42 }
43
44 - (int)clientViewType
45 {
46 return kGraphicPianoRollViewType;
47 }
48
49 - (void)drawStavesInRect: (NSRect)aRect
50 {
51 int index, n, i;
52 NSPoint pt1, pt2;
53 float ys, limitx, startx, endx;
54 NSBezierPath *staves, *subStaves, *path;
55
56 staves = [[NSBezierPath allocWithZone: [self zone]] init];
57 subStaves = [[NSBezierPath allocWithZone: [self zone]] init];
58 limitx = [dataSource sequenceDuration] * [dataSource pixelsPerTick];
59 index = 0;
60 /* Line start/end points are set to multiples of sDashWidth to avoid complication of calculating appropriate phase for setLineDash: */
61 startx = (float)(gDashWidth * floor(aRect.origin.x / gDashWidth));
62 endx = (float)(gDashWidth * ceil((aRect.origin.x + aRect.size.width) / gDashWidth + 1.0));
63 ys = [self yScale];
64 for (i = 0; i < 2; i++) {
65 if (i == 0) {
66 if (limitx >= aRect.origin.x + aRect.size.width)
67 continue;
68 [[NSColor grayColor] set];
69 pt1.x = gDashWidth * (float)floor(limitx / gDashWidth);
70 pt2.x = endx;
71 } else {
72 [staves removeAllPoints];
73 [subStaves removeAllPoints];
74 [[NSColor blackColor] set];
75 pt1.x = startx;
76 if (limitx < aRect.origin.x + aRect.size.width)
77 pt2.x = limitx;
78 else pt2.x = endx;
79 }
80 for (index = -17; index <= +19; index++) {
81 n = MDEventStaffIndexToNoteNumber(index);
82 if (n < 0 || n >= 128)
83 continue;
84 path = ((n >= 43 && n <= 77 && n != 60) ? staves : subStaves);
85 pt1.y = (CGFloat)(floor((n + 0.5) * ys) + 0.5);
86 pt2.y = pt1.y;
87 [path moveToPoint: pt1];
88 [path lineToPoint: pt2];
89 }
90 [staves setLineDash: gLineDash1 count: 2 phase: 0.0f];
91 [subStaves setLineDash: gLineDash2 count: 2 phase: 0.0f];
92 [staves stroke];
93 [subStaves stroke];
94 }
95 [staves release];
96 [subStaves release];
97 }
98
99 - (void)cacheNotesBeforeTick: (MDTickType)tick
100 {
101 int num, trackNum, i;
102 MDTrack *track;
103 IntGroup *pset;
104 // MDPointer *pt;
105 // MDEvent *ep;
106 [cacheArray release];
107 num = [self visibleTrackCount];
108 cacheArray = [[NSMutableArray allocWithZone: [self zone]] initWithCapacity: num + 1];
109 cacheTick = tick;
110 for (i = 0; i <= num; i++) {
111 if (i == num) {
112 track = [[[dataSource document] myMIDISequence] recordTrack];
113 if (track == NULL)
114 break;
115 trackNum = -1;
116 } else {
117 trackNum = [self sortedTrackNumberAtIndex: i];
118 track = [[[dataSource document] myMIDISequence] getTrackAtIndex: trackNum];
119 }
120 #if 1
121 pset = MDTrackSearchEventsWithDurationCrossingTick(track, tick);
122 // fprintf(stderr, "%s[%d]: track %p tick %ld, ", __FILE__, __LINE__, track, (int32_t)tick); IntGroupDump(pset);
123 #else
124 pset = IntGroupNew();
125 if (pset != NULL) {
126 pt = MDPointerNew(track);
127 if (pt != NULL) {
128 MDPointerJumpToTick(pt, tick);
129 while ((ep = MDPointerBackward(pt)) != NULL) {
130 if (MDGetKind(ep) == kMDEventNote && MDGetTick(ep) + MDGetDuration(ep) >= tick)
131 IntGroupAdd(pset, MDPointerGetPosition(pt), 1);
132 }
133 MDPointerRelease(pt);
134 }
135 }
136 // fprintf(stderr, "%s[%d]: ", __FILE__, __LINE__);
137 // IntGroupDump(pset);
138 #endif
139 [cacheArray addObject: [[[IntGroupObject allocWithZone: [self zone]] initWithMDPointSet: pset] autorelease]];
140 if (pset != NULL)
141 IntGroupRelease(pset);
142 }
143 }
144
145 static void
146 appendNotePath(NSBezierPath *path, float x1, float x2, float y, float ys)
147 {
148 if (x2 - x1 <= ys) {
149 [path appendBezierPathWithOvalInRect: NSMakeRect(x1, y, x2 - x1, ys)];
150 } else {
151 float ys2 = ys * 0.5f;
152 [path moveToPoint: NSMakePoint(x1 + ys2, y)];
153 [path lineToPoint: NSMakePoint(x2 - ys2, y)];
154 [path appendBezierPathWithArcWithCenter: NSMakePoint(x2 - ys2, y + ys2) radius: ys2 startAngle: -90.0f endAngle: 90.0f];
155 [path lineToPoint: NSMakePoint(x1 + ys2, y + ys)];
156 [path appendBezierPathWithArcWithCenter: NSMakePoint(x1 + ys2, y + ys2) radius: ys2 startAngle: 90.0f endAngle: -90.0f];
157 [path closePath];
158 }
159 }
160
161 - (void)drawNotesInRect: (NSRect)aRect selectionOnly: (BOOL)selectionOnly offset: (NSPoint)offset addDuration: (MDTickType)addDuration
162 {
163 int num, i;
164 NSBezierPath *normalPath, *selectedPath;
165 NSRect rect = [self visibleRect];
166 float ppt = [dataSource pixelsPerTick];
167 float ys = [self yScale];
168 MyDocument *document = (MyDocument *)[dataSource document];
169 MDTickType originTick = (MDTickType)(rect.origin.x / ppt);
170 MDTickType startTick, endTick;
171 int startNote, endNote;
172 MDTickType duration;
173 static CGFloat sDash[] = { 2, 2 };
174 BOOL isDraggingImage = NO;
175
176 if (draggingMode > 0 && draggingMode < 3 && draggingImage != nil)
177 isDraggingImage = YES;
178
179 num = [self visibleTrackCount];
180 if (cacheArray == nil || originTick != cacheTick)
181 [self cacheNotesBeforeTick: originTick];
182 normalPath = [[NSBezierPath allocWithZone: [self zone]] init];
183 selectedPath = [[NSBezierPath allocWithZone: [self zone]] init];
184 if (isDraggingImage)
185 [selectedPath setLineDash:sDash count:2 phase:0.0f];
186 startTick = (MDTickType)(aRect.origin.x / ppt);
187 endTick = (MDTickType)((aRect.origin.x + aRect.size.width) / ppt);
188 // NSLog(@"drawNotesInRect: startTick %ld endTick %ld", (int32_t)startTick, (int32_t)endTick);
189 startNote = (int)floor(aRect.origin.y / ys);
190 endNote = (int)ceil((aRect.origin.y + aRect.size.height) / ys);
191 for (i = num; i >= 0; i--) {
192 int trackNum;
193 int n;
194 MDTrack *track;
195 IntGroup *pset;
196 MDPointer *pt;
197 MDEvent *ep;
198 NSColor *color;
199 float x1, x2, y;
200 BOOL isFocus;
201 if (i == num) {
202 track = [[document myMIDISequence] recordTrack];
203 if (track == NULL)
204 continue;
205 trackNum = -1;
206 isFocus = NO;
207 } else {
208 trackNum = [self sortedTrackNumberAtIndex: i];
209 track = [[document myMIDISequence] getTrackAtIndex: trackNum];
210 if (track == NULL)
211 continue;
212 isFocus = [dataSource isFocusTrack: trackNum];
213 }
214 if (i >= [cacheArray count])
215 continue; /* Nothing to draw */
216 pset = [[cacheArray objectAtIndex: i] pointSet];
217 pt = MDPointerNew(track);
218 if (pt != NULL)
219 MDPointerSetPositionWithPointSet(pt, pset, -1, &n);
220 while (pt != NULL) { /* Infinite loop */
221 MDTickType tick2;
222 int note;
223 NSBezierPath *path;
224 BOOL selected;
225 /* Loop the note events; first in pset, then from originTick */
226 if (pset != NULL) {
227 while ((ep = MDPointerForwardWithPointSet(pt, pset, &n)) != NULL && MDGetKind(ep) != kMDEventNote)
228 ;
229 // fprintf(stderr, "%s[%d]: ep=%p pos=%ld n=%ld\n", __FILE__, __LINE__, ep, (int32_t)MDPointerGetPosition(pt), (int32_t)n);
230 if (ep == NULL) {
231 pset = NULL;
232 MDPointerJumpToTick(pt, originTick);
233 MDPointerBackward(pt);
234 continue;
235 }
236 } else {
237 while ((ep = MDPointerForward(pt)) != NULL && MDGetTick(ep) < endTick && MDGetKind(ep) != kMDEventNote)
238 ;
239 }
240 if (ep == NULL || MDGetTick(ep) >= endTick) {
241 break;
242 }
243 if (trackNum >= 0)
244 selected = [document isSelectedAtPosition: MDPointerGetPosition(pt) inTrack: trackNum];
245 else selected = NO;
246 duration = MDGetDuration(ep);
247 if (addDuration != 0 && selected) {
248 duration += addDuration;
249 if (duration <= 0)
250 duration = 1;
251 else if (duration >= kMDMaxTick / 2)
252 duration = kMDMaxTick / 2;
253 }
254 if ((tick2 = MDGetTick(ep) + duration) < startTick || (note = MDGetCode(ep)) < startNote || note > endNote)
255 continue; /* Need not draw this one */
256 x1 = (float)floor(MDGetTick(ep) * ppt);
257 x2 = (float)floor(tick2 * ppt);
258 y = (float)floor(note * ys + 0.5);
259 // if (isDragging && !pencilOn && !isLoupeDragging && draggingMode == 0 && isFocus) {
260 if (isFocus && !isLoupeDragging && selectionPath != nil) {
261 /* Change selection by dragging */
262 if ([self isPointInSelectRegion: NSMakePoint(x1, y + 0.5f * ys)]) {
263 if (currentModifierFlags & NSShiftKeyMask)
264 selected = !selected;
265 else
266 selected = YES;
267 }
268 }
269 if (selectionOnly && !selected)
270 continue; /* Need not draw this one */
271 if (selected)
272 path = selectedPath;
273 else path = normalPath;
274 x1 += offset.x;
275 x2 += offset.x;
276 y += offset.y;
277 appendNotePath(path, x1, x2, y, ys);
278 /* if (x2 - x1 <= ys) {
279 [path appendBezierPathWithOvalInRect: NSMakeRect(x1, y, x2 - x1, ys)];
280 } else {
281 float ys2 = ys * 0.5;
282 [path moveToPoint: NSMakePoint(x1 + ys2, y)];
283 [path lineToPoint: NSMakePoint(x2 - ys2, y)];
284 [path appendBezierPathWithArcWithCenter: NSMakePoint(x2 - ys2, y + ys2) radius: ys2 startAngle: -90.0 endAngle: 90.0];
285 [path lineToPoint: NSMakePoint(x1 + ys2, y + ys)];
286 [path appendBezierPathWithArcWithCenter: NSMakePoint(x1 + ys2, y + ys2) radius: ys2 startAngle: 90.0 endAngle: -90.0];
287 [path closePath];
288 } */
289 }
290 if (pencilOn && isFocus) {
291 /* Drawing note */
292 /* x1 = draggingStartPoint.x;
293 x2 = draggingPoint.x;
294 if (x2 < x1) {
295 x2 = x1;
296 x1 = draggingPoint.x;
297 } else if (x2 == x1) {
298 x2 = x1 + [dataSource pixelsPerQuarter];
299 }
300 y = floor(draggingStartPoint.y / ys) * ys; */
301 x1 = draggingPoint.x;
302 x2 = x1 + [dataSource pixelsPerQuarter];
303 y = (float)(floor(draggingPoint.y / ys) * ys);
304 appendNotePath(selectedPath, x1, x2, y, ys);
305 }
306 color = [document colorForTrack: (trackNum >= 0 ? trackNum : 0) enabled: isFocus];
307 [[NSColor whiteColor] set];
308 [selectedPath fill];
309 [color set];
310 [selectedPath stroke];
311 if (!selectionOnly)
312 [normalPath fill];
313 [selectedPath removeAllPoints];
314 [normalPath removeAllPoints];
315 }
316 [selectedPath release];
317 [normalPath release];
318 }
319
320 - (void)drawContentsInRect: (NSRect)aRect
321 {
322 MDTickType deltaDuration;
323 if (draggingMode == 3)
324 deltaDuration = (MDTickType)floor((MDTickType)((draggingPoint.x - draggingStartPoint.x) / [dataSource pixelsPerTick]) + 0.5);
325 else deltaDuration = 0;
326 NSEraseRect(aRect);
327 [self paintEditingRange: aRect startX: NULL endX: NULL];
328 [self drawVerticalLinesInRect: aRect];
329 [self drawStavesInRect: aRect];
330 [self drawNotesInRect: aRect selectionOnly: NO offset: NSMakePoint(0,0) addDuration: deltaDuration];
331 if (draggingMode > 0) {
332 if (draggingImage != nil) {
333 NSSize size;
334 if (draggingMode == 3) {
335 // Changing duration
336 // rect = NSMakeRect(0, 0, aRect.size.width, aRect.size.height);
337 // [draggingImage lockFocus];
338 // [[NSColor clearColor] set];
339 // NSRectFill(rect);
340 // [self drawNotesInRect: aRect selectionOnly: YES offset: NSMakePoint(-aRect.origin.x, -aRect.origin.y) addDuration: (MDTickType)((draggingPoint.x - draggingStartPoint.x) / [dataSource pixelsPerTick])];
341 // [draggingImage unlockFocus];
342 // [draggingImage dissolveToPoint: aRect.origin fromRect: rect fraction: 0.8];
343 } else {
344 size = [draggingImage size];
345 [draggingImage drawAtPoint:NSMakePoint(draggingPoint.x - size.width / 2, draggingPoint.y - size.height / 2) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:0.8f];
346 }
347 }
348 }
349 [self drawSelectRegion];
350 if (rubbingArray != nil) {
351 NSPoint pt;
352 pt.x = (CGFloat)(floor(rubbingTick * [dataSource pixelsPerTick]) + 0.5);
353 pt.y = aRect.origin.y;
354 [[NSColor blueColor] set];
355 [NSBezierPath strokeLineFromPoint:pt toPoint:NSMakePoint(pt.x, pt.y + aRect.size.height)];
356 }
357 }
358
359 - (void)reloadData
360 {
361 [super reloadData];
362 [cacheArray release];
363 cacheArray = nil;
364 }
365
366 - (NSRect)willInvalidateSelectRect: (NSRect)rect
367 {
368 float ys, y0, y1;
369 NSRect visibleRect;
370 /* Redraw whole horizontal area, to avoid partial redrawing of a single note */
371 ys = [self yScale];
372 y0 = (float)(floor(rect.origin.y / ys)) * ys - 1;
373 y1 = (float)(ceil((rect.origin.y + rect.size.height) / ys) + 1.0) * ys + 1;
374 rect.origin.y = y0;
375 rect.size.height = y1 - y0;
376 visibleRect = [self visibleRect];
377 rect.origin.x = visibleRect.origin.x;
378 rect.size.width = visibleRect.size.width;
379 return rect;
380 }
381
382 /* Returns 0-3; 0: no note, 1: left 1/3 of a note, 2: middle 1/3 of a note,
383 3: right 1/3 of a note */
384 - (int)findNoteUnderPoint: (NSPoint)aPoint track: (int32_t *)outTrack position: (int32_t *)outPosition mdEvent: (MDEvent **)outEvent
385 {
386 int num, i;
387 NSRect rect = [self visibleRect];
388 float ppt = [dataSource pixelsPerTick];
389 float ys = [self yScale];
390 MyDocument *document = (MyDocument *)[dataSource document];
391 MDTickType originTick = (MDTickType)(rect.origin.x / ppt);
392 MDTickType limitTick = (MDTickType)((rect.origin.x + rect.size.width) / ppt);
393 MDTickType theTick;
394 int theNote;
395
396 num = [self visibleTrackCount];
397 theTick = (MDTickType)(aPoint.x / ppt);
398 theNote = (int)floor(aPoint.y / ys);
399 for (i = 0; i < num; i++) {
400 int trackNum;
401 int n;
402 MDTrack *track;
403 IntGroup *pset;
404 MDPointer *pt;
405 MDEvent *ep;
406 MDTickType duration;
407 trackNum = [self sortedTrackNumberAtIndex: i];
408 if (![self isFocusTrack:trackNum])
409 continue;
410 track = [[document myMIDISequence] getTrackAtIndex: trackNum];
411 if (track == NULL)
412 continue;
413 pt = MDPointerNew(track);
414 if (pt == NULL)
415 break;
416 // pset = [[cacheArray objectAtIndex: i] pointSet];
417 // MDPointerSetPositionWithPointSet(pt, pset, -1, &n);
418 pset = NULL;
419 MDPointerJumpToTick(pt, theTick);
420 while (1) {
421 MDTickType tick2;
422 if (pset == NULL) {
423 ep = MDPointerBackward(pt);
424 if (ep != NULL && MDGetTick(ep) < originTick)
425 ep = NULL;
426 } else {
427 ep = MDPointerBackwardWithPointSet(pt, pset, &n);
428 }
429 if (ep == NULL) {
430 if (pset == NULL && cacheArray != nil && (pset = [[cacheArray objectAtIndex: i] pointSet]) != NULL) {
431 MDPointerForward(pt); // We should retry the last 'failed' event
432 n = -1; // The next MDPointerBackwardWithPointSet() will give the last event in pset
433 continue;
434 }
435 else break; // not found in this track
436 }
437 if (MDGetKind(ep) != kMDEventNote || MDGetCode(ep) != theNote || (tick2 = theTick - MDGetTick(ep)) > MDGetDuration(ep))
438 continue;
439 if (outTrack != NULL)
440 *outTrack = trackNum;
441 if (outPosition != NULL)
442 *outPosition = MDPointerGetPosition(pt);
443 if (outEvent != NULL)
444 *outEvent = ep;
445 MDPointerRelease(pt);
446 duration = MDGetDuration(ep);
447 // The return value is determined by the visible part of the note
448 if (MDGetTick(ep) + duration > limitTick) {
449 duration = limitTick - MDGetTick(ep);
450 }
451 if (MDGetTick(ep) < originTick) {
452 tick2 = theTick - originTick;
453 duration = MDGetTick(ep) + duration - originTick;
454 }
455 if (tick2 < duration / 3)
456 return 1;
457 else if (tick2 < duration * 2 / 3)
458 return 2;
459 else return 3;
460 }
461 MDPointerRelease(pt);
462 }
463 return 0;
464 }
465
466 - (NSRect)boundRectForSelection
467 {
468 int i, n;
469 short note, minNote, maxNote;
470 MDTickType tick, minTick, maxTick;
471 float ys, ppt;
472 MyDocument *document = [dataSource document];
473 minTick = kMDMaxTick;
474 maxTick = kMDNegativeTick;
475 minNote = 128;
476 maxNote = -1;
477 ys = [self yScale];
478 ppt = [dataSource pixelsPerTick];
479 for (i = 0; (n = [self sortedTrackNumberAtIndex: i]) >= 0; i++) {
480 int index;
481 MDPointer *pt;
482 MDEvent *ep;
483 MDTrack *track = [[document myMIDISequence] getTrackAtIndex: n];
484 IntGroup *pset = [[document selectionOfTrack: n] pointSet];
485 if (track == NULL || pset == NULL)
486 continue;
487 pt = MDPointerNew(track);
488 if (pt == NULL)
489 break;
490 MDPointerSetPositionWithPointSet(pt, pset, -1, &index);
491 while ((ep = MDPointerForwardWithPointSet(pt, pset, &index)) != NULL) {
492 if (MDGetKind(ep) != kMDEventNote)
493 continue;
494 note = MDGetCode(ep);
495 tick = MDGetTick(ep);
496 if (note < minNote)
497 minNote = note;
498 if (note > maxNote)
499 maxNote = note;
500 if (tick < minTick)
501 minTick = tick;
502 if (tick + MDGetDuration(ep) > maxTick)
503 maxTick = tick + MDGetDuration(ep);
504 }
505 }
506 if (minTick > maxTick || minNote > maxNote) {
507 return NSMakeRect(0, 0, 0, 0);
508 } else {
509 return NSMakeRect(minTick * ppt, minNote * ys, (maxTick - minTick) * ppt, (maxNote - minNote + 1) * ys);
510 }
511 }
512
513 - (void)setDraggingCursor: (int)mode
514 {
515 switch (mode) {
516 case 1:
517 [[NSCursor horizontalMoveCursor] set];
518 break;
519 case 2:
520 [[NSCursor verticalMoveCursor] set];
521 break;
522 case 3:
523 [[NSCursor stretchCursor] set];
524 break;
525 case 17:
526 [[NSCursor horizontalMovePlusCursor] set];
527 break;
528 case 18:
529 [[NSCursor verticalMovePlusCursor] set];
530 break;
531 default:
532 [[NSCursor arrowCursor] set];
533 break;
534 }
535 }
536
537 - (void)invalidateDraggingRegion
538 {
539 NSSize size;
540 NSRect rect;
541 if (pencilOn) {
542 float ys = [self yScale];
543 /* rect.origin.x = draggingStartPoint.x;
544 rect.origin.y = floor(draggingStartPoint.y / ys) * ys;
545 rect.size.width = draggingPoint.x - draggingStartPoint.x;
546 if (rect.size.width < 0) {
547 rect.origin.x += rect.size.width;
548 rect.size.width = -rect.size.width;
549 } else if (rect.size.width == 0.0) {
550 rect.size.width = [dataSource pixelsPerQuarter];
551 } */
552 rect.origin.x = draggingPoint.x;
553 rect.origin.y = (CGFloat)(floor(draggingPoint.y / ys) * ys);
554 rect.size.width = [dataSource pixelsPerQuarter];
555 rect.size.height = ys;
556 rect = NSInsetRect(rect, -1, -1);
557 } else if (draggingMode > 0 && draggingImage != nil) {
558 size = [draggingImage size];
559 rect.origin.x = draggingPoint.x - size.width / 2;
560 rect.origin.y = draggingPoint.y - size.height / 2;
561 rect.size = size;
562 rect = NSIntersectionRect(rect, [self visibleRect]);
563 } else return;
564 [self setNeedsDisplayInRect: rect];
565 }
566
567 - (NSString *)infoTextForMousePoint:(NSPoint)pt dragging:(BOOL)flag option:(int *)option
568 {
569 int theNote;
570 int32_t measure, beat, tick;
571 char buf[8];
572 NSString *tickString;
573 float ys = [self yScale];
574 if (draggingMode > 0) {
575 theNote = initialDraggedValue + deltaDraggedValue;
576 if (option != NULL)
577 *option = 1;
578 } else {
579 theNote = (int)floor(pt.y / ys);
580 if (option != NULL)
581 *option = 0;
582 }
583 MDEventNoteNumberToNoteName(theNote, buf);
584 switch (draggingMode) {
585 case 1:
586 case 2:
587 [dataSource convertTick:initialDraggedTick + deltaDraggedTick toMeasure:&measure beat:&beat andTick:&tick];
588 tickString = [NSString stringWithFormat:@"%d.%d.%d", measure, beat, tick];
589 break;
590 case 3:
591 tickString = [NSString stringWithFormat:@"%d", initialDraggedTick + deltaDraggedTick];
592 break;
593 default:
594 tickString = [super infoTextForMousePoint:pt dragging:flag option:option];
595 break;
596 }
597 return [NSString stringWithFormat:@"%s, %@", buf, tickString];
598 }
599
600 - (void)updateInfoTextForPoint:(NSPoint)pt
601 {
602 // Show the cursor info
603 MDTickType theTick;
604 int theNote;
605 char buf[8];
606 float ys = [self yScale];
607 int32_t measure, beat, tick;
608 theTick = [dataSource quantizedTickFromPixel:pt.x];
609 [dataSource convertTick:theTick toMeasure:&measure beat:&beat andTick:&tick];
610 theNote = (int)floor(pt.y / ys);
611 MDEventNoteNumberToNoteName(theNote, buf);
612 [dataSource setInfoText:[NSString stringWithFormat:@"%s, %d.%d.%d", buf, measure, beat, tick]];
613 }
614
615 - (void)doFlagsChanged: (NSEvent *)theEvent
616 {
617 NSUInteger flags = [theEvent modifierFlags];
618 localGraphicTool = [self modifyLocalGraphicTool:[[self dataSource] graphicTool]];
619 if (![[[[self dataSource] document] myMIDISequence] isPlaying]) {
620 if ((flags & NSControlKeyMask) != 0) {
621 [[NSCursor speakerCursor] set];
622 rubbing = YES;
623 return;
624 }
625 }
626 rubbing = NO;
627 [super doFlagsChanged: theEvent];
628 }
629
630 - (void)doMouseMoved: (NSEvent *)theEvent
631 {
632 int32_t track;
633 int32_t pos;
634 MDEvent *ep;
635 int n;
636 NSPoint pt;
637 NSUInteger flags = [theEvent modifierFlags];
638 localGraphicTool = [self modifyLocalGraphicTool:[[self dataSource] graphicTool]];
639 if (![[[[self dataSource] document] myMIDISequence] isPlaying]) {
640 if ((flags & NSControlKeyMask) != 0) {
641 [[NSCursor speakerCursor] set];
642 rubbing = YES;
643 return;
644 }
645 }
646 rubbing = NO;
647 pt = [self convertPoint:[theEvent locationInWindow] fromView: nil];
648 n = [self findNoteUnderPoint: pt track: &track position: &pos mdEvent: &ep];
649 if (n > 0) {
650 if (n == 3)
651 initialDraggedTick = MDGetDuration(ep);
652 else
653 initialDraggedTick = MDGetTick(ep);
654 initialDraggedValue = MDGetCode(ep);
655 deltaDraggedTick = 0;
656 deltaDraggedValue = 0;
657 if (n > 0) {
658 draggingMode = n;
659 [dataSource updateCursorInfoForView:self atPosition:pt];
660 draggingMode = 0;
661 }
662 [self setDraggingCursor: n];
663 return;
664 } else if (localGraphicTool == kGraphicPencilTool) {
665 [[NSCursor pencilCursor] set];
666 return;
667 }
668 [super doMouseMoved: theEvent];
669 }
670
671 - (void)playMIDINote:(int)note inTrack:(int)track withVelocity: (int)velocity
672 {
673 MyMIDISequence *seq = [[[self dataSource] document] myMIDISequence];
674 int dev, ch, i, tr;
675 unsigned char data[4];
676 for (i = [dataSource trackCount] - 1; i >= 0; i--) {
677 if (track >= 0)
678 tr = track; // i is ignored
679 else {
680 tr = i;
681 if (![self isFocusTrack:tr])
682 continue;
683 }
684 dev = MDPlayerGetDestinationNumberFromName([[seq deviceName: tr] UTF8String]);
685 ch = [seq trackChannel: tr];
686 data[0] = kMDEventSMFNoteOn | (ch & 15);
687 data[1] = (note & 127);
688 data[2] = (velocity & 127);
689 MDPlayerSendRawMIDI(NULL, data, 3, dev, -1);
690 if (track >= 0)
691 break;
692 }
693 }
694
695 - (void)invalidateRubbingTickLine
696 {
697 NSRect rect = [self bounds];
698 rect.origin.x = (CGFloat)(floor(rubbingTick * [dataSource pixelsPerTick]) - 1);
699 rect.size.width = 3;
700 [self setNeedsDisplayInRect:rect];
701 }
702
703 - (void)playNotesAtPoint:(CGFloat)xpos noteOn:(BOOL)flag
704 {
705 int i, num, trackNum, n;
706 MDTrack *track;
707 MDTickType tick = (MDTickType)(floor(xpos / [dataSource pixelsPerTick] + 0.5));
708 IntGroup *pset, *pset2, *pset3;
709 MDPointer *pt;
710 MDEvent *ep;
711 num = [self visibleTrackCount];
712 if (flag) {
713 if (rubbingArray == nil) {
714 /* Create empty rubbingArray */
715 rubbingArray = [[NSMutableArray allocWithZone:[self zone]] initWithCapacity:num];
716 for (i = 0; i < num; i++) {
717 [rubbingArray addObject: [[[IntGroupObject allocWithZone: [self zone]] init] autorelease]];
718 }
719 } else {
720 [self invalidateRubbingTickLine];
721 }
722 for (i = 0; i < num; i++) {
723 trackNum = [self sortedTrackNumberAtIndex:i];
724 if (![self isFocusTrack:trackNum])
725 continue;
726 pset = [[rubbingArray objectAtIndex:i] pointSet];
727 track = [[[dataSource document] myMIDISequence] getTrackAtIndex: trackNum];
728 pset2 = MDTrackSearchEventsWithDurationCrossingTick(track, tick);
729 pt = MDPointerNew(track);
730 pset3 = IntGroupNew();
731 IntGroupDifference(pset, pset2, pset3); /* Notes to be turned off */
732 n = -1;
733 while ((ep = MDPointerForwardWithPointSet(pt, pset3, &n)) != NULL) {
734 [self playMIDINote:MDGetCode(ep) inTrack:trackNum withVelocity:0];
735 }
736 IntGroupDifference(pset, pset3, pset); /* Notes that are on at present */
737 IntGroupDifference(pset2, pset, pset3); /* Notes to be turned on */
738 MDPointerSetPosition(pt, -1);
739 n = -1;
740 while ((ep = MDPointerForwardWithPointSet(pt, pset3, &n)) != NULL) {
741 [self playMIDINote:MDGetCode(ep) inTrack:trackNum withVelocity:MDGetData1(ep)];
742 }
743 IntGroupCopy(pset, pset2);
744 IntGroupRelease(pset2);
745 IntGroupRelease(pset3);
746 MDPointerRelease(pt);
747 }
748 rubbingTick = tick;
749 [self invalidateRubbingTickLine];
750 } else {
751 /* Send note off and dispose rubbingArray */
752 if (rubbingArray != nil) {
753 [self invalidateRubbingTickLine];
754 for (i = 0; i < num; i++) {
755 trackNum = [self sortedTrackNumberAtIndex:i];
756 if (![self isFocusTrack:trackNum])
757 continue;
758 track = [[[dataSource document] myMIDISequence] getTrackAtIndex: trackNum];
759 pset = [[rubbingArray objectAtIndex:i] pointSet];
760 pt = MDPointerNew(track);
761 n = -1;
762 while ((ep = MDPointerForwardWithPointSet(pt, pset, &n)) != NULL) {
763 [self playMIDINote:MDGetCode(ep) inTrack:trackNum withVelocity:0];
764 }
765 MDPointerRelease(pt);
766 }
767 [rubbingArray release];
768 rubbingArray = nil;
769 }
770 }
771 }
772
773 - (int)modifyLocalGraphicTool:(int)originalGraphicTool
774 {
775 NSEvent *event = [NSApp currentEvent];
776 NSUInteger flags = [event modifierFlags];
777 int tool = originalGraphicTool;
778 if ((flags & NSCommandKeyMask) != 0) {
779 if (tool == kGraphicPencilTool)
780 tool = kGraphicRectangleSelectTool;
781 else if (tool == kGraphicRectangleSelectTool)
782 tool = kGraphicPencilTool;
783 }
784 return tool;
785 }
786
787 - (void)doMouseDown: (NSEvent *)theEvent
788 {
789 // int track;
790 // int32_t pos;
791 MDEvent *ep;
792 // NSSize size;
793 // BOOL shiftDown;
794
795 float ys = [self yScale];
796 NSPoint pt = [self convertPoint: [theEvent locationInWindow] fromView: nil];
797 // shiftDown = (([theEvent modifierFlags] & NSShiftKeyMask) != 0);
798
799 draggingMode = [self findNoteUnderPoint: pt track: &mouseDownTrack position: &mouseDownPos mdEvent: &ep];
800 if (rubbing) {
801 [self playNotesAtPoint:pt.x noteOn:YES];
802 return;
803 }
804 pt.x = [dataSource quantizedPixelFromPixel: pt.x];
805 draggingStartPoint = draggingPoint = pt;
806 if (draggingMode > 0) {
807 playingTrack = mouseDownTrack;
808 playingNote = MDGetCode(ep);
809 playingVelocity = MDGetNoteOnVelocity(ep);
810 [self playMIDINote:playingNote inTrack:playingTrack withVelocity:playingVelocity];
811 return;
812 } else if (localGraphicTool == kGraphicPencilTool) {
813 pencilOn = YES;
814 [self invalidateDraggingRegion];
815 [self displayIfNeeded];
816 playingTrack = -1; // All editable tracks
817 playingNote = (int)(floor(pt.y / ys) + 0.5);
818 playingVelocity = 64;
819 [self playMIDINote:playingNote inTrack:playingTrack withVelocity:playingVelocity];
820 return;
821 } else {
822 [super doMouseDown: theEvent];
823 playingNote = -1;
824 }
825 }
826
827 - (void)doMouseDragged: (NSEvent *)theEvent
828 {
829 int i;
830 id clientView;
831 NSPoint pt = [self convertPoint: [theEvent locationInWindow] fromView: nil];
832 float ys = [self yScale];
833 if (rubbing) {
834 [self playNotesAtPoint:pt.x noteOn:YES];
835 return;
836 }
837 pt.x = [dataSource quantizedPixelFromPixel: pt.x];
838 if (draggingMode > 0) {
839
840 if (draggingMode == 1 || draggingMode == 3)
841 pt.y = draggingStartPoint.y;
842 else if (draggingMode == 2) {
843 pt.x = draggingStartPoint.x;
844 pt.y = (CGFloat)(floor((pt.y - draggingStartPoint.y) / ys + 0.5) * ys + draggingStartPoint.y);
845 }
846
847 if (draggingImage == nil) {
848 // Create an NSImage for dragging
849 NSRect rect, rect2, bounds;
850 NSSize size;
851 float pixelQuantum;
852 NSImage *image;
853 MyDocument *document = [dataSource document];
854 BOOL shiftDown = (([theEvent modifierFlags] & NSShiftKeyMask) != 0);
855 BOOL optionDown = (([theEvent modifierFlags] & NSAlternateKeyMask) != 0);
856 if (![document isSelectedAtPosition: mouseDownPos inTrack: mouseDownTrack]) {
857 // int i, n;
858 if (!shiftDown) {
859 // for (i = 0; (n = [self sortedTrackNumberAtIndex: i]) >= 0; i++)
860 // [document unselectAllEventsInTrack: n sender: self];
861 [document unselectAllEventsInAllTracks: self];
862 }
863 [document selectEventAtPosition: mouseDownPos inTrack: mouseDownTrack sender: self];
864 }
865 /* else {
866 if (shiftDown) {
867 [document unselectEventAtPosition: pos inTrack: track sender: self];
868 draggingMode = 0;
869 return;
870 }
871 } */
872 size = [self visibleRect].size;
873 rect.origin.x = pt.x - size.width;
874 rect.origin.y = pt.y - size.height;
875 rect.size.width = size.width * 2;
876 rect.size.height = size.height * 2;
877 image = [[NSImage allocWithZone: [self zone]] initWithSize: rect.size];
878 [image lockFocus];
879 [self drawNotesInRect: rect selectionOnly: YES offset: NSMakePoint(-rect.origin.x, -rect.origin.y) addDuration: 0];
880 [image unlockFocus];
881 draggingImage = image;
882 [self setDraggingCursor: draggingMode + (draggingMode != 3 && optionDown ? 16 : 0)];
883 [self invalidateDraggingRegion];
884 [self displayIfNeeded];
885 // Calculate limit rectangle for the dragging point
886 bounds = [self bounds];
887 rect2 = [self boundRectForSelection];
888 limitRect= NSMakeRect(
889 pt.x - (rect2.origin.x - bounds.origin.x),
890 pt.y - (rect2.origin.y - bounds.origin.y),
891 bounds.size.width - rect2.size.width,
892 bounds.size.height - rect2.size.height);
893 pixelQuantum = [dataSource pixelQuantum];
894 limitRect.origin.x = [dataSource quantizedPixelFromPixel: limitRect.origin.x];
895 if (limitRect.origin.x < 0.0)
896 limitRect.origin.x += pixelQuantum;
897 limitRect.size.width = (CGFloat)(floor(limitRect.size.width / pixelQuantum) * pixelQuantum);
898 if (limitRect.size.width + limitRect.origin.x > bounds.origin.x + bounds.size.width)
899 limitRect.size.width -= pixelQuantum;
900 if (draggingMode == 1) {
901 // Notify stripChartViews to scroll
902 for (i = 0; (clientView = [dataSource clientViewAtIndex:i]) != nil; i++) {
903 if ([clientView isKindOfClass:[StripChartView class]]) {
904 [clientView startExternalDraggingAtPoint:draggingStartPoint mode:draggingMode];
905 }
906 }
907 }
908 }
909
910 // Support autoscroll (cf. GraphicClientView.mouseDragged)
911 /* if (autoscrollTimer != nil) {
912 [autoscrollTimer invalidate];
913 [autoscrollTimer release];
914 autoscrollTimer = nil;
915 }
916 pt = [self convertPoint: pt toView: nil];
917 [self invalidateDraggingRegion];
918 if ([self autoscroll: [theEvent mouseEventWithLocation: pt]])
919 autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.2 target: self selector:@selector(autoscrollTimerCallback:) userInfo: theEvent repeats: NO] retain];
920 pt = [self convertPoint: pt fromView: nil]; */
921
922 if (draggingMode != 3) {
923 if (pt.x < limitRect.origin.x)
924 pt.x = limitRect.origin.x;
925 else if (pt.x > limitRect.origin.x + limitRect.size.width)
926 pt.x = limitRect.origin.x + limitRect.size.width;
927 if (pt.y < limitRect.origin.y)
928 pt.y = limitRect.origin.y;
929 else if (pt.y > limitRect.origin.y + limitRect.size.height)
930 pt.y = limitRect.origin.y + limitRect.size.height;
931 }
932 draggingPoint = pt;
933 [self invalidateDraggingRegion];
934 [self displayIfNeeded];
935 if (draggingMode == 1) {
936 // Notify stripChartViews to scroll
937 for (i = 0; (clientView = [dataSource clientViewAtIndex:i]) != nil; i++) {
938 if ([clientView isKindOfClass:[StripChartView class]]) {
939 [clientView setExternalDraggingPoint:pt];
940 }
941 }
942 }
943 pt.x = pt.x - draggingStartPoint.x;
944 pt.y = pt.y - draggingStartPoint.y;
945 deltaDraggedTick = (MDTickType)(floor(pt.x / [dataSource pixelsPerTick] + 0.5));
946 deltaDraggedValue = (int)floor(pt.y / [self yScale] + 0.5);
947 } else if (pencilOn) {
948 [self invalidateDraggingRegion];
949 draggingPoint = pt;
950 [self invalidateDraggingRegion];
951 [self displayIfNeeded];
952 } else {
953 [super doMouseDragged: theEvent];
954 return;
955 }
956 if (playingNote >= 0) {
957 int note = (int)(floor(draggingPoint.y / ys) + 0.5);
958 if (note != playingNote) {
959 [self playMIDINote:playingNote inTrack:playingTrack withVelocity:0];
960 playingNote = note;
961 [self playMIDINote:playingNote inTrack:playingTrack withVelocity:playingVelocity];
962 }
963 }
964 }
965
966 - (void)doMouseUp: (NSEvent *)theEvent
967 {
968 int i, trackNo;
969 float ppt, ys;
970 MDTickType minTick, maxTick;
971 BOOL shiftDown, optionDown;
972 NSRect bounds;
973 MyDocument *document = (MyDocument *)[dataSource document];
974
975 if (playingNote >= 0) {
976 [self playMIDINote:playingNote inTrack:playingTrack withVelocity:0];
977 playingNote = playingVelocity = playingTrack = -1;
978 }
979
980 if (rubbing) {
981 [self playNotesAtPoint:0.0f noteOn:NO]; /* 0.0 is dummy; all notes off */
982 return;
983 }
984
985 /* Mouse down on a note */
986 if (draggingMode > 0) {
987 NSPoint pt;
988 MDTickType deltaTick;
989 int deltaNote;
990 if (draggingImage == nil) {
991 /* No dragging --- just change selection */
992 shiftDown = (([theEvent modifierFlags] & NSShiftKeyMask) != 0);
993 if (![document isSelectedAtPosition: mouseDownPos inTrack: mouseDownTrack]) {
994 // int i, n;
995 if (!shiftDown) {
996 // for (i = 0; (n = [self sortedTrackNumberAtIndex: i]) >= 0; i++)
997 // [document unselectAllEventsInTrack: n sender: self];
998 [document unselectAllEventsInAllTracks: self];
999 }
1000 [document selectEventAtPosition: mouseDownPos inTrack: mouseDownTrack sender: self];
1001 } else {
1002 if (shiftDown) {
1003 [document unselectEventAtPosition: mouseDownPos inTrack: mouseDownTrack sender: self];
1004 }
1005 }
1006 draggingMode = 0;
1007 [self displayIfNeeded];
1008 return;
1009 }
1010
1011 [self invalidateDraggingRegion];
1012 pt.x = draggingPoint.x - draggingStartPoint.x;
1013 pt.y = draggingPoint.y - draggingStartPoint.y;
1014 deltaTick = (MDTickType)(floor(pt.x / [dataSource pixelsPerTick] + 0.5));
1015 deltaNote = (int)floor(pt.y / [self yScale] + 0.5);
1016 optionDown = (([theEvent modifierFlags] & NSAlternateKeyMask) != 0);
1017 if (draggingMode == 1 || draggingMode == 2) {
1018 [dataSource dragNotesByTick: deltaTick andNote: deltaNote sender: self optionFlag: optionDown];
1019 } else if (draggingMode == 3) {
1020 [dataSource dragDurationByTick: deltaTick sender: self];
1021 }
1022 [draggingImage release];
1023 draggingImage = nil;
1024 if (draggingMode == 1) {
1025 // Notify stripChartViews
1026 int i;
1027 id clientView;
1028 for (i = 0; (clientView = [dataSource clientViewAtIndex:i]) != nil; i++) {
1029 if ([clientView isKindOfClass:[StripChartView class]]) {
1030 [clientView endExternalDragging];
1031 }
1032 }
1033 }
1034 draggingMode = 0;
1035 [dataSource updateCursorInfoForView:self atPosition:draggingPoint];
1036 // [self displayIfNeeded];
1037 return;
1038 }
1039
1040 /* Pencil edit */
1041 if (pencilOn) {
1042 int keyCode;
1043 float x1;
1044 [self invalidateDraggingRegion];
1045
1046 ppt = [dataSource pixelsPerTick];
1047 ys = [self yScale];
1048 /* x1 = draggingStartPoint.x;
1049 x2 = draggingPoint.x;
1050 if (x2 < x1) {
1051 x2 = x1;
1052 x1 = draggingPoint.x;
1053 } else if (x2 == x1) {
1054 x2 = x1 + [dataSource pixelsPerQuarter];
1055 }
1056 minTick = (MDTickType)(x1 / ppt);
1057 maxTick = (MDTickType)(x2 / ppt);
1058 // This should be double checked, because it is possible that x2 > x1 but maxTick == minTick
1059 if (minTick == maxTick)
1060 maxTick = minTick + [document timebase];
1061 keyCode = (int)(floor(draggingStartPoint.y / ys)); */
1062 x1 = draggingPoint.x;
1063 minTick = (MDTickType)(x1 / ppt);
1064 maxTick = minTick + [document timebase];
1065 if (minTick == maxTick)
1066 maxTick = minTick + 1;
1067 keyCode = (int)(floor(draggingPoint.y / ys));
1068 if (keyCode < 0)
1069 keyCode = 0;
1070 if (keyCode > 127)
1071 keyCode = 127;
1072 for (i = 0; (trackNo = [self sortedTrackNumberAtIndex: i]) >= 0; i++) {
1073 MDEventObject *newEvent;
1074 MDEvent *ep;
1075 if (![self isFocusTrack:trackNo])
1076 continue;
1077 newEvent = [[[MDEventObject allocWithZone: [self zone]] init] autorelease];
1078 ep = &(newEvent->event);
1079 MDSetTick(ep, minTick);
1080 MDSetKind(ep, kMDEventNote);
1081 MDSetCode(ep, keyCode);
1082 MDSetNoteOnVelocity(ep, 64);
1083 MDSetNoteOffVelocity(ep, 64);
1084 MDSetDuration(ep, maxTick - minTick);
1085 [document insertEvent: newEvent toTrack: trackNo];
1086 }
1087
1088 pencilOn = NO;
1089 return;
1090 }
1091
1092 /* Box selection */
1093 if (isDragging && !isLoupeDragging) {
1094 shiftDown = (([theEvent modifierFlags] & NSShiftKeyMask) != 0);
1095 bounds = [[self selectionPath] bounds];
1096 ppt = [dataSource pixelsPerTick];
1097 ys = [self yScale];
1098 minTick = (MDTickType)(bounds.origin.x / ppt);
1099 maxTick = (MDTickType)((bounds.origin.x + bounds.size.width) / ppt);
1100 document = (MyDocument *)[dataSource document];
1101 for (i = 0; (trackNo = [self sortedTrackNumberAtIndex: i]) >= 0; i++) {
1102 MDTrack *track;
1103 MDPointer *pt;
1104 MDEvent *ep;
1105 IntGroup *pset;
1106 MDSelectionObject *obj;
1107 if (![self isFocusTrack:trackNo])
1108 continue;
1109 track = [[document myMIDISequence] getTrackAtIndex: trackNo];
1110 if (track == NULL)
1111 continue;
1112 pt = MDPointerNew(track);
1113 if (pt == NULL)
1114 break;
1115 pset = IntGroupNew();
1116 if (pset == NULL)
1117 break;
1118 MDPointerJumpToTick(pt, minTick);
1119 MDPointerBackward(pt);
1120 while ((ep = MDPointerForward(pt)) != NULL && MDGetTick(ep) < maxTick) {
1121 NSPoint point;
1122 if (MDGetKind(ep) != kMDEventNote)
1123 continue;
1124 point.x = (CGFloat)(floor(MDGetTick(ep) * ppt));
1125 point.y = (CGFloat)(floor(MDGetCode(ep) * ys + 0.5) + 0.5 * ys);
1126 if ([self isPointInSelectRegion: point]) {
1127 if (IntGroupAdd(pset, MDPointerGetPosition(pt), 1) != kMDNoError)
1128 break;
1129 }
1130 }
1131 obj = [[MDSelectionObject allocWithZone: [self zone]] initWithMDPointSet: pset];
1132 if (shiftDown) {
1133 [document toggleSelection: obj inTrack: trackNo sender: self];
1134 } else {
1135 [document setSelection: obj inTrack: trackNo sender: self];
1136 }
1137 [obj release];
1138 IntGroupRelease(pset);
1139 MDPointerRelease(pt);
1140 }
1141 return;
1142
1143 } else {
1144 /* LoupeDragging, etc. */
1145 [super doMouseUp: theEvent];
1146 return;
1147 }
1148 }
1149
1150 // Called from -[GraphicWindowController mouseExited:]
1151 - (void)mouseExited: (NSEvent *)theEvent
1152 {
1153
1154 }
1155
1156 #if 0
1157 - (void)mouseUp: (NSEvent *)theEvent
1158 {
1159 NSPoint pt;
1160 MDTickType deltaTick;
1161 int deltaNote;
1162 if (draggingMode > 0) {
1163 [self invalidateDraggingRegion];
1164 pt.x = draggingPoint.x - draggingStartPoint.x;
1165 pt.y = draggingPoint.y - draggingStartPoint.y;
1166 deltaTick = floor(pt.x / [dataSource pixelsPerTick] + 0.5);
1167 deltaNote = (int)floor(pt.y / [self yScale] + 0.5);
1168 if (draggingMode == 1 || draggingMode == 2) {
1169 [dataSource dragNotesByTick: deltaTick andNote: deltaNote sender: self];
1170 } else if (draggingMode == 3) {
1171 [dataSource dragDurationByTick: deltaTick sender: self];
1172 }
1173 [draggingImage release];
1174 draggingImage = nil;
1175 draggingMode = 0;
1176 [self displayIfNeeded];
1177 } else [super mouseUp: theEvent];
1178 }
1179 #endif
1180
1181 @end

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26