## SwingOSC – Java-based GUI classes

This class is meant as an emulation of SCTextView. last mod: 31-dec-09 sciss
Also refer to JSCView for different behaviour affecting all widgets

different behaviour
string_ | effective even when the view is made non-editable (in CocoaGUI, string_ doesn't work in this case)
enterInterpretsSelection | uses Ctrl+Return instead
extended functionality
caret_ | sets the cursor position
select | changes the selection range
selectAll | select whole document
caretColor_ | colour of the cursor (useful for dark backgrounds)
openURL | allows loading of remote files via HTTP
action_ | action function is called whenever text is entered, removed or caret moved
known issues / todo
textBounds_ | not available
usesTabToFocusNextView_ | not yet available
mouseDownAction | not called with extra "pos" argument
string_ | doesn't clear previously used style, there should be a way to clear it
text transmission | due to the trickery of the SuperCollider unicode support (a unicode character can be reflected by more than one Char in a SuperCollider String), there occurs a discrepency between the character count (and hence cursor position) of the server (Swing) and the client (SuperCollider). You'll get "discrepancy" warning messages, indicating that the text representation on the client (SuperCollider) might be wrong

# JSCTextView

Note: please use the abstraction layer TextView if possible!

A text view can display a large body of text, either plain or with attributes (such as in HTML). It can also be used to edit multiline text.

    (
    var win;

    win = JSCWindow.new;
    t = JSCTextView( win, win.view.bounds.insetBy( 4, 4 ))
        .resize_( 5 )
        .hasVerticalScroller_( true )
        .autohidesScrollers_( true )
        .focus( true );
    win.front;
    )

The content can be programmatically set from a plain string, using the `string_` method:

    t.string = "Schoko";

There is a general approach to changing and styling the text: Global methods such as `string_` and `font_` apply to the whole text, and they are accompanied by methods of the type `set<PropertyName>` which along with the new property value take two additional parameters, the cursor start position and the amount of characters to change. For `setString` it means that text is inserted when the amount of characters is zero, or replaced if it is greater than zero. Examples:

    t.setString( "lade", 7, 0 );  // insert given string at position 7
    t.setString( "Klang", 0, 6 ); // at position 0 replace 6 characters with the given string

The other properties are `font`, `stringColor`, `leftIndent`, `rightIndent`, `spaceAbove`, `spaceBelow`, `lineSpacing`, and `align`. Here are `font` and `stringColor`:

### Character attributes

    t.font = JFont( "Times", 14 );
    t.setFont( JFont( "Times-Bold", 20 ), 0, 5 );
    t.stringColor = Color.blue;
    t.setStringColor( Color.red, 5, 4 );
    t.background = Color.yellow;  // the global background colour
    t.background = Color.white;
    t.caretColor = Color.green;   // cursor colour (SwingOSC only)
    t.caretColor = Color.black;

### Paragraph attributes

The indentation of paragraphs is controlled through the properties `leftIndent`, `rightIndent`, `spaceAbove`, and `spaceBelow`, where each value is specified in pixels:

    t.string      = "".catList(({{ "riverrun, past Eve and Adams".scramble } ! 8 ++ "\n" } ! 3).flatten );
    t.leftIndent  = 30;             // all paragraphs
    t.rightIndent = 30;
    t.setLeftIndent( 60, 550, 1 );  // just the last paragraph
    t.setRightIndent( 0, 550, 1 );  // just the last paragraph
    t.spaceAbove  = 15;
    t.spaceBelow  = 15;

Paragraph line spacing and alignment:

    t.setLineSpacing( 1.0, 0, 1 );    // first paragraph: +100% (double-spacing)
    t.setLineSpacing( -0.5, 225, 1 ); // second paragraph: -50% (half-spacing)
    t.lineSpacing = 0.0;              // all normal (+- 0 %)
    t.align = \right;
    t.setAlign( \center, 0, 1 );      // just the first paragraph
    t.setAlign( \justified, 225, 1 ); // just the second paragraph
    t.setAlign( \left, 550, 1 );      // just the third paragraph

### Custom tabulator settings

First, let's start with a basic text:

    t.string = "\talpha\n\t\tbeta\n\t\t\tgamma\n\t\t\t\tdelta";
    t.leftIndent = 0; t.align = \left;  // (reset previously made indentation)

Now let's customize the tabs:

    // either just positions in pixels
    t.tabs   = [ 30, 60, 90, 120 ];
    t.tabs   = Array.geom( 5, 20, 1.5 ).integrate;
    // ...or combos of positions with alignment
    t.tabs   = [[ 30, \center ], [ 60, \center ], [ 90, \center ], [ 120, \center ]];
    t.tabs   = [[ 30, \right ], [ 60, \right ], [ 90, \right ], [ 120, \right ]];

    t.string = "\t1.0\n\t2.3456\n\t-133.3"
    t.tabs   = [[ 40, \decimal ]];

### Disabling the editor

When the text view is a display-only element and you don't want the content to be editable, you can call

    t.editable = false;  // disable editing
    t.editable = true;   // enable editing

### Text editing notifications

When using the view as an editor, you may want to be notified about the editing process. To do so, register an action function:

    t.action = { arg view, state ... params; ([ state ] ++ params).postcs };

The following states and state-parameters are defined:

- `\insert, <(Integer) pos>, <(Integer) length>, <(String) insertedText>`
- `\remove, <(Integer) pos>, <(Integer) length>`
- `\caret, <(Integer) pos1>, <(Integer) pos2>`

`\caret` means the cursor was moved or the selection changed. with no text selected (cursor displayed) `pos1` equals `pos2`, otherwise `pos1` and `pos2` correspond to the selected range (`pos1` may be both greater or less than `pos2`).

### Selections

While the whole text (without styling) can be queried with the `string` method, `selectedString` returns the currently selected text (possibly empty). The cursor position where the selections begins is returned by `selectionStart` and the number of characters in the selection by `selectionSize`. The following example uses these methods to provide a styling button:

    (
        t.bounds = t.bounds.insetAll( 0, 30, 0, 0 );
        x = JSCButton( t.parent, Rect( 4, 4, 60, 22 ))
            .enabled_( false )
            .canFocus_( false )
            .states_([[ "Style" ]])
            .action_({ t.setStringColor( Color.rand, t.selectionStart, t.selectionSize )});
        t.action = { arg view, what, pos1, pos2; if( what == \caret, { x.enabled = pos1 != pos2 })};
    )

### Code execution

`JSCTextView` by default allows you to execute the selected text (or the current line if no selection has been made) in the SuperCollider interpreter, by pressing `Ctrl+Return`. For example, try to focus the view and press `Ctrl+Return` here:

    t.string = "JSCTextView.superclasses"; t.selectAll;

To disable this keystroke feature:

    t.enterInterpretsSelection_( false );

### Loading files

There are two methods, `open` and `openURL` to open local and remote text files. These can be plain text or HTML formatted files.

    t.open( Document.current.path ); // load this help document
    t.openURL( "http://en.wikipedia.org/wiki/SuperCollider" );  // a web document

As you can see with the Wikipedia page, the support for modern HTML is limited. E.g. see this bug report: bugs.sun.com/bugdatabase/view_bug.do?bug_id=4296022. Also note that the display becomes better when the view is made non-editable (see next section).

### Hyperlink notifications

When viewing HTML documents, you may wish to be notified about hyperlink mouse-overs and activations. A precondition is that the view is read-only, that is non-editable:

    t.editable = false;

Three action fields are provided, `linkAction` (executed when the user clicks on a link), `linkEnteredAction` (executed when the user moves the mouse over a link), and `linkExitedAction` (executed when the user move the mouse away from a link). The arguments passed into the functions registered here are `view` (the instance of the `JSCTextView`), the `url` and the `description`. For special links like `"javascript:..."` the `url` is empty. Examples:

    t.linkAction        = { arg view, url, description; [ "activated", url, description ].postln };
    t.linkEnteredAction = { arg view, url, description; [ "entered", url, description ].postln };
    t.linkExitedAction  = { arg view, url, description; [ "exited", url, description ].postln };

    // a simple link-action to follow links
    t.linkAction = { arg view, url; view.openURL( url )};

### Scrollbars

By default, the view does not have scrollbars. These can be enabled using the `hasHorizontalScroller_` and `hasVerticalScroller_` methods:

    (
    var win;

    win = JSCWindow.new;
    t = JSCTextView( win, win.view.bounds.insetBy( 4, 4 ))
        .resize_( 5 )
    	.string_( UGen.allSubclasses.collect( _.name ).asCompileString );
    win.front;
    )

    // now display the vertical scrollbars
    t.hasVerticalScroller   = true;
    // now display the horizontal scrollbars
    // ...note that since lineWrap is on, there is
    // ...nothing to scroll here...
    t.hasHorizontalScroller = true;

    // therefore, when we enable automatic hiding
    // ...of unused scrollbars, the horizontal bar
    // ...will disappear again
    t.autohidesScrollers   = true;