/******************************************************************************* * Copyright (c) 2007 committers of openArchitectureWare and others. * 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: * committers of openArchitectureWare - initial API and implementation *******************************************************************************/ package org.eclipse.xtend.shared.ui.editor; import java.lang.reflect.Method; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.ui.actions.ActionGroup; import org.eclipse.ui.editors.text.TextEditor; /** * Action group with 2 actions: "Toggle Breakpoints" and * "Enable/Disable Breakpoints".<br> * Despite of usual breakpoint actions these actions can be used not only for * vertical ruler context menu (incl. double click), but also for editor context * menu. That way "in line" breakpoints can be handled. * * @author Dennis H�bner */ public class BreakpointActionGroup extends ActionGroup { private EnableDisableBreakpointAction enableAction; private ToggleBreakpointAction toggleAction; private int lastSelectedLine; private int lastSelectedOffset; private IVerticalRuler verticalRuler; private boolean rulerSelected; private StyledText textWidget; // ------------------------------------------------------------------------- public BreakpointActionGroup(final TextEditor editor) { Assert.isNotNull(editor); // Note: We don't want to define a new "IOurOwnTextEditor" interface, so // we do it via Reflection Object obj = getterMethod("getSourceViewer", editor); if (obj == null) { return; } textWidget = ((ISourceViewer) obj).getTextWidget(); obj = getterMethod("getVerticalRuler", editor); if (obj == null) { return; } verticalRuler = (IVerticalRuler) obj; enableAction = new EnableDisableBreakpointAction(editor, this); toggleAction = new ToggleBreakpointAction(editor, this); // set lastSelectedLine if RightMouseClick on text textWidget.addMouseListener(new MouseAdapter() { @Override public void mouseDown(final MouseEvent e) { if (e.button == 3) { updateLastSelectedOffset(e.x, e.y); } } }); // set lastSelectedLine if RightMouseClick or DoubleClick on vertical // ruler verticalRuler.getControl().addMouseListener(new MouseAdapter() { @Override public void mouseDown(final MouseEvent e) { if (e.button == 3) { updateLastSelectedLine(e.y); } } @Override public void mouseDoubleClick(final MouseEvent e) { updateLastSelectedLine(e.y); // see note below for why we call it here toggleAction.run(); } }); } private void updateLastSelectedLine(final int y) { // Note: we use our own "lastSelectedLine mechanism" (not the ruler's // one) because of sequencing problems // our action mouseHandler would be called after the ruler mouseHandler, // so for doubleClick the // lastSelectedLine value may not be correct // (see AbstractTextEditor.createPartCOntrol(): createVerticalRuler() is // before createActions() rulerSelected = true; final int oldLine = lastSelectedLine; final int oldOffset = lastSelectedOffset; try { lastSelectedLine = verticalRuler.toDocumentLineNumber(y); lastSelectedOffset = textWidget .getOffsetAtLocation(new Point(textWidget.getLeftMargin(), y)); } catch (IllegalArgumentException e) { // Restore original values lastSelectedLine = oldLine; lastSelectedOffset = oldOffset; } } private void updateLastSelectedOffset(final int x, final int y) { rulerSelected = false; final int oldLine = lastSelectedLine; try { lastSelectedLine = verticalRuler.toDocumentLineNumber(y); try { lastSelectedOffset = textWidget.getOffsetAtLocation(new Point( x, y)); } catch (Exception e) { try { // If we got the offset, move to the end of the line. lastSelectedOffset = textWidget .getOffsetAtLocation(new Point(textWidget.getLeftMargin(), y)); int lineIndex = textWidget .getLineAtOffset(lastSelectedOffset); lastSelectedOffset += textWidget.getLine(lineIndex) .length(); } catch (Exception e2) { // Otherwise, create an offset to the end of the entire // text. lastSelectedOffset = textWidget.getText().length(); } } } catch (Exception e) { lastSelectedLine = oldLine; } } // ------------------------------------------------------------------------- public boolean isRulerSelected() { return rulerSelected; } public int getLastSelectedLine() { return lastSelectedLine; } public int getLastSelectedOffset() { return lastSelectedOffset; } public int getOffsetAtLine(final int line) { return textWidget.getOffsetAtLine(line); } // ------------------------------------------------------------------------- @Override public void fillContextMenu(final IMenuManager manager) { toggleAction.updateText(); manager.appendToGroup("Xpand", toggleAction); enableAction.updateText(); manager.appendToGroup("Xpand", enableAction); } @Override public void dispose() { enableAction = null; toggleAction = null; super.dispose(); } private Object getterMethod(final String name, final Object element) { try { Method m = findMethod(name, element.getClass()); if (m != null) { m.setAccessible(true); return m.invoke(element, new Object[] {}); } } catch (Exception e) { System.out.println("BreakpointActionGroup.getterMethod() caused an error"); } return null; } private Method findMethod(final String name, final Class<?> clazz) { if (!Object.class.equals(clazz)) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (method.getName().equals(name)) { return method; } } return findMethod(name, clazz.getSuperclass()); } return null; } }