Package org.locationtech.udig.image.georeferencing.internal.ui.imagepanel

Source Code of org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.ImageComposite

/* Image Georeferencing
*
* Axios Engineering
*      http://www.axios.es
*
* (C) 2011, Axios Engineering S.L. (Axios)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Axios BSD
* License v1.0 (http://udig.refractions.net/files/asd3-v10.html).
*/
package org.locationtech.udig.image.georeferencing.internal.ui.imagepanel;

import java.awt.Color;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

import org.locationtech.udig.project.ui.tool.IToolContext;

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;

import org.locationtech.udig.image.georeferencing.internal.i18n.Messages;
import org.locationtech.udig.image.georeferencing.internal.preferences.Preferences;
import org.locationtech.udig.image.georeferencing.internal.process.MarkModel;
import org.locationtech.udig.image.georeferencing.internal.ui.GeoReferencingCommand;
import org.locationtech.udig.image.georeferencing.internal.ui.GeoReferencingComposite;
import org.locationtech.udig.image.georeferencing.internal.ui.GeoreferencingCommandEventChange;
import org.locationtech.udig.image.georeferencing.internal.ui.InputEvent;
import org.locationtech.udig.image.georeferencing.internal.ui.MainComposite;
import org.locationtech.udig.image.georeferencing.internal.ui.MouseSelectionListener;
import org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.ImageComposite.ZoomFeedBack.ZOOM_TYPE;
import org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.tools.AddMarkImageTool;
import org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.tools.DeleteMarkImageTool;
import org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.tools.ImageInputEvent;
import org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.tools.ImageTool;
import org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.tools.MoveMarkImageTool;
import org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.tools.PanImageTool;
import org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.tools.ZoomInImageTool;
import org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.tools.ZoomOutImageTool;

/**
* Class responsible of the canvas and its management.
*
* It's also responsible of the creation of the mark models. This class contains
* tools which are used to manipulate marks in the canvas.
*
*
* @author Mauricio Pazos (www.axios.es)
* @author Aritz Davila (www.axios.es)
* @since 1.3.3
*
*/
public final class ImageComposite extends Composite implements Observer, GeoReferencingComposite {

  private Composite          imageComposite            = null;
  private Composite          buttonComposite            = null;
  private Canvas            canvas                = null;
  private Image            image                = null;
  private IToolContext        toolContext              = null;
  private ToolBar            imageToolBar            = null;
  private ToolItem          itemLoad              = null;
  private ToolItem          itemAdd                = null;
  private ToolItem          itemDelete              = null;
  private ToolItem          itemDeleteAll            = null;
  private ToolItem          itemDragDrop            = null;
  private ToolItem          itemZoomIn              = null;
  private ToolItem          itemZoomOut              = null;
  private ToolItem          itemZoomFit              = null;
  private ToolItem          itemPan                = null;

  private ImageRegistry        registry              = null;
  @SuppressWarnings("unused")
  private Thread            uiThread              = null;

  private List<MarkImagePresenter>  markPresenterList          = new LinkedList<MarkImagePresenter>();

  private GeoReferencingCommand    cmd                  = null;

  private ImageMetricPosition      imageMetrics            = null;
  private List<ImageTool>        tools                = null;

  private ZoomFeedBack        zoomFeedback            = null;

  private MouseSelectionListener    mapMouseSelectionListener      = null;
  private MouseSelectionListener    coordPanelMouseSelectionListener  = null;

  public ImageComposite(GeoReferencingCommand cmd, Composite parent, int style) {
    super(parent, style);

    assert cmd != null;
    this.cmd = cmd;
    createContent();
    pack();
  }

  private void createContent() {

    this.registry = createImageRegistry();
    this.uiThread = Thread.currentThread();
    this.imageMetrics = new ImageMetricPosition();

    createListeners();

    createNewTools();

    // layout for this image composite
    GridData gridData = new GridData();
    gridData.horizontalAlignment = GridData.FILL;
    gridData.grabExcessVerticalSpace = false;
    gridData.grabExcessHorizontalSpace = true;
    gridData.horizontalSpan = 3;
    gridData.verticalAlignment = GridData.FILL;

    GridData gridData2 = new GridData();
    gridData2.horizontalAlignment = GridData.FILL;
    gridData2.grabExcessVerticalSpace = true;
    gridData2.grabExcessHorizontalSpace = true;
    gridData2.horizontalSpan = 3;
    gridData2.verticalAlignment = GridData.FILL;

    GridLayout gridlayout = new GridLayout(3, true);
    setLayout(gridlayout);

    buttonComposite = new Composite(this, SWT.BORDER);
    buttonComposite.setLayoutData(gridData);
    buttonComposite.setLayout(gridlayout);
    createButtons(buttonComposite);

    imageComposite = new Composite(this, SWT.BORDER);
    imageComposite.setLayoutData(gridData2);
    imageComposite.setLayout(gridlayout);
    createCanvas(imageComposite);
  }

