blob: 97860a52b82f40c77c9581d4b2e7ff42f948eb11 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.widgets;
import org.eclipse.swt.internal.motif.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
/**
* Instances of this class provide the appearance and
* behavior of <code>Shells</code>, but are not top
* level shells or dialogs. Class <code>Shell</code>
* shares a significant amount of code with this class,
* and is a subclass.
* <p>
* Instances are always displayed in one of the maximized,
* minimized or normal states:
* <ul>
* <li>
* When an instance is marked as <em>maximized</em>, the
* window manager will typically resize it to fill the
* entire visible area of the display, and the instance
* is usually put in a state where it can not be resized
* (even if it has style <code>RESIZE</code>) until it is
* no longer maximized.
* </li><li>
* When an instance is in the <em>normal</em> state (neither
* maximized or minimized), its appearance is controlled by
* the style constants which were specified when it was created
* and the restrictions of the window manager (see below).
* </li><li>
* When an instance has been marked as <em>minimized</em>,
* its contents (client area) will usually not be visible,
* and depending on the window manager, it may be
* "iconified" (that is, replaced on the desktop by a small
* simplified representation of itself), relocated to a
* distinguished area of the screen, or hidden. Combinations
* of these changes are also possible.
* </li>
* </ul>
* </p>
* Note: The styles supported by this class must be treated
* as <em>HINT</em>s, since the window manager for the
* desktop on which the instance is visible has ultimate
* control over the appearance and behavior of decorations.
* For example, some window managers only support resizable
* windows and will always assume the RESIZE style, even if
* it is not set.
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>BORDER, CLOSE, MIN, MAX, NO_TRIM, RESIZE, TITLE, ON_TOP, TOOL</dd>
* <dt><b>Events:</b></dt>
* <dd>(none)</dd>
* </dl>
* Class <code>SWT</code> provides two "convenience constants"
* for the most commonly required style combinations:
* <dl>
* <dt><code>SHELL_TRIM</code></dt>
* <dd>
* the result of combining the constants which are required
* to produce a typical application top level shell: (that
* is, <code>CLOSE | TITLE | MIN | MAX | RESIZE</code>)
* </dd>
* <dt><code>DIALOG_TRIM</code></dt>
* <dd>
* the result of combining the constants which are required
* to produce a typical application dialog shell: (that
* is, <code>TITLE | CLOSE | BORDER</code>)
* </dd>
* </dl>
* <p>
* IMPORTANT: This class is intended to be subclassed <em>only</em>
* within the SWT implementation.
* </p>
*
* @see #getMinimized
* @see #getMaximized
* @see Shell
* @see SWT
*/
public class Decorations extends Canvas {
String label;
Image image;
Image [] images = new Image [0];
int dialogHandle;
boolean minimized, maximized;
Menu menuBar;
Menu [] menus;
Control savedFocus;
Button defaultButton, saveDefault;
Decorations () {
/* Do nothing */
}
/**
* 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#BORDER
* @see SWT#CLOSE
* @see SWT#MIN
* @see SWT#MAX
* @see SWT#RESIZE
* @see SWT#TITLE
* @see SWT#NO_TRIM
* @see SWT#SHELL_TRIM
* @see SWT#DIALOG_TRIM
* @see SWT#ON_TOP
* @see SWT#TOOL
* @see Widget#checkSubclass
* @see Widget#getStyle
*/
public Decorations (Composite parent, int style) {
super (parent, checkStyle (style));
}
void _setImages (Image [] images) {
Image icon = images != null && images.length > 0 ? icon = images [0] : null;
int pixmap = 0, mask = 0;
if (icon != null) {
switch (icon.type) {
case SWT.BITMAP:
pixmap = icon.pixmap;
break;
case SWT.ICON:
pixmap = icon.pixmap;
mask = icon.mask;
break;
default:
error (SWT.ERROR_INVALID_IMAGE);
}
}
int [] argList = {
OS.XmNiconPixmap, pixmap,
OS.XmNiconMask, mask,
};
int topHandle = topHandle ();
OS.XtSetValues (topHandle, argList, argList.length / 2);
}
void add (Menu menu) {
if (menus == null) menus = new Menu [4];
for (int i=0; i<menus.length; i++) {
if (menus [i] == null) {
menus [i] = menu;
return;
}
}
Menu [] newMenus = new Menu [menus.length + 4];
newMenus [menus.length] = menu;
System.arraycopy (menus, 0, newMenus, 0, menus.length);
menus = newMenus;
}
void bringToTop (boolean force) {
moveAbove (null);
}
static int checkStyle (int style) {
if ((style & (SWT.MENU | SWT.MIN | SWT.MAX | SWT.CLOSE)) != 0) {
style |= SWT.TITLE;
}
return style;
}
protected void checkSubclass () {
if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
}
Control computeTabGroup () {
return this;
}
Control computeTabRoot () {
return this;
}
public Rectangle computeTrim (int x, int y, int width, int height) {
checkWidget();
Rectangle trim = super.computeTrim (x, y, width, height);
if (menuBar != null) {
XtWidgetGeometry request = new XtWidgetGeometry ();
XtWidgetGeometry result = new XtWidgetGeometry ();
request.request_mode = OS.CWWidth;
request.width = trim.width;
OS.XtQueryGeometry (menuBar.handle, request, result);
trim.height += result.height;
}
return trim;
}
void createHandle (int index) {
state |= HANDLE | CANVAS;
createHandle (index, parent.handle, true);
}
void createWidget (int index) {
super.createWidget (index);
label = "";
}
int dialogHandle () {
if (dialogHandle != 0) return dialogHandle;
return dialogHandle = OS.XmCreateDialogShell (handle, null, null, 0);
}
/**
* Returns the receiver's default button if one had
* previously been set, otherwise returns null.
*
* @return the default button or null
*
* @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 #setDefaultButton
*/
public Button getDefaultButton () {
checkWidget();
return defaultButton;
}
/**
* Returns the receiver's image if it had previously been
* set using <code>setImage()</code>. The image is typically
* displayed by the window manager when the instance is
* marked as iconified, and may also be displayed somewhere
* in the trim when the instance is in normal or maximized
* states.
* <p>
* Note: This method will return null if called before
* <code>setImage()</code> is called. It does not provide
* access to a window manager provided, "default" image
* even if one exists.
* </p>
*
* @return the image
*
* @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 Image getImage () {
checkWidget();
return image;
}
/**
* Returns <code>true</code> if the receiver is currently
* maximized, and false otherwise.
* <p>
*
* @return the maximized 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>
*
* @see #setMaximized
*/
public boolean getMaximized () {
checkWidget();
return maximized;
}
/**
* Returns the receiver's menu bar if one had previously
* been set, otherwise returns null.
*
* @return the menu bar or null
*
* @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 Menu getMenuBar () {
checkWidget();
return menuBar;
}
/**
* Returns <code>true</code> if the receiver is currently
* minimized, and false otherwise.
* <p>
*
* @return the minimized 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>
*
* @see #setMinimized
*/
public boolean getMinimized () {
checkWidget();
return minimized;
}
String getNameText () {
return getText ();
}
/**
* Returns the receiver's text, which is the string that the
* window manager will typically display as the receiver's
* <em>title</em>. If the text has not previously been set,
* returns an empty string.
*
* @return the 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 label;
}
boolean isTabGroup () {
return true;
}
boolean isTabItem () {
return false;
}
Decorations menuShell () {
return this;
}
void propagateWidget (boolean enabled) {
super.propagateWidget (enabled);
int [] argList = {OS.XmNmenuBar, 0};
OS.XtGetValues (scrolledHandle, argList, argList.length / 2);
if (argList [1] != 0) propagateHandle (enabled, argList [1], OS.None);
}
void releaseHandle () {
super.releaseHandle ();
dialogHandle = 0;
}
void releaseWidget () {
if (menus != null) {
for (int i=0; i<menus.length; i++) {
Menu menu = menus [i];
if (menu != null && !menu.isDisposed ()) menu.releaseResources ();
}
}
menuBar = null;
menus = null;
super.releaseWidget ();
image = null;
images = null;
defaultButton = saveDefault = null;
label = null;
}
boolean restoreFocus () {
if (savedFocus != null && savedFocus.isDisposed ()) savedFocus = null;
boolean restored = savedFocus != null && savedFocus.setFocus ();
savedFocus = null;
/*
* This code is intentionally commented. When no widget
* has been given focus, some platforms give focus to the
* default button. Motif doesn't do this.
*/
// if (restored) return true;
// if (defaultButton != null && !defaultButton.isDisposed ()) {
// if (defaultButton.setFocus ()) return true;
// }
// return false;
return restored;
}
void remove (Menu menu) {
if (menus == null) return;
for (int i=0; i<menus.length; i++) {
if (menus [i] == menu) {
menus [i] = null;
return;
}
}
}
/**
* If the argument is not null, sets the receiver's default
* button to the argument, and if the argument is null, sets
* the receiver's default button to the first button which
* was set as the receiver's default button (called the
* <em>saved default button</em>). If no default button had
* previously been set, or the saved default button was
* disposed, the receiver's default button will be set to
* null.
*
* @param the new default button
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the button has been disposed</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 setDefaultButton (Button button) {
checkWidget();
setDefaultButton (button, true);
}
void setDefaultButton (Button button, boolean save) {
if (button == null) {
if (defaultButton == saveDefault) {
if (save) saveDefault = null;
return;
}
} else {
if (button.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
if ((button.style & SWT.PUSH) == 0) return;
if (button == defaultButton) return;
}
if (defaultButton != null) {
if (!defaultButton.isDisposed ()) defaultButton.setDefault (false);
}
if ((defaultButton = button) == null) defaultButton = saveDefault;
if (defaultButton != null) {
if (!defaultButton.isDisposed ()) defaultButton.setDefault (true);
}
if (save) saveDefault = defaultButton;
if (saveDefault != null && saveDefault.isDisposed ()) saveDefault = null;
}
/**
* Sets the receiver's image to the argument, which may
* be null. The image is typically displayed by the window
* manager when the instance is marked as iconified, and
* may also be displayed somewhere in the trim when the
* instance is in normal or maximized states.
*
* @param image the new image (or null)
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</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 setImage (Image image) {
checkWidget();
if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
this.image = image;
_setImages (image != null ? new Image [] {image} : null);
}
public void setImages (Image [] images) {
checkWidget ();
if (images == null) error (SWT.ERROR_INVALID_ARGUMENT);
for (int i = 0; i < images.length; i++) {
if (images [i] == null || images [i].isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
}
this.images = images;
_setImages (images);
}
/**
* Sets the maximized state of the receiver.
* If the argument is <code>true</code> causes the receiver
* to switch to the maximized state, and if the argument is
* <code>false</code> and the receiver was previously maximized,
* causes the receiver to switch back to either the minimized
* or normal states.
* <p>
* Note: The result of intermixing calls to<code>setMaximized(true)</code>
* and <code>setMinimized(true)</code> will vary by platform. Typically,
* the behavior will match the platform user's expectations, but not
* always. This should be avoided if possible.
* </p>
*
* @param the new maximized 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>
*
* @see #setMinimized
*/
public void setMaximized (boolean maximized) {
checkWidget();
this.maximized = maximized;
}
/**
* Sets the receiver's menu bar to the argument, which
* may be null.
*
* @param menu the new menu bar
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li>
* <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</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 setMenuBar (Menu menu) {
checkWidget();
if (menuBar == menu) return;
if (menu != null) {
if (menu.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
if ((menu.style & SWT.BAR) == 0) error (SWT.ERROR_MENU_NOT_BAR);
if (menu.parent != this) error (SWT.ERROR_INVALID_PARENT);
}
/* Ensure the new menu bar is correctly enabled */
if (menuBar != null) {
if (!isEnabled () && menuBar.getEnabled ()) {
propagateHandle (true, menuBar.handle, OS.None);
}
menuBar.removeAccelerators ();
}
if (menu != null) {
if (!isEnabled ()) {
propagateHandle (false, menu.handle, OS.None);
}
menu.addAccelerators ();
}
/* Save the old client area */
int [] argList1 = {OS.XmNwidth, 0, OS.XmNheight, 0};
OS.XtGetValues (handle, argList1, argList1.length / 2);
/*
* Bug in Motif. When a XmMainWindowSetAreas () is used
* to replace an existing menu, both menus must be managed
* before the call to XmMainWindowSetAreas () or the new
* menu will not be layed out properly.
*/
int newHandle = (menu != null) ? menu.handle : 0;
int oldHandle = (menuBar != null) ? menuBar.handle : 0;
menuBar = menu;
int hHandle = (horizontalBar != null) ? horizontalBar.handle : 0;
int vHandle = (verticalBar != null) ? verticalBar.handle : 0;
if (newHandle != 0) {
OS.XtSetMappedWhenManaged (newHandle, false);
OS.XtManageChild (newHandle);
}
int clientHandle = formHandle != 0 ? formHandle : handle;
OS.XmMainWindowSetAreas (scrolledHandle, newHandle, 0, hHandle, vHandle, clientHandle);
if (oldHandle != 0) OS.XtUnmanageChild (oldHandle);
if (newHandle != 0) {
OS.XtSetMappedWhenManaged (newHandle, true);
}
/*
* Bug in Motif. When a menu bar is removed after the
* main window has been realized, the main window does
* not layout the new menu bar or the work window.
* The fix is to force a layout by temporarily resizing
* the main window.
*/
if (newHandle == 0 && OS.XtIsRealized (scrolledHandle)) {
int [] argList = {OS.XmNwidth, 0, OS.XmNheight, 0, OS.XmNborderWidth, 0};
OS.XtGetValues (scrolledHandle, argList, argList.length / 2);
OS.XtResizeWidget (scrolledHandle, argList [1] + 1, argList [3], argList [5]);
OS.XtResizeWidget (scrolledHandle, argList [1], argList [3], argList [5]);
}
/*
* Compare the old client area with the new client area.
* If the client area has changed, send a resize event
* and layout.
*/
int [] argList2 = {OS.XmNwidth, 0, OS.XmNheight, 0};
OS.XtGetValues (handle, argList2, argList2.length / 2);
if (argList1 [1] != argList2 [1] || argList1 [3] != argList2 [3]) {
sendEvent (SWT.Resize);
if (layout != null) layout (false);
}
}
/**
* Sets the minimized stated of the receiver.
* If the argument is <code>true</code> causes the receiver
* to switch to the minimized state, and if the argument is
* <code>false</code> and the receiver was previously minimized,
* causes the receiver to switch back to either the maximized
* or normal states.
* <p>
* Note: The result of intermixing calls to<code>setMaximized(true)</code>
* and <code>setMinimized(true)</code> will vary by platform. Typically,
* the behavior will match the platform user's expectations, but not
* always. This should be avoided if possible.
* </p>
*
* @param the new maximized 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>
*
* @see #setMaximized
*/
public void setMinimized (boolean minimized) {
checkWidget();
this.minimized = minimized;
}
void setSavedFocus (Control control) {
if (this == control) return;
savedFocus = control;
}
/**
* Sets the receiver's text, which is the string that the
* window manager will typically display as the receiver's
* <em>title</em>, to the argument, which may not be null.
*
* @param string the new text
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the text 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);
label = string;
}
boolean translateAccelerator (char key, int keysym, XKeyEvent xEvent, boolean doit) {
if (menuBar == null || !menuBar.getEnabled ()) return false;
int accelerator = Display.translateKey (keysym);
if (accelerator == 0) accelerator = key;
if (accelerator == 0) return false;
if ((xEvent.state & OS.Mod1Mask) != 0) accelerator |= SWT.ALT;
if ((xEvent.state & OS.ShiftMask) != 0) accelerator |= SWT.SHIFT;
if ((xEvent.state & OS.ControlMask) != 0) accelerator |= SWT.CONTROL;
return menuBar.translateAccelerator (accelerator, doit);
}
boolean traverseItem (boolean next) {
return false;
}
boolean traverseReturn () {
if (defaultButton == null || defaultButton.isDisposed ()) return false;
if (!defaultButton.isVisible () || !defaultButton.isEnabled ()) return false;
defaultButton.click ();
return true;
}
}