org.xmind.ui.properties.PropertiesEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.xmind.ui.properties.PropertiesEditor.java

Source

/* ******************************************************************************
 * 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.properties;

import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.IInputSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
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.Menu;
import org.xmind.ui.util.Chainability;
import org.xmind.ui.util.IChained;

public class PropertiesEditor implements IInputSelectionProvider {

    public static final String COLOR_BACKGROUND = "org.xmind.ui.color.PropertiesEditor.background"; //$NON-NLS-1$

    public static final String COLOR_CATEGORY_TITLE = "org.xmind.ui.color.PropertiesEditor.categoryTitle.foreground"; //$NON-NLS-1$

    public static final String COLOR_ENTRY_FOREGROUND = "org.xmind.ui.color.PropertiesEditor.entry.foreground"; //$NON-NLS-1$

    public static final String COLOR_ENTRY_SELECTED_BACKGROUND = "org.xmind.ui.color.PropertiesEditor.entry.selected.background"; //$NON-NLS-1$

    public static final String COLOR_ENTRY_SELECTED_FOREGROUND = "org.xmind.ui.color.PropertiesEditor.entry.selected.foreground"; //$NON-NLS-1$

    public static final String FONT_CATEGORY_TITLE = "org.xmind.ui.font.PropertiesEditor.categoryTitle"; //$NON-NLS-1$

    public static final String FONT_ENTRY = "org.xmind.ui.font.PropertiesEditor.entry"; //$NON-NLS-1$

    public static final String FONT_ENTRY_SELECTED = "org.xmind.ui.font.PropertiesEditor.entry.selected"; //$NON-NLS-1$

    private static final class Section implements PropertyChangeListener, IChained<Section> {

        private final PropertiesEditor editor;

        private final String title;

        private PropertyEditingEntry firstEntry = null;

        private PropertyEditingEntry lastEntry = null;

        private PropertyEditingSection section = null;

        private Section prev = null;

        private Section next = null;

        public Section(PropertiesEditor editor, String title) {
            this.editor = editor;
            this.title = title;
        }

        public void create(Composite parent) {
            section = new PropertyEditingSection(parent);
            section.setTitleText(title == null ? "" : title); //$NON-NLS-1$
            section.getEventSupport().addPropertyChangeListener(this);

            Composite client = section.getClient();
            GridLayout gridLayout = new GridLayout(1, true);
            gridLayout.marginWidth = 5;
            gridLayout.marginHeight = 5;
            gridLayout.verticalSpacing = 3;
            gridLayout.horizontalSpacing = 0;
            client.setLayout(gridLayout);

            Iterator<PropertyEditingEntry> it = entries();
            while (it.hasNext()) {
                PropertyEditingEntry entry = it.next();
                entry.createControl(client);
                GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
                gridData.widthHint = SWT.DEFAULT;
                gridData.heightHint = SWT.DEFAULT;
                entry.getControl().setLayoutData(gridData);
            }
        }

        public void dispose() {
            Iterator<PropertyEditingEntry> it = entries();
            while (it.hasNext()) {
                it.next().dispose();
            }
            if (section != null) {
                section.getEventSupport().removePropertyChangeListener(this);
                if (section.getControl() != null && !section.getControl().isDisposed())
                    section.getControl().dispose();
                section = null;
            }
            firstEntry = null;
            lastEntry = null;
        }

        public Iterator<PropertyEditingEntry> entries() {
            return Chainability.iterate(firstEntry, lastEntry);
        }

        public void propertyChange(java.beans.PropertyChangeEvent evt) {
            if (PropertyEditingSection.PROP_EXPANDED.equals(evt.getPropertyName())) {
                editor.reflow();
            }
        }

        public Section getPrevious() {
            return prev;
        }

        public Section getNext() {
            return next;
        }

        public void setPrevious(Section element) {
            this.prev = element;
        }

        public void setNext(Section element) {
            this.next = element;
        }

    }

    private ScrolledComposite container = null;

    private IPropertySource source = null;

    private Map<String, Section> sectionRegistry = new HashMap<String, Section>();

    private Map<String, PropertyEditingEntry> entryRegistry = new HashMap<String, PropertyEditingEntry>();

    private Map<String, String> colorFontOverrides = new HashMap<String, String>();

    private boolean reflowing = false;

    private Section firstSection = null;

    private Section lastSection = null;

    private PropertyEditingEntry selectedEntry = null;

    private ListenerList listeners = new ListenerList();

    private Menu popupMenu = null;

    private IPropertyTransfer transfer = null;

    private List<String> filtedDescriptorIds = new ArrayList<String>();

    public PropertiesEditor() {
        super();
    }

    public Object getInput() {
        return source;
    }

    public void setInput(IPropertySource source) {
        IPropertySource oldSource = this.source;
        this.source = source;
        if (oldSource != source) {
            refresh();
        }
    }

    public ISelection getSelection() {
        if (selectedEntry == null)
            return StructuredSelection.EMPTY;
        return new StructuredSelection(selectedEntry);
    }

    public void setSelection(ISelection selection) {
    }

    public void addSelectionChangedListener(ISelectionChangedListener listener) {
        listeners.add(listener);
    }

    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
        listeners.remove(listener);
    }

    public void create(Composite parent) {
        Assert.isTrue(container == null);
        container = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
        container.setExpandHorizontal(true);
        container.setExpandVertical(true);
        container.setMinWidth(200);
        container.setMinHeight(40);
        container.addControlListener(new ControlListener() {

            public void controlResized(ControlEvent e) {
                reflow();
            }

            public void controlMoved(ControlEvent e) {
                reflow();
            }
        });

        Composite contents = new Composite(container, SWT.NONE);
        GridLayout gridLayout = new GridLayout(1, false);
        gridLayout.marginWidth = 3;
        gridLayout.marginHeight = 3;
        gridLayout.verticalSpacing = 3;
        gridLayout.horizontalSpacing = 0;
        contents.setLayout(gridLayout);

        container.setContent(contents);
        initColorsFonts();
        refresh();
    }

    public void setColorFontOverrides(String id, String overridedId) {
        if (overridedId == null) {
            colorFontOverrides.remove(id);
        } else {
            colorFontOverrides.put(id, overridedId);
        }
    }

    private String getColorFontId(String id) {
        String overridedId = colorFontOverrides.get(id);
        if (overridedId != null)
            return overridedId;
        return id;
    }

    private Composite getContents() {
        return (Composite) container.getContent();
    }

    private void initColorsFonts() {
        final IPropertyChangeListener colorChangeListener = new IPropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent event) {
                String colorId = event.getProperty();
                if (getColorFontId(COLOR_BACKGROUND).equals(colorId)) {
                    updateBackgroundColor();
                } else if (getColorFontId(COLOR_CATEGORY_TITLE).equals(colorId)) {
                    updateCategoryTitlesColor();
                } else if (getColorFontId(COLOR_ENTRY_FOREGROUND).equals(colorId)) {
                    updateEntriesForegroundColor();
                } else if (getColorFontId(COLOR_ENTRY_SELECTED_BACKGROUND).equals(colorId)) {
                    updateEntriesSelectedBackgroundColor();
                } else if (getColorFontId(COLOR_ENTRY_SELECTED_FOREGROUND).equals(colorId)) {
                    updateEntriesSelectedForegroundColor();
                }
            }
        };

        final IPropertyChangeListener fontChangeListener = new IPropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent event) {
                String fontId = event.getProperty();
                if (getColorFontId(FONT_CATEGORY_TITLE).equals(fontId)) {
                    updateCategoryTitlesFont();
                } else if (getColorFontId(FONT_ENTRY).equals(fontId)) {
                    updateEntriesFont();
                } else if (getColorFontId(FONT_ENTRY_SELECTED).equals(fontId)) {
                    updateEntriesSelectedFont();
                }
            }
        };

        JFaceResources.getColorRegistry().addListener(colorChangeListener);
        JFaceResources.getFontRegistry().addListener(fontChangeListener);
        container.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                JFaceResources.getColorRegistry().removeListener(colorChangeListener);
                JFaceResources.getFontRegistry().removeListener(fontChangeListener);
            }
        });

        updateColorsFonts();
    }

    protected void updateColorsFonts() {
        updateBackgroundColor();
        updateCategoryTitlesColor();
        updateCategoryTitlesFont();
        updateEntriesForegroundColor();
        updateEntriesFont();
        updateEntriesSelectedBackgroundColor();
        updateEntriesSelectedForegroundColor();
        updateEntriesSelectedFont();
    }

    private void updateBackgroundColor() {
        Color color = JFaceResources.getColorRegistry().get(getColorFontId(COLOR_BACKGROUND));
        container.setBackground(color);
        getContents().setBackground(color);
        Iterator<Section> sections = sections();
        while (sections.hasNext()) {
            Section section = sections.next();
            if (section.section != null)
                section.section.setBackground(color);
        }
    }

    private void updateCategoryTitlesColor() {
        Color color = JFaceResources.getColorRegistry().get(getColorFontId(COLOR_CATEGORY_TITLE));
        Iterator<Section> sections = sections();
        while (sections.hasNext()) {
            Section section = sections.next();
            if (section.section != null) {
                section.section.setTitleColor(color);
            }
        }
    }

    private void updateEntriesForegroundColor() {
        Color color = JFaceResources.getColorRegistry().get(getColorFontId(COLOR_ENTRY_FOREGROUND));
        Iterator<PropertyEditingEntry> entries = entries();
        while (entries.hasNext()) {
            entries.next().setForeground(color);
        }
    }

    private void updateCategoryTitlesFont() {
        Font font = JFaceResources.getFontRegistry().get(getColorFontId(FONT_CATEGORY_TITLE));
        Iterator<Section> sections = sections();
        while (sections.hasNext()) {
            Section section = sections.next();
            if (section.section != null) {
                section.section.setTitleFont(font);
            }
        }
    }

    private void updateEntriesFont() {
        Font font = JFaceResources.getFontRegistry().get(getColorFontId(FONT_ENTRY));
        Iterator<PropertyEditingEntry> entries = entries();
        while (entries.hasNext()) {
            entries.next().setFont(font);
        }
    }

    private void updateEntriesSelectedBackgroundColor() {
        Color color = JFaceResources.getColorRegistry().get(getColorFontId(COLOR_ENTRY_SELECTED_BACKGROUND));
        Iterator<PropertyEditingEntry> entries = entries();
        while (entries.hasNext()) {
            entries.next().setSelectedBackground(color);
        }
    }

    private void updateEntriesSelectedForegroundColor() {
        Color color = JFaceResources.getColorRegistry().get(getColorFontId(COLOR_ENTRY_SELECTED_FOREGROUND));
        Iterator<PropertyEditingEntry> entries = entries();
        while (entries.hasNext()) {
            entries.next().setSelectedForeground(color);
        }
    }

    private void updateEntriesSelectedFont() {
        Font font = JFaceResources.getFontRegistry().get(getColorFontId(FONT_ENTRY_SELECTED));
        Iterator<PropertyEditingEntry> entries = entries();
        while (entries.hasNext()) {
            entries.next().setSelectedFont(font);
        }
    }

    protected Iterator<PropertyEditingEntry> entries() {
        return Chainability.iterate(firstSection == null ? null : firstSection.firstEntry, null);
    }

    protected Iterator<Section> sections() {
        return Chainability.iterate(firstSection, lastSection);
    }

    public void setFilter(List<String> filtedDescriptorIds) {
        this.filtedDescriptorIds = filtedDescriptorIds;
    }

    public void refresh() {
        if (container == null || container.isDisposed())
            return;

        container.setRedraw(false);
        try {
            // Remove all sections and entries:
            Iterator<Section> sectionIt = sections();
            while (sectionIt.hasNext()) {
                sectionIt.next().dispose();
            }
            firstSection = null;
            lastSection = null;
            selectedEntry = null;
            sectionRegistry.clear();
            entryRegistry.clear();
            Control[] controls = getContents().getChildren();
            for (int i = 0; i < controls.length; i++) {
                controls[i].dispose();
            }

            if (source != null) {
                IPropertyDescriptor[] descs = source.getPropertyDescriptors();
                for (int i = 0; i < descs.length; i++) {
                    IPropertyDescriptor descriptor = descs[i];
                    if (filtedDescriptorIds != null && filtedDescriptorIds.contains(descriptor.getId()))
                        continue;
                    addEditingEntry(descriptor);
                }
            }

            createSectionControls();

            updateColorsFonts();
            reflow();
        } finally {
            container.setRedraw(true);
        }
    }

    public void updateAll() {
        Iterator<PropertyEditingEntry> entries = entries();
        while (entries.hasNext()) {
            entries.next().update();
        }
    }

    public void update(String propertyId) {
        PropertyEditingEntry entry = entryRegistry.get(propertyId);
        if (entry != null) {
            entry.update();
        }
    }

    protected void addEditingEntry(IPropertyDescriptor descriptor) {
        PropertyEditingEntry entry = new PropertyEditingEntry(this, source, descriptor);
        entryRegistry.put(descriptor.getId(), entry);

        String category = descriptor.getCategory();
        Section section = sectionRegistry.get(category);
        if (section == null) {
            section = new Section(this, category);
            sectionRegistry.put(category, section);
            if (firstSection == null || lastSection == null) {
                firstSection = section;
            } else {
                Chainability.insertAfter(lastSection, section);
            }
            lastSection = section;
        }
        if (section.firstEntry == null || section.lastEntry == null) {
            section.firstEntry = entry;
            if (section.getPrevious() != null) {
                Chainability.insertAfter(section.getPrevious().lastEntry, entry);
            }
        } else {
            Chainability.insertAfter(section.lastEntry, entry);
        }
        section.lastEntry = entry;
    }

    private void createSectionControls() {
        if (firstSection == null)
            return;

        Composite parent = getContents();
        if (firstSection.getNext() == null && (firstSection.title == null || "".equals(firstSection.title))) { //$NON-NLS-1$
            Iterator<PropertyEditingEntry> it = firstSection.entries();
            while (it.hasNext()) {
                PropertyEditingEntry entry = it.next();
                entry.createControl(parent);
                entry.getControl().setLayoutData(createSectionLayoutData());
            }
        } else {
            Iterator<Section> sectionIt = sections();
            while (sectionIt.hasNext()) {
                Section section = sectionIt.next();
                section.create(parent);
                if (section.section != null) {
                    section.section.getControl().setLayoutData(createSectionLayoutData());
                }
            }
        }

        if (popupMenu != null) {
            Iterator<PropertyEditingEntry> entries = entries();
            while (entries.hasNext()) {
                entries.next().setPopupMenu(popupMenu);
            }
        }
    }

    private GridData createSectionLayoutData() {
        GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        gridData.widthHint = SWT.DEFAULT;
        gridData.heightHint = SWT.DEFAULT;
        return gridData;
    }

    public Control getControl() {
        return container;
    }

    public void setFocus() {
        if (selectedEntry != null) {
            selectedEntry.setFocus();
        } else if (firstSection != null && firstSection.firstEntry != null) {
            firstSection.firstEntry.setFocus();
        } else if (container != null && !container.isDisposed()) {
            container.setFocus();
        }
    }

    public void reflow() {
        if (container == null || container.isDisposed())
            return;
        if (reflowing)
            return;
        reflowing = true;
        container.getDisplay().asyncExec(new Runnable() {
            public void run() {
                try {
                    if (container != null && !container.isDisposed()) {
                        container.layout(true, true);
                        container.setMinHeight(
                                container.getContent().computeSize(container.getClientArea().width, SWT.DEFAULT).y);
                    }
                } finally {
                    reflowing = false;
                }
            }
        });
    }

    public void select(String propertyId) {
        PropertyEditingEntry entry = entryRegistry.get(propertyId);
        if (entry != null) {
            select(entry);
        }
    }

    protected boolean select(PropertyEditingEntry entry) {
        if (entry == null)
            return false;

        PropertyEditingEntry oldEntry = this.selectedEntry;
        this.selectedEntry = entry;
        if (oldEntry != null) {
            oldEntry.setSelected(false);
        }
        entry.setSelected(true);
        entry.setFocus();
        reveal(entry.getControl());
        fireSelectionChanged();
        return true;
    }

    private void reveal(Control control) {
        if (container != null && !container.isDisposed())
            container.showControl(control);
    }

    protected void fireSelectionChanged() {
        ISelection selection = getSelection();
        for (Object listener : listeners.getListeners()) {
            if (listener instanceof ISelectionChangedListener) {
                ((ISelectionChangedListener) listener).selectionChanged(new SelectionChangedEvent(this, selection));
            }
        }
    }

    public void setPopupMenu(Menu menu) {
        this.popupMenu = menu;
        Iterator<PropertyEditingEntry> entries = entries();
        while (entries.hasNext()) {
            entries.next().setPopupMenu(menu);
        }
        if (container != null && !container.isDisposed()) {
            getContents().setMenu(menu);
        }
    }

    public void dispose() {
        Iterator<Section> sectionIt = sections();
        while (sectionIt.hasNext()) {
            sectionIt.next().dispose();
        }
        sectionRegistry.clear();
        firstSection = null;
        lastSection = null;
        source = null;
        if (container != null && !container.isDisposed()) {
            container.dispose();
        }
        container = null;
    }

    /**
     * @return the transfer
     */
    public IPropertyTransfer getTransfer() {
        return transfer;
    }

    /**
     * @param transfer
     *            the transfer to set
     */
    public void setTransfer(IPropertyTransfer transfer) {
        this.transfer = transfer;
    }

}