Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 405525b9ea3e1fa3d5c5d0f0010ac06a763a72e6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*******************************************************************************
 * 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.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.IWorkbenchPart;
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) {
		IWorkbenchPart part= HandlerUtil.getActiveEditor(event);
		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 null;
	}

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

	/**
	 * 
	 * @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;
		// identify "root" font to change
		while (fgFontToDefault.get(currentFontName) != null && Arrays.equals(referenceFontData, fontRegistry.getFontData(currentFontName))) {
			currentFontName= fgFontToDefault.get(currentFontName);
		}
		LinkedList<String> fontsToProcess= new LinkedList<>();
		fontsToProcess.add(currentFontName);
		// 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;
	}

}

Back to the top