com.google.gdt.eclipse.designer.mobile.preferences.device.DevicesPreferencePage.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gdt.eclipse.designer.mobile.preferences.device.DevicesPreferencePage.java

Source

/*******************************************************************************
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package com.google.gdt.eclipse.designer.mobile.preferences.device;

import com.google.common.collect.Lists;
import com.google.gdt.eclipse.designer.mobile.Activator;
import com.google.gdt.eclipse.designer.mobile.device.DeviceManager;
import com.google.gdt.eclipse.designer.mobile.device.command.CategoryAddCommand;
import com.google.gdt.eclipse.designer.mobile.device.command.CategoryMoveCommand;
import com.google.gdt.eclipse.designer.mobile.device.command.CategoryNameCommand;
import com.google.gdt.eclipse.designer.mobile.device.command.CategoryRemoveCommand;
import com.google.gdt.eclipse.designer.mobile.device.command.Command;
import com.google.gdt.eclipse.designer.mobile.device.command.DeviceMoveCommand;
import com.google.gdt.eclipse.designer.mobile.device.command.DeviceRemoveCommand;
import com.google.gdt.eclipse.designer.mobile.device.command.ElementVisibilityCommand;
import com.google.gdt.eclipse.designer.mobile.device.model.AbstractDeviceInfo;
import com.google.gdt.eclipse.designer.mobile.device.model.CategoryInfo;
import com.google.gdt.eclipse.designer.mobile.device.model.DeviceInfo;

import org.eclipse.wb.internal.core.utils.GenericsUtils;
import org.eclipse.wb.internal.core.utils.check.Assert;
import org.eclipse.wb.internal.core.utils.ui.EmptyTransfer;
import org.eclipse.wb.internal.core.utils.ui.GridDataFactory;
import org.eclipse.wb.internal.core.utils.ui.GridLayoutFactory;

import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;

import org.apache.commons.lang.ArrayUtils;

import java.util.List;

/**
 * {@link PreferencePage} for configuring {@link DeviceManager}.
 * 
 * @author scheglov_ke
 * @coverage gwt.mobile.device
 */
