com.arc.cdt.debug.seecode.ui.internal.display.DeferredCombo.java Source code

Java tutorial

Introduction

Here is the source code for com.arc.cdt.debug.seecode.ui.internal.display.DeferredCombo.java

Source

/*******************************************************************************
 * Copyright (c) 2005-2012 Synopsys, Incorporated
 * 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:
 * Synopsys, Inc - Initial implementation 
 *******************************************************************************/
package com.arc.cdt.debug.seecode.ui.internal.display;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.ControlContribution;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;

import com.arc.cdt.debug.seecode.ui.UISeeCodePlugin;
import com.arc.widgets.IChoice;
import com.arc.widgets.ILabel;

/**
 * Create a combo that can be instantiated later when the view toolbar is being
 * rendered.
 * @author davidp
 * @currentOwner <a href="mailto:davidp@arc.com">davidp</a>
 * @version $Revision$
 * @lastModified $Date$
 * @lastModifiedBy $Author$
 * @reviewed 0 $Revision:1$
 */
class DeferredCombo extends DeferredComponent implements IChoice, IToolBarItem {
    private List<IObserver> mObservers = new ArrayList<IObserver>();
    private List<ITextObserver> mTextObservers = null;
    private List<String> mItems = new ArrayList<String>();
    private int mSelectionIndex = -1;
    private ArrayList<ActionListener> mActionListeners;
    private int mColumns = 0;
    private boolean mEditable;
    private static int sItem = 0;
    private String mID;

    private CCombo mCombo = null; // set when added to View's toolbar
    private ILabel mLabel;
    private boolean mContentsChanged = false;
    private IContributionItem mItem = null;
    private IToolBarManager mToolBarManager;
    // To avoid recursive selection event when "setSelection()" is called from
    // selection event handler.
    private boolean _suppressSelectionEvent = false;

    /**
     * 
     * @param label label that prefixes this combo, or <code>null</code>; used to
     * enable/disable.
     * @param editable
     */
    DeferredCombo(ILabel label, boolean editable) {
        mLabel = label;
        mEditable = editable;
        mID = "combo" + sItem++;
        if (editable)
            mColumns = 10;
    }

    @Override
    public void addObserver(IObserver o) {
        mObservers.add(o);
    }

    @Override
    public void removeObserver(IObserver o) {
        mObservers.remove(o);

    }

    @Override
    public void addTextObserver(ITextObserver o) {
        if (mTextObservers == null) {
            mTextObservers = new ArrayList<ITextObserver>();
        }
        mTextObservers.add(o);
    }

    @Override
    public void removeTextObserver(ITextObserver o) {
        if (mTextObservers != null)
            mTextObservers.remove(o);
    }

    @Override
    public void setItems(String[] items) {
        mItems.clear();
        mItems.addAll(Arrays.asList(items));
        if (mCombo != null) {
            mCombo.setItems(items);
            if (items.length > 0)
                setSelectionIndex(0);
            mContentsChanged = true;
        }
    }

    @Override
    public int addItem(String text) {
        mItems.add(text);
        if (mCombo != null) {
            mCombo.add(text);
            mCombo.getParent().layout(true);
            setSelectionIndex(mItems.size() - 1);
            mContentsChanged = true; // make contribution item dynamic
        }
        return mItems.size() - 1;
    }

    @Override
    public String getText() {
        if (mCombo != null) {
            return mCombo.getText();
        }
        return mSelectionIndex >= 0 && mSelectionIndex < mItems.size() ? mItems.get(mSelectionIndex) : null;
    }

    @Override
    public void clear() {
        if (mCombo != null) {
            mCombo.removeAll();
        }
        mItems.clear();

    }

    @Override
    public int getSelectionIndex() {
        if (mCombo != null) {
            return mCombo.getSelectionIndex();
        }
        return mSelectionIndex;
    }

    @Override
    public void setSelection(String text) {

        if (mCombo != null) {
            String items[] = mCombo.getItems();
            boolean selected = false;
            for (int i = 0; i < items.length; i++) {
                if (items[i].equals(text)) {
                    setSelectionIndex(i);
                    selected = true;
                    break;
                }
            }
            if (!selected) {
                addItem(text);
            }
        } else {
            int prev = mSelectionIndex;
            mSelectionIndex = mItems.indexOf(text);
            if (mSelectionIndex < 0) {
                mItems.add(text);
                mSelectionIndex = mItems.size() - 1;
            }
            if (prev != mSelectionIndex)
                notifyObservers();
        }

    }

