Java tutorial
/******************************************************************************* * 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(); } }