/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.viewers; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.RowData; import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.Section; import org.xmind.ui.forms.WidgetFactory; public abstract class CategorizedViewer extends StructuredViewer { private List<Object> categories = new ArrayList<Object>(); private Map<Object, List<Object>> categorizedElements = new HashMap<Object, List<Object>>(); private List<Composite> sections = new ArrayList<Composite>(); private ScrolledForm container = null; private WidgetFactory factory; private int sectionStyle = Section.TITLE_BAR | Section.TWISTIE | Section.EXPANDED; private Listener sectionContentListener = new Listener() { public void handleEvent(Event event) { if (event.type == SWT.MouseWheel) { scroll(event); } } }; public int getSectionStyle() { return sectionStyle; } public void setSectionStyle(int sectionStyle) { this.sectionStyle = sectionStyle; } protected Composite getContainer() { return container; } public void createControl(Composite parent, int style) { container = createContainer(parent, style); hookControl(container); } private ScrolledForm createContainer(Composite parent, int style) { if (factory == null) factory = new WidgetFactory(parent.getDisplay()); ScrolledForm form = factory.createScrolledForm(parent); configureForm(form); hookForm(form); return form; } private void configureForm(ScrolledForm form) { form.getBody().setLayout(createFormLayout()); form.setMinWidth(1); } private void hookForm(ScrolledForm form) { Listener eventHandler = new Listener() { public void handleEvent(Event event) { relayout(); } }; form.addListener(SWT.Resize, eventHandler); } private Layout createFormLayout() { RowLayout layout = new RowLayout(SWT.VERTICAL); layout.wrap = false; layout.fill = true; return layout; } private void relayout() { int width = getContainer().getClientArea().width; for (Composite section : sections) { resetWidth(width, section); } container.reflow(true); } private void resetWidth(int width, Control control) { Object ld = control.getLayoutData(); if (ld instanceof GridData) { GridData gd = (GridData) ld; GridLayout gl = (GridLayout) control.getParent().getLayout(); gd.widthHint = width - gl.marginWidth - gl.marginLeft - gl.marginRight; } else if (ld instanceof RowData) { RowData rd = (RowData) ld; RowLayout rl = (RowLayout) control.getParent().getLayout(); rd.width = width - rl.marginWidth - rl.marginLeft - rl.marginRight; } } protected List<Object> getCategories() { return categories; } protected List<Object> getElements(Object category) { return categorizedElements.get(category); } protected Composite getSection(Object category) { return sections.get(categories.indexOf(category)); } protected Widget doFindInputItem(Object element) { return getControl(); } protected Widget doFindItem(Object element) { if (getCategories().contains(element)) return getSection(element); return getControl(); } protected void inputChanged(Object input, Object oldInput) { super.inputChanged(input, oldInput); Map<Object, Boolean> expansionStates = new HashMap<Object, Boolean>(); for (Object category : getCategories()) { Composite section = getSection(category); if (section instanceof Section) { expansionStates.put(category, ((Section) section).isExpanded()); } } rebuildMap(getSortedChildren(input)); if (getContainer() != null && !getContainer().isDisposed()) { refreshControls(); for (Object category : getCategories()) { Boolean exp = expansionStates.get(category); if (exp != null) { Composite section = getSection(category); if (section instanceof Section) { ((Section) section).setExpanded(exp); } } } relayout(); } } private void rebuildMap(Object[] elements) { clearMap(); for (Object element : elements) { addElement(element); } Object[] array = null; ViewerFilter[] filters = getFilters(); if (filters != null && filters.length > 0) { array = categories.toArray(); for (ViewerFilter f : filters) { array = f.filter(this, getInput(), array); } } if (getSorter() != null) { if (array == null) array = categories.toArray(); getSorter().sort(this, array); } if (array != null) { categories = new ArrayList<Object>(Arrays.asList(array)); } } private void clearMap() { categories.clear(); categorizedElements.clear(); } private void addElement(Object element) { Object category = getCategory(element); if (category == null) return; List<Object> list = categorizedElements.get(category); if (list == null) { list = new ArrayList<Object>(); categorizedElements.put(category, list); categories.add(category); } list.add(element); } private Object getCategory(Object element) { Object category = null; if (getContentProvider() instanceof ICategorizedContentProvider) { category = ((ICategorizedContentProvider) getContentProvider()) .getCategory(element); } return category; } protected void refreshControls() { getContainer().setRedraw(false); for (Composite section : sections) { section.dispose(); } sections.clear(); Composite parent = container.getBody(); for (Object category : categories) { Composite section = createSection(parent, category); hookSection(section); sections.add(section); } getContainer().setRedraw(true); } protected void hookSection(Composite section) { } private void scroll(Event event) { if (container != null && !container.isDisposed()) { Point origin = container.getOrigin(); int count = event.count; ScrollBar vBar = container.getVerticalBar(); if (vBar != null && !vBar.isDisposed() && vBar.isVisible() && vBar.isEnabled()) { int increment = vBar.getIncrement(); int delta = count * increment; origin.y -= delta; } else { ScrollBar hBar = container.getHorizontalBar(); if (hBar != null && !hBar.isDisposed() && hBar.isVisible() && hBar.isEnabled()) { int increment = hBar.getIncrement(); int delta = count * increment; origin.x -= delta; } } container.setOrigin(origin); } } private Composite createSection(Composite parent, Object category) { Section section = factory.createSection(parent, getSectionStyle()); section.setLayoutData(getSectionLayoutData()); updateSection(category, section); Composite client = factory.createComposite(section, SWT.WRAP); client.setBackground(parent.getBackground()); section.setClient(client); StackLayout layout = new StackLayout(); client.setLayout(layout); Control content = createSectionContent(client, category, getElements(category)); layout.topControl = content; hookSectionContent(content); return section; } protected void hookSectionContent(Control content) { content.addListener(SWT.MouseWheel, sectionContentListener); } private Object getSectionLayoutData() { return new RowData(); } protected String getText(Object element) { if (getLabelProvider() instanceof ILabelProvider) { return ((ILabelProvider) getLabelProvider()).getText(element); } return element == null ? null : element.toString(); } protected Image getImage(Object element) { if (getLabelProvider() instanceof ILabelProvider) { return ((ILabelProvider) getLabelProvider()).getImage(element); } return null; } protected Color getForeground(Object element) { if (getLabelProvider() instanceof IColorProvider) { return ((IColorProvider) getLabelProvider()).getForeground(element); } return null; } protected Color getBackground(Object element) { if (getLabelProvider() instanceof IColorProvider) { return ((IColorProvider) getLabelProvider()).getBackground(element); } return null; } protected Control createSectionContent(Composite parent, Object category, List<Object> elements) { Composite content = factory.createComposite(parent, SWT.WRAP); content.setBackground(parent.getBackground()); content.setLayout(new GridLayout(1, true)); return content; } protected void doUpdateItem(Widget item, Object element, boolean fullMap) { if (getCategories().contains(element)) { updateSection(element, getSection(element)); } } protected void updateSection(Object category, Composite section) { section.setData(category); if (section instanceof Section) { Section s = (Section) section; String text = getText(category); if (text != null) s.setText(text); Color foreground = getForeground(category); if (foreground != null) s.setTitleBarForeground(foreground); Color background = getBackground(category); if (background != null) s.setTitleBarBackground(background); } } protected void internalRefresh(Object element) { if (getCategories().contains(element)) updateSection(element, getSection(element)); } public void reveal(Object element) { Object category = getCategory(element); reveal(category, element); } protected void reveal(Object category, Object element) { Composite section = getSection(category); if (section != null && section instanceof Section) { ((Section) section).setExpanded(true); Point loc = section.toDisplay(0, 0); reveal(loc.x, loc.y); } } protected void reveal(int x, int y) { Point loc = container.toControl(x, y); Point origin = container.getOrigin(); origin.x += loc.x; origin.y += loc.y; container.setOrigin(origin); } public Control getControl() { return container; } protected List getSelectionFromWidget() { List list = new ArrayList(); for (Object category : getCategories()) { fillSelection(category, list); } return list; } protected abstract void fillSelection(Object category, List selection); protected void setSelectionToWidget(List l, boolean reveal) { } protected void setSelectionToWidget(ISelection selection, boolean reveal) { for (Object category : getCategories()) { setSelectionToCategory(category, selection, reveal); } } protected abstract void setSelectionToCategory(Object category, ISelection selection, boolean reveal); protected void handleDispose(DisposeEvent event) { sections.clear(); clearMap(); if (factory != null) { factory.dispose(); factory = null; } super.handleDispose(event); } }