/**
* This file is part of Archiv-Editor.
*
* The software Archiv-Editor serves as a client user interface for working with
* the Person Data Repository. See: pdr.bbaw.de
*
* The software Archiv-Editor was developed at the Berlin-Brandenburg Academy
* of Sciences and Humanities, Jägerstr. 22/23, D-10117 Berlin.
* www.bbaw.de
*
* Copyright (C) 2010-2013 Berlin-Brandenburg Academy
* of Sciences and Humanities
*
* The software Archiv-Editor was developed by @author: Christoph Plutte.
*
* Archiv-Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Archiv-Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Archiv-Editor.
* If not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package org.bbaw.pdr.ae.export.swt;
import java.util.Vector;
import org.bbaw.pdr.ae.export.internal.PdrObjectSelectionStructure;
import org.bbaw.pdr.ae.export.pluggable.AeExportCoreProvider;
import org.bbaw.pdr.ae.metamodel.PdrId;
import org.bbaw.pdr.ae.model.Aspect;
import org.bbaw.pdr.ae.model.PdrObject;
import org.bbaw.pdr.ae.model.Person;
import org.bbaw.pdr.ae.view.control.PDRObjectsProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Tree;
import org.osgi.framework.FrameworkUtil;
//TODO: doc
/**
*
* @author jhoeper
*
*/
@Deprecated
public class PdrSelectionPreview extends CheckboxTreeViewer
implements IPdrWidgetStructure{
/**
* Content provider for {@link PdrSelectionPreview}.
*
* <p>Relies heavily on {@link PdrObjectSelectionStructure}, where
* {@link #getChildren(Object)} and {@link #inputChanged(Viewer, Object, Object)}
* are handled with the help of a {@link PDRObjectsProvider} overall structure.
* </p>
*
* @author jhoeper
* @see #getContent()
* @see PdrObject
*/
@Deprecated
private class PdrPreviewContentProvider implements ITreeContentProvider{
protected PdrObjectSelectionStructure structure;
public PdrPreviewContentProvider() {
System.out.println(" setting up tree viewer content provider");
structure = new PdrObjectSelectionStructure();
}
public PdrObjectSelectionStructure getContent() {
return structure;
}
@Override
public void dispose() {
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
try {
PdrObject[] pdrObjects = (PdrObject[])newInput;
if (pdrObjects != null) {
this.structure.setInput(pdrObjects);
}
} catch (Exception e) {
System.out.println(" Tree Content Provider input update failed");
System.out.println(" "+e);
}
}
@Override
public Object[] getElements(Object inputElement) {
return this.structure.getObjects();
}
@Override
public Object[] getChildren(Object parentElement) {
PdrObject parentPdrObject = ((PdrObject)parentElement);
// System.out.println(" initiating search for child elements of: "+parentElement);
return this.structure.getChildren(parentPdrObject);
}
@Override
public Object getParent(Object element) {
return structure.getParent(element);
}
@Override
public boolean hasChildren(Object element) {
return ((PdrObject)element).getAspectIds().size() > 0;
}
}
/**
* Basic {@link LabelProvider} extension decorating {@link CheckboxTreeViewer}
* node elements with the display String of the represented {@link PdrObject}
* object and an Icon classifying it as either {@link Person} or {@link Aspect}.
* @author jhoeper
*
*/
@Deprecated
private class PdrPreviewLabelProvider extends LabelProvider {
//TODO: http://javawiki.sowas.com/doku.php?id=swt-jface:treetableviewer
@Override
public String getText(Object element) {
PdrObject pdrObject = (PdrObject)element;
return pdrObject.getDisplayNameWithID();
}
@Override
public Image getImage(Object element) {
PdrId pdrId = ((PdrObject)element).getPdrId();
if (pdrId.isValid()) {
String type = pdrId.getType();
if (type.equals("pdrPo"))
return AeExportCoreProvider.ICON_PDR_PERSON;
else if (type.endsWith("pdrAo"))
return AeExportCoreProvider.ICON_PDR_ASPECT;
}
return null;
}
}
/**
* This Listener basically just obverses which tree items are being
* selected and in what way a new selection is different from the one
* before.
* @author jhoeper
* @see PdrPreviewCheckStateListener
*/
@Deprecated
private class PdrPreviewSelectionListener implements
ISelectionChangedListener {
private StructuredSelection previous, current;
public PdrPreviewSelectionListener() {
addSelectionChangedListener(this);
previous = null;
current = new StructuredSelection();
}
@Override
public void selectionChanged(SelectionChangedEvent event) {
StructuredSelection arg = (StructuredSelection)event.getSelection();
previous = current;
current = arg;
}
/**
* Returns a presumption on weather it seems like a multi-item selection
* put together recently has collapsed even more recently due to a user
* interaction that probably was performed with the intention of keeping
* said selection.
* <p>The most likely use case to require this is that a user has
* selected multiple tree items and wants to change the <code>check
* state</code> value of every selected item simultaneously. Any action
* leading to an updated item check state will also result in a new
* single item selection, so {@link PdrPreviewCheckStateListener}
* probably will {@link #rewind()} the selection history in order to
* operate on every of the intentionally selected items.</p>
* @return <code>true</code> if the current selection seems to have
* replaced a larger one
*/
public boolean rewindable() {
return (current.size() == 1) &&
(previous.size() > 1) &&
(previous.toList().contains(current.getFirstElement()));
}
/**
* Backs up one step in selection history by restoring the very
* selection that has been replaced the most recently.
*/
public void rewind() {
setSelection(previous);
}
}
/**
* This listener keeps the hierarchic checking logic of the preview tree
* up to date. Whenever the check state of an item changes, the check states
* of related items are updated according to the following rules:
* <ul><li>If the item whose check state was changed is expandable, assign
* its check state to all its direct children</li>
* <li>If the item is representing an {@link Aspect}, and its check state
* has been set to <code>true</code>, make sure the parent gets checked
* as well in order to prevent <code>Aspect</code> items from ending up
* being selected for export with no {@link Person} alongside itself</li>
* <li>If multiple items are selected at once, have them all being of the
* check state that was changed</li></ul>
* <p>The last rule is enforced by asking {@link
* PdrPreviewSelectionListener} if the user's interaction that resulted in
* an updated check state might have destroyed a recently set up multi-item
* selection. If so, we assume that the user did actually intend to apply
* the outcome of their interaction to each item formerly assembled by the
* multi-item selection. To make this implicitly expected result come true,
* we {@link PdrPreviewSelectionListener#rewind()} the most recent selection
* history and proceed as specified above.</p>
* <p>Both <code>PdrPreviewCheckStateListener</code> and {@link
* PdrPreviewSelectionListener} auto-activate themselves as soon as they
* are being instantiated by {@link PdrSelectionPreview}</p>
*
* @author jhoeper
* @see PdrPreviewSelectionListener
*/
@Deprecated
private class PdrPreviewCheckStateListener implements ICheckStateListener {
public PdrPreviewCheckStateListener() {
addCheckStateListener(this);
}
public void checkStateChanged(CheckStateChangedEvent event) {
// if there is more than one item selected, assign the same
// checked status to all of them
if (selectionListener.rewindable()) {
selectionListener.rewind();
StructuredSelection selectedItems =
(StructuredSelection)getSelection();
for (Object obj : selectedItems.toArray())
setChecked(obj, event.getChecked());
}
// if object is expandable, meaning likely a person,
// check all related aspects according to check state
if (isExpandable(event.getElement())) {
expandToLevel(event.getElement(),
CheckboxTreeViewer.ALL_LEVELS);
setSubtreeChecked(event.getElement(),
event.getChecked());
}
if (event.getChecked()) {
// if checked object is an Aspect, check the person as well
PdrObject node = (PdrObject)event.getElement();
if (node instanceof Aspect) {
setChecked(((PdrPreviewContentProvider) getContentProvider()).
getParent(node), true);
setValid();
} else if (node instanceof Person)
setValid();
} else {
// check if at least one Pdr Person is checked in preview
// otherwise, preview contents are marked invalid
Object[] checkedElements = getCheckedElements();
if (checkedElements.length>0) {
for (Object chckObj : checkedElements)
if (chckObj instanceof Person) {
setValid();
return;
}
setInvalid("Please select at least one Person.");
} else {
setInvalid("Please select some content to export.");
}
}
}
}
//TODO Maybe extend widget in a way that export to multiple files at once is possible
/* In order to allow for choosing to export multiple pdr persons in multiple files
* at once, this class would then have to inform the core export plugin, which crafts
* the PdrObject selection representation used by extending plugins, about
* that choice, possibly via the local export provider.
*/
// PdrSelectionPreview private fields
private Tree tree;
ITreeContentProvider contentProvider;
LabelProvider labelProvider;
PdrPreviewSelectionListener selectionListener;
PdrPreviewCheckStateListener checkStateListener;
private boolean isvalid;
private String message;
private WizardPage wizardPage;
/**
* Creates a PDR Selection Preview on a newly-created tree under the given
* parent.
* <p>Explicitly intended to be used in a {@link GridLayout} environment.
* That means that the <code>parent</code> composite must provide such a
* layout.</p>
* <p>Standard layout data is applied using {@link SWT#FILL} and
* grabbing excess space flags both horizontally and vertically. <br/>
* Layout data may however be customized by using {@link #setLayoutData(int,
* int, boolean, boolean, int, int)} if specification row or column span
* is desired.</p>
* @param parent the parent Control with {@link GridLayout}
*/
public PdrSelectionPreview(WizardPage page, Composite parent) {
super(parent, SWT.BORDER | SWT.CHECK | SWT.MULTI | SWT.V_SCROLL);
this.wizardPage = page;
tree = getTree();
// bypass to prevent eclipse window builder from crashing
if (FrameworkUtil.getBundle(getClass()) != null) {
contentProvider = new PdrPreviewContentProvider();
labelProvider = new PdrPreviewLabelProvider();
setContentProvider(contentProvider);
setLabelProvider(labelProvider);
}
setLayoutData(SWT.FILL, SWT.FILL, true, true);
isvalid = true;
}
/**
* Sets a custom layout data for this tree viewer. Calling this method
* does the same as calling {@link Control#setLayoutData(Object)}.
* <p>In addition, minimum width and height are set as 150 and 100 pixels
* respectively, {@link Tree#setLinesVisible(boolean)} is set <code>true
* </code> and an {@link ICheckStateListener} is initialized for triggering
* shallow recursive checking of subtrees whenever a expandable node is
* checked and checking of the nodes of which at least one child node
* has been checked.</p>
* <p>Span numbers are optional. {@link #setLayoutData(int, int, boolean,
* boolean)} may be used as well.</p>
* @param horizontalAlignment {@link SWT} style for horizontal alignment
* @param verticalAlignment {@link SWT} style for vertical alignment
* @param grabExcessHorizontalSpace boolean space grabbing flag horizontal
* @param grabExcessVerticalSpace space grabbing flag vertical
* @param horizontalSpan number of columns to be used in gridlayout
* @param verticalSpan number of rows to be used in gridlayout
*/
public void setLayoutData(int horizontalAlignment, int verticalAlignment,
boolean grabExcessHorizontalSpace, boolean grabExcessVerticalSpace,
int horizontalSpan, int verticalSpan) {
GridData gd = new GridData(horizontalAlignment, verticalAlignment,
grabExcessHorizontalSpace, grabExcessVerticalSpace,
horizontalSpan, verticalSpan);
gd.minimumHeight = 100;
gd.minimumWidth = 150;
tree.setLayoutData(gd);
tree.setLinesVisible(true);
checkStateListener = new PdrPreviewCheckStateListener();
selectionListener = new PdrPreviewSelectionListener();
}
/**
* The same thing as {@link #setLayoutData(int, int, boolean, boolean,
* int, int)} except for that no column and row spans are used.
* @param horizontalAlignment {@link SWT} style for horizontal alignment
* @param verticalAlignment {@link SWT} style for vertical alignment
* @param grabExcessHorizontalSpace boolean space grabbing flag horizontal
* @param grabExcessVerticalSpace space grabbing flag vertical
* @see #setLayoutData(int, int, boolean, boolean, int, int)
*/
public void setLayoutData(int horizontalAlignment, int verticalAlignment,
boolean grabExcessHorizontalSpace, boolean grabExcessVerticalSpace) {
setLayoutData(horizontalAlignment, verticalAlignment, grabExcessHorizontalSpace,
grabExcessVerticalSpace, 1, 1);
}
/**
* Sets the input data for this tree viewer as an array of {@link PdrObject}
* elements. Each and every object is being 'checked' by default.
* @param input An array of {@link PdrObject}s
*/
public void setInput(PdrObject[] input) {
super.setInput(input);
for (PdrObject pdro : input) {
setChecked(pdro, true);
for (Object child :
((PdrPreviewContentProvider)getContentProvider()).
getChildren(pdro))
//TODO: represent aspect view selections?
setChecked(child, true);
}
}
/**
* returns an array of all objects represented by tree items whose check
* state is true, no matter if they are persons or aspects
* @return
*/
public PdrObject[] getSelectedObjects() {
Vector<PdrObject> checked = new Vector<PdrObject>();
PdrObject[] persons = ((PdrPreviewContentProvider)contentProvider).
getContent().getObjects();
for (PdrObject person : persons)
if (getChecked(person)) {
checked.add(person);
for (Object child : contentProvider.getChildren(person))
if (getChecked(child))
checked.add((PdrObject)child);
}
return checked.toArray(new PdrObject[checked.size()]);
}
/**
* Returns wheather the widget's contents are valid in a way that for
* instance export wizards can continue given its state.
* @return
*/
@Override
public boolean isValid() {
return isvalid;
}
/**
* Mark this widget invalid.
* @param message Complementary error message.
*/
private void setInvalid(String message) {
if (isvalid) if (message != null && !message.equals(this.message)){
System.out.println("Mark previewer as invalid");
isvalid = false;
this.message = message;
IWizardContainer wizardContainer = this.wizardPage.getWizard().getContainer();
wizardContainer.updateButtons();
wizardContainer.updateMessage();
}
}
/**
* Mark this widget valid
*/
private void setValid() {
if (!isvalid) {
isvalid = true;
this.message = null;
IWizardContainer wizardContainer = this.wizardPage.getWizard().getContainer();
wizardContainer.updateButtons();
wizardContainer.updateMessage();
}
}
@Override
public String getMessage() {
return message;
}
@Override
public void performFinish() {
}
}
/* setCheckStateProvider(new ICheckStateProvider(){
@Override
public boolean isChecked(Object element) {
return getChecked(element);
}
@Override
public boolean isGrayed(Object element) {
PdrObject pdrO = (PdrObject) element;
if (pdrO instanceof Person) {
Object[] children =
((PdrPreviewContentProvider) getContentProvider()).
getChildren(pdrO);
int checked = 0;
for (Object child : children)
if (getChecked(child))
checked++;
System.out.println("children checked out of: "+checked+"/"+children.length);
return (checked < children.length) |
(checked > 0);
}
return false;
}
});*/