/*******************************************************************************
 * Copyright (c) 2000, 2018 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal.editors.text;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.ibm.icu.text.Collator;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;

import org.eclipse.core.runtime.Assert;

import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.preference.ColorSelector;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;

import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.editors.text.OverlayPreferenceStore.OverlayKey;

import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;


/**
 * Configures Annotation preferences.
 *
 * @since 3.0
 */
class AnnotationsConfigurationBlock implements IPreferenceConfigurationBlock {
	private static final class ListItem {
		final String label;
		final Image image;
		final String colorKey;
		final String highlightKey;
		final String overviewRulerKey;
		final String textStyleKey;
		final String textKey;
		final String verticalRulerKey;

		final String isNextPreviousNavigationKey;

		ListItem(String label, Image image, String colorKey, String textKey, String overviewRulerKey, String highlightKey, String verticalRulerKey, String textStyleKey, String navigationKey) {
			this.label= label;
			this.image= image;
			this.colorKey= colorKey;
			this.highlightKey= highlightKey;
			this.overviewRulerKey= overviewRulerKey;
			this.textKey= textKey;
			this.textStyleKey= textStyleKey;
			this.verticalRulerKey= verticalRulerKey;
			this.isNextPreviousNavigationKey= navigationKey;
		}
	}

	private static final class ItemContentProvider implements IStructuredContentProvider {

		@Override
		public Object[] getElements(Object inputElement) {
			return (ListItem[]) inputElement;
		}

