    001package org.vishia.gral.swt;
    002
    003import org.eclipse.swt.SWT;
    004import org.eclipse.swt.events.MouseEvent;
    005import org.eclipse.swt.events.MouseListener;
    006import org.eclipse.swt.events.MouseMoveListener;
    007import org.eclipse.swt.graphics.Color;
    008import org.eclipse.swt.graphics.Point;
    009import org.eclipse.swt.widgets.Control;
    010import org.eclipse.swt.widgets.Widget;
    011import org.vishia.gral.base.GralWidget;
    012import org.vishia.gral.base.GralMng;
    013import org.vishia.gral.base.GralMouseWidgetAction_ifc;
    014import org.vishia.gral.ifc.GralRectangle;
    015import org.vishia.gral.ifc.GralUserAction;
    016import org.vishia.gral.ifc.GralWidget_ifc;
    017import org.vishia.util.Assert;
    018import org.vishia.util.KeyCode;
    019
    020/**This class contains implementations of {@link MouseListener} for Gral adaption of SWT.
    021 * The static inner classes implements the MouseListener
    022 * @author Hartmut Schorrig
    023 *
    024 */
    025public final class SwtGralMouseListener
    026{
    027  /**Version, History and copyright
    028   * <ul>
    029   * <li>2015-09-21 Hartmut chg: The capabilities of {@link MouseListenerNoAction} is contained in {@link MouseListenerGralAction} yet.
    030   *   Therefore it is possible to get a mouse action in any {@link GralWidget#setActionChange(GralUserAction)}.
    031   * <li>2014-09-21 Hartmut chg: Using of {@link GralWidget#toString()} for double click info.
    032   * <li>2013-05-13 Hartmut chg: All methods of {@link GralMouseWidgetAction_ifc} changed, parameter key, position.
    033   * <li>2012-10-10 Hartmut the keycode for mouse pressed user actions contains pressing ctrl, alt, sh too.
    034   * <li>2012-03-09 Hartmut The methods {@link GralMouseWidgetAction_ifc#mouse1Up()} etc. are not called
    035   *   if the cursor was removed from the widget.
    036   * </ul>
    037   *
    038   * <b>Copyright/Copyleft</b>:<br>
    039   * For this source the LGPL Lesser General Public License,
    040   * published by the Free Software Foundation is valid.
    041   * It means:
    042   * <ol>
    043   * <li> You can use this source without any restriction for any desired purpose.
    044   * <li> You can redistribute copies of this source to everybody.
    045   * <li> Every user of this source, also the user of redistribute copies
    046   *    with or without payment, must accept this license for further using.
    047   * <li> But the LPGL is not appropriate for a whole software product,
    048   *    if this source is only a part of them. It means, the user
    049   *    must publish this part of source,
    050   *    but doesn't need to publish the whole source of the own product.
    051   * <li> You can study and modify (improve) this source
    052   *    for own using or for redistribution, but you have to license the
    053   *    modified sources likewise under this LGPL Lesser General Public License.
    054   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
    055   * </ol>
    056   * If you intent to use this source without publishing its usage, you can get
    057   * a second license subscribing a special contract with the author.
    058   *
    059   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
    060   */
    061  public static final int version = 20120309;
    062
    063
    064  /**This class implements a MouseListener which does not call a user method.
    065   * Only the information about the clicked widget are stored in the GralMng
    066   * and the Gral designer is supported.
    067   *
    068   */
    069  private static class MouseListenerNoAction implements MouseListener
    070  {
    071
    072
    073    public MouseListenerNoAction()
    074    {
    075    }
    076
    077    int xDown, yDown;
    078
    079    /**The mouse doubleclick is left empty. It may be overridden by an derived class. */
    080    @Override public void mouseDoubleClick(MouseEvent arg0)
    081    {
    082      Widget widget = arg0.widget;
    083      Object oInfo = widget.getData();
    084      GralWidget widgg = GralWidget.ImplAccess.gralWidgetFromImplData(oInfo);
    085      if(widgg !=null){
    086        GralMng guiMng = widgg.gralMng();
    087        try{
    088          String widggInfo = widgg.toString();
    089          guiMng.log.sendMsg(0, "Info widget: %s", widggInfo);
    090          //guiMng.log.sendMsg(0, "Info widget: %s / %s", widgg.name, widgg.getDataPath());
    091        } catch(Exception exc){ guiMng.writeLog(0, exc); }
    092
    093      }
    094
    095    }
    096
    097    /**The mouse-down action save some informations about the widget.
    098     * It may be overridden by an derived class, then this method should be invoked within.
    099     */
    100    @Override public void mouseDown(MouseEvent ev)
    101    {
    102      Widget widget = ev.widget;
    103      Object oInfo = widget.getData();
    104      GralWidget widgg = GralWidget.ImplAccess.gralWidgetFromImplData(oInfo);
    105      if(widgg !=null){
    106        GralMng guiMng = widgg.gralMng();
    107        try{
    108          String sDataPath = widgg.getDataPath();
    109          if( sDataPath !=null  //no datapath given, write info!
    110            && !sDataPath.equals("widgetInfo")  //don't write info if it is a widgetInfo widget itself.
    111            ){
    112            guiMng.setLastClickedWidget(widgg );
    113          }
    114          if(guiMng.bDesignMode){
    115            GralRectangle rr = new GralRectangle(ev.x, ev.y, 0, 0);
    116            if(ev.button == 1){ //left
    117              xDown = ev.x; yDown = ev.y;
    118              guiMng.pressedLeftMouseDownForDesign(widgg, rr);
    119            } else if(ev.button == 3){ //right
    120              //guiMng.pressedRightMouseDownForDesign(widgetInfo, rr);
    121            }
    122          }
    123        } catch(Exception exc){ guiMng.writeLog(0, exc); }
    124
    125    }
    126
    127    }
    128
    129    /**The default behavior for mouse up is used for design mode. */
    130    @Override public void mouseUp(MouseEvent ev)
    131  {   Widget widget = ev.widget;
    132      Object oInfo = widget.getData();
    133      GralWidget widgg = GralWidget.ImplAccess.gralWidgetFromImplData(oInfo);
    134      if(widgg !=null){
    135        GralMng guiMng = widgg.gralMng();
    136        try{
    137          int dx = ev.x - xDown, dy = ev.y - yDown;
    138          if(guiMng.bDesignMode && ev.button == 1){
    139            if((ev.stateMask & SWT.ALT)!=0){
    140              boolean bCopy = (ev.stateMask & org.eclipse.swt.SWT.CTRL) !=0;
    141              GralRectangle rr = new GralRectangle(ev.x, ev.y, 0, 0);
    142              guiMng.releaseLeftMouseForDesign(widgg, rr, bCopy);
    143            } else {
    144              guiMng.markWidgetForDesign(widgg);
    145            }
    146          }
    147          //widgd.redraw();
    148        } catch(Exception exc){ guiMng.writeLog(0, exc); }
    149
    150      }
    151
    152    }
    153
    154
    155    private final MouseMoveListener mouseMoveListenerDesignMode = new MouseMoveListener()
    156    {
    157
    158      @Override public void mouseMove(MouseEvent e)
    159      {
    160        //xMouse = e.x;
    161        //yMouse = e.y;
    162      }//method mouseMove
    163    };
    164
    165
    166
    167  }
    168
    169
    170
    171
    172
    173
    174  /**This class is the implementation of a SWT {@link MouseListener} and  implements methods which invokes the
    175   * {@link GralWidget#setActionMouse(GralMouseWidgetAction_ifc, int)} or the {@link GralWidget#setActionChange(GralUserAction)}
    176   * on the determined mouse clicks.
    177   * @author Hartmut Schorrig
    178   *
    179   */
    180  public static class MouseListenerGralAction extends MouseListenerNoAction
    181  implements MouseListener
    182  {
    183
    184    /**Positions saved on mouse press, to detect whether the mouse-release occurs in the pressed area.
    185     * If the mouse-position is shifted outside the area of the widget, the mouse-release-user-action
    186     * is not executed.
    187     */
    188    private int xMousePress, yMousePress;
    189
    190    /**Used in the implementation level for the paint routine. Therefore it is package private.
    191     */
    192    protected boolean isPressed;
    193
    194
    195
    196    /**Constructor.
    197     * @param mouseWidgetAction Action invoked, maybe null
    198     * @param mUser 0 or or-combinations of bits in {@link GralMouseWidgetAction_ifc#mUser1down}
    199     *   and all other mUser... If one of this bits is set, the {@link GralWidget#setActionChange(GralUserAction)}
    200     *   is invoked on the appropriate mouse action after and independent of the mouseWidgetAction.
    201     */
    202    public MouseListenerGralAction()
    203    { super();
    204    }
    205
    206
    207
    208    @Override
    209    public void mouseDoubleClick(MouseEvent e) {
    210      xMousePress = e.x;
    211      yMousePress = e.y;
    212      Control widget = (Control) e.widget;  //a widget is a Control always.
    213      GralWidget widgg = GralWidget.ImplAccess.gralWidgetFromImplData(widget.getData());
    214      try{
    215        String widggInfo = widgg.toString();
    216        GralMng.get().log.sendMsg(0, "Info widget: %s", widggInfo);
    217        //guiMng.log.sendMsg(0, "Info widget: %s / %s", widgg.name, widgg.getDataPath());
    218      } catch(Exception exc){ GralMng.get().writeLog(0, exc); }
    219      try{
    220        final int keyCode = SwtGralKey.convertMouseKey(e.button, SwtGralKey.MouseAction.doubleClick, e.stateMask);
    221        Control widgetSwt = (Control) e.widget;  //a widget is a Control always.
    222        if(widgg.cfg.mouseWidgetAction !=null){
    223          Point size = widgetSwt.getSize();
    224          widgg.cfg.mouseWidgetAction.mouse1Double(keyCode, xMousePress, yMousePress, size.x, size.y, widgg);
    225        }
    226        boolean bStrict = (widgg.cfg.mMouseToActionChange & GralMouseWidgetAction_ifc.mUserDouble) ==0;  //search strict if no special bit is given.
    227        //then a strict mouse doube action wins.
    228        GralWidget_ifc.ActionChange action = widgg.getActionChangeStrict(GralWidget_ifc.ActionChangeWhen.onMouse1Double, bStrict);
    229        if(action !=null){
    230          Object[] args = action.args();
    231          if(args == null){ action.action().exec(KeyCode.mouse1Double, widgg, new Integer(e.x), new Integer(e.y)); }
    232          else {
    233            //additional 2 arguments: copy in one args2.
    234            Object[] args2 = new Object[args.length +2];
    235            System.arraycopy(args, 0, args2, 0, args.length);
    236            args2[args.length] = new Integer(e.x);
    237            args2[args.length+1] = new Integer(e.y);
    238            action.action().exec(KeyCode.mouse1Double, widgg, args2);
    239          }
    240        }
    241      } catch(Exception exc){ System.err.printf("SwtGralMouseListener - any exception while mouse double; %s\n", exc.getMessage()); }
    242    }
    243
    244    @Override public void mouseDown(MouseEvent ev) {
    245      super.mouseDown(ev);
    246      isPressed = true;
    247      xMousePress = ev.x;
    248      yMousePress = ev.y;
    249      Control widget = (Control) ev.widget;  //a widget is a Control always.
    250      widget.addMouseMoveListener(mouseMoveListener);
    251      Object owdgg = widget.getData();
    252      GralWidget widgg = GralWidget.ImplAccess.gralWidgetFromImplData(owdgg);
    253      GralMng guiMng = widgg.gralMng();
    254      try{
    255        String sDataPath = widgg.getDataPath();
    256        if( sDataPath !=null  //no datapath given, write info!
    257          && !sDataPath.equals("widgetInfo")  //don't write info if it is a widgetInfo widget itself.
    258          ){
    259          guiMng.setLastClickedWidget(widgg );
    260        }
    261        if(guiMng.bDesignMode){
    262          GralRectangle rr = new GralRectangle(ev.x, ev.y, 0, 0);
    263          if(ev.button == 1){ //left
    264            xDown = ev.x; yDown = ev.y;
    265            guiMng.pressedLeftMouseDownForDesign(widgg, rr);
    266          } else if(ev.button == 3){ //right
    267            //guiMng.pressedRightMouseDownForDesign(widgetInfo, rr);
    268          }
    269        }
    270      } catch(Exception exc){ guiMng.writeLog(0, exc); }
    271      try{
    272        final int keyCode = SwtGralKey.convertMouseKey(ev.button, SwtGralKey.MouseAction.down, ev.stateMask);
    273        GralWidget_ifc.ActionChangeWhen whenAction;
    274        final int mUser1;
    275        switch(ev.button){
    276          case 1: mUser1 = GralMouseWidgetAction_ifc.mUser1down; whenAction = GralWidget_ifc.ActionChangeWhen.onMouse1Dn; break;
    277          case 3: mUser1 = GralMouseWidgetAction_ifc.mUser2down; whenAction = null; break;  //the usual right button is 3 in SWT!
    278          case 2: mUser1 = GralMouseWidgetAction_ifc.mUser3down; whenAction = null; break;  //the usual middle button is 2 in SWT!
    279          default: mUser1 = 0; whenAction = null; break;
    280        }//switch:
    281        if(widgg.cfg.mouseWidgetAction !=null){
    282          Point size = widget.getSize();
    283          switch(ev.button){
    284            case 1:
    285              widgg.cfg.mouseWidgetAction.mouse1Down(keyCode, xMousePress, yMousePress, size.x, size.y, widgg);
    286              break;
    287            case 3:
    288              widgg.cfg.mouseWidgetAction.mouse2Down(keyCode, xMousePress, yMousePress, size.x, size.y, widgg);
    289              break;
    290          }
    291        }
    292        GralWidget_ifc.ActionChange action = widgg.getActionChangeStrict(whenAction, true);
    293        if(action !=null){
    294          Object[] args = action.args();
    295          if(args == null){ action.action().exec(keyCode, widgg, new Integer(ev.x), new Integer(ev.y)); }
    296          else {
    297            //additional 2 arguments: copy in one args2.
    298            Object[] args2 = new Object[args.length +2];
    299            System.arraycopy(args, 0, args2, 0, args.length);
    300            args2[args.length] = new Integer(ev.x);
    301            args2[args.length+1] = new Integer(ev.y);
    302            action.action().exec(keyCode, widgg, args2);
    303          }
    304        }
    305      } catch(Exception exc){ System.err.printf("SwtGralMouseListener - any exception while mouse down; %s\n", exc.getMessage()); }
    306    }
    307
    308
    309
    310
    311    @Override public void mouseUp(MouseEvent ev) {
    312      //set the background color to the originally value again if it was changed.
    313      super.mouseUp(ev);
    314      if(isPressed){  //prevent any action of mouse up if the mouse is not designated as pressed
    315        //especially if the mouse was removed from the widget.
    316        Control widget = (Control)ev.widget;
    317        widget.removeMouseMoveListener(mouseMoveListener);
    318        isPressed = false;
    319        Point size = widget.getSize();
    320        GralWidget widgg = GralWidget.ImplAccess.gralWidgetFromImplData(widget.getData());
    321        final int mUser1;
    322        GralWidget_ifc.ActionChangeWhen whenAction;
    323        switch(ev.button){
    324          case 1: mUser1 = GralMouseWidgetAction_ifc.mUser1up; whenAction = GralWidget_ifc.ActionChangeWhen.onMouse1Up; break;
    325          case 3: mUser1 = GralMouseWidgetAction_ifc.mUser2up; whenAction = GralWidget_ifc.ActionChangeWhen.onMouse2Up; break;  //the usual right button is 3 in SWT!
    326          case 2: mUser1 = GralMouseWidgetAction_ifc.mUser3up; whenAction = null; break;  //the usual middle button is 2 in SWT!
    327          default: mUser1 = 0; whenAction = null; break;
    328        }//switch:
    329        try{
    330          //int dx = e.x - xMousePress, dy = e.y - yMousePress;
    331          boolean moved = ev.x < 0 || ev.x > size.x || ev.y < 0 || ev.y > size.y;
    332          SwtGralKey.MouseAction mouseAction = moved ? SwtGralKey.MouseAction.upMovedOutside : SwtGralKey.MouseAction.up;
    333          final int keyCode = SwtGralKey.convertMouseKey(ev.button, mouseAction, ev.stateMask);
    334                  GralMng guiMng = widgg.gralMng();
    335          int dx = ev.x - xDown, dy = ev.y - yDown;
    336          if(guiMng.bDesignMode && ev.button == 1){
    337            if((ev.stateMask & SWT.ALT)!=0){
    338              boolean bCopy = (ev.stateMask & org.eclipse.swt.SWT.CTRL) !=0;
    339              GralRectangle rr = new GralRectangle(ev.x, ev.y, 0, 0);
    340              guiMng.releaseLeftMouseForDesign(widgg, rr, bCopy);
    341            } else {
    342              guiMng.markWidgetForDesign(widgg);
    343            }
    344          }
    345          //widgd.redraw();
    346
    347          if(widgg.cfg.mouseWidgetAction !=null){
    348            switch(ev.button){
    349              case 1:
    350                widgg.cfg.mouseWidgetAction.mouse1Up(keyCode, ev.x, ev.y, size.x, size.y, widgg);
    351                break;
    352              case 3:
    353                widgg.cfg.mouseWidgetAction.mouse2Up(keyCode, ev.x, ev.y, size.x, size.y, widgg);
    354                break;
    355            }
    356          }
    357          GralWidget_ifc.ActionChange action = widgg.getActionChangeStrict(whenAction, (widgg.cfg.mMouseToActionChange & mUser1) !=0);
    358          if(action !=null){
    359            Object[] args = action.args();
    360            if(args == null){ action.action().exec(keyCode, widgg, new Integer(ev.x), new Integer(ev.y)); }
    361            else {
    362              //additional 2 arguments: copy in one args2.
    363              Object[] args2 = new Object[args.length +2];
    364              System.arraycopy(args, 0, args2, 0, args.length);
    365              args2[args.length] = new Integer(ev.x);
    366              args2[args.length+1] = new Integer(ev.y);
    367              action.action().exec(keyCode, widgg, args2);
    368            }
    369          }
    370        } catch(Exception exc){
    371          CharSequence text = Assert.exceptionInfo("SwtGralMouseListener - any exception while mouse down;", exc, 0, 20);
    372          //System.err.append(text).append('\n');
    373          System.err.printf(text.toString());
    374        }
    375      }
    376    }
    377
    378
    379
    380
    381    /**TODO invoke user action if mouse releases the area
    382     *
    383     */
    384    protected MouseMoveListener mouseMoveListener = new MouseMoveListener()
    385    {
    386
    387      @Override public void mouseMove(MouseEvent e)
    388      {
    389        if(e.widget instanceof Control){
    390          Control widget = (Control)e.widget;
    391          GralWidget widgg = GralWidget.ImplAccess.gralWidgetFromImplData(widget.getData());
    392          Point size = widget.getSize();
    393          if(widgg.cfg.mouseWidgetAction !=null){
    394            if(!widgg.cfg.mouseWidgetAction.mouseMoved(e.x, e.y, size.x, size.y)){
    395              isPressed = false;
    396              widget.removeMouseMoveListener(mouseMoveListener);
    397            }
    398            //mouseWidgetAction.removeMouseCursorFromWidgetWhilePressed();
    399          }
    400        }
    401      }//method mouseMove
    402    };
    403
    404
    405  }
    406
    407
    408
    409  /**The only one instance can used for all widgets because the working data are given with the mouse action methods.
    410   * The associated GralWidget data are accessed via the data field (the GralWidget is known in all Widget implementations).
    411   */
    412  protected static MouseListener mouseActionStd = new MouseListenerGralAction();
    413
    414}