| /******************************************************************************* |
| * Copyright (c) 2010 The Eclipse Foundation and others. |
| * 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: |
| * The Eclipse Foundation - initial API and implementation |
| * Yatta Solutions - bug 432803: public API |
| *******************************************************************************/ |
| package org.eclipse.epp.internal.mpc.ui.wizards; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.epp.internal.mpc.ui.catalog.MarketplaceNodeCatalogItem; |
| import org.eclipse.epp.mpc.ui.Operation; |
| import org.eclipse.equinox.internal.p2.discovery.model.CatalogItem; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| |
| /** |
| * A controller that controls the multi-state install/uninstall button |
| * |
| * @author David Green |
| */ |
| @SuppressWarnings("rawtypes") |
| class ItemButtonController { |
| private enum ButtonState { |
| INSTALL(Messages.ItemButtonController_install, Operation.NONE, false), // |
| UNINSTALL(Messages.ItemButtonController_uninstall, Operation.NONE, false), // |
| INSTALL_PENDING(Messages.ItemButtonController_installPending, Operation.INSTALL, false), // |
| UNINSTALL_PENDING(Messages.ItemButtonController_uninstallPending, Operation.UNINSTALL, false), // |
| DISABLED(Messages.ItemButtonController_install, Operation.NONE, true), // |
| UPDATE_DISABLED(Messages.ItemButtonController_update, Operation.NONE, true), // |
| UPDATE(Messages.ItemButtonController_update, Operation.NONE, false), // |
| UPDATE_PENDING(Messages.ItemButtonController_updatePending, Operation.UPDATE, false), // |
| CHANGE(Messages.ItemButtonController_change, Operation.NONE, false), // |
| CHANGE_PENDING(Messages.ItemButtonController_changePending, Operation.CHANGE, false); |
| |
| final String label; |
| |
| private final Operation operation; |
| |
| private final boolean disabled; |
| |
| private ButtonState(String label, Operation operation, boolean disabled) { |
| this.label = label; |
| this.operation = operation; |
| this.disabled = disabled; |
| } |
| |
| public ButtonState nextState() { |
| switch (this) { |
| case INSTALL: |
| return INSTALL_PENDING; |
| case INSTALL_PENDING: |
| return INSTALL; |
| case UNINSTALL: |
| return UNINSTALL_PENDING; |
| case UNINSTALL_PENDING: |
| return UNINSTALL; |
| case UPDATE: |
| return UPDATE_PENDING; |
| case UPDATE_PENDING: |
| return UPDATE; |
| case CHANGE: |
| return CHANGE_PENDING; |
| case CHANGE_PENDING: |
| return CHANGE; |
| } |
| return this; |
| } |
| |
| public ButtonState noActionState() { |
| switch (this) { |
| case INSTALL_PENDING: |
| return INSTALL; |
| case UNINSTALL_PENDING: |
| return UNINSTALL; |
| case UPDATE_PENDING: |
| return UPDATE; |
| case CHANGE_PENDING: |
| return CHANGE; |
| } |
| return this; |
| } |
| |
| } |
| |
| private final DiscoveryItem item; |
| |
| private final DropDownButton button; |
| |
| private ButtonState primaryState; |
| |
| private List<ButtonState> selectableStates; |
| |
| private final Menu menu; |
| |
| @SuppressWarnings("unused") |
| private final MarketplaceViewer viewer; |
| |
| public ItemButtonController(MarketplaceViewer marketplaceViewer, DiscoveryItem discoveryItem, DropDownButton button) { |
| this.item = discoveryItem; |
| this.button = button; |
| this.viewer = marketplaceViewer; |
| |
| updateButtonState(); |
| menu = createMenu(button.getButton()); |
| updateAppearance(); |
| button.addSelectionListener(new SelectionListener() { |
| public void widgetSelected(SelectionEvent e) { |
| if (e.detail == SWT.ARROW) { |
| menuClicked(); |
| } else { |
| buttonClicked(primaryState); |
| } |
| } |
| |
| public void widgetDefaultSelected(SelectionEvent e) { |
| widgetSelected(e); |
| } |
| }); |
| } |
| |
| private Menu createMenu(Button button) { |
| final Menu menu = new Menu(button); |
| createMenuItems(menu); |
| button.addDisposeListener(new DisposeListener() { |
| |
| public void widgetDisposed(DisposeEvent e) { |
| menu.dispose(); |
| } |
| }); |
| return menu; |
| } |
| |
| private void createMenuItems(Menu menu) { |
| if (selectableStates != null) { |
| if (primaryState != null) { |
| createMenuItem(menu, primaryState); |
| } |
| for (ButtonState state : selectableStates) { |
| createMenuItem(menu, state); |
| } |
| } |
| } |
| |
| private void createMenuItem(Menu menu, ButtonState state) { |
| MenuItem menuItem = new MenuItem(menu, SWT.PUSH); |
| menuItem.setData(state); |
| menuItem.setText(state.label); |
| menuItem.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| buttonClicked((ButtonState) e.widget.getData()); |
| } |
| }); |
| } |
| |
| private void updateMenuItems() { |
| MenuItem[] items = menu.getItems(); |
| for (MenuItem menuItem : items) { |
| menuItem.dispose(); |
| } |
| createMenuItems(menu); |
| } |
| |
| private void menuClicked() { |
| List<ButtonState> selectableStates = getSelectableStates(); |
| if (selectableStates.isEmpty()) { |
| buttonClicked(primaryState); |
| return; |
| } |
| Rectangle rect = button.getButton().getBounds(); |
| Point p = button.getButton().toDisplay(rect.x, rect.y + rect.height); |
| menu.setLocation(p.x - rect.x, p.y - rect.y); |
| menu.setVisible(true); |
| } |
| |
| private void buttonClicked(ButtonState primary) { |
| if (primary != ButtonState.DISABLED) { |
| primary = primary.nextState(); |
| item.maybeModifySelection(primary.operation); |
| } |
| refresh(); |
| } |
| |
| private void updateButtonState() { |
| primaryState = ButtonState.DISABLED; |
| MarketplaceNodeCatalogItem catalogItem = (MarketplaceNodeCatalogItem) item.getData(); |
| if (catalogItem.getInstallableUnits().isEmpty()) { |
| primaryState = ButtonState.DISABLED; |
| } else { |
| Operation operation = item.getSelectedOperation(); |
| boolean installed = isItemInstalled(); |
| selectableStates = Collections.emptyList(); |
| if (installed) { |
| switch (operation) { |
| case UPDATE: |
| primaryState = ButtonState.UPDATE_PENDING; |
| break; |
| case UNINSTALL: |
| primaryState = ButtonState.UNINSTALL_PENDING; |
| break; |
| case CHANGE: |
| primaryState = ButtonState.CHANGE_PENDING; |
| break; |
| case NONE: |
| primaryState = ButtonState.UPDATE; |
| if (hasUpdateAvailable()) { |
| selectableStates = hasOptionalFeatures() ? Arrays.asList(ButtonState.CHANGE, |
| ButtonState.UNINSTALL) : Collections.singletonList(ButtonState.UNINSTALL); |
| } else { |
| primaryState = hasOptionalFeatures() ? ButtonState.CHANGE : ButtonState.UNINSTALL; |
| selectableStates = hasOptionalFeatures() ? Collections.singletonList(ButtonState.UNINSTALL) |
| : Collections.<ButtonState> emptyList(); |
| } |
| break; |
| } |
| } else { |
| switch (operation) { |
| case INSTALL: |
| primaryState = ButtonState.INSTALL_PENDING; |
| break; |
| case NONE: |
| primaryState = ButtonState.INSTALL; |
| break; |
| } |
| if (!isItemAvailable()) { |
| primaryState = ButtonState.DISABLED; |
| } |
| } |
| } |
| } |
| |
| private List<ButtonState> getSelectableStates() { |
| if (selectableStates == null) { |
| updateButtonState(); |
| } |
| return selectableStates; |
| } |
| |
| private boolean hasUpdateAvailable() { |
| Boolean available = ((MarketplaceNodeCatalogItem) item.getData()).getUpdateAvailable(); |
| return available == null || available.booleanValue(); |
| } |
| |
| private boolean hasOptionalFeatures() { |
| Boolean optional = ((MarketplaceNodeCatalogItem) item.getData()).getHasOptionalFeatures(); |
| return isItemInstalled() && (optional == null || optional.booleanValue()); |
| } |
| |
| private boolean isItemInstalled() { |
| return ((CatalogItem) item.getData()).isInstalled(); |
| } |
| |
| private boolean isItemAvailable() { |
| Boolean available = ((CatalogItem) item.getData()).getAvailable(); |
| return available == null || available; |
| } |
| |
| private void updateAppearance() { |
| boolean relayout = false; |
| Button control = button.getButton(); |
| if (!primaryState.label.equals(button.getText())) { |
| if (primaryState == ButtonState.INSTALL) { //bold |
| button.setFont(JFaceResources.getFontRegistry().getBold("")); //$NON-NLS-1$ |
| } else if (primaryState.noActionState() == primaryState) { // no "pending" state |
| button.setFont(JFaceResources.getFontRegistry().defaultFont()); |
| } else { //"pending" state - italic |
| button.setFont(JFaceResources.getFontRegistry().getItalic("")); //$NON-NLS-1$ |
| } |
| button.setText(primaryState.label); |
| |
| Point preferredSize = control.computeSize(SWT.DEFAULT, SWT.DEFAULT); |
| int preferredWidth = preferredSize.x + 10;//Give a bit of extra padding for bold or italic font |
| ((GridData) control.getLayoutData()).minimumWidth = preferredWidth; |
| |
| relayout = true; |
| } |
| control.setEnabled(!primaryState.disabled); |
| // button image? Due to platform limitations we can't set the button color |
| |
| boolean menu = !getSelectableStates().isEmpty(); |
| if (menu != button.isShowArrow()) { |
| relayout = true; |
| button.setShowArrow(menu); |
| } |
| if (relayout) { |
| item.layout(true, false); |
| } |
| } |
| |
| public void refresh() { |
| updateButtonState(); |
| updateMenuItems(); |
| updateAppearance(); |
| } |
| |
| } |