Package de.innovationgate.eclipse.utils.ui

Source Code of de.innovationgate.eclipse.utils.ui.QuickMenuAction

/*******************************************************************************
* Copyright (c) 2009, 2010 Innovation Gate GmbH.
* 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:
*     Innovation Gate GmbH - initial API and implementation
******************************************************************************/
package de.innovationgate.eclipse.utils.ui;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.keys.IBindingService;

/**
* A quick menu actions provides support to assign short cuts
* to sub menus.
*
* @since 3.0
*/
public abstract class QuickMenuAction extends Action {

  private static final int CHAR_INDENT= 3;
 
  /**
   * Creates a new quick menu action with the given command id.
   *
   * @param commandId the command id of the short cut used to open
   *  the sub menu
   */
  public QuickMenuAction(String commandId) {
    setActionDefinitionId(commandId);
  }

  /**
   * {@inheritDoc}
   */
  public void run() {
    Display display= Display.getCurrent();
    if (display == null)
      return;
    Control focus= display.getFocusControl();
    if (focus == null || focus.isDisposed())
      return;
   
    MenuManager menu= new MenuManager();
    fillMenu(menu);
    final Menu widget= menu.createContextMenu(focus.getShell());
    Point location= computeMenuLocation(focus, widget);
    if (location == null)
      return;
    widget.setLocation(location);
    widget.setVisible(true);
    while (!widget.isDisposed() && widget.isVisible()) {
      if (!display.readAndDispatch())
        display.sleep();
    }
    if (!widget.isDisposed()) {
      widget.dispose();
    }
  }
 
  /**
   * Hook to fill a menu manager with the items of the sub menu.
   *
   * @param menu the sub menu to fill
   */
  protected abstract void fillMenu(IMenuManager menu);
 
  /**
   * Adds the shortcut to the given menu text and returns it.
   *
   * @param menuText the menu text
   * @return the menu text with the shortcut
   * @since 3.1
   */
  public String addShortcut(String menuText) {
    String shortcut= getShortcutString();
    if (menuText == null || shortcut == null)
      return menuText;
   
    return menuText + '\t' + shortcut;
  }
 
  /**
   * Returns the shortcut assigned to the sub menu or <code>null</code> if
   * no short cut is assigned.
   *
   * @return the shortcut as a human readable string or <code>null</code>
   */
  private String getShortcutString() {
    IBindingService bindingService= (IBindingService)PlatformUI.getWorkbench().getAdapter(IBindingService.class);
    if (bindingService == null)
      return null;
    return bindingService.getBestActiveBindingFormattedFor(getActionDefinitionId());
  }

  private Point computeMenuLocation(Control focus, Menu menu) {
    Point cursorLocation= focus.getDisplay().getCursorLocation();
    Rectangle clientArea= null;
    Point result= null;
    if (focus instanceof StyledText) {
      StyledText styledText= (StyledText)focus;
      clientArea= styledText.getClientArea();
      result= computeMenuLocation(styledText);
    } else if (focus instanceof Tree) {
      Tree tree= (Tree)focus;
      clientArea= tree.getClientArea();
      result= computeMenuLocation(tree);
    } else if (focus instanceof Table) {
      Table table= (Table)focus;
      clientArea= table.getClientArea();
      result= computeMenuLocation(table);
    }
    if (result == null) {
      result= focus.toControl(cursorLocation);
    }
    if (clientArea != null && !clientArea.contains(result)) {
      result= new Point(
        clientArea.x + clientArea.width  / 2,
        clientArea.y + clientArea.height / 2);
    }
    Rectangle shellArea= focus.getShell().getClientArea();
    if (!shellArea.contains(focus.getShell().toControl(focus.toDisplay(result)))) {
      result= new Point(
        shellArea.x + shellArea.width  / 2,
        shellArea.y + shellArea.height / 2);
    }
    return focus.toDisplay(result);
  }
 
  /**
   * Hook to compute the menu location if the focus widget is
   * a styled text widget.
   *
   * @param text the styled text widget that has the focus
   *
   * @return a widget relative position of the menu to pop up or
   *  <code>null</code> if now position inside the widget can
   *  be computed
   */
  protected Point computeMenuLocation(StyledText text) {
    int offset= text.getCaretOffset();
    Point result= text.getLocationAtOffset(offset);
    result.y+= text.getLineHeight(offset);
    if (!text.getClientArea().contains(result))
      return null;
    return result;
  }
 
