|
About Andrew Choi
MIDI Programs
MIDI File Player (External Device)
MIDI Destination Pop-Up Button
MIDI File Player (Internal Synth)
MusicSequence Sample Code
MIDI File Writer
MIDI Name Document Parser
NameConfigSetup
Fish Creek MIDI Framework
MidnamUtility
SysExSenderX
Other Programs
FCBlogEditor
FCBlog and Patch
Chinese Checkers Program
jyut6 ping3 Cantonese Input Method
Cocoa Sample Programs
Syntax Coloring Using Flex
NSTextField and Undo
NSToolbar
Implementing File Import
Launch Application and Open URL
Saving Uncommitted Text Field Edits
Algorithms
Jazz Chord Analysis as Optimization
Optimal Line Breaking for Music
Optimal Chord Spacing
|
|
|
|
A blog where I will write mostly about programming in Cocoa and CoreMIDI, and experiences from my ports of Emacs and XEmacs to the Mac OS.
|
How Not to Lose Uncommitted Text Field Edits
|
Friday October 15, 2004
When characters are typed in a text field in a Cocoa application, before the tab or return key is hit, the characters are still considered uncommitted. For instance, if the document window is closed at this time, the user isnt given a chance to save the document! Try creating a new message in Mail.app, type something in the To: text field, and close the message window. The window closes and you lose whatever you have typed! To me this really isnt intuitive GUI behavior. I searched the Cocoa Builder archive and found a number of messages with different opinions on how this might be fixed but none that really knew how to do it.
I wrote a sample application to solve this problem. The solution is really simple: I keep track of whether a text field commit is pending, i.e., after the delegate method controlTextDidBeginEditing: is called but before the corresponding controlTextDidEndEditing: is called. Then all one needs to do is override NSDocuments isDocumentEdited to return either the super classs isDocumentEdited or a text field commit is pending. Its so simple that no one thought of it :-)! What can I say?
Heres a screen shot of the sample application in action. The text fields in the two documents contain characters that were typed but not committed. Quit was selected at this time, which results in the dialog for giving the user a chance to review the two changed documents.
|
Overriding NSDocument Methods
|
Thursday October 14, 2004
A chord chart is considered invalid if no chord is entered for the first beat of the first bar because we wouldnt know what chord to play for the first few beats. In the following example, no chord is specified for the first two beats of the song. The chord editor will report an error when it reads such a chord chart in text format. Therefore it must not save such a chord chart either.
To display an alert sheet with the appropriate error message, I overrode the two methods saveDocument: and saveDocumentAs: in my NSDocument subclass. Heres a screen shot of the alert sheet that appears when the user tries to save the document.
What complicates matter is instead of saving the document, the user can simply close it, or quit the application. When this happens an error should also be reported in place of the alert that usually gives the user a last chance to save the document. To implement this I overrode canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:. Heres a screen shot of that alert sheet.
|
Effects of Bar Insertion and Deletion on Chorus End Points
|
Wednesday October 13, 2004
If you examine other MIDI applications that contain a chord editor, youll find that the bars where the chorus begins and ends arent affected when bars are inserted into and deleted from the chord chart in them. This makes the GUI quite unintuitive especially when these end points end up lying beyond the end of the chord chart as a result of a deletion!
I spent some time today on implementing smart adjustment of the chorus end points after bar insertions and deletions. Well, its not as easy as it sounds!
Insertions are easier so let me show you some examples. Heres one of a chord chart with four bars where the chorus consists of bars 2 and 3.
Suppose two bars (containing the chords E and F, resp.) are added before the first bar. Notice how the chorus is shifted two bars forward, and still contains the two bars with chords B and C.
Suppose the two bars are inserted before bar 3 in the following chord chart (i.e., within the chorus):
The resulting chorus now contains four bars: the original two bars and the two new bars:
Of course if the new bars are added somewhere after the end of the chorus, for example, before bar 4 in the chord chart:
the chorus end points will not be affected at all:
Deletion is a little trickier. Lets consider the simpler cases first. If the bars to be deleted lie within the chorus, for example:
the chorus will of course shorten after the deletion:
If the bars to be deleted all come before the chorus, for example:
the chorus is shifted backwards after the deletion:
But what if the bars to be deleted contain one of the end points? For example, the beginning of the chorus can be deleted, as in:
this affected end point must be shifted accordingly:
Heres an example when the end of the chorus is deleted:
and heres the result:
What if both end points are deleted? For example:
in this case we can only reset the end points to some reasonable default values (beginning and end of the entire chord sequence):
Coming up with a simple algorithm for handling all these cases has been an interesting exercise!
|
Cut, Copy, Paste, and Other Operations on Bars
|
Tuesday October 12, 2004
Back when I was thinking about how the chord editor should work, I made a design decision regarding how bars should be selected and how the selections should appear in the GUI. Editing bars are really very much like editing characters, in that sometimes a sequence of them are selected but yet at other times wed like to place the cursor between two consecutive bars. On the other hand, visually they look more like the cells in a spreadsheet (albeit in one-dimension). Because of this, and to avoid the confusion to the user of switching between a selection and a cursor, I decided against implementing a cursor that goes between bars and always have a selection that contains at least one bar. This makes the programming a little more complicated because special cases need to be handled separately. Consider a document containing four bars, all of them selected.
What should happen when we choose the menu function Cut at this time? If there is a cursor that goes between bars, the document should be empty as a result and that cursor be placed at its beginning (also its end). Without such a cursor, our chord editor must create a new empty bar automatically after the cut function and select that new bar instead. With the need to support undo and a number of different operations, the implementation requires a bit of work. Thats what I worked on today.
Without a cursor that goes between bars, we must also decide where an insert operation occurs. The natural place is before the first bar of the current selection. So for example if we choose Copy and then Insert From Pasteboard in the first example, the four selected bars are duplicated.
|
Representing and Editing Sections
|
Monday October 11, 2004
I took quite a simple approach to store sections and section marks in a tune. The following sample progression:
is represented by the (self-explanatory) text file:
Name: ii-V's descending by whole note
Meter: 4/4
Key: C
Tempo: 125
Chorus: bars 1 to 6 (repeat 5 times)
Sections: 1A 3B 5A
| Dm7 G7 | Cm7 F7 | Bbm7 Eb7 | Abm7 Db7 |
| F#m7 B7 | Em7 A7 |
Editing the section marks is also simple: when a bar is selected, the appropriate menu items are enabled to allow the user to set the section mark to A and B, and to clear it. In the following example bar 5 with the section mark A is selected. So the user can either set it to B or clear it.
|
Search this blog with
Lists
Less-Known Facts About Emacs
Emacs Rants
Chinese Restaurants in Calgary
Calgary/Banff Tourist Attractions
C++ Reading List
Science Fiction Series
Top-10 Reason I Stopped Working on Emacs
Top-10 Types of Questions I Get About Emacs
10 Defining Moments as Programmer
Misc
Carbon XEmacs
Emacs for
Mac OS X
|