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}