  /**
   * Hook to compute the menu location if the focus widget is
   * a tree widget.
   *
   * @param tree the tree widget that has the focus
   *
   * @return a widget relative position of the menu to pop up or
   *  <code>null</code> if now position inside the widget can
   *  be computed
   */
  protected Point computeMenuLocation(Tree tree) {
    TreeItem[] items= tree.getSelection();
    Rectangle clientArea= tree.getClientArea();
    switch (items.length) {
      case 0:
        return null;
      case 1:
        Rectangle bounds= items[0].getBounds();
        Rectangle intersect= clientArea.intersection(bounds);
        if (intersect != null && intersect.height == bounds.height) {
          return new Point(
            Math.max(0, bounds.x + getAvarageCharWith(tree) * CHAR_INDENT),
            bounds.y + bounds.height);
        } else {
          return null;
        }
      default:
        Rectangle[] rectangles= new Rectangle[items.length];
        for (int i= 0; i < rectangles.length; i++) {
          rectangles[i]= items[i].getBounds();
        }
        Point cursorLocation= tree.getDisplay().getCursorLocation();
        Point result= findBestLocation(getIncludedPositions(rectangles, clientArea),
          tree.toControl(cursorLocation));
        if (result != null)
          result.x= result.x + getAvarageCharWith(tree) * CHAR_INDENT;
        return result;
    }
  }
 
  /**
   * Hook to compute the menu location if the focus widget is
   * a table widget.
   *
   * @param table the table widget that has the focus
   *
   * @return a widget relative position of the menu to pop up or
   *  <code>null</code> if now position inside the widget can
   *  be computed
   */
  protected Point computeMenuLocation(Table table) {
    TableItem[] items= table.getSelection();
    Rectangle clientArea= table.getClientArea();
    switch (items.length) {
      case 0: {
        return null;
      } case 1: {
        Rectangle bounds= items[0].getBounds(0);
        Rectangle iBounds= items[0].getImageBounds(0);
        Rectangle intersect= clientArea.intersection(bounds);
        if (intersect != null && intersect.height == bounds.height) {
          return new Point(
            Math.max(0, bounds.x + iBounds.width + getAvarageCharWith(table) * CHAR_INDENT),
            bounds.y + bounds.height);
        } else {
          return null;
        }
      } default: {
        Rectangle[] rectangles= new Rectangle[items.length];
        for (int i= 0; i < rectangles.length; i++) {
          rectangles[i]= items[i].getBounds(0);
        }
        Rectangle iBounds= items[0].getImageBounds(0);
        Point cursorLocation= table.getDisplay().getCursorLocation();
        Point result= findBestLocation(getIncludedPositions(rectangles, clientArea),
          table.toControl(cursorLocation));
        if (result != null)
          result.x= result.x + iBounds.width + getAvarageCharWith(table) * CHAR_INDENT;
        return result;
      }
    }
  }
 
  private Point[] getIncludedPositions(Rectangle[] rectangles, Rectangle widgetBounds) {
    List result= new ArrayList();
    for (int i= 0; i < rectangles.length; i++) {
      Rectangle rectangle= rectangles[i];
      Rectangle intersect= widgetBounds.intersection(rectangle);
      if (intersect != null && intersect.height == rectangle.height) {
        result.add(new Point(intersect.x, intersect.y + intersect.height));
      }
    }
    return (Point[]) result.toArray(new Point[result.size()]);
  }
 
  private Point findBestLocation(Point[] points, Point relativeCursor) {
    Point result= null;
    double bestDist= Double.MAX_VALUE;
    for (int i= 0; i < points.length; i++) {
      Point point= points[i];
      int a= 0;
      int b= 0;
      if (point.x > relativeCursor.x) {
        a= point.x - relativeCursor.x;
      } else {
        a= relativeCursor.x - point.x;
      }
      if (point.y > relativeCursor.y) {
        b= point.y - relativeCursor.y; 
      } else {
        b= relativeCursor.y - point.y;
      }
      double dist= Math.sqrt(a * a + b * b);
      if (dist < bestDist) {
        result= point;
        bestDist= dist;
      }
    }
    return result;
  }
 
  private int getAvarageCharWith(Control control) {
    GC gc= null;
    try {
      gc= new GC(control);
      return gc.getFontMetrics().getAverageCharWidth();
    } finally {
      if (gc != null)
        gc.dispose();
    }
  }
}
TOP

Related Classes of de.innovationgate.eclipse.utils.ui.QuickMenuAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.