Geertjan's Blog

  • July 16, 2006

Simple Interactions Between NetBeans TopComponents

Geertjan Wielenga
Product Manager
I've made a bit of progress with the Music NotePad. Now, when you hover over a note, a tooltip appears with the correct JFugue notation for the selected note. For example, in the screenshot below, the selected note is a "G", in the 5th octave, and with an "i" duration (which represents a quarter note):

The content of the tooltip is taken straight from the Editor. And the content of the Editor is generated when notes are dragged and dropped onto the Music Sheet (and when instruments are selected in the Instruments panel). And the Music Sheet and Editor are separate TopComponents. So, if you're wondering how the two TopComponents interact, read on.

  • Communication from Music Sheet to Editor. When a note is dropped on the Music Sheet, the Y value of the drop location determines the note type (i.e., whether it is an "E" or a "G", and so on, as well as the octave, either 5 or 6, which is the currently supported range). Then this snippet sends the music string to the Editor:

    if (data.getComment().equals("Whole Note")) {
    } else if (data.getComment().equals("Half Note")) {
    } else if (data.getComment().equals("Quarter Note")) {
    } else {

    So, first a check is done to see what note length has been dropped ("Whole Note" or "Half Note", and so on). This is determined by the transferable provided by the NetBeans Palette API drag-and-drop framework, as discussed in an earlier blog entry (and demonstrated in the Simpsons Sample from some blog entries ago). Then the note type (for example, "E5") is sent, together with a character representing the note length (for example, "w" for "Whole Note" and "h" for "Half Note"), using JFugue notation. The editor_instance refers to this declaration at the top of the file:

    private static textareaTopComponent editor_instance;

    The findInstance() method is the one that does all the magic. You can use it in one TopComponent to find a method in another TopComponent. Here, the addNote method is found, which just retrieves the current content of the Editor and then adds the new note to it:

    jEditorPane1.setText(jEditorPane1.getText().toString().trim() + " " + current_note);

  • Communication from Editor to Music Sheet. Going the other way, part of the reason why I wanted the related string to appear in a note's tooltip was so that I'd have a connection between a note in the Music Sheet and the related string in the Editor. Ultimately, moving a note in the Music Sheet should result in the related string in the Editor being replaced with a new one. Therefore, the note in the Music Sheet needs some way of 'knowing' which string in the Editor should be changed. So, instead of adding a string to the tooltip at the same time as the string is added to the Editor, it is better to add the string to the tooltip by querying the Editor, so that there's a starting point for manipulating the Editor from the Music Sheet.

    So, the first thing to think about is when do we need to get the content of the Editor. It makes sense to do so after a drop, because that's when the Editor is most up to date. This is the method call found right after the Editor is updated with the next note after a drop:


    And this is how we retrieve the current note from the Editor. First, we again use the findInstance() method to call a method that is in the Editor's TopComponent. This is all that the Editor's getContent method does:

    public String getContent(){
    return jEditorPane1.getText();

    So, after calling that method and putting its content in a string, we use a StringTokenizer (see the method below) to break the string into tokens, each delimited by one of the linebreaks that each note in the Editor provides. Once we have tokens, we return the last token in the tokenizer, each time we drop a note...

    private String getCurrentNote() {
    String content = editor_instance.findInstance().getDefault().getContent();
    StringTokenizer stoken = new StringTokenizer(content,"\\n");
    while (stoken.hasMoreTokens()) {
    nextToken = stoken.nextToken();
    return nextToken;

    ...because the last token in the tokenizer is the current note, because the last token in the tokenizer is the string that was generated when the current note was dropped on the Music Sheet. Then we return the token as a string provided to the method call discussed earlier:


    And this adds the string to the transferable. Then we can retrieve the note like this, from the transferable:

    ItemDataDisplayer displayer = (ItemDataDisplayer)displayers.get(data);

    And that's how the tooltip gets the correct note! This is quite useful, because now we can use the tooltips in the Music Sheet to access methods in the Editor. For example, when you select a note in the Music Sheet and decide to delete it, the related string in the Editor should also be deleted, right? So, this is how that would be done:

    String toBeRemoved = displayer.getToolTipText();
    String content = editor_instance.findInstance().getDefault().getContent();
    String newContent = content.replace(toBeRemoved,"");

    So, we first get the string to be removed from the tooltip, then we get the content from the Editor, then we find the string in the content and replace it with nothing, and reset the content. But what happens when there is more than one instance of the same string? Hmmm. I hadn't thought of that scenario yet (which is a bit stupid, because how many pieces of music, other than those belonging to the heavy metal genre, are there that only have one instance of each note?). But here's one possibility, that I've partly provided for already—whenever a note is dropped, it gets a unique ID number. The first note dropped is 1, the second is 2, and so on. Maybe I should use that number to find the related string in the Editor, instead of the note which is never unique...

So anyway, this is a simple approach for communicating between NetBeans TopComponents within the same module. How about if they were in different modules? Well, that's a different story for another day.

Join the discussion

Comments ( 1 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.