Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.hierarchyviewerlib.ui;
     18 
     19 import com.android.hierarchyviewerlib.device.ViewNode;
     20 import com.android.hierarchyviewerlib.models.PixelPerfectModel;
     21 import com.android.hierarchyviewerlib.models.PixelPerfectModel.IImageChangeListener;
     22 
     23 import org.eclipse.swt.SWT;
     24 import org.eclipse.swt.custom.ScrolledComposite;
     25 import org.eclipse.swt.events.DisposeEvent;
     26 import org.eclipse.swt.events.DisposeListener;
     27 import org.eclipse.swt.events.KeyEvent;
     28 import org.eclipse.swt.events.KeyListener;
     29 import org.eclipse.swt.events.MouseEvent;
     30 import org.eclipse.swt.events.MouseListener;
     31 import org.eclipse.swt.events.MouseMoveListener;
     32 import org.eclipse.swt.events.PaintEvent;
     33 import org.eclipse.swt.events.PaintListener;
     34 import org.eclipse.swt.graphics.Color;
     35 import org.eclipse.swt.graphics.Image;
     36 import org.eclipse.swt.graphics.Point;
     37 import org.eclipse.swt.graphics.RGB;
     38 import org.eclipse.swt.widgets.Canvas;
     39 import org.eclipse.swt.widgets.Composite;
     40 import org.eclipse.swt.widgets.Display;
     41 
     42 public class PixelPerfect extends ScrolledComposite implements IImageChangeListener {
     43     private Canvas mCanvas;
     44 
     45     private PixelPerfectModel mModel;
     46 
     47     private Image mImage;
     48 
     49     private Color mCrosshairColor;
     50 
     51     private Color mMarginColor;
     52 
     53     private Color mBorderColor;
     54 
     55     private Color mPaddingColor;
     56 
     57     private int mWidth;
     58 
     59     private int mHeight;
     60 
     61     private Point mCrosshairLocation;
     62 
     63     private ViewNode mSelectedNode;
     64 
     65     private Image mOverlayImage;
     66 
     67     private double mOverlayTransparency;
     68 
     69     public PixelPerfect(Composite parent) {
     70         super(parent, SWT.H_SCROLL | SWT.V_SCROLL);
     71         mCanvas = new Canvas(this, SWT.NONE);
     72         setContent(mCanvas);
     73         setExpandHorizontal(true);
     74         setExpandVertical(true);
     75         mModel = PixelPerfectModel.getModel();
     76         mModel.addImageChangeListener(this);
     77 
     78         mCanvas.addPaintListener(mPaintListener);
     79         mCanvas.addMouseListener(mMouseListener);
     80         mCanvas.addMouseMoveListener(mMouseMoveListener);
     81         mCanvas.addKeyListener(mKeyListener);
     82 
     83         addDisposeListener(mDisposeListener);
     84 
     85         mCrosshairColor = new Color(Display.getDefault(), new RGB(0, 255, 255));
     86         mBorderColor = new Color(Display.getDefault(), new RGB(255, 0, 0));
     87         mMarginColor = new Color(Display.getDefault(), new RGB(0, 255, 0));
     88         mPaddingColor = new Color(Display.getDefault(), new RGB(0, 0, 255));
     89 
     90         imageLoaded();
     91     }
     92 
     93     private DisposeListener mDisposeListener = new DisposeListener() {
     94         public void widgetDisposed(DisposeEvent e) {
     95             mModel.removeImageChangeListener(PixelPerfect.this);
     96             mCrosshairColor.dispose();
     97             mBorderColor.dispose();
     98             mPaddingColor.dispose();
     99         }
    100     };
    101 
    102     @Override
    103     public boolean setFocus() {
    104         return mCanvas.setFocus();
    105     }
    106 
    107     private MouseListener mMouseListener = new MouseListener() {
    108 
    109         public void mouseDoubleClick(MouseEvent e) {
    110             // pass
    111         }
    112 
    113         public void mouseDown(MouseEvent e) {
    114             handleMouseEvent(e);
    115         }
    116 
    117         public void mouseUp(MouseEvent e) {
    118             handleMouseEvent(e);
    119         }
    120 
    121     };
    122 
    123     private MouseMoveListener mMouseMoveListener = new MouseMoveListener() {
    124         public void mouseMove(MouseEvent e) {
    125             if (e.stateMask != 0) {
    126                 handleMouseEvent(e);
    127             }
    128         }
    129     };
    130 
    131     private void handleMouseEvent(MouseEvent e) {
    132         synchronized (PixelPerfect.this) {
    133             if (mImage == null) {
    134                 return;
    135             }
    136             int leftOffset = mCanvas.getSize().x / 2 - mWidth / 2;
    137             int topOffset = mCanvas.getSize().y / 2 - mHeight / 2;
    138             e.x -= leftOffset;
    139             e.y -= topOffset;
    140             e.x = Math.max(e.x, 0);
    141             e.x = Math.min(e.x, mWidth - 1);
    142             e.y = Math.max(e.y, 0);
    143             e.y = Math.min(e.y, mHeight - 1);
    144         }
    145         mModel.setCrosshairLocation(e.x, e.y);
    146     }
    147 
    148     private KeyListener mKeyListener = new KeyListener() {
    149 
    150         public void keyPressed(KeyEvent e) {
    151             boolean crosshairMoved = false;
    152             synchronized (PixelPerfect.this) {
    153                 if (mImage != null) {
    154                     switch (e.keyCode) {
    155                         case SWT.ARROW_UP:
    156                             if (mCrosshairLocation.y != 0) {
    157                                 mCrosshairLocation.y--;
    158                                 crosshairMoved = true;
    159                             }
    160                             break;
    161                         case SWT.ARROW_DOWN:
    162                             if (mCrosshairLocation.y != mHeight - 1) {
    163                                 mCrosshairLocation.y++;
    164                                 crosshairMoved = true;
    165                             }
    166                             break;
    167                         case SWT.ARROW_LEFT:
    168                             if (mCrosshairLocation.x != 0) {
    169                                 mCrosshairLocation.x--;
    170                                 crosshairMoved = true;
    171                             }
    172                             break;
    173                         case SWT.ARROW_RIGHT:
    174                             if (mCrosshairLocation.x != mWidth - 1) {
    175                                 mCrosshairLocation.x++;
    176                                 crosshairMoved = true;
    177                             }
    178                             break;
    179                     }
    180                 }
    181             }
    182             if (crosshairMoved) {
    183                 mModel.setCrosshairLocation(mCrosshairLocation.x, mCrosshairLocation.y);
    184             }
    185         }
    186 
    187         public void keyReleased(KeyEvent e) {
    188             // pass
    189         }
    190 
    191     };
    192 
    193     private PaintListener mPaintListener = new PaintListener() {
    194         public void paintControl(PaintEvent e) {
    195             synchronized (PixelPerfect.this) {
    196                 e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
    197                 e.gc.fillRectangle(0, 0, mCanvas.getSize().x, mCanvas.getSize().y);
    198                 if (mImage != null) {
    199                     // Let's be cool and put it in the center...
    200                     int leftOffset = mCanvas.getSize().x / 2 - mWidth / 2;
    201                     int topOffset = mCanvas.getSize().y / 2 - mHeight / 2;
    202                     e.gc.drawImage(mImage, leftOffset, topOffset);
    203                     if (mOverlayImage != null) {
    204                         e.gc.setAlpha((int) (mOverlayTransparency * 255));
    205                         int overlayTopOffset =
    206                                 mCanvas.getSize().y / 2 + mHeight / 2
    207                                         - mOverlayImage.getBounds().height;
    208                         e.gc.drawImage(mOverlayImage, leftOffset, overlayTopOffset);
    209                         e.gc.setAlpha(255);
    210                     }
    211 
    212                     if (mSelectedNode != null) {
    213                         // If the screen is in landscape mode, the
    214                         // coordinates are backwards.
    215                         int leftShift = 0;
    216                         int topShift = 0;
    217                         int nodeLeft = mSelectedNode.left;
    218                         int nodeTop = mSelectedNode.top;
    219                         int nodeWidth = mSelectedNode.width;
    220                         int nodeHeight = mSelectedNode.height;
    221                         int nodeMarginLeft = mSelectedNode.marginLeft;
    222                         int nodeMarginTop = mSelectedNode.marginTop;
    223                         int nodeMarginRight = mSelectedNode.marginRight;
    224                         int nodeMarginBottom = mSelectedNode.marginBottom;
    225                         int nodePadLeft = mSelectedNode.paddingLeft;
    226                         int nodePadTop = mSelectedNode.paddingTop;
    227                         int nodePadRight = mSelectedNode.paddingRight;
    228                         int nodePadBottom = mSelectedNode.paddingBottom;
    229                         ViewNode cur = mSelectedNode;
    230                         while (cur.parent != null) {
    231                             leftShift += cur.parent.left - cur.parent.scrollX;
    232                             topShift += cur.parent.top - cur.parent.scrollY;
    233                             cur = cur.parent;
    234                         }
    235 
    236                         // Everything is sideways.
    237                         if (cur.width > cur.height) {
    238                             e.gc.setForeground(mPaddingColor);
    239                             e.gc.drawRectangle(leftOffset + mWidth - nodeTop - topShift - nodeHeight
    240                                     + nodePadBottom,
    241                                     topOffset + leftShift + nodeLeft + nodePadLeft, nodeHeight
    242                                             - nodePadBottom - nodePadTop, nodeWidth - nodePadRight
    243                                             - nodePadLeft);
    244                             e.gc.setForeground(mMarginColor);
    245                             e.gc.drawRectangle(leftOffset + mWidth - nodeTop - topShift - nodeHeight
    246                                     - nodeMarginBottom, topOffset + leftShift + nodeLeft
    247                                     - nodeMarginLeft,
    248                                     nodeHeight + nodeMarginBottom + nodeMarginTop, nodeWidth
    249                                             + nodeMarginRight + nodeMarginLeft);
    250                             e.gc.setForeground(mBorderColor);
    251                             e.gc.drawRectangle(
    252                                     leftOffset + mWidth - nodeTop - topShift - nodeHeight, topOffset
    253                                             + leftShift + nodeLeft, nodeHeight, nodeWidth);
    254                         } else {
    255                             e.gc.setForeground(mPaddingColor);
    256                             e.gc.drawRectangle(leftOffset + leftShift + nodeLeft + nodePadLeft,
    257                                     topOffset + topShift + nodeTop + nodePadTop, nodeWidth
    258                                             - nodePadRight - nodePadLeft, nodeHeight
    259                                             - nodePadBottom - nodePadTop);
    260                             e.gc.setForeground(mMarginColor);
    261                             e.gc.drawRectangle(leftOffset + leftShift + nodeLeft - nodeMarginLeft,
    262                                     topOffset + topShift + nodeTop - nodeMarginTop, nodeWidth
    263                                             + nodeMarginRight + nodeMarginLeft, nodeHeight
    264                                             + nodeMarginBottom + nodeMarginTop);
    265                             e.gc.setForeground(mBorderColor);
    266                             e.gc.drawRectangle(leftOffset + leftShift + nodeLeft, topOffset
    267                                     + topShift + nodeTop, nodeWidth, nodeHeight);
    268                         }
    269                     }
    270                     if (mCrosshairLocation != null) {
    271                         e.gc.setForeground(mCrosshairColor);
    272                         e.gc.drawLine(leftOffset, topOffset + mCrosshairLocation.y, leftOffset
    273                                 + mWidth - 1, topOffset + mCrosshairLocation.y);
    274                         e.gc.drawLine(leftOffset + mCrosshairLocation.x, topOffset, leftOffset
    275                                 + mCrosshairLocation.x, topOffset + mHeight - 1);
    276                     }
    277                 }
    278             }
    279         }
    280     };
    281 
    282     private void doRedraw() {
    283         Display.getDefault().syncExec(new Runnable() {
    284             public void run() {
    285                 mCanvas.redraw();
    286             }
    287         });
    288     }
    289 
    290     private void loadImage() {
    291         mImage = mModel.getImage();
    292         if (mImage != null) {
    293             mWidth = mImage.getBounds().width;
    294             mHeight = mImage.getBounds().height;
    295         } else {
    296             mWidth = 0;
    297             mHeight = 0;
    298         }
    299         setMinSize(mWidth, mHeight);
    300     }
    301 
    302     public void imageLoaded() {
    303         Display.getDefault().syncExec(new Runnable() {
    304             public void run() {
    305                 synchronized (this) {
    306                     loadImage();
    307                     mCrosshairLocation = mModel.getCrosshairLocation();
    308                     mSelectedNode = mModel.getSelected();
    309                     mOverlayImage = mModel.getOverlayImage();
    310                     mOverlayTransparency = mModel.getOverlayTransparency();
    311                 }
    312             }
    313         });
    314         doRedraw();
    315     }
    316 
    317     public void imageChanged() {
    318         Display.getDefault().syncExec(new Runnable() {
    319             public void run() {
    320                 synchronized (this) {
    321                     loadImage();
    322                 }
    323             }
    324         });
    325         doRedraw();
    326     }
    327 
    328     public void crosshairMoved() {
    329         synchronized (this) {
    330             mCrosshairLocation = mModel.getCrosshairLocation();
    331         }
    332         doRedraw();
    333     }
    334 
    335     public void selectionChanged() {
    336         synchronized (this) {
    337             mSelectedNode = mModel.getSelected();
    338         }
    339         doRedraw();
    340     }
    341 
    342     // Note the syncExec and then synchronized... It avoids deadlock
    343     public void treeChanged() {
    344         Display.getDefault().syncExec(new Runnable() {
    345             public void run() {
    346                 synchronized (this) {
    347                     mSelectedNode = mModel.getSelected();
    348                 }
    349             }
    350         });
    351         doRedraw();
    352     }
    353 
    354     public void zoomChanged() {
    355         // pass
    356     }
    357 
    358     public void overlayChanged() {
    359         synchronized (this) {
    360             mOverlayImage = mModel.getOverlayImage();
    361             mOverlayTransparency = mModel.getOverlayTransparency();
    362         }
    363         doRedraw();
    364     }
    365 
    366     public void overlayTransparencyChanged() {
    367         synchronized (this) {
    368             mOverlayTransparency = mModel.getOverlayTransparency();
    369         }
    370         doRedraw();
    371     }
    372 }
    373