blob: 52bb7393c60aa992051b0ee3ca9669e63755d1a7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.widgets;
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.cocoa.*;
/**
* Instances of this class are controls that allow the user
* to choose an item from a list of items, or optionally
* enter a new value by typing it into an editable text
* field. Often, <code>Combo</code>s are used in the same place
* where a single selection <code>List</code> widget could
* be used but space is limited. A <code>Combo</code> takes
* less space than a <code>List</code> widget and shows
* similar information.
* <p>
* Note: Since <code>Combo</code>s can contain both a list
* and an editable text field, it is possible to confuse methods
* which access one versus the other (compare for example,
* <code>clearSelection()</code> and <code>deselectAll()</code>).
* The API documentation is careful to indicate either "the
* receiver's list" or the "the receiver's text field" to
* distinguish between the two cases.
* </p><p>
* Note that although this class is a subclass of <code>Composite</code>,
* it does not make sense to add children to it, or set a layout on it.
* </p>
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>DROP_DOWN, READ_ONLY, SIMPLE</dd>
* <dt><b>Events:</b></dt>
* <dd>DefaultSelection, Modify, Selection, Verify, OrientationChange</dd>
* </dl>
* <p>
* Note: Only one of the styles DROP_DOWN and SIMPLE may be specified.
* </p><p>
* IMPORTANT: This class is <em>not</em> intended to be subclassed.
* </p>
*
* @see List
* @see <a href="http://www.eclipse.org/swt/snippets/#combo">Combo snippets</a>
* @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
* @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
* @noextend This class is not intended to be subclassed by clients.
*/
public class Combo extends Composite {
String text;
int textLimit = LIMIT;
boolean receivingFocus;
boolean ignoreSetObject, ignoreSelection;
NSRange selectionRange;
boolean listVisible;
static final int VISIBLE_COUNT = 5;
/**
* the operating system limit for the number of characters
* that the text field in an instance of this class can hold
*/
public static final int LIMIT;
/*
* These values can be different on different platforms.
* Therefore they are not initialized in the declaration
* to stop the compiler from inlining.
*/
static {
LIMIT = 0x7FFFFFFF;
}
/**
* Constructs a new instance of this class given its parent
* and a style value describing its behavior and appearance.
* <p>
* The style value is either one of the style constants defined in
* class <code>SWT</code> which is applicable to instances of this
* class, or must be built by <em>bitwise OR</em>'ing together
* (that is, using the <code>int</code> "|" operator) two or more
* of those <code>SWT</code> style constants. The class description
* lists the style constants that are applicable to the class.
* Style bits are also inherited from superclasses.
* </p>
*
* @param parent a composite control which will be the parent of the new instance (cannot be null)
* @param style the style of control to construct
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
* </ul>
*
* @see SWT#DROP_DOWN
* @see SWT#READ_ONLY
* @see SWT#SIMPLE
* @see Widget#checkSubclass
* @see Widget#getStyle
*/
public Combo (Composite parent, int style) {
super (parent, checkStyle (style));
}
/**
* Adds the argument to the end of the receiver's list.
* <p>
* Note: If control characters like '\n', '\t' etc. are used
* in the string, then the behavior is platform dependent.
* </p>
* @param string the new item
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the string is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see #add(String,int)
*/
public void add (String string) {
checkWidget ();
if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
NSAttributedString str = createString(string);
if ((style & SWT.READ_ONLY) != 0) {
NSPopUpButton widget = (NSPopUpButton)view;
long /*int*/ selection = widget.indexOfSelectedItem();
NSMenu nsMenu = widget.menu();
NSMenuItem nsItem = (NSMenuItem)new NSMenuItem().alloc();
NSString empty = NSString.string();
nsItem.initWithTitle(empty, 0, empty);
nsItem.setAttributedTitle(str);
nsMenu.addItem(nsItem);
nsItem.release();
if (selection == -1) widget.selectItemAtIndex(-1);
} else {
((NSComboBox)view).addItemWithObjectValue(str);
}
}
/**
* Adds the argument to the receiver's list at the given
* zero-relative index.
* <p>
* Note: To add an item at the end of the list, use the
* result of calling <code>getItemCount()</code> as the
* index or use <code>add(String)</code>.
* </p><p>
* Also note, if control characters like '\n', '\t' etc. are used
* in the string, then the behavior is platform dependent.
* </p>
*
* @param string the new item
* @param index the index for the item
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the string is null</li>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see #add(String)
*/
public void add (String string, int index) {
checkWidget ();
if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
int count = getItemCount ();
if (0 > index || index > count) error (SWT.ERROR_INVALID_RANGE);
NSAttributedString str = createString(string);
if ((style & SWT.READ_ONLY) != 0) {
NSPopUpButton widget = (NSPopUpButton)view;
long /*int*/ selection = widget.indexOfSelectedItem();
NSMenu nsMenu = widget.menu();
NSMenuItem nsItem = (NSMenuItem)new NSMenuItem().alloc();
NSString empty = NSString.string();
nsItem.initWithTitle(empty, 0, empty);
nsItem.setAttributedTitle(str);
nsMenu.insertItem(nsItem, index);
nsItem.release();
if (selection == -1) widget.selectItemAtIndex(-1);
} else {
((NSComboBox)view).insertItemWithObjectValue(str, index);
}
}
/**
* Adds the listener to the collection of listeners who will
* be notified when the receiver's text is modified, by sending
* it one of the messages defined in the <code>ModifyListener</code>
* interface.
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see ModifyListener
* @see #removeModifyListener
*/
public void addModifyListener (ModifyListener listener) {
checkWidget();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
TypedListener typedListener = new TypedListener (listener);
addListener (SWT.Modify, typedListener);
}
/**
* Adds a segment listener.
* <p>
* A <code>SegmentEvent</code> is sent whenever text content is being modified or
* a segment listener is added or removed. You can
* customize the appearance of text by indicating certain characters to be inserted
* at certain text offsets. This may be used for bidi purposes, e.g. when
* adjacent segments of right-to-left text should not be reordered relative to
* each other.
* E.g., multiple Java string literals in a right-to-left language
* should generally remain in logical order to each other, that is, the
* way they are stored.
* </p>
* <p>
* <b>Warning</b>: This API is currently only implemented on Windows.
* <code>SegmentEvent</code>s won't be sent on GTK and Cocoa.
* </p>
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SegmentEvent
* @see SegmentListener
* @see #removeSegmentListener
*
* @since 3.103
*/
public void addSegmentListener (SegmentListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
addListener (SWT.Segments, new TypedListener (listener));
}
/**
* Adds the listener to the collection of listeners who will
* be notified when the user changes the receiver's selection, by sending
* it one of the messages defined in the <code>SelectionListener</code>
* interface.
* <p>
* <code>widgetSelected</code> is called when the user changes the combo's list selection.
* <code>widgetDefaultSelected</code> is typically called when ENTER is pressed the combo's text area.
* </p>
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SelectionListener
* @see #removeSelectionListener
* @see SelectionEvent
*/
public void addSelectionListener(SelectionListener listener) {
checkWidget();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
TypedListener typedListener = new TypedListener (listener);
addListener (SWT.Selection,typedListener);
addListener (SWT.DefaultSelection,typedListener);
}
/**
* Adds the listener to the collection of listeners who will
* be notified when the receiver's text is verified, by sending
* it one of the messages defined in the <code>VerifyListener</code>
* interface.
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see VerifyListener
* @see #removeVerifyListener
*
* @since 3.1
*/
public void addVerifyListener (VerifyListener listener) {
checkWidget();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
TypedListener typedListener = new TypedListener (listener);
addListener (SWT.Verify, typedListener);
}
@Override
boolean becomeFirstResponder (long /*int*/ id, long /*int*/ sel) {
receivingFocus = true;
boolean result = super.becomeFirstResponder (id, sel);
receivingFocus = false;
return result;
}
static int checkStyle (int style) {
/*
* Feature in Windows. It is not possible to create
* a combo box that has a border using Windows style
* bits. All combo boxes draw their own border and
* do not use the standard Windows border styles.
* Therefore, no matter what style bits are specified,
* clear the BORDER bits so that the SWT style will
* match the Windows widget.
*
* The Windows behavior is currently implemented on
* all platforms.
*/
style &= ~SWT.BORDER;
/*
* Even though it is legal to create this widget
* with scroll bars, they serve no useful purpose
* because they do not automatically scroll the
* widget's client area. The fix is to clear
* the SWT style.
*/
style &= ~(SWT.H_SCROLL | SWT.V_SCROLL);
style = checkBits (style, SWT.DROP_DOWN, SWT.SIMPLE, 0, 0, 0, 0);
if ((style & SWT.SIMPLE) != 0) return style & ~SWT.READ_ONLY;
return style;
}
@Override
protected void checkSubclass () {
if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
}
/**
* Sets the selection in the receiver's text field to an empty
* selection starting just before the first character. If the
* text field is editable, this has the effect of placing the
* i-beam at the start of the text.
* <p>
* Note: To clear the selected items in the receiver's list,
* use <code>deselectAll()</code>.
* </p>
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see #deselectAll
*/
public void clearSelection () {
checkWidget();
if ((style & SWT.READ_ONLY) == 0) {
Point selection = getSelection ();
selection.y = selection.x;
setSelection (selection);
}
}
@Override
void setObjectValue(long /*int*/ id, long /*int*/ sel, long /*int*/ arg0) {
super.setObjectValue(id, sel, ignoreSetObject ? arg0 : createString(text).id);
}
@Override
void comboBoxSelectionDidChange(long /*int*/ id, long /*int*/ sel, long /*int*/ notification) {
NSComboBox widget = (NSComboBox)view;
long /*int*/ tableSelection = widget.indexOfSelectedItem();
widget.selectItemAtIndex(tableSelection);
NSAttributedString attStr = new NSAttributedString (widget.itemObjectValueAtIndex(tableSelection));
NSString nsString = attStr.string();
if (nsString != null) setText(nsString.getString(), true);
if (!ignoreSelection) sendSelectionEvent (SWT.Selection, null, display.trackingControl != this);
}
@Override
public Point computeSize (int wHint, int hHint, boolean changed) {
checkWidget ();
int width = 0, height = 0;
NSControl widget = (NSControl)view;
NSCell viewCell = widget.cell ();
NSSize size = viewCell.cellSize ();
width = (int)Math.ceil (size.width);
height = (int)Math.ceil (size.height);
if ((style & SWT.READ_ONLY) == 0) {
ignoreSetObject = true;
NSComboBoxCell cell = new NSComboBoxCell (viewCell.id);
NSArray array = cell.objectValues ();
int length = (int)/*64*/array.count ();
if (length > 0) {
cell = new NSComboBoxCell (cell.copy ());
for (int i = 0; i < length; i++) {
NSAttributedString attStr = new NSAttributedString (array.objectAtIndex (i));
cell.setAttributedStringValue(attStr);
size = cell.cellSize ();
width = Math.max (width, (int)Math.ceil (size.width));
}
cell.release ();
}
ignoreSetObject = false;
/*
* Attempting to create an NSComboBox with a height > 27 spews a
* very long warning message to stdout and draws the combo incorrectly.
* Limit height to frame height when combo has multiline text.
*/
NSString nsStr = widget.stringValue();
if (nsStr != null ){
String str = nsStr.getString();
if (str != null && (str.indexOf('\n') >= 0 || str.indexOf('\r') >= 0)){
int frameHeight = (int) view.frame().height;
if (frameHeight > 0){
height = frameHeight;
}
}
}
} else {
/*
* In a SWT.READ_ONLY Combo with single item, but no selection,
* the width of the cell returned by cellSize() is smaller than expected.
* Get the correct width by setting and resetting the selected item.
*/
NSPopUpButton nsPopUpButton = (NSPopUpButton)view;
if ((nsPopUpButton.numberOfItems () == 1) && (nsPopUpButton.indexOfSelectedItem () == -1)) {
nsPopUpButton.selectItemAtIndex (0);
size = viewCell.cellSize ();
width = Math.max (width, (int)Math.ceil (size.width));
nsPopUpButton.selectItemAtIndex (-1);
}
}
/*
* Feature in Cocoa. Attempting to create an NSComboBox with a
* height > 27 spews a very long warning message to stdout and
* often draws the combo incorrectly. The workaround is to limit
* the returned height of editable Combos to the height that is
* required to display their text, even if a larger hHint is specified.
*/
if (hHint != SWT.DEFAULT) {
if ((style & SWT.READ_ONLY) != 0 || hHint < height) height = hHint;
}
if (wHint != SWT.DEFAULT) width = wHint;
return new Point (width, height);
}
/**
* Copies the selected text.
* <p>
* The current selection is copied to the clipboard.
* </p>
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 2.1
*/
public void copy () {
checkWidget ();
Point selection = getSelection ();
if (selection.x == selection.y) return;
copyToClipboard (getText (selection.x, selection.y));
}
@Override
void createHandle () {
if ((style & SWT.READ_ONLY) != 0) {
NSPopUpButton widget = (NSPopUpButton)new SWTPopUpButton().alloc();
widget.initWithFrame(new NSRect(), false);
widget.menu().setAutoenablesItems(false);
widget.setTarget(widget);
widget.setAction(OS.sel_sendSelection);
widget.menu().setDelegate(widget);
view = widget;
} else {
NSComboBox widget = (NSComboBox)new SWTComboBox().alloc();
widget.init();
widget.setDelegate(widget);
NSCell cell = widget.cell();
if (OS.VERSION >= 0x1060 && cell != null){
cell.setUsesSingleLineMode(true);
}
view = widget;
}
}
NSAttributedString createString(String string) {
NSAttributedString attribStr = createString(string, null, foreground, SWT.LEFT, false, true, false);
attribStr.autorelease();
return attribStr;
}
@Override
void createWidget() {
text = "";
super.createWidget();
if ((style & SWT.READ_ONLY) == 0) {
NSComboBox widget = (NSComboBox)view;
NSScreen screen = widget.window().screen();
NSRect rect = screen != null ? screen.frame() : NSScreen.mainScreen().frame();
int visibleCount = Math.max(VISIBLE_COUNT, (int)(rect.height / 3 / widget.itemHeight()));
widget.setNumberOfVisibleItems(visibleCount);
}
}
@Override
void comboBoxWillDismiss(long /*int*/ id, long /*int*/ sel, long /*int*/ notification) {
display.currentCombo = null;
listVisible = false;
}
@Override
void comboBoxWillPopUp(long /*int*/ id, long /*int*/ sel, long /*int*/ notification) {
display.currentCombo = this;
listVisible = true;
}
/**
* Cuts the selected text.
* <p>
* The current selection is first copied to the
* clipboard and then deleted from the widget.
* </p>
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 2.1
*/
public void cut () {
checkWidget ();
if ((style & SWT.READ_ONLY) != 0) return;
Point selection = getSelection ();
if (selection.x == selection.y) return;
int start = selection.x, end = selection.y;
String text = getText ();
String leftText = text.substring (0, start);
String rightText = text.substring (end, text.length ());
String oldText = text.substring (start, end);
String newText = "";
if (hooks (SWT.Verify) || filters (SWT.Verify)) {
newText = verifyText (newText, start, end, null);
if (newText == null) return;
}
char [] buffer = new char [oldText.length ()];
oldText.getChars (0, buffer.length, buffer, 0);
copyToClipboard (buffer);
setText (leftText + newText + rightText, false);
start += newText.length ();
setSelection (new Point (start, start));
sendEvent (SWT.Modify);
}
@Override
Color defaultBackground () {
return display.getWidgetColor (SWT.COLOR_LIST_BACKGROUND);
}
@Override
NSFont defaultNSFont() {
if ((style & SWT.READ_ONLY) != 0) return display.popUpButtonFont;
return display.comboBoxFont;
}
@Override
Color defaultForeground () {
return display.getWidgetColor (SWT.COLOR_LIST_FOREGROUND);
}
@Override
void deregister() {
super.deregister();
display.removeWidget(((NSControl)view).cell());
}
/**
* Deselects the item at the given zero-relative index in the receiver's
* list. If the item at the index was already deselected, it remains
* deselected. Indices that are out of range are ignored.
*
* @param index the index of the item to deselect
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void deselect (int index) {
checkWidget ();
if (index == -1) return;
if (index == getSelectionIndex ()) {
if ((style & SWT.READ_ONLY) != 0) {
((NSPopUpButton)view).selectItem(null);
sendEvent (SWT.Modify);
} else {
((NSComboBox)view).deselectItemAtIndex(index);
}
}
}
/**
* Deselects all selected items in the receiver's list.
* <p>
* Note: To clear the selection in the receiver's text field,
* use <code>clearSelection()</code>.
* </p>
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see #clearSelection
*/
public void deselectAll () {
checkWidget ();
if ((style & SWT.READ_ONLY) != 0) {
((NSPopUpButton)view).selectItem(null);
sendEvent (SWT.Modify);
} else {
NSComboBox widget = (NSComboBox)view;
long /*int*/ index = widget.indexOfSelectedItem();
if (index != -1) widget.deselectItemAtIndex(index);
}
}
@Override
boolean dragDetect(int x, int y, boolean filter, boolean[] consume) {
if ((style & SWT.READ_ONLY) == 0) {
NSText fieldEditor = ((NSControl)view).currentEditor();
if (fieldEditor != null) {
NSRange selectedRange = fieldEditor.selectedRange();
if (selectedRange.length > 0) {
NSTextView feAsTextView = new NSTextView(fieldEditor);
NSPoint textViewMouse = new NSPoint();
textViewMouse.x = x;
textViewMouse.y = y;
long /*int*/ charPosition = feAsTextView.characterIndexForInsertionAtPoint(textViewMouse);
if (charPosition != OS.NSNotFound && charPosition >= selectedRange.location && charPosition < (selectedRange.location + selectedRange.length)) {
if (super.dragDetect(x, y, filter, consume)) {
if (consume != null) consume[0] = true;
return true;
}
}
}
}
return false;
}
return super.dragDetect(x, y, filter, consume);
}
@Override
Cursor findCursor () {
Cursor cursor = super.findCursor ();
return (cursor != null || (style & SWT.READ_ONLY) != 0) ? cursor : display.getSystemCursor (SWT.CURSOR_IBEAM);
}
@Override
NSRect focusRingMaskBoundsForFrame (long /*int*/ id, long /*int*/ sel, NSRect cellFrame, long /*int*/ view) {
return cellFrame;
}
/**
* Returns the character position of the caret.
* <p>
* Indexing is zero based.
* </p>
*
* @return the position of the caret
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.8
*/
public int getCaretPosition() {
checkWidget();
return selectionRange != null ? (int)/*64*/selectionRange.location : 0;
}
/**
* Returns a point describing the location of the caret relative
* to the receiver.
*
* @return a point, the location of the caret
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.8
*/
public Point getCaretLocation() {
checkWidget();
NSTextView widget = null;
if (this.hasFocus()) {
widget = new NSTextView(view.window().fieldEditor(true, view));
}
if (widget == null) return new Point (0, 0);
NSLayoutManager layoutManager = widget.layoutManager();
NSTextContainer container = widget.textContainer();
NSRange range = widget.selectedRange();
long /*int*/ [] rectCount = new long /*int*/ [1];
long /*int*/ pArray = layoutManager.rectArrayForCharacterRange(range, range, container, rectCount);
NSRect rect = new NSRect();
if (rectCount[0] > 0) OS.memmove(rect, pArray, NSRect.sizeof);
NSPoint pt = new NSPoint();
pt.x = (int)rect.x;
pt.y = (int)rect.y;
pt = widget.convertPoint_toView_(pt, view);
return new Point((int)pt.x, (int)pt.y);
}
int getCharCount() {
NSString str;
if ((style & SWT.READ_ONLY) != 0) {
str = ((NSPopUpButton)view).titleOfSelectedItem();
} else {
str = new NSCell(((NSComboBox)view).cell()).title();
}
if (str == null) return 0;
return (int)/*64*/str.length();
}
/**
* Returns the item at the given, zero-relative index in the
* receiver's list. Throws an exception if the index is out
* of range.
*
* @param index the index of the item to return
* @return the item at the given index
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public String getItem (int index) {
checkWidget ();
int count = getItemCount ();
if (0 > index || index >= count) error (SWT.ERROR_INVALID_RANGE);
NSString str = null;
if ((style & SWT.READ_ONLY) != 0) {
str = ((NSPopUpButton)view).itemTitleAtIndex(index);
} else {
NSAttributedString attString = new NSAttributedString(((NSComboBox)view).itemObjectValueAtIndex(index));
if (attString != null) str = attString.string();
}
if (str == null) error(SWT.ERROR_CANNOT_GET_ITEM);
return str.getString();
}
/**
* Returns the number of items contained in the receiver's list.
*
* @return the number of items
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int getItemCount () {
checkWidget ();
if ((style & SWT.READ_ONLY) != 0) {
return (int)/*64*/((NSPopUpButton)view).numberOfItems();
} else {
return (int)/*64*/((NSComboBox)view).numberOfItems();
}
}
/**
* Returns the height of the area which would be used to
* display <em>one</em> of the items in the receiver's list.
*
* @return the height of one item
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int getItemHeight () {
checkWidget ();
//TODO - not supported by the OS
return 26;
}
/**
* Returns a (possibly empty) array of <code>String</code>s which are
* the items in the receiver's list.
* <p>
* Note: This is not the actual structure used by the receiver
* to maintain its list of items, so modifying the array will
* not affect the receiver.
* </p>
*
* @return the items in the receiver's list
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public String [] getItems () {
checkWidget ();
int count = getItemCount ();
String [] result = new String [count];
for (int i=0; i<count; i++) result [i] = getItem (i);
return result;
}
/**
* Returns <code>true</code> if the receiver's list is visible,
* and <code>false</code> otherwise.
* <p>
* If one of the receiver's ancestors is not visible or some
* other condition makes the receiver not visible, this method
* may still indicate that it is considered visible even though
* it may not actually be showing.
* </p>
*
* @return the receiver's list's visibility state
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.4
*/
public boolean getListVisible () {
checkWidget ();
return listVisible;
}
@Override
String getNameText () {
return getText ();
}
@Override
int getMininumHeight () {
return getTextHeight ();
}
/**
* Returns the orientation of the receiver.
*
* @return the orientation style
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 2.1.2
*/
@Override
public int getOrientation () {
checkWidget();
return style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
}
/**
* Returns a <code>Point</code> whose x coordinate is the
* character position representing the start of the selection
* in the receiver's text field, and whose y coordinate is the
* character position representing the end of the selection.
* An "empty" selection is indicated by the x and y coordinates
* having the same value.
* <p>
* Indexing is zero based. The range of a selection is from
* 0..N where N is the number of characters in the widget.
* </p>
*
* @return a point representing the selection start and end
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public Point getSelection () {
checkWidget ();
if ((style & SWT.READ_ONLY) != 0) {
return new Point (0, getCharCount ());
} else {
if (selectionRange == null) {
NSString str = new NSTextFieldCell (((NSTextField) view).cell ()).title ();
return new Point((int)/*64*/str.length (), (int)/*64*/str.length ());
}
return new Point((int)/*64*/selectionRange.location, (int)/*64*/(selectionRange.location + selectionRange.length));
}
}
/**
* Returns the zero-relative index of the item which is currently
* selected in the receiver's list, or -1 if no item is selected.
*
* @return the index of the selected item
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int getSelectionIndex () {
checkWidget ();
if ((style & SWT.READ_ONLY) != 0) {
return (int)/*64*/((NSPopUpButton)view).indexOfSelectedItem();
} else {
return (int)/*64*/((NSComboBox)view).indexOfSelectedItem();
}
}
/**
* Returns a string containing a copy of the contents of the
* receiver's text field, or an empty string if there are no
* contents.
*
* @return the receiver's text
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public String getText () {
checkWidget ();
return new String (getText(0, -1));
}
char [] getText (int start, int end) {
NSString str;
if ((style & SWT.READ_ONLY) != 0) {
str = ((NSPopUpButton)view).titleOfSelectedItem();
} else {
str = new NSCell(((NSComboBox)view).cell()).title();
}
if (str == null) return new char[0];
NSRange range = new NSRange ();
range.location = start;
if (end == -1) {
long /*int*/ length = str.length();
range.length = length - start;
} else {
range.length = end - start;
}
char [] buffer= new char [(int)/*64*/range.length];
str.getCharacters(buffer, range);
return buffer;
}
/**
* Returns the height of the receivers's text field.
*
* @return the text height
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int getTextHeight () {
checkWidget();
NSCell cell;
if ((style & SWT.READ_ONLY) != 0) {
cell = ((NSPopUpButton)view).cell();
} else {
cell = ((NSComboBox)view).cell();
}
return (int)cell.cellSize().height;
}
/**
* Returns the maximum number of characters that the receiver's
* text field is capable of holding. If this has not been changed
* by <code>setTextLimit()</code>, it will be the constant
* <code>Combo.LIMIT</code>.
*
* @return the text limit
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see #LIMIT
*/
public int getTextLimit () {
checkWidget();
return textLimit;
}
/**
* Gets the number of items that are visible in the drop
* down portion of the receiver's list.
* <p>
* Note: This operation is a hint and is not supported on
* platforms that do not have this concept.
* </p>
*
* @return the number of items that are visible
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.0
*/
public int getVisibleItemCount () {
checkWidget ();
if ((style & SWT.READ_ONLY) != 0) {
return getItemCount ();
} else {
return (int)/*64*/((NSComboBox)view).numberOfVisibleItems();
}
}
/**
* Searches the receiver's list starting at the first item
* (index 0) until an item is found that is equal to the
* argument, and returns the index of that item. If no item
* is found, returns -1.
*
* @param string the search item
* @return the index of the item
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the string is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int indexOf (String string) {
return indexOf (string, 0);
}
/**
* Searches the receiver's list starting at the given,
* zero-relative index until an item is found that is equal
* to the argument, and returns the index of that item. If
* no item is found or the starting index is out of range,
* returns -1.
*
* @param string the search item
* @param start the zero-relative index at which to begin the search
* @return the index of the item
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the string is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int indexOf (String string, int start) {
checkWidget();
if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
int count = getItemCount ();
if (!(0 <= start && start < count)) return -1;
for (int i=start; i<count; i++) {
if (string.equals (getItem (i))) {
return i;
}
}
return -1;
}
@Override
boolean isEventView (long /*int*/ id) {
return true;
}
@Override
void menuWillOpen(long /*int*/ id, long /*int*/ sel, long /*int*/ menu) {
listVisible = true;
}
@Override
void menuDidClose(long /*int*/ id, long /*int*/ sel, long /*int*/ menu) {
listVisible = false;
}
@Override
void mouseDown(long /*int*/ id, long /*int*/ sel, long /*int*/ theEvent) {
// If this is a combo box with an editor field and the control is disposed
// while the view's cell editor is open we crash while tearing down the
// popup window. Fix is to retain the view before letting Cocoa track
// the mouse events.
display.sendPreExternalEventDispatchEvent();
// 'view' will be cleared if disposed during the mouseDown so cache it.
NSView viewCopy = view;
viewCopy.retain();
super.mouseDown(id, sel, theEvent);
viewCopy.release();
display.sendPostExternalEventDispatchEvent();
}
/**
* Pastes text from clipboard.
* <p>
* The selected text is deleted from the widget
* and new text inserted from the clipboard.
* </p>
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 2.1
*/
public void paste () {
checkWidget ();
if ((style & SWT.READ_ONLY) != 0) return;
Point selection = getSelection ();
int start = selection.x, end = selection.y;
String text = getText ();
String leftText = text.substring (0, start);
String rightText = text.substring (end, text.length ());
String newText = getClipboardText ();
if (newText == null) return;
if (hooks (SWT.Verify) || filters (SWT.Verify)) {
newText = verifyText (newText, start, end, null);
if (newText == null) return;
}
if (textLimit != LIMIT) {
int charCount = text.length ();
if (charCount - (end - start) + newText.length() > textLimit) {
newText = newText.substring(0, textLimit - charCount + (end - start));
}
}
setText (leftText + newText + rightText, false);
start += newText.length ();
setSelection (new Point (start, start));
sendEvent (SWT.Modify);
}
@Override
void register() {
super.register();
display.addWidget(((NSControl)view).cell(), this);
}
@Override
void releaseWidget () {
if (display.currentCombo == this) {
display.currentCombo = null;
}
super.releaseWidget ();
if ((style & SWT.READ_ONLY) == 0) {
((NSControl)view).abortEditing();
}
text = null;
selectionRange = null;
}
/**
* Removes the item from the receiver's list at the given
* zero-relative index.
*
* @param index the index for the item
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void remove (int index) {
checkWidget ();
if (index == -1) error (SWT.ERROR_INVALID_RANGE);
int count = getItemCount ();
if (0 > index || index >= count) error (SWT.ERROR_INVALID_RANGE);
if ((style & SWT.READ_ONLY) != 0) {
((NSPopUpButton)view).removeItemAtIndex(index);
} else {
((NSComboBox)view).removeItemAtIndex(index);
}
}
/**
* Removes the items from the receiver's list which are
* between the given zero-relative start and end
* indices (inclusive).
*
* @param start the start of the range
* @param end the end of the range
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void remove (int start, int end) {
checkWidget();
if (start > end) return;
int count = getItemCount ();
if (!(0 <= start && start <= end && end < count)) {
error (SWT.ERROR_INVALID_RANGE);
}
int newEnd = Math.min (end, count - 1);
for (int i=newEnd; i>=start; i--) {
remove(i);
}
}
/**
* Searches the receiver's list starting at the first item
* until an item is found that is equal to the argument,
* and removes that item from the list.
*
* @param string the item to remove
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the string is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void remove (String string) {
checkWidget ();
if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
int index = indexOf (string, 0);
if (index == -1) error (SWT.ERROR_INVALID_ARGUMENT);
remove (index);
}
/**
* Removes all of the items from the receiver's list and clear the
* contents of receiver's text field.
* <p>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void removeAll () {
checkWidget ();
ignoreSelection = true;
if ((style & SWT.READ_ONLY) != 0) {
((NSPopUpButton)view).removeAllItems();
} else {
setText ("", true);
((NSComboBox)view).removeAllItems();
}
ignoreSelection = false;
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the receiver's text is modified.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see ModifyListener
* @see #addModifyListener
*/
public void removeModifyListener (ModifyListener listener) {
checkWidget();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
if (eventTable == null) return;
eventTable.unhook (SWT.Modify, listener);
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the receiver's text is modified.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SegmentEvent
* @see SegmentListener
* @see #addSegmentListener
*
* @since 3.103
*/
public void removeSegmentListener (SegmentListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
eventTable.unhook (SWT.Segments, listener);
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the user changes the receiver's selection.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SelectionListener
* @see #addSelectionListener
*/
public void removeSelectionListener (SelectionListener listener) {
checkWidget();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
if (eventTable == null) return;
eventTable.unhook (SWT.Selection, listener);
eventTable.unhook (SWT.DefaultSelection,listener);
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the control is verified.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see VerifyListener
* @see #addVerifyListener
*
* @since 3.1
*/
public void removeVerifyListener (VerifyListener listener) {
checkWidget();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
if (eventTable == null) return;
eventTable.unhook (SWT.Verify, listener);
}
/**
* Selects the item at the given zero-relative index in the receiver's
* list. If the item at the index was already selected, it remains
* selected. Indices that are out of range are ignored.
*
* @param index the index of the item to select
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void select (int index) {
checkWidget ();
int count = getItemCount ();
if (0 <= index && index < count) {
if (index == getSelectionIndex()) return;
ignoreSelection = true;
if ((style & SWT.READ_ONLY) != 0) {
((NSPopUpButton)view).selectItemAtIndex(index);
sendEvent (SWT.Modify);
} else {
NSComboBox widget = (NSComboBox)view;
widget.deselectItemAtIndex(index);
widget.selectItemAtIndex(index);
}
ignoreSelection = false;
}
}
@Override
void sendSelection () {
sendEvent(SWT.Modify);
if (!ignoreSelection) sendSelectionEvent(SWT.Selection);
}
@Override
boolean sendKeyEvent (NSEvent nsEvent, int type) {
boolean result = super.sendKeyEvent (nsEvent, type);
if (!result) return result;
int stateMask = 0;
long /*int*/ modifierFlags = nsEvent.modifierFlags();
if ((modifierFlags & OS.NSAlternateKeyMask) != 0) stateMask |= SWT.ALT;
if ((modifierFlags & OS.NSShiftKeyMask) != 0) stateMask |= SWT.SHIFT;
if ((modifierFlags & OS.NSControlKeyMask) != 0) stateMask |= SWT.CONTROL;
if ((modifierFlags & OS.NSCommandKeyMask) != 0) stateMask |= SWT.COMMAND;
if (type != SWT.KeyDown) return result;
short keyCode = nsEvent.keyCode ();
if (stateMask == SWT.COMMAND) {
switch (keyCode) {
case 7: /* X */
cut ();
return false;
case 8: /* C */
copy ();
return false;
case 9: /* V */
paste ();
return false;
case 0: /* A */
if ((style & SWT.READ_ONLY) == 0) {
((NSComboBox)view).selectText(null);
return false;
}
}
}
switch (keyCode) {
case 76: /* KP Enter */
case 36: /* Return */
sendSelectionEvent (SWT.DefaultSelection);
}
return result;
}
boolean sendTrackingKeyEvent (NSEvent nsEvent, int type) {
/*
* Feature in Cocoa. Combo does not send arrow down/up
* key down events while the list is showing. The fix is
* to send these events when the event is removed from the
* queue.
*/
long /*int*/ modifiers = nsEvent.modifierFlags();
if ((modifiers & OS.NSShiftKeyMask) == 0) {
short keyCode = nsEvent.keyCode ();
switch (keyCode) {
case 125: /* Arrow Down */
case 126: /* Arrow Up */
sendKeyEvent(nsEvent, type);
return true;
}
}
return false;
}
@Override
void setBackgroundColor(NSColor nsColor) {
if ((style & SWT.READ_ONLY) != 0) {
//TODO
} else {
((NSTextField)view).setBackgroundColor(nsColor);
}
}
@Override
void setBackgroundImage(NSImage image) {
//TODO setDrawsBackground is ignored by NSComboBox?
}
@Override
void setBounds (int x, int y, int width, int height, boolean move, boolean resize) {
/*
* Feature in Cocoa. Attempting to create an NSComboBox with a
* height > 27 spews a very long warning message to stdout and
* often draws the combo incorrectly.
* The workaround is to limit the height of editable Combos to the
* height that is required to display their text. For multiline text,
* limit the height to frame height.
*/
if ((style & SWT.READ_ONLY) == 0) {
NSControl widget = (NSControl)view;
int hLimit = 0;
NSString nsStr = widget.stringValue();
if (nsStr != null ){
String str = nsStr.getString();
if (str != null && (str.indexOf('\n') >= 0 || str.indexOf('\r') >= 0)) {
int frameHeight = (int) view.frame().height;
if (frameHeight > 0) {
hLimit = frameHeight;
}
}
}
if (hLimit == 0) {
NSSize size = widget.cell ().cellSize ();
hLimit = (int)Math.ceil (size.height);
}
height = Math.min (height, hLimit);
}
super.setBounds (x, y, width, height, move, resize);
}
@Override
void setFont (NSFont font) {
super.setFont(font);
updateItems();
}
@Override
void setForeground (double /*float*/ [] color) {
super.setForeground(color);
updateItems();
if ((style & SWT.READ_ONLY) == 0) {
NSColor nsColor;
if (color == null) {
nsColor = NSColor.textColor ();
} else {
nsColor = NSColor.colorWithDeviceRed(color[0], color[1], color[2], 1);
}
((NSTextField)view).setTextColor(nsColor);
}
}
/**
* Sets the text of the item in the receiver's list at the given
* zero-relative index to the string argument.
*
* @param index the index for the item
* @param string the new text for the item
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* <li>ERROR_NULL_ARGUMENT - if the string is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setItem (int index, String string) {
checkWidget ();
if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
int count = getItemCount ();
if (0 > index || index >= count) error (SWT.ERROR_INVALID_RANGE);
int selection = getSelectionIndex();
NSAttributedString str = createString(string);
ignoreSelection = true;
if ((style & SWT.READ_ONLY) != 0) {
NSMenuItem nsItem = ((NSPopUpButton)view).itemAtIndex(index);
nsItem.setAttributedTitle(str);
/*
* Feature in Cocoa. Setting the attributed title on an NSMenuItem
* also sets the title, but clearing the attributed title does not
* clear the title. The fix is to explicitly set the title to an
* empty string in this case.
*/
if (string.length() == 0) nsItem.setTitle(NSString.string());
} else {
NSComboBox widget = (NSComboBox)view;
widget.insertItemWithObjectValue(str, index);
widget.removeItemAtIndex(index + 1);
}
if (selection != -1) select (selection);
ignoreSelection = false;
}
/**
* Sets the receiver's list to be the given array of items.
*
* @param items the array of items
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the items array is null</li>
* <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setItems (String [] items) {
checkWidget();
if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
for (int i=0; i<items.length; i++) {
if (items [i] == null) error (SWT.ERROR_INVALID_ARGUMENT);
}
removeAll();
if (items.length == 0) return;
ignoreSelection = true;
for (int i= 0; i < items.length; i++) {
NSAttributedString str = createString(items[i]);
if ((style & SWT.READ_ONLY) != 0) {
NSMenu nsMenu = ((NSPopUpButton)view).menu();
NSMenuItem nsItem = (NSMenuItem)new NSMenuItem().alloc();
NSString empty = NSString.string();
nsItem.initWithTitle(empty, 0, empty);
nsItem.setAttributedTitle(str);
nsMenu.addItem(nsItem);
nsItem.release();
//clear the selection
((NSPopUpButton)view).selectItemAtIndex(-1);
} else {
((NSComboBox)view).addItemWithObjectValue(str);
}
}
ignoreSelection = false;
}
/**
* Marks the receiver's list as visible if the argument is <code>true</code>,
* and marks it invisible otherwise.
* <p>
* If one of the receiver's ancestors is not visible or some
* other condition makes the receiver not visible, marking
* it visible may not actually cause it to be displayed.
* </p>
*
* @param visible the new visibility state
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.4
*/
public void setListVisible (boolean visible) {
checkWidget ();
if ((style & SWT.READ_ONLY) != 0) {
((NSPopUpButton)view).setPullsDown(visible);
} else {
}
}
/**
* Sets the orientation of the receiver, which must be one
* of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
* <p>
*
* @param orientation new orientation style
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 2.1.2
*/
@Override
public void setOrientation (int orientation) {
checkWidget();
}
@Override
void setOrientation () {
int direction = (style & SWT.RIGHT_TO_LEFT) != 0 ? OS.NSWritingDirectionRightToLeft : OS.NSWritingDirectionLeftToRight;
((NSControl)view).setBaseWritingDirection(direction);
}
/**
* Sets the selection in the receiver's text field to the
* range specified by the argument whose x coordinate is the
* start of the selection and whose y coordinate is the end
* of the selection.
*
* @param selection a point representing the new selection start and end
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the point is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setSelection (Point selection) {
checkWidget ();
if (selection == null) error (SWT.ERROR_NULL_ARGUMENT);
if ((style & SWT.READ_ONLY) == 0) {
NSComboBox widget = (NSComboBox)view;
NSString str = new NSCell(widget.cell()).title();
int length = (int)/*64*/str.length();
int start = Math.min (Math.max (Math.min (selection.x, selection.y), 0), length);
int end = Math.min (Math.max (Math.max (selection.x, selection.y), 0), length);
selectionRange = new NSRange();
selectionRange.location = start;
selectionRange.length = end - start;
NSText fieldEditor = widget.currentEditor();
if (fieldEditor != null) fieldEditor.setSelectedRange(selectionRange);
}
}
/**
* Sets the contents of the receiver's text field to the
* given string.
* <p>
* This call is ignored when the receiver is read only and
* the given string is not in the receiver's list.
* </p>
* <p>
* Note: The text field in a <code>Combo</code> is typically
* only capable of displaying a single line of text. Thus,
* setting the text to a string containing line breaks or
* other special characters will probably cause it to
* display incorrectly.
* </p><p>
* Also note, if control characters like '\n', '\t' etc. are used
* in the string, then the behavior is platform dependent.
* </p>
*
* @param string the new text
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the string is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setText (String string) {
checkWidget ();
if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
setText (string, true);
}
void setText (String string, boolean notify) {
if (notify) {
if (hooks (SWT.Verify) || filters (SWT.Verify)) {
string = verifyText (string, 0, getCharCount (), null);
if (string == null) return;
}
}
if ((style & SWT.READ_ONLY) != 0) {
int index = indexOf (string);
if (index != -1) {
select (index);
}
} else {
char[] buffer = new char [Math.min(string.length (), textLimit)];
string.getChars (0, buffer.length, buffer, 0);
text = new String (buffer,0, buffer.length);
((NSComboBox)view).cell().setAttributedStringValue(createString(text));
if (notify) sendEvent (SWT.Modify);
}
selectionRange = null;
}
/**
* Sets the maximum number of characters that the receiver's
* text field is capable of holding to be the argument.
* <p>
* To reset this value to the default, use <code>setTextLimit(Combo.LIMIT)</code>.
* Specifying a limit value larger than <code>Combo.LIMIT</code> sets the
* receiver's limit to <code>Combo.LIMIT</code>.
* </p>
* @param limit new text limit
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see #LIMIT
*/
public void setTextLimit (int limit) {
checkWidget ();
if (limit == 0) error (SWT.ERROR_CANNOT_BE_ZERO);
textLimit = limit;
}
/**
* Sets the number of items that are visible in the drop
* down portion of the receiver's list.
* <p>
* Note: This operation is a hint and is not supported on
* platforms that do not have this concept.
* </p>
*
* @param count the new number of items to be visible
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.0
*/
public void setVisibleItemCount (int count) {
checkWidget ();
if (count < 0) return;
if ((style & SWT.READ_ONLY) != 0) {
//TODO
} else {
((NSComboBox)view).setNumberOfVisibleItems(count);
}
}
@Override
boolean shouldChangeTextInRange_replacementString(long /*int*/ id, long /*int*/ sel, long /*int*/ affectedCharRange, long /*int*/ replacementString) {
NSRange range = new NSRange();
OS.memmove(range, affectedCharRange, NSRange.sizeof);
boolean result = callSuperBoolean(id, sel, range, replacementString);
if (hooks (SWT.Verify)) {
String string = new NSString(replacementString).getString();
NSEvent currentEvent = display.application.currentEvent();
long /*int*/ type = currentEvent.type();
if (type != OS.NSKeyDown && type != OS.NSKeyUp) currentEvent = null;
String newText = verifyText(string, (int)/*64*/range.location, (int)/*64*/(range.location+range.length), currentEvent);
if (newText == null) return false;
if (!string.equals(newText)) {
int length = newText.length();
Point selection = getSelection();
if (textLimit != LIMIT) {
int charCount = getCharCount();
if (charCount - (selection.y - selection.x) + length > textLimit) {
length = textLimit - charCount + (selection.y - selection.x);
}
}
char [] buffer = new char [length];
newText.getChars (0, buffer.length, buffer, 0);
NSString nsstring = NSString.stringWithCharacters (buffer, buffer.length);
NSText fieldEditor = ((NSTextField) view).currentEditor ();
fieldEditor.replaceCharactersInRange (fieldEditor.selectedRange (), nsstring);
text = fieldEditor.string().getString();
sendEvent (SWT.Modify);
result = false;
}
}
if (result) {
char[] chars = new char[text.length()];
text.getChars(0, chars.length, chars, 0);
NSMutableString mutable = (NSMutableString) NSMutableString.stringWithCharacters(chars, chars.length);
mutable.replaceCharactersInRange(range, new NSString(replacementString));
text = mutable.getString();
selectionRange = null;
}
return result;
}
@Override
void textViewDidChangeSelection(long /*int*/ id, long /*int*/ sel, long /*int*/ aNotification) {
NSNotification notification = new NSNotification(aNotification);
NSText editor = new NSText(notification.object().id);
selectionRange = editor.selectedRange();
}
@Override
void textDidChange (long /*int*/ id, long /*int*/ sel, long /*int*/ aNotification) {
super.textDidChange (id, sel, aNotification);
postEvent (SWT.Modify);
}
@Override
NSRange textView_willChangeSelectionFromCharacterRange_toCharacterRange(long /*int*/ id, long /*int*/ sel, long /*int*/ aTextView, long /*int*/ oldSelectedCharRange, long /*int*/ newSelectedCharRange) {
/*
* If the selection is changing as a result of the receiver getting focus
* then return the receiver's last selection range, otherwise the full
* text will be automatically selected.
*/
if (receivingFocus && selectionRange != null) return selectionRange;
/* allow the selection change to proceed */
NSRange result = new NSRange();
OS.memmove(result, newSelectedCharRange, NSRange.sizeof);
return result;
}
void updateItems () {
if ((style & SWT.READ_ONLY) != 0) {
NSPopUpButton widget = (NSPopUpButton)view;
int count = (int)/*64*/ widget.numberOfItems();
for (int i = 0; i < count; i++) {
NSMenuItem item = new NSMenuItem (widget.itemAtIndex(i));
NSAttributedString attStr = item.attributedTitle();
String string = attStr.string().getString();
item.setAttributedTitle(createString(string));
}
} else {
NSComboBox widget = (NSComboBox)view;
int count = (int)/*64*/ widget.numberOfItems();
for (int i = 0; i < count; i++) {
NSAttributedString attStr = new NSAttributedString (widget.itemObjectValueAtIndex(i));
String string = attStr.string().getString();
widget.insertItemWithObjectValue(createString(string), i);
widget.removeItemAtIndex(i + 1);
}
widget.cell().setAttributedStringValue(createString(text));
}
}
String verifyText (String string, int start, int end, NSEvent keyEvent) {
Event event = new Event ();
if (keyEvent != null) setKeyState(event, SWT.MouseDown, keyEvent);
event.text = string;
event.start = start;
event.end = end;
/*
* It is possible (but unlikely), that application
* code could have disposed the widget in the verify
* event. If this happens, answer null to cancel
* the operation.
*/
sendEvent (SWT.Verify, event);
if (!event.doit || isDisposed ()) return null;
return event.text;
}
}