  /**
   * Listener used by the Image composite.
   *
   * This composite listen to the map tools and to the coordinate table panel.
   */
  private void createListeners() {

    this.mapMouseSelectionListener = new MouseSelectionListener() {

      public void inEvent(MarkModel mark) {

        // feedback
        mouseOverFeedback(mark);
      }

      public void outEvent(MarkModel mark) {

        mouseNotOverFeedback(mark);
      }
    };

    this.coordPanelMouseSelectionListener = new MouseSelectionListener() {

      public void inEvent(MarkModel mark) {
        // feedback
        mouseOverFeedback(mark);
      }

      public void outEvent(MarkModel mark) {

        mouseNotOverFeedback(mark);
      }
    };
  }

  /**
   * Create the images that are shown in the tools.
   *
   * @return The imageRegistry
   */
  private ImageRegistry createImageRegistry() {

    ImageRegistry registry = new ImageRegistry(this.getDisplay());

    String opId = "DeleteAll"; //$NON-NLS-1$
    String imgFile = "image/cancel_all_co2.gif"; //$NON-NLS-1$
    registry.put(opId, ImageDescriptor.createFromFile(ImageComposite.class, imgFile));

    opId = "Delete"; //$NON-NLS-1$
    imgFile = "image/clear_co2.gif"; //$NON-NLS-1$
    registry.put(opId, ImageDescriptor.createFromFile(ImageComposite.class, imgFile));

    opId = "Load"; //$NON-NLS-1$
    imgFile = "image/folder.png"; //$NON-NLS-1$
    registry.put(opId, ImageDescriptor.createFromFile(ImageComposite.class, imgFile));

    opId = "Add"; //$NON-NLS-1$
    imgFile = "image/placemark_pointer2.gif"; //$NON-NLS-1$
    registry.put(opId, ImageDescriptor.createFromFile(ImageComposite.class, imgFile));

    opId = "Move"; //$NON-NLS-1$
    imgFile = "image/movemarker.png"; //$NON-NLS-1$
    registry.put(opId, ImageDescriptor.createFromFile(ImageComposite.class, imgFile));

    opId = "ZoomIn"; //$NON-NLS-1$
    imgFile = "image/zoom-in-5.png"; //$NON-NLS-1$
    registry.put(opId, ImageDescriptor.createFromFile(ImageComposite.class, imgFile));

    opId = "ZoomOut"; //$NON-NLS-1$
    imgFile = "image/zoom-out-5.png"; //$NON-NLS-1$
    registry.put(opId, ImageDescriptor.createFromFile(ImageComposite.class, imgFile));

    opId = "ZoomFit"; //$NON-NLS-1$
    imgFile = "image/zoom_extent.gif"; //$NON-NLS-1$
    registry.put(opId, ImageDescriptor.createFromFile(ImageComposite.class, imgFile));

    opId = "Pan"; //$NON-NLS-1$
    imgFile = "image/pan.png"; //$NON-NLS-1$
    registry.put(opId, ImageDescriptor.createFromFile(ImageComposite.class, imgFile));

    return registry;
  }

  /**
   * Creates all the {@link ImageTool} with its associated cursor.
   */
  private void createNewTools() {

    this.tools = new LinkedList<ImageTool>();

    // cursors:
    Cursor cursor = null;
    Image image = null;
    Display device = Display.getCurrent();

    image = this.registry.get("Add"); //$NON-NLS-1$
    cursor = new Cursor(device, image.getImageData(), 8, 8);
    AddMarkImageTool addTool = new AddMarkImageTool(cursor, this);

    image = this.registry.get("Delete"); //$NON-NLS-1$
    cursor = new Cursor(device, image.getImageData(), 1, 14);
    DeleteMarkImageTool deleteTool = new DeleteMarkImageTool(cursor, this);

    image = this.registry.get("Move"); //$NON-NLS-1$
    cursor = new Cursor(device, image.getImageData(), 6, 6);
    MoveMarkImageTool moveTool = new MoveMarkImageTool(cursor, this);

    image = this.registry.get("ZoomIn"); //$NON-NLS-1$
    cursor = new Cursor(device, image.getImageData(), 5, 5);
    ZoomInImageTool zoomInTool = new ZoomInImageTool(cursor, this);

    image = this.registry.get("ZoomOut"); //$NON-NLS-1$
    cursor = new Cursor(device, image.getImageData(), 5, 5);
    ZoomOutImageTool zoomOutTool = new ZoomOutImageTool(cursor, this);

    image = this.registry.get("Pan"); //$NON-NLS-1$
    cursor = new Cursor(device, image.getImageData(), 7, 7);
    PanImageTool panTool = new PanImageTool(cursor, this);

    this.tools.add(addTool);
    this.tools.add(deleteTool);
    this.tools.add(moveTool);
    this.tools.add(zoomInTool);
    this.tools.add(zoomOutTool);
    this.tools.add(panTool);
  }