public final class DevicesPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    public DevicesPreferencePage() {
        setPreferenceStore(Activator.getDefault().getPreferenceStore());
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // IWorkbenchPreferencePage
    //
    ////////////////////////////////////////////////////////////////////////////
    public void init(IWorkbench workbench) {
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // GUI
    //
    ////////////////////////////////////////////////////////////////////////////
    private static final Image IMAGE_CATEGORY = Activator.getImage("device/category.png");
    private static final Image IMAGE_DEVICE = Activator.getImage("device/device.png");
    private CheckboxTreeViewer m_viewer;

    @Override
    protected Control createContents(Composite parent) {
        Composite container = new Composite(parent, SWT.NONE);
        GridLayoutFactory.create(container).columns(3);
        //
        createViewer(container);
        createButtonsComposite(container);
        createPreviewComposite(container);
        //
        return container;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // GUI: viewer
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Creates {@link CheckboxTreeViewer} for categories/devices.
     */
    private void createViewer(Composite parent) {
        m_viewer = new CheckboxTreeViewer(parent, SWT.BORDER | SWT.MULTI);
        GridDataFactory.create(m_viewer.getTree()).grab().fill().hintC(50, 20);
        // content provider
        m_viewer.setContentProvider(new ITreeContentProvider() {
            public Object[] getElements(Object inputElement) {
                return DeviceManager.getCategories().toArray();
            }

            public Object[] getChildren(Object parentElement) {
                if (parentElement instanceof CategoryInfo) {
                    return ((CategoryInfo) parentElement).getDevices().toArray();
                }
                return ArrayUtils.EMPTY_OBJECT_ARRAY;
            }

            public boolean hasChildren(Object element) {
                return getChildren(element).length != 0;
            }

            public Object getParent(Object element) {
                if (element instanceof DeviceInfo) {
                    return ((DeviceInfo) element).getCategory();
                }
                return null;
            }

            public void dispose() {
            }

            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            }
        });
        // label provider
        m_viewer.setLabelProvider(new LabelProvider() {
            @Override
            public Image getImage(Object element) {
                if (element instanceof CategoryInfo) {
                    return IMAGE_CATEGORY;
                }
                return IMAGE_DEVICE;
            }

            @Override
            public String getText(Object element) {
                if (element instanceof CategoryInfo) {
                    CategoryInfo category = (CategoryInfo) element;
                    return category.getName();
                } else if (element instanceof DeviceInfo) {
                    DeviceInfo device = (DeviceInfo) element;
                    return device.getName() + "     -     " + device.getDisplayBounds().width + "x"
                            + device.getDisplayBounds().height;
                }
                return null;
            }
        });
        // set input
        m_viewer.setInput(this);
        refreshViewer();
        refreshViewChecks();
        // listeners
        m_viewer.addCheckStateListener(new ICheckStateListener() {
            public void checkStateChanged(CheckStateChangedEvent event) {
                AbstractDeviceInfo element = (AbstractDeviceInfo) event.getElement();
                commands_add(new ElementVisibilityCommand(element, event.getChecked()));
            }
        });
        m_viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                updateButtons();
                updatePreview();
            }
        });
        m_viewer.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                if (m_editButton.isEnabled()) {
                    onEdit();
                }
            }
        });
        // DND
        configureDND();
    }

    /**
     * Shows actual state of devices in {@link #m_viewer}.
     */
    private void refreshViewer() {
        m_viewer.refresh();
    }

    /**
     * Shows state of "visible" state. Note that this method is slow, in particular
     * {@link CheckboxTreeViewer#setCheckedElements(Object[])}, so should be used carefully.
     */
    private void refreshViewChecks() {
        List<Object> visibleElements = Lists.newArrayList();
        for (CategoryInfo category : DeviceManager.getCategories()) {
            if (category.isVisible()) {
                visibleElements.add(category);
            }
            for (DeviceInfo device : category.getDevices()) {
                if (device.isVisible()) {
                    visibleElements.add(device);
                }
            }
        }
        // update viewer
        m_viewer.setCheckedElements(visibleElements.toArray());
    }

    /**
     * @return the {@link List} of selected elements.
     */
    @SuppressWarnings("unchecked")
    private List<Object> getSelectedElements() {
        IStructuredSelection selection = (IStructuredSelection) m_viewer.getSelection();
        return selection.toList();
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // GUI: buttons
    //
    ////////////////////////////////////////////////////////////////////////////
    private Button m_addEntryButton;
    private Button m_editButton;
    private Button m_removeButton;
    private Button m_moveUpButton;
    private Button m_moveDownButton;

    /**
     * Creates {@link Composite} with buttons to modify devices.
     */
    private void createButtonsComposite(Composite parent) {
        Composite buttonsComposite = new Composite(parent, SWT.NONE);
        GridDataFactory.create(buttonsComposite).grabV().fill();
        GridLayoutFactory.create(buttonsComposite).noMargins();
        //
        createButton(buttonsComposite, "Add Category...", new Listener() {
            public void handleEvent(Event event) {
                onAddCategory();
            }
        });
        m_addEntryButton = createButton(buttonsComposite, "Add device...", new Listener() {
            public void handleEvent(Event event) {
                onAddDevice();
            }
        });
        //
        createButtonSeparator(buttonsComposite);
        m_editButton = createButton(buttonsComposite, "Edit...", new Listener() {
            public void handleEvent(Event event) {
                onEdit();
            }
        });
        m_removeButton = createButton(buttonsComposite, "Remove...", new Listener() {
            public void handleEvent(Event event) {
                onRemove();
            }
        });
        //
        createButtonSeparator(buttonsComposite);
        m_moveUpButton = createButton(buttonsComposite, "Up", new Listener() {
            public void handleEvent(Event event) {
                onMove(-1);
            }
        });
        m_moveDownButton = createButton(buttonsComposite, "Down", new Listener() {
            public void handleEvent(Event event) {
                onMove(+2);
            }
        });
        //
        createButtonSeparator(buttonsComposite);
        createButton(buttonsComposite, "Collapse All", new Listener() {
            public void handleEvent(Event event) {
                m_viewer.collapseAll();
            }
        });
        createButton(buttonsComposite, "Expand All", new Listener() {
            public void handleEvent(Event event) {
                m_viewer.expandAll();
            }
        });
        // update buttons first time
        updateButtons();
    }

    /**
     * Creates {@link Button} with given text and {@link SWT#Selection} listener.
     */
    private static Button createButton(Composite parent, String text, Listener selectionListener) {
        Button button = new Button(parent, SWT.NONE);
        GridDataFactory.create(button).grabH().fillH();
        button.setText(text);
        button.addListener(SWT.Selection, selectionListener);
        return button;
    }

    /**
     * Creates separator between buttons on vertical buttons bar.
     */
    private static void createButtonSeparator(Composite parent) {
        Label separator = new Label(parent, SWT.NONE);
        GridDataFactory.create(separator).hintV(7);
    }

    /**
     * Updates buttons according to selection in {@link #m_viewer}.
     */
    private void updateButtons() {
        List<Object> selection = getSelectedElements();
        m_addEntryButton.setEnabled(!selection.isEmpty());
        m_editButton.setEnabled(selection.size() == 1);
        m_removeButton.setEnabled(!selection.isEmpty());
        // move up/down
        List<CategoryInfo> categories = DeviceManager.getCategories();
        {
            boolean upEnabled = !selection.isEmpty();
            boolean downEnabled = !selection.isEmpty();
            for (Object element : selection) {
                if (element instanceof CategoryInfo) {
                    upEnabled &= categories.indexOf(element) != 0;
                    downEnabled &= categories.indexOf(element) != categories.size() - 1;
                } else if (element instanceof DeviceInfo) {
                    DeviceInfo device = (DeviceInfo) element;
                    List<DeviceInfo> devices = device.getCategory().getDevices();
                    m_editButton.setEnabled(m_editButton.isEnabled() && !device.isContributed());
                    upEnabled &= devices.indexOf(device) != 0;
                    downEnabled &= devices.indexOf(device) != devices.size() - 1;
                }
            }
            m_moveUpButton.setEnabled(upEnabled);
            m_moveDownButton.setEnabled(downEnabled);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // GUI: preview
    //
    ////////////////////////////////////////////////////////////////////////////
    private DevicePreviewCanvas m_previewCanvas;

    /**
     * Creates {@link DevicePreviewCanvas} for preview selected {@link DeviceInfo}.
     */
    private void createPreviewComposite(Composite parent) {
        m_previewCanvas = new DevicePreviewCanvas(parent, SWT.NONE);
        GridDataFactory.create(m_previewCanvas).fill().hintH(150);
    }

    /**
     * Shows selected {@link DeviceInfo} in preview.
     */
    private void updatePreview() {
        List<Object> selection = getSelectedElements();
        if (selection.size() == 1 && selection.get(0) instanceof DeviceInfo) {
            DeviceInfo device = (DeviceInfo) selection.get(0);
            m_previewCanvas.setDevice(device);
        } else {
            m_previewCanvas.setDevice(null);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Operations
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Edits device element.
     */
    private void onEdit() {
        Object element = getSelectedElements().get(0);
        if (element instanceof CategoryInfo) {
            CategoryInfo category = (CategoryInfo) element;
            InputDialog inputDialog = new InputDialog(getShell(), "Category", "Enter new category name:",
                    category.getName(), null);
            // execute dialog
            if (inputDialog.open() == Window.OK) {
                commands_add(new CategoryNameCommand(category, inputDialog.getValue()));
            }
        } else if (element instanceof DeviceInfo) {
            DeviceInfo device = (DeviceInfo) element;
            DeviceEditDialog dialog = new DeviceEditDialog(device);
            // execute dialog
            if (dialog.open() == Window.OK) {
                commands_add(dialog.getCommand());
            }
        }
    }

    /**
     * Removes selected {@link CategoryInfo} or {@link DeviceInfo}.
     */
    private void onRemove() {
        List<Object> selection = getSelectedElements();
        if (MessageDialog.openConfirm(getShell(), "Confirm",
                "Are you sure you want to remove " + selection.size() + " selected element(s)?")) {
            for (Object element : selection) {
                if (element instanceof CategoryInfo) {
                    commands_add(new CategoryRemoveCommand((CategoryInfo) element));
                } else if (element instanceof DeviceInfo) {
                    commands_add(new DeviceRemoveCommand((DeviceInfo) element));
                }
            }
            refreshViewer();
        }
    }

    /**
     * Adds new {@link CategoryInfo}.
     */
    private void onAddCategory() {
        InputDialog inputDialog = new InputDialog(getShell(), "New category", "Enter new category name:", "", null);
        if (inputDialog.open() == Window.OK) {
            commands_add(new CategoryAddCommand("category_" + System.currentTimeMillis(), inputDialog.getValue()));
        }
    }

    /**
     * Adds new {@link DeviceInfo}.
     */
    private void onAddDevice() {
        final CategoryInfo targetCategory;
        {
            Object element = getSelectedElements().get(0);
            if (element instanceof CategoryInfo) {
                targetCategory = (CategoryInfo) element;
            } else if (element instanceof DeviceInfo) {
                targetCategory = ((DeviceInfo) element).getCategory();
            } else {
                return;
            }
        }
        // open dialog
        DeviceAddDialog deviceAddDialog = new DeviceAddDialog();
        if (deviceAddDialog.open() == Window.OK) {
            commands_add(deviceAddDialog.getCommand(targetCategory));
        }
    }

    /**
     * Moves selected elements up/down.
     */
    private void onMove(int delta) {
        m_viewer.getTree().setRedraw(false);
        try {
            for (Object element : getSelectedElements()) {
                if (element instanceof CategoryInfo) {
                    CategoryInfo category = (CategoryInfo) element;
                    List<CategoryInfo> categories = DeviceManager.getCategories();
                    int index = categories.indexOf(element);
                    int targetIndex = index + delta;
                    CategoryInfo nextCategory = targetIndex < categories.size()
                            ? (CategoryInfo) categories.get(targetIndex)
                            : null;
                    commands_add(new CategoryMoveCommand(category, nextCategory));
                } else if (element instanceof DeviceInfo) {
                    DeviceInfo device = (DeviceInfo) element;
                    CategoryInfo category = device.getCategory();
                    List<DeviceInfo> devices = category.getDevices();
                    int index = devices.indexOf(device);
                    if (index + delta < devices.size()) {
                        commands_add(new DeviceMoveCommand(device, category, devices.get(index + delta)));
                    } else {
                        commands_add(new DeviceMoveCommand(device, category, null));
                    }
                }
            }
            refreshViewer();
            updateButtons();
        } finally {
            m_viewer.getTree().setRedraw(true);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // DND
    //
    ////////////////////////////////////////////////////////////////////////////
    private List<Object> m_dragElements;
    private boolean m_dragCategory;

    /**
     * Configures DND in {@link #m_viewer}.
     */
    private void configureDND() {
        Transfer[] transfers = new Transfer[] { EmptyTransfer.INSTANCE };
        m_viewer.addDragSupport(DND.DROP_MOVE, transfers, new DragSourceListener() {
            public void dragStart(DragSourceEvent event) {
                m_dragElements = getSelectedElements();
                m_dragCategory = m_dragElements.get(0) instanceof CategoryInfo;
                // check that we drag only categories or only entries
                for (Object element : m_dragElements) {
                    if (m_dragCategory != element instanceof CategoryInfo) {
                        event.doit = false;
                    }
                }
            }

            public void dragSetData(DragSourceEvent event) {
            }

            public void dragFinished(DragSourceEvent event) {
            }
        });
        ViewerDropAdapter dropAdapter = new ViewerDropAdapter(m_viewer) {
            @Override
            protected int determineLocation(DropTargetEvent event) {
                if (event.item instanceof Item) {
                    Item item = (Item) event.item;
                    Point coordinates = m_viewer.getControl().toControl(event.x, event.y);
                    Rectangle bounds = getBounds(item);
                    // when drag device, relation with category can be only ON
                    if (!m_dragCategory && determineTarget(event) instanceof CategoryInfo) {
                        return LOCATION_ON;
                    }
                    // in all other cases, drop before/after
                    return coordinates.y < bounds.y + bounds.height / 2 ? LOCATION_BEFORE : LOCATION_AFTER;
                }
                return LOCATION_NONE;
            }

            @Override
            public boolean validateDrop(Object target, int operation, TransferData transferType) {
                // category can be dragged only relative other category
                if (m_dragCategory) {
                    return target instanceof CategoryInfo;
                }
                // all other cases are valid
                return true;
            }

            @Override
            public boolean performDrop(Object data) {
                Object target = getCurrentTarget();
                int location = getCurrentLocation();
                if (m_dragCategory) {
                    Assert.instanceOf(CategoryInfo.class, target);
                    Assert.isTrue(location == LOCATION_BEFORE || location == LOCATION_AFTER);
                    // prepare next category
                    CategoryInfo nextCategory;
                    {
                        List<CategoryInfo> categories = DeviceManager.getCategories();
                        int index = categories.indexOf(target);
                        if (location == LOCATION_BEFORE) {
                            nextCategory = categories.get(index);
                        } else {
                            nextCategory = GenericsUtils.getNextOrNull(categories, index);
                        }
                    }
                    // add commands
                    for (Object element : m_dragElements) {
                        CategoryInfo category = (CategoryInfo) element;
                        commands_add(new CategoryMoveCommand(category, nextCategory));
                    }
                } else if (target instanceof CategoryInfo) {
                    Assert.isTrue(location == LOCATION_ON);
                    CategoryInfo targetCategory = (CategoryInfo) target;
                    for (Object element : m_dragElements) {
                        DeviceInfo device = (DeviceInfo) element;
                        commands_add(new DeviceMoveCommand(device, targetCategory, null));
                    }
                } else {
                    DeviceInfo targetDevice = (DeviceInfo) target;
                    CategoryInfo targetCategory = targetDevice.getCategory();
                    // prepare next device
                    DeviceInfo nextDevice;
                    {
                        List<DeviceInfo> entries = targetCategory.getDevices();
                        int index = entries.indexOf(targetDevice);
                        if (location == LOCATION_BEFORE) {
                            nextDevice = entries.get(index);
                        } else {
                            nextDevice = GenericsUtils.getNextOrNull(entries, index);
                        }
                    }
                    // add commands
                    for (Object element : m_dragElements) {
                        DeviceInfo device = (DeviceInfo) element;
                        commands_add(new DeviceMoveCommand(device, targetCategory, nextDevice));
                    }
                }
                // refresh viewer to show result of applying commands
                refreshViewer();
                return true;
            }
        };
        dropAdapter.setScrollExpandEnabled(false);
        m_viewer.addDropSupport(DND.DROP_MOVE, transfers, dropAdapter);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Commands
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Executes new {@link Command} and updates {@link #m_viewer} accordingly.
     */
    private void commands_add(Command command) {
        DeviceManager.commandsAdd(command);
        refreshViewer();
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // State
    //
    ////////////////////////////////////////////////////////////////////////////
    @Override
    public boolean performOk() {
        // write all executed commands
        DeviceManager.commandsWrite();
        return true;
    }

    @Override
    public boolean performCancel() {
        // don't save commands, force reload to undo executed commands
        DeviceManager.forceReload();
        return super.performCancel();
    }

    @Override
    protected void performDefaults() {
        super.performDefaults();
        // remove all commands
        DeviceManager.resetToDefaults();
        // show new state
        refreshViewer();
    }
}