javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/qt/org/eclipse/swt/internal/qt/CommandArranger.java
author hgs
Fri, 15 Oct 2010 12:29:39 +0300
changeset 80 d6dafc5d983f
parent 78 71ad690e91f5
permissions -rw-r--r--
v2.2.19_1

/*******************************************************************************
 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
 * 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:
 *     Nokia Corporation - initial implementation
 *******************************************************************************/
package org.eclipse.swt.internal.qt;

import org.eclipse.ercp.swt.mobile.Command;
import org.eclipse.ercp.swt.mobile.Internal_MobilePackageSupport;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Decorations;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Internal_PackageSupport;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;

/**
 * Manages the Command presentation cycle. This is a singleton object that gets
 * notified about the events that can have an effect on the way the Commands are
 * represented. It maintains an ordered list of Commands on the current focus
 * context with the help of {@link CommandCollection}. It also notifies the
 * currently active {@link CommandPresentationStrategy} so that the presentation
 * is updated if necessary.
 * 
 * @see Command
 * @see CommandCollection
 * @see CommandPresentationStrategy
 */
public class CommandArranger
{

/**
 * A helper for keeping an ordered list of {@link Command}s in the current
 * focus context. The Commands are ordered according to their proximity to
 * the currently focused control. The ordered list starts with Commands from
 * the currently focused Control if any and ends with the active Shell and
 * includes all the Commands in between. If a Control has more than one
 * Command they are ordered among themselves according to creation order.
 */
public class CommandCollection
{

private Command[] fCommands;

CommandCollection() {
    super();
}

/**
 * Adds the command to the collection. Warning: This does not make
 * duplicate control.
 * 
 * @param command
 */
void addCommand(Command command) {
    if (command == null)
        return;
    if (fCommands == null) {
        fCommands = new Command[1];
        fCommands[0] = command;
        return;
    }
    int size = fCommands.length + 1;
    Command[] newList = new Command[size];
    // find the insertion point so that the order is correct
    int insertPoint = 0;
    Shell activeShell = display.getActiveShell();
    Control ctrl = display.getFocusControl();
    while (ctrl != null && ctrl != activeShell) {
        if (ctrl == Internal_MobilePackageSupport.control(command)) {
            // Adding a command to focused control increment by one.
            // adding Command.internal_getCommands(ctrl).length will
            // just duplicate the count for Commands already in array.
            insertPoint++;
            break;
        }

        insertPoint += Internal_PackageSupport.getCommands(ctrl).length;
        ctrl = ctrl.getParent();
    }
    System.arraycopy(fCommands, 0, newList, 0, insertPoint);
    System.arraycopy(fCommands, insertPoint, newList, insertPoint + 1, fCommands.length
        - insertPoint);
    newList[insertPoint] = command;
    fCommands = newList;
}

/**
 * Adds the list of Commands to the collection.
 * 
 * @param commands
 */
void addCommand(Command[] commands) {
    if (commands == null || commands.length == 0)
        return;
    if (fCommands == null) {
        fCommands = new Command[commands.length];
        System.arraycopy(commands, 0, fCommands, 0, commands.length);
        return;
    }
    int size = commands.length + fCommands.length;
    Command[] newList = new Command[size];
    System.arraycopy(fCommands, 0, newList, 0, fCommands.length);
    System.arraycopy(commands, 0, newList, fCommands.length, commands.length);
    fCommands = newList;
}

/**
 * Removes the command from the list.
 * 
 * @param command
 */
void removeCommand(Command command) {
    if (command == null)
        return;
    if (fCommands == null || fCommands.length == 0)
        return;
    int removeIndex = -1;
    for (int i = 0; i < fCommands.length; i++) {
        if (fCommands[i] == command) {
            removeIndex = i;
        }
    }
    if (removeIndex == -1)
        return;
    Command[] newList = new Command[fCommands.length - 1];
    System.arraycopy(fCommands, 0, newList, 0, removeIndex);
    System.arraycopy(fCommands, removeIndex + 1, newList, removeIndex, (fCommands.length
        - removeIndex - 1));
    fCommands = newList;
}

/**
 * Returns the number of the Commands
 * 
 * @return boolean
 */
int getSize() {
    int size = 0;
    if (fCommands != null)
        size = fCommands.length;
    return size;
}

/**
 * Retrieves the Commands of the types indicated by the commandTypes
 * array. The order of the commandTypes array has no significance.
 * Passing a null parameter or an empty array retrieves all the
 * available Commands.
 * 
 * @param commandTypes
 * @return Command list
 */
Command[] getCommands(int[] commandTypes) {
    if (commandTypes == null || commandTypes.length == 0) {
        return fCommands;
    }
    int size = getSize();
    Command[] filteredCommands = new Command[size];
    int index = 0;
    for (int i = 0; i < fCommands.length; i++) {
        for (int j = 0; j < commandTypes.length; j++) {
            if (Internal_MobilePackageSupport.type(fCommands[i]) == commandTypes[j]) {
                filteredCommands[index] = fCommands[i];
                index++;
                break;
            }
        }
    }
    if (size > (index)) {// Some commands filtered resize the Array
        Command[] shrunk = new Command[index];
        System.arraycopy(filteredCommands, 0, shrunk, 0, index);
        return shrunk;
    }
    return filteredCommands;
}

}// CommandCollection

private CommandCollection currentCommands;
private Command defaultCommand;
private Display display;
Control focusedControl;
Shell lastKnownActiveShell;
private Command[] positiveKeyCommands;
private Command negativeKeyCommand;

public CommandArranger(Display display) {
    super();
    this.display = display;
    currentCommands = new CommandCollection();
}

/**
 * Called when the application changes the QMenuBar. This method does not
 * handle the cases when the QMenuBar may change when the active top-level
 * Shell changes. Since this does not cause a menu bar change on all
 * platforms.
 * 
 * @see org.eclipse.swt.widgets.Decorations#setMenuBar(Menu)
 * 
 **/
public void menuBarChanged(Decorations decorations) {
    if (currentCommands == null || currentCommands.getSize() < 1)
        return;
    // if the changed menu bar is not on the active shell ignore and leave
    // it to focus change.
    if (decorations.getShell() != decorations.getDisplay().getActiveShell())
        return;
    // Call internal_getOwnMenuBar because the menu bar can be set to null
    // in Decorations and
    // we may need to create the internal one.
    int menuBarHandle = decorations.internal_getOwnMenuBar();

    handleMenuBarChanged(menuBarHandle, currentCommands);
}

/**
 * Called when a new Shell becomes active.
 * 
 * @see Control#qt_swt_event_focusWasGained() //TODO
 */
public void shellActivityChanged() {
    if (display == null) {
        return;
    }

    Shell activeShell = display.getActiveShell();

    if (activeShell == lastKnownActiveShell) {
        return;
    }
    lastKnownActiveShell = activeShell;

    cleanPositiveCommands();
    cleanNegativeCommand();

    currentCommands = new CommandCollection();

    if (activeShell != null && Internal_PackageSupport.getCommands(activeShell).length > 0) {
        currentCommands.addCommand(Internal_PackageSupport.getCommands(activeShell));
    }

    // Determine where the commands go
    if (currentCommands.getSize() > 0) {
        Command[] add = currentCommands.getCommands(null);
        updateCommandPositions(add);
        placePositiveCommands();
        placeNegativeCommand();
    }
}

/**
 * Called when a new Command is created
 * 
 * @param command
 */
public void commandAdded(Command command) {
    if (isInFocusContext(Internal_MobilePackageSupport.control(command))) {
        currentCommands.addCommand(command);
        handleCommandListChange(command, null, currentCommands);
    }
}

/**
 * Called when a Command is disposed
 * 
 * @param command
 */
public void commandRemoved(Command command) {
    if (command == defaultCommand)
        defaultCommand = null;
    if (isInFocusContext(Internal_MobilePackageSupport.control(command))) {
        currentCommands.removeCommand(command);
        handleCommandListChange(null, command, currentCommands);
    }
}

/**
 * Called when the Display is getting disposed.
 */
public void dispose() {
    currentCommands = null;
    display = null;
    positiveKeyCommands = null;
    negativeKeyCommand = null;
    defaultCommand = null;
    lastKnownActiveShell = null;
}

/**
 * Called when a Command is set default
 * 
 * @param command
 * @see Command#setDefaultCommand();
 */
public void setDefaultCommand(Command command) {
    defaultCommand = command;
    if (isInFocusContext(Internal_MobilePackageSupport.control(command))) {
        handleDefaultCommandChange(command);
    }
}

/**
 * Returns the default command or null if there is none.
 * 
 * @return
 * @see Command#isDefaultCommand()
 */
public Command getDefaultCommand() {
    return defaultCommand;
}

private boolean isInFocusContext(Control control) {
    Display display = control.getDisplay();
    Shell activeShell = display.getActiveShell();
    return control == activeShell;
}

private void handleCommandListChange(Command added, Command removed, CommandCollection commands) {
    cleanNegativeCommand();
    cleanPositiveCommands();
    updateCommandPositions(commands.getCommands(null));
    placeNegativeCommand();
    placePositiveCommands();
}

private void handleDefaultCommandChange(Command defaultCommand) {
    this.defaultCommand = defaultCommand;
    cleanPositiveCommands();
    placePositiveCommands();
}

private void updateCommandPositions(Command[] commands) {
    positiveKeyCommands = new Command[commands.length];
    int positiveKeyIndex = 0;
    for (int i = 0; i < commands.length; i++) {
        Command cmd = commands[i];
        if (cmd.isDefaultCommand()) {
            defaultCommand = cmd;
            continue;
        }
        if (CommandUtils.isNegativeType(Internal_MobilePackageSupport.type(cmd))) {
            if (negativeKeyCommand == null || negativeKeyCommand.isDisposed()) {
                negativeKeyCommand = cmd;
            }
            else if (negativeKeyCommand.getPriority() <= cmd.getPriority()) {
                positiveKeyCommands[positiveKeyIndex] = negativeKeyCommand;
                positiveKeyIndex++;
                negativeKeyCommand = cmd;
            }
            else {
                positiveKeyCommands[positiveKeyIndex] = cmd;
                positiveKeyIndex++;
            }
            continue;
        }
        positiveKeyCommands[positiveKeyIndex] = cmd;
        positiveKeyIndex++;
    }
    if ((positiveKeyIndex) < positiveKeyCommands.length) {// needs to shrink
        Command[] rightSized = new Command[positiveKeyIndex];
        System.arraycopy(positiveKeyCommands, 0, rightSized, 0, rightSized.length);
        positiveKeyCommands = rightSized;
    }
}

private void cleanPositiveCommands() {
    boolean useBar = false;
    if ((positiveKeyCommands != null && positiveKeyCommands.length > 1)
        || (defaultCommand != null && positiveKeyCommands != null)) {
        useBar = true;
    }
    if (defaultCommand != null && !defaultCommand.isDisposed()
        && !Internal_MobilePackageSupport.control(defaultCommand).isDisposed()) {
        if (useBar) {
            OS.QWidget_removeAction(Internal_MobilePackageSupport.control(defaultCommand)
                .getShell().internal_getOwnMenuBar(), topHandle(defaultCommand));
        }
        else {
            OS.QWidget_removeAction(
                topHandle(Internal_MobilePackageSupport.control(defaultCommand)),
                topHandle(defaultCommand));
        }
    }
    if (positiveKeyCommands != null) {
        for (int i = 0; i < positiveKeyCommands.length; i++) {
            Command cmd = positiveKeyCommands[i];
            if (cmd == null || cmd.isDisposed()
                || Internal_MobilePackageSupport.control(cmd).isDisposed()) {
                continue;
            }
            int handle = 0;
            if (useBar) {
                handle = Internal_MobilePackageSupport.control(cmd).getShell()
                    .internal_getOwnMenuBar();
            }
            else {
                handle = topHandle(Internal_MobilePackageSupport
                    .control(positiveKeyCommands[0]));
            }
            OS.QWidget_removeAction(handle, topHandle(cmd));

        }
    }
}

private void cleanNegativeCommand() {
    if (negativeKeyCommand != null && !negativeKeyCommand.isDisposed()
        && !Internal_MobilePackageSupport.control(negativeKeyCommand).isDisposed()) {
        OS.QWidget_removeAction(
            topHandle(Internal_MobilePackageSupport.control(negativeKeyCommand)),
            topHandle(negativeKeyCommand));
    }
}

private void placeNegativeCommand() {
    if (negativeKeyCommand != null) {
        OS.QWidget_addAction(Internal_PackageSupport.topHandle(Internal_MobilePackageSupport
            .control(negativeKeyCommand)), topHandle(negativeKeyCommand));
    }
}

private void placePositiveCommands() {
    if (defaultCommand != null) {
        int defaultCmdHandle = topHandle(defaultCommand);
        if (positiveKeyCommands != null) {
            OS.QMenuBar_addAction(Internal_MobilePackageSupport.control(defaultCommand)
                .getShell().internal_getOwnMenuBar(), defaultCmdHandle);
        }
        else {
            OS.QWidget_addAction(Internal_PackageSupport
                .topHandle(Internal_MobilePackageSupport.control(defaultCommand)),
                defaultCmdHandle);
        }
    }
    if (positiveKeyCommands != null) {
        if (positiveKeyCommands.length == 1 && defaultCommand == null) {
            OS.QWidget_addAction(Internal_PackageSupport
                .topHandle(Internal_MobilePackageSupport.control(positiveKeyCommands[0])),
                topHandle(positiveKeyCommands[0]));
        }
        else {
            CommandUtils.sort(positiveKeyCommands);
            for (int i = 0; i < positiveKeyCommands.length; i++) {
                OS.QMenuBar_addAction(
                    Internal_MobilePackageSupport.control(positiveKeyCommands[i]).getShell()
                        .internal_getOwnMenuBar(), topHandle(positiveKeyCommands[i]));
            }
        }
    }
}

private void handleMenuBarChanged(int newMenuBar, CommandCollection commands) {
    placePositiveCommands();
}

private static final int topHandle(Widget w) {
    return Internal_PackageSupport.topHandle(w);
}

}