Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/Classes/ListWindowController.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 190 - (show annotations) (download)
Sat Mar 14 14:32:34 2020 UTC (4 years, 2 months ago) by toshinagata1964
File size: 53830 byte(s)
Copy & paste for multiple tracks are reworked
1 //
2 // ListWindowController.m
3 //
4 // Created by Toshi Nagata.
5 /*
6 Copyright (c) 2000-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 "ListWindowController.h"
19 #import "NSWindowControllerAdditions.h"
20 #import "MyDocument.h"
21 #import "MyMIDISequence.h"
22 #import "MyTableHeaderView.h"
23 #import "MyTableView.h"
24 #import "MyDocument.h"
25 //#import "MyFieldEditor.h"
26 #import "MDObjects.h"
27 #import "EventFilterPanelController.h"
28 #import "MDRubyExtern.h"
29
30 @implementation ListWindowController
31
32 /* イベント表示の色分け */
33 static NSColor *sTextMetaColor = nil;
34 static NSColor *sMetaColor = nil;
35 static NSColor *sSysexColor = nil;
36 static NSColor *sProgramColor = nil;
37 static NSColor *sControlColor = nil;
38 static NSColor *sNoteColor = nil;
39 static NSColor *sKeyPresColor = nil;
40 static NSColor *sMiscColor = nil;
41
42 #pragma mark ====== NSWindowController(Addition) Overrides ======
43
44 - (id)init {
45 self = [super initWithWindowNibName:@"ListWindow"];
46 if (self) {
47 [self setShouldCloseDocument: NO];
48 }
49 return self;
50 }
51
52 - (void)dealloc {
53 [[NSNotificationCenter defaultCenter]
54 removeObserver:self];
55 if (myPointer != NULL)
56 MDPointerRelease(myPointer);
57 if (myCalibrator != NULL)
58 MDCalibratorRelease(myCalibrator);
59 /* [myFieldEditor release]; */
60 [super dealloc];
61 }
62
63 - (void)windowDidLoad {
64 NSArray *array;
65 int n;
66 NSFont *font;
67 NSTableColumn *column;
68 NSTableHeaderView *headerView;
69 MyTableHeaderView *myHeaderView;
70 MDSequence *seq;
71 NSMenu *menu;
72
73 [super windowDidLoad];
74
75 myPlayingRow = -1;
76
77 array = [myEventTrackView tableColumns];
78 n = (int)[array count];
79 font = [NSFont userFixedPitchFontOfSize: 10];
80
81 [myEventTrackView setRowHeight: [[NSWindowController sharedLayoutManager] defaultLineHeightForFont:font] + 2];
82 while (--n >= 0) {
83 column = (NSTableColumn *)[array objectAtIndex: n];
84 [[column dataCell] setFont: font];
85 }
86 headerView = [myEventTrackView headerView];
87 myHeaderView = [[[MyTableHeaderView allocWithZone: [self zone]] initWithFrame:[headerView frame]] autorelease];
88 [myEventTrackView setHeaderView:myHeaderView];
89
90 MDEventInit(&myDefaultEvent);
91 MDSetKind(&myDefaultEvent, kMDEventNote);
92 MDSetCode(&myDefaultEvent, 60);
93 MDSetNoteOnVelocity(&myDefaultEvent, 64);
94 MDSetNoteOffVelocity(&myDefaultEvent, 0);
95 seq = [[[self document] myMIDISequence] mySequence];
96 MDSetDuration(&myDefaultEvent, (seq == NULL ? 48 : MDSequenceGetTimebase(seq)));
97
98 /* Register the notification */
99 [[NSNotificationCenter defaultCenter]
100 addObserver:self
101 selector:@selector(updateEventTableView:)
102 name:MyDocumentTrackModifiedNotification
103 object:[self document]];
104
105 [[NSNotificationCenter defaultCenter]
106 addObserver:self
107 selector:@selector(trackInserted:)
108 name:MyDocumentTrackInsertedNotification
109 object:[self document]];
110
111 [[NSNotificationCenter defaultCenter]
112 addObserver:self
113 selector:@selector(trackDeleted:)
114 name:MyDocumentTrackDeletedNotification
115 object:[self document]];
116
117 [[NSNotificationCenter defaultCenter]
118 addObserver:self
119 selector:@selector(documentSelectionDidChange:)
120 name:MyDocumentSelectionDidChangeNotification
121 object:[self document]];
122
123 [[NSNotificationCenter defaultCenter]
124 addObserver:self
125 selector:@selector(editingRangeChanged:)
126 name:MyDocumentEditingRangeDidChangeNotification
127 object:[self document]];
128
129 [[NSNotificationCenter defaultCenter]
130 addObserver:self
131 selector:@selector(showPlayPosition:)
132 name:MyDocumentPlayPositionNotification
133 object:[self document]];
134
135 [NSBundle loadNibNamed:@"EventKindContextMenu" owner:kindDataCell];
136 menu = [[[NSMenu alloc] init] autorelease];
137 [dataDataCell setMenu:menu];
138 }
139
140 - (NSString *)windowTitleForDocumentDisplayName:(NSString *)displayName {
141 if (myTrack != NULL) {
142 char buf[256];
143 MDTrackGetName(myTrack, buf, sizeof buf);
144 // NSLog(@"Track name = %s\n", buf);
145 if (buf[0] == 0) {
146 snprintf(buf, sizeof buf, "(Track %d)", myTrackNumber);
147 }
148 return [NSString stringWithFormat:@"%@:%s", displayName, buf];
149 } else return displayName;
150 }
151
152 - (MyMIDISequence *)myMIDISequence {
153 return [[self document] myMIDISequence];
154 }
155
156 - (BOOL)containsTrack: (int)index
157 {
158 return (index == myTrackNumber);
159 }
160
161 - (void)addTrack: (int)index
162 {
163 if (index < 0 || index >= [[self myMIDISequence] trackCount])
164 return;
165 myTrackNumber = index;
166 myTrack = [[self myMIDISequence] getTrackAtIndex: index];
167 if (myPointer != NULL)
168 MDPointerRelease(myPointer);
169 if (myCalibrator != NULL)
170 MDCalibratorRelease(myCalibrator);
171 myPointer = MDPointerNew(myTrack);
172 MDPointerSetAutoAdjust(myPointer, 1);
173 myCalibrator = MDCalibratorNew([[self myMIDISequence] mySequence], myTrack, kMDEventTimeSignature, -1);
174 MDCalibratorAppend(myCalibrator, myTrack, kMDEventTempo, -1);
175 MDCalibratorAppend(myCalibrator, myTrack, kMDEventControl, 0); // Bank select MSB
176 MDCalibratorAppend(myCalibrator, myTrack, kMDEventControl, 32); // Bank select LSB
177 myRow = -1;
178 myCount = -1;
179 myTickColumnCount = 1;
180 isLastRowSelected = NO;
181 [self updateEventTableView:nil];
182 }
183
184 - (BOOL)isFocusTrack: (int)trackNum
185 {
186 return (trackNum == myTrackNumber);
187 }
188
189 #pragma mark ====== Notification handlers ======
190
191 - (void)trackInserted: (NSNotification *)notification
192 {
193 myTrackNumber = [[self myMIDISequence] lookUpTrack: myTrack];
194 }
195
196 - (void)trackDeleted: (NSNotification *)notification
197 {
198 myTrackNumber = [[self myMIDISequence] lookUpTrack: myTrack];
199 if (myTrackNumber < 0) {
200 // This track is deleted
201 [[self window] performClose: nil];
202 }
203 }
204
205 - (void)documentSelectionDidChange: (NSNotification *)notification
206 {
207 NSDictionary *info = [notification userInfo];
208 id keys;
209 if (selectionDidChangeNotificationLevel > 0)
210 return;
211 if ([myEventTrackView editedRow] >= 0)
212 return; // Don't touch during TableView editing
213 keys = [info objectForKey: @"keys"];
214 if (keys != nil && [keys containsObject: [NSNumber numberWithInt: myTrackNumber]])
215 [self reloadSelection];
216 }
217
218 - (void)updateEventTableView:(NSNotification *)notification
219 {
220 if (notification == nil || [[[notification userInfo] objectForKey: @"track"] intValue] == myTrackNumber) {
221 // Reset myRow and myPointer
222 // MDPointerSetPosition(myPointer, -1);
223 // MDPointerForwardWithSelector(myPointer, EventSelector, myFilter);
224 // myRow = 0;
225 myCount = -1;
226 [myEventTrackView reloadData];
227
228 // Synchronize window title
229 [self synchronizeWindowTitleWithDocumentName];
230
231 [self updateInfoText];
232 [self updateEditingRangeText];
233 }
234 }
235
236 - (int)maxRowBeforeTick:(MDTickType)tick
237 {
238 MDPointer *pt;
239 int lastRow, n, row;
240 if (tick < 0)
241 return -1;
242 lastRow = (int)[self numberOfRowsInTableView:myEventTrackView] - 1;
243 pt = MDPointerNew(myTrack);
244 if (tick >= MDTrackGetDuration(myTrack))
245 return lastRow;
246 if (MDPointerJumpToTick(pt, tick + 1)) {
247 n = MDPointerGetPosition(pt) - 1;
248 } else {
249 n = MDTrackGetNumberOfEvents(myTrack) - 1;
250 }
251 [self rowForEventPosition:n nearestRow:&row];
252 MDPointerRelease(pt);
253 return row;
254 }
255
256 - (void)showPlayPosition:(NSNotification *)notification
257 {
258 float beat = [[[notification userInfo] objectForKey: @"position"] floatValue];
259 MDTickType tick = beat * [[self document] timebase];
260 int32_t n;
261 int nearestRow, maxRow;
262
263 /* If event tracking in this window, then don't autoscroll to play position */
264 if ([[[NSRunLoop currentRunLoop] currentMode] isEqualToString:NSEventTrackingRunLoopMode] && [[NSApp currentEvent] window] == [self window])
265 return;
266
267 /* Look for the event representing tick */
268 maxRow = (int)[self numberOfRowsInTableView:myEventTrackView] - 1;
269 nearestRow = [self maxRowBeforeTick:tick];
270 if (nearestRow >= 0) {
271 NSRange visibleRowRange = [myEventTrackView rowsInRect:[myEventTrackView visibleRect]];
272 if (!NSLocationInRange(nearestRow, visibleRowRange)) {
273 n = nearestRow + (int)visibleRowRange.length - 3;
274 if (n >= maxRow)
275 n = maxRow;
276 [myEventTrackView scrollRowToVisible:n];
277 }
278 }
279 [myEventTrackView setUnderlineRow:nearestRow];
280 }
281
282 #pragma mark ====== NSTableView handling methods ======
283
284 static int
285 EventSelector(const MDEvent *ep, int32_t position, void *inUserData)
286 {
287 ListWindowFilterRecord *filter = (ListWindowFilterRecord *)inUserData;
288 MDEventKind kind;
289 int i;
290 BOOL retval;
291 if (filter == NULL)
292 return 1;
293 if (ep == NULL)
294 return 0;
295 kind = MDGetKind(ep);
296 if (filter->mode == 0)
297 return 1;
298 if (kind == kMDEventNull)
299 return 1; /* Always selected */
300 retval = (filter->mode == 1);
301 for (i = 0; i < filter->count && filter->table[i].kind != kMDEventStop; i++) {
302 if (kind == filter->table[i].kind) {
303 if ((kind == kMDEventControl || kind == kMDEventMetaText || kind == kMDEventMetaMessage || kind == kMDEventMeta) && MDGetCode(ep) != filter->table[i].data)
304 continue;
305 return retval;
306 }
307 }
308 return !retval;
309 }
310
311 - (void)reloadSelection
312 {
313 MDSelectionObject *obj = [[self document] selectionOfTrack: myTrackNumber];
314 IntGroup *pset = [obj pointSet];
315 NSMutableIndexSet *iset = [NSMutableIndexSet indexSet];
316 int32_t min = IntGroupMinimum(pset);
317 int32_t max = IntGroupMaximum(pset);
318 selectionDidChangeNotificationLevel++;
319 if (myTrack != NULL && myPointer != NULL) {
320 MDPointerSetPosition(myPointer, -1);
321 myRow = 0;
322 while (MDPointerForwardWithSelector(myPointer, EventSelector, myFilter) != NULL) {
323 int32_t pos = MDPointerGetPosition(myPointer);
324 if (pos >= min && IntGroupLookup(pset, pos, NULL))
325 [iset addIndex: myRow];
326 if (pos > max)
327 break;
328 myRow++;
329 }
330 }
331 if (obj->isEndOfTrackSelected) {
332 isLastRowSelected = YES;
333 [iset addIndex: myCount];
334 }
335 [myEventTrackView selectRowIndexes: iset byExtendingSelection: NO];
336 [myEventTrackView reloadData];
337 selectionDidChangeNotificationLevel--;
338 }
339
340 - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
341 {
342 if (myTrack != NULL && myPointer != NULL) {
343 if (myCount == -1) {
344 /* Count the number of events to display */
345 MDPointerSetPosition(myPointer, -1);
346 myCount = 0;
347 while (MDPointerForwardWithSelector(myPointer, EventSelector, myFilter) != NULL)
348 myCount++;
349 myRow = myCount;
350 [myInfoText setStringValue:[NSString localizedStringWithFormat:@"%5d events, %5d shown",
351 (int32_t)MDTrackGetNumberOfEvents(myTrack), myCount]];
352 }
353 return myCount + 1;
354 } else return 0;
355 }
356
357 - (MDEvent *)eventPointerForTableRow:(int)rowIndex
358 {
359 int32_t pos = [self eventPositionForTableRow: rowIndex];
360 if (pos < 0)
361 return NULL;
362 if (rowIndex == myCount)
363 return NULL; /* End of track */
364 return MDPointerCurrent(myPointer);
365 }
366
367 - (int32_t)eventPositionForTableRow:(int)rowIndex
368 {
369 if (myTrack == NULL || myPointer == NULL)
370 return -1;
371 if (myCount == -1)
372 /* Recalculate myCount */
373 [self numberOfRowsInTableView: myEventTrackView];
374 if (rowIndex > myCount)
375 return -1;
376 if (rowIndex == myCount)
377 return MDTrackGetNumberOfEvents(myTrack); /* End-of-track */
378 if (myRow > rowIndex) {
379 while (myRow > rowIndex) {
380 MDPointerBackwardWithSelector(myPointer, EventSelector, myFilter);
381 myRow--;
382 }
383 } else if (myRow < rowIndex) {
384 while (myRow < rowIndex) {
385 MDPointerForwardWithSelector(myPointer, EventSelector, myFilter);
386 myRow++;
387 }
388 }
389 return MDPointerGetPosition(myPointer);
390 }
391
392 - (MDTickType)eventTickForTableRow:(int)rowIndex
393 {
394 MDEvent *ep = [self eventPointerForTableRow: rowIndex];
395 if (ep != NULL)
396 return MDGetTick(ep);
397 if (rowIndex == myCount)
398 return MDTrackGetDuration(myTrack);
399 else return kMDNegativeTick;
400 }
401
402 - (int)rowForEventPosition: (int32_t)position nearestRow: (int *)nearestRow
403 {
404 int32_t mypos;
405 if (myTrack == NULL || myPointer == NULL)
406 return -1;
407 if (myCount == -1)
408 /* Recalculate myCount */
409 [self numberOfRowsInTableView: myEventTrackView];
410 if (position < 0) {
411 if (nearestRow != NULL)
412 *nearestRow = -1;
413 return -1;
414 } else if (position >= MDTrackGetNumberOfEvents(myTrack)) {
415 if (nearestRow != NULL)
416 *nearestRow = myCount;
417 return -1;
418 }
419 mypos = MDPointerGetPosition(myPointer);
420 if (mypos == position) {
421 if (nearestRow != NULL)
422 *nearestRow = myRow;
423 return myRow;
424 } else if (mypos > position) {
425 do {
426 myRow--;
427 } while (MDPointerBackwardWithSelector(myPointer, EventSelector, myFilter) && (mypos = MDPointerGetPosition(myPointer)) > position);
428 if (nearestRow != NULL)
429 *nearestRow = myRow;
430 } else {
431 do {
432 myRow++;
433 } while (MDPointerForwardWithSelector(myPointer, EventSelector, myFilter) && (mypos = MDPointerGetPosition(myPointer)) < position);
434 if (nearestRow != NULL)
435 *nearestRow = (mypos == position ? myRow : myRow - 1);
436 }
437 if (mypos == position)
438 return myRow;
439 else return -1;
440 }
441
442 - (void)updateInfoText
443 {
444 int n = MDTrackGetNumberOfEvents(myTrack) + 1;
445 int m = (int)[myEventTrackView numberOfSelectedRows];
446 [myInfoText setStringValue:[NSString stringWithFormat:@"%d row%s/%d shown/%d selected", n, (n == 1 ? "" : "s"), (int)myCount + 1, m]];
447 }
448
449 - (void)updateEditingRangeText
450 {
451 MDTickType startTick, endTick;
452 NSString *startString, *endString;
453 [(MyDocument *)[self document] getEditingRangeStart: &startTick end: &endTick];
454 if (startTick < 0 || startTick >= kMDMaxTick) {
455 startString = endString = @"";
456 } else {
457 int32_t startMeasure, startBeat, startSubTick;
458 int32_t endMeasure, endBeat, endSubTick;
459 MDCalibratorTickToMeasure(myCalibrator, startTick, &startMeasure, &startBeat, &startSubTick);
460 MDCalibratorTickToMeasure(myCalibrator, endTick, &endMeasure, &endBeat, &endSubTick);
461 startString = [NSString stringWithFormat: @"%4d:%2d:%4d", startMeasure, startBeat, startSubTick];
462 endString = [NSString stringWithFormat: @"%4d:%2d:%4d", endMeasure, endBeat, endSubTick];
463 }
464
465 [startEditingRangeText setStringValue: startString];
466 [endEditingRangeText setStringValue: endString];
467 }
468
469 - (id)tableView:(NSTableView *)aTableView
470 objectValueForTableColumn:(NSTableColumn *)aTableColumn
471 row:(NSInteger)rowIndex
472 {
473 id identifier = [aTableColumn identifier];
474 MDEvent *ep;
475 MDTickType tick;
476 MDTimeType time;
477 int32_t bar, beat, count;
478
479 if (rowIndex == myCount) {
480 /* The "end of track" row */
481 ep = NULL;
482 tick = MDTrackGetDuration(myTrack);
483 } else {
484 ep = [self eventPointerForTableRow:(int)rowIndex];
485 if (ep == NULL)
486 return nil;
487 tick = MDGetTick(ep);
488 }
489
490 if ([@"bar" isEqualToString: identifier]) {
491 MDCalibratorTickToMeasure(myCalibrator, tick, &bar, &beat, &count);
492 return [NSString localizedStringWithFormat:@"%4d:%2d:%4d", (int)bar, (int)beat, (int)count];
493 } else if ([@"sec" isEqualToString: identifier]) {
494 time = MDCalibratorTickToTime(myCalibrator, tick);
495 return [NSString localizedStringWithFormat:@"%8.3f", (double)(time / 1000000.0)];
496 } else if ([@"msec" isEqualToString: identifier]) {
497 time = MDCalibratorTickToTime(myCalibrator, tick);
498 return [NSString localizedStringWithFormat:@"%8.2f", (double)(time / 1000.0)];
499 } else if ([@"count" isEqualToString: identifier]) {
500 return [NSString localizedStringWithFormat:@"%8d", (int32_t)tick];
501 } else if ([@"deltacount" isEqualToString: identifier]) {
502 if (rowIndex == myCount) {
503 ep = [self eventPointerForTableRow:(int)rowIndex - 1];
504 if (ep != NULL)
505 tick -= MDGetTick(ep);
506 } else if (rowIndex > 0) {
507 ep = MDPointerBackward(myPointer);
508 MDPointerForward(myPointer);
509 if (ep != NULL)
510 tick -= MDGetTick(ep);
511 }
512 return [NSString localizedStringWithFormat:@"%6d", (int32_t)tick];
513 } else if ([@"st" isEqualToString: identifier]) {
514 ep = MDPointerForward(myPointer);
515 MDPointerBackward(myPointer);
516 if (ep == NULL)
517 tick = 0;
518 else tick = MDGetTick(ep) - tick;
519 return [NSString localizedStringWithFormat:@"%6d", (int32_t)tick];
520 }
521 if ([@"ch" isEqualToString: identifier]) {
522 if (ep != NULL && MDIsChannelEvent(ep))
523 return [NSString localizedStringWithFormat:@"%d", (int)MDGetChannel(ep)];
524 else return nil;
525 } else {
526 char eventStr[2048];
527 if ([@"event" isEqualToString: identifier]) {
528 if (ep == NULL)
529 return @"--End--";
530 else
531 MDEventToKindString(ep, eventStr, sizeof(eventStr));
532 } else if (ep != NULL) {
533 if ([@"duration" isEqualToString: identifier]) {
534 MDEventToGTString(ep, eventStr, sizeof(eventStr));
535 } else if ([@"data" isEqualToString: identifier]) {
536 if (MDGetKind(ep) == kMDEventProgram) {
537 MDEvent *ep1;
538 int bank = 0;
539 int n, data1;
540 int32_t dev;
541 int32_t pos;
542 data1 = MDGetData1(ep);
543 n = snprintf(eventStr, sizeof eventStr, "%d:", data1);
544 pos = MDPointerGetPosition(myPointer);
545 MDCalibratorJumpToPositionInTrack(myCalibrator, pos, myTrack);
546 ep1 = MDCalibratorGetEvent(myCalibrator, myTrack, kMDEventControl, 0);
547 if (ep1 != NULL)
548 bank = MDGetData1(ep1) * 256;
549 ep1 = MDCalibratorGetEvent(myCalibrator, myTrack, kMDEventControl, 32);
550 if (ep1 != NULL)
551 bank += MDGetData1(ep1);
552 dev = MDTrackGetDevice(myTrack);
553 if (MDPlayerGetPatchName(dev, bank, data1, eventStr + n, sizeof(eventStr) - n) < 0) {
554 // Patch name was not available
555 snprintf(eventStr, sizeof eventStr, "%d", data1);
556 }
557 } else MDEventToDataString(ep, eventStr, sizeof(eventStr));
558 } else return nil;
559 } else return nil;
560 return [NSString stringWithUTF8String:eventStr];
561 }
562 return nil;
563 }
564
565 - (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
566 {
567 id identifier = [aTableColumn identifier];
568 MDEvent *ep;
569 NSColor *color;
570
571 [(ContextMenuTextFieldCell *)aCell setDrawsUnderline:(rowIndex == myPlayingRow)];
572
573 if ([@"bar" isEqualToString: identifier]
574 || [@"sec" isEqualToString: identifier]
575 || [@"msec" isEqualToString: identifier]
576 || [@"count" isEqualToString: identifier]
577 || [@"ch" isEqualToString: identifier])
578 return;
579
580 /* Set color according to the event kind */
581 /* Note: use black color if the row is selected (for better visibility) */
582 if ([aTableView isRowSelected: rowIndex]) {
583 [aCell setTextColor: [NSColor blackColor]];
584 return;
585 }
586
587 ep = [self eventPointerForTableRow:rowIndex];
588 if (ep == NULL || MDIsTextMetaEvent(ep)) {
589 if (sTextMetaColor == nil)
590 sTextMetaColor = [[NSColor colorWithDeviceRed: 0.5f green: 0.0f blue: 0.5f alpha: 1.0f] retain];
591 color = sTextMetaColor;
592 } else if (MDIsMetaEvent(ep)) {
593 if (sMetaColor == nil)
594 sMetaColor = [[NSColor colorWithDeviceRed: 0.0f green: 0.5f blue: 0.2f alpha: 1.0f] retain];
595 color = sMetaColor;
596 } else if (MDIsSysexEvent(ep)) {
597 if (sSysexColor == nil)
598 sSysexColor = [[NSColor colorWithDeviceRed: 0.5f green: 0.5f blue: 0.0f alpha: 1.0f] retain];
599 color = sSysexColor;
600 } else {
601 switch (MDGetKind(ep)) {
602 case kMDEventProgram:
603 if (sProgramColor == nil)
604 sProgramColor = [[NSColor colorWithDeviceRed: 0.0f green: 0.0f blue: 1.0f alpha: 1.0f] retain];
605 color = sProgramColor;
606 break;
607 case kMDEventControl:
608 /* case kMDEventRPNControl:
609 case kMDEventRPNFine:
610 case kMDEventRPNInc: */
611 if (sControlColor == nil)
612 sControlColor = [[NSColor colorWithDeviceRed: 0.0f green: 0.5f blue: 0.0f alpha: 1.0f] retain];
613 color = sControlColor;
614 break;
615 case kMDEventNote:
616 if (sNoteColor == nil)
617 sNoteColor = [[NSColor blackColor] retain];
618 color = sNoteColor;
619 break;
620 case kMDEventKeyPres:
621 case kMDEventChanPres:
622 if (sKeyPresColor == nil)
623 sKeyPresColor = [[NSColor colorWithDeviceRed: 0.5f green: 0.2f blue: 0.0f alpha: 1.0f] retain];
624 color = sKeyPresColor;
625 break;
626 default:
627 if (sMiscColor == nil)
628 sMiscColor = [[NSColor colorWithDeviceRed: 0.5f green: 0.0f blue: 0.0f alpha: 1.0f] retain];
629 color = sMiscColor;
630 break;
631 }
632 }
633 [aCell setTextColor: color];
634 }
635
636 static void
637 newEventFromKindAndCode(MDEvent *ep, MDEventFieldData ed)
638 {
639 MDEventDefault(ep, ed.ucValue[0]);
640 MDSetCode(ep, ed.ucValue[1]);
641 }
642
643 - (void)tableView:(NSTableView *)aTableView
644 setObjectValue:(id)anObject
645 forTableColumn:(NSTableColumn *)aTableColumn
646 row:(NSInteger)rowIndex
647 {
648 id identifier = [aTableColumn identifier];
649 // const char *descStr = [[anObject description] UTF8String];
650 MDEvent *ep;
651 MDTickType tick;
652 MDTimeType time;
653 MyDocument *document;
654 int32_t trackNo, position;
655 char buf[2048];
656 BOOL mod = NO;
657
658 strncpy(buf, [[anObject description] UTF8String], sizeof buf - 1);
659 buf[sizeof buf - 1] = 0;
660
661 /* Prepare arguments for posting action */
662 trackNo = myTrackNumber; /* [[self myMIDISequence] lookUpTrack: [self MIDITrack]]; */
663 position = [self eventPositionForTableRow:(int)rowIndex];
664 document = (MyDocument *)[self document];
665
666 tick = kMDNegativeTick;
667 if ([@"bar" isEqualToString: identifier]) {
668 int32_t bar, beat, count;
669 if (MDEventParseTickString(buf, &bar, &beat, &count) < 3)
670 return;
671 tick = MDCalibratorMeasureToTick(myCalibrator, bar, beat, count);
672 } else if ([@"sec" isEqualToString: identifier]) {
673 time = atof(buf) * 1000000.0;
674 tick = MDCalibratorTimeToTick(myCalibrator, time);
675 } else if ([@"msec" isEqualToString: identifier]) {
676 time = atof(buf) * 1000.0;
677 tick = MDCalibratorTimeToTick(myCalibrator, time);
678 } else if ([@"count" isEqualToString: identifier]) {
679 tick = (int32_t)atol(buf);
680 } else if ([@"deltacount" isEqualToString: identifier]) {
681 tick = (int32_t)atol(buf);
682 if (rowIndex > 0) {
683 ep = [self eventPointerForTableRow:(int)rowIndex - 1];
684 if (ep != NULL)
685 tick += MDGetTick(ep);
686 }
687 }
688
689 ep = [self eventPointerForTableRow:(int)rowIndex];
690 if (tick != kMDNegativeTick) {
691 /* set tick */
692 int32_t npos;
693 if (rowIndex == myCount) {
694 /* Change track duration */
695 if ([document changeTrackDuration: tick ofTrack: trackNo]) {
696 [[document undoManager] setActionName:NSLocalizedString(
697 @"Change Track Duration", @"Name of undo/redo menu item after the track duration is explicitly changed")];
698 }
699 } else {
700 npos = [document changeTick: tick atPosition: position inTrack: trackNo originalPosition: -1];
701 if (npos >= 0) {
702 /* Select npos'th event and show it */
703 [myEventTrackView selectRowIndexes: [NSIndexSet indexSetWithIndex:npos] byExtendingSelection: NO];
704 [[document undoManager] setActionName:NSLocalizedString(
705 @"Change Tick", @"Name of undo/redo menu item after a single tick value is changed")];
706 }
707 }
708
709 } else {
710
711 /* If ep == NULL, then edit of elements other than tick is impossible */
712 if (ep == NULL)
713 return;
714
715 if ([@"st" isEqualToString: identifier]) {
716
717 tick = (int32_t)atol(buf);
718 /* set ST */
719
720 } else if ([@"ch" isEqualToString: identifier]) {
721
722 int ch;
723 ch = atoi(buf);
724 /* set channel */
725
726 } else {
727 MDEventFieldCode code;
728 MDEventFieldData ed;
729 ed.whole = 0;
730 if ([@"event" isEqualToString: identifier]) {
731 code = MDEventKindStringToEvent(buf, &ed);
732 if (code == kMDEventFieldKindAndCode) {
733 if (ed.ucValue[0] == MDGetKind(ep)
734 || (ed.ucValue[0] == kMDEventNote && (MDGetKind(ep) == kMDEventNote))) {
735 mod = [document changeValue: ed.whole ofType: code atPosition: position inTrack: trackNo];
736 } else {
737 MDEventObject *newEvent = [[[MDEventObject allocWithZone: [self zone]] init] autorelease];
738 newEventFromKindAndCode(&newEvent->event, ed);
739 MDSetTick(&newEvent->event, MDGetTick(ep));
740 newEvent->position = position;
741 if ((myTrackNumber == 0 && MDEventIsEventAllowableInConductorTrack(&newEvent->event))
742 || (myTrackNumber != 0 && MDEventIsEventAllowableInNonConductorTrack(&newEvent->event))) {
743 mod = [document replaceEvent: newEvent inTrack: trackNo];
744 } else {
745 [[NSAlert alertWithMessageText: @"Bad Event Type" defaultButton: nil alternateButton: nil otherButton: nil informativeTextWithFormat: @"This event type cannot be placed in a %s track.", (myTrackNumber == 0 ? "conductor" : "non-conductor")] runModal];
746 }
747 }
748 }
749 } else if ([@"duration" isEqualToString: identifier]) {
750 /* code = MDEventGTStringToEvent(ep, buf, &ed); */
751 if (MDGetKind(ep) == kMDEventNote) {
752 /* Change the tick of the note off */
753 tick = (int32_t)atol(buf);
754 mod = [document changeDuration: tick atPosition: position inTrack: trackNo];
755 }
756 } else if ([@"data" isEqualToString: identifier]) {
757 NSData *data = nil; // Will be used for kMDEventFieldBinaryData
758 code = MDEventDataStringToEvent(ep, buf, &ed);
759 if (code == kMDEventFieldInvalid) {
760 char *msg;
761 asprintf(&msg, "%.*s<?>%s", (int)ed.intValue, buf, buf + ed.intValue);
762 [[NSAlert alertWithMessageText: @"Bad Event Data" defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"The data string cannot be converted to an event data: %s", msg] runModal];
763 free(msg);
764 } else if (code == kMDEventFieldBinaryData) {
765 if (data == nil) {
766 if (ed.binaryData != NULL)
767 data = [NSData dataWithBytes: ed.binaryData + sizeof(int32_t) length: *((int32_t *)ed.binaryData)];
768 }
769 mod = [document changeMessage: data atPosition: position inTrack: trackNo];
770 if (ed.binaryData != NULL) {
771 free(ed.binaryData);
772 ed.binaryData = NULL;
773 }
774 } else {
775 mod = [document changeValue: ed.whole ofType: code atPosition: position inTrack: trackNo];
776 }
777 } else return; /* No action */
778
779 if (mod) {
780 const MDEvent *cep = [document eventAtPosition: position inTrack: trackNo];
781 MDEventClear(&myDefaultEvent); /* Release the message if present */
782 MDEventCopy(&myDefaultEvent, cep, 1);
783 [[document undoManager] setActionName: NSLocalizedString(
784 @"Modify Event", @"Name of undo/redo menu item after event is modified")];
785 }
786 }
787 }
788
789 // Update other windows before going forward
790 [[self document] postTrackModifiedNotification: nil];
791 }
792
793 //- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex
794 //{
795 // if (rowIndex == myCount)
796 // return allowSelectingLastRow;
797 // else return YES;
798 //}
799
800 //- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView
801 //{
802 // NSLog(@"selectionShouldChangeInTableView invoked");
803 // return YES;
804 //}
805 /*
806 - (BOOL)selectionShouldChangeInTableView: (NSTableView *)aTableView
807 {
808 id firstResponder = [[self window] firstResponder];
809 if ([firstResponder isKindOfClass: [NSText class]] && [[firstResponder delegate] isKindOfClass: [MyTableView class]]) {
810 if ([[self window] makeFirstResponder: aTableView]) {
811 [[self window] endEditingFor: nil];
812 return YES;
813 } else {
814 NSBeep();
815 return NO;
816 }
817 }
818 return YES;
819 }
820 */
821 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification
822 {
823 MDSelectionObject *pointSet;
824 NSIndexSet *iset;
825 MDStatus sts;
826 NSInteger numberOfRows;
827 NSUInteger flags;
828 NSUInteger idx;
829 if (selectionDidChangeNotificationLevel > 0)
830 return;
831
832 selectionDidChangeNotificationLevel++;
833
834 /* Check whether shift or command key is pressed */
835 flags = [[[self window] currentEvent] modifierFlags];
836 if (!(flags & (NSCommandKeyMask | NSShiftKeyMask))) {
837 /* If not, then unselect all events */
838 [(MyDocument *)[self document] unselectAllEventsInAllTracks: self];
839 }
840
841 pointSet = [[MDSelectionObject allocWithZone: [self zone]] init];
842 iset = [myEventTrackView selectedRowIndexes];
843 numberOfRows = [self numberOfRowsInTableView: myEventTrackView];
844 isLastRowSelected = NO;
845 for (idx = [iset firstIndex]; idx != NSNotFound; idx = [iset indexGreaterThanIndex:idx]) {
846 if (idx == numberOfRows - 1) {
847 pointSet->isEndOfTrackSelected = YES;
848 isLastRowSelected = YES;
849 continue;
850 }
851 sts = IntGroupAdd(pointSet->pointSet, [self eventPositionForTableRow: (int)idx], 1);
852 if (sts != kMDNoError)
853 break;
854 }
855 [(MyDocument *)[self document] setSelection: pointSet inTrack: myTrackNumber sender: self];
856 [pointSet release];
857 selectionDidChangeNotificationLevel--;
858 [self updateInfoText];
859 }
860
861 #pragma mark ====== Delegate methods ======
862
863 // MyTableView delegate method
864 - (BOOL)myTableView:(MyTableView *)tableView shouldEditColumn:(int)column row:(int)row
865 {
866 if (tableView == myEventTrackView) {
867 MDEvent *ep;
868 NSTableColumn *col = [[tableView tableColumns] objectAtIndex:column];
869 if (row == myCount && [self tagForTickIdentifier:[col identifier]] >= 0)
870 return YES;
871 ep = [self eventPointerForTableRow:row];
872 if (ep == NULL)
873 return NO;
874 /* if (MDGetKind(ep) == kMDEventSysex) {
875 // Special editing feature
876 int n = Ruby_callMethodOfDocument("edit_sysex_dialog", [self document], 0, "ii", (int)myTrackNumber, count);
877 if (n != 0)
878 Ruby_showError(n);
879 return NO;
880 } */
881 }
882 return YES;
883 }
884
885 // NSControl delegate method; check if the edited text is valid
886 - (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
887 {
888 if (control == myEventTrackView) {
889 id identifier;
890 int col = (int)[myEventTrackView editedColumn];
891 int row = (int)[myEventTrackView editedRow];
892 if (col < 0)
893 return NO;
894 identifier = [[[myEventTrackView tableColumns] objectAtIndex: col] identifier];
895 if ([@"event" isEqualToString: identifier]) {
896 MDEventFieldCode code;
897 MDEventFieldData ed;
898 ed.whole = 0;
899 code = MDEventKindStringToEvent([[fieldEditor string] UTF8String], &ed);
900 if (code == kMDEventFieldKindAndCode) {
901 MDEvent event;
902 int32_t position;
903 MDEventInit(&event);
904 newEventFromKindAndCode(&event, ed);
905 position = [self eventPositionForTableRow: row];
906 if (!EventSelector(&event, position, myFilter)) {
907 if (NSRunAlertPanel(@"Alert", @"This event will be hidden and disappear from this window.", @"OK", @"Change Event", nil) == NSAlertAlternateReturn)
908 return NO;
909 }
910 return YES;
911 }
912 }
913 }
914 return YES;
915 }
916
917 - (id)willUseMenu:(id)menu ofCell:(ContextMenuTextFieldCell *)cell inRow:(int)row
918 {
919 if (cell == kindDataCell) {
920 if (row == myCount)
921 return nil;
922 else return menu;
923 } else if (cell == dataDataCell) {
924 MDEvent *ep;
925 int32_t dev;
926 int i, count;
927 if (row == myCount)
928 return nil;
929 ep = [self eventPointerForTableRow:row];
930 if (ep == NULL || MDGetKind(ep) != kMDEventProgram)
931 return nil;
932 dev = MDTrackGetDevice(myTrack);
933 count = MDPlayerGetNumberOfPatchNames(dev);
934 if (count <= 0)
935 return nil;
936 /* Create context menu for patch names */
937 [menu removeAllItems];
938 for (i = 0; i < count; i++) {
939 id item;
940 NSMenu *submenu;
941 NSString *title;
942 char buf[256];
943 int bank, prog;
944 int instno = MDPlayerGetPatchName(dev, -1, i, buf, sizeof(buf));
945 if (instno < 0)
946 continue;
947 bank = (instno >> 8) & 0x7fff;
948 prog = instno & 0x7f;
949 item = [menu itemWithTag:bank];
950 if (item == nil) {
951 // Create a submenu with the bank number
952 title = [NSString stringWithFormat:@"Bank %d:%d", (bank >> 8), bank & 0x7f];
953 item = [menu addItemWithTitle:title action:nil keyEquivalent:@""];
954 [item setTag:bank];
955 submenu = [[[NSMenu alloc] init] autorelease];
956 [item setSubmenu:submenu];
957 }
958 submenu = [item submenu];
959 title = [NSString stringWithFormat:@"%d:%s", prog, buf];
960 item = [submenu addItemWithTitle:title action:@selector(contextMenuSelected:) keyEquivalent:@""];
961 [item setTag:instno];
962 [item setTarget:dataDataCell];
963 }
964 return menu;
965 } else return menu;
966 }
967
968 - (NSString *)stringValueForEventKindTag:(int)tag inRow:(int)row
969 {
970 MDEvent event;
971 int len;
972 unsigned char *ucp;
973 char buf[64];
974
975 MDEventInit(&event);
976 if (tag < 1000) {
977 switch (tag) {
978 case 0: /* Note */
979 MDSetKind(&event, kMDEventNote);
980 MDSetCode(&event, 60);
981 break;
982 case 1: /* Control (non-specific) */
983 MDSetKind(&event, kMDEventControl);
984 break;
985 case 2: /* Pitch bend */
986 MDSetKind(&event, kMDEventPitchBend);
987 break;
988 case 3: /* Program change */
989 MDSetKind(&event, kMDEventProgram);
990 break;
991 case 4: /* Channel pressure */
992 MDSetKind(&event, kMDEventChanPres);
993 break;
994 case 5: /* Polyphonic key pressure */
995 MDSetKind(&event, kMDEventKeyPres);
996 MDSetCode(&event, 60);
997 break;
998 case 6: /* Meta event (non-specific) */
999 MDSetKind(&event, kMDEventMetaText);
1000 MDSetCode(&event, kMDMetaText);
1001 break;
1002 case 7: /* System exclusive */
1003 MDSetKind(&event, kMDEventSysex);
1004 break;
1005 default:
1006 return @"";
1007 }
1008 } else if (tag >= 1000 && tag < 2000) {
1009 /* Control events */
1010 MDSetKind(&event, kMDEventControl);
1011 MDSetCode(&event, (tag - 1000) & 127);
1012 } else if (tag >= 2000 && tag < 3000) {
1013 /* Meta events */
1014 switch (tag) {
1015 case 2000: /* tempo */
1016 MDSetKind(&event, kMDEventTempo);
1017 MDSetTempo(&event, 120.0f);
1018 break;
1019 case 2001: /* meter */
1020 MDSetKind(&event, kMDEventTimeSignature);
1021 ucp = MDGetMetaDataPtr(&event);
1022 ucp[0] = 4; ucp[1] = 2; ucp[2] = 24; ucp[3] = 8;
1023 break;
1024 case 2002: /* key */
1025 MDSetKind(&event, kMDEventKey);
1026 break;
1027 case 2003: /* smpte */
1028 MDSetKind(&event, kMDEventSMPTE);
1029 break;
1030 case 2004: /* port */
1031 MDSetKind(&event, kMDEventPortNumber);
1032 break;
1033 case 2005: /* text */
1034 case 2006: /* copyright */
1035 case 2007: /* sequence */
1036 case 2008: /* instrument */
1037 case 2009: /* lyric */
1038 case 2010: /* marker */
1039 case 2011: /* cue */
1040 case 2012: /* program */
1041 case 2013: /* device */
1042 MDSetKind(&event, kMDEventMetaText);
1043 MDSetCode(&event, tag - 2005 + kMDMetaText);
1044 break;
1045 default:
1046 return @"";
1047 }
1048 } else return @"";
1049
1050 /* Get the authentic string representation */
1051 len = 0;
1052 if (len == 0)
1053 len = MDEventToKindString(&event, buf, sizeof buf);
1054 if (len <= 0)
1055 buf[0] = 0;
1056 return [NSString stringWithUTF8String:buf];
1057 }
1058
1059 - (NSString *)stringValueForProgramTag:(int)tag inRow:(int)row
1060 {
1061 BOOL mod;
1062 MDEventFieldData ed;
1063 MDEventObject *newEvent;
1064 MDEvent *ep;
1065 int32_t pos_bank;
1066 int32_t pos;
1067 int i;
1068 MDTickType tick;
1069 MyDocument *document = (MyDocument *)[self document];
1070 int32_t trackNo = myTrackNumber;
1071
1072 ep = [self eventPointerForTableRow:row];
1073 tick = MDGetTick(ep);
1074 pos = MDPointerGetPosition(myPointer);
1075
1076 /* Program change */
1077 ed.intValue = (tag & 0x7f);
1078 mod = [document changeValue: ed.whole ofType:kMDEventFieldData atPosition:pos inTrack:trackNo];
1079
1080 /* Do bank select MSB and LSB need to be updated? */
1081 for (i = 1; i >= 0; i--) {
1082 /* 0: MSB, 1: LSB */
1083 MDPointer *pt;
1084 MDEvent *ep1;
1085 ed.intValue = ((tag >> (16 - i * 8)) & 0x7f);
1086 MDCalibratorJumpToPositionInTrack(myCalibrator, pos, myTrack);
1087 ep = MDCalibratorGetEvent(myCalibrator, myTrack, kMDEventControl, i * 32);
1088 if (ep != NULL && MDGetData1(ep) == ed.intValue)
1089 continue; /* No need to update */
1090 /* Is it OK to change the value of the existing event? */
1091 pt = MDCalibratorCopyPointer(myCalibrator, myTrack, kMDEventControl, i * 32);
1092 while ((ep1 = MDPointerForward(pt)) != NULL && MDPointerGetPosition(pt) < pos) {
1093 if (MDGetKind(ep1) != kMDEventControl || (MDGetCode(ep1) & ~32) != 0) {
1094 /* Event other than bank select is present ->
1095 we need to insert a new bank select event */
1096 ep = NULL;
1097 break;
1098 }
1099 }
1100 if (ep == NULL) {
1101 /* Insert a bank select event at 'pos' */
1102 newEvent = [[MDEventObject allocWithZone: [self zone]] init];
1103 ep1 = &(newEvent->event);
1104 MDSetTick(ep1, tick);
1105 MDSetKind(ep1, kMDEventControl);
1106 MDSetCode(ep1, i * 32);
1107 MDSetData1(ep1, ed.intValue);
1108 newEvent->position = pos;
1109 [document insertEvent: newEvent toTrack: trackNo];
1110 [newEvent release];
1111 pos++;
1112 mod = YES;
1113 } else {
1114 /* Change the value of the existing event */
1115 pos_bank = MDCalibratorGetEventPosition(myCalibrator, myTrack, kMDEventControl, i * 32);
1116 mod = [document changeValue:ed.whole ofType:kMDEventFieldData atPosition:pos_bank inTrack:trackNo] || mod;
1117 }
1118 }
1119
1120 return @""; // All editing is done, so we do not need text editing
1121 }
1122
1123 - (NSString *)stringValueForMenuItem:(id)item ofCell:(ContextMenuTextFieldCell *)cell inRow:(int)row
1124 {
1125 if (cell == kindDataCell)
1126 return [self stringValueForEventKindTag:(int)[item tag] inRow:row];
1127 else if (cell == dataDataCell)
1128 return [self stringValueForProgramTag:(int)[item tag] inRow:row];
1129 else return @"";
1130 }
1131
1132 #pragma mark ====== PopUp button handlers ======
1133
1134 static NSString *sTickIdentifiers[] = { @"bar", @"sec", @"msec", @"count", @"deltacount", nil };
1135
1136 - (int)tagForTickIdentifier:(NSString *)identifier
1137 {
1138 int tag;
1139 for (tag = 0; sTickIdentifiers[tag] != nil; tag++) {
1140 if ([sTickIdentifiers[tag] isEqualToString:identifier])
1141 return tag;
1142 }
1143 return -1;
1144 }
1145
1146 - (NSString *)tickIdentifierForTag:(int)tag
1147 {
1148 if (tag >= 0 && tag < sizeof(sTickIdentifiers) / sizeof(sTickIdentifiers[0]) - 1)
1149 return sTickIdentifiers[tag];
1150 else return nil;
1151 }
1152
1153 - (IBAction)myAppendColumn:(id)sender
1154 {
1155 }
1156
1157 - (IBAction)myRemoveColumn:(id)sender
1158 {
1159 }
1160
1161 - (IBAction)myShowSecond:(id)sender
1162 {
1163 [myClickedColumn setIdentifier:@"sec"];
1164 [[myClickedColumn headerCell] setStringValue:@"Seconds"];
1165 [myEventTrackView reloadData];
1166 }
1167
1168 - (IBAction)myShowMillisecond:(id)sender
1169 {
1170 [myClickedColumn setIdentifier:@"msec"];
1171 [[myClickedColumn headerCell] setStringValue:@"Milliseconds"];
1172 // [[myClickedColumn headerCell] setAlignment:NSCenterTextAlignment];
1173 [myEventTrackView reloadData];
1174 }
1175
1176 - (IBAction)myShowBarBeatCount:(id)sender
1177 {
1178 [myClickedColumn setIdentifier:@"bar"];
1179 [[myClickedColumn headerCell] setStringValue:@"Bar: beat: count"];
1180 // [[myClickedColumn headerCell] setAlignment:NSCenterTextAlignment];
1181 [myEventTrackView reloadData];
1182 }
1183
1184 - (IBAction)myShowCount:(id)sender
1185 {
1186 [myClickedColumn setIdentifier:@"count"];
1187 [[myClickedColumn headerCell] setStringValue:@"Count"];
1188 // [[myClickedColumn headerCell] setAlignment:NSCenterTextAlignment];
1189 [myEventTrackView reloadData];
1190 }
1191
1192 - (IBAction)myShowDeltaCount: (id)sender
1193 {
1194 [myClickedColumn setIdentifier:@"deltacount"];
1195 [[myClickedColumn headerCell] setStringValue:@"delta count"];
1196 [myEventTrackView reloadData];
1197 }
1198
1199 - (NSMenu *)tableHeaderView:(NSTableHeaderView *)headerView popUpMenuAtHeaderColumn:(int)column
1200 {
1201 NSTableColumn *tableColumn;
1202 int i, tag;
1203 tableColumn = (NSTableColumn *)[[myEventTrackView tableColumns] objectAtIndex:column];
1204 tag = [self tagForTickIdentifier:[tableColumn identifier]];
1205 if (tag < 0)
1206 return nil;
1207 for (i = 0; i < 4; i++) {
1208 [[myTickDescriptionMenu itemWithTag:i] setState:(i == tag ? NSOnState : NSOffState)];
1209 }
1210 myClickedColumn = tableColumn;
1211 return myTickDescriptionMenu;
1212 }
1213
1214 #pragma mark ====== Editing ======
1215
1216 - (IBAction)deleteSelectedEvents: (id)sender
1217 {
1218 NSIndexSet *iset = [myEventTrackView selectedRowIndexes];
1219 IntGroupObject *pointSet = [[[IntGroupObject allocWithZone: [self zone]] init] autorelease];
1220 MDStatus sts;
1221 BOOL mod;
1222 NSUInteger idx;
1223 int eot;
1224 eot = (int)[myEventTrackView numberOfRows] - 1; // End Of Track
1225 for (idx = [iset firstIndex]; idx != NSNotFound; idx = [iset indexGreaterThanIndex:idx]) {
1226 if (idx == eot)
1227 continue;
1228 sts = IntGroupAdd(pointSet->pointSet, [self eventPositionForTableRow:(int)idx], 1);
1229 if (sts != kMDNoError)
1230 return; // Cannot proceed
1231 }
1232 mod = [(MyDocument *)[self document]
1233 deleteMultipleEventsAt: (IntGroupObject *)pointSet
1234 fromTrack: myTrackNumber deletedEvents: NULL];
1235 if (mod) {
1236 [[[self document] undoManager] setActionName:NSLocalizedString(
1237 @"Delete Selection", @"Name of undo/redo menu item after event is modified")];
1238 }
1239 myCount = -1;
1240 [myEventTrackView reloadData];
1241 }
1242
1243 - (void)startEditAtColumn: (int)column row: (int)row
1244 {
1245 [myEventTrackView selectRowIndexes: [NSIndexSet indexSetWithIndex:row] byExtendingSelection: NO];
1246 if (column < 0)
1247 // column = [myEventTrackView columnWithIdentifier: @"event"];
1248 column = 0;
1249 [myEventTrackView editColumn: column row: row withEvent: nil select: YES];
1250 }
1251
1252 - (void)startEditAtColumn: (int)column creatingEventWithTick: (MDTickType)tick atPosition: (int32_t)position
1253 {
1254 MDEvent *ep;
1255 MDPointer *ptr = MDPointerNew(myTrack);
1256 MDEventObject *newEvent;
1257 // int32_t num = MDTrackGetNumberOfEvents(myTrack);
1258 int nearestRow, row;
1259 if (!EventSelector(&myDefaultEvent, position, myFilter))
1260 /* The default event will not be displayed: clear the default event */
1261 MDEventClear(&myDefaultEvent);
1262 if (myTrackNumber == 0 && !MDEventIsEventAllowableInConductorTrack(&myDefaultEvent)) {
1263 MDEventClear(&myDefaultEvent);
1264 MDSetKind(&myDefaultEvent, kMDEventTempo);
1265 MDSetTempo(&myDefaultEvent, 120.0f);
1266 } else if (myTrackNumber != 0 && !MDEventIsEventAllowableInNonConductorTrack(&myDefaultEvent)) {
1267 MDEventClear(&myDefaultEvent);
1268 MDSetKind(&myDefaultEvent, kMDEventNote);
1269 MDSetCode(&myDefaultEvent, 60);
1270 MDSetNoteOnVelocity(&myDefaultEvent, 64);
1271 MDSetNoteOffVelocity(&myDefaultEvent, 0);
1272 MDSetDuration(&myDefaultEvent, [[self document] timebase]);
1273 }
1274
1275 newEvent = [[[MDEventObject allocWithZone: [self zone]] initWithMDEvent: &myDefaultEvent] autorelease];
1276 if (tick < 0) {
1277 /* If tick is not specified, then the tick at (position-1) is used */
1278 tick = 0;
1279 if (position >= 0) {
1280 MDPointerSetPosition(ptr, position - 1);
1281 if ((ep = MDPointerCurrent(ptr)) != NULL)
1282 tick = MDGetTick(ep);
1283 }
1284 }
1285 if (position < 0) {
1286 /* If position is not specified, then the last position before tick is used */
1287 position = 0;
1288 if (tick >= 0) {
1289 MDPointerJumpToTick(ptr, tick + 1);
1290 position = MDPointerGetPosition(ptr);
1291 if (position < 0)
1292 position = 0;
1293 }
1294 }
1295 newEvent->position = position;
1296 MDSetTick(&newEvent->event, tick);
1297 [(MyDocument *)[self document]
1298 insertEvent: newEvent
1299 toTrack: myTrackNumber];
1300 myCount = -1;
1301 [myEventTrackView reloadData];
1302
1303 // Update other windows before going forward
1304 [[self document] postTrackModifiedNotification: nil];
1305
1306 row = [self rowForEventPosition: position nearestRow: &nearestRow];
1307 [self startEditAtColumn: column row: row];
1308 }
1309
1310 - (IBAction)insertNewEvent: (id)sender
1311 {
1312 MDTickType tick, endTick;
1313 int32_t position;
1314 MDEvent *ep;
1315 int row = (int)[myEventTrackView selectedRow];
1316 if (row < 0) {
1317 position = -1;
1318 [(MyDocument *)[self document] getEditingRangeStart: &tick end: &endTick];
1319 if (tick < 0) {
1320 tick = 0;
1321 position = 0;
1322 }
1323 } else {
1324 position = [self eventPositionForTableRow: row];
1325 ep = [self eventPointerForTableRow: row];
1326 if (ep != NULL) {
1327 /* An event is selected: a new event is inserted after the selected event
1328 with the same tick, and this new event is edited */
1329 tick = MDGetTick(ep);
1330 position++;
1331 } else {
1332 /* End of track is selected: a new event is inserted at the end of track
1333 and this new event is edited */
1334 tick = MDTrackGetDuration(myTrack);
1335 }
1336 }
1337 [self startEditAtColumn: -1 creatingEventWithTick: tick atPosition: position];
1338 }
1339
1340 - (IBAction)editSelectedEvent: (id)sender
1341 {
1342 int row = (int)[myEventTrackView selectedRow];
1343 if (row >= 0)
1344 [self startEditAtColumn: -1 row: row];
1345 }
1346
1347 #pragma mark ====== Other UI ======
1348
1349 - (void)editingRangeChanged: (NSNotification *)notification
1350 {
1351 [self updateEditingRangeText];
1352 }
1353
1354 - (IBAction)editingRangeTextModified: (id)sender
1355 {
1356 BOOL startFlag;
1357 int32_t bar, beat, subtick;
1358 MDTickType tick, duration, endtick;
1359 const char *s;
1360 if (sender == startEditingRangeText)
1361 startFlag = YES;
1362 else startFlag = NO;
1363 s = [[sender stringValue] UTF8String];
1364 if (s[0] == 0) {
1365 /* Empty string: clear editing range */
1366 tick = endtick = -1;
1367 } else {
1368 if (MDEventParseTickString(s, &bar, &beat, &subtick) < 3)
1369 return;
1370 tick = MDCalibratorMeasureToTick(myCalibrator, bar, beat, subtick);
1371 duration = [[[self document] myMIDISequence] sequenceDuration];
1372 if (tick < 0)
1373 tick = 0;
1374 // if (tick > duration)
1375 // tick = duration;
1376 if (startFlag)
1377 endtick = tick;
1378 else {
1379 MDTickType tick1, tick2;
1380 [[self document] getEditingRangeStart: &tick1 end: &tick2];
1381 if (tick1 >= 0 && tick1 <= tick) {
1382 endtick = tick;
1383 tick = tick1;
1384 } else {
1385 endtick = tick;
1386 }
1387 }
1388 }
1389 [[self document] unselectAllEventsInAllTracks: self];
1390 [[self document] setEditingRangeStart: tick end: endtick];
1391 }
1392
1393 - (IBAction)showEditingRange:(id)sender
1394 {
1395 MDTickType startTick, endTick;
1396 int startRow, endRow, n;
1397 NSRange visibleRowRange;
1398 [[self document] getEditingRangeStart:&startTick end:&endTick];
1399 if (startTick < 0 || startTick >= kMDMaxTick)
1400 return; /* No action */
1401 startRow = [self maxRowBeforeTick:startTick];
1402 endRow = [self maxRowBeforeTick:endTick];
1403 visibleRowRange = [myEventTrackView rowsInRect:[myEventTrackView visibleRect]];
1404 if (!NSLocationInRange(startRow, visibleRowRange)) {
1405 n = ((int)visibleRowRange.length - (endRow - startRow)) * 2 / 3;
1406 if (n < 0)
1407 n = 0;
1408 [myEventTrackView scrollRowToVisible:startRow + n];
1409 }
1410 }
1411
1412 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
1413 {
1414 ListWindowFilterRecord *filter;
1415 int n;
1416 EventFilterPanelController *cont = [sheet windowController];
1417 NSEnumerator *en;
1418 id obj;
1419
1420 if (returnCode != 1) {
1421 [cont close];
1422 return;
1423 }
1424
1425 filter = (ListWindowFilterRecord *)calloc(sizeof(ListWindowFilterRecord), 1);
1426 if (filter == NULL)
1427 return; /* TODO: throw exception */
1428
1429 filter->mode = [cont mode];
1430 n = 0;
1431 filter->table = (void *)calloc(sizeof(filter->table[0]), 300);
1432 if (filter->table == NULL)
1433 return; /* TODO: throw exception */
1434 if ([cont isSelectedForKey: gChannelPressureKey]) {
1435 filter->table[n].flag = YES;
1436 filter->table[n].kind = kMDEventChanPres;
1437 n++;
1438 }
1439 if ([cont isSelectedForKey: gNoteKey]) {
1440 filter->table[n].flag = YES;
1441 filter->table[n].kind = kMDEventNote;
1442 n++;
1443 }
1444 if ([cont isSelectedForKey: gPitchBendKey]) {
1445 filter->table[n].flag = YES;
1446 filter->table[n].kind = kMDEventPitchBend;
1447 n++;
1448 }
1449 if ([cont isSelectedForKey: gPolyPressureKey]) {
1450 filter->table[n].flag = YES;
1451 filter->table[n].kind = kMDEventKeyPres;
1452 n++;
1453 }
1454 if ([cont isSelectedForKey: gProgramKey]) {
1455 filter->table[n].flag = YES;
1456 filter->table[n].kind = kMDEventProgram;
1457 n++;
1458 }
1459 if ([cont isSelectedForKey: gSysexKey]) {
1460 filter->table[n].flag = YES;
1461 filter->table[n].kind = kMDEventSysex;
1462 n++;
1463 filter->table[n].flag = YES;
1464 filter->table[n].kind = kMDEventSysexCont;
1465 n++;
1466 }
1467 en = [[cont ccMetaFilters] objectEnumerator];
1468 while ((obj = [en nextObject]) != nil) {
1469 int num = [[obj valueForKey: gCCMetaNumberKey] intValue];
1470 filter->table[n].flag = [[obj valueForKey: gCCMetaSelectedKey] boolValue];
1471 if (num >= 128) {
1472 filter->table[n].kind = MDEventSMFMetaNumberToEventKind(num - 128);
1473 filter->table[n].data = num - 128;
1474 } else {
1475 filter->table[n].kind = kMDEventControl;
1476 filter->table[n].data = num;
1477 }
1478 if (n++ >= 256)
1479 break;
1480 }
1481 filter->table[n++].kind = kMDEventStop;
1482 filter->table = realloc(filter->table, sizeof(filter->table[0]) * n);
1483 filter->count = n;
1484
1485 [cont close];
1486
1487 if (myFilter != NULL) {
1488 if (myFilter->table != NULL)
1489 free(myFilter->table);
1490 free(myFilter);
1491 }
1492 myFilter = filter;
1493 myCount = -1;
1494 [myEventTrackView reloadData];
1495 [self reloadSelection];
1496 }
1497
1498 - (IBAction)openEventFilterPanel: (id)sender
1499 {
1500 int i;
1501 EventFilterPanelController *cont;
1502 cont = [[EventFilterPanelController allocWithZone: [self zone]] init];
1503 [cont window]; // Load the nib file
1504
1505 // Set up the panel settings
1506 if (myFilter == NULL) {
1507 [cont setMode: 0];
1508 } else {
1509 [cont setMode: myFilter->mode];
1510 for (i = 0; i < myFilter->count; i++) {
1511 int kind = myFilter->table[i].kind;
1512 int n;
1513 id key = nil;
1514 if (kind == kMDEventStop)
1515 break;
1516 n = MDEventMetaKindCodeToSMFMetaNumber(kind, myFilter->table[i].data);
1517 if (n >= 0 && n < 128) {
1518 /* Meta event */
1519 [cont addNewCCMetaFilter: n + 128 selected: myFilter->table[i].flag];
1520 continue;
1521 }
1522 switch (kind) {
1523 case kMDEventControl:
1524 [cont addNewCCMetaFilter: myFilter->table[i].data selected: myFilter->table[i].flag];
1525 continue;
1526 case kMDEventChanPres:
1527 key = gChannelPressureKey; break;
1528 case kMDEventNote:
1529 key = gNoteKey; break;
1530 case kMDEventPitchBend:
1531 key = gPitchBendKey; break;
1532 case kMDEventKeyPres:
1533 key = gPolyPressureKey; break;
1534 case kMDEventProgram:
1535 key = gProgramKey; break;
1536 case kMDEventSysex:
1537 case kMDEventSysexCont:
1538 key = gSysexKey; break;
1539 }
1540 if (key != nil && myFilter->table[i].flag)
1541 [cont select: YES forKey: key];
1542 }
1543 }
1544
1545 [[NSApplication sharedApplication] beginSheet: [cont window]
1546 modalForWindow: [self window]
1547 modalDelegate: self
1548 didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
1549 contextInfo: nil];
1550 }
1551
1552 #pragma mark ==== Pasteboard support ====
1553
1554 - (void)doCopy: (BOOL)copyFlag andDelete: (BOOL)deleteFlag
1555 {
1556 MDSelectionObject *sel, **selArray;
1557 MDTickType startTick, endTick;
1558 MyDocument *doc = (MyDocument *)[self document];
1559 int numberOfTracks = [[doc myMIDISequence] trackCount];
1560
1561 if ([myEventTrackView numberOfSelectedRows] == 0)
1562 return;
1563
1564 selArray = (MDSelectionObject **)calloc(sizeof(MDSelectionObject *), numberOfTracks);
1565 if (selArray == NULL)
1566 return;
1567
1568 selArray[myTrackNumber] = [doc selectionOfTrack: myTrackNumber];
1569 [doc getEditingRangeStart: &startTick end: &endTick];
1570
1571 if (copyFlag)
1572 [doc copyWithSelections: selArray rangeStart: startTick rangeEnd: endTick];
1573
1574 if (deleteFlag) {
1575 sel = [doc selectionOfTrack: myTrackNumber];
1576 [doc deleteMultipleEventsAt: sel fromTrack: myTrackNumber deletedEvents: NULL];
1577 }
1578
1579 free(selArray);
1580 }
1581
1582 - (void)doPasteWithMergeFlag: (BOOL)mergeFlag
1583 {
1584 //- (BOOL)getPasteboardSequence: (MDSequence **)outSequence catalog: (MDCatalog **)outCatalog;
1585 MyDocument *doc = (MyDocument *)[self document];
1586 MDSequence *seq;
1587 MDCatalog *catalog;
1588 int targetTrack[2], result;
1589
1590 if (![doc getPasteboardSequence: &seq catalog: &catalog])
1591 return;
1592 if (catalog->num != 1) {
1593 NSRunCriticalAlertPanel(@"Cannot paste", @"You are trying to paste multiple tracks in this single-track window. Please try pasting in the graphic window or try copying again.", @"OK", @"", @"");
1594 return;
1595 }
1596
1597 targetTrack[0] = myTrackNumber;
1598 targetTrack[1] = -1;
1599 result = [doc doPaste: seq toTracks: targetTrack rangeStart: catalog->startTick rangeEnd: catalog->endTick mergeFlag: mergeFlag];
1600
1601 switch (result) {
1602 case 1: /* Trying to paste MIDI track to the conductor track */
1603 NSRunCriticalAlertPanel(@"Cannot paste", @"You are trying to paste a MIDI track to the conductor track. Please try pasting in another window or try copying again.", @"OK", @"", @"");
1604 break;
1605 }
1606
1607 free(catalog);
1608 MDSequenceRelease(seq);
1609 }
1610
1611 - (IBAction)copy: (id)sender
1612 {
1613 [self doCopy: YES andDelete: NO];
1614 }
1615
1616 - (IBAction)cut: (id)sender
1617 {
1618 [self doCopy: YES andDelete: YES];
1619 }
1620
1621 - (IBAction)delete: (id)sender
1622 {
1623 [self doCopy: NO andDelete: YES];
1624 }
1625
1626 - (IBAction)paste: (id)sender
1627 {
1628 [self doPasteWithMergeFlag: YES];
1629 }
1630
1631 - (IBAction)pasteWithReplace: (id)sender
1632 {
1633 [self doPasteWithMergeFlag: NO];
1634 }
1635
1636 - (IBAction)merge: (id)sender
1637 {
1638 [self doPasteWithMergeFlag: YES];
1639 }
1640
1641 - (BOOL)validateUserInterfaceItem: (id)anItem
1642 {
1643 SEL sel = [anItem action];
1644 if (sel == @selector(copy:) || sel == @selector(cut:) || sel == @selector(delete:)) {
1645 if ([myEventTrackView numberOfSelectedRows] > 0)
1646 return YES;
1647 else return NO;
1648 } else if (sel == @selector(paste:) || sel == @selector(pasteWithReplace:) || sel == @selector(merge:)) {
1649 if ([[self document] isSequenceInPasteboard])
1650 return YES;
1651 else return NO;
1652 } else if (sel == @selector(showEditingRange:)) {
1653 MDTickType startTick, endTick;
1654 [(MyDocument *)[self document] getEditingRangeStart: &startTick end: &endTick];
1655 if (startTick < 0 || startTick >= kMDMaxTick)
1656 return NO;
1657 else return YES;
1658 } else return YES;
1659 }
1660
1661 @end

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