		@Override
		public void dispose() {
		}

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}
	}


	private final class ItemLabelProvider extends LabelProvider {

		@Override
		public String getText(Object element) {
			return ((ListItem)element).label;
		}

		@Override
		public Image getImage(Object element) {
			ListItem item= (ListItem)element;
			return item.image;
		}
	}


	private static class ArrayLabelProvider extends LabelProvider {
		@Override
		public String getText(Object element) {
			return ((String[]) element)[0].toString();
		}
	}

	final static String[] HIGHLIGHT= new String[] {TextEditorMessages.AnnotationsConfigurationBlock_HIGHLIGHT, "not used"}; //$NON-NLS-1$
	final static String[] UNDERLINE= new String[] {TextEditorMessages.AnnotationsConfigurationBlock_UNDERLINE, AnnotationPreference.STYLE_UNDERLINE};
	final static String[] BOX= new String[] {TextEditorMessages.AnnotationsConfigurationBlock_BOX, AnnotationPreference.STYLE_BOX};
	final static String[] DASHED_BOX= new String[] {TextEditorMessages.AnnotationsConfigurationBlock_DASHED_BOX, AnnotationPreference.STYLE_DASHED_BOX};
	final static String[] IBEAM= new String[] {TextEditorMessages.AnnotationsConfigurationBlock_IBEAM, AnnotationPreference.STYLE_IBEAM};
	final static String[] SQUIGGLES= new String[] {TextEditorMessages.AnnotationsConfigurationBlock_SQUIGGLES, AnnotationPreference.STYLE_SQUIGGLES};
	final static String[] PROBLEM_UNDERLINE= new String[] { TextEditorMessages.AnnotationsConfigurationBlock_PROBLEM_UNDERLINE, AnnotationPreference.STYLE_PROBLEM_UNDERLINE };



	private OverlayPreferenceStore fStore;
	private ColorSelector fAnnotationForegroundColorEditor;

	private Button fShowInTextCheckBox;
	private Button fShowInOverviewRulerCheckBox;
	private Button fShowInVerticalRulerCheckBox;

	private Button fIsNextPreviousTargetCheckBox;

	private StructuredViewer fAnnotationTypeViewer;
	private final ListItem[] fListModel;

	private ComboViewer fDecorationViewer;
	private final Set<String> fImageKeys= new HashSet<>();

	public AnnotationsConfigurationBlock(OverlayPreferenceStore store) {
		Assert.isNotNull(store);
		MarkerAnnotationPreferences markerAnnotationPreferences= EditorsPlugin.getDefault().getMarkerAnnotationPreferences();
		fStore= store;
		fStore.addKeys(createOverlayStoreKeys(markerAnnotationPreferences));
		fListModel= createAnnotationTypeListModel(markerAnnotationPreferences);
	}

	private OverlayPreferenceStore.OverlayKey[] createOverlayStoreKeys(MarkerAnnotationPreferences preferences) {

		ArrayList<OverlayKey> overlayKeys= new ArrayList<>();
		Iterator<AnnotationPreference> e= preferences.getAnnotationPreferences().iterator();

		while (e.hasNext()) {
			AnnotationPreference info= e.next();
			overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, info.getColorPreferenceKey()));
			overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, info.getTextPreferenceKey()));
			if (info.getHighlightPreferenceKey() != null)
				overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, info.getHighlightPreferenceKey()));
			overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, info.getOverviewRulerPreferenceKey()));
			if (info.getVerticalRulerPreferenceKey() != null)
				overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, info.getVerticalRulerPreferenceKey()));
			if (info.getTextStylePreferenceKey() != null)
				overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, info.getTextStylePreferenceKey()));
			if (info.getIsGoToNextNavigationTargetKey() != null)
				overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, info.getIsGoToNextNavigationTargetKey()));
		}
		OverlayPreferenceStore.OverlayKey[] keys= new OverlayPreferenceStore.OverlayKey[overlayKeys.size()];
		overlayKeys.toArray(keys);
		return keys;
	}

	@Override
	public Control createControl(Composite parent) {

		PixelConverter pixelConverter= new PixelConverter(parent);

		Composite composite= new Composite(parent, SWT.NULL);
		GridLayout layout= new GridLayout();
		layout.numColumns= 2;
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		composite.setLayout(layout);

		Label label= new Label(composite, SWT.LEFT);
		label.setText(TextEditorMessages.AnnotationsConfigurationBlock_annotationPresentationOptions);
		GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
		gd.horizontalSpan= 2;
		label.setLayoutData(gd);

		Composite editorComposite= new Composite(composite, SWT.NONE);
		layout= new GridLayout();
		layout.numColumns= 2;
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		editorComposite.setLayout(layout);
		gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.FILL_VERTICAL);
		gd.horizontalSpan= 2;
		editorComposite.setLayoutData(gd);

		fAnnotationTypeViewer= new TableViewer(editorComposite, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
		fAnnotationTypeViewer.setLabelProvider(new ItemLabelProvider());
		fAnnotationTypeViewer.setContentProvider(new ItemContentProvider());
		gd= new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false);
		gd.heightHint= pixelConverter.convertHeightInCharsToPixels(20);
		fAnnotationTypeViewer.getControl().setLayoutData(gd);

		Composite optionsComposite= new Composite(editorComposite, SWT.NONE);
		layout= new GridLayout();
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		layout.numColumns= 2;
		optionsComposite.setLayout(layout);
		optionsComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

		label= new Label(optionsComposite, SWT.LEFT);
		label.setText(TextEditorMessages.AnnotationsConfigurationBlock_labels_showIn);
		gd= new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalAlignment= GridData.BEGINNING;
		gd.horizontalSpan= 2;
		label.setLayoutData(gd);

		fShowInVerticalRulerCheckBox= new Button(optionsComposite, SWT.CHECK);
		fShowInVerticalRulerCheckBox.setText(TextEditorMessages.AnnotationsConfigurationBlock_showInVerticalRuler);
		gd= new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalAlignment= GridData.BEGINNING;
		gd.horizontalSpan= 2;
		gd.horizontalIndent= 20;
		fShowInVerticalRulerCheckBox.setLayoutData(gd);

		fShowInOverviewRulerCheckBox= new Button(optionsComposite, SWT.CHECK);
		fShowInOverviewRulerCheckBox.setText(TextEditorMessages.AnnotationsConfigurationBlock_showInOverviewRuler);
		gd= new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalAlignment= GridData.BEGINNING;
		gd.horizontalSpan= 2;
		gd.horizontalIndent= 20;
		fShowInOverviewRulerCheckBox.setLayoutData(gd);

		fShowInTextCheckBox= new Button(optionsComposite, SWT.CHECK);
		fShowInTextCheckBox.setText(TextEditorMessages.AnnotationsConfigurationBlock_showInText);
		gd= new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalAlignment= GridData.BEGINNING;
		gd.horizontalIndent= 20;
		fShowInTextCheckBox.setLayoutData(gd);


		fDecorationViewer= new ComboViewer(optionsComposite, SWT.READ_ONLY);
		fDecorationViewer.setContentProvider(new ArrayContentProvider());
		fDecorationViewer.setLabelProvider(new ArrayLabelProvider());
		fDecorationViewer.setComparator(new ViewerComparator(Collator.getInstance()));

		gd= new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalAlignment= GridData.BEGINNING;
		fDecorationViewer.getControl().setLayoutData(gd);
		fDecorationViewer.setInput(new Object[] { HIGHLIGHT, SQUIGGLES, BOX, DASHED_BOX, UNDERLINE, PROBLEM_UNDERLINE, IBEAM });
		fDecorationViewer.getCombo().setVisibleItemCount(fDecorationViewer.getCombo().getItemCount());

		label= new Label(optionsComposite, SWT.LEFT);
		label.setText(TextEditorMessages.AnnotationsConfigurationBlock_color);
		gd= new GridData();
		gd.horizontalAlignment= GridData.BEGINNING;
		gd.horizontalIndent= 20;
		label.setLayoutData(gd);

		fAnnotationForegroundColorEditor= new ColorSelector(optionsComposite);
		Button foregroundColorButton= fAnnotationForegroundColorEditor.getButton();
		gd= new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalAlignment= GridData.BEGINNING;
		foregroundColorButton.setLayoutData(gd);

		addFiller(optionsComposite);

		fIsNextPreviousTargetCheckBox= new Button(optionsComposite, SWT.CHECK);
		fIsNextPreviousTargetCheckBox.setText(TextEditorMessages.AnnotationsConfigurationBlock_isNavigationTarget);
		gd= new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalAlignment= GridData.BEGINNING;
		gd.horizontalSpan= 2;
		gd.horizontalIndent= 0;
		fIsNextPreviousTargetCheckBox.setLayoutData(gd);


		fAnnotationTypeViewer.addSelectionChangedListener(event -> handleAnnotationListSelection());

		fShowInTextCheckBox.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// do nothing
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				ListItem item= getSelectedItem();
				final boolean value= fShowInTextCheckBox.getSelection();
				if (value) {
					// enable whatever is in the combo
					String[] decoration= (String[]) fDecorationViewer.getStructuredSelection().getFirstElement();
					if (HIGHLIGHT.equals(decoration))
						fStore.setValue(item.highlightKey, true);
					else
						fStore.setValue(item.textKey, true);
				} else {
					// disable both
					if (item.textKey != null)
						fStore.setValue(item.textKey, false);
					if (item.highlightKey != null)
						fStore.setValue(item.highlightKey, false);
				}
				fStore.setValue(item.textKey, value);
				updateDecorationViewer(item, false);
				fAnnotationTypeViewer.refresh(item);
			}
		});

		fShowInOverviewRulerCheckBox.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// do nothing
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				ListItem item= getSelectedItem();
				fStore.setValue(item.overviewRulerKey, fShowInOverviewRulerCheckBox.getSelection());
				fAnnotationTypeViewer.refresh(item);
			}
		});

		fShowInVerticalRulerCheckBox.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// do nothing
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				ListItem item= getSelectedItem();
				fStore.setValue(item.verticalRulerKey, fShowInVerticalRulerCheckBox.getSelection());
				fAnnotationTypeViewer.refresh(item);
			}
		});

		fIsNextPreviousTargetCheckBox.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// do nothing
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				ListItem item= getSelectedItem();
				fStore.setValue(item.isNextPreviousNavigationKey, fIsNextPreviousTargetCheckBox.getSelection());
				fAnnotationTypeViewer.refresh(item);
			}
		});

		foregroundColorButton.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// do nothing
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				ListItem item= getSelectedItem();
				PreferenceConverter.setValue(fStore, item.colorKey, fAnnotationForegroundColorEditor.getColorValue());
				fAnnotationTypeViewer.refresh(item);
			}
		});

		fDecorationViewer.addSelectionChangedListener(event -> {
			String[] decoration= (String[]) fDecorationViewer.getStructuredSelection().getFirstElement();
			ListItem item= getSelectedItem();

			if (fShowInTextCheckBox.getSelection()) {
				if (HIGHLIGHT.equals(decoration)) {
					fStore.setValue(item.highlightKey, true);
					if (item.textKey != null) {
						fStore.setValue(item.textKey, false);
						if (item.textStyleKey != null)
							fStore.setValue(item.textStyleKey, AnnotationPreference.STYLE_NONE);
					}
				} else {
					if (item.highlightKey != null)
						fStore.setValue(item.highlightKey, false);
					if (item.textKey != null) {
						fStore.setValue(item.textKey, true);
						if (item.textStyleKey != null)
							fStore.setValue(item.textStyleKey, decoration[1]);
					}
				}
			}

			fAnnotationTypeViewer.refresh(item);
		});

		composite.layout();
		return composite;
	}

	private void addFiller(Composite composite) {
		PixelConverter pixelConverter= new PixelConverter(composite);

		Label filler= new Label(composite, SWT.LEFT);
		GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
		gd.horizontalSpan= 2;
		gd.heightHint= pixelConverter.convertHeightInCharsToPixels(1) / 2;
		filler.setLayoutData(gd);
	}

	/**
	 * Applies the given data.
	 *
	 * @param data the annotation type to select in the list or <code>null</code>
	 * @see org.eclipse.ui.internal.editors.text.IPreferenceConfigurationBlock#applyData(java.lang.Object)
	 * @since 3.4
	 */
	@Override
	public void applyData(Object data) {
		if (!(data instanceof String))
			return;

		for (int i= 0; i < fListModel.length; i++) {
			final ListItem element= fListModel[i];
			if (data.equals(element.label)) {
				final Control control= fAnnotationTypeViewer.getControl();
				control.getDisplay().asyncExec(() -> {
					control.setFocus();
					fAnnotationTypeViewer.setSelection(new StructuredSelection(element), true);
				});
				return;
			}
		}
	}

	@Override
	public boolean canPerformOk() {
		return true;
	}

	@Override
	public void performOk() {
	}

	@Override
	public void performDefaults() {
		fStore.loadDefaults();
		fAnnotationTypeViewer.refresh();
		handleAnnotationListSelection();
	}

	private void handleAnnotationListSelection() {
		ListItem item= getSelectedItem();

		RGB rgb= PreferenceConverter.getColor(fStore, item.colorKey);
		fAnnotationForegroundColorEditor.setColorValue(rgb);

		boolean highlight= item.highlightKey == null ? false : fStore.getBoolean(item.highlightKey);
		boolean showInText = item.textKey == null ? false : fStore.getBoolean(item.textKey);
		fShowInTextCheckBox.setSelection(showInText || highlight);

		updateDecorationViewer(item, true);

		fShowInOverviewRulerCheckBox.setSelection(fStore.getBoolean(item.overviewRulerKey));

		if (item.isNextPreviousNavigationKey != null) {
			fIsNextPreviousTargetCheckBox.setEnabled(true);
			fIsNextPreviousTargetCheckBox.setSelection(fStore.getBoolean(item.isNextPreviousNavigationKey));
		} else {
			fIsNextPreviousTargetCheckBox.setEnabled(false);
			fIsNextPreviousTargetCheckBox.setSelection(false);
		}

		if (item.verticalRulerKey != null) {
			fShowInVerticalRulerCheckBox.setSelection(fStore.getBoolean(item.verticalRulerKey));
			fShowInVerticalRulerCheckBox.setEnabled(true);
		} else {
			fShowInVerticalRulerCheckBox.setSelection(true);
			fShowInVerticalRulerCheckBox.setEnabled(false);
		}
	}



	@Override
	public void initialize() {

		fAnnotationTypeViewer.setInput(fListModel);
		fAnnotationTypeViewer.getControl().getDisplay().asyncExec(() -> {
			if (fAnnotationTypeViewer != null && !fAnnotationTypeViewer.getControl().isDisposed()) {
				fAnnotationTypeViewer.setSelection(new StructuredSelection(fListModel[0]));
			}
		});

	}

	private ListItem[] createAnnotationTypeListModel(MarkerAnnotationPreferences preferences) {
		ArrayList<ListItem> listModelItems= new ArrayList<>();
		Iterator<AnnotationPreference> e= preferences.getAnnotationPreferences().iterator();

		while (e.hasNext()) {
			AnnotationPreference info= e.next();
			if (info.isIncludeOnPreferencePage()) {
				String label= info.getPreferenceLabel();
				if (containsMoreThanOne(preferences.getAnnotationPreferences().iterator(), label))
					label += " (" + info.getAnnotationType() + ")";  //$NON-NLS-1$//$NON-NLS-2$

				Image image= getImage(info);

				listModelItems.add(new ListItem(label, image, info.getColorPreferenceKey(), info.getTextPreferenceKey(), info.getOverviewRulerPreferenceKey(), info.getHighlightPreferenceKey(), info
						.getVerticalRulerPreferenceKey(), info.getTextStylePreferenceKey(), info.getIsGoToNextNavigationTargetKey()));
			}
		}

		Comparator<ListItem> comparator= (o1, o2) -> {
			String label1= o1.label;
			String label2= o2.label;

			return Collator.getInstance().compare(label1, label2);

		};
		Collections.sort(listModelItems, comparator);

		ListItem[] items= new ListItem[listModelItems.size()];
		listModelItems.toArray(items);
		return items;
	}

	/**
	 * Returns the image for the given annotation and the given annotation preferences or
	 * <code>null</code> if there is no such image.
	 *
	 * @param preference the annotation preference
	 * @return the image or <code>null</code>
	 * @since 3.1
	 */
	private Image getImage(AnnotationPreference preference) {

		ImageRegistry registry= EditorsPlugin.getDefault().getImageRegistry();

		String annotationType= (String) preference.getAnnotationType();
		if (annotationType == null)
			return null;

		String customImage= annotationType + "__AnnotationsConfigurationBlock_Image"; //$NON-NLS-1$

		Image image;
		image= registry.get(customImage);
		if (image != null)
			return image;

		image= registry.get(annotationType);
		if (image == null) {
			AnnotationPreference delegatingPreference= EditorsPlugin.getDefault().getAnnotationPreferenceLookup().getAnnotationPreference(annotationType);
			ImageDescriptor descriptor= delegatingPreference.getImageDescriptor();
			if (descriptor != null) {
				registry.put(annotationType, descriptor);
				image= registry.get(annotationType);
			} else {
				String symbolicImageName= preference.getSymbolicImageName();
				if (symbolicImageName != null) {
					String key= DefaultMarkerAnnotationAccess.getSharedImageName(symbolicImageName);
					if (key != null) {
						ISharedImages sharedImages= PlatformUI.getWorkbench().getSharedImages();
						image= sharedImages.getImage(key);
					}
				}
			}
		}

		if (image == null)
			return null;

		// create custom image
		final int SIZE= 16; // square images
		ImageData data= image.getImageData();
		Image copy;
		if (data.height > SIZE || data.width > SIZE) {
			// scale down to icon size
			copy= new Image(Display.getCurrent(), data.scaledTo(SIZE, SIZE));

		} else if (data.height == SIZE && data.width == SIZE) {
			// nothing to scale, return the image
			return image;

		} else {
			// don't scale up, but rather copy into the middle and mark everything else transparent
			ImageData mask= data.getTransparencyMask();
			ImageData resized= new ImageData(SIZE, SIZE, data.depth, data.palette);
			ImageData resizedMask= new ImageData(SIZE, SIZE, mask.depth, mask.palette);

			int xo= Math.max(0, (SIZE - data.width) / 2);
			int yo= Math.max(0, (SIZE - data.height) / 2);

			for (int y= 0; y < SIZE; y++) {
				for (int x= 0; x < SIZE; x++) {
					if (y >= yo && x >= xo && y < yo + data.height && x < xo + data.width) {
						resized.setPixel(x, y, data.getPixel(x - xo, y - yo));
						resizedMask.setPixel(x, y, mask.getPixel(x - xo, y - yo));
					}
				}
			}

			copy= new Image(Display.getCurrent(), resized, resizedMask);
		}

		fImageKeys.add(customImage);
		registry.put(customImage, copy);
		return copy;
	}

	private boolean containsMoreThanOne(Iterator<AnnotationPreference> annotationPrefernceIterator, String label) {
		if (label == null)
			return false;

		int count= 0;
		while (annotationPrefernceIterator.hasNext()) {
			if (label.equals(annotationPrefernceIterator.next().getPreferenceLabel()))
				count++;

			if (count == 2)
				return true;
		}
		return false;
	}

	@Override
	public void dispose() {
		ImageRegistry registry= EditorsPlugin.getDefault().getImageRegistry();

		for (Iterator<String> it= fImageKeys.iterator(); it.hasNext();) {
			String string= it.next();
			registry.remove(string);
		}

		fImageKeys.clear();
	}

	private ListItem getSelectedItem() {
		return (ListItem) fAnnotationTypeViewer.getStructuredSelection().getFirstElement();
	}

	private void updateDecorationViewer(ListItem item, boolean changed) {
		// decoration selection: if the checkbox is enabled, there is
		// only one case where the combo is not enabled: if both the highlight and textStyle keys are null
		final boolean enabled= fShowInTextCheckBox.getSelection() && !(item.highlightKey == null && item.textStyleKey == null);
		fDecorationViewer.getControl().setEnabled(enabled);

		if (changed) {
			String[] selection= null;
			ArrayList<String[]> list= new ArrayList<>();

			// highlighting
			if (item.highlightKey != null) {
				list.add(HIGHLIGHT);
				if (fStore.getBoolean(item.highlightKey))
					selection= HIGHLIGHT;
			}

			// legacy default= squiggly lines
			list.add(SQUIGGLES);

			// advanced styles
			if (item.textStyleKey != null) {
				list.add(UNDERLINE);
				list.add(PROBLEM_UNDERLINE);
				list.add(BOX);
				list.add(DASHED_BOX);
				list.add(IBEAM);
			}

			// set selection
			if (selection == null) {
				String val= item.textStyleKey == null ? SQUIGGLES[1] : fStore.getString(item.textStyleKey);
				for (Iterator<String[]> iter= list.iterator(); iter.hasNext();) {
					String[] elem= iter.next();
					if (elem[1].equals(val)) {
						selection= elem;
						break;
					}
				}
			}

			fDecorationViewer.setInput(list.toArray(new Object[list.size()]));
			if (selection != null)
				fDecorationViewer.setSelection(new StructuredSelection((Object) selection), true);
		}
	}
}