  /**
   * Given the class, it'll active from the list of tools the one that match
   * the desired class.
   *
   * @param clazz
   *            ImageTool class to be activated.
   */
  private void setToolActive(Class<?> clazz) {

    for (ImageTool tool : tools) {

      if (tool.getClass().equals(clazz)) {
        tool.setActive(true);
      } else {
        tool.setActive(false);
      }
    }
  }

  private void createButtons(Composite parent) {

    GridData gridData = new GridData();
    gridData.horizontalAlignment = GridData.FILL;
    gridData.grabExcessHorizontalSpace = true;
    gridData.grabExcessVerticalSpace = false;
    gridData.verticalAlignment = GridData.FILL;

    imageToolBar = new ToolBar(parent, SWT.LEFT_TO_RIGHT);

    itemLoad = new ToolItem(imageToolBar, SWT.PUSH);
    itemLoad.setImage(this.registry.get("Load")); //$NON-NLS-1$
    itemLoad.setToolTipText(Messages.ImageComposite_itemLoadTooltip);
    itemLoad.addListener(SWT.Selection, new Listener() {

      public void handleEvent(Event event) {

        loadFile();
      }
    });
    itemLoad.setEnabled(false);

    itemAdd = new ToolItem(imageToolBar, SWT.RADIO);
    itemAdd.setImage(this.registry.get("Add")); //$NON-NLS-1$
    itemAdd.setToolTipText(Messages.ImageComposite_itemAddTooltip);
    itemAdd.addListener(SWT.Selection, new Listener() {

      public void handleEvent(Event event) {

        setToolActive(AddMarkImageTool.class);
      }
    });

    itemDelete = new ToolItem(imageToolBar, SWT.RADIO);
    itemDelete.setImage(this.registry.get("Delete")); //$NON-NLS-1$
    itemDelete.setToolTipText(Messages.ImageComposite_itemDeleteTooltip);
    itemDelete.addListener(SWT.Selection, new Listener() {

      public void handleEvent(Event event) {

        setToolActive(DeleteMarkImageTool.class);
      }
    });

    itemDragDrop = new ToolItem(imageToolBar, SWT.RADIO);
    itemDragDrop.setImage(this.registry.get("Move")); //$NON-NLS-1$
    itemDragDrop.setToolTipText(Messages.ImageComposite_itemDragDropTooltip);
    itemDragDrop.addListener(SWT.Selection, new Listener() {

      public void handleEvent(Event event) {

        setToolActive(MoveMarkImageTool.class);
      }
    });

    itemZoomIn = new ToolItem(imageToolBar, SWT.RADIO);
    itemZoomIn.setImage(this.registry.get("ZoomIn")); //$NON-NLS-1$
    itemZoomIn.setToolTipText(Messages.ImageComposite_itemZoomInTooltip);
    itemZoomIn.addListener(SWT.Selection, new Listener() {

      public void handleEvent(Event event) {

        setToolActive(ZoomInImageTool.class);
      }
    });

    itemZoomOut = new ToolItem(imageToolBar, SWT.RADIO);
    itemZoomOut.setImage(this.registry.get("ZoomOut")); //$NON-NLS-1$
    itemZoomOut.setToolTipText(Messages.ImageComposite_itemZoomOutTooltip);
    itemZoomOut.addListener(SWT.Selection, new Listener() {

      public void handleEvent(Event event) {

        setToolActive(ZoomOutImageTool.class);
      }
    });

    itemPan = new ToolItem(imageToolBar, SWT.RADIO);
    itemPan.setImage(this.registry.get("Pan")); //$NON-NLS-1$
    itemPan.setToolTipText(Messages.ImageComposite_itemPanTooltip);
    itemPan.addListener(SWT.Selection, new Listener() {

      public void handleEvent(Event event) {

        setToolActive(PanImageTool.class);
      }
    });

    itemDeleteAll = new ToolItem(imageToolBar, SWT.PUSH);
    itemDeleteAll.setImage(this.registry.get("DeleteAll")); //$NON-NLS-1$
    itemDeleteAll.setToolTipText(Messages.ImageComposite_itemDelAllTooltip);
    itemDeleteAll.addListener(SWT.Selection, new Listener() {

      public void handleEvent(Event event) {

        deactivateTools();
        deleteAllPoints();
        getMainComposite().refreshMapGraphicLayer();
      }
    });

    itemZoomFit = new ToolItem(imageToolBar, SWT.PUSH);
    itemZoomFit.setImage(this.registry.get("ZoomFit")); //$NON-NLS-1$
    itemZoomFit.setToolTipText(Messages.ImageComposite_itemFitTooltip);
    itemZoomFit.addListener(SWT.Selection, new Listener() {

      public void handleEvent(Event event) {

        zoomFit();
      }
    });

    setItemsEnabled(false);
    setCertainToolsEnabled(false);
  }

