| /******************************************************************************* |
| * Copyright (c) 2015 Red Hat 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: |
| * Mickael Istria (Red Hat Inc.) - 469918 Zoom In/Out |
| *******************************************************************************/ |
| package org.eclipse.ui.texteditor; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.swt.graphics.FontData; |
| |
| import org.eclipse.core.commands.AbstractHandler; |
| import org.eclipse.core.commands.ExecutionEvent; |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.expressions.IEvaluationContext; |
| |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| |
| import org.eclipse.jface.resource.FontDescriptor; |
| import org.eclipse.jface.resource.FontRegistry; |
| import org.eclipse.jface.resource.JFaceResources; |
| |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.ISources; |
| import org.eclipse.ui.handlers.HandlerUtil; |
| import org.eclipse.ui.part.AbstractMultiEditor; |
| import org.eclipse.ui.part.MultiPageEditorPart; |
| |
| /** |
| * Abstract handler to change the default font size on Text editors. |
| */ |
| abstract class AbstractTextZoomHandler extends AbstractHandler { |
| |
| private static Map<String, String> fgFontToDefault; |
| |
| private static Map<String, Set<String>> fgDefaultToFonts; |
| |
| private int fFontSizeOffset; |
| |
| /** |
| * Implementations of this class have to specify in the constructor how much the font would |
| * change when the handler is executed. |
| * |
| * @param fontSizeOffset how many points the default font will change. The offset can be |
| * negative, to reduce font size (zoom out) or positive to increase font size (zoom |
| * in) |
| */ |
| protected AbstractTextZoomHandler(int fontSizeOffset) { |
| fFontSizeOffset= fontSizeOffset; |
| } |
| |
| @Override |
| public Object execute(ExecutionEvent event) throws ExecutionException { |
| AbstractTextEditor textEditor= getActiveTextEditor(event); |
| if (textEditor == null) { |
| return null; |
| } |
| FontRegistry fontRegistry= textEditor.getSite().getWorkbenchWindow().getWorkbench().getThemeManager().getCurrentTheme().getFontRegistry(); |
| String fontProperty= textEditor.getSymbolicFontName(); |
| if (fontProperty == null) { |
| fontProperty= JFaceResources.TEXT_FONT; |
| } |
| Set<String> fontsToSet= getAffectedFontNames(fontProperty, fontRegistry); |
| FontData[] initialFontData= null; |
| String currentFontName= fontProperty; |
| while (currentFontName != null && (initialFontData= fontRegistry.getFontData(currentFontName)) == null) { |
| currentFontName= fgFontToDefault.get(currentFontName); |
| } |
| |
| FontData[] newFontData= createFontDescriptor(initialFontData).getFontData(); |
| if (newFontData != null) { |
| fontsToSet.stream().forEach(fontName -> fontRegistry.put(fontName, newFontData)); |
| } |
| return Status.OK_STATUS; |
| } |
| |
| private FontDescriptor createFontDescriptor(FontData[] initialFontData) { |
| int destFontSize= initialFontData[0].getHeight() + fFontSizeOffset; |
| if (destFontSize <= 0) { |
| return FontDescriptor.createFrom(initialFontData); |
| } |
| return FontDescriptor.createFrom(initialFontData).setHeight(destFontSize); |
| } |
| |
| private AbstractTextEditor getActiveTextEditor(ExecutionEvent event) { |
| return getActiveTextEditor(HandlerUtil.getActiveEditor(event)); |
| } |
| |
| private AbstractTextEditor getActiveTextEditor(IEditorPart part) { |
| if (part instanceof AbstractTextEditor) { |
| return (AbstractTextEditor)part; |
| } else if ((part instanceof AbstractMultiEditor) && ((AbstractMultiEditor)part).getActiveEditor() instanceof AbstractTextEditor) { |
| return (AbstractTextEditor)((AbstractMultiEditor)part).getActiveEditor(); |
| } else if ((part instanceof MultiPageEditorPart) && ((MultiPageEditorPart)part).getSelectedPage() instanceof AbstractTextEditor) { |
| return (AbstractTextEditor)((MultiPageEditorPart)part).getSelectedPage(); |
| } |
| return part != null ? part.getAdapter(AbstractTextEditor.class) : null; |
| } |
| |
| @Override |
| public void setEnabled(Object evaluationContext) { |
| boolean enabled = false; |
| if (evaluationContext instanceof IEvaluationContext) { |
| Object activeEditor = ((IEvaluationContext) evaluationContext).getVariable(ISources.ACTIVE_EDITOR_NAME); |
| if (activeEditor instanceof IEditorPart) { |
| enabled = getActiveTextEditor((IEditorPart) activeEditor) != null; |
| } |
| } |
| setBaseEnabled(enabled); |
| } |
| |
| /** |
| * |
| * @param referenceFontName the font name on which change is initially requested |
| * @param fontRegistry the font registry |
| * @return the names of the fonts that should be modified together with the referenceFontName. |
| * Those are parent fonts from which the reference font inherit, or children font that |
| * are set to default or inherit from reference font or a common parent that is affected |
| * too. |
| */ |
| private Set<String> getAffectedFontNames(String referenceFontName, FontRegistry fontRegistry) { |
| synchronized (AbstractTextZoomHandler.class) { |
| if (fgFontToDefault == null) { |
| // TODO: This should rely on ThemeRegistry and IThemeElementDefinition, |
| // but those aren't visible at that time. So we're recreating the font hierarchy |
| fgFontToDefault= new HashMap<>(); |
| fgDefaultToFonts= new HashMap<>(); |
| IConfigurationElement[] themeElements= Platform.getExtensionRegistry().getConfigurationElementsFor("org.eclipse.ui.themes"); //$NON-NLS-1$ |
| for (int i= 0; i < themeElements.length; i++) { |
| IConfigurationElement extension= themeElements[i]; |
| if ("fontDefinition".equals(extension.getName())) { //$NON-NLS-1$ |
| String fontId= extension.getAttribute("id"); //$NON-NLS-1$ |
| String defaultsTo= extension.getAttribute("defaultsTo"); //$NON-NLS-1$ |
| if (defaultsTo != null) { |
| fgFontToDefault.put(fontId, defaultsTo); |
| if (!fgDefaultToFonts.containsKey(defaultsTo)) { |
| fgDefaultToFonts.put(defaultsTo, new HashSet<>()); |
| } |
| fgDefaultToFonts.get(defaultsTo).add(fontId); |
| } |
| |
| } |
| } |
| } |
| } |
| Set<String> res= new HashSet<>(); |
| FontData[] referenceFontData= fontRegistry.getFontData(referenceFontName); |
| if (fontRegistry.hasValueFor(referenceFontName)) { |
| res.add(referenceFontName); |
| } |
| String currentFontName= referenceFontName; |
| String rootFontName= referenceFontName; |
| // identify "root" font to change |
| do { |
| currentFontName= fgFontToDefault.get(currentFontName); |
| if (currentFontName != null && Arrays.equals(referenceFontData, fontRegistry.getFontData(currentFontName))) { |
| rootFontName= currentFontName; |
| } |
| } while (currentFontName != null); |
| LinkedList<String> fontsToProcess= new LinkedList<>(); |
| fontsToProcess.add(rootFontName); |
| // propage to "children" fonts |
| Set<String> alreadyProcessed= new HashSet<>(); |
| while (!fontsToProcess.isEmpty()) { |
| currentFontName= fontsToProcess.get(0); |
| fontsToProcess.remove(0); |
| // with recent Java, use currentFOntName = fontsToProcess.poll instead of the 2 lines above |
| if (!alreadyProcessed.contains(currentFontName)) { // avoid infinite loop |
| alreadyProcessed.add(currentFontName); |
| FontData[] currentFontData= fontRegistry.getFontData(currentFontName); |
| if (currentFontData == null || Arrays.equals(referenceFontData, currentFontData)) { |
| if (fontRegistry.hasValueFor(currentFontName)) { |
| res.add(currentFontName); |
| } |
| Set<String> children= fgDefaultToFonts.get(currentFontName); |
| if (children != null) { |
| fontsToProcess.addAll(children); |
| } |
| } |
| } |
| } |
| return res; |
| } |
| |
| } |