--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/javauis/lcdui_qt/src/javax/microedition/lcdui/Displayable.java Mon May 03 12:27:20 2010 +0300
@@ -0,0 +1,795 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+package javax.microedition.lcdui;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.microedition.lcdui.EventDispatcher.LCDUIEvent;
+
+import org.eclipse.ercp.swt.mobile.MobileShell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.internal.extension.CompositeExtension;
+import org.eclipse.swt.widgets.*;
+import com.nokia.mj.impl.rt.support.ApplicationUtils;
+import com.nokia.mj.impl.rt.support.ApplicationInfo;
+
+/**
+ * Implementation of LCDUI <code>Displayable</code> class.
+ */
+public abstract class Displayable {
+
+ private EswtCommandListener eswtCommandListener = new EswtCommandListener();
+
+ private EswtShellListener eswtShellListener = new EswtShellListener();
+
+ private EswtControlListener eswtControlListener = new EswtControlListener();
+
+ private EswtDisposeListener eswtDisposeListener = new EswtDisposeListener();
+
+ /**
+ * Are the commands enabled or not.
+ */
+ private boolean isEnabledCmds = true;
+
+ /**
+ * Shell is activated/de-activated. Called by shell listener.
+ */
+ private boolean isShellActive = true;
+
+ /**
+ * Visible from OpenLCDUI's point of view. Called by Display.setCurrent().
+ */
+ private boolean isLcduiVisible;
+
+ /**
+ * Owned mobile shell.
+ */
+ private Shell shell;
+
+ /**
+ * Content composite.
+ */
+ private Composite contentComp;
+
+ private Rectangle contentArea;
+
+ private boolean initialized;
+
+ private boolean isShownReturnValue;
+
+ private com.nokia.mj.impl.rt.support.Finalizer finalizer;
+
+ private String title;
+
+ private Vector commands = new Vector();
+
+ private CommandListener iCommandListener;
+
+ private Ticker ticker;
+
+ /**
+ * eSWT Label which is used to display the Ticker. This is stored in
+ * displayable because same Ticker may exists in many displayables but eSWT
+ * Controls are always associated with only one Displayable.
+ */
+ private Label tickerLabel;
+
+ /**
+ * Default Constructor.
+ */
+ Displayable(String title) {
+ this.title = title;
+ finalizer = ((finalizer != null) ? finalizer
+ : new com.nokia.mj.impl.rt.support.Finalizer() {
+ public void finalizeImpl() {
+ if (finalizer != null) {
+ finalizer = null;
+ if (!ESWTUIThreadRunner.isDisposed()) {
+ dispose();
+ }
+ }
+ }
+ });
+ ESWTUIThreadRunner.update(getClass().getName(), 1);
+ }
+
+ /**
+ * Performs eSWT construction of shell and content composite. <br>
+ * Should be called from child level constructors.
+ */
+ final void construct() {
+ ESWTUIThreadRunner.safeSyncExec(new Runnable() {
+ public void run() {
+ shell = eswtConstructShell(SWT.SYSTEM_MODAL);
+ eswtSetTitle();
+ contentComp = eswtConstructContent(SWT.NONE);
+ contentArea = eswtLayoutShellContent();
+ }
+ });
+ }
+
+ /**
+ * Dispose Displayable.
+ */
+ void dispose() {
+ if (ticker != null) {
+ ticker.removeLabel(tickerLabel);
+ }
+ ESWTUIThreadRunner.update(getClass().getName(), -1);
+ ESWTUIThreadRunner.safeSyncExec(new Runnable() {
+ public void run() {
+ if (shell != null) {
+ shell.dispose();
+ }
+ }
+ });
+ }
+
+ /**
+ * Constructs default eSWT Shell.<br>
+ * Default SWT shell style is SWT.SYSTEM_MODAL
+ *
+ * @param style eSWT style
+ *
+ * @return eSWT shell
+ */
+ Shell eswtConstructShell(int style) {
+ return new MobileShell(ESWTUIThreadRunner.getInstance().getDisplay(), style);
+ }
+
+ /**
+ * Creates content Composite. this Composite is placed inside of
+ * shell and contains the actual displayable's content (excluding ticker).
+ *
+ * Child classes may override this is if for example scrollbar is needed.
+ *
+ * @return Composite where the content is placed.
+ */
+ Composite eswtConstructContent(int style) {
+ Composite comp = new CompositeExtension(shell, style);
+ return comp;
+ }
+
+ /**
+ * Called by Display when Displayable should become visible.
+ */
+ void eswtHandleShowCurrentEvent() {
+ if (!shell.isDisposed()) {
+ eswtUpdateSizes();
+ shell.addShellListener(eswtShellListener);
+ shell.addDisposeListener(eswtDisposeListener);
+ shell.addControlListener(eswtControlListener);
+ eswtAddSelectionListenerForCommands();
+ // calling open() causes a resize event to be sent
+ shell.open();
+ isLcduiVisible = true;
+ // shell.setVisible(true);
+ // TODO: needed because of eSWT focus bug
+ /*if (!isDialogShell()) {
+ Shell[] shells = shell.getDisplay().getShells();
+ for (int i = 0; i < shells.length; i++) {
+ if (shells[i] != shell && shells[i].isVisible()) {
+ shells[i].setVisible(false);
+ }
+ }
+ }*/
+ }
+ }
+
+ /**
+ * Called by Display when Displayable should become hidden.
+ */
+ void eswtHandleHideCurrentEvent() {
+ isLcduiVisible = false;
+ if (!shell.isDisposed()) {
+ shell.removeShellListener(eswtShellListener);
+ shell.removeDisposeListener(eswtDisposeListener);
+ shell.removeControlListener(eswtControlListener);
+ eswtRemoveSelectionListenerForCommands();
+ }
+ }
+
+ /**
+ * Handle event.
+ *
+ * @param e eSWT event
+ */
+ void eswtHandleEvent(Event e) {
+ // implementation in child classes
+ // Logger.method(this, "eswtHandleEvent", e);
+ }
+
+ /**
+ * Called by ShellListener when shell gets activated.
+ */
+ void handleShellActivatedEvent() {
+ // Implementation in child-classes
+ // Logger.method(this, "handleShellActivatedEvent");
+ if (ESWTUIThreadRunner.getInstance().getDisplay().getActiveShell()
+ != null) {
+ if ( JadAttributeUtil.isValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_BACKGROUND_EVENT,
+ JadAttributeUtil.VALUE_PAUSE)) {
+ ApplicationUtils.getInstance().resumeApplication();
+ }
+ isShellActive = true;
+ }
+ }
+
+ /**
+ * Called by ShellListener when shell gets de-activated.
+ */
+ void handleShellDeActivatedEvent() {
+ // Implementation in child-classes
+ // Logger.method(this, "handleShellDeActivatedEvent");
+ if(isShellActive) {
+ if (ESWTUIThreadRunner.getInstance().getDisplay().getActiveShell()
+ == null) {
+ if ( JadAttributeUtil.isValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_BACKGROUND_EVENT,
+ JadAttributeUtil.VALUE_PAUSE)) {
+ ApplicationUtils.getInstance().pauseApplication();
+ isShellActive = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * This is called if resolution or orientation was changed. This should be
+ * overwritten by sub-classes to get notifications about size changes.
+ *
+ * @param width new width of Displayable.
+ * @param height new height of Displayable.
+ */
+ void eswtHandleResizeEvent(int width, int height) {
+ Logger.method(this, "eswtHandleResizeEvent",
+ String.valueOf(width), String.valueOf(height));
+ LCDUIEvent event = EventDispatcher.instance().newEvent(LCDUIEvent.DISPLAYABLE_SIZECHANGED, this);
+ event.width = width;
+ event.height = height;
+ EventDispatcher.instance().postEvent(event);
+ }
+
+ /**
+ * Returns if the shell is Dialog styled.
+ */
+ private boolean isDialogShell() {
+ return (shell.getStyle() & SWT.DIALOG_TRIM) == SWT.DIALOG_TRIM;
+ }
+
+ /**
+ * Set content size.
+ *
+ * @param aWidth required width or -1 to ignore
+ * @param aHeight required height or -1 to ignore
+ */
+ void eswtSetPreferredContentSize(int aWidth, int aHeight) {
+ if (isDialogShell()) {
+ // aHeight += Config.DISPLAYABLE_DIALOGSHELL_HEIGHT_DISPLACEMENT;
+
+ Logger.method(this, "eswtSetPreferredContentSize",
+ String.valueOf(aWidth), String.valueOf(aHeight));
+
+ Rectangle contentBounds = contentComp.getBounds();
+ int newWidth = (aWidth > 0 ? aWidth : contentBounds.width);
+ int newHeight = (aHeight > 0 ? aHeight : contentBounds.height);
+
+ if (tickerLabel != null) {
+ newHeight += tickerLabel.getBounds().height;
+ }
+
+ Rectangle shellBounds = shell.getBounds();
+ // compute the trimmed shell size
+ Rectangle newSize = shell.computeTrim(0, 0, newWidth, newHeight);
+ // set the new size
+ shell.setSize(newSize.width, newSize.height);
+ // set the location - attached to the bottom growing upwards
+ shell.setLocation(shellBounds.x, (shellBounds.y + shellBounds.height) - newSize.height);
+ }
+ }
+
+ Rectangle eswtLayoutShellContent() {
+ Rectangle shellArea = shell.getClientArea();
+ if (tickerLabel != null) {
+ int tickerHeight = tickerLabel.getBounds().height;
+ contentComp.setBounds(0, tickerHeight,
+ shellArea.width, shellArea.height - tickerHeight);
+ }
+ else {
+ contentComp.setBounds(0, 0, shellArea.width, shellArea.height);
+ }
+ return contentComp.getClientArea();
+ }
+
+ /**
+ * Update internal size of Displayable.
+ */
+ void eswtUpdateSizes() {
+ Rectangle newArea = eswtLayoutShellContent();
+ // if the content size has changed or its not initialized
+ if (!initialized
+ || newArea.width != contentArea.width
+ || newArea.height != contentArea.height) {
+ contentArea = newArea;
+ initialized = true;
+ if (ticker != null) {
+ ticker.updateSpeed();
+ }
+ eswtHandleResizeEvent(contentArea.width, contentArea.height);
+ }
+ }
+
+ /**
+ * Tells is this Displayable visible.
+ *
+ * @return true if this Displayable is currently visible.
+ */
+ public synchronized boolean isShown() {
+ ESWTUIThreadRunner.syncExec(new Runnable() {
+ public void run() {
+ isShownReturnValue = eswtIsShown();
+ }
+ });
+ return isShownReturnValue;
+ }
+
+ /**
+ * eSWT call-back that verifies that the Displayable is shown.
+ */
+ boolean eswtIsShown() {
+ if (!isLcduiVisible || !isShellActive) {
+ // shell.isVisible() doesn't return false if MIDlet
+ // is in background. That's why isVisible-variable is
+ // used instead.
+ return false;
+ }
+ if (shell.isDisposed() || !shell.isEnabled()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Validates a Command.
+ *
+ * @param command a Command
+ */
+ void validateCommand(Command command) {
+ if (command == null) {
+ throw new NullPointerException(
+ MsgRepository.DISPLAYABLE_EXCEPTION_NULL_PARAMETER);
+ }
+ }
+
+ /**
+ * Set commands visibility.
+ *
+ * @param enableCmds visibility switch
+ */
+ void setCommandsVisibility(boolean enableCmds) {
+ if (enableCmds != isEnabledCmds) {
+ isEnabledCmds = enableCmds;
+ ESWTUIThreadRunner.syncExec(new Runnable() {
+ public void run() {
+ int numCmd = getNumCommands();
+ for (int i = 0; i < numCmd; i++) {
+ if (isEnabledCmds) {
+ eswtAddCommand(getCommand(i));
+ }
+ else {
+ eswtRemoveCommand(getCommand(i));
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Adds a command to this Displayable.
+ *
+ * @param command The Command to be added. If the Command already is added
+ * to this Displayable, nothing happens.
+ * @throws NullPointerException If parameter is null.
+ */
+ public void addCommand(Command command) {
+ validateCommand(command);
+ if (!commands.contains(command)) {
+ commands.addElement(command);
+ // Command is not yet added to this Displayable.
+ if (isEnabledCmds) {
+ final Command finalCommand = command;
+ ESWTUIThreadRunner.syncExec(new Runnable() {
+ public void run() {
+ eswtAddCommand(finalCommand);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * eSWT callback to add a Command.
+ */
+ void eswtAddCommand(Command cmd) {
+ cmd.eswtAddESWTCommand(shell, false);
+ if (eswtIsShown()) {
+ cmd.eswtAddCommandSelectionListener(shell, eswtCommandListener);
+ }
+ }
+
+ /**
+ * Removes command from the Displayable.
+ *
+ * @param command Command to be removed. If parameter is null or Command
+ * isn't added to Displayable, nothing happens.
+ */
+ public void removeCommand(Command command) {
+ if (command != null && commands.contains(command)) {
+ // Remove command from iCommands-vector
+ commands.removeElement(command);
+ if (isEnabledCmds) {
+ final Command finalCommand = command;
+ ESWTUIThreadRunner.syncExec(new Runnable() {
+ public void run() {
+ eswtRemoveCommand(finalCommand);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * eSWT callback to remove a Command.
+ */
+ void eswtRemoveCommand(Command cmd) {
+ if (eswtIsShown()) {
+ cmd.eswtRemoveCommandSelectionListener(shell, eswtCommandListener);
+ }
+ cmd.eswtRemoveESWTCommand(shell);
+ }
+
+ /**
+ * Sets CommandListener. If CommandListener already exists, it is replaced
+ * with the new one.
+ *
+ * @param commandListener New CommandListener. If null, existing
+ * CommandListener is removed. If null and no CommandListener
+ * exists, nothing happens.
+ */
+ public void setCommandListener(CommandListener commandListener) {
+ this.iCommandListener = commandListener;
+ }
+
+ public boolean hasCommandListener() {
+ if(this.iCommandListener != null) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Add command listener for all Commands added to this Displayable.
+ */
+ void eswtAddSelectionListenerForCommands() {
+ Command cmd = null;
+ for (Enumeration e = commands.elements(); e.hasMoreElements();) {
+ cmd = (Command) e.nextElement();
+ cmd.eswtAddCommandSelectionListener(shell, eswtCommandListener);
+ }
+ }
+
+ /**
+ * Remove command listener from Commands added to this Displayable.
+ */
+ void eswtRemoveSelectionListenerForCommands() {
+ Command cmd = null;
+ for (Enumeration e = commands.elements(); e.hasMoreElements();) {
+ cmd = (Command) e.nextElement();
+ cmd.eswtRemoveCommandSelectionListener(shell, eswtCommandListener);
+ }
+ }
+
+ /**
+ * Calls the command action on the current command listener.
+ *
+ * @param command the Command
+ */
+ final void callCommandAction(Command command) {
+ if (iCommandListener != null && command != null) {
+ LCDUIEvent event = EventDispatcher.instance().newEvent(LCDUIEvent.DISPLAYABLE_COMMANDACTION, this);
+ event.command = command;
+ event.commandListener = iCommandListener;
+ EventDispatcher.instance().postEvent(event);
+ }
+ }
+
+ /**
+ * Gets the number of commands.
+ *
+ * @return the number of commands in this Displayable
+ */
+ final int getNumCommands() {
+ return commands.size();
+ }
+
+ /**
+ * Gets a command in the command array.
+ *
+ * @param index index of command
+ * @return the command
+ */
+ final Command getCommand(int index) {
+ return (Command) commands.elementAt(index);
+ }
+
+ /**
+ * Gets width.
+ *
+ * @return Width of the Displayable in pixels.
+ */
+ public int getWidth() {
+ return contentArea.width;
+ }
+
+ /**
+ * Gets height.
+ *
+ * @return Height of the Displayable in pixels.
+ */
+ public int getHeight() {
+ return contentArea.height;
+ }
+
+ /**
+ * Sets ticker. If ticker is added already to other displayable(s),
+ * it continues running from position where it was. Otherwise
+ * it will start running from beginning when this method returns.
+ *
+ * @param newTicker New ticker. If null, current ticker is removed.
+ */
+ public void setTicker(Ticker newTicker) {
+ if (ticker == newTicker) {
+ return;
+ }
+ if (ticker != null) {
+ // Ticker exists, removing it:
+ ticker.removeLabel(getTickerLabel());
+ }
+ ticker = newTicker;
+ if (ticker != null) {
+ ticker.addLabel(getTickerLabel());
+ }
+
+ final Ticker finalTicker = ticker;
+ ESWTUIThreadRunner.syncExec(new Runnable() {
+ public void run() {
+ if (finalTicker != null) {
+ // Setting ticker:
+ tickerLabel.setText(finalTicker.getFormattedString());
+ tickerLabel.pack();
+ // Avoid ticker flashing by setting it out of the
+ // screen first:
+ tickerLabel.setBounds(Integer.MIN_VALUE, 0,
+ tickerLabel.getBounds().width,
+ tickerLabel.getBounds().height);
+ }
+ else {
+ // Removing ticker:
+ tickerLabel.setText("");
+ tickerLabel.setBounds(Integer.MIN_VALUE, 0, 0, 0);
+ }
+ eswtUpdateSizes();
+ }
+ });
+ if (ticker != null) {
+ // Start to scroll the ticker. Ticker may be already running
+ // if it exists in some other displayable already, but
+ // calling this again wont do any harm:
+ ticker.start();
+ }
+ }
+
+ /**
+ * Gets current ticker.
+ *
+ * @return Current ticker or null if no ticker set.
+ */
+ public Ticker getTicker() {
+ return ticker;
+ }
+
+ /**
+ * Gets the current title.
+ *
+ * @return The title of the Displayable, or null if no title set.
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Sets the title of this Displayable.
+ *
+ * @param newTitle new title or null for no title.
+ */
+ public void setTitle(String newTitle) {
+ this.title = newTitle;
+ ESWTUIThreadRunner.syncExec(new Runnable() {
+ public void run() {
+ eswtSetTitle();
+ }
+ });
+ }
+
+ /**
+ * Sets shell's title. Nulls are not allowed.
+ */
+ void eswtSetTitle() {
+ // eSWT Shell doesn't take null value as title
+ shell.setText((title != null ? title : ""));
+ }
+
+ /**
+ * Creates singleton Label instance used by Ticker.
+ */
+ private Label getTickerLabel() {
+ if (tickerLabel == null) {
+ ESWTUIThreadRunner.syncExec(new Runnable() {
+ public void run() {
+ tickerLabel = new Label(shell,
+ SWT.SHADOW_NONE | SWT.HORIZONTAL | SWT.CENTER);
+ }
+ });
+ }
+ return tickerLabel;
+ }
+
+ /**
+ * Called by underlying system when the size of the Displayable changes.
+ * This might be overwritten by user-side classes.
+ *
+ * @param width new width of Displayable.
+ * @param height new height of Displayable.
+ */
+ protected void sizeChanged(int width, int height) {
+ }
+
+ /**
+ * eSWT callback to get the Shell of the Displayable.
+ */
+ final Shell getShell() {
+ return this.shell;
+ }
+
+ /**
+ * Gets composite that contains displayable's content.
+ *
+ * @return Composite.
+ */
+ Composite getContentComp() {
+ return contentComp;
+ }
+
+ /*
+ * The client area. It's ensured that after the construction this is always
+ * set.
+ */
+ final Rectangle getContentArea() {
+ return contentArea;
+ }
+
+ /*
+ * Dispatcher thread calls.
+ */
+ void doCallback(LCDUIEvent event) {
+ switch(event.type) {
+ case LCDUIEvent.DISPLAYABLE_SIZECHANGED:
+ sizeChanged(event.width, event.height);
+ break;
+ case LCDUIEvent.DISPLAYABLE_COMMANDACTION:
+ event.commandListener.commandAction(event.command, this);
+ break;
+ }
+ }
+
+ /**
+ * Inner class which receives SelectionEvents from eSWT and convert and
+ * forwards those events to LCDUI's CommandListener.
+ */
+ class EswtCommandListener implements SelectionListener {
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ /**
+ * Executed by eSWT when event occurs. This method will then call
+ * Displayable's CommandListener if event source matches with the
+ * Commands added to the Displayable.
+ */
+ public void widgetSelected(SelectionEvent event) {
+ // Go through all Commands added to this Displayable:
+ for (Enumeration e = commands.elements(); e.hasMoreElements();) {
+ Command cmd = (Command) e.nextElement();
+ // Select eSWT Command from Command which is connected to
+ // this Displayable and compare it to the widget which
+ // launched the event:
+ if (cmd.getESWTCommand(shell) == event.widget) {
+ callCommandAction(cmd);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Every Displayable must listen shell events to be able to tell is the
+ * MIDlet sent to background.
+ */
+ class EswtShellListener implements ShellListener {
+
+ public void shellActivated(ShellEvent e) {
+ if (!isShellActive) {
+ handleShellActivatedEvent();
+ }
+ }
+
+ public void shellDeactivated(ShellEvent e) {
+ ESWTUIThreadRunner.getInstance().getDisplay()
+ .asyncExec(new Runnable() {
+ public void run() {
+ handleShellDeActivatedEvent();
+ }
+ });
+
+ }
+
+ public void shellClosed(ShellEvent e) {
+ }
+
+ public void shellIconified(ShellEvent e) {
+ }
+
+ public void shellDeiconified(ShellEvent e) {
+ }
+
+ }
+
+ class EswtDisposeListener implements DisposeListener {
+
+ public void widgetDisposed(DisposeEvent e) {
+ isShellActive = false;
+ }
+ }
+
+ /**
+ * Displayable must listen resize-events to be able to call
+ * sizeChanged()-method at the right time.
+ */
+ class EswtControlListener implements ControlListener {
+ public void controlResized(ControlEvent e) {
+ eswtUpdateSizes();
+ }
+
+ public void controlMoved(ControlEvent e) {
+ }
+ }
+
+}