    @Override
    public int getItemCount() {
        if (mCombo != null) {
            return mCombo.getItemCount();
        }
        return mItems.size();
    }

    @Override
    public void removeAllItems() {
        if (mCombo != null) {
            mCombo.removeAll();
        }
        mItems.clear();

    }

    @Override
    public Object getItemAt(int i) {
        if (mCombo != null) {
            return mCombo.getItem(i);
        }
        return mItems.get(i);
    }

    @Override
    public void removeItemAt(int i) {
        if (mCombo != null) {
            mCombo.remove(i);
        }
        mItems.remove(i);
    }

    @Override
    public void setSelectionIndex(int i) {
        if (getSelectionIndex() != i) {
            mSelectionIndex = i;
            if (mCombo != null) {
                mCombo.select(i);
            }
            notifyObservers();
        }
    }

    @Override
    public void addActionListener(ActionListener listener) {
        if (mActionListeners == null) {
            mActionListeners = new ArrayList<ActionListener>();
        }
        mActionListeners.add(listener);

    }

    @Override
    public void removeActionListener(ActionListener listener) {
        if (mActionListeners != null) {
            mActionListeners.remove(listener);
        }

    }

    @Override
    public void setEnabled(boolean v) {
        super.setEnabled(v);
        if (mCombo != null) {
            mCombo.setEnabled(v);
        }
        if (mLabel != null) {
            mLabel.setEnabled(v);
        }
    }

    @Override
    public void setColumns(int col) {
        mColumns = col;
    }

    /**
     * {@inheritDoc} Implementation of overridden (or abstract) method.
     * @param tip
     */
    @Override
    public void setToolTipText(String tip) {
        super.setToolTipText(tip);
        if (mCombo != null) {
            mCombo.setToolTipText(tip);
        }
    }

    private void notifyObservers() {
        if (mObservers != null && !_suppressSelectionEvent) {
            for (IObserver o : mObservers) {
                o.selectionChanged(this);
            }
        }
    }

    private void notifyTextObservers() {
        if (mTextObservers != null) {
            String text = mCombo.getText();
            for (ITextObserver o : mTextObservers) {
                o.textChanged(this, text);
            }
        }
    }

    @Override
    public void revalidate() {
        if (mCombo != null) {

            // Bug in Windows version as of Eclipse 3.1M5: the toolbar
            // isn't redrawn!
            // We must remove all items and restore them to fix this!
            if (isWindows()) {
                IContributionItem items[] = mToolBarManager.getItems();
                mToolBarManager.removeAll();
                mToolBarManager.update(true);
                for (IContributionItem item : items) {
                    mToolBarManager.add(item);
                }
            }
            mToolBarManager.update(true);
        }
    }

    static boolean isWindows() {
        return Platform.getOS().equals("win32");
    }

    @Override
    public void addToToolBar(IToolBarManager manager) {
        mContentsChanged = false;
        mToolBarManager = manager;
        mItem = new MyContributionItem(getName() != null ? getName() : mID);
        manager.add(mItem);
    }

    class MyContributionItem extends ControlContribution {

        protected MyContributionItem(String id) {
            super(id);
        }

