blob: e21f03bd13aeb2a67c3f956c6f45ea7e0da7dfe1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 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.texteditor;
import java.util.ResourceBundle;
import org.osgi.framework.FrameworkUtil;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.texteditor.NLSUtility;
import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
/**
* Action for jumping to a particular line in the editor's text viewer. The user
* is requested to enter the line number into an input dialog. The action is
* initially associated with a text editor via the constructor, but that can be
* subsequently changed using <code>setEditor</code>.
* <p>
* The following keys, prepended by the given option prefix, are used for
* retrieving resources from the given bundle:
* </p>
* <ul>
* <li><code>"dialog.invalid_range"</code> - to indicate an invalid line
* number</li>
* <li><code>"dialog.invalid_input"</code> - to indicate an invalid line number
* format</li>
* <li><code>"dialog.title"</code> - the input dialog's title</li>
* <li><code>"dialog.message"</code> - the input dialog's message</li>
* </ul>
* <p>
* This class may be instantiated; it is not intended to be subclassed.
* </p>
*
* @noextend This class is not intended to be subclassed by clients.
*/
public class GotoLineAction extends TextEditorAction {
/**
* Validates whether the text found in the input field of the dialog forms a valid line number.
* A number is valid if it is one to which can be jumped.
*/
class NumberValidator implements IInputValidator {
@Override
public String isValid(String input) {
if (input == null || input.isEmpty())
return " "; //$NON-NLS-1$
try {
int i= Integer.parseInt(input);
if (i <= 0 || fLastLine < i)
return fBundle.getString(fPrefix + "dialog.invalid_range"); //$NON-NLS-1$
} catch (NumberFormatException x) {
return fBundle.getString(fPrefix + "dialog.invalid_input"); //$NON-NLS-1$
}
return null;
}
}
/**
* Standard input dialog with custom dialog bounds strategy and settings.
*
* @since 2.0
*/
class GotoLineDialog extends InputDialog {
public GotoLineDialog(Shell parent, String title, String message, String initialValue, IInputValidator validator) {
super(parent, title, message, initialValue, validator);
}
@Override
protected IDialogSettings getDialogBoundsSettings() {
String sectionName= getClass().getName() + "_dialogBounds"; //$NON-NLS-1$
IDialogSettings settings = PlatformUI
.getDialogSettingsProvider(FrameworkUtil.getBundle(GotoLineAction.class)).getDialogSettings();
IDialogSettings section= settings.getSection(sectionName);
if (section == null)
section= settings.addNewSection(sectionName);
return section;
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
super.createButtonsForButtonBar(parent);
Button button = getOkButton();
button.setText(GotoLineAction.this.fBundle.getString(fPrefix + "button")); //$NON-NLS-1$
}
@Override
protected int getDialogBoundsStrategy() {
return DIALOG_PERSISTLOCATION;
}
}
/** The biggest valid line number of the presented document */
private int fLastLine;
/** This action's resource bundle */
private ResourceBundle fBundle;
/** This action's prefix used for accessing the resource bundle */
private String fPrefix;
/**
* Creates a new action for the given text editor. The action configures its visual
* representation from the given resource bundle.
*
* @param bundle the resource bundle
* @param prefix a prefix to be prepended to the various resource keys (described in
* <code>ResourceAction</code> constructor), or <code>null</code> if none
* @param editor the text editor
* @see TextEditorAction#TextEditorAction(ResourceBundle, String, ITextEditor)
*/
public GotoLineAction(ResourceBundle bundle, String prefix, ITextEditor editor) {
super(bundle, prefix, editor);
fBundle= bundle;
fPrefix= prefix;
}
/**
* Creates a new action for the given text editor. The action configures its visual
* representation with default values.
*
* @param editor the text editor
* @see TextEditorAction#TextEditorAction(ResourceBundle, String, ITextEditor)
* @since 3.5
*/
public GotoLineAction(ITextEditor editor) {
super(EditorMessages.getBundleForConstructedKeys(), "Editor.GotoLine.", editor); //$NON-NLS-1$
fBundle= EditorMessages.getBundleForConstructedKeys();
fPrefix= "Editor.GotoLine."; //$NON-NLS-1$
}
/**
* Jumps to the given line.
*
* @param line the line to jump to
*/
private void gotoLine(int line) {
ITextEditor editor= getTextEditor();
IDocumentProvider provider= editor.getDocumentProvider();
IDocument document= provider.getDocument(editor.getEditorInput());
try {
int start= document.getLineOffset(line);
editor.selectAndReveal(start, 0);
IWorkbenchPage page= editor.getSite().getPage();
page.activate(editor);
} catch (BadLocationException x) {
// ignore
}
}
@Override
public void run() {
ITextEditor editor= getTextEditor();
if (editor == null)
return;
IDocumentProvider docProvider= editor.getDocumentProvider();
if (docProvider == null)
return;
IDocument document= docProvider.getDocument(editor.getEditorInput());
if (document == null)
return;
try {
fLastLine= document.getLineOfOffset(document.getLength()) + 1;
} catch (BadLocationException ex) {
IStatus status= new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, "Go to Line failed", ex); //$NON-NLS-1$
TextEditorPlugin.getDefault().getLog().log(status);
return;
}
String title= fBundle.getString(fPrefix + "dialog.title"); //$NON-NLS-1$
String message= NLSUtility.format(fBundle.getString(fPrefix + "dialog.message"), Integer.valueOf(fLastLine)); //$NON-NLS-1$
String currentLineStr= ""; //$NON-NLS-1$
ISelection selection= editor.getSelectionProvider().getSelection();
if (selection instanceof ITextSelection) {
ITextSelection textSelection= (ITextSelection)selection;
Control textWidget= editor.getAdapter(Control.class);
boolean caretAtStartOfSelection= false;
if (textWidget instanceof StyledText)
caretAtStartOfSelection= ((StyledText)textWidget).getSelection().x == ((StyledText)textWidget).getCaretOffset();
int currentLine;
if (caretAtStartOfSelection)
currentLine= textSelection.getStartLine();
else {
int endOffset= textSelection.getOffset() + textSelection.getLength();
try {
currentLine= document.getLineOfOffset(endOffset);
} catch (BadLocationException ex) {
currentLine= -1;
}
}
if (currentLine > -1)
currentLineStr= Integer.toString(currentLine + 1);
}
GotoLineDialog d= new GotoLineDialog(editor.getSite().getShell(), title, message, currentLineStr, new NumberValidator());
if (d.open() == Window.OK) {
try {
int line= Integer.parseInt(d.getValue());
gotoLine(line - 1);
} catch (NumberFormatException x) {
}
}
}
}