| /******************************************************************************* |
| * Copyright (c) 2000, 2008 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.editors.text; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ResourceBundle; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.content.IContentDescription; |
| |
| import org.eclipse.core.resources.IFile; |
| |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.dialogs.IInputValidator; |
| import org.eclipse.jface.dialogs.InputDialog; |
| import org.eclipse.jface.window.Window; |
| |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IWorkbenchActionConstants; |
| import org.eclipse.ui.actions.ActionGroup; |
| import org.eclipse.ui.internal.editors.text.NLSUtility; |
| |
| import org.eclipse.ui.texteditor.ITextEditor; |
| import org.eclipse.ui.texteditor.IUpdate; |
| import org.eclipse.ui.texteditor.ResourceAction; |
| import org.eclipse.ui.texteditor.RetargetTextEditorAction; |
| import org.eclipse.ui.texteditor.TextEditorAction; |
| |
| |
| /** |
| * Action group for encoding actions. |
| * |
| * @since 2.0 |
| * @deprecated As of 3.1, encoding needs to be changed via properties dialog. This class is planned |
| * for removal after March 2021 (see bug#544309 for details). |
| * @noextend This class is not intended to be subclassed by clients. |
| * @noinstantiate This class is not intended to be instantiated by clients. |
| * @noreference This class is not intended to be referenced by clients. |
| */ |
| @Deprecated(forRemoval= true) |
| public class EncodingActionGroup extends ActionGroup { |
| |
| private static final String FILE_CONTENT_ENCODING_FORMAT= TextEditorMessages.ResourceInfo_fileContentEncodingFormat; |
| private static final String FILE_CONTAINER_ENCODING_FORMAT= TextEditorMessages.ResourceInfo_fileContainerEncodingFormat; |
| |
| |
| /** |
| * Action for setting the encoding of the editor to the value this action has |
| * been initialized with. |
| */ |
| static class PredefinedEncodingAction extends TextEditorAction { |
| |
| /** The target encoding of this action. */ |
| private String fEncoding; |
| /** The action label. */ |
| private String fLabel; |
| /** Indicates whether the target encoding is the default encoding. */ |
| private boolean fIsDefault; |
| |
| /** |
| * Creates a new action for the given specification. |
| * |
| * @param bundle the resource bundle |
| * @param prefix the prefix for lookups from the resource bundle |
| * @param encoding the target encoding |
| * @param editor the target editor |
| */ |
| public PredefinedEncodingAction(ResourceBundle bundle, String prefix, String encoding, ITextEditor editor) { |
| super(bundle, prefix, editor); |
| fEncoding= encoding; |
| if (prefix == null) |
| setText(encoding); |
| fLabel= getText(); |
| } |
| |
| /** |
| * Creates a new action for the given specification. |
| * |
| * @param bundle the resource bundle |
| * @param encoding the target encoding |
| * @param editor the target editor |
| */ |
| public PredefinedEncodingAction(ResourceBundle bundle, String encoding, ITextEditor editor) { |
| super(bundle, null, editor); |
| fEncoding= encoding; |
| setText(encoding); |
| fLabel= getText(); |
| } |
| |
| /** |
| * Returns the encoding support of the action's editor. |
| * |
| * @return the encoding support of the action's editor or <code>null</code> if none |
| */ |
| private IEncodingSupport getEncodingSupport() { |
| ITextEditor editor= getTextEditor(); |
| if (editor != null) |
| return editor.getAdapter(IEncodingSupport.class); |
| return null; |
| } |
| |
| @Override |
| public void run() { |
| IEncodingSupport s= getEncodingSupport(); |
| if (s != null) |
| s.setEncoding(fIsDefault ? null : fEncoding); |
| } |
| |
| /** |
| * Returns the encoding currently used in the given editor. |
| * |
| * @param editor the editor |
| * @return the encoding currently used in the given editor or <code>null</code> if no encoding support is installed |
| */ |
| private String getEncoding(ITextEditor editor) { |
| |
| IEditorInput input= (editor.getEditorInput()); |
| if (input instanceof IFileEditorInput) { |
| IFile file= ((IFileEditorInput)input).getFile(); |
| try { |
| String explicitEncoding; |
| explicitEncoding= file.getCharset(false); |
| if (explicitEncoding == null) |
| return null; |
| } catch (CoreException e) { |
| // continue - assume file is not using default encoding |
| } |
| } |
| |
| IEncodingSupport s= getEncodingSupport(); |
| if (s != null) |
| return s.getEncoding(); |
| |
| return null; |
| } |
| |
| @Override |
| public void update() { |
| |
| if (fEncoding == null) { |
| setEnabled(false); |
| return; |
| } |
| |
| ITextEditor editor= getTextEditor(); |
| if (editor == null) { |
| setEnabled(false); |
| return; |
| } |
| |
| // update label |
| fIsDefault= IEncodingActionsConstants.DEFAULT.equals(fEncoding); |
| if (fIsDefault) |
| setText(getDefaultEncodingText(editor, fLabel)); |
| else |
| setText(fLabel); |
| |
| // update enable state |
| if (editor.isDirty()) |
| setEnabled(false); |
| else |
| setEnabled(true); |
| |
| // update checked state |
| String current= getEncoding(editor); |
| if (fIsDefault) |
| setChecked(current == null); |
| else |
| setChecked(fEncoding.equals(current)); |
| } |
| |
| } |
| |
| /* |
| * @since 3.0 |
| */ |
| private static String getDefaultEncodingText(ITextEditor editor, String defaultText) { |
| IEditorInput input= (editor.getEditorInput()); |
| if (!(input instanceof IFileEditorInput)) |
| return defaultText; |
| |
| IFile file= ((IFileEditorInput)input).getFile(); |
| |
| String format= FILE_CONTENT_ENCODING_FORMAT; |
| String encoding; |
| try { |
| encoding= getEncodingFromContent(file); |
| if (encoding == null) { |
| format= FILE_CONTAINER_ENCODING_FORMAT; |
| encoding= file.getParent().getDefaultCharset(); |
| } |
| } catch (CoreException ex) { |
| return defaultText; |
| } |
| |
| return NLSUtility.format(format, encoding); |
| } |
| |
| /* |
| * @since 3.0 |
| */ |
| private static String getEncodingFromContent(IFile file) throws CoreException { |
| IContentDescription description = file.getContentDescription(); |
| if (description != null) { |
| byte[] bom= (byte[])description.getProperty(IContentDescription.BYTE_ORDER_MARK); |
| if (bom == null) |
| return (String)description.getProperty(IContentDescription.CHARSET); |
| if (bom == IContentDescription.BOM_UTF_8) |
| return TextEditorMessages.WorkbenchPreference_encoding_BOM_UTF_8; |
| if (bom == IContentDescription.BOM_UTF_16BE) |
| return TextEditorMessages.WorkbenchPreference_encoding_BOM_UTF_16BE; |
| if (bom == IContentDescription.BOM_UTF_16LE) |
| return TextEditorMessages.WorkbenchPreference_encoding_BOM_UTF_16LE; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Sets the encoding of an editor to the value that has interactively been defined. |
| */ |
| static class CustomEncodingAction extends TextEditorAction { |
| |
| |
| /* |
| * @see org.eclipse.ui.texteditor.TextEditorAction#TextEditorAction(ResourceBundle, String, ITextEditor) |
| */ |
| protected CustomEncodingAction(ResourceBundle bundle, String prefix, ITextEditor editor) { |
| super(bundle, prefix, editor); |
| } |
| |
| @Override |
| public void update() { |
| ITextEditor editor= getTextEditor(); |
| setEnabled(editor != null && !editor.isDirty()); |
| } |
| |
| @Override |
| public void run() { |
| |
| ITextEditor editor= getTextEditor(); |
| if (editor == null) |
| return; |
| |
| IEncodingSupport encodingSupport= editor.getAdapter(IEncodingSupport.class); |
| if (encodingSupport == null) |
| return; |
| |
| String title= TextEditorMessages.Editor_ConvertEncoding_Custom_dialog_title; |
| String message= TextEditorMessages.Editor_ConvertEncoding_Custom_dialog_message; |
| IInputValidator inputValidator = newText -> (newText == null || newText.isEmpty()) ? " " : null; |
| |
| String initialValue= encodingSupport.getEncoding(); |
| if (initialValue == null) |
| initialValue= encodingSupport.getDefaultEncoding(); |
| if (initialValue == null) |
| initialValue= ""; //$NON-NLS-1$ |
| |
| InputDialog d= new InputDialog(editor.getSite().getShell(), title, message, initialValue, inputValidator); |
| if (d.open() == Window.OK) |
| encodingSupport.setEncoding(d.getValue()); |
| } |
| } |
| |
| |
| /** List of predefined encodings. */ |
| private static final String[][] ENCODINGS; |
| |
| /** The default encoding. */ |
| private static final String SYSTEM_ENCODING; |
| |
| /** |
| * Initializer: computes the set of predefined encoding actions. |
| */ |
| static { |
| |
| String[][] encodings= { |
| { IEncodingActionsConstants.DEFAULT, IEncodingActionsHelpContextIds.DEFAULT, IEncodingActionsDefinitionIds.DEFAULT }, |
| { IEncodingActionsConstants.US_ASCII, IEncodingActionsHelpContextIds.US_ASCII, IEncodingActionsDefinitionIds.US_ASCII }, |
| { IEncodingActionsConstants.ISO_8859_1, IEncodingActionsHelpContextIds.ISO_8859_1, IEncodingActionsDefinitionIds.ISO_8859_1 }, |
| { IEncodingActionsConstants.UTF_8, IEncodingActionsHelpContextIds.UTF_8, IEncodingActionsDefinitionIds.UTF_8 }, |
| { IEncodingActionsConstants.UTF_16BE, IEncodingActionsHelpContextIds.UTF_16BE, IEncodingActionsDefinitionIds.UTF_16BE }, |
| { IEncodingActionsConstants.UTF_16LE, IEncodingActionsHelpContextIds.UTF_16LE, IEncodingActionsDefinitionIds.UTF_16LE }, |
| { IEncodingActionsConstants.UTF_16, IEncodingActionsHelpContextIds.UTF_16, IEncodingActionsDefinitionIds.UTF_16 } |
| }; |
| |
| String system= System.getProperty("file.encoding"); //$NON-NLS-1$ |
| if (system != null) { |
| |
| int i; |
| for (i= 0; i < encodings.length; i++) { |
| if (encodings[i][0].equals(system)) |
| break; |
| } |
| |
| if (i != encodings.length) { |
| // bring system encoding in second position - first is default encoding |
| String[] s= encodings[i]; |
| encodings[i]= encodings[1]; |
| encodings[1]= s; |
| // forget system encoding as it's already in the list |
| system= null; |
| } |
| } |
| |
| SYSTEM_ENCODING= system; |
| ENCODINGS= encodings; |
| } |
| |
| |
| |
| /** List of encoding actions of this group. */ |
| private List<RetargetTextEditorAction> fRetargetActions= new ArrayList<>(); |
| |
| /** |
| * Creates a new encoding action group for an action bar contributor. |
| */ |
| public EncodingActionGroup() { |
| |
| fRetargetActions.add(new RetargetTextEditorAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding." + ENCODINGS[0][0] + ".", ENCODINGS[0][0], IAction.AS_RADIO_BUTTON)); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| if (SYSTEM_ENCODING != null) |
| fRetargetActions.add(new RetargetTextEditorAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding.System.", IEncodingActionsConstants.SYSTEM, IAction.AS_RADIO_BUTTON)); //$NON-NLS-1$ |
| |
| for (int i= 1; i < ENCODINGS.length; i++) |
| fRetargetActions.add(new RetargetTextEditorAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding." + ENCODINGS[i][0] + ".", ENCODINGS[i][0], IAction.AS_RADIO_BUTTON)); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| fRetargetActions.add(new RetargetTextEditorAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding.Custom.", IEncodingActionsConstants.CUSTOM, IAction.AS_PUSH_BUTTON)); //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void fillActionBars(IActionBars actionBars) { |
| IMenuManager menuManager= actionBars.getMenuManager(); |
| IMenuManager editMenu= menuManager.findMenuUsingPath(IWorkbenchActionConstants.M_EDIT); |
| if (editMenu != null && !fRetargetActions.isEmpty()) { |
| MenuManager subMenu= new MenuManager(TextEditorMessages.Editor_ConvertEncoding_submenu_label); |
| subMenu.addMenuListener(manager -> update()); |
| |
| Iterator<RetargetTextEditorAction> e= fRetargetActions.iterator(); |
| subMenu.add(e.next()); |
| subMenu.add(new Separator()); |
| while (e.hasNext()) |
| subMenu.add(e.next()); |
| |
| editMenu.add(subMenu); |
| } |
| } |
| |
| /** |
| * Retargets this action group to the given editor. |
| * |
| * @param editor the text editor to which the group should be retargeted |
| */ |
| public void retarget(ITextEditor editor) { |
| fTextEditor= editor; |
| Iterator<RetargetTextEditorAction> e= fRetargetActions.iterator(); |
| while (e.hasNext()) { |
| RetargetTextEditorAction a= e.next(); |
| a.setAction(editor == null ? null : editor.getAction(a.getId())); |
| } |
| } |
| |
| |
| //------------------------------------------------------------------------------------------ |
| |
| |
| /** Text editor this group is associated with. */ |
| private ITextEditor fTextEditor; |
| |
| /** |
| * Creates a new encoding action group for the given editor. |
| * |
| * @param editor the text editor |
| */ |
| public EncodingActionGroup(ITextEditor editor) { |
| |
| fTextEditor= editor; |
| |
| ResourceAction a; |
| if (SYSTEM_ENCODING != null) { |
| a= new PredefinedEncodingAction(TextEditorMessages.getBundleForConstructedKeys(), SYSTEM_ENCODING, editor); |
| a.setHelpContextId(IEncodingActionsHelpContextIds.SYSTEM); |
| a.setActionDefinitionId(IEncodingActionsDefinitionIds.SYSTEM); |
| editor.setAction(IEncodingActionsConstants.SYSTEM, a); |
| } |
| |
| for (String[] e : ENCODINGS) { |
| a = new PredefinedEncodingAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding." + e[0] + ".", e[0], editor); //$NON-NLS-1$ //$NON-NLS-2$ |
| a.setHelpContextId(e[1]); |
| a.setActionDefinitionId(e[2]); |
| editor.setAction(e[0], a); |
| } |
| |
| a= new CustomEncodingAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding." + IEncodingActionsConstants.CUSTOM + ".", editor); //$NON-NLS-1$ //$NON-NLS-2$ |
| a.setHelpContextId(IEncodingActionsHelpContextIds.CUSTOM); |
| a.setActionDefinitionId(IEncodingActionsDefinitionIds.CUSTOM); |
| editor.setAction(IEncodingActionsConstants.CUSTOM, a); |
| } |
| |
| /** |
| * Updates all actions of this action group. |
| */ |
| public void update() { |
| if (fTextEditor == null) |
| return; |
| |
| IAction a= fTextEditor.getAction(IEncodingActionsConstants.SYSTEM); |
| if (a instanceof IUpdate) |
| ((IUpdate) a).update(); |
| |
| for (String[] e : ENCODINGS) { |
| a = fTextEditor.getAction(e[0]); |
| if (a instanceof IUpdate) |
| ((IUpdate) a).update(); |
| } |
| |
| a= fTextEditor.getAction(IEncodingActionsConstants.CUSTOM); |
| if (a instanceof IUpdate) |
| ((IUpdate) a).update(); |
| } |
| |
| @Override |
| public void dispose() { |
| if (fTextEditor != null) { |
| fTextEditor.setAction(IEncodingActionsConstants.SYSTEM, null); |
| for (String[] e : ENCODINGS) { |
| fTextEditor.setAction(e[0], null); |
| } |
| fTextEditor.setAction(IEncodingActionsConstants.CUSTOM, null); |
| |
| fTextEditor= null; |
| } |
| } |
| } |