/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.directory.studio.common.ui.widgets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.apache.directory.studio.common.ui.AddEditDialog;
import org.apache.directory.studio.common.ui.Messages;
import org.apache.directory.studio.common.ui.TableDecorator;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
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.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.forms.widgets.FormToolkit;
/**
* The TableWidget provides a table viewer to add/edit/remove an element.
*
* <pre>
* +--------------------------------------+
* | Element 1 | (Add... )
* | Element 2 | (Edit...)
* | | (Delete )
* +--------------------------------------+
* </pre>
*
* The elements are ordered.
*
* <pre>
* Note : This class contain codes from the Apache PDF box project ('sort' method)
* </pre>
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class TableWidget<E> extends AbstractWidget
{
/** The element list */
private List<E> elements = new ArrayList<E>();
/** The current selection index, if any */
private int currentSelection;
/** A flag set to tell if we have a Edit button */
private boolean hasEdit;
/** A flag set when the table is ordered (ie, it has a Up and Down buttons) */
private boolean isOrdered;
/** A flag that says if teh table is enabled or disabled */
private boolean isEnabled = true;
/** The flag set when the table is ordered */
private static final boolean ORDERED = true;
/** The flag set when the table is not ordered */
private static final boolean UNORDERED = false;
// UI widgets
private Composite composite;
private Table elementTable;
private TableViewer elementTableViewer;
// The buttons
private Button addButton;
private Button editButton;
private Button deleteButton;
private Button upButton;
private Button downButton;
/** The decorator */
private TableDecorator<E> decorator;
// A listener on the Elements table, that modifies the button when an Element is selected
private ISelectionChangedListener tableViewerSelectionChangedListener = new ISelectionChangedListener()
{
public void selectionChanged( SelectionChangedEvent event )
{
if ( isEnabled )
{
int selectionLine = elementTableViewer.getTable().getSelectionIndex();
if ( selectionLine == currentSelection )
{
// We have selected the line twice, deselect the line
elementTableViewer.getTable().deselect( selectionLine );
currentSelection = -1;
}
else
{
currentSelection = selectionLine;
StructuredSelection selection = ( StructuredSelection ) elementTableViewer.getSelection();
if ( hasEdit )
{
editButton.setEnabled( !selection.isEmpty() );
}
deleteButton.setEnabled( !selection.isEmpty() );
if ( isOrdered )
{
// We can't enable the UP button when we don't have any element in the table,
// or when we have only one, or when the selection is the first one in the table
upButton.setEnabled( !selection.isEmpty() && ( elements.size() > 1 ) && ( selectionLine > 0 ) );
// We can't enable the DOWN button when we don't have any element in the table,
// or when we have only one element, or when the selected element is the last one
downButton.setEnabled( !selection.isEmpty() && ( elements.size() > 1 ) && ( selectionLine < elements.size() - 1 ) );
}
}
}
}
};
// A listener on the Element table, that reacts to a doubleClick : it's opening the Element editor
private IDoubleClickListener tableViewerDoubleClickListener = new IDoubleClickListener()
{
public void doubleClick( DoubleClickEvent event )
{
if ( isEnabled )
{
editElement();
}
}
};
// A listener on the Add button, which opens the Element addition editor
private SelectionListener addButtonListener = new SelectionAdapter()
{
public void widgetSelected( SelectionEvent e )
{
addElement();
}
};
// A listener on the Edit button, that open the Element editor
private SelectionListener editButtonListener = new SelectionAdapter()
{
public void widgetSelected( SelectionEvent e )
{
editElement();
}
};
// A listener on the Delete button, which delete the selected Element
private SelectionListener deleteButtonListener = new SelectionAdapter()
{
public void widgetSelected( SelectionEvent e )
{
deleteElement();
}
};
// A listener on the Up button, that move the selected elemnt up one position
private SelectionListener upButtonListener = new SelectionAdapter()
{
public void widgetSelected( SelectionEvent e )
{
upElement();
}
};
// A listener on the Down button, that move the selected element down one position
private SelectionListener downButtonListener = new SelectionAdapter()
{
public void widgetSelected( SelectionEvent e )
{
downElement();
}
};
/**
* Creates a new instance of TableWidget.
*
* @param decorator the decorator to use, containing the Dialog comparator and labelProvider
*/
public TableWidget( TableDecorator<E> decorator )
{
this.decorator = decorator;
currentSelection = -1;
}
/**
* Creates the Table widget. It's a Table and three buttons :
* <pre>
* +--------------------------------------+
* | Element 1 | (Add... )
* | Element 2 | (Edit...)
* | | (Delete )
* +--------------------------------------+
* </pre>
* </pre>
*
* @param parent the parent
* @param toolkit the toolkit
*/
public void createWidgetWithEdit( Composite parent, FormToolkit toolkit )
{
createWidget( parent, toolkit, true, UNORDERED );
}
/**
* Creates the Table widget. It's a Table and two buttons :
* <pre>
* +--------------------------------------+
* | Element 1 | (Add... )
* | Element 2 | (Delete )
* | |
* +--------------------------------------+
* </pre>
*
* @param parent the parent
* @param toolkit the toolkit
*/
public void createWidgetNoEdit( Composite parent, FormToolkit toolkit )
{
createWidget( parent, toolkit, false, UNORDERED );
}
/**
* Creates the ordered Table widget. It's a Table and five buttons :
* <pre>
* +--------------------------------------+
* | Element 1 | (Add... )
* | Element 2 | (Edit...)
* | | (Delete )
* | | ---------
* | | (Up... )
* | | (Down.. )
* +--------------------------------------+
* </pre>
* The 'Up' and 'Down' buttons are used to order the elements.
*
* @param parent the parent
* @param toolkit the toolkit
*/
public void createOrderedWidgetWithEdit( Composite parent, FormToolkit toolkit )
{
createWidget( parent, toolkit, true, ORDERED );
}
/**
* Creates the Table widget. It's a Table and four buttons :
* <pre>
* +--------------------------------------+
* | Element 1 | (Add... )
* | Element 2 | (Delete )
* | | ---------
* | | (Up... )
* | | (Down.. )
* +--------------------------------------+
* </pre>
* The 'Up' and 'Down' buttons are used to order the elements.
*
* @param parent the parent
* @param toolkit the toolkit
*/
public void createOrderedWidgetNoEdit( Composite parent, FormToolkit toolkit )
{
createWidget( parent, toolkit, true, ORDERED );
}
/**
* Creates the Table widget. It's a Table and two or three button :
* <pre>
* +--------------------------------------+
* | Element 1 | (Add... )
* | Element 2 | (Edit...)
* | | (Delete )
* +--------------------------------------+
* </pre>
* or :
* <pre>
* +--------------------------------------+
* | Element 1 | (Add... )
* | Element 2 | (Delete )
* | |
* +--------------------------------------+
* </pre>
*
* If the table is ordered, we have two additional buttons to re-organize
* the elements at will :
* <pre>
* ...
* | | ---------
* | | (Up... )
* | | (Down.. )
* +--------------------------------------+
* </pre>
*
* @param parent the parent
* @param toolkit the toolkit
* @param hasEdit the flag set when the Edit button is present
* @param isOrdered the flag set when we have the Up and Down buttons
*/
private void createWidget( Composite parent, FormToolkit toolkit, boolean hasEdit, boolean isOrdered )
{
this.hasEdit = hasEdit;
this.isOrdered = isOrdered;
// Composite
if ( toolkit != null )
{
composite = toolkit.createComposite( parent );
}
else
{
composite = new Composite( parent, SWT.NONE );
}
// First, define a grid of 3 columns (two for the table, one for the buttons)
GridLayout compositeGridLayout = new GridLayout( 2, false );
compositeGridLayout.marginHeight = compositeGridLayout.marginWidth = 0;
composite.setLayout( compositeGridLayout );
// Create the Element Table and Table Viewer
if ( toolkit != null )
{
elementTable = toolkit.createTable( composite, SWT.NULL );
}
else
{
elementTable = new Table( composite, SWT.NULL );
}
// Define the table size and height. It will span on 3 to 5 lines,
// depending on the number of buttons
int nbLinesSpan = 3;
if ( isOrdered )
{
// If it's an ordered table, we add 3 line s: one for Up, one for Down and one for the separator
nbLinesSpan += 3;
}
GridData gd = new GridData( SWT.FILL, SWT.FILL, true, true, 1, nbLinesSpan );
elementTable.setLayoutData( gd );
// Create the index TableViewer
elementTableViewer = new TableViewer( elementTable );
elementTableViewer.setContentProvider( new ArrayContentProvider() );
// The LabelProvider
elementTableViewer.setLabelProvider( decorator );
elementTableViewer.addSelectionChangedListener( tableViewerSelectionChangedListener );
// Listeners : we want to catch changes and double clicks (if we have an edit button)
if ( hasEdit )
{
elementTableViewer.addDoubleClickListener( tableViewerDoubleClickListener );
}
// Inject the existing elements
elementTableViewer.setInput( elements );
GridData buttonGd = new GridData( SWT.FILL, SWT.FILL, false, false, 1, 1 );
buttonGd.widthHint = 60;
// Create the Add Button and its listener
if ( toolkit != null )
{
addButton = toolkit.createButton( composite, Messages.getString( "CommonUIWidgets.AddButton" ), SWT.PUSH );
addButton.setLayoutData( buttonGd );
}
else
{
addButton = BaseWidgetUtils.createButton( composite, Messages.getString( "CommonUIWidgets.AddButton" ), 1 );
addButton.setLayoutData( buttonGd );
}
addButton.setLayoutData( buttonGd );
addButton.addSelectionListener( addButtonListener );
// Create the Edit Button and its listener, if requested
if ( hasEdit )
{
if ( toolkit != null )
{
editButton = toolkit.createButton( composite, Messages.getString( "CommonUIWidgets.EditButton" ), SWT.PUSH );
}
else
{
editButton = BaseWidgetUtils.createButton( composite, Messages.getString( "CommonUIWidgets.EditButton" ), SWT.PUSH );
}
// It's not enabled unless we have selected an element
editButton.setEnabled( false );
editButton.setLayoutData( buttonGd );
editButton.addSelectionListener( editButtonListener );
}
// Create the Delete Button and its listener
if ( toolkit != null )
{
deleteButton = toolkit.createButton( composite, Messages.getString( "CommonUIWidgets.DeleteButton" ), SWT.PUSH );
}
else
{
deleteButton = BaseWidgetUtils.createButton( composite, Messages.getString( "CommonUIWidgets.DeleteButton" ), SWT.PUSH );
}
// It's not selected unless we have selected an index
deleteButton.setEnabled( false );
deleteButton.setLayoutData( buttonGd );
deleteButton.addSelectionListener( deleteButtonListener );
// Create the Up and Down button, if requested
if ( isOrdered )
{
Label separator = BaseWidgetUtils.createSeparator( composite, 1 );
separator.setLayoutData( new GridData( SWT.NONE, SWT.BEGINNING, false, false ) );
// Create the Up Button and its listener
if ( toolkit != null )
{
upButton = toolkit.createButton( composite, Messages.getString( "CommonUIWidgets.UpButton" ), SWT.PUSH );
}
else
{
upButton = BaseWidgetUtils.createButton( composite, Messages.getString( "CommonUIWidgets.UpButton" ), SWT.PUSH );
}
// It's not selected unless we have selected an index
upButton.setEnabled( false );
upButton.setLayoutData( buttonGd );
//upButton.setLayoutData( new GridData( SWT.FILL, SWT.BEGINNING, false, false ) );
upButton.addSelectionListener( upButtonListener );
// Create the Down Button and its listener
if ( toolkit != null )
{
downButton = toolkit.createButton( composite, Messages.getString( "CommonUIWidgets.DownButton" ), SWT.PUSH );
}
else
{
downButton = BaseWidgetUtils.createButton( composite, Messages.getString( "CommonUIWidgets.DownButton" ), SWT.PUSH );
}
// It's not selected unless we have selected an index
downButton.setEnabled( false );
downButton.setLayoutData( buttonGd );
//downButton.setLayoutData( new GridData( SWT.FILL, SWT.BEGINNING, false, false ) );
downButton.addSelectionListener( downButtonListener );
}
}
/**
* Enable the table (Buttons will be active, except the Edit and Delete ones, actions on the table will be active)
*/
public void enable()
{
if ( addButton != null )
{
addButton.setEnabled( true );
}
if ( upButton != null )
{
upButton.setEnabled( true );
}
if ( downButton != null )
{
downButton.setEnabled( true );
}
isEnabled = true;
}
/**
* Disable the table (Buttons will be inactive, actions on the table will be inactive)
*/
public void disable()
{
if ( addButton != null )
{
addButton.setEnabled( false );
}
if ( deleteButton != null )
{
deleteButton.setEnabled( false );
}
if ( editButton != null )
{
editButton.setEnabled( false );
}
if ( upButton != null )
{
upButton.setEnabled( false );
}
if ( downButton != null )
{
downButton.setEnabled( false );
}
isEnabled = false;
}
/**
* Returns the primary control associated with this widget.
*
* @return the primary control associated with this widget.
*/
public Control getControl()
{
return composite;
}
/* --------------------------------------------------------------------------------------------------------------- */
/* Taken from the Apache PdfBox project, */
/* @author UWe Pachler */
/* --------------------------------------------------------------------------------------------------------------- */
/**
* Sorts the given list using the given comparator.
*
* @param list list to be sorted
* @param cmp comparator used to compare the object swithin the list
*/
public static <T> void sort( List<T> list, Comparator<T> cmp )
{
int size = list.size();
if ( size < 2 )
{
return;
}
quicksort( list, cmp, 0, size - 1 );
}
private static <T> void quicksort( List<T> list, Comparator<T> cmp, int left, int right )
{
if ( left < right )
{
int splitter = split( list, cmp, left, right );
quicksort( list, cmp, left, splitter - 1 );
quicksort( list, cmp, splitter + 1, right );
}
}
private static <T> void swap( List<T> list, int i, int j )
{
T tmp = list.get( i );
list.set( i, list.get( j ) );
list.set( j, tmp );
}
private static <T> int split( List<T> list, Comparator<T> cmp, int left, int right )
{
int i = left;
int j = right - 1;
T pivot = list.get( right );
do
{
while ( ( cmp.compare( list.get( i ), pivot ) <= 0 ) && ( i < right ) )
{
++i;
}
while ( ( cmp.compare( pivot, list.get( j ) ) <= 0 ) && ( j > left ) )
{
--j;
}
if ( i < j )
{
swap( list, i, j );
}
} while ( i < j );
if ( cmp.compare( pivot, list.get( i ) ) < 0 )
{
swap( list, i, right );
}
return i;
}
/* --------------------------------------------------------------------------------------------------------------- */
/* End of the QuickSort implementation taken from the Apache PdfBox project, */
/* --------------------------------------------------------------------------------------------------------------- */
/**
* Sets the Elements.
*
* @param elements the elements
*/
public void setElements( List<E> elements )
{
this.elements.clear();
if ( ( elements != null ) && ( elements.size() > 0 ) )
{
sort( elements, decorator );
this.elements.addAll( elements );
}
elementTableViewer.refresh();
}
/**
* Gets the elements.
*
* @return the elements
*/
public List<E> getElements()
{
if ( elements != null )
{
List<E> copy = new ArrayList<E>( elements.size() );
copy.addAll( elements );
return copy;
}
return null;
}
/**
* This method is called when the 'Add...' button is clicked.
*/
private void addElement()
{
AddEditDialog<E> dialog = decorator.getDialog();
dialog.setAdd();
dialog.addNewElement();
dialog.setElements( elements );
// Inject the position if we have a selected value
StructuredSelection selection = ( StructuredSelection ) elementTableViewer.getSelection();
int insertionPos = elements.size();
if ( !selection.isEmpty() )
{
insertionPos = elementTableViewer.getTable().getSelectionIndex();
}
// Open the Dialog, and process the addition if it went fine
if ( decorator.getDialog().open() == Dialog.OK )
{
E newElement = decorator.getDialog().getEditedElement();
if ( !elements.contains( newElement ) )
{
String elementStr = newElement.toString();
int pos = 0;
if ( isOrdered )
{
// The table is ordered, insert the element at the right position
((OrderedElement)newElement).setPrefix( insertionPos );
elements.add( insertionPos, newElement );
// Move up the following elements
for ( int i = insertionPos + 1; i < elements.size(); i++ )
{
E element = elements.get( i );
((OrderedElement)element).incrementPrefix();
}
}
else
{
if ( selection.isEmpty() )
{
// no selected element, add at the end
pos = elements.size();
}
else
{
pos = elementTableViewer.getTable().getSelectionIndex() + 1;
}
elements.add( pos, newElement );
}
elementTableViewer.refresh();
elementTableViewer.setSelection( new StructuredSelection( elementStr ) );
}
notifyListeners();
}
}
/**
* This method is called when the 'Edit...' button is clicked
* or the table viewer is double-clicked.
*/
private void editElement()
{
StructuredSelection selection = ( StructuredSelection ) elementTableViewer.getSelection();
if ( !selection.isEmpty() )
{
AddEditDialog<E> dialog = decorator.getDialog();
dialog.setEdit();
E selectedElement = (E)selection.getFirstElement();
int editPosition = elementTableViewer.getTable().getSelectionIndex();
dialog.setEditedElement( selectedElement );
dialog.setSelectedPosition( editPosition );
// Open the element dialog, with the selected index
if ( decorator.getDialog().open() == Dialog.OK )
{
E newElement = dialog.getEditedElement();
if ( !isOrdered )
{
// Check to see if the modified element does not already exist
if ( elements.contains( newElement ) )
{
// Remove the original element
elements.remove( selectedElement );
// Replace the existing element with the new one
elements.remove( newElement );
int pos = 0;
for ( E element : elements )
{
if ( decorator.compare( element, newElement ) > 0 )
{
break;
}
else
{
pos++;
}
}
elements.add( pos, newElement );
}
else
{
// We will remove the modified element, and replace it with the new element
// Replace the old element by the new one
elements.remove( editPosition );
elements.add( editPosition, newElement );
}
}
else
{
// Remove the original element
elements.remove( selectedElement );
elements.add( editPosition, newElement );
}
elementTableViewer.refresh();
elementTableViewer.setSelection( new StructuredSelection( newElement.toString() ) );
notifyListeners();
}
}
}
/**
* This method is called when the 'Delete' button is clicked.
*/
private void deleteElement()
{
StructuredSelection selection = ( StructuredSelection ) elementTableViewer.getSelection();
if ( !selection.isEmpty() )
{
// If the table is ordered, we need to decrement the prefix of all the following elements
if ( isOrdered )
{
int selectedPosition = elementTableViewer.getTable().getSelectionIndex();
for ( int i = selectedPosition + 1; i < elements.size(); i++ )
{
E nextElement = elements.get( i );
((OrderedElement)nextElement).decrementPrefix();
elements.set( i - 1, nextElement );
}
elements.remove( elements.size() - 1 );
}
else
{
int selectedPosition = elementTableViewer.getTable().getSelectionIndex();
elements.remove( selectedPosition );
}
elementTableViewer.refresh();
notifyListeners();
}
}
/**
* This method is called when the 'Up...' button is clicked
*/
private void upElement()
{
StructuredSelection selection = ( StructuredSelection ) elementTableViewer.getSelection();
if ( !selection.isEmpty() )
{
// Get the line the selected element is in. We will move it up
int selectionLine = elementTableViewer.getTable().getSelectionIndex();
// The selected element
E selectedElement = (E)selection.getFirstElement();
// Decrease the prefix
((OrderedElement)selectedElement).decrementPrefix();
// Just swap the elements which is just before with the selected one
E previousElement = getElements().get( selectionLine - 1 );
// Increase the prefix
((OrderedElement)previousElement).incrementPrefix();
elements.remove( selectionLine - 1 );
elements.add( selectionLine, previousElement );
// Refresh the table now
elementTableViewer.refresh();
elementTableViewer.setSelection( new StructuredSelection( selectedElement ) );
notifyListeners();
}
}
/**
* This method is called when the 'Down...' button is clicked
* or the table viewer is double-clicked.
*/
private void downElement()
{
StructuredSelection selection = ( StructuredSelection ) elementTableViewer.getSelection();
if ( !selection.isEmpty() )
{
// Get the line the selected element is in. We will move it down
int selectionLine = elementTableViewer.getTable().getSelectionIndex();
// The selected element
E selectedElement = (E)selection.getFirstElement();
// Increase the prefix
((OrderedElement)selectedElement).incrementPrefix();
// Just swap the elements which is just after with the selected one
E previousElement = getElements().get( selectionLine + 1 );
// Decrease the prefix
((OrderedElement)previousElement).decrementPrefix();
elements.remove( selectionLine + 1 );
elements.add( selectionLine, previousElement );
// refresh the table now
elementTableViewer.refresh();
elementTableViewer.setSelection( new StructuredSelection( selectedElement ) );
notifyListeners();
}
}
}