javauis/lcdui_qt/src/javax/microedition/lcdui/Displayable.java
changeset 21 2a9601315dfc
child 23 98ccebc37403
--- /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) {
+        }
+    }
+
+}