1 /* 2 * Copyright (C) 2013 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.ide.eclipse.adt.internal.editors.draw9patch.ui; 18 19 import com.android.ide.eclipse.adt.AdtPlugin; 20 import com.android.ide.eclipse.adt.internal.editors.draw9patch.graphics.NinePatchedImage; 21 import com.android.ide.eclipse.adt.internal.editors.draw9patch.graphics.NinePatchedImage.Chunk; 22 import com.android.ide.eclipse.adt.internal.editors.draw9patch.graphics.NinePatchedImage.Tick; 23 24 import org.eclipse.swt.SWT; 25 import org.eclipse.swt.events.KeyEvent; 26 import org.eclipse.swt.events.KeyListener; 27 import org.eclipse.swt.events.MouseEvent; 28 import org.eclipse.swt.events.MouseListener; 29 import org.eclipse.swt.events.MouseMoveListener; 30 import org.eclipse.swt.events.PaintEvent; 31 import org.eclipse.swt.events.PaintListener; 32 import org.eclipse.swt.events.SelectionAdapter; 33 import org.eclipse.swt.events.SelectionEvent; 34 import org.eclipse.swt.graphics.Color; 35 import org.eclipse.swt.graphics.GC; 36 import org.eclipse.swt.graphics.Image; 37 import org.eclipse.swt.graphics.ImageData; 38 import org.eclipse.swt.graphics.Point; 39 import org.eclipse.swt.graphics.RGB; 40 import org.eclipse.swt.graphics.Rectangle; 41 import org.eclipse.swt.widgets.Canvas; 42 import org.eclipse.swt.widgets.Composite; 43 import org.eclipse.swt.widgets.ScrollBar; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 import java.util.concurrent.ArrayBlockingQueue; 48 49 /** 50 * View and edit Draw 9-patch image. 51 */ 52 public class ImageViewer extends Canvas implements PaintListener, KeyListener, MouseListener, 53 MouseMoveListener { 54 private static final boolean DEBUG = false; 55 56 public static final String HELP_MESSAGE_KEY_TIPS = "Press Shift to erase pixels." 57 + " Press Control to draw layout bounds."; 58 59 public static final String HELP_MESSAGE_KEY_TIPS2 = "Release Shift to draw pixels."; 60 61 private static final Color BLACK_COLOR = AdtPlugin.getDisplay().getSystemColor(SWT.COLOR_BLACK); 62 private static final Color RED_COLOR = AdtPlugin.getDisplay().getSystemColor(SWT.COLOR_RED); 63 64 private static final Color BACK_COLOR 65 = new Color(AdtPlugin.getDisplay(), new RGB(0x00, 0xFF, 0x00)); 66 private static final Color LOCK_COLOR 67 = new Color(AdtPlugin.getDisplay(), new RGB(0xFF, 0x00, 0x00)); 68 private static final Color PATCH_COLOR 69 = new Color(AdtPlugin.getDisplay(), new RGB(0xFF, 0xFF, 0x00)); 70 private static final Color PATCH_ONEWAY_COLOR 71 = new Color(AdtPlugin.getDisplay(), new RGB(0x00, 0x00, 0xFF)); 72 private static final Color CORRUPTED_COLOR 73 = new Color(AdtPlugin.getDisplay(), new RGB(0xFF, 0x00, 0x00)); 74 75 private static final int NONE_ALPHA = 0xFF; 76 private static final int LOCK_ALPHA = 50; 77 private static final int PATCH_ALPHA = 50; 78 private static final int GUIDE_ALPHA = 60; 79 80 private static final int MODE_NONE = 0x00; 81 private static final int MODE_BLACK_TICK = 0x01; 82 private static final int MODE_RED_TICK = 0x02; 83 private static final int MODE_ERASE = 0xFF; 84 85 private int mDrawMode = MODE_NONE; 86 87 private static final int MARGIN = 5; 88 private static final String CHECKER_PNG_PATH = "/icons/checker.png"; 89 90 private Image mBackgroundLayer = null; 91 92 private NinePatchedImage mNinePatchedImage = null; 93 94 private Chunk[][] mChunks = null; 95 private Chunk[][] mBadChunks = null; 96 97 private boolean mIsLockShown = true; 98 private boolean mIsPatchesShown = false; 99 private boolean mIsBadPatchesShown = false; 100 101 private ScrollBar mHorizontalBar; 102 private ScrollBar mVerticalBar; 103 104 private int mZoom = 500; 105 106 private int mHorizontalScroll = 0; 107 private int mVerticalScroll = 0; 108 109 private final Rectangle mPadding = new Rectangle(0, 0, 0, 0); 110 111 // one pixel size that considered zoom 112 private int mZoomedPixelSize = 1; 113 114 private Image mBufferImage = null; 115 116 private boolean isCtrlPressed = false; 117 private boolean isShiftPressed = false; 118 119 private final List<UpdateListener> mUpdateListenerList 120 = new ArrayList<UpdateListener>(); 121 122 private final Point mCursorPoint = new Point(0, 0); 123 124 private static final int DEFAULT_UPDATE_QUEUE_SIZE = 10; 125 126 private final ArrayBlockingQueue<NinePatchedImage> mUpdateQueue 127 = new ArrayBlockingQueue<NinePatchedImage>(DEFAULT_UPDATE_QUEUE_SIZE); 128 129 private final Runnable mUpdateRunnable = new Runnable() { 130 @Override 131 public void run() { 132 if (isDisposed()) { 133 return; 134 } 135 136 redraw(); 137 138 fireUpdateEvent(); 139 } 140 }; 141 142 private final Thread mUpdateThread = new Thread() { 143 @Override 144 public void run() { 145 while (!isDisposed()) { 146 try { 147 mUpdateQueue.take(); 148 mNinePatchedImage.findPatches(); 149 mNinePatchedImage.findContentsArea(); 150 151 mChunks = mNinePatchedImage.getChunks(mChunks); 152 mBadChunks = mNinePatchedImage.getCorruptedChunks(mBadChunks); 153 154 AdtPlugin.getDisplay().asyncExec(mUpdateRunnable); 155 156 } catch (InterruptedException e) { 157 } 158 } 159 } 160 }; 161 162 private StatusChangedListener mStatusChangedListener = null; 163 164 public void addUpdateListener(UpdateListener l) { 165 mUpdateListenerList.add(l); 166 } 167 168 public void removeUpdateListener(UpdateListener l) { 169 mUpdateListenerList.remove(l); 170 } 171 172 private void fireUpdateEvent() { 173 int len = mUpdateListenerList.size(); 174 for(int i=0; i < len; i++) { 175 mUpdateListenerList.get(i).update(mNinePatchedImage); 176 } 177 } 178 179 public void setStatusChangedListener(StatusChangedListener l) { 180 mStatusChangedListener = l; 181 if (mStatusChangedListener != null) { 182 mStatusChangedListener.helpTextChanged(HELP_MESSAGE_KEY_TIPS); 183 } 184 } 185 186 void setShowLock(boolean show) { 187 mIsLockShown = show; 188 redraw(); 189 } 190 191 void setShowPatchesArea(boolean show) { 192 mIsPatchesShown = show; 193 redraw(); 194 } 195 196 void setShowBadPatchesArea(boolean show) { 197 mIsBadPatchesShown = show; 198 redraw(); 199 } 200 201 void setZoom(int zoom) { 202 mZoom = zoom; 203 mZoomedPixelSize = getZoomedPixelSize(1); 204 redraw(); 205 } 206 207 public ImageViewer(Composite parent, int style) { 208 super(parent, style); 209 210 mUpdateThread.start(); 211 212 mBackgroundLayer = AdtPlugin.getImageDescriptor(CHECKER_PNG_PATH).createImage(); 213 214 addMouseListener(this); 215 addMouseMoveListener(this); 216 addPaintListener(this); 217 218 mHorizontalBar = getHorizontalBar(); 219 mHorizontalBar.setThumb(1); 220 mHorizontalBar.addSelectionListener(new SelectionAdapter() { 221 @Override 222 public void widgetSelected(SelectionEvent event) { 223 super.widgetSelected(event); 224 ScrollBar bar = (ScrollBar) event.widget; 225 if (mHorizontalBar.isVisible() 226 && mHorizontalScroll != bar.getSelection()) { 227 mHorizontalScroll = bar.getSelection(); 228 redraw(); 229 } 230 } 231 }); 232 233 mVerticalBar = getVerticalBar(); 234 mVerticalBar.setThumb(1); 235 mVerticalBar.addSelectionListener(new SelectionAdapter() { 236 @Override 237 public void widgetSelected(SelectionEvent event) { 238 super.widgetSelected(event); 239 ScrollBar bar = (ScrollBar) event.widget; 240 if (mVerticalBar.isVisible() 241 && mVerticalScroll != bar.getSelection()) { 242 mVerticalScroll = bar.getSelection(); 243 redraw(); 244 } 245 } 246 }); 247 } 248 249 /** 250 * Load the image file. 251 * 252 * @param fileName must be absolute path 253 */ 254 public NinePatchedImage loadFile(String fileName) { 255 mNinePatchedImage = new NinePatchedImage(fileName); 256 257 return mNinePatchedImage; 258 } 259 260 /** 261 * Start displaying the image. 262 */ 263 public void startDisplay() { 264 mZoomedPixelSize = getZoomedPixelSize(1); 265 266 scheduleUpdate(); 267 } 268 269 private void draw(int x, int y, int drawMode) { 270 if (drawMode == MODE_ERASE) { 271 erase(x, y); 272 } else { 273 int color = (drawMode == MODE_RED_TICK) ? NinePatchedImage.RED_TICK 274 : NinePatchedImage.BLACK_TICK; 275 mNinePatchedImage.setPatch(x, y, color); 276 redraw(); 277 278 scheduleUpdate(); 279 } 280 } 281 282 private void erase(int x, int y) { 283 mNinePatchedImage.erase(x, y); 284 redraw(); 285 286 scheduleUpdate(); 287 } 288 289 private void scheduleUpdate() { 290 try { 291 mUpdateQueue.put(mNinePatchedImage); 292 } catch (InterruptedException e) { 293 } 294 } 295 296 @Override 297 public void mouseDown(MouseEvent event) { 298 if (event.button == 1 && !isShiftPressed) { 299 mDrawMode = !isCtrlPressed ? MODE_BLACK_TICK : MODE_RED_TICK; 300 draw(mCursorPoint.x, mCursorPoint.y, mDrawMode); 301 } else if (event.button == 3 || isShiftPressed) { 302 mDrawMode = MODE_ERASE; 303 erase(mCursorPoint.x, mCursorPoint.y); 304 } 305 } 306 307 @Override 308 public void mouseUp(MouseEvent event) { 309 mDrawMode = MODE_NONE; 310 } 311 312 @Override 313 public void mouseDoubleClick(MouseEvent event) { 314 } 315 316 private int getLogicalPositionX(int x) { 317 return Math.round((x - mPadding.x + mHorizontalScroll) / ((float) mZoom / 100)); 318 } 319 320 private int getLogicalPositionY(int y) { 321 return Math.round((y - mPadding.y + mVerticalScroll) / ((float) mZoom / 100)); 322 } 323 324 @Override 325 public void mouseMove(MouseEvent event) { 326 int posX = getLogicalPositionX(event.x); 327 int posY = getLogicalPositionY(event.y); 328 329 int width = mNinePatchedImage.getWidth(); 330 int height = mNinePatchedImage.getHeight(); 331 332 if (posX < 0) { 333 posX = 0; 334 } 335 if (posX >= width) { 336 posX = width - 1; 337 } 338 if (posY < 0) { 339 posY = 0; 340 } 341 if (posY >= height) { 342 posY = height - 1; 343 } 344 345 if (mDrawMode != MODE_NONE) { 346 int drawMode = mDrawMode; 347 if (isShiftPressed) { 348 drawMode = MODE_ERASE; 349 } else if (mDrawMode != MODE_ERASE) { 350 drawMode = !isCtrlPressed ? MODE_BLACK_TICK : MODE_RED_TICK; 351 } 352 353 /* 354 * Consider the previous cursor position because mouseMove events are 355 * scatter. 356 */ 357 int x = mCursorPoint.x; 358 int y = mCursorPoint.y; 359 for (; y != posY; y += (y > posY) ? -1 : 1) { 360 draw(x, y, drawMode); 361 } 362 363 x = mCursorPoint.x; 364 y = mCursorPoint.y; 365 for (; x != posX; x += (x > posX) ? -1 : 1) { 366 draw(x, y, drawMode); 367 } 368 } 369 mCursorPoint.x = posX; 370 mCursorPoint.y = posY; 371 372 redraw(); 373 374 if (mStatusChangedListener != null) { 375 // Update position on status panel if position is in logical size. 376 if (posX >= 0 && posY >= 0 377 && posX <= mNinePatchedImage.getWidth() 378 && posY <= mNinePatchedImage.getHeight()) { 379 mStatusChangedListener.cursorPositionChanged(posX + 1, posY + 1); 380 } 381 } 382 } 383 384 private synchronized void calcPaddings(int width, int height) { 385 Point canvasSize = getSize(); 386 387 mPadding.width = getZoomedPixelSize(width); 388 mPadding.height = getZoomedPixelSize(height); 389 390 int margin = getZoomedPixelSize(MARGIN); 391 392 if (mPadding.width < canvasSize.x) { 393 mPadding.x = (canvasSize.x - mPadding.width) / 2; 394 } else { 395 mPadding.x = margin; 396 } 397 398 if (mPadding.height < canvasSize.y) { 399 mPadding.y = (canvasSize.y - mPadding.height) / 2; 400 } else { 401 mPadding.y = margin; 402 } 403 } 404 405 private void calcScrollBarSettings() { 406 Point size = getSize(); 407 int screenWidth = size.x; 408 int screenHeight = size.y; 409 410 int imageWidth = getZoomedPixelSize(mNinePatchedImage.getWidth() + 1); 411 int imageHeight = getZoomedPixelSize(mNinePatchedImage.getHeight() + 1); 412 413 // consider the scroll bar sizes 414 int verticalBarSize = mVerticalBar.getSize().x; 415 int horizontalBarSize = mHorizontalBar.getSize().y; 416 417 int horizontalScroll = imageWidth - (screenWidth - verticalBarSize); 418 int verticalScroll = imageHeight - (screenHeight - horizontalBarSize); 419 420 int margin = getZoomedPixelSize(MARGIN) * 2; 421 422 if (horizontalScroll > 0) { 423 mHorizontalBar.setVisible(true); 424 425 // horizontal maximum 426 int max = horizontalScroll + verticalBarSize + margin; 427 mHorizontalBar.setMaximum(max); 428 429 // set corrected scroll size 430 int value = mHorizontalBar.getSelection(); 431 value = max < value ? max : value; 432 433 mHorizontalBar.setSelection(value); 434 mHorizontalScroll = value; 435 436 } else { 437 mHorizontalBar.setSelection(0); 438 mHorizontalBar.setMaximum(0); 439 mHorizontalBar.setVisible(false); 440 } 441 442 if (verticalScroll > 0) { 443 mVerticalBar.setVisible(true); 444 445 // vertical maximum 446 int max = verticalScroll + horizontalBarSize + margin; 447 mVerticalBar.setMaximum(max); 448 449 // set corrected scroll size 450 int value = mVerticalBar.getSelection(); 451 value = max < value ? max : value; 452 453 mVerticalBar.setSelection(value); 454 mVerticalScroll = value; 455 456 } else { 457 mVerticalBar.setSelection(0); 458 mVerticalBar.setMaximum(0); 459 mVerticalBar.setVisible(false); 460 } 461 } 462 463 private int getZoomedPixelSize(int val) { 464 return Math.round(val * (float) mZoom / 100); 465 } 466 467 @Override 468 public void paintControl(PaintEvent pe) { 469 if (mNinePatchedImage == null) { 470 return; 471 } 472 473 // Use buffer 474 GC bufferGc = null; 475 if (mBufferImage == null) { 476 mBufferImage = new Image(AdtPlugin.getDisplay(), pe.width, pe.height); 477 } else { 478 int width = mBufferImage.getBounds().width; 479 int height = mBufferImage.getBounds().height; 480 if (width != pe.width || height != pe.height) { 481 mBufferImage = new Image(AdtPlugin.getDisplay(), pe.width, pe.height); 482 } 483 } 484 485 // Draw previous image once for prevent flicking 486 pe.gc.drawImage(mBufferImage, 0, 0); 487 488 bufferGc = new GC(mBufferImage); 489 bufferGc.setAdvanced(true); 490 491 // Make interpolation disable 492 bufferGc.setInterpolation(SWT.NONE); 493 494 // clear buffer 495 bufferGc.fillRectangle(0, 0, pe.width, pe.height); 496 497 calcScrollBarSettings(); 498 499 // padding from current zoom 500 int width = mNinePatchedImage.getWidth(); 501 int height = mNinePatchedImage.getHeight(); 502 calcPaddings(width, height); 503 504 int baseX = mPadding.x - mHorizontalScroll; 505 int baseY = mPadding.y - mVerticalScroll; 506 507 // draw checker image 508 bufferGc.drawImage(mBackgroundLayer, 509 0, 0, mBackgroundLayer.getImageData().width, 510 mBackgroundLayer.getImageData().height, 511 baseX, baseY, mPadding.width, mPadding.height); 512 513 if (DEBUG) { 514 System.out.println(String.format("%d,%d %d,%d %d,%d", 515 width, height, baseX, baseY, mPadding.width, mPadding.height)); 516 } 517 518 // draw image 519 /* TODO: Do not draw invisible area, for better performance. */ 520 bufferGc.drawImage(mNinePatchedImage.getImage(), 0, 0, width, height, baseX, baseY, 521 mPadding.width, mPadding.height); 522 523 bufferGc.setBackground(BLACK_COLOR); 524 525 // draw patch ticks 526 drawHorizontalPatches(bufferGc, baseX, baseY); 527 drawVerticalPatches(bufferGc, baseX, baseY); 528 529 // draw content ticks 530 drawHorizontalContentArea(bufferGc, baseX, baseY); 531 drawVerticalContentArea(bufferGc, baseX, baseY); 532 533 if (mNinePatchedImage.isValid(mCursorPoint.x, mCursorPoint.y)) { 534 bufferGc.setForeground(BLACK_COLOR); 535 } else if (mIsLockShown) { 536 drawLockArea(bufferGc, baseX, baseY); 537 } 538 539 // Patches 540 if (mIsPatchesShown) { 541 drawPatchAreas(bufferGc, baseX, baseY); 542 } 543 544 // Bad patches 545 if (mIsBadPatchesShown) { 546 drawBadPatchAreas(bufferGc, baseX, baseY); 547 } 548 549 if (mNinePatchedImage.isValid(mCursorPoint.x, mCursorPoint.y)) { 550 bufferGc.setForeground(BLACK_COLOR); 551 } else { 552 bufferGc.setForeground(RED_COLOR); 553 } 554 555 drawGuideLine(bufferGc, baseX, baseY); 556 557 bufferGc.dispose(); 558 559 pe.gc.drawImage(mBufferImage, 0, 0); 560 } 561 562 private static final Color getColor(int color) { 563 switch (color) { 564 case NinePatchedImage.RED_TICK: 565 return RED_COLOR; 566 default: 567 return BLACK_COLOR; 568 } 569 } 570 571 private void drawVerticalPatches(GC gc, int baseX, int baseY) { 572 List<Tick> verticalPatches = mNinePatchedImage.getVerticalPatches(); 573 for (Tick t : verticalPatches) { 574 if (t.color != NinePatchedImage.TRANSPARENT_TICK) { 575 gc.setBackground(getColor(t.color)); 576 gc.fillRectangle( 577 baseX, 578 baseY + getZoomedPixelSize(t.start), 579 mZoomedPixelSize, 580 getZoomedPixelSize(t.getLength())); 581 } 582 } 583 } 584 585 private void drawHorizontalPatches(GC gc, int baseX, int baseY) { 586 List<Tick> horizontalPatches = mNinePatchedImage.getHorizontalPatches(); 587 for (Tick t : horizontalPatches) { 588 if (t.color != NinePatchedImage.TRANSPARENT_TICK) { 589 gc.setBackground(getColor(t.color)); 590 gc.fillRectangle( 591 baseX + getZoomedPixelSize(t.start), 592 baseY, 593 getZoomedPixelSize(t.getLength()), 594 mZoomedPixelSize); 595 } 596 } 597 } 598 599 private void drawHorizontalContentArea(GC gc, int baseX, int baseY) { 600 List<Tick> horizontalContentArea = mNinePatchedImage.getHorizontalContents(); 601 for (Tick t : horizontalContentArea) { 602 if (t.color != NinePatchedImage.TRANSPARENT_TICK) { 603 gc.setBackground(getColor(t.color)); 604 gc.fillRectangle( 605 baseX + getZoomedPixelSize(t.start), 606 baseY + getZoomedPixelSize(mNinePatchedImage.getHeight() - 1), 607 getZoomedPixelSize(t.getLength()), 608 mZoomedPixelSize); 609 } 610 } 611 612 } 613 614 private void drawVerticalContentArea(GC gc, int baseX, int baseY) { 615 List<Tick> verticalContentArea = mNinePatchedImage.getVerticalContents(); 616 for (Tick t : verticalContentArea) { 617 if (t.color != NinePatchedImage.TRANSPARENT_TICK) { 618 gc.setBackground(getColor(t.color)); 619 gc.fillRectangle( 620 baseX + getZoomedPixelSize(mNinePatchedImage.getWidth() - 1), 621 baseY + getZoomedPixelSize(t.start), 622 mZoomedPixelSize, 623 getZoomedPixelSize(t.getLength())); 624 } 625 } 626 } 627 628 private void drawLockArea(GC gc, int baseX, int baseY) { 629 gc.setAlpha(LOCK_ALPHA); 630 gc.setForeground(LOCK_COLOR); 631 gc.setBackground(LOCK_COLOR); 632 633 gc.fillRectangle( 634 baseX + mZoomedPixelSize, 635 baseY + mZoomedPixelSize, 636 getZoomedPixelSize(mNinePatchedImage.getWidth() - 2), 637 getZoomedPixelSize(mNinePatchedImage.getHeight() - 2)); 638 gc.setAlpha(NONE_ALPHA); 639 640 } 641 642 private void drawPatchAreas(GC gc, int baseX, int baseY) { 643 if (mChunks != null) { 644 int yLen = mChunks.length; 645 int xLen = mChunks[0].length; 646 647 gc.setAlpha(PATCH_ALPHA); 648 649 for (int yPos = 0; yPos < yLen; yPos++) { 650 for (int xPos = 0; xPos < xLen; xPos++) { 651 Chunk c = mChunks[yPos][xPos]; 652 653 if (c.type == Chunk.TYPE_FIXED) { 654 gc.setBackground(BACK_COLOR); 655 } else if (c.type == Chunk.TYPE_HORIZONTAL) { 656 gc.setBackground(PATCH_ONEWAY_COLOR); 657 } else if (c.type == Chunk.TYPE_VERTICAL) { 658 gc.setBackground(PATCH_ONEWAY_COLOR); 659 } else if (c.type == Chunk.TYPE_HORIZONTAL + Chunk.TYPE_VERTICAL) { 660 gc.setBackground(PATCH_COLOR); 661 } 662 Rectangle r = c.rect; 663 gc.fillRectangle( 664 baseX + getZoomedPixelSize(r.x), 665 baseY + getZoomedPixelSize(r.y), 666 getZoomedPixelSize(r.width), 667 getZoomedPixelSize(r.height)); 668 } 669 } 670 } 671 gc.setAlpha(NONE_ALPHA); 672 } 673 674 private void drawBadPatchAreas(GC gc, int baseX, int baseY) { 675 if (mBadChunks != null) { 676 int yLen = mBadChunks.length; 677 int xLen = mBadChunks[0].length; 678 679 gc.setAlpha(NONE_ALPHA); 680 gc.setForeground(CORRUPTED_COLOR); 681 gc.setBackground(CORRUPTED_COLOR); 682 683 for (int yPos = 0; yPos < yLen; yPos++) { 684 for (int xPos = 0; xPos < xLen; xPos++) { 685 Chunk c = mBadChunks[yPos][xPos]; 686 if ((c.type & Chunk.TYPE_CORRUPT) != 0) { 687 Rectangle r = c.rect; 688 gc.drawRectangle( 689 baseX + getZoomedPixelSize(r.x), 690 baseY + getZoomedPixelSize(r.y), 691 getZoomedPixelSize(r.width), 692 getZoomedPixelSize(r.height)); 693 } 694 } 695 } 696 } 697 } 698 699 private void drawGuideLine(GC gc, int baseX, int baseY) { 700 gc.setAntialias(SWT.ON); 701 gc.setInterpolation(SWT.HIGH); 702 703 int x = Math.round((mCursorPoint.x * ((float) mZoom / 100) + baseX) 704 + ((float) mZoom / 100 / 2)); 705 int y = Math.round((mCursorPoint.y * ((float) mZoom / 100) + baseY) 706 + ((float) mZoom / 100 / 2)); 707 gc.setAlpha(GUIDE_ALPHA); 708 709 Point size = getSize(); 710 gc.drawLine(x, 0, x, size.y); 711 gc.drawLine(0, y, size.x, y); 712 713 gc.setAlpha(NONE_ALPHA); 714 } 715 716 @Override 717 public void keyPressed(KeyEvent event) { 718 int keycode = event.keyCode; 719 if (keycode == SWT.CTRL) { 720 isCtrlPressed = true; 721 } 722 if (keycode == SWT.SHIFT) { 723 isShiftPressed = true; 724 if (mStatusChangedListener != null) { 725 mStatusChangedListener.helpTextChanged(HELP_MESSAGE_KEY_TIPS2); 726 } 727 } 728 } 729 730 @Override 731 public void keyReleased(KeyEvent event) { 732 int keycode = event.keyCode; 733 if (keycode == SWT.CTRL) { 734 isCtrlPressed = false; 735 } 736 if (keycode == SWT.SHIFT) { 737 isShiftPressed = false; 738 if (mStatusChangedListener != null) { 739 mStatusChangedListener.helpTextChanged(HELP_MESSAGE_KEY_TIPS); 740 } 741 } 742 } 743 744 @Override 745 public void dispose() { 746 mBackgroundLayer.dispose(); 747 super.dispose(); 748 } 749 750 /** 751 * Listen image updated event. 752 */ 753 public interface UpdateListener { 754 /** 755 * 9-patched image has been updated. 756 */ 757 public void update(NinePatchedImage image); 758 } 759 760 /** 761 * Listen status changed event. 762 */ 763 public interface StatusChangedListener { 764 /** 765 * Mouse cursor position has been changed. 766 */ 767 public void cursorPositionChanged(int x, int y); 768 769 /** 770 * Help text has been changed. 771 */ 772 public void helpTextChanged(String text); 773 } 774 } 775