  /**
   * Deactivate the tools and deselect them.
   */
  private void deactivateTools() {

    for (ImageTool tool : tools) {

      tool.setActive(false);
    }

    setItemsSelected(false);
  }

  private void setItemsSelected(boolean selected) {

    this.itemAdd.setSelection(selected);
    this.itemDelete.setSelection(selected);
    this.itemDragDrop.setSelection(selected);
    this.itemDeleteAll.setSelection(selected);
    this.itemZoomIn.setSelection(selected);
    this.itemZoomOut.setSelection(selected);
    this.itemZoomFit.setSelection(selected);
    this.itemPan.setSelection(selected);
  }

  private void setItemsEnabled(boolean enabled) {

    this.itemAdd.setEnabled(enabled);
    this.itemZoomIn.setEnabled(enabled);
    this.itemZoomOut.setEnabled(enabled);
    this.itemZoomFit.setEnabled(enabled);
    this.itemPan.setEnabled(enabled);
  }

  private void setCertainToolsEnabled(boolean enabled) {

    this.itemDelete.setEnabled(enabled);
    this.itemDragDrop.setEnabled(enabled);
    this.itemDeleteAll.setEnabled(enabled);

    if (!enabled) {
      this.itemDelete.setSelection(false);
      this.itemDragDrop.setSelection(false);
      this.itemDeleteAll.setSelection(false);
    }
  }

  private void createCanvas(Composite parent) {

    GridData gridData2 = new GridData();
    gridData2.horizontalAlignment = GridData.FILL;
    gridData2.grabExcessVerticalSpace = true;
    gridData2.grabExcessHorizontalSpace = true;
    gridData2.horizontalSpan = 3;
    gridData2.verticalAlignment = GridData.FILL;

    canvas = new Canvas(parent, SWT.BACKGROUND | SWT.DOUBLE_BUFFERED);
    canvas.setLayoutData(gridData2);
    canvas.addMouseListener(new MouseAdapter() {

      @Override
      public void mouseUp(MouseEvent e) {

        performAction(e, InputEvent.MOUSE_UP);
      }

      @Override
      public void mouseDown(MouseEvent e) {

        performAction(e, InputEvent.MOUSE_DOWN);
      }
    });

    canvas.addMouseMoveListener(new MouseMoveListener() {

      public void mouseMove(MouseEvent e) {

        performAction(e, InputEvent.MOUSE_DRAG);
      }
    });

    canvas.addMouseWheelListener(new MouseWheelListener() {

      public void mouseScrolled(MouseEvent e) {

        performAction(e, InputEvent.MOUSE_SCROLL);
      }
    });

    canvas.addListener(SWT.Paint, new Listener() {
      public void handleEvent(Event event) {

        if (image == null) {
          return;
        }
        GC gc = event.gc;
        ImageData imageData = imageMetrics.getImageData();

        if (zoomFeedback != null) {
          zoomFeedback(gc, imageData);
        } else {

          // XXX we may improve it. Give the image already modified
          /*
           * Scale the image when drawing, using the user's selected
           * scaling factor.
           */
          int w = Math.round(imageData.width * imageMetrics.getScale());
          int h = Math.round(imageData.height * imageMetrics.getScale());

          /* Draw the image */
          gc.drawImage(image, 0, 0, imageData.width, imageData.height, imageMetrics.getHScrollValue()
                + imageData.x, imageMetrics.getVScrollValue() + imageData.y, w, h);
        }
      }
    });

    canvas.addControlListener(new ControlAdapter() {
      @Override
      public void controlResized(ControlEvent event) {
        if (image == null) {
          return;
        }
        imageMetrics.updateMaxXY();
      }
    });

  }

