SpaceTimeMiniCanvas.java

Go to the documentation of this file.
00001 package edu.rice.cs.hpc.traceviewer.misc;
00002 
00003 import org.eclipse.core.commands.ExecutionException;
00004 import org.eclipse.core.commands.operations.IOperationHistoryListener;
00005 import org.eclipse.core.commands.operations.IUndoableOperation;
00006 import org.eclipse.core.commands.operations.OperationHistoryEvent;
00007 import org.eclipse.core.commands.operations.OperationHistoryFactory;
00008 import org.eclipse.swt.SWT;
00009 import org.eclipse.swt.SWTException;
00010 import org.eclipse.swt.events.ControlAdapter;
00011 import org.eclipse.swt.events.ControlEvent;
00012 import org.eclipse.swt.events.DisposeEvent;
00013 import org.eclipse.swt.events.DisposeListener;
00014 import org.eclipse.swt.events.MouseEvent;
00015 import org.eclipse.swt.events.PaintEvent;
00016 import org.eclipse.swt.events.PaintListener;
00017 import org.eclipse.swt.graphics.Color;
00018 import org.eclipse.swt.graphics.GC;
00019 import org.eclipse.swt.graphics.Image;
00020 import org.eclipse.swt.graphics.Pattern;
00021 import org.eclipse.swt.graphics.Point;
00022 import org.eclipse.swt.graphics.Rectangle;
00023 import org.eclipse.swt.widgets.Composite;
00024 import edu.rice.cs.hpc.data.experiment.extdata.IBaseData;
00025 import edu.rice.cs.hpc.traceviewer.data.util.Debugger;
00026 import edu.rice.cs.hpc.traceviewer.operation.BufferRefreshOperation;
00027 import edu.rice.cs.hpc.traceviewer.operation.TraceOperation;
00028 import edu.rice.cs.hpc.traceviewer.operation.ZoomOperation;
00029 import edu.rice.cs.hpc.traceviewer.painter.ITraceCanvas;
00030 import edu.rice.cs.hpc.traceviewer.painter.ImageTraceAttributes;
00031 import edu.rice.cs.hpc.traceviewer.painter.SpaceTimeCanvas;
00032 import edu.rice.cs.hpc.traceviewer.spaceTimeData.Frame;
00033 import edu.rice.cs.hpc.traceviewer.spaceTimeData.SpaceTimeDataController;
00034 
00035 /*****************************************************************************
00036  * 
00037  * The Canvas onto which the MiniMap is painted.
00038  * 
00039  ****************************************************************************/
00040 
00041 public class SpaceTimeMiniCanvas extends SpaceTimeCanvas 
00042     implements ITraceCanvas, PaintListener, IOperationHistoryListener
00043 {
00045     private MouseState mouseState;
00046     
00048     private Point mouseDown;
00049     
00051     private Point mousePrevious;
00052     
00054     private Point mouseUp;
00055     
00057     private boolean insideBox;
00058     
00059     private Rectangle view;
00060     
00061     final private Color COMPLETELY_FILTERED_OUT_COLOR;
00062     final private Color NOT_FILTERED_OUT_COLOR;
00063     final private Color COLOR_BLACK, COLOR_GRAY;
00064     
00069     private final Pattern PARTIALLY_FILTERED_PATTERN; 
00070 
00072     public SpaceTimeMiniCanvas(Composite _composite)
00073     {   
00074         super(_composite);
00075         
00076         mouseState = MouseState.ST_MOUSE_INIT;
00077         insideBox = true;
00078         view =  new Rectangle(0, 0, 0, 0);
00079         
00080         // initialize colors
00081         COMPLETELY_FILTERED_OUT_COLOR = new Color(this.getDisplay(), 50,50,50);
00082         NOT_FILTERED_OUT_COLOR = getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
00083         COLOR_BLACK = getDisplay().getSystemColor(SWT.COLOR_BLACK);
00084         COLOR_GRAY  = getDisplay().getSystemColor(SWT.COLOR_GRAY);
00085 
00086         // initialize pattern for filtered ranks
00087         PARTIALLY_FILTERED_PATTERN = createStripePattern();
00088         
00089         addDisposeListener( new DisposeListener() {
00090             
00091             @Override
00092             public void widgetDisposed(DisposeEvent e) {
00093                 dispose();              
00094             }
00095         });
00096         addControlListener( new ControlAdapter() {
00097             
00098             @Override
00099             public void controlResized(ControlEvent e) {
00100                 if (stData != null) {
00101                     final Frame frame = stData.getAttributes().getFrame();
00102                     setBox(frame);
00103                 }
00104             }
00105         } );
00106     }
00107 
00108     /*
00109      * (non-Javadoc)
00110      * @see org.eclipse.swt.widgets.Widget#dispose()
00111      */
00112     @Override
00113     public void dispose() {
00114         COMPLETELY_FILTERED_OUT_COLOR.dispose();
00115         PARTIALLY_FILTERED_PATTERN.dispose();
00116     }
00117     
00118     /*****
00119      * Create a pattern image for representing a non-dense filter region
00120      * 
00121      * @return pattern
00122      */
00123     private Pattern createStripePattern() {
00124             Image image = new Image(getDisplay(), 15, 15);
00125             GC gc = new GC(image);
00126             gc.setBackground(NOT_FILTERED_OUT_COLOR);
00127             gc.fillRectangle(image.getBounds());
00128             gc.setForeground(COMPLETELY_FILTERED_OUT_COLOR);
00129             // Oddly enough, drawing from points outside of the image makes the
00130             // lines look a lot better when the pattern is tiled.
00131             for (int i = 5; i < 15; i+= 5) {
00132                     gc.drawLine(-5, i+5, i+5, -5);
00133                     gc.drawLine(i-5, 20, 20, i-5);
00134             }
00135             gc.dispose();
00136             return new Pattern(getDisplay(), image);
00137     }
00138 
00139 
00140     /**********
00141      * update the content of the view due to a new database
00142      * 
00143      * @param _stData : the new database
00144      **********/
00145     public void updateView(SpaceTimeDataController _stData) 
00146     {
00147         setSpaceTimeData(_stData);
00148 
00149         if (this.mouseState == MouseState.ST_MOUSE_INIT) {
00150             this.mouseState = MouseState.ST_MOUSE_NONE;
00151 
00152             addMouseListener(this);
00153             addMouseMoveListener(this);
00154             addPaintListener(this);
00155             
00156             OperationHistoryFactory.getOperationHistory().addOperationHistoryListener(this);
00157         }
00158         Rectangle r = this.getClientArea();
00159         view.x = 0;
00160         view.y = 0;
00161         view.height = r.height;
00162         view.width  = r.width;
00163         
00164         redraw();
00165     }
00166     
00167     /******
00168      * update the view when a filtering event occurs
00169      * in this case, we need to reset the content of view with the attribute
00170      */
00171     public void updateView() 
00172     {
00173         final Frame frame = stData.getAttributes().getFrame();      
00174         IBaseData baseData = stData.getBaseData();
00175 
00176         int p1 = (int) Math.round( (frame.begProcess+baseData.getFirstIncluded()) * getScalePixelsPerRank() );
00177         int p2 = (int) Math.round( (frame.endProcess+baseData.getFirstIncluded()) * getScalePixelsPerRank() );
00178         
00179         int t1 = (int) Math.round( frame.begTime * getScalePixelsPerTime() );
00180         int t2 = (int) Math.round( frame.endTime * getScalePixelsPerTime() );
00181         
00182         int dp = Math.max(p2-p1, 1);
00183         int dt = Math.max(t2-t1, 1);
00184 
00185         view.x = t1;
00186         view.y = p1;
00187         view.width  = dt; 
00188         view.height = dp;
00189         
00190         redraw();
00191     }
00192     
00194     public void paintControl(PaintEvent event)
00195     {
00196         if (this.stData == null)
00197             return;
00198         
00199         final Rectangle clientArea = getClientArea();
00200         
00201         // paint the background with black color
00202         
00203         event.gc.setBackground(COLOR_BLACK);
00204         event.gc.fillRectangle(clientArea);
00205         
00206         // paint the current view
00207         
00208         final Frame frame = stData.getAttributes().getFrame();      
00209         IBaseData baseData = stData.getBaseData();
00210 
00211         int p1 = (int) Math.round( (baseData.getFirstIncluded()) * getScalePixelsPerRank() );
00212         int p2 = (int) Math.round( (baseData.getLastIncluded()+1) * getScalePixelsPerRank() );
00213         
00214         int t1 = (int) Math.round( frame.begTime * getScalePixelsPerTime() );
00215         int t2 = (int) Math.round( frame.endTime * getScalePixelsPerTime() );
00216         
00217         int dp = Math.max(p2-p1, 1);
00218         int dt = Math.max(t2-t1, 1);
00219 
00220         if (baseData.isDenseBetweenFirstAndLast()){
00221             event.gc.setBackground(NOT_FILTERED_OUT_COLOR);
00222         } else {
00223             try{
00224             event.gc.setBackgroundPattern(PARTIALLY_FILTERED_PATTERN);
00225             }
00226             catch (SWTException e){
00227                     System.out.println("Advanced graphics not supported");
00228                     event.gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_RED));
00229             }
00230         }
00231         // The width of the region is always the same as the width of the
00232         // minimap because you can't filter by time
00233         event.gc.fillRectangle(0, p1, getClientArea().width, dp);
00234 
00235         // only for the dense region. For non-dense region, it's too tricky
00236         if (baseData.isDenseBetweenFirstAndLast()) {
00237             // original current position 
00238             p1 = (int) Math.round( (baseData.getFirstIncluded() +  frame.begProcess )* getScalePixelsPerRank());
00239             p2 = (int) Math.round( (baseData.getFirstIncluded() +  frame.endProcess )* getScalePixelsPerRank());
00240             dp = Math.max(1, p2 - p1);
00241             
00242             event.gc.setBackground(COLOR_GRAY);     
00243             event.gc.fillRectangle(t1, p1, dt, dp);
00244         }
00245 
00246         if (insideBox) {
00247             // when we move the box
00248             event.gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE));
00249             event.gc.fillRectangle(view);
00250         } else {
00251             // when we want to create a new box
00252             event.gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_YELLOW));
00253             event.gc.drawRectangle(view);
00254         }
00255     }
00256     
00257     
00310     private void setBox(Frame frame)
00311     {
00312         if (this.stData == null)
00313             return;
00314         
00315         view.x = (int)Math.round(frame.begTime * getScalePixelsPerTime());
00316         view.y = (int)Math.round(frame.begProcess * getScalePixelsPerRank());
00317         
00318         int bottomRightPixelX = (int)Math.round(frame.endTime * getScalePixelsPerTime());
00319         int bottomRightPixelY = (int)Math.round(frame.endProcess * getScalePixelsPerRank());
00320         
00321         view.width  = Math.max(1, bottomRightPixelX-view.x);
00322         view.height = Math.max(1, bottomRightPixelY-view.y);
00323         
00324         Debugger.printDebug(1, "STMC set view: " + view);
00325         insideBox = true;
00326         redraw();
00327     }
00328     
00330     private void moveBox(Point mouseCurrent)
00331     {
00332         // compute the different cursor movement 
00333         int changeX = mouseCurrent.x-mousePrevious.x;
00334         int changeY = mouseCurrent.y-mousePrevious.y;
00335         
00336         // update the values of the view based on the different cursor movement
00337         view.x += changeX;
00338         view.y += changeY;
00339         
00340         // make sure that the view is not out of range
00341 
00342         view.x = Math.max(view.x, 0);
00343         view.y = Math.max(view.y, getLowestY());
00344 
00345         // ---------------------------------------------------------
00346         // make sure that the selected box is within the range
00347         // ---------------------------------------------------------
00348         checkRegion(view);
00349 
00350         mousePrevious = mouseCurrent;
00351     }
00352     
00353 
00355     private void confirmNewRegion()
00356     {
00357         Point miniTopLeft = new Point( view.x, view.y);
00358         Point miniBottomRight = new Point( view.x+view.width, view.y+view.height);
00359         
00360         final IBaseData data = stData.getBaseData();
00361         
00362         long detailTopLeftTime = (long)(miniTopLeft.x/getScalePixelsPerTime());
00363         int detailTopLeftProcess = (int) Math.round( miniTopLeft.y/getScalePixelsPerRank() - data.getFirstIncluded());
00364         
00365         long detailBottomRightTime = (long)(miniBottomRight.x / getScalePixelsPerTime());
00366         int detailBottomRightProcess = (int) Math.round( miniBottomRight.y/getScalePixelsPerRank()) - data.getFirstIncluded();
00367         
00368         // hack: make sure p2 > than p1 since the detail canvas assumes exclusive p2 :-(
00369         if (detailBottomRightProcess-detailTopLeftProcess <= 0) {
00370             detailBottomRightProcess = detailTopLeftProcess + 1;
00371         }
00372         
00373         final Frame originalFrame = stData.getAttributes().getFrame();
00374         
00375         // copy the frame from the original one so that we can copy the values of depth and position
00376         Frame frame = new Frame( originalFrame );
00377         frame.set(detailTopLeftTime, detailBottomRightTime, detailTopLeftProcess, detailBottomRightProcess);
00378         
00379         int totTraces = stData.getTotalTraceCount();
00380         
00381         assert frame.begProcess >= 0 && frame.begProcess < totTraces : 
00382             "incorrect beg rank: " + frame.begProcess + " should be in the range [0," + 
00383             totTraces + "]";
00384         
00385         assert frame.endProcess <= totTraces && frame.endProcess > 0 :
00386             "incorrect end rank: " + frame.endProcess + " should be in the range [ 0," 
00387              + totTraces + "]";
00388         
00389         // do not submit new frame is the region area is the same as the old one
00390         if (!frame.equals(originalFrame))
00391             notifyRegionChangeOperation(frame);
00392     }
00393     
00394     /****
00395      * notify to other views that we have changed the region to view
00396      * 
00397      * @param frame
00398      */
00399     private void notifyRegionChangeOperation( Frame frame )
00400     {
00401         try {
00402             TraceOperation.getOperationHistory().execute(
00403                     new ZoomOperation("Change region", frame),
00404                     null, null);
00405         } catch (ExecutionException e) 
00406         {
00407             e.printStackTrace();
00408         }
00409     }
00410     
00411     /****
00412      * retrieve the highest Y pixels based on the number of visible ranks
00413      * 
00414      * @return
00415      */
00416     private int getHighestY() {
00417         
00418         final IBaseData baseData = stData.getBaseData();
00419         int highestRank = baseData.getLastIncluded()+1;
00420         return (int) Math.round(highestRank * getScalePixelsPerRank());
00421     }
00422     
00423     /****
00424      * retrieve the minimum Y pixel based on the number of visible ranks
00425      * In case of filters, the lowest rank can be other than zero
00426      * 
00427      * @return
00428      */
00429     private int getLowestY() {
00430         final IBaseData baseData = stData.getBaseData();
00431         final int lowestRank = baseData.getFirstIncluded();
00432         return (int) Math.round(lowestRank * getScalePixelsPerRank());
00433     }
00434     
00436     private void adjustSelection(Point p1, Point p2)
00437     {
00438         // ---------------------------------------------------------
00439         // get the region of the selection
00440         // ---------------------------------------------------------
00441         view.x = Math.max(0, Math.min(p1.x, p2.x) );
00442         view.y = Math.max(getLowestY(), Math.min(p1.y, p2.y) );
00443         
00444         view.width = Math.abs( p1.x - p2.x );
00445         view.height = Math.abs( p1.y - p2.y );
00446         
00447         // ---------------------------------------------------------
00448         // make sure that the selected box is within the range
00449         // ---------------------------------------------------------
00450         checkRegion(view);
00451     }
00452     
00453     /****
00454      * check if the new region is within the allowed area
00455      * We just assume the start x and y are already good, and now
00456      *  we check the end x and y which is very tricky
00457      * @param region
00458      */
00459     private void checkRegion(Rectangle region) 
00460     {
00461         final Rectangle area = getClientArea();
00462         
00463         // check if the width is within the range
00464         if ( region.x + region.width > area.width )
00465             region.x = area.width - region.width;
00466         
00467         // check if the height is within the view
00468         int y_end = region.y + region.height; 
00469         if ( y_end > area.height )
00470             region.y = area.height - region.height;
00471         
00472         int highestY = getHighestY();
00473         int lowestY = getLowestY();
00474         
00475         // make sure the end y is less than the max y
00476         if (y_end > highestY) {
00477             region.y = Math.max(highestY - region.height, lowestY);
00478             
00479             final int maxHeight = highestY - lowestY;
00480             region.height = Math.min(region.height, maxHeight);
00481         }
00482         
00483         // make sure the start y is less than the max y
00484         region.y = Math.min(region.y, highestY - 1);
00485     }
00486     
00488     public double getScalePixelsPerTime()
00489     {
00490         return (double)getClientArea().width / (double)stData.getTimeWidth();
00491     }
00492 
00494     public double getScalePixelsPerRank()
00495     {
00496         final IBaseData data = stData.getBaseData();
00497         final Rectangle area = getClientArea();
00498         return (double)area.height / (data.getNumberOfRanks());
00499     }
00500 
00501     
00502     /* *****************************************************************
00503      *      
00504      *      MouseListener and MouseMoveListener interface Implementation
00505      *      
00506      ******************************************************************/
00507 
00508     public void mouseDown(MouseEvent e)
00509     {
00510         if (mouseState == MouseState.ST_MOUSE_NONE)
00511         {
00512             mouseState = MouseState.ST_MOUSE_DOWN;
00513             mouseDown = new Point(e.x,e.y);
00514             mousePrevious = new Point(e.x,e.y);
00515             
00516             insideBox = ( mouseDown.x>=view.x && 
00517                     mouseDown.x<=view.x+view.width && 
00518                     mouseDown.y>=view.y &&  
00519                     mouseDown.y<=view.y+view.height );
00520         }
00521     }
00522 
00523     public void mouseUp(MouseEvent e)
00524     {
00525         if (mouseState == MouseState.ST_MOUSE_DOWN)
00526         {
00527             mouseUp = new Point(e.x,e.y);
00528             mouseState = MouseState.ST_MOUSE_NONE;
00529             if (insideBox)
00530             {
00531                 moveBox(mouseUp);
00532                 confirmNewRegion();
00533             }
00534             else
00535             {
00536                 // If the user draws a very small region to zoom in on, we are
00537                 // going to assume it was a mistake and not draw anything.
00538                 if(Math.abs(mouseUp.x-mouseDown.x)>3 || Math.abs(mouseUp.y-mouseDown.y)>3) 
00539                 {
00540                     adjustSelection(mouseDown, mouseUp);    
00541                     confirmNewRegion();
00542                 }else {
00543                     final ImageTraceAttributes attributes = stData.getAttributes();
00544                     //Set the selection box back to what it was because we didn't zoom
00545                     setBox(attributes.getFrame());
00546                 }
00547             }
00548         }
00549     }
00550     
00551     public void mouseMove(MouseEvent e)
00552     {
00553         if(mouseState == MouseState.ST_MOUSE_DOWN)
00554         {
00555             Point mouseCurrent = new Point(e.x,e.y);
00556             if (insideBox)
00557                 moveBox(mouseCurrent);
00558             else
00559                 adjustSelection(mouseDown, mouseCurrent);
00560             
00561             redraw();
00562         }
00563     }
00564 
00565     @Override
00566     public void mouseDoubleClick(MouseEvent e) {    }
00567     
00568     @Override
00569     /*
00570      * (non-Javadoc)
00571      * @see org.eclipse.core.commands.operations.IOperationHistoryListener#historyNotification(org.eclipse.core.commands.operations.OperationHistoryEvent)
00572      */
00573     public void historyNotification(final OperationHistoryEvent event) {
00574         final IUndoableOperation operation = event.getOperation();
00575 
00576         if (operation.hasContext(BufferRefreshOperation.context)) {
00577             final Frame frame = stData.getAttributes().getFrame();
00578             setBox(frame);
00579         }
00580     }
00581 }

Generated on 5 May 2015 for HPCVIEWER by  doxygen 1.6.1