/* * Copyright 2008-2012 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://aws.amazon.com/apache2.0 * * This file 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.amazonaws.eclipse.ec2.ui; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.layout.TreeColumnLayout; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TreeItem; import com.amazonaws.eclipse.core.AccountInfo; import com.amazonaws.eclipse.core.AwsToolkitCore; import com.amazonaws.eclipse.core.regions.Region; import com.amazonaws.eclipse.core.regions.RegionUtils; import com.amazonaws.eclipse.core.regions.ServiceAbbreviations; import com.amazonaws.services.ec2.AmazonEC2; /** * A generic table supporting basic operations (column creation, context menu * creation, etc) suitable to use as a base for more complex tables. */ public abstract class SelectionTable extends Composite { /** The internal table viewer control */ protected TreeViewer viewer; /** An optional listener to notify before and after loading AMIs */ protected SelectionTableListener selectionTableListener; /** The shared user account info */ private AccountInfo accountInfo = AwsToolkitCore.getDefault().getAccountInfo(); /** True if this selection table allows multiple items to be selected */ private final boolean allowMultipleSelections; /** True if this table should be virtual because of data sizes */ private final boolean virtualTable; /** An optional comparator to control sorting by columns */ protected SelectionTableComparator comparator; /** An optional account ID to use in EC2 requests, otherwise the default will be used */ protected String accountIdOverride; /** * Specifying an EC2 region override will cause the EC2 client for this * selection table to always use this overridden endpoint, instead of using * the user's currently selected endpoint from the system preferences. */ protected Region ec2RegionOverride; /** * Creates a new selection table with the specified parent. * * @param parent * The composite object to contain this new selection table. */ public SelectionTable(Composite parent) { this(parent, false, false); } /** * Creates a new selection table with the specified parent. * * @param parent * The composite object to contain this new selection table. * @param allowMultipleSelections * True if this new selection table should allow multiple rows to * be selected at once, otherwise false if only one row should be * selectable at a time. * @param virtualTable * True if this new selection table should use the SWT VIRTUAL * style, indicating that it will supply a lazy content provider. */ public SelectionTable(Composite parent, boolean allowMultipleSelections, boolean virtualTable) { super(parent, SWT.NONE); this.allowMultipleSelections = allowMultipleSelections; this.virtualTable = virtualTable; createControl(); } /** * Adds a selection listener to this selection table. * * @param listener The selection listener to add to this table. */ public void addSelectionListener(SelectionListener listener) { viewer.getTree().addSelectionListener(listener); } /** * Clears the selection in this selection table. */ public void clearSelection() { viewer.getTree().setSelection(new TreeItem[0]); } /** * Sets the optional SelectionTableListener that will be notified before * and after data is loaded. Loading data can be a long operation, so * providing a listener allows another class to receive events as the * loading status changes so it can update itself appropriately. * * @param listener * The listener to notify before and after loading data. */ public void setListener(SelectionTableListener listener) { this.selectionTableListener = listener; } /** * Sets the optional EC2 region override for this selection table. Setting * this value will cause the EC2 client returned by the getClient method to * *always* use this EC2 region, and will completely ignore the EC2 region * selected in the system preferences. * * @param region * The EC2 region to use instead of defaulting to the currently * selected EC2 region in the system preferences. */ public void setEc2RegionOverride(Region region) { this.ec2RegionOverride = region; } /** * Sets the optional EC2 account ID override for this selection table. * Setting this will cause the EC2 client to authenticate requests with the * specified account, otherwise the default account is used. * * @param accountId * The account ID to use for making requests in this selection * table. */ public void setAccountIdOverride(String accountId) { this.accountIdOverride = accountId; } /** * Returns the viewer used in this table. */ protected Viewer getViewer() { return viewer; } /** * Returns a ready-to-use EC2 client using the currently selected AWS * account. * * @return A fully configured AWS EC2 client. */ protected AmazonEC2 getAwsEc2Client() { if (accountIdOverride != null) return getAwsEc2Client(accountIdOverride); else return getAwsEc2Client(null); } /** * Returns a ready-to-use EC2 client for the account ID given. * * TODO: move the account ID into a member variable, deprecate this method */ protected AmazonEC2 getAwsEc2Client(String accountId) { if ( ec2RegionOverride != null ) { return AwsToolkitCore.getClientFactory(accountId).getEC2ClientByEndpoint(ec2RegionOverride.getServiceEndpoint(ServiceAbbreviations.EC2)); } Region defaultRegion = RegionUtils.getCurrentRegion(); String regionEndpoint = defaultRegion.getServiceEndpoints().get(ServiceAbbreviations.EC2); return AwsToolkitCore.getClientFactory(accountId).getEC2ClientByEndpoint(regionEndpoint); } /** * Returns the current selection in this table. * * @return The current selection in this table. */ protected Object getSelection() { StructuredSelection selection = (StructuredSelection)viewer.getSelection(); return selection.getFirstElement(); } /** * Packs the columns in this selection table. */ protected void packColumns() { if (viewer == null) return; Tree table = viewer.getTree(); for (TreeColumn col : table.getColumns()) { col.pack(); } } /** * Creates a new column with the specified text and weight. * * @param columnText * The text for the column header. * @param weight * The weight of the new column, relative to the other columns. * * @return The new TableColum that was created. */ protected TreeColumn newColumn(String columnText, int weight) { Tree table = viewer.getTree(); TreeColumn column = new TreeColumn(table, SWT.NONE); column.setText(columnText); TreeColumnLayout tableColumnLayout = (TreeColumnLayout)getLayout(); tableColumnLayout.setColumnData(column, new ColumnWeightData(weight)); return column; } /** * Creates and configures the actual tree control. */ protected void createControl() { TreeColumnLayout treeColumnLayout = new TreeColumnLayout(); setLayout(treeColumnLayout); int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER; if (allowMultipleSelections) { style |= SWT.MULTI; } else { style |= SWT.SINGLE; } if (virtualTable) { style |= SWT.VIRTUAL; } viewer = new TreeViewer(this, style); if (virtualTable) viewer.setUseHashlookup(true); Tree tree = viewer.getTree(); tree.setLinesVisible(true); tree.setHeaderVisible(true); createColumns(); makeActions(); hookContextMenu(); } /** * Subclasses must implement this to set up the table's columns. */ protected abstract void createColumns(); /** * Subclasses must implement this to create any needed actions. */ protected abstract void makeActions(); /** * Subclasses must implement this to provide any context menu contributions. * * @param manager * The manager for the table's context menu. */ protected abstract void fillContextMenu(IMenuManager manager); /** * Hooks a context (popup) menu for the table control. */ private void hookContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new AcountValidatingMenuListener()); Menu menu = menuMgr.createContextMenu(viewer.getControl()); viewer.getControl().setMenu(menu); menuMgr.createContextMenu(this); } /** * Sets the comparator used to control sorting by columns in this selection * table and adds a selection listener to each column in this table that will * update the sorting information on the comparator. * * @param comparator * The comparator used to control sorting by columns in this * selection table. */ protected void setComparator(SelectionTableComparator comparator) { this.comparator = comparator; viewer.setComparator(comparator); viewer.getTree().setSortColumn(viewer.getTree().getColumn(comparator.getColumn())); viewer.getTree().setSortDirection(comparator.getDirection()); int i = 0; for (TreeColumn column : viewer.getTree().getColumns()) { column.addSelectionListener(new SelectionTableColumnClickListener(i++, viewer, comparator)); } } /** * A MenuListener that checks to see if the user's AWS account information has * been entered. If it hasn't, the user will be directed to the plugin * preferences so they can enter it and start using the tools. */ private final class AcountValidatingMenuListener implements IMenuListener { /* (non-Javadoc) * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager) */ public void menuAboutToShow(IMenuManager manager) { if (accountInfo != null && accountInfo.isValid()) { fillContextMenu(manager); return; } manager.add(new SetupAwsAccountAction()); } } /** * A simple interface that objects can implement if they want to register * themselves as a listener for data loading events in this SelectionTable. */ public interface SelectionTableListener { /** * This method is called to notify the listener that this selection * table is currently reloading its data. */ public void loadingData(); /** * This method is called to notify the listener that this selection * table has finished loading all data. This doesn't necessarily mean * that they were loaded successfully, just that this selection table is * no longer querying for data. * Will also pass along the number of records that got loaded */ public void finishedLoadingData(int recordCount); } }