Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/Classes/StripChartView.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 202 - (show annotations) (download)
Mon Apr 25 14:09:56 2022 UTC (23 months, 2 weeks ago) by toshinagata1964
File size: 48902 byte(s)
Strip chart resolution was not working correctly. Fixed.
1 //
2 // StripChartView.m
3 // Created by Toshi Nagata on Sun Jan 26 2003.
4 //
5 /*
6 Copyright (c) 2003-2017 Toshi Nagata. All rights reserved.
7
8 This program 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 version 2 of the License.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 */
17
18 #import "StripChartView.h"
19 #import "GraphicSplitterView.h"
20 #import <math.h>
21 #import "GraphicWindowController.h"
22 #import "MyDocument.h"
23 #import "MyMIDISequence.h"
24 #import "MDObjects.h"
25 #import "NSEventAdditions.h"
26 #import "NSCursorAdditions.h"
27
28 /* The constants to approximate a parabola with a cubic bezier curve */
29 /* The cubic bezier curve (0, 0)-(ALPHA, 0)-(1-BETA, 1-2*BETA)-(1,1) approximates
30 a parabola y = x^2 in [0, 1]. */
31 /* #define ALPHA 0.377009
32 #define BETA 0.286601 */
33 static const float sParabolaPoints[] = {0, 0, 0.35f, 0, 0.7f, 0.4f, 1, 1, -1};
34 static const float sArcPoints[] = {0, 0, 0.15f, 0.6f, 0.33f, 1, 0.5f, 1, 0.67f, 1, 0.85f, 0.6f, 1, 0, -1};
35 static const float sSigmoidPoints[] = {0, 0, 0.45f, 0, 0.55f, 1, 1, 1, -1};
36
37 /* The resolutions for pencil drawing */
38 /* New events are generated so that the time/tick/value intervals are no less than
39 these values. */
40 static MDTimeType sTimeResolution = 5000;
41 static MDTickType sTickResolution = 1;
42 static float sValueResolution = 1.0f;
43
44 static const int sVerticalMargin = 2;
45
46 @implementation StripChartView
47
48 - (int)clientViewType
49 {
50 return kGraphicStripChartViewType;
51 }
52
53 - (id)initWithFrame:(NSRect)frame
54 {
55 self = [super initWithFrame:frame];
56 if (self) {
57 eventKind = 0; /* Undefined */
58 eventCode = 0;
59 minValue = 0.0f;
60 maxValue = 128.0f;
61 calib = NULL;
62 focusTrack = -1;
63 resolution = 1.0;
64 }
65 return self;
66 }
67
68 - (void)dealloc
69 {
70 if (calib != NULL)
71 MDCalibratorRelease(calib);
72 [super dealloc];
73 }
74
75 /*
76 - (BOOL)hasVerticalScroller
77 {
78 return NO;
79 }
80 */
81
82 - (void)updateCenterY
83 {
84 NSRect bounds = [[self superview] bounds];
85 float y = floor(bounds.origin.y + bounds.size.height * 0.5);
86 // Round to the nearest Y value
87 float newCenterY = [self convertFromYValue:[self convertToYValue:y]] + 0.5;
88 // If lineShape > 0, then we do not update the baseline
89 if (lineShape == 0 && centerY != newCenterY) {
90 [self invalidateCenterLine];
91 centerY = newCenterY;
92 [self invalidateCenterLine];
93 }
94 }
95
96 // Respond when the scroll position changed
97 - (void)superBoundsDidChange: (NSNotification *)aNotification
98 {
99 [self updateCenterY];
100 }
101
102 // Respond when the frame of the superview changed
103 // i.e. the scroll view containing the client view is resized
104 - (void)superFrameDidChange: (NSNotification *)aNotification
105 {
106 [self updateCenterY];
107 }
108
109 // Respond when the frame of the client view, i.e. data range and/or scale are changed
110 - (void)frameDidChange: (NSNotification *)aNotification
111 {
112 [self updateCenterY];
113 }
114
115 static float
116 getYValue(const MDEvent *ep, int eventKind)
117 {
118 if (eventKind == kMDEventNote)
119 return MDGetNoteOnVelocity(ep);
120 else if (eventKind == kMDEventInternalNoteOff)
121 return MDGetNoteOffVelocity(ep);
122 else if (eventKind == kMDEventTempo)
123 return MDGetTempo(ep);
124 else return MDGetData1(ep);
125 }
126
127 - (CGFloat)convertFromYValue:(float)yValue
128 {
129 NSRect bounds = [self bounds];
130 return (CGFloat)(floor(bounds.origin.y + (yValue - minValue) * ((bounds.size.height - sVerticalMargin * 2) / (maxValue - minValue)) + 0.5) + sVerticalMargin);
131 }
132
133 - (float)convertToYValue:(CGFloat)y
134 {
135 NSRect bounds = [self bounds];
136 return (float)(floor(((y - bounds.origin.y - sVerticalMargin) * (maxValue - minValue) / (bounds.size.height - sVerticalMargin * 2)) / resolution + 0.5) * resolution + minValue);
137 }
138
139 - (void)drawVelocityInRect: (NSRect)aRect
140 {
141 float ppt, height;
142 MDTickType beginTick, endTick;
143 int32_t n;
144 float dx, dy;
145 NSBezierPath *draggingPath;
146 NSMutableArray *array;
147 height = [self bounds].size.height;
148 ppt = [dataSource pixelsPerTick];
149 beginTick = (MDTickType)floor(aRect.origin.x / ppt);
150 endTick = (MDTickType)ceil((aRect.origin.x + aRect.size.width) / ppt);
151 draggingPath = nil;
152 array = nil;
153 if (stripDraggingMode > 0) {
154 dx = draggingPoint.x - draggingStartPoint.x;
155 dy = draggingPoint.y - draggingStartPoint.y;
156 if (dx < -0.5 || dx > 0.5 || dy < -0.5 || dy > 0.5) {
157 array = [NSMutableArray array];
158 }
159 beginTick = (MDTickType)floor((aRect.origin.x - (dx > 0 ? dx : -dx)) / ppt);
160 endTick = (MDTickType)ceil((aRect.origin.x + aRect.size.width + (dx > 0 ? dx : -dx)) / ppt);
161 }
162 for (n = [self visibleTrackCount] - 1; n >= 0; n--) {
163 float x, y, ybase;
164 MDEvent *ep;
165 MDPointer *pt;
166 NSColor *color;
167 MDTrack *track;
168 int32_t trackNo;
169 trackNo = [self sortedTrackNumberAtIndex: n];
170 track = [[[dataSource document] myMIDISequence] getTrackAtIndex: trackNo];
171 if (track == NULL)
172 continue;
173 color = [[dataSource document] colorForTrack: trackNo enabled: [self isFocusTrack: trackNo]];
174 [color set];
175 pt = MDPointerNew(track);
176 if (pt == NULL)
177 break;
178 MDPointerJumpToTick(pt, beginTick);
179 MDPointerBackward(pt);
180 ybase = aRect.origin.y;
181 while ((ep = MDPointerForward(pt)) != NULL && MDGetTick(ep) < endTick) {
182 if (MDGetKind(ep) != kMDEventNote)
183 continue;
184 //y = (float)(ceil((getYValue(ep, eventKind) - minValue) / (maxValue - minValue) * (height - sVerticalMargin * 2)) + 0.5) + sVerticalMargin;
185 y = [self convertFromYValue:getYValue(ep, eventKind)];
186 x = (float)(floor(MDGetTick(ep) * ppt) + 0.5);
187 if (y >= aRect.origin.y) {
188 if ([[dataSource document] isSelectedAtPosition: MDPointerGetPosition(pt) inTrack: trackNo]) {
189 NSFrameRect(NSMakeRect(x - 1, y - 1, 3, 3));
190 if (array != nil) {
191 if (draggingPath == nil)
192 draggingPath = [NSBezierPath bezierPath];
193 [draggingPath appendBezierPathWithRect: NSMakeRect(x + dx - 1, y + dy - 1, 3, 3)];
194 [draggingPath moveToPoint: NSMakePoint(x + dx, y + dy)];
195 [draggingPath lineToPoint: NSMakePoint(x + dx, ybase)];
196 }
197 y -= 1.0f;
198 }
199 [NSBezierPath strokeLineFromPoint: NSMakePoint(x, y) toPoint: NSMakePoint(x, ybase)];
200 }
201 }
202 if (array != nil && draggingPath != nil) {
203 [array addObject:[color colorWithAlphaComponent: 0.5f]];
204 [array addObject:draggingPath];
205 draggingPath = nil;
206 }
207 MDPointerRelease(pt);
208 }
209 if (array != nil) {
210 int i = 0;
211 n = (int)[array count];
212 while (i < n) {
213 // NSLog(@"color: %@ path (%d)", [array objectAtIndex: i], [[array objectAtIndex: i + 1] elementCount]);
214 [(NSColor *)[array objectAtIndex: i++] set];
215 [[array objectAtIndex: i++] stroke];
216 }
217 }
218 }
219
220 - (void)drawBoxStripInRect: (NSRect)aRect
221 {
222 float ppt;
223 int32_t n;
224 MDTickType beginTick, endTick;
225 float height;
226 float dx, dy;
227 NSBezierPath *draggingPath;
228 NSMutableArray *array;
229
230 if (calib == NULL) {
231 [self reallocateCalibrators];
232 }
233
234 height = [self bounds].size.height;
235 ppt = [dataSource pixelsPerTick];
236 draggingPath = nil;
237 array = nil;
238 if (stripDraggingMode > 0) {
239 dx = draggingPoint.x - draggingStartPoint.x;
240 dy = draggingPoint.y - draggingStartPoint.y;
241 if (dx < -0.5 || dx > 0.5 || dy < -0.5 || dy > 0.5) {
242 array = [NSMutableArray array];
243 aRect.origin.x -= (dx > 0 ? dx : -dx);
244 aRect.size.width += (dx > 0 ? dx : -dx) * 2;
245 }
246 }
247 beginTick = (MDTickType)floor(aRect.origin.x / ppt);
248 endTick = (MDTickType)ceil((aRect.origin.x + aRect.size.width) / ppt);
249 MDCalibratorJumpToTick(calib, beginTick);
250 if (eventKind == kMDEventTempo)
251 n = 0;
252 else
253 n = [self visibleTrackCount] - 1;
254 for ( ; n >= 0; n--) {
255 float x, y, xlast, ylast;
256 MDEvent *ep;
257 MDPointer *pt;
258 NSRect rect;
259 NSColor *color, *shadowColor;
260 MDTrack *track;
261 int32_t trackNo, poslast;
262 BOOL isFocused;
263 if (eventKind == kMDEventTempo)
264 trackNo = 0;
265 else
266 trackNo = [self sortedTrackNumberAtIndex: n];
267 track = [[[dataSource document] myMIDISequence] getTrackAtIndex: trackNo];
268 isFocused = [self isFocusTrack: trackNo];
269 color = [[dataSource document] colorForTrack: trackNo enabled: isFocused];
270 shadowColor = (isFocused ? [color shadowWithLevel: 0.1f] : color);
271 pt = MDCalibratorCopyPointer(calib, track, eventKind, eventCode);
272 if (pt == NULL)
273 continue;
274 ep = MDPointerCurrent(pt);
275 if (ep == NULL) {
276 xlast = ylast = 0;
277 poslast = -1;
278 } else {
279 //ylast = (float)(ceil((getYValue(ep, eventKind) - minValue) / (maxValue - minValue) * (height - sVerticalMargin * 2))) + sVerticalMargin;
280 ylast = [self convertFromYValue:getYValue(ep, eventKind)];
281 xlast = (float)(floor(MDGetTick(ep) * ppt));
282 poslast = MDPointerGetPosition(pt);
283 }
284 while (1) {
285 ep = MDPointerForward(pt);
286 if (ep != NULL) {
287 if (MDGetKind(ep) != eventKind)
288 continue;
289 if (eventCode != -1 && MDGetCode(ep) != eventCode)
290 continue;
291 //y = (float)(ceil((getYValue(ep, eventKind) - minValue) / (maxValue - minValue) * (height - sVerticalMargin * 2))) + sVerticalMargin;
292 y = [self convertFromYValue:getYValue(ep, eventKind)];
293 x = (float)(floor(MDGetTick(ep) * ppt));
294 } else {
295 x = [self bounds].size.width;
296 }
297 rect = NSMakeRect(xlast, -1, x - xlast + 1, ylast + 1);
298 if (NSIntersectsRect(rect, aRect)) {
299 if ([[dataSource document] isSelectedAtPosition: poslast inTrack: trackNo]) {
300 [color set];
301 NSFrameRect(NSMakeRect(xlast - 1, ylast - 2, 3, 3));
302 if (array != nil) {
303 if (draggingPath == nil)
304 draggingPath = [NSBezierPath bezierPath];
305 [draggingPath appendBezierPathWithRect: NSMakeRect(xlast + dx - 1, ylast + dy - 2, 3, 3)];
306 [draggingPath moveToPoint: NSMakePoint(xlast + dx, ylast + dy)];
307 [draggingPath lineToPoint: NSMakePoint(xlast + dx, 0)];
308 }
309 [[NSColor whiteColor] set];
310 } else {
311 [shadowColor set];
312 }
313 [NSBezierPath fillRect: rect]; // NSRectFill(rect);
314 [color set];
315 [NSBezierPath strokeRect: rect]; // NSFrameRect(rect);
316 }
317 if (ep == NULL || xlast >= aRect.origin.x + aRect.size.width)
318 break;
319 xlast = x;
320 ylast = y;
321 poslast = MDPointerGetPosition(pt);
322 }
323 if (draggingPath != nil) {
324 [array addObject:[color colorWithAlphaComponent: 0.5f]];
325 [array addObject:draggingPath];
326 draggingPath = nil;
327 }
328 MDPointerRelease(pt);
329 }
330 if (array != nil) {
331 int i = 0;
332 n = (int)[array count];
333 while (i < n) {
334 [(NSColor *)[array objectAtIndex: i++] set];
335 [[array objectAtIndex: i++] stroke];
336 }
337 }
338 }
339
340 - (float)horizontalGridInterval
341 {
342 NSRect frame, visibleRect;
343 float visibleRange, aRange;
344 frame = [self frame];
345 visibleRect = [(NSClipView *)[self superview] documentVisibleRect];
346 aRange = maxValue - minValue + 1.0f;
347 visibleRange = aRange / frame.size.height * visibleRect.size.height;
348 while (aRange > visibleRange * 0.7f)
349 aRange *= 0.5f;
350 return aRange;
351 }
352
353 - (NSColor *)verticalLineColor: (BOOL)beforeEndOfSequence;
354 {
355 if (beforeEndOfSequence)
356 return [NSColor lightGrayColor];
357 else
358 return [NSColor grayColor];
359 }
360
361 - (void)drawContentsInRect: (NSRect)aRect
362 {
363 NSPoint pt;
364 NSRect bounds;
365 float y, grid;
366 NSEraseRect(aRect);
367 [self paintEditingRange: aRect startX: NULL endX: NULL];
368 [self drawVerticalLinesInRect: aRect];
369 if (eventKind == kMDEventNote || eventKind == kMDEventInternalNoteOff)
370 [self drawVelocityInRect: aRect];
371 else
372 [self drawBoxStripInRect: aRect];
373
374 /* Draw grid lines */
375 bounds = [self bounds];
376 pt.x = bounds.origin.x;
377 grid = [self horizontalGridInterval];
378 [[NSColor lightGrayColor] set];
379 for (y = minValue; y <= maxValue + 1; y += grid) {
380 float y0 = (y > maxValue ? maxValue : y);
381 //pt.y = (CGFloat)(floor(bounds.origin.y + y0 * ((bounds.size.height - sVerticalMargin * 2) / (maxValue - minValue)) + sVerticalMargin) + 0.5);
382 pt.y = [self convertFromYValue:y0];
383 [NSBezierPath strokeLineFromPoint: pt toPoint: NSMakePoint(pt.x + bounds.size.width, pt.y)];
384 }
385 int editingMode = [[self dataSource] graphicEditingMode];
386 if (editingMode == kGraphicAddMode || editingMode == kGraphicScaleMode) {
387 // Draw baseline
388 pt.y = centerY;
389 if (pt.y >= bounds.origin.y && pt.y < bounds.origin.y + bounds.size.height) {
390 float saveLineWidth = [NSBezierPath defaultLineWidth];
391 [NSBezierPath setDefaultLineWidth: saveLineWidth * 2];
392 [[NSColor colorWithDeviceRed:1.0 green:1.0 blue:1.0 alpha:0.5] set];
393 [NSBezierPath strokeLineFromPoint: pt toPoint: NSMakePoint(pt.x + bounds.size.width, pt.y)];
394 [NSBezierPath setDefaultLineWidth: saveLineWidth];
395 if (editingMode == kGraphicAddMode)
396 [[NSColor blueColor] set];
397 else
398 [[NSColor redColor] set];
399 [NSBezierPath strokeLineFromPoint: pt toPoint: NSMakePoint(pt.x + bounds.size.width, pt.y)];
400 }
401 }
402 if ([self isDragging])
403 [self drawSelectRegion];
404 }
405
406 - (void)reallocateCalibrators
407 {
408 MDSequence *sequence;
409 if (calib != NULL)
410 MDCalibratorRelease(calib);
411 calib = NULL;
412 sequence = [[[dataSource document] myMIDISequence] mySequence];
413 calib = MDCalibratorNew(sequence, NULL, kMDEventTempo, -1);
414 if (eventKind != kMDEventTempo && eventKind != kMDEventNote && eventKind != kMDEventInternalNoteOff) {
415 int i;
416 MDTrack *track;
417 for (i = [self visibleTrackCount] - 1; i >= 0; i--) {
418 track = MDSequenceGetTrack(sequence, [self sortedTrackNumberAtIndex: i]);
419 if (track != NULL) {
420 if (calib == NULL)
421 calib = MDCalibratorNew(sequence, track, eventKind, eventCode);
422 else
423 MDCalibratorAppend(calib, track, eventKind, eventCode);
424 }
425 }
426 }
427 }
428
429 - (void)setKindAndCode: (int32_t)kindAndCode
430 {
431 int newKind, newCode, ftrack;
432 float minval, maxval;
433 newKind = (kindAndCode >> 16) & 65535;
434 newCode = kindAndCode & 65535;
435 if ((newKind == 65535 || newKind == eventKind) && (newCode == 65535 || newCode == eventCode))
436 return; /* Do nothing */
437 if (newKind != 65535) {
438 eventKind = newKind;
439 if (eventKind == kMDEventTempo) {
440 minval = 0.0f;
441 maxval = 511.0f;
442 } else if (eventKind == kMDEventPitchBend) {
443 minval = -8192.0f;
444 maxval = 8191.0f;
445 } else {
446 minval = 0.0f;
447 maxval = 127.0f;
448 }
449 [self setMinValue: minval];
450 [self setMaxValue: maxval];
451 if (eventKind == kMDEventNote || eventKind == kMDEventInternalNoteOff)
452 mode = kStripChartBarMode;
453 else
454 mode = kStripChartBoxMode;
455 [self setYScale: [[self superview] bounds].size.height / (maxval - minval)];
456 }
457 if (newCode != 65535)
458 eventCode = newCode;
459 else eventCode = -1;
460 ftrack = focusTrack;
461 if (eventKind == kMDEventTempo)
462 ftrack = 0; /* Conductor Track */
463 else if (ftrack == 0)
464 ftrack = -1; /* As piano roll */
465 if (ftrack != focusTrack)
466 [self setFocusTrack:ftrack];
467 else {
468 [self reallocateCalibrators];
469 [self reloadData];
470 [self setNeedsDisplay: YES];
471 }
472 [self updateCenterY];
473 }
474
475 - (void)setResolution: (float)resolution
476 {
477 self->resolution = resolution;
478 }
479
480
481 - (BOOL)isFocusTrack: (int)trackNum
482 {
483 if (focusTrack >= 0)
484 return (trackNum == focusTrack);
485 else return [super isFocusTrack:trackNum];
486 }
487
488 - (int32_t)visibleTrackCount
489 {
490 if (focusTrack >= 0)
491 return 1;
492 else return [super visibleTrackCount];
493 }
494
495 - (int)sortedTrackNumberAtIndex: (int)index
496 {
497 if (focusTrack >= 0)
498 return (index == 0 ? focusTrack : -1);
499 else return [super sortedTrackNumberAtIndex:index];
500 }
501
502 - (void)setFocusTrack:(int)aTrack
503 {
504 /* TODO: We need to set the first responder to the active client view, rather than to the main view. Otherwise, pasted events will go to the 'editable' track in the track list instead of the focus track. */
505 int i;
506 id view;
507 focusTrack = aTrack;
508 for (i = 0; (view = [dataSource clientViewAtIndex:i]) != nil; i++) {
509 if (view == self) {
510 [[dataSource splitterViewAtIndex:i] setTrack:aTrack];
511 break;
512 }
513 }
514 [self reallocateCalibrators];
515 [self reloadData];
516 [self setNeedsDisplay:YES];
517 }
518
519 - (int)focusTrack
520 {
521 return focusTrack;
522 }
523
524 - (void)doTrackModified:(int)aTrack
525 {
526 /* Check if the calibrator support matches the track status */
527 /* If no, then reset the calibrator */
528 if (calib != NULL) {
529 BOOL focus = ([self isFocusTrack:aTrack] != 0);
530 BOOL support = (MDCalibratorIsSupporting(calib, [[[dataSource document] myMIDISequence] getTrackAtIndex:aTrack], eventKind, eventCode) != 0);
531 if (focus != support) {
532 MDCalibratorRelease(calib);
533 calib = NULL;
534 }
535 }
536 }
537
538 - (int32_t)kindAndCode
539 {
540 return ((((int32_t)eventKind) & 65535) << 16) | (eventCode & 65535);
541 }
542
543 - (void)invalidateDraggingRegion
544 {
545 NSRect rect = selectionRect;
546 rect.origin.x += draggingPoint.x - draggingStartPoint.x;
547 if (draggingPoint.y > draggingStartPoint.y)
548 rect.size.height += draggingPoint.y - draggingStartPoint.y;
549 rect = NSInsetRect(rect, -2, -2);
550 dprintf(2, "invalidateDraggingRegion: (%g %g %g %g)\n", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
551 [self setNeedsDisplayInRect: rect];
552 }
553
554 // Invalidate the region including the center line
555 // (Should be only called when centerY is valid, i.e. during add/sub or scale dragging)
556 - (void)invalidateCenterLine
557 {
558 NSRect rect = [self bounds];
559 rect.origin.y = centerY - 1;
560 rect.size.height = 2;
561 [self setNeedsDisplayInRect: rect];
562 }
563
564 - (NSRect)boundRectForSelection
565 {
566 int i, n;
567 MDTickType tick, minTick, maxTick;
568 float ppt;
569 // float minY;
570 float maxY;
571 float height = [self bounds].size.height;
572 MyDocument *document = [dataSource document];
573 minTick = kMDMaxTick;
574 maxTick = kMDNegativeTick;
575 // minY = 10000000.0;
576 maxY = -10000000.0f;
577 ppt = [dataSource pixelsPerTick];
578 for (i = 0; (n = [self sortedTrackNumberAtIndex: i]) >= 0; i++) {
579 int index;
580 MDPointer *pt;
581 MDEvent *ep;
582 float y;
583 MDTrack *track = [[document myMIDISequence] getTrackAtIndex: n];
584 IntGroup *pset = [[document selectionOfTrack: n] pointSet];
585 if (track == NULL || pset == NULL)
586 continue;
587 pt = MDPointerNew(track);
588 if (pt == NULL)
589 break;
590 MDPointerSetPositionWithPointSet(pt, pset, -1, &index);
591 while ((ep = MDPointerForwardWithPointSet(pt, pset, &index)) != NULL) {
592 if (MDGetKind(ep) != eventKind || (eventCode != -1 && MDGetCode(ep) != eventCode))
593 continue;
594 tick = MDGetTick(ep);
595 y = getYValue(ep, eventKind);
596 // if (y < minY)
597 // minY = y;
598 if (y > maxY)
599 maxY = y;
600 if (tick < minTick)
601 minTick = tick;
602 if (tick > maxTick)
603 maxTick = tick;
604 }
605 }
606 if (minTick > maxTick || maxY < 0) {
607 return NSMakeRect(0, 0, 0, 0);
608 } else {
609 // minY = (minY - minValue) / (maxValue - minValue) * height;
610 maxY = (maxY - minValue) / (maxValue - minValue) * height;
611 return NSMakeRect(minTick * ppt - 1, 0, (maxTick - minTick) * ppt + 3, maxY + 1);
612 }
613 }
614
615 /* Returns 0-3; 0: no event, 1: the hot spot, 2: on the vertical line, 3: on the horizontal line (box mode only) */
616 - (int)findStripUnderPoint: (NSPoint)aPoint track: (int *)outTrack position: (int32_t *)outPosition mdEvent: (MDEvent **)outEvent
617 {
618 int num, i, retval;
619 int trackNum;
620 int32_t poslast;
621 MDEvent *ep;
622 float ppt = [dataSource pixelsPerTick];
623 float x, y, xlast, ylast;
624 float height = [self bounds].size.height;
625 MyDocument *document = (MyDocument *)[dataSource document];
626 MDTickType theTick;
627
628 num = [self visibleTrackCount];
629 theTick = (MDTickType)((aPoint.x - 1) / ppt);
630 if (calib == NULL)
631 [self reallocateCalibrators];
632 MDCalibratorJumpToTick(calib, theTick);
633 retval = 0;
634 for (i = 0; i < num; i++) {
635 MDTrack *track;
636 MDPointer *pt;
637 trackNum = [self sortedTrackNumberAtIndex: i];
638 track = [[document myMIDISequence] getTrackAtIndex: trackNum];
639 if (track == NULL)
640 continue;
641 if (eventKind != kMDEventNote && eventKind != kMDEventInternalNoteOff)
642 pt = MDCalibratorCopyPointer(calib, track, eventKind, eventCode);
643 else {
644 pt = MDPointerNew(track);
645 if (pt != NULL) {
646 MDPointerJumpToTick(pt, theTick);
647 MDPointerBackward(pt);
648 }
649 }
650 if (pt == NULL)
651 continue;
652 ep = MDPointerCurrent(pt);
653 if (ep == NULL) {
654 xlast = ylast = 0;
655 poslast = -1;
656 } else {
657 //ylast = (float)ceil((getYValue(ep, eventKind) - minValue) / (maxValue - minValue) * (height - sVerticalMargin * 2)) + sVerticalMargin;
658 ylast = [self convertFromYValue:getYValue(ep, eventKind)];
659 xlast = (float)floor(MDGetTick(ep) * ppt);
660 poslast = MDPointerGetPosition(pt);
661 }
662 while (retval == 0) {
663 ep = MDPointerForward(pt);
664 if (ep != NULL) {
665 if (MDGetKind(ep) != eventKind)
666 continue;
667 if (eventCode != -1 && MDGetCode(ep) != eventCode)
668 continue;
669 //y = (float)ceil((getYValue(ep, eventKind) - minValue) / (maxValue - minValue) * (height - sVerticalMargin * 2)) + sVerticalMargin;
670 y = [self convertFromYValue:getYValue(ep, eventKind)];
671 x = (float)floor(MDGetTick(ep) * ppt);
672 } else {
673 x = [self bounds].size.width + 2;
674 }
675 if (aPoint.x >= x - 1 && aPoint.x <= x + 1) {
676 if (aPoint.y >= y - 1 && aPoint.y <= y + 1)
677 retval = 1;
678 else if (aPoint.y <= y + 1 || (poslast >= 0 && aPoint.y <= ylast + 1))
679 retval = 2;
680 poslast = MDPointerGetPosition(pt);
681 break; /* found */
682 }
683 if (aPoint.x < x - 1) {
684 if (eventKind != kMDEventNote && eventKind != kMDEventInternalNoteOff) {
685 /* horizontal line: box mode */
686 if (poslast >= 0) {
687 if (aPoint.y >= ylast - 1 && aPoint.y <= ylast + 1) {
688 MDPointerSetPosition(pt, poslast);
689 ep = MDPointerCurrent(pt);
690 retval = 3;
691 } else if (aPoint.y <= ylast) {
692 /* Stop searching, as the box hides events in the following tracks */
693 retval = -1;
694 }
695 }
696 }
697 break;
698 }
699 if (ep == NULL)
700 break;
701 }
702 MDPointerRelease(pt);
703 if (retval != 0)
704 break;
705 }
706 if (retval > 0) {
707 if (outTrack != NULL)
708 *outTrack = trackNum;
709 if (outPosition != NULL)
710 *outPosition = poslast;
711 if (outEvent != NULL)
712 *outEvent = ep;
713 } else if (retval < 0)
714 retval = 0;
715 return retval;
716 }
717
718 // Override of the GraphicClientView method. Treats the pencil mode specifically.
719 - (void)drawSelectRegion
720 {
721 int n;
722 float saveLineWidth;
723 NSPoint pt1, pt2, dp;
724 NSRect r;
725 NSBezierPath *path;
726
727 n = [self modifyLocalGraphicTool:[[self dataSource] graphicTool]];
728 if (lineShape == 0 && (n == kGraphicIbeamSelectTool || n == kGraphicRectangleSelectTool)) {
729 [super drawSelectRegion];
730 return;
731 }
732
733 // Pencil mode
734 // Set the line shape (>0): this is the indicator for pencil editing (used in the mouseUp handler)
735 // lineShape = [[self dataSource] graphicLineShape];
736
737 // selectPoints is an instance variable of GraphicClientView
738 n = (int)[selectPoints count];
739 if (n == 0)
740 return;
741 [[NSColor cyanColor] set];
742 pt1 = [[selectPoints objectAtIndex: 0] pointValue];
743 if (n < 2) {
744 [NSBezierPath fillRect: NSMakeRect(pt1.x - 1, pt1.y - 1, 2, 2)];
745 return;
746 }
747 pt2 = [[selectPoints objectAtIndex: 1] pointValue];
748
749 /* Calculate the rect with pt1/pt2 at the corners */
750 r.origin = pt1;
751 r.size.width = pt2.x - pt1.x;
752 r.size.height = pt2.y - pt1.y;
753 if (r.size.width < 0) {
754 r.size.width = -r.size.width;
755 r.origin.x = pt2.x;
756 }
757 if (r.size.height < 0) {
758 r.size.height = -r.size.height;
759 r.origin.y = pt2.y;
760 }
761
762 saveLineWidth = [NSBezierPath defaultLineWidth];
763 [NSBezierPath setDefaultLineWidth: 1.0f];
764 [NSBezierPath fillRect: NSMakeRect(pt1.x - 1, pt1.y - 1, 2, 2)];
765 [NSBezierPath fillRect: NSMakeRect(pt2.x - 1, pt2.y - 1, 2, 2)];
766
767 if (lineShape == kGraphicLinearShape) {
768 [NSBezierPath strokeLineFromPoint: pt1 toPoint: pt2];
769 } else if (lineShape == kGraphicRandomShape) {
770 [NSBezierPath strokeRect: NSInsetRect(r, -0.5f, -0.5f)];
771 } else {
772 const float *p;
773 switch (lineShape) {
774 case kGraphicParabolaShape:
775 p = sParabolaPoints;
776 break;
777 case kGraphicArcShape:
778 p = sArcPoints;
779 break;
780 case kGraphicSigmoidShape:
781 p = sSigmoidPoints;
782 break;
783 default:
784 return;
785 }
786 dp.x = pt2.x - pt1.x;
787 dp.y = pt2.y - pt1.y;
788 path = [NSBezierPath bezierPath];
789 while (p[2] >= 0.0) {
790 [path moveToPoint: NSMakePoint(pt1.x + dp.x * p[0], pt1.y + dp.y * p[1])];
791 [path curveToPoint: NSMakePoint(pt1.x + dp.x * p[6], pt1.y + dp.y * p[7])
792 controlPoint1: NSMakePoint(pt1.x + dp.x * p[2], pt1.y + dp.y * p[3])
793 controlPoint2: NSMakePoint(pt1.x + dp.x * p[4], pt1.y + dp.y * p[5])];
794 p += 6;
795 }
796 [path stroke];
797 /* Eye guide */
798 if (p[0] < 1.0 || p[1] < 1.0) {
799 [[[NSColor cyanColor] colorWithAlphaComponent: 0.5f] set];
800 [NSBezierPath strokeLineFromPoint: NSMakePoint(pt1.x + dp.x * p[0] + 0.5f, pt1.y + dp.y * p[1] + 0.5f) toPoint: NSMakePoint(pt2.x + 0.5f, pt2.y + 0.5f)];
801 }
802 }
803 [NSBezierPath setDefaultLineWidth: saveLineWidth];
804 }
805
806 // Calculate the value of cubic bezier coordinates from the parameter t
807 static float
808 cubicFunc(float t, const float *points)
809 {
810 // The control parameters are given as points[0, 2, 4, 6]
811 float a0, a1, a2, a3;
812 a0 = -points[0] + 3 * points[2] - 3 * points[4] + points[6];
813 a1 = 3 * (points[0] - 2 * points[2] + points[4]);
814 a2 = 3 * (-points[0] + points[2]);
815 a3 = points[0];
816 return a3 + t * (a2 + t * (a1 + t * a0));
817 }
818
819 // Calculate the parameter t from the coordinate value
820 // tt is the hint value for solving the equation.
821 static float
822 cubicReverseFunc(float x, const float *points, float tt)
823 {
824 double a0, a1, a2, a3, dx, t, dxdt, t0, t1;
825 int iter;
826 a0 = -points[0] + 3 * points[2] - 3 * points[4] + points[6];
827 a1 = 3 * (points[0] - 2 * points[2] + points[4]);
828 a2 = 3 * (-points[0] + points[2]);
829 a3 = points[0];
830 t = tt;
831 iter = 0;
832 while (1) {
833 dx = a3 + t * (a2 + t * (a1 + t * a0)) - x;
834 if (fabs(dx) < 1e-8)
835 return (float)t;
836 dxdt = a2 + t * (2 * a1 + t * 3 * a0);
837 if (++iter > 10 || fabs(dxdt) < 1e-8 || (t0 = t - dx / dxdt) >= 1.0 || t0 <= 0) {
838 // Switch to binary search
839 if (dx < 0) {
840 t0 = t;
841 t1 = (a3 > x ? 0 : 1);
842 } else {
843 t1 = t;
844 t0 = (a3 < x ? 0 : 1);
845 }
846 while (1) {
847 t = (t0 + t1) / 2;
848 dx = a3 + t * (a2 + t * (a1 + t * a0)) - x;
849 if (dx < -1e-8) {
850 t0 = t;
851 } else if (dx > 1e-8) {
852 t1 = t;
853 } else return (float)t;
854 if (fabs(t1 - t0) < 1e-8)
855 return (float)t;
856 }
857 }
858 if (fabs(t - t0) < 1e-8)
859 return (float)t;
860 t = t0;
861 }
862 }
863
864 - (int)modifyLocalGraphicTool:(int)originalGraphicTool
865 {
866 NSEvent *event = [NSApp currentEvent];
867 NSUInteger flags = (isDragging ? initialModifierFlags : [event modifierFlags]);
868 int tool = originalGraphicTool;
869 if ((flags & NSCommandKeyMask) != 0) {
870 if (tool == kGraphicPencilTool)
871 tool = kGraphicRectangleSelectTool;
872 else if (tool == kGraphicRectangleSelectTool)
873 tool = kGraphicPencilTool;
874 }
875 return tool;
876 }
877
878 // Edit in the pencil mode, i.e. edit the strip chart values according to
879 // the graphicLineShape and graphicEditingMode.
880 - (void)doPencilEdit
881 {
882 int i, n;
883 NSPoint pt1, pt2;
884 MDTickType t1, t2;
885 MDTickType fromTick, toTick;
886 MDPointer *mdptr;
887 float fromValue, toValue;
888 float pixelsPerTick, height;
889 float valueResolution = resolution;
890 const float *p;
891 float v1, v2;
892 int editingMode;
893 BOOL shiftFlag = (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0);
894 MyDocument *doc = (MyDocument *)[dataSource document];
895
896 // selectPoints is an instance variable of GraphicClientView
897 n = (int)[selectPoints count];
898 if (n == 0)
899 return;
900 pt1 = [[selectPoints objectAtIndex: 0] pointValue];
901 if (n < 2)
902 pt2 = pt1;
903 else pt2 = [[selectPoints objectAtIndex: 1] pointValue];
904 pixelsPerTick = [dataSource pixelsPerTick];
905 height = [self bounds].size.height;
906 t1 = (MDTickType)floor(pt1.x / pixelsPerTick + 0.5);
907 t2 = (MDTickType)floor(pt2.x / pixelsPerTick + 0.5);
908 //v1 = (int)floor((pt1.y - sVerticalMargin) * (maxValue - minValue) / (height - sVerticalMargin * 2) + 0.5 + minValue);
909 //v2 = (int)floor((pt2.y - sVerticalMargin) * (maxValue - minValue) / (height - sVerticalMargin * 2) + 0.5 + minValue);
910 v1 = [self convertToYValue:pt1.y];
911 v2 = [self convertToYValue:pt2.y];
912 if (v1 < minValue)
913 v1 = minValue;
914 if (v2 < minValue)
915 v2 = minValue;
916 if (v1 > maxValue)
917 v1 = maxValue;
918 if (v2 > maxValue)
919 v2 = maxValue;
920 if (t1 < t2) {
921 fromTick = t1;
922 toTick = t2;
923 fromValue = v1;
924 toValue = v2;
925 } else {
926 fromTick = t2;
927 toTick = t1;
928 fromValue = v2;
929 toValue = v1;
930 }
931 if (t1 == t2 || lineShape == kGraphicRandomShape) {
932 // Let fromValue <= toValue
933 if (fromValue > toValue) {
934 float vw = fromValue;
935 fromValue = toValue;
936 toValue = vw;
937 }
938 }
939 switch (lineShape) {
940 case kGraphicParabolaShape:
941 p = sParabolaPoints;
942 break;
943 case kGraphicArcShape:
944 p = sArcPoints;
945 break;
946 case kGraphicSigmoidShape:
947 p = sSigmoidPoints;
948 break;
949 default:
950 p = NULL;
951 break;
952 }
953
954 if (calib == NULL)
955 [self reallocateCalibrators];
956
957 editingMode = [[self dataSource] graphicEditingMode];
958 if (editingMode == kGraphicSetMode && eventKind != kMDEventNote && eventKind != kMDEventInternalNoteOff && !shiftFlag) {
959 // Generate a series of events
960 MDTrackObject *trackObj;
961 MDEvent event;
962 trackObj = [[[MDTrackObject allocWithZone: [self zone]] init] autorelease];
963 mdptr = MDPointerNew([trackObj track]);
964 MDEventInit(&event);
965 MDSetKind(&event, eventKind);
966 MDSetCode(&event, eventCode);
967 if (t1 == t2 || v1 == v2 || lineShape == kGraphicLinearShape || lineShape == kGraphicRandomShape) {
968 MDTickType tick;
969 float v, v0;
970 tick = fromTick;
971 v0 = -100000;
972 while (1) {
973 MDTickType tick2;
974 if (t1 == t2)
975 v = 1.0f;
976 else if (lineShape == kGraphicRandomShape)
977 v = (random() % 0x10000000) / (float)0x10000000;
978 else
979 v = (float)((double)(tick - fromTick) / (toTick - fromTick));
980 v = v * (toValue - fromValue) + fromValue;
981 // Generate an event
982 MDSetTick(&event, tick);
983 if (eventKind == kMDEventTempo) {
984 v = floor(v / valueResolution) * valueResolution;
985 MDSetTempo(&event, v);
986 } else {
987 v = floor(floor(v / valueResolution) * valueResolution);
988 MDSetData1(&event, (int)(v));
989 }
990 if (v != v0 || tick >= toTick) {
991 MDPointerInsertAnEvent(mdptr, &event);
992 v0 = v;
993 }
994 // NSLog(@"tick=%ld value=%d", tick, (int)floor(v));
995 if (tick >= toTick)
996 break;
997 tick2 = MDCalibratorTimeToTick(calib, MDCalibratorTickToTime(calib, tick) + sTimeResolution);
998 if (tick2 < tick + sTickResolution)
999 tick2 = tick + sTickResolution;
1000 if (lineShape == kGraphicLinearShape) {
1001 MDTickType tick3;
1002 if (fabs(toValue - fromValue) < 1e-6)
1003 tick3 = toTick;
1004 else
1005 tick3 = tick + (MDTickType)floor(fabs(valueResolution / (toValue - fromValue) * (toTick - fromTick)));
1006 if (tick3 > tick2)
1007 tick2 = tick3;
1008 }
1009 if (tick2 > toTick)
1010 tick = toTick;
1011 else tick = tick2;
1012 }
1013 } else if (p != NULL) {
1014 float x, y, v, t;
1015 int n;
1016 n = 0;
1017 while (p[2] >= 0.0f) {
1018 // Initial point
1019 t = 0.0f;
1020 x = p[0];
1021 y = p[1];
1022 while (1) {
1023 float ta, tb, tc;
1024 float xa, xb, yc;
1025 MDTimeType tm;
1026 MDTickType tk;
1027 if (n == 0 || t > 0.0f) {
1028 // Generate an event
1029 MDSetTick(&event, (MDTickType)(x * (t2 - t1) + t1));
1030 v = y * (v2 - v1) + v1;
1031 if (eventKind == kMDEventTempo) {
1032 v = floor(v / valueResolution) * valueResolution;
1033 MDSetTempo(&event, v);
1034 } else {
1035 v = floor(floor(v / valueResolution) * valueResolution);
1036 MDSetData1(&event, (int)v);
1037 }
1038 MDPointerInsertAnEvent(mdptr, &event);
1039 // NSLog(@"t=%f tick=%ld value=%d", t, (MDTickType)(x * (t2 - t1) + t1), (int)floor(v));
1040 }
1041 if (t >= 1.0)
1042 break;
1043 xa = x + (float)fabs((double)sTickResolution / (t2 - t1));
1044 if (xa < p[6]) {
1045 ta = cubicReverseFunc(xa, p, t);
1046 } else ta = 1.0f;
1047 tm = MDCalibratorTickToTime(calib, (MDTickType)(x * (t2 - t1) + t1));
1048 if (t2 > t1)
1049 tm += sTimeResolution;
1050 else
1051 tm -= sTimeResolution;
1052 tk = MDCalibratorTimeToTick(calib, tm);
1053 xb = (float)((double)(tk - t1)) / (t2 - t1);
1054 // xb = (double)(MDCalibratorTimeToTick(calib,
1055 // MDCalibratorTickToTime(calib, (MDTickType)(x * (t2 - t1) + t1))
1056 // + sTimeResolution) - t1) / (t2 - t1);
1057 if (xb <= x) { // This can happen due to the round-off of tk
1058 tb = t;
1059 } else if (xb < p[6]) {
1060 tb = cubicReverseFunc(xb, p, t);
1061 } else tb = 1.0f;
1062 if (v1 == v2 || p[1] == p[7]) {
1063 tc = 1.0f;
1064 } else {
1065 yc = valueResolution / (v2 - v1);
1066 if (p[1] < p[7]) {
1067 yc = y + (float)fabs(yc);
1068 if (yc < p[7])
1069 tc = cubicReverseFunc(yc, p + 1, t);
1070 else tc = 1.0f;
1071 } else {
1072 yc = y - (float)fabs(yc);
1073 if (yc > p[7])
1074 tc = cubicReverseFunc(yc, p + 1, t);
1075 else tc = 1.0f;
1076 }
1077 }
1078 t = (ta < tb ? tb : ta);
1079 t = (t < tc ? tc : t);
1080 x = cubicFunc(t, p);
1081 y = cubicFunc(t, p + 1);
1082 }
1083 p += 6;
1084 n++;
1085 }
1086 }
1087 MDPointerRelease(mdptr);
1088 // MDTrackCheck([trackObj track]);
1089 if (MDTrackGetNumberOfEvents([trackObj track]) == 0)
1090 return;
1091 for (i = 0; (n = [self sortedTrackNumberAtIndex: i]) >= 0; i++) {
1092 IntGroup *insertedPositionSet;
1093 if (eventKind == kMDEventTempo && n != 0)
1094 continue;
1095 if (eventKind != kMDEventTempo && n == 0)
1096 continue;
1097 if ([self isFocusTrack:n]) {
1098 IntGroupObject *psetObj = [doc eventSetInTrack: n eventKind: eventKind eventCode: eventCode fromTick: fromTick toTick: toTick fromData: kMDMinData toData: kMDMaxData inPointSet: nil];
1099 [doc deleteMultipleEventsAt: psetObj fromTrack: n deletedEvents: NULL];
1100 if ([doc insertMultipleEvents: trackObj at: nil toTrack: n selectInsertedEvents: YES insertedPositions: &insertedPositionSet] && insertedPositionSet != NULL) {
1101 if (!shiftFlag) {
1102 MDSelectionObject *selObj = [[MDSelectionObject alloc] initWithMDPointSet:insertedPositionSet];
1103 [doc setSelection:selObj inTrack:n sender:self];
1104 [selObj release];
1105 }
1106 }
1107 }
1108 }
1109 } else {
1110 // Modify the data of the existing events
1111 for (i = 0; (n = [self sortedTrackNumberAtIndex: i]) >= 0; i++) {
1112 MDEvent *ep;
1113 MDTrack *track;
1114 MDSelectionObject *psetObj;
1115 IntGroup *pset;
1116 int idx;
1117 int32_t count, j;
1118 NSMutableData *theData;
1119 float *fp;
1120 float x, y, t, v, v0;
1121 if (![self isFocusTrack:n])
1122 continue;
1123 if (eventKind == kMDEventTempo && n != 0)
1124 continue;
1125 if (eventKind != kMDEventTempo && n == 0)
1126 continue;
1127 track = [[[dataSource document] myMIDISequence] getTrackAtIndex: n];
1128 if (track == NULL)
1129 continue;
1130 psetObj = [doc eventSetInTrack: n eventKind: (eventKind == kMDEventInternalNoteOff ? kMDEventNote : eventKind) eventCode: eventCode fromTick: fromTick toTick: toTick fromData: kMDMinData toData: kMDMaxData inPointSet: (shiftFlag ? [doc selectionOfTrack: n] : nil)];
1131 if (psetObj == nil)
1132 continue;
1133 pset = [psetObj pointSet];
1134 count = IntGroupGetCount(pset);
1135 if (count == 0)
1136 continue;
1137 theData = [NSMutableData dataWithLength: count * sizeof(float)];
1138 fp = (float *)[theData mutableBytes];
1139 mdptr = MDPointerNew(track);
1140 idx = -1;
1141 if (t1 < t2)
1142 t = 0;
1143 else t = 1;
1144 for (j = 0; j < count; j++) {
1145 ep = MDPointerForwardWithPointSet(mdptr, [psetObj pointSet], &idx);
1146 if (ep == NULL)
1147 break;
1148 if (t1 == t2) {
1149 v = toValue;
1150 } else {
1151 x = (float)(((double)MDGetTick(ep) - t1) / (t2 - t1));
1152 if (lineShape == kGraphicLinearShape)
1153 y = x;
1154 else if (lineShape == kGraphicRandomShape)
1155 y = (random() % 0x10000000) / (float)0x10000000;
1156 else if (p != NULL) {
1157 const float *pp;
1158 for (pp = p; pp[2] >= 0; pp += 6) {
1159 if (pp[0] <= x && x <= pp[6])
1160 break;
1161 }
1162 if (pp[2] >= 0) {
1163 t = cubicReverseFunc(x, pp, t);
1164 y = cubicFunc(t, pp + 1);
1165 } else break;
1166 } else break;
1167 v = y * (v2 - v1) + v1;
1168 }
1169 switch (eventKind) {
1170 case kMDEventNote:
1171 v0 = MDGetNoteOnVelocity(ep);
1172 break;
1173 case kMDEventInternalNoteOff:
1174 v0 = MDGetNoteOffVelocity(ep);
1175 break;
1176 case kMDEventTempo:
1177 v0 = MDGetTempo(ep);
1178 break;
1179 default:
1180 v0 = MDGetData1(ep);
1181 break;
1182 }
1183 if (editingMode == kGraphicAddMode) {
1184 // The center line will be zero
1185 float cy = [self convertToYValue:centerY];
1186 v = v0 + v - cy;
1187 } else if (editingMode == kGraphicScaleMode) {
1188 // The visible vertical range is 0..200%
1189 float visibleHeight = [[self superview] bounds].size.height;
1190 float cy = [self convertToYValue:centerY];
1191 float fully = [self convertToYValue:(centerY + visibleHeight * 0.5)];
1192 if (fully > cy)
1193 v = v0 * ((v - cy) / (fully - cy) + 1.0);
1194 } else if (editingMode == kGraphicLimitMaxMode) {
1195 if (v0 < v)
1196 v = v0;
1197 } else if (editingMode == kGraphicLimitMinMode) {
1198 if (v0 > v)
1199 v = v0;
1200 }
1201 if (v < minValue)
1202 v = minValue;
1203 else if (v > maxValue)
1204 v = maxValue;
1205 v = floor(v / valueResolution) * valueResolution;
1206 if (eventKind != kMDEventTempo)
1207 v = floor(v);
1208 fp[j] = v;
1209 }
1210 MDPointerRelease(mdptr);
1211 if ([doc modifyData: theData forEventKind: eventKind ofMultipleEventsAt: psetObj inTrack: n mode: MyDocumentModifySet]) {
1212 if (!shiftFlag) {
1213 [doc setSelection:psetObj inTrack:n sender:self];
1214 }
1215 }
1216 }
1217 }
1218 }
1219
1220 - (NSString *)infoTextForMousePoint:(NSPoint)pt dragging:(BOOL)flag option:(int *)option
1221 {
1222 float yval;
1223 NSString *s = nil;
1224 float valueResolution = resolution;
1225 if (option != NULL)
1226 *option = 0;
1227 if (stripDraggingMode > 0) {
1228 int32_t measure, beat, tick;
1229 [dataSource convertTick:initialDraggedTick + deltaDraggedTick toMeasure:&measure beat:&beat andTick:&tick];
1230 s = [NSString stringWithFormat:@"%g, %d.%d.%d", initialDraggedValue + deltaDraggedValue, measure, beat, tick];
1231 if (option != NULL)
1232 *option = 1;
1233 return s;
1234 }
1235 //yval = (int)floor((maxValue - minValue) * (pt.y - sVerticalMargin) / ([self frame].size.height - sVerticalMargin * 2) + minValue + 0.5);
1236 yval = [self convertToYValue:pt.y];
1237 if (yval < minValue)
1238 yval = minValue;
1239 if (yval > maxValue)
1240 yval = maxValue;
1241 if (localGraphicTool == kGraphicPencilTool) {
1242 int editingMode = [[self dataSource] graphicEditingMode];
1243 float cy = [self convertToYValue:centerY];
1244 if (editingMode == kGraphicAddMode) {
1245 s = [NSString stringWithFormat:@"%+g, ", floor((yval - cy) / valueResolution + 0.5) * valueResolution];
1246 } else if (editingMode == kGraphicScaleMode) {
1247 float visibleHeight = [[self superview] bounds].size.height;
1248 float fully = [self convertToYValue:(centerY + visibleHeight * 0.5)];
1249 if (fully > cy)
1250 yval = floor(((yval - cy) / (fully - cy) + 1.0) * 100);
1251 else yval = 100;
1252 s = [NSString stringWithFormat:@"%d%%, ", (int)yval];
1253 }
1254 }
1255 if (s == nil) {
1256 s = [NSString stringWithFormat:@"%g, ", yval];
1257 }
1258 s = [s stringByAppendingString:[super infoTextForMousePoint:pt dragging:flag option:option]];
1259 if (!flag) {
1260 return s;
1261 } else {
1262 NSPoint pt0;
1263 if (selectPoints != nil && [selectPoints count] > 0) {
1264 pt0 = [[selectPoints objectAtIndex:0] pointValue];
1265 return [NSString stringWithFormat:@"%@-%@", [self infoTextForMousePoint:pt0 dragging:NO option:option], s];
1266 } else return s;
1267 }
1268 }
1269
1270 - (void)doMouseMoved: (NSEvent *)theEvent
1271 {
1272 int track;
1273 int32_t pos;
1274 MDEvent *ep;
1275 int n;
1276 NSPoint pt = [NSEvent mouseLocation]; /* Use mouseLocation in case this is called from flagsChanged: handler (not implemented yet) */
1277 pt = [self convertPoint: [[self window] convertScreenToBase:pt] fromView: nil];
1278 localGraphicTool = [self modifyLocalGraphicTool:[[self dataSource] graphicTool]];
1279 if (localGraphicTool == kGraphicPencilTool) {
1280 [[NSCursor pencilCursor] set];
1281 // Set the vertical center of the visible rect
1282 /* NSRect bounds = [[self superview] bounds];
1283 float y = floor(bounds.origin.y + bounds.size.height * 0.5);
1284 // Round to the nearest Y value
1285 float newCenterY = [self convertFromYValue:[self convertToYValue:y]] + 0.5;
1286 if (centerY != newCenterY) {
1287 [self invalidateCenterLine];
1288 centerY = newCenterY;
1289 [self invalidateCenterLine];
1290 [self displayIfNeeded];
1291 } */
1292 return;
1293 }
1294 n = [self findStripUnderPoint: pt track: &track position: &pos mdEvent: &ep];
1295 if (n != 0) {
1296 initialDraggedTick = MDGetTick(ep);
1297 switch (eventKind) {
1298 case kMDEventNote:
1299 initialDraggedValue = MDGetNoteOnVelocity(ep);
1300 break;
1301 case kMDEventInternalNoteOff:
1302 initialDraggedValue = MDGetNoteOffVelocity(ep);
1303 break;
1304 case kMDEventTempo:
1305 initialDraggedValue = MDGetTempo(ep);
1306 break;
1307 default:
1308 initialDraggedValue = MDGetData1(ep);
1309 break;
1310 }
1311 deltaDraggedTick = 0;
1312 deltaDraggedValue = 0;
1313 if (n > 0) {
1314 stripDraggingMode = n;
1315 [dataSource updateCursorInfoForView:self atPosition:pt];
1316 stripDraggingMode = 0;
1317 }
1318 switch (n) {
1319 case 1:
1320 [[NSCursor moveAroundCursor] set];
1321 return;
1322 case 2:
1323 [[NSCursor horizontalMoveCursor] set];
1324 return;
1325 case 3:
1326 [[NSCursor verticalMoveCursor] set];
1327 return;
1328 }
1329 }
1330 [super doMouseMoved: theEvent];
1331 }
1332
1333 - (void)doMouseDown: (NSEvent *)theEvent
1334 {
1335 int32_t pos;
1336 MDEvent *ep;
1337 int track;
1338 NSRect bounds;
1339 NSPoint pt;
1340
1341 if (localGraphicTool == kGraphicPencilTool) {
1342 // Invoke the common dragging procedure without checking mouse hitting on the existing chart
1343 // The overridden method drawSelectRegion: implements the specific treatment
1344 // for this class.
1345 [super doMouseDown: theEvent];
1346 lineShape = [[self dataSource] graphicLineShape];
1347 [self invalidateCenterLine];
1348 return;
1349 }
1350
1351 lineShape = 0; /* Reset the line shape */
1352
1353 pt = [self convertPoint: [theEvent locationInWindow] fromView: nil];
1354 stripDraggingMode = [self findStripUnderPoint: pt track: &track position: &pos mdEvent: &ep];
1355 pt.x = [dataSource quantizedPixelFromPixel: pt.x];
1356 if (stripDraggingMode > 0) {
1357 float pixelQuantum;
1358 MyDocument *document = [dataSource document];
1359 if (![document isSelectedAtPosition: pos inTrack: track]) {
1360 // int i, n;
1361 // for (i = 0; (n = [self sortedTrackNumberAtIndex: i]) >= 0; i++)
1362 // [document unselectAllEventsInTrack: n sender: self];
1363 [document unselectAllEventsInAllTracks: self];
1364 [document selectEventAtPosition: pos inTrack: track sender: self];
1365 }
1366 draggingStartPoint = draggingPoint = pt;
1367 horizontal = (stripDraggingMode == 2);
1368 [self displayIfNeeded];
1369 // Calculate limit rectangle for the dragging point
1370 selectionRect = [self boundRectForSelection];
1371 bounds = [self bounds];
1372 limitRect = NSMakeRect(
1373 pt.x - (selectionRect.origin.x - bounds.origin.x),
1374 1,
1375 bounds.size.width - selectionRect.size.width,
1376 bounds.size.height - 2);
1377 pixelQuantum = [dataSource pixelQuantum];
1378 limitRect.origin.x = [dataSource quantizedPixelFromPixel: limitRect.origin.x];
1379 if (limitRect.origin.x < 0.0)
1380 limitRect.origin.x += pixelQuantum;
1381 limitRect.size.width = (CGFloat)(floor(limitRect.size.width / pixelQuantum) * pixelQuantum);
1382 if (limitRect.size.width + limitRect.origin.x > bounds.origin.x + bounds.size.width)
1383 limitRect.size.width -= pixelQuantum;
1384 // [super doMouseDown: theEvent];
1385 return;
1386 }
1387
1388 [super doMouseDown: theEvent];
1389 }
1390
1391 - (void)doMouseDragged: (NSEvent *)theEvent
1392 {
1393 // Pencil mode: invoke the common dragging procedure
1394 // The overridden method drawSelectRegion: implements the specific treatment
1395 // for this class.
1396 if (lineShape > 0) {
1397 [super doMouseDragged: theEvent];
1398 // Redraw the baseline region
1399 [self invalidateCenterLine];
1400 return;
1401 }
1402
1403 if (stripDraggingMode > 0) {
1404 NSPoint pt = [self convertPoint: [theEvent locationInWindow] fromView: nil];
1405 BOOL optionDown = (([theEvent modifierFlags] & NSAlternateKeyMask) != 0);
1406 float valueResolution = resolution;
1407 pt.x = [dataSource quantizedPixelFromPixel: pt.x];
1408 // Support autoscroll (cf. GraphicClientView.mouseDragged)
1409 /* if (autoscrollTimer != nil) {
1410 [autoscrollTimer invalidate];
1411 [autoscrollTimer release];
1412 autoscrollTimer = nil;
1413 } */
1414 if (stripDraggingMode == 1) {
1415 /* horizontal or vertical, depending on the mouse position */
1416 NSSize delta;
1417 delta.width = (CGFloat)fabs(pt.x - draggingStartPoint.x);
1418 delta.height = (CGFloat)fabs(pt.y - draggingStartPoint.y);
1419 if (delta.width > 3 || delta.height > 3)
1420 horizontal = (delta.width > delta.height);
1421 if (horizontal) {
1422 if (optionDown)
1423 [[NSCursor horizontalMovePlusCursor] set];
1424 else
1425 [[NSCursor horizontalMoveCursor] set];
1426 } else {
1427 /* Note: no duplicate on vertical move with option key */
1428 [[NSCursor verticalMoveCursor] set];
1429 }
1430 }
1431 if (horizontal)
1432 pt.y = draggingStartPoint.y;
1433 else
1434 pt.x = draggingStartPoint.x;
1435 /* pt = [self convertPoint: pt toView: nil];
1436 // [self invalidateDraggingRegion];
1437 if ([self autoscroll: [theEvent mouseEventWithLocation: pt]])
1438 autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.2 target: self selector:@selector(autoscrollTimerCallback:) userInfo: theEvent repeats: NO] retain];
1439 pt = [self convertPoint: pt fromView: nil]; */
1440 if (pt.x < limitRect.origin.x)
1441 pt.x = limitRect.origin.x;
1442 else if (pt.x > limitRect.origin.x + limitRect.size.width)
1443 pt.x = limitRect.origin.x + limitRect.size.width;
1444 if (pt.y < limitRect.origin.y)
1445 pt.y = limitRect.origin.y;
1446 else if (pt.y > limitRect.origin.y + limitRect.size.height)
1447 pt.y = limitRect.origin.y + limitRect.size.height;
1448 [self invalidateDraggingRegion];
1449 draggingPoint = pt;
1450 [self invalidateDraggingRegion];
1451 pt.x = pt.x - draggingStartPoint.x;
1452 pt.y = pt.y - draggingStartPoint.y;
1453 deltaDraggedTick = (MDTickType)floor(pt.x / [dataSource pixelsPerTick] + 0.5);
1454 deltaDraggedValue = floor((pt.y * (maxValue - minValue) / ([self bounds].size.height - sVerticalMargin * 2)) / valueResolution + 0.5) * valueResolution;
1455 [self displayIfNeeded];
1456 } else [super doMouseDragged: theEvent];
1457 }
1458
1459 - (void)doMouseUp: (NSEvent *)theEvent
1460 {
1461 int i, trackNo;
1462 float ppt;
1463 float height;
1464 MyDocument *document;
1465 MDTickType minTick, maxTick;
1466 NSRect bounds;
1467 float valueResolution = resolution;
1468
1469 // Pencil mode: edit the strip chart values according to the line shape and
1470 // editing mode
1471 if (lineShape > 0) {
1472 [self doPencilEdit];
1473 lineShape = 0;
1474 [self updateCenterY];
1475 return;
1476 }
1477
1478 if (stripDraggingMode > 0) {
1479 NSPoint pt;
1480 MDTickType deltaTick;
1481 float deltaValue;
1482 BOOL optionDown = (([theEvent modifierFlags] & NSAlternateKeyMask) != 0);
1483 [self invalidateDraggingRegion];
1484 pt.x = draggingPoint.x - draggingStartPoint.x;
1485 pt.y = draggingPoint.y - draggingStartPoint.y;
1486 deltaTick = (MDTickType)floor(pt.x / [dataSource pixelsPerTick] + 0.5);
1487 deltaValue = floor((pt.y * (maxValue - minValue) / ([self bounds].size.height - sVerticalMargin * 2)) / valueResolution + 0.5) * valueResolution;
1488 [dataSource dragEventsOfKind: eventKind andCode: eventCode byTick: deltaTick andValue: deltaValue sender: self optionFlag: optionDown];
1489 stripDraggingMode = 0;
1490 [dataSource updateCursorInfoForView:self atPosition:draggingPoint];
1491 [self displayIfNeeded];
1492 return;
1493 } else if (!isDragging || isLoupeDragging) {
1494 [super doMouseUp: theEvent];
1495 return;
1496 }
1497
1498 /* Change selection */
1499 bounds = [[self selectionPath] bounds];
1500 height = [self bounds].size.height;
1501 ppt = [dataSource pixelsPerTick];
1502 minTick = (MDTickType)(bounds.origin.x / ppt);
1503 maxTick = (MDTickType)((bounds.origin.x + bounds.size.width) / ppt);
1504 document = (MyDocument *)[dataSource document];
1505 for (i = 0; (trackNo = [self sortedTrackNumberAtIndex: i]) >= 0; i++) {
1506 MDTrack *track;
1507 MDPointer *pt;
1508 MDEvent *ep;
1509 IntGroup *pset;
1510 MDSelectionObject *obj;
1511 if (![self isFocusTrack:trackNo])
1512 continue;
1513 track = [[document myMIDISequence] getTrackAtIndex: trackNo];
1514 if (track == NULL)
1515 continue;
1516 pt = MDPointerNew(track);
1517 if (pt == NULL)
1518 break;
1519 pset = IntGroupNew();
1520 if (pset == NULL)
1521 break;
1522 MDPointerJumpToTick(pt, minTick);
1523 MDPointerBackward(pt);
1524 while ((ep = MDPointerForward(pt)) != NULL && MDGetTick(ep) < maxTick) {
1525 NSPoint point;
1526 if (MDGetKind(ep) != eventKind)
1527 continue;
1528 if (eventCode != -1 && MDGetCode(ep) != eventCode)
1529 continue;
1530
1531 // point.y = (CGFloat)ceil((getYValue(ep, eventKind) - minValue) / (maxValue - minValue) * (height - 2)) + 1;
1532 point.y = [self convertFromYValue:getYValue(ep, eventKind)];
1533 point.x = (CGFloat)floor(MDGetTick(ep) * ppt);
1534 // point.x = floor(MDGetTick(ep) * ppt);
1535 // point.y = floor(MDGetCode(ep) * ys + 0.5) + 0.5 * ys;
1536 if ([self isPointInSelectRegion: point]) {
1537 if (IntGroupAdd(pset, MDPointerGetPosition(pt), 1) != kMDNoError)
1538 break;
1539 }
1540 }
1541 obj = [[MDSelectionObject allocWithZone: [self zone]] initWithMDPointSet: pset];
1542 if (currentModifierFlags & NSShiftKeyMask) {
1543 [document toggleSelection: obj inTrack: trackNo sender: self];
1544 } else {
1545 [document setSelection: obj inTrack: trackNo sender: self];
1546 }
1547 [obj release];
1548 IntGroupRelease(pset);
1549 MDPointerRelease(pt);
1550 }
1551 }
1552
1553 - (void)startExternalDraggingAtPoint:(NSPoint)aPoint mode:(int)aMode
1554 {
1555 stripDraggingMode = aMode;
1556 draggingStartPoint = draggingPoint = aPoint;
1557 [self setNeedsDisplay:YES];
1558 }
1559
1560 - (void)endExternalDragging
1561 {
1562 stripDraggingMode = 0;
1563 [self setNeedsDisplay:YES];
1564 }
1565
1566 - (void)setExternalDraggingPoint:(NSPoint)aPoint
1567 {
1568 draggingPoint = aPoint;
1569 [self setNeedsDisplay:YES];
1570 }
1571
1572 @end

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