package org.nightlabs.jfire.asterisk.ui.config;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxCellEditor;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.nightlabs.base.ui.labelprovider.ColumnSpanLabelProvider;
import org.nightlabs.base.ui.layout.WeightedTableLayout;
import org.nightlabs.base.ui.notification.IDirtyStateManager;
import org.nightlabs.base.ui.resource.SharedImages;
import org.nightlabs.base.ui.table.AbstractTableComposite;
import org.nightlabs.jfire.asterisk.AsteriskServer;
import org.nightlabs.jfire.asterisk.config.AsteriskConfigModule;
import org.nightlabs.jfire.asterisk.ui.ContactAsteriskPlugin;
import org.nightlabs.jfire.asterisk.ui.resource.Messages;
import org.nightlabs.util.Util;
/**
* @author Chairat Kongarayawetchakun <!-- chairat [AT] nightlabs [DOT] de -->
* @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
*/
public class CallFilePropertyCfModTable
extends AbstractTableComposite<String>
{
private static final Logger logger = Logger.getLogger(CallFilePropertyCfModTable.class);
private static final String KEY_COLUMN_ID = "property-key"; //$NON-NLS-1$
private static final String DEFAULT_VALUE_COLUMN_ID = "default-property-value"; //$NON-NLS-1$
private static final String OVERRIDDEN_COLUMN_ID = "overridden"; //$NON-NLS-1$
private static final String PROPERTY_VALUE_COLUMN_ID = "property-value"; //$NON-NLS-1$
private IDirtyStateManager dirtyStateManager;
private AsteriskConfigModule asteriskConfigModule;
private AsteriskServer asteriskServer;
/**
* @param parent
* @param style
*/
public CallFilePropertyCfModTable(Composite parent, IDirtyStateManager dirtyStateManager) {
this(parent, SWT.NONE, DEFAULT_STYLE_MULTI_BORDER, dirtyStateManager);
}
/**
* @param parent
* @param style
* @param initTable
*/
public CallFilePropertyCfModTable(Composite parent, int style, int viewerStyle, IDirtyStateManager dirtyStateManager) {
super(parent, style, true, viewerStyle);
this.dirtyStateManager = dirtyStateManager;
getTable().setHeaderVisible(true);
// emulatedNativeCheckBoxTableLabelProvider = new EmulatedNativeCheckBoxTableLabelProvider(getTableViewer()) {
// @Override
// public String getColumnText(Object element, int columnIndex) {
// throw new UnsupportedOperationException("This method should never be called!"); //$NON-NLS-1$
// }
// };
getTableViewer().setSorter(new ViewerSorter());
// hookContextMenu();
createContextMenu(getTableViewer().getControl());
}
// ------------------------------------------------------------------------------------- ++ ------------------------------->>
// Note: @Kai
// Since 2010.04.13, we now have the super class ContextMenuReadyXComposite (which the AbstractTableComposite now extends)
// to efficiently manage (priority-ordered) context-menus when needed (thru the method integratePriorityOrderedContextMenu()),
// which has been streamlined to handle 3 types of contextMenuContributions:
// (i) IContributionItem, (ii) IAction, and (iii) IViewActionDelegate.
// ------------------------------------------------------------------------------------- ++ ------------------------------->>
// private void hookContextMenu() {
// MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
// menuMgr.setRemoveAllWhenShown(true);
// menuMgr.addMenuListener(new IMenuListener() {
// public void menuAboutToShow(IMenuManager manager) {
// CallFilePropertyCfModTable.this.fillContextMenu(manager);
// }
// });
// Menu menu = menuMgr.createContextMenu(getTableViewer().getControl());
// getTableViewer().getControl().setMenu(menu);
//// if (getSite() != null)
//// getSite().registerContextMenu(menuMgr, getTableViewer());
// }
//
// /**
// * Contains instances of both, {@link IContributionItem} and {@link IAction}
// */
// private List<Object> contextMenuContributions;
//
// @Override
// public void addContextMenuContribution(IContributionItem contributionItem)
// {
// if (contextMenuContributions == null)
// contextMenuContributions = new LinkedList<Object>();
//
// contextMenuContributions.add(contributionItem);
// }
//
// @Override
// public void addContextMenuContribution(IAction action)
// {
// if (contextMenuContributions == null)
// contextMenuContributions = new LinkedList<Object>();
//
// contextMenuContributions.add(action);
// }
//
// @Override
// private void fillContextMenu(IMenuManager manager) {
// if (contextMenuContributions != null) {
// for (Object contextMenuContribution : contextMenuContributions) {
// if (contextMenuContribution instanceof IContributionItem)
// manager.add((IContributionItem)contextMenuContribution);
// else if (contextMenuContribution instanceof IAction)
// manager.add((IAction)contextMenuContribution);
// else
// throw new IllegalStateException("How the hell got an instance of " + (contextMenuContribution == null ? "null" : contextMenuContribution.getClass()) + " in the contextMenuContributions list?!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
// }
// }
//
// // Other plug-ins can contribute their actions here
// manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
// }
// ------------------------------------------------------------------------------------- ++ ------------------------------->>
@Override
protected void createTableColumns(TableViewer tableViewer, Table table) {
TableColumn tc;
tc = new TableColumn(table, SWT.LEFT); // @column 0
tc.setMoveable(true);
tc.setText(Messages.getString("org.nightlabs.jfire.asterisk.ui.config.CallFilePropertyCfModTable.columnHeader[key].text")); //$NON-NLS-1$
tc = new TableColumn(table, SWT.LEFT); // @column 1
tc.setMoveable(true);
tc.setText(Messages.getString("org.nightlabs.jfire.asterisk.ui.config.CallFilePropertyCfModTable.columnHeader[asteriskServerValue].text")); //$NON-NLS-1$
tc = new TableColumn(table, SWT.LEFT); // @column 2
tc.setMoveable(true);
tc.setText(Messages.getString("org.nightlabs.jfire.asterisk.ui.config.CallFilePropertyCfModTable.columnHeader[overridden].text")); //$NON-NLS-1$
tc = new TableColumn(table, SWT.LEFT); // @column 3
tc.setMoveable(true);
tc.setText(Messages.getString("org.nightlabs.jfire.asterisk.ui.config.CallFilePropertyCfModTable.columnHeader[value].text")); //$NON-NLS-1$
WeightedTableLayout layout = new WeightedTableLayout(new int[]{
20,
35,
10,
35});
table.setLayout(layout);
tableViewer.setColumnProperties(new String[] {KEY_COLUMN_ID,
DEFAULT_VALUE_COLUMN_ID,
OVERRIDDEN_COLUMN_ID,
PROPERTY_VALUE_COLUMN_ID});
tableViewer.setCellEditors(new CellEditor[] {
null,
null,
new CheckboxCellEditor(table),
new TextCellEditor(table)}
);
tableViewer.setCellModifier(new CallFilePropertyCellModifier());
}
@Override
protected void setTableProvider(TableViewer tableViewer) {
tableViewer.setContentProvider(new ArrayContentProvider());
tableViewer.setLabelProvider(new CallFilePropertyLabelProvider(tableViewer));
}
public void setConfigModule(AsteriskConfigModule configModule) {
super.setInput(null);
if (Display.getCurrent() != getDisplay())
throw new IllegalStateException("Thread mismatch! This method must be called on the SWT UI thread!");
this.asteriskConfigModule = configModule;
// setAsteriskServer(asteriskConfigModule.getAsteriskServer());
setAsteriskServer(asteriskServer);
}
public void setAsteriskServer(AsteriskServer asteriskServer) {
super.setInput(null);
if (Display.getCurrent() != getDisplay())
throw new IllegalStateException("Thread mismatch! This method must be called on the SWT UI thread!");
this.asteriskServer = asteriskServer;
if (asteriskServer == null || asteriskConfigModule == null) {
super.setInput(null);
return;
}
keys = collectKeys();
super.setInput(keys);
}
private Set<String> keys;
private Set<String> collectKeys()
{
Set<String> asteriskServerKeys = asteriskServer.getCallFileProperties().keySet();
Set<String> configModuleKeys = asteriskConfigModule.getCallFileProperties().keySet();
Set<String> overrideKeys = asteriskConfigModule.getOverrideCallFilePropertyKeys();
Set<String> keys = new HashSet<String>();
keys.addAll(asteriskServerKeys);
keys.addAll(configModuleKeys);
keys.addAll(overrideKeys); // in case a key is only in the overrides-set, we should still show it.
return keys;
}
private void updateKeys()
{
if (keys == null) // no input loaded yet
return;
Set<String> newKeys = collectKeys();
keys.retainAll(newKeys);
keys.addAll(newKeys);
}
@Override
public void refresh() {
updateKeys();
super.refresh();
}
@Override
public void refresh(boolean updateLabels) {
updateKeys();
super.refresh(updateLabels);
}
@Override
public void setInput(Object input) {
throw new IllegalArgumentException("This table should not be set with the input directly, use setConfigModule instead!"); //$NON-NLS-1$
}
// protected class CallFilePropertyContentProvider implements IStructuredContentProvider {
// @Override
// public Object[] getElements(Object inputElement) {
// if (inputElement instanceof AsteriskServer) {
// AsteriskServer asteriskServer = (AsteriskServer) inputElement;
// return CollectionUtil.collection2TypedArray(
// (Collection)(asteriskServer.getCallFileProperties().entrySet()),
// Map.Entry.class,
// false);
// Collection<Map.Entry<String, String>> c = asteriskServer.getCallFileProperties().entrySet();
// @SuppressWarnings("unchecked")
// Map.Entry<String, String>[] a = c.toArray(new Map.Entry[c.size()]);
// return a;
// }
// return null;
// }
//
// @Override
// public void dispose() {
//
// }
//
// @Override
// public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
//
// }
// }
// private EmulatedNativeCheckBoxTableLabelProvider emulatedNativeCheckBoxTableLabelProvider;
private class CallFilePropertyLabelProvider
extends ColumnSpanLabelProvider
{
// private final Image CHECKED = ContactAsteriskPlugin.getImageDescriptor(
// "icons/editor/asteriskserver/preference/checked.gif").createImage();
// private final Image UNCHECKED = ContactAsteriskPlugin.getImageDescriptor(
// "icons/editor/asteriskserver/preference/unchecked.gif").createImage();
public CallFilePropertyLabelProvider(ColumnViewer columnViewer) {
super(columnViewer);
}
@Override
public String getColumnText(Object element, int columnIndex)
{
String key = (String)element;
switch (columnIndex) {
case 0:
return key;
case 1:
if (asteriskServer == null)
return null;
else
return asteriskServer.getCallFileProperties().get(key);
case 2:
return null; //overriden column
case 3:
if (asteriskConfigModule == null)
return null;
else
return asteriskConfigModule.getCallFileProperties().get(key);
default: return "";//$NON-NLS-1$
}
}
@Override
protected int[][] getColumnSpan(Object element) {
return null;
}
/**
* Eclipse 3.3 doesn't provide the button for CheckboxCellEditor, so I have to handle it this way.
* See - http://www.eclipsezone.com/eclipse/forums/t110249.html
* http://tom-eclipse-dev.blogspot.com/2007/01/tableviewers-and-nativelooking.html
*/
@Override
public Image getColumnImage(Object element, int spanColIndex) {
String key = (String)element;
if (spanColIndex == 2) {
// @Yo: First, you should not use GIF files, but only PNG, because PNGs have a real alpha channel and look better.
// Second, our SharedImages utility automatically determines the correct path and thus minimizes the risk of
// inconsistent naming (e.g. due to refactorings). I changed the code to use the SharedImages class:
if (Boolean.valueOf(asteriskConfigModule.getOverrideCallFilePropertyKeys().contains(key))) {
return SharedImages.getSharedImage(
ContactAsteriskPlugin.getDefault(),
CallFilePropertyCfModTable.CallFilePropertyLabelProvider.class,
"override-checked" // We use this only here, thus we don't need a constant.
);
} else {
return SharedImages.getSharedImage(
ContactAsteriskPlugin.getDefault(),
CallFilePropertyCfModTable.CallFilePropertyLabelProvider.class,
"override-unchecked"
);
}
// Added 2009-12-01: Unfortunately, the below code doesn't work properly and often renders a bad image. I therefore
// had to switch to the above code using static pictures :-( Marco.
//
// However, the above code would cause the check-box to look the same on all systems, but
// it is nicer to use sth. that looks like a native check-box. Hence, we
// delegate to our emulated-native-check-box-drawing-tool ;-)
// Marco.
// return emulatedNativeCheckBoxTableLabelProvider.getCheckBoxImage(
// asteriskConfigModule.getOverrideCallFilePropertyKeys().contains(key)
// );
}
return null;
}
}
private class CallFilePropertyCellModifier implements ICellModifier {
@Override
public boolean canModify(Object element, String property) {
return (
property.equals(OVERRIDDEN_COLUMN_ID) ||
property.equals(PROPERTY_VALUE_COLUMN_ID)
);
}
@Override
public Object getValue(Object element, String property) {
String key = (String)element;
if(property.equals(OVERRIDDEN_COLUMN_ID)){
return Boolean.valueOf(asteriskConfigModule.getOverrideCallFilePropertyKeys().contains(key));
}
else if(property.equals(PROPERTY_VALUE_COLUMN_ID)){
String value = asteriskConfigModule.getCallFileProperties().get(key);
return value == null ? "" : value; //$NON-NLS-1$
}
return null;
}
@Override
public void modify(Object element, String property, Object value) {
if (element == null) {
logger.warn("element is null!!! property='" + property + "', value='" + value + "'", new IllegalStateException("element == null"));
// I think this happens, when the input is set while the data is edited. IMHO this happens when asynchronous load operations are still running
// while the user already clicks into the cell editor. The best way to handle this is not yet clear to me. For the moment, this warn-log
// and a silent return seems to be the best. Marco.
return;
}
if (!(property.equals(OVERRIDDEN_COLUMN_ID) ||
property.equals(PROPERTY_VALUE_COLUMN_ID))){
return;
}
TableItem tableItem = (TableItem)element;
String key = (String)tableItem.getData();
if (property.equals(OVERRIDDEN_COLUMN_ID)) {
boolean modified;
if (Boolean.TRUE.equals(value))
modified = asteriskConfigModule.addOverrideCallFilePropertyKey(key);
else
modified = asteriskConfigModule.removeOverrideCallFilePropertyKey(key);
if (!modified)
return; // no change => no mark dirty and no refresh
}
else if (property.equals(PROPERTY_VALUE_COLUMN_ID)) {
String oldValue = asteriskConfigModule.getCallFileProperties().get(key);
String newValue = (String) value;
if (newValue != null && newValue.isEmpty())
newValue = null; // we remove empty elements from the config-module
if (Util.equals(newValue, oldValue))
return; // no change => no mark dirty and no refresh
asteriskConfigModule.setCallFileProperty(key, newValue);
}
// getTableViewer().update(entry, new String[] { property });
getTableViewer().refresh();
dirtyStateManager.markDirty();
}
}
}