/* | |
* Copyright (C) 2012 The Android Open Source Project | |
* | |
* 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://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* 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.motorola.studio.android.wizards.elements; | |
import java.util.Collection; | |
import org.eclipse.jface.wizard.WizardPage; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.events.SelectionEvent; | |
import org.eclipse.swt.events.SelectionListener; | |
import org.eclipse.swt.layout.GridData; | |
import org.eclipse.swt.layout.GridLayout; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.swt.widgets.Layout; | |
import org.eclipse.swt.widgets.Table; | |
import org.eclipse.swt.widgets.TableColumn; | |
import org.eclipse.swt.widgets.TableItem; | |
import com.motorola.studio.android.i18n.AndroidNLS; | |
import com.motorola.studio.android.wizards.mat.DumpHPROFTable; | |
import com.motorola.studio.android.wizards.mat.DumpHPROFWizardPage; | |
/** | |
* <p> | |
* This widget displays a table with the ability of getting data asynchronously | |
* and showing it. Basically, while retrieving data, a Displaying info... is displayed. | |
* After the data is retrieved, its info is shown in the Table. | |
* </p> | |
* <p> | |
* To use this component, first one must implement it and at least fill in the methods | |
* {@link #callServiceForRetrievingDataToPopulateTable(Object)} and | |
* {@link #addTableData(Collection)}. The former aims to call the service and | |
* get its data. Note that the asynchronous process is left to this widget. The latter | |
* adds data to the widget´s table. | |
* </p> | |
* <p> | |
* When using this widget in the class, firstly is necessary to instantiate it, add its columns | |
* and finally call the method {@link #populateTableAsynchronously(Object)}. Note that | |
* the object is the parameter(s) for the service calling in method {@link #callServiceForRetrievingDataToPopulateTable(Object)}; | |
* </p> | |
* <p> | |
* One should also note that there are several {@link Table} methods wrapped by this component. | |
* In case it is needed, one may add more. The {@link Table} itself is exposed by the method | |
* {@link #getTable()}. The only restriction that should be observed is that | |
* no data should be added manually, only by the method {@link #addTableData(Collection)}. | |
* </p> | |
* <p> | |
* For an example of this widget in action, please consult {@link DumpHPROFTable} and {@link DumpHPROFWizardPage}. | |
* </p> | |
* | |
* @param <P> Class which is the type of the Calling Page, retrieved by | |
* the method {@link TableWithLoadingInfo#getCallingPage()}. | |
* @param <E> Class which is the type of the Element list retrieved by the asynchronous | |
* service. This service is called by the method {@link TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable(Object)}. | |
* @param <V> The type of value which represents the parameter entered as a parameter | |
* to call the asynchronous service: {@link TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable(Object)}. | |
*/ | |
public abstract class TableWithLoadingInfo<P, E, V> extends Composite | |
{ | |
/** | |
* This thread displays the Loading info... message in the table. | |
*/ | |
private final class LoadingInfoAnimationThread extends Thread | |
{ | |
private TableItem item; | |
private final Table table; | |
/** | |
* Instantiates the Loading info message. | |
* | |
* @param threadName Thread name. | |
* @param table Table which holds the Loading Info... message. | |
* @param item Table item which holds the Loading Info... message. | |
*/ | |
private LoadingInfoAnimationThread(String threadName, Table table, TableItem item) | |
{ | |
super(threadName); | |
this.table = table; | |
this.item = item; | |
} | |
/** | |
* Add animated text of Loading Data... | |
*/ | |
@Override | |
public void run() | |
{ | |
int i = 0; | |
StringBuffer text; | |
while (!isInterrupted()) | |
{ | |
text = | |
new StringBuffer(animatedTextLabel != null ? animatedTextLabel | |
: AndroidNLS.TableWithLoadingInfo__UI_LoadingData); | |
for (int j = 0; j < (i % 4); j++) | |
{ | |
text = text.append("."); //$NON-NLS-1$ | |
} | |
final StringBuffer finalText = new StringBuffer(text); | |
Display.getDefault().syncExec(new Runnable() | |
{ | |
public void run() | |
{ | |
// in case the item is disposed, re-create it. | |
if (item.isDisposed()) | |
{ | |
item = new TableItem(table, SWT.NONE); | |
} | |
item.setText(0, finalText.toString()); | |
} | |
}); | |
i++; | |
try | |
{ | |
sleep(1000); | |
} | |
catch (InterruptedException e) | |
{ | |
break; | |
} | |
} | |
} | |
} | |
/** | |
* This listener implements the functionality of deselecting all | |
* data from a determined table. | |
*/ | |
private class DeselectListener implements SelectionListener | |
{ | |
Table table; | |
/** | |
* Instantiates the Listener for deselecting all data from | |
* the entered table. | |
* | |
* @param table Table which all rows will be deselected. | |
*/ | |
public DeselectListener(Table table) | |
{ | |
this.table = table; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent) | |
*/ | |
public void widgetDefaultSelected(SelectionEvent e) | |
{ | |
this.table.deselectAll(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) | |
*/ | |
public void widgetSelected(SelectionEvent e) | |
{ | |
this.table.deselectAll(); | |
} | |
} | |
E elementList; | |
private Thread animatedText; | |
private String animatedTextLabel = null; | |
private final SelectionListener deselectionItem; | |
private final Table table; | |
private P callingPage; | |
/** | |
* Set the page which called this widget. | |
* | |
* @param The page which called this widget. | |
*/ | |
public void setCallingPage(P callingPage) | |
{ | |
this.callingPage = callingPage; | |
} | |
/** | |
* Get the page which called this widget. | |
* | |
* @return The page which called this widget. | |
*/ | |
public P getCallingPage() | |
{ | |
return callingPage; | |
} | |
/** | |
* Get the table which is populated. It is strongly recommended that | |
* the insertion of data in this table should be done by the method | |
* {@link TableWithLoadingInfo#addTableData(Collection)}. This table | |
* is exposed in order to allow its customization. | |
* | |
* @return The table to be populated. | |
*/ | |
public Table getTable() | |
{ | |
return table; | |
} | |
/** | |
* Get the collection of data of this table, after the table is populated | |
* by calling the service asynchronously. | |
* | |
* @return List of elements retrieved by the service called by the method | |
* {@link #populateTableAsynchronously()}. | |
* | |
* @see TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable() | |
* @see TableWithLoadingInfo#populateTableAsynchronously() | |
*/ | |
public E getElementList() | |
{ | |
return elementList; | |
} | |
/** | |
* Set the "collection" of elements for populating this table. When this method is called, | |
* the Loading info... message is replaced by the list of elements to be inserted, in case | |
* this list is not null. If null is entered as a parameter, the Loading info... | |
* message is displayed. | |
* | |
* @param elementList The Set of elements to populate this table to set | |
*/ | |
public void populateTable(E elementList) | |
{ | |
this.elementList = elementList; | |
// stop the animation | |
if (animatedText != null) | |
{ | |
animatedText.interrupt(); | |
} | |
// populate and display the list of elements | |
Display.getDefault().syncExec(new Runnable() | |
{ | |
/* | |
* (non-Javadoc) | |
* @see java.lang.Runnable#run() | |
*/ | |
public void run() | |
{ | |
if (!table.isDisposed()) | |
{ | |
// remove the Loading info... message | |
table.removeAll(); | |
// populate the table and display the data | |
populateTable(); | |
} | |
} | |
}); | |
this.elementList = elementList; | |
} | |
/** | |
* Instantiates a {@link Table} with Loading info component. It holds | |
* the composite parent the the {@link SWT} style. This constructor can | |
* call the method for starting the {@link Table}´s population process. | |
* | |
* @param parent Composite parent. | |
* @param style {@link SWT} style. | |
* @param animatedTextLabel Text to be displayed when data is being loaded. | |
* @param callingPage The page which called this widget. | |
*/ | |
public TableWithLoadingInfo(Composite parent, int style, String animatedTextLabel, P callingPage) | |
{ | |
super(parent, SWT.FILL); | |
// create the layout | |
Layout layout = new GridLayout(); | |
this.setLayout(layout); | |
// add table | |
this.table = new Table(this, style); | |
this.table.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true)); | |
// set attributes | |
this.animatedTextLabel = animatedTextLabel; | |
this.setCallingPage(callingPage); | |
// instantiate listeners | |
deselectionItem = new DeselectListener(table); | |
} | |
/** | |
* Remove all items from the {@link Table}. | |
* | |
* @see Table#removeAll() | |
*/ | |
public void removeAllTableItems() | |
{ | |
table.removeAll(); | |
} | |
/** | |
* Add a column to the {@link Table} of this component. | |
* | |
* @param columnStyle Column style. | |
* @param tableIndex {@link Table} index where the column will be added. | |
* | |
* @return The added {@link TableColumn} | |
* | |
* @see TableColumn#TableColumn(Table, int, int) | |
*/ | |
public TableColumn addTableColumn(int columnStyle, int tableIndex) | |
{ | |
return new TableColumn(table, columnStyle, tableIndex); | |
} | |
/** | |
* Add a column the the {@link Table} of this component. | |
* | |
* @param columnStyle Column style. | |
* | |
* @return The added {@link TableColumn} | |
* | |
* @see TableColumn#TableColumn(Table, int) | |
*/ | |
public TableColumn addTableColumn(int columnStyle) | |
{ | |
return new TableColumn(table, columnStyle); | |
} | |
/** | |
* Add a {@link SelectionListener} to the table of this component. | |
* | |
* @param selectionListener Selection Listener to be added. | |
* | |
* @see Table#addSelectionListener(SelectionListener) | |
*/ | |
public void addTableSelectionListener(SelectionListener selectionListener) | |
{ | |
table.addSelectionListener(selectionListener); | |
} | |
/** | |
* Get the number of selected elements in the table. | |
* | |
* @return Number of selected elements. | |
* | |
* @see Table#getSelectionCount() | |
*/ | |
public int getTableSelectionCont() | |
{ | |
return table.getSelectionCount(); | |
} | |
/** | |
* Get the index of a selected item. | |
* | |
* @return Index of a selected item | |
* | |
* @see Table#getSelectionIndex() | |
*/ | |
public int getTableSelectionIndex() | |
{ | |
return table.getSelectionIndex(); | |
} | |
/** | |
* The the list of selected indices. | |
* | |
* @return List of selection indices. | |
* | |
* @see Table#getSelectionIndices() | |
*/ | |
public int[] getSelectionIndices() | |
{ | |
return table.getSelectionIndices(); | |
} | |
/** | |
* The the list of selected elements. | |
* | |
* @return List of selected Elements | |
* | |
* @see Table#getSelection() | |
*/ | |
public TableItem[] getTableSelection() | |
{ | |
return table.getSelection(); | |
} | |
/** | |
* Get all Table Items from a {@link Table}. | |
* | |
* @return Array of Table Items ({@link TableItem}). | |
* | |
* @see Table#getItems() | |
*/ | |
public TableItem[] getTableItems() | |
{ | |
return table.getItems(); | |
} | |
/** | |
* Get a {@link TableItem} based on its index on the {@link Table}. | |
* | |
* @param index Index on the table to retrieve the {@link TableItem}. | |
* | |
* @return Retrieved {@link TableItem}. | |
* | |
* @see Table#getItem(int) | |
*/ | |
public TableItem getTableItem(int index) | |
{ | |
return table.getItem(index); | |
} | |
/** | |
* <p> | |
* Marks the {@link Table}´s receiver's lines as visible if the argument is true, | |
* and marks it invisible otherwise. Note that some platforms draw | |
* grid lines while others may draw alternating row colors. | |
* </p> | |
* <p> | |
* If one of the receiver's ancestors is not visible or some other | |
* condition makes | |
* </p> | |
* | |
* @param The new visibility state. | |
* | |
* see {@link Table#setLinesVisible(boolean)} | |
*/ | |
public void setTableLinesVisible(boolean show) | |
{ | |
table.setLinesVisible(show); | |
} | |
/** | |
* Makes the {@link Table}´s header visible, is <code>true</code> is set. | |
* Otherwise, mark it not visible. | |
* | |
* @param show Parameter which determines whether tha {@link Table} will be | |
* marked as visible. It will be so, if <code>true</code> is entered. | |
* | |
* @see Table#setHeaderVisible(boolean) | |
*/ | |
public void setTableHeaderVisible(boolean show) | |
{ | |
table.setHeaderVisible(show); | |
} | |
/** | |
* Get the number of selected items in a {@link Table}. | |
* | |
* @return Number of selected items in a {@link Table}. | |
* | |
* @see Table#getSelectionCount() | |
*/ | |
public int getTableSelectionCount() | |
{ | |
return table.getSelectionCount(); | |
} | |
/** | |
* Deselect all items from a {@link Table}. | |
* | |
* @see Table#deselectAll() | |
*/ | |
public void deselectAllTableItems() | |
{ | |
table.deselectAll(); | |
} | |
/** | |
* Set all items entered as selected. | |
* | |
* @param item Items to be selected. | |
* | |
* @see Table#setSelection(TableItem[]) | |
*/ | |
public void setTableSelection(TableItem[] item) | |
{ | |
table.setSelection(item); | |
} | |
/** | |
* This method is responsible for calling the service which will populate the {@link Table}. Note | |
* that no asynchronous task is to be done here. This feature is implemented by this widget. Thus, | |
* basically this method should simply call the service for populating the {@link Table} | |
* and return its data as this method´s parameter. | |
* | |
* @param parameters Parameters used by the service | |
* | |
* @return The data retrieved by the service. | |
*/ | |
protected abstract E callServiceForRetrievingDataToPopulateTable(V parameters); | |
/** | |
* This method is responsible for adding data to the {@link Table}. Here the user has | |
* to worry only with inserting elements in the table. The mechanism for | |
* creating the Loading info... is taken care by this table implementation. The data | |
* in inserted in the table refereed by the object retrieved by the method {@link TableWithLoadingInfo#getTable()}. | |
*/ | |
protected abstract void addTableData(E elementList); | |
/** | |
* This method executes needed operations after the table is populated. Here, | |
* thinks like {@link WizardPage#setPageComplete(boolean)} and layout wrap ups ought to be | |
* executed. Note that this method can be empty. Moreover, observe that this method | |
* can be called even if the {@link Table} is empty, because the service could not yet have been called. | |
* It means this method could be called twice, firstly when the Loading info... message is displayed and secondly | |
* when the data is retrieved from the asynchronous service and populates the table. | |
*/ | |
protected abstract void executeOperationsAfterTableIsPopulated(); | |
/** | |
* This method populates the Table asynchronously. It means, that it | |
* call the service implemented by the method {@link TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable(V)} | |
* and while nothing is retrieved, the Loading info... message is displayed. When | |
* the service retrieves data, its value populates the table. | |
* | |
* @param asynchronousServiceParameters The parameters to be passed to be | |
* asynchronous service which will be called. | |
*/ | |
public void populateTableAsynchronously(final V asynchronousServiceParameters) | |
{ | |
// reset element list | |
elementList = null; | |
// populate the table with nothing | |
populateTable(); | |
// call the service for populating the table asynchronously | |
Thread callServiceThread = new Thread("listPackages") | |
{ | |
/** | |
* Call the service implemented by {@link TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable(V)} | |
* and populate the Table in this widget. | |
*/ | |
@Override | |
public void run() | |
{ | |
// retrieve the list of running processes in the device | |
E elementList = | |
callServiceForRetrievingDataToPopulateTable(asynchronousServiceParameters); | |
// populate the table | |
populateTable(elementList); | |
}; | |
}; | |
// start thread for retrieving data and populating the table | |
callServiceThread.start(); | |
} | |
/** | |
* Populate the table with data provided by {@link TableWithLoadingInfo#addTableData()}. This method | |
* is responsible for creating the Loading info... within the table. In the end, | |
* the method {@link TableWithLoadingInfo#executeOperationsAfterTableIsPopulated()} is executed in order | |
* to perform operations needed after data population is performed. This method, of course, can be empty. | |
*/ | |
private synchronized void populateTable() | |
{ | |
// add data to the table in case there are elements to do so | |
if (elementList != null) | |
{ | |
// clear table | |
table.clearAll(); | |
if (deselectionItem != null) | |
{ | |
table.removeSelectionListener(deselectionItem); | |
} | |
// add data to the table | |
addTableData(elementList); | |
} | |
// since there is no data yet retrieve, add the animation | |
else | |
{ | |
// add listeners | |
table.addSelectionListener(deselectionItem); | |
// add animated text | |
TableItem item = new TableItem(table, SWT.NONE); | |
animatedText = new LoadingInfoAnimationThread("TextAnimator", table, item); | |
animatedText.start(); | |
} | |
// execute final operations | |
executeOperationsAfterTableIsPopulated(); | |
} | |
} |