/*******************************************************************************
* Copyright (c) 2012 xored software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* xored software, Inc. - initial API and implementation (Yuri Strot)
******************************************************************************/
package com.xored.glance.ui.controls.decor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Item;
import com.xored.glance.ui.sources.ColorManager;
import com.xored.glance.ui.sources.ITextBlock;
import com.xored.glance.ui.sources.ITextSource;
import com.xored.glance.ui.sources.ITextSourceListener;
import com.xored.glance.ui.sources.Match;
import com.xored.glance.ui.sources.SourceSelection;
import com.xored.glance.ui.utils.TextUtils;
public abstract class StructSource implements ITextSource, IStructProvider,
SelectionListener {
private final StructDecorator decorator;
private final Composite composite;
protected IStructContent content;
public StructSource(final Composite composite) {
this.composite = composite;
content = createContent();
decorator = new StructDecorator(composite, this);
}
public StructCell getCell(final Item item, final int column) {
final StructCell cell = createCell(item, column);
StyleRange[] styles = cellToStyles.get(cell);
if (styles == null) {
styles = calcStyles(cell);
cellToStyles.put(cell, styles);
}
cell.styles = styles;
return cell;
}
public boolean isIndexRequired() {
return true;
}
private StyleRange[] calcStyles(final StructCell cell) {
final ITextBlock block = content.getContent(cell);
blockToCell.put(block, cell);
final StyleRange[] cellStyles = cell.nativeStyles();
final StyleRange[] blockStyles = createStyles(block);
if (blockStyles == null || blockStyles.length == 0)
return cellStyles;
if (cellStyles.length == 0)
return blockStyles;
final Region region = new Region(0, cell.getText().length());
final int size = cellStyles.length + blockStyles.length;
final TextPresentation presentation = new TextPresentation(region, size);
presentation.replaceStyleRanges(cellStyles);
presentation.mergeStyleRanges(blockStyles);
return TextUtils.getStyles(presentation);
}
protected abstract StructCell createCell(Item item, int column);
protected abstract IStructContent createContent();
protected abstract SourceSelection getSourceSelection();
/**
* @return the composite
*/
public Composite getControl() {
return composite;
}
public ITextBlock[] getBlocks() {
return content.getBlocks();
}
public void dispose() {
content.dispose();
decorator.dispose();
}
public boolean isDisposed() {
return decorator.isDisposed();
}
public void index(final IProgressMonitor monitor) {
content.index(monitor);
}
public void widgetDefaultSelected(final SelectionEvent e) {
fireSelectionChanged();
}
public void widgetSelected(final SelectionEvent e) {
fireSelectionChanged();
}
protected void fireSelectionChanged() {
discardSelection();
final SourceSelection selection = getSourceSelection();
final ITextSourceListener[] listeners = content.getListeners();
for (final ITextSourceListener listener : listeners) {
listener.selectionChanged(selection);
}
}
public void select(final Match match) {
if (match != null) {
discardSelection();
path = content.getPath(match.getBlock());
path.select(getControl());
}
setMatch(match);
}
public void show(final Match[] matches) {
setMatches(matches);
}
public void addTextSourceListener(final ITextSourceListener listener) {
content.addListener(listener);
}
public void removeTextSourceListener(final ITextSourceListener listener) {
content.removeListener(listener);
}
private void discardSelection() {
if (path != null) {
path.discardSelection();
path = null;
}
}
private void setMatch(final Match match) {
final StructCell oldSel = updateSelection(false);
selection = match;
final StructCell newSel = updateSelection(true);
if (oldSel != null) {
decorator.redraw(oldSel);
}
if (newSel != null && newSel != oldSel) {
decorator.redraw(newSel);
}
}
private StructCell updateSelection(final boolean add) {
if (selection != null) {
final ITextBlock block = selection.getBlock();
final StructCell cell = blockToCell.get(block);
if (cell != null) {
cellToStyles.remove(cell);
return cell;
}
}
return null;
}
private void setMatches(final Match[] matches) {
init();
for (final Match match : matches) {
final ITextBlock block = match.getBlock();
List<Match> list = blockToMatches.get(block);
if (list == null) {
list = new ArrayList<Match>();
blockToMatches.put(block, list);
}
list.add(match);
}
decorator.redraw();
}
private StyleRange[] createStyles(final ITextBlock block) {
final List<StyleRange> list = new ArrayList<StyleRange>();
boolean selectionFound = false;
final List<Match> matches = blockToMatches.get(block);
if (matches != null) {
for (final Match match : matches) {
final boolean sel = match.equals(selection);
selectionFound = selectionFound | sel;
final StyleRange range = createStyle(match, sel);
list.add(range);
}
}
if (!selectionFound && selection != null) {
final ITextBlock sBlock = selection.getBlock();
if (sBlock.equals(block)) {
list.add(createStyle(selection, true));
}
}
return list.toArray(new StyleRange[list.size()]);
}
private StyleRange createStyle(final Match match, final boolean selection) {
final Display display = composite.getDisplay();
Color fgColor, bgColor;
if (selection) {
// Lighten to avoid search selection on system selection
fgColor = ColorManager.lighten(display
.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT), 50);
bgColor = ColorManager.getInstance().getSelectedBackgroundColor();
} else {
// To avoid white text on light-green background
fgColor = display.getSystemColor(SWT.COLOR_BLACK);
bgColor = ColorManager.getInstance().getBackgroundColor();
}
return new StyleRange(match.getOffset(), match.getLength(), fgColor,
bgColor);
}
public SourceSelection getSelection() {
return null;
}
public void init() {
blockToMatches = new HashMap<ITextBlock, List<Match>>();
blockToCell = new HashMap<ITextBlock, StructCell>();
cellToStyles = new HashMap<StructCell, StyleRange[]>();
}
private Match selection;
private IPath path;
private Map<ITextBlock, List<Match>> blockToMatches;
private Map<ITextBlock, StructCell> blockToCell;
private Map<StructCell, StyleRange[]> cellToStyles;
}