  /**
   * Loads the file inside the canvas.
   */
  private void loadFile() {

    setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_WAIT));

    FileDialog dialog = new FileDialog(Display.getCurrent().getActiveShell());

    dialog.setFileName(Preferences.getImagePath());
    // dialog.setFileName(cmd.getImagePath());

    String file = dialog.open();

    if (file != null && !file.equals("")) { //$NON-NLS-1$

      this.image = new Image(Display.getCurrent(), file);
      setDefaultLoadData();
      Preferences.setImagePath(file);
      this.cmd.setImagePath(file);
      this.cmd.setImageData(this.imageMetrics.getImageData());

      canvas.redraw(0, 0, this.canvas.getClientArea().width, this.canvas.getClientArea().height, false);

      // restore the default layout/values
      deleteAllPoints();
      getMainComposite().refreshMapGraphicLayer();
    }
    setCursor(null);
  }

  /**
   * Set the default data for the ImageMetrics class.
   */
  private void setDefaultLoadData() {

    assert this.image != null;
    assert this.imageMetrics != null;

    this.imageMetrics.setImageData(image.getImageData());
    this.imageMetrics.setDefaultValues();
  }

  public void update(Observable o, Object arg) {

    if (!(arg instanceof GeoreferencingCommandEventChange))
      return;
    GeoreferencingCommandEventChange cmdEvent = (GeoreferencingCommandEventChange) arg;

    switch (cmdEvent.getEvent()) {
    case IMAGE_LOADED:
      setItemsEnabled(true);
      break;
    default:
      setCertainToolsEnabled(cmd.canEnableImageTools());
      break;
    }
  }

  /**
   * Set buttons deselected.
   */
  private void resetRadioButtons() {

    itemAdd.setSelection(false);
    itemDelete.setSelection(false);
    itemDragDrop.setSelection(false);
    itemZoomIn.setSelection(false);
    itemZoomOut.setSelection(false);
    itemPan.setSelection(false);
  }

  /**
   * Delete all the points from the canvas and also the current preview if
   * exist.
   */
  public void deleteAllPoints() {

    this.markPresenterList.clear();
    this.cmd.deleteAllMarks();
    this.canvas.redraw();
    this.canvas.setCursor(null);
    resetRadioButtons();
    deactivateTools();
  }

  /**
   * Handles all the input actions of the canvas.
   *
   * @param e
   *            Mouse event.
   * @param eventType
   *            Input event.
   */
  private void performAction(MouseEvent e, InputEvent eventType) {

    // get the clicked point
    int x = e.x;
    int y = e.y;
    ImageInputEvent event = new ImageInputEvent(e, eventType, x, y);

    ImageTool tool = getActiveTool();
    if (tool == null) {
      return;

    }
    this.canvas.setCursor(tool.getCursor());
    tool.eventHandle(event);
  }

  /**
   * @return The active tool.
   */
  private ImageTool getActiveTool() {

    for (ImageTool tool : tools) {

      if (tool.isActive()) {
        return tool;
      }
    }
    return null;
  }

  public void setContext(IToolContext newContext) {

    if (this.toolContext == null) {
      // add the listener the first time.
      getMainComposite().getMapMarkGraphic().addMouseSelectionListener(mapMouseSelectionListener);
      getMainComposite().addMouseSelectionListenerToCoordinate(coordPanelMouseSelectionListener);
    }
    this.toolContext = newContext;
    if (cmd.getMap() != null) {
      itemLoad.setEnabled(true);
    }
  }

  /**
   * When the view is closed, delete the listeners.
   *
   * @param mainComposite
   *            The main composite.
   */
  public void close(MainComposite mainComposite) {

    if (this.toolContext != null) {// && !isDisposed()) {
      // delete the listener
      mainComposite.getMapMarkGraphic().deleteMouseSelectionListener(mapMouseSelectionListener);
      mainComposite.deleteMouseSelectionListenerToCoordinate(coordPanelMouseSelectionListener);
    }
  }

  /**
   * Given the following mark list, creates its presenters and store these
   * marks in the command.
   *
   * @param marks
   *            Marks loaded from a property file.
   */
  public void createMarks(Map<String, MarkModel> marks) {

    int hScroll = Math.abs(imageMetrics.getHScrollValue());
    int vScroll = Math.abs(imageMetrics.getVScrollValue());

    Set<Entry<String, MarkModel>> entrySet = marks.entrySet();
    Iterator<Entry<String, MarkModel>> iter = entrySet.iterator();
    while (iter.hasNext()) {

      Entry<String, MarkModel> entry = iter.next();
      MarkModel markModel = entry.getValue();
     
      Point position = new Point(markModel.getXImage(), markModel.getYImage());
     
      MarkImagePresenter markPresenter = MarkImagePresenterFactory.createMarkPresenter(markModel, position,  hScroll, vScroll, getCanvas(), getScale());
     
      addMarkPresenter(markPresenter);

      this.cmd.addMark(markModel);
    }
    this.cmd.evalPrecondition();
    canvas.redraw();
  }

  /**
   * Get the main composite.
   *
   * @return The {@link MainComposite}.
   */
  public MainComposite getMainComposite() {

    Composite parent = getParent();
    for (;;) {
      if (parent instanceof MainComposite) {
        return (MainComposite) parent;
      } else {
        parent = parent.getParent();
      }
    }
  }

  @Override
  public void setEnabled(boolean enabled) {

    this.imageToolBar.setEnabled(enabled);
    this.canvas.setEnabled(enabled);

    super.setEnabled(enabled);
  }

  /**
   * The scale.
   *
   * @return
   */
  public float getScale() {

    return this.imageMetrics.getScale();
  }

  /**
   * Broadcast the pan event through all the mark presenters.
   */
  public void broadcastPanEvent() {

    int hScroll = Math.abs(imageMetrics.getHScrollValue());
    int vScroll = Math.abs(imageMetrics.getVScrollValue());

    // broadcast this event through all the presenters.
    for (MarkImagePresenter presenter : markPresenterList) {

      presenter.eventHandler(InputEvent.PAN, hScroll, vScroll);
    }
  }

  /**
   * Broadcast the zoom event through all the mark presenters.
   */
  public void broadcastZoomEvent() {

    // broadcast this event through all the presenters.
    for (MarkImagePresenter presenter : markPresenterList) {

      presenter.eventHandler(InputEvent.ZOOM, this.imageMetrics.getScale());
    }
  }

  /**
   * Fit the image inside the canvas. Calculates which side will fit better,
   * width or height.
   */
  private void zoomFit() {

    this.imageMetrics.updateScrollValues(0, 0);

    // calculate the xscale and yscale so the image width or height will fit
    // the canvas size.
    ImageData imageData = this.imageMetrics.getImageData();

    float canvasHeight = this.canvas.getClientArea().height;
    float canvasWidth = this.canvas.getClientArea().width;
    float imageHeight = imageData.height;
    float imageWidth = imageData.width;

    float heightFactor = canvasHeight / imageHeight;
    float widthFactor = canvasWidth / imageWidth;

    if (heightFactor > widthFactor) {
      this.imageMetrics.updateScale(heightFactor);
    } else {
      this.imageMetrics.updateScale(widthFactor);
    }

    updateZoomState();
  }

  /**
   * Update some variables that involves zooming, broadcast zoom and pan event
   * to the mark presenters and repaint them.
   */
  private void updateZoomState() {

    this.imageMetrics.updateMaxXY();

    this.broadcastPanEvent();
    this.broadcastZoomEvent();

    if (this.image != null) {
      this.canvas.redraw();
    }
  }

  /**
   * Check if the given coordinate is under any of the existent mark image
   * presenters
   *
   * @param x
   *            Position
   * @param y
   *            Position
   * @return If it found a presenter, it'll return it. Null otherwise.
   */
  public MarkImagePresenter getMarkUnderCursor(int x, int y) {

    for (MarkImagePresenter presenter : this.markPresenterList) {

      if (presenter.eventHandler(InputEvent.MOUSE_OVER, x, y)) {

        return presenter;
      }
    }
    return null;
  }

  /**
   * @return The mark presenters list.
   */
  public List<MarkImagePresenter> getMarkPresenterList() {

    return this.markPresenterList;
  }

  /**
   * Used by the presenters.
   *
   * @return The canvas.
   */
  public Canvas getCanvas() {

    return this.canvas;
  }

  /**
   * @return The {@link GeoReferencingCommand}.
   */
  public GeoReferencingCommand getCmd() {

    return this.cmd;
  }

  /**
   * Add the given presenter to the list of {@link MarkImagePresenterImp}.
   *
   * @param newMarkPresenter
   *            The presenter.
   */
  public void addMarkPresenter(MarkImagePresenter newMarkPresenter) {

    this.markPresenterList.add(newMarkPresenter);
  }

  /**
   * @return The are of the canvas.
   */
  public Rectangle getCanvasClientArea() {

    return this.canvas.getClientArea();
  }

  /**
   * Makes a canvas redraw.
   */
  public void canvasRedraw() {

    this.canvas.redraw();
  }

  /**
   * @return The horizontal scroll value.
   */
  public int getHScrollValue() {

    return this.imageMetrics.getHScrollValue();
  }

  /**
   * @return The vertical scroll value.
   */
  public int getVScrollValue() {

    return this.imageMetrics.getVScrollValue();
  }

  /**
   * @return The max X.
   */
  public int getMaxX() {

    return this.imageMetrics.getMaxX();
  }

  /**
   * @return The max Y.
   */
  public int getMaxY() {

    return this.imageMetrics.getMaxY();
  }

  /**
   * Updates the values of both scroll.
   *
   * @param xScrollSelection
   *            Horizontal scroll value.
   * @param yScrollSelection
   *            Vertical scroll value.
   */
  public void updateScrollValues(int xScrollSelection, int yScrollSelection) {

    this.imageMetrics.updateScrollValues(xScrollSelection, yScrollSelection);
  }

  /**
   * Increase the current value of the scale.
   *
   * @param addValue
   *            Increment value.
   */
  public void increaseScale(float addValue) {

    this.imageMetrics.increaseScale(addValue);
  }

  /**
   * Decrease the current value of the scale.
   *
   * @param subtractValue
   *            Decrement value.
   */
  public void decreaseScale(float subtractValue) {

    this.imageMetrics.decreaseScale(subtractValue);
  }

  /**
   * Check if the given point is inside the image borders.
   *
   * @param point
   *            Point to validate.
   * @return True if it lies inside the image.
   */
  public boolean validateInside(Point point) {

    return this.image.getBounds().contains(point);
  }

  /**
   * <p>
   * Zoom operation.
   * </p>
   *
   * Given the clicked point and the actual and previous zoom factor, it'll
   * calculate the current image position inside the canvas.
   *
   * <p>
   * If the image position is smaller than the canvas, it'll show at the 0,0
   * position.
   *
   * If the entire image can be shown inside the canvas area, it'll show it no
   * matter what are the scrolls position in that moment.
   * </p>
   *
   * @param x
   *            Position in the canvas.
   * @param y
   *            Position in the canvas.
   */
  public void focusPosition(int x, int y) {

    ImageData imageData = this.imageMetrics.getImageData();
    int w = Math.round(imageData.width * this.imageMetrics.getScale());
    int h = Math.round(imageData.height * this.imageMetrics.getScale());
    Rectangle area = this.getCanvasClientArea();

    // first, calculate if any of the sides of the image can fit inside the
    // canvas. If that's true, there will not be any scroll.
    int HScroll;
    int VScroll;
    if (area.width >= w || area.height >= h) {

      HScroll = 0;
      VScroll = 0;
    } else {

      float oldHScrollValue = this.imageMetrics.getHScrollValue();
      float oldVScrollValue = this.imageMetrics.getVScrollValue();

      // calculate the actual click point value respect the image.
      int xImg = x + Math.abs(Math.round(oldHScrollValue));
      int yImg = y + Math.abs(Math.round(oldVScrollValue));

      int actualXimg = Math.round((Math.round(xImg * this.imageMetrics.getScale()))
            / this.imageMetrics.getPreviousScale());
      int actualYimg = Math.round((Math.round(yImg * this.imageMetrics.getScale()))
            / this.imageMetrics.getPreviousScale());

      // negative values
      HScroll = -(Math.abs(actualXimg - x));
      VScroll = -(Math.abs(actualYimg - y));

      // check the scroll fit inside the canvas
      if ((HScroll != 0) && (Math.abs(HScroll) + this.imageMetrics.getMaxX() < area.width)) {
        HScroll = 0;
      }
      if ((VScroll != 0) && (Math.abs(VScroll) + this.imageMetrics.getMaxY() < area.height)) {
        VScroll = 0;
      }
    }

    this.imageMetrics.updatePreviousScroll();
    this.imageMetrics.updateScrollValues(HScroll, VScroll);
    this.imageMetrics.updatePreviousScale();

    this.updateZoomState();
  }

  /**
   * Creates the zoom feedback when zoom in happens.
   *
   * @param x
   *            Image position.
   * @param y
   *            Image position.
   */
  public void setZoomInFeedback(int x, int y) {
    this.zoomFeedback = new ZoomFeedBack(x, y, ZOOM_TYPE.ZOOM_IN);
  }

  /**
   * Creates the zoom feedback when zoom out happens.
   *
   * @param x
   *            Image position.
   * @param y
   *            Image position.
   */
  public void setZoomOutFeedback(int x, int y) {
    this.zoomFeedback = new ZoomFeedBack(x, y, ZOOM_TYPE.ZOOM_OUT);
  }

  /**
   * Feedback method, draw the image as it is before zoom and show the
   * feedback.
   *
   * @param gc
   * @param imageData
   */
  private void zoomFeedback(GC gc, ImageData imageData) {

    int w = Math.round(imageData.width * imageMetrics.getScaleBeforeZoom());
    int h = Math.round(imageData.height * imageMetrics.getScaleBeforeZoom());

    /* Draw the image */
    gc.drawImage(image, 0, 0, imageData.width, imageData.height, imageMetrics.getHScrollBeforeZoom() + imageData.x,
          imageMetrics.getVScrollBeforeZoom() + imageData.y, w, h);

    switch (zoomFeedback.type) {
    case ZOOM_IN:
      zoomInFeedBack(gc, zoomFeedback.x, zoomFeedback.y, zoomFeedback.startNumber);
      break;
    case ZOOM_OUT:
      zoomOutFeedBack(gc, zoomFeedback.x, zoomFeedback.y, zoomFeedback.startNumber);
      break;
    default:
      break;
    }

    this.canvas.redraw();

  }

  /**
   * Updates the number of times the feedback will be shown when doing zoom
   * in.
   *
   * @param gc
   *            Graphic context
   * @param x
   *            Position
   * @param y
   *            Position
   * @param startNumber
   *            feedback number counter.
   */
  private void zoomInFeedBack(GC gc, int x, int y, int startNumber) {

    drawZoomFeedBack(gc, x, y, startNumber);

    if (startNumber == 3) {
      zoomFeedback = null;
    } else {
      zoomFeedback.startNumber++;
    }
  }

  /**
   * Updates the number of times the feedback will be shown when doing zoom
   * out.
   *
   * @param gc
   *            Graphic context
   * @param x
   *            Position
   * @param y
   *            Position
   * @param startNumber
   *            feedback number counter.
   */
  private void zoomOutFeedBack(GC gc, int x, int y, int startNumber) {

    drawZoomFeedBack(gc, x, y, startNumber);

    if (startNumber == 1) {
      zoomFeedback = null;
    } else {
      zoomFeedback.startNumber--;
    }
  }

  /**
   * Draws the feedback. It'll draw 3 different feedbacks depending on the
   * start number.
   *
   * @param gc
   *            Graphic context
   * @param x
   *            Position
   * @param y
   *            Position
   * @param startNumber
   *            Current tick number, between 1 and 3.
   */
  private void drawZoomFeedBack(GC gc, int x, int y, int startNumber) {

    Color c = Color.RED;
    org.eclipse.swt.graphics.Color color = new org.eclipse.swt.graphics.Color(gc.getDevice(), c.getRed(),
          c.getGreen(), c.getBlue());
    gc.setForeground(color);
    gc.setBackground(color);

    int subtractValue = 0, addValue = 0;

    switch (startNumber) {
    case 1:
      subtractValue = 15;
      addValue = 30;
      break;
    case 2:
      subtractValue = 20;
      addValue = 40;
      break;
    case 3:
      subtractValue = 25;
      addValue = 50;
      break;
    default:
      break;
    }
    gc.drawRectangle(x - subtractValue, y - subtractValue, addValue, addValue);

    gc.drawLine(x, y + subtractValue, x, y + addValue);
    gc.drawLine(x, y - subtractValue, x, y - addValue);
    gc.drawLine(x - subtractValue, y, x - addValue, y);
    gc.drawLine(x + subtractValue, y, x + addValue, y);
  }

  /**
   * When a mouse over happens on the map, show the feedback in the image
   * composite.
   *
   * @param mark
   *            The mark model.
   */
  private void mouseOverFeedback(MarkModel mark) {

    // find the presenter which contains this mark, and show the feedback
    // for the given mark.

    for (MarkImagePresenter presenter : this.markPresenterList) {

      if (presenter.getMarkModel().equals(mark)) {
        presenter.showSelectedFeedback(true);
      } else {
        presenter.showSelectedFeedback(false);
      }
    }
    this.canvas.redraw();
  }

  /**
   * Represents the feedback when the mouse is not over a mark, that means
   * that this mark musn't show his feedback.
   *
   * @param mark
   *            The mark model.
   */
  private void mouseNotOverFeedback(MarkModel mark) {

    for (MarkImagePresenter presenter : this.markPresenterList) {

      if (presenter.getMarkModel().equals(mark)) {
        presenter.showSelectedFeedback(false);
      }
    }
    this.canvas.redraw();
  }

  /**
   * Adds a {@link MouseSelectionListener} to the tools.
   *
   * @param listener
   *            The listener.
   */
  public void addMouseSelectionListener(MouseSelectionListener listener) {

    for (ImageTool tool : tools) {

      tool.addMouseSelectionListener(listener);
    }
  }

  /**
   * Delete a {@link MouseSelectionListener} from the tools.
   *
   * @param listener
   *            The listener
   */
  public void deleteMouseSelectionListener(MouseSelectionListener listener) {

    for (ImageTool tool : tools) {

      tool.deleteMouseSelectionListener(listener);
    }
  }

  /**
   * Class used to maintain valuable info about the feedback process.
   */
  static class ZoomFeedBack {
    protected int    x, y, startNumber;
    protected ZOOM_TYPE  type;

    public enum ZOOM_TYPE {
      ZOOM_IN, ZOOM_OUT
    }

    /**
     * Constructor. Depending on the zoom_type it'll automatically set a
     * value in the startNumber variable.
     *
     * @param x
     *            Position.
     * @param y
     *            Position.
     * @param type
     *            Zoom type (in/out).
     */
    public ZoomFeedBack(int x, int y, ZOOM_TYPE type) {

      this.x = x;
      this.y = y;
      this.type = type;
      switch (type) {
      case ZOOM_IN:
        this.startNumber = 1;
        break;
      case ZOOM_OUT:
        this.startNumber = 3;
        break;
      default:
        break;
      }
    }
  }
}
TOP

Related Classes of org.locationtech.udig.image.georeferencing.internal.ui.imagepanel.ImageComposite

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.