        @Override
        protected Control createControl(Composite parent) {
            //NOTE: we must use "CCombo" instead of "Combo" because
            // the latter doesn't render correctly under Windows
            // when the toolbar wraps.
            //NOTE2: we must wrap the control in a composite so that we
            // can control the layout. Otherwise, Combos end up very narrow if
            // they are empty.
            Composite comboContainer = new Composite(parent, SWT.NONE);
            comboContainer.setLayout(new MyComboLayout());
            final CCombo c = new CCombo(comboContainer,
                    SWT.DROP_DOWN | SWT.BORDER | (mEditable ? 0 : SWT.READ_ONLY));
            DeferredCombo.this.mCombo = c;
            if (getName() != null) {
                c.setData("name", getName()); // for GUI tester.
            }
            //Use fixed-width font it combobox is editable because it
            // typically corresponds to program text.
            // Use the SeeCode display font. Also, listener for a font change
            // and respond accordingly.
            if (mEditable) {
                c.setFont(UISeeCodePlugin.getSeeCodeFont());
                IPropertyChangeListener fontListener = new IPropertyChangeListener() {

                    @Override
                    public void propertyChange(PropertyChangeEvent event) {
                        if (event.getProperty().equals(UISeeCodePlugin.SEECODE_FONT)) {
                            c.setFont(UISeeCodePlugin.getSeeCodeFont());
                        }

                    }

                };
                JFaceResources.getFontRegistry().addListener(fontListener);
            }

            c.addDisposeListener(new DisposeListener() {

                @Override
                public void widgetDisposed(DisposeEvent e) {
                    DeferredCombo.this.mCombo = null;

                }
            });
            c.setEnabled(isEnabled());

            for (String item : mItems) {
                c.add(item);
            }
            if (mSelectionIndex < 0 && !mEditable)
                mSelectionIndex = 0;
            if (mSelectionIndex >= 0) {
                c.select(mSelectionIndex);
            }
            if (getToolTipText() != null) {
                c.setToolTipText(getToolTipText());
            }

            c.addSelectionListener(new SelectionListener() {

                @Override
                public void widgetSelected(SelectionEvent e) {
                    if (!_suppressSelectionEvent) {
                        mSelectionIndex = c.getSelectionIndex();
                        notifyObservers();
                    }
                }

                @Override
                public void widgetDefaultSelected(SelectionEvent e) {
                    // "setSelection()" causes a recursive selection event,
                    // so set flag to prevent it.
                    if (!_suppressSelectionEvent) {
                        try {
                            widgetSelected(e);
                            _suppressSelectionEvent = true;
                            setSelection(mCombo.getText());
                            if (mActionListeners != null) {
                                ActionEvent ae = new ActionEvent(c, ActionEvent.ACTION_PERFORMED, "");
                                for (ActionListener a : mActionListeners) {
                                    a.actionPerformed(ae);
                                }
                            }
                        } finally {
                            _suppressSelectionEvent = false;
                        }
                    }

                }
            });

            c.addModifyListener(new ModifyListener() {

                @Override
                public void modifyText(ModifyEvent e) {
                    notifyTextObservers();
                }
            });
            //When writable combo loses focus, the whatever is typed is added
            //to the item list.
            if (mEditable)
                c.addFocusListener(new FocusListener() {
                    private Button saveDefaultButton;

                    @Override
                    public void focusGained(FocusEvent event) {
                        // We want to process return key; don't dismiss shell!
                        saveDefaultButton = mCombo.getShell().getDefaultButton();
                        mCombo.getShell().setDefaultButton(null);
                    }

                    @Override
                    public void focusLost(FocusEvent event) {
                        mCombo.getShell().setDefaultButton(saveDefaultButton);
                        setSelection(mCombo.getText());
                    }
                });
            return comboContainer;
        }

        @Override
        public boolean isDynamic() {
            return mContentsChanged;
        }

        @Override
        public void dispose() {
            if (mCombo != null) {
                mCombo.dispose();
            }
        }

        @Override
        protected int computeWidth(Control control) {
            return control.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x;
        }
    }

    @Override
    protected Composite getParentComposite() {
        if (mCombo != null) {
            return mCombo.getParent();
        }
        return null;
    }

    @Override
    public boolean isEditable() {
        return mEditable;
    }

    /**
     * A layout that wraps a combobox that is constrained to a particular
     * width.
     * @author davidp
     * @currentOwner <a href="mailto:davidp@arc.com">davidp</a>
     * @version $Revision$
     * @lastModified $Date$
     * @lastModifiedBy $Author$
     * @reviewed 0 $Revision:1$
     */
    class MyComboLayout extends Layout {

        MyComboLayout() {
        }

        @Override
        protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
            for (Control kid : composite.getChildren()) {
                Point size = kid.computeSize(wHint, hHint, flushCache);
                if (mColumns > 0) {
                    GC gc = new GC(kid);
                    FontMetrics fm = gc.getFontMetrics();
                    // We assume the downarrow on the right is a square: its width is the same
                    // as its height. Also assume a left border of 2 pixels.
                    // Often the "average" char width seems too conservatibe. Add 1.
                    size.x = (mColumns + 1) * fm.getAverageCharWidth() + size.y + 2;
                    gc.dispose();
                }
                return size;
            }
            return new Point(0, 0); // shouldn't get here
        }

        @Override
        protected void layout(Composite composite, boolean flushCache) {
            Point size = composite.getSize();
            for (Control kid : composite.getChildren()) {
                kid.setBounds(0, 0, size.x, size.y);
            }
        }
    }
}