protected PointF getRelativePositionToSuperobject() {

    float x = centerX;
    float y = centerY;

    if (this.isCustomPivotUsed()) {
      if (this.angle == 0.0f) {
        x += this.getPivotXRelativeToCenter() * this.scaleX;
        y += this.getPivotYRelativeToCenter() * this.scaleY;
      } else {
        PointF pivotPosition = this.getPivotPointPositionConsideringScalingAndAngle();

        x += pivotPosition.x;
        y += pivotPosition.y;
      }
    }

    float superAngle = superDrawable.angle;
    float angleToCenter = (float) Math.atan2(y - superDrawable.centerY, x - superDrawable.centerX);

    float angle = superAngle - angleToCenter;

    float radius =
        (float)
                Math.sqrt(
                    Math.pow(Math.abs(x - superDrawable.centerX), 2)
                        + Math.pow(Math.abs(y - superDrawable.centerY), 2))
            / superDrawable.scaleX;

    float newX = (float) (radius * Math.cos(angle) + superDrawable.getWidth() / 2);
    float newY = (float) (radius * Math.sin(angle * -1) + superDrawable.getHeight() / 2);

    return new PointF(newX, newY);
  }
  /** Called by activity's onResume() method to load the images */
  public void load() {
    if (firstLoad) {
      getMetrics();
      firstLoad = false;
    }

    for (MultiTouchDrawable sub : subDrawables) {
      sub.load();
    }

    //		float cx, cy, sx, sy;
    //		if (firstLoad) {
    //			cx = 0;
    //			cy = 0;
    //
    //			float sc = 1;
    //			sx = sy = sc;
    //			firstLoad = false;
    //		} else {
    //			// Reuse position and scale information if it is available
    //			// FIXME this doesn't actually work because the whole activity is
    //			// torn down and re-created on rotate
    //			cx = this.centerX;
    //			cy = this.centerY;
    //			sx = this.scaleX;
    //			sy = this.scaleY;
    //		}
    //		setPos(cx, cy, sx, sy, 0.0f, FLAG_FORCEALL, false);
  }
  /**
   * callback method for onTouch events
   *
   * @param pointinfo info about the touch event
   * @return
   *     <p><b>true</b> if the touch event is handled by this event<br>
   *     <b>false</b> if this touch event is not for this element
   *     <p>This is intended, that a subobject can check, if it should handle a click or so, or if
   *     the event should be sent to the underlying object.
   */
  public boolean onTouch(PointInfo pointinfo) {
    boolean handleEvent = false;

    // first ask our subobjects to handle it, otherwise let us handle it

    for (int i = subDrawables.size() - 1; i >= 0 && !handleEvent; i--) {

      MultiTouchDrawable sub = subDrawables.get(i);

      if (sub.containsPoint(pointinfo.getX(), pointinfo.getY())) {
        handleEvent = sub.onTouch(pointinfo);
      }
    }

    // Logger.d("touch action: "+this.getClass().getSimpleName()+" " +
    // pointinfo.getAction()+
    // " 0x"+Integer.toHexString(pointinfo.getAction()));

    // it's hard to find MOTION_UP events, because if the finger does not
    // stay exactly on the correct position,
    // this will change to dragging and we wont't get the drag end as
    // MOTION_UP

    if (!handleEvent
        && pointinfo.isMultiTouch() == false
        && pointinfo.getNumTouchPoints() == 1
        && pointinfo.getAction() == MotionEvent.ACTION_DOWN) {
      handleEvent = this.onSingleTouch(pointinfo);
    }

    return handleEvent;
  }
  /** hide all open popups */
  public void hidePopups() {
    if (this instanceof Popup) {
      ((Popup) this).setActive(false);
    }

    for (MultiTouchDrawable d : subDrawables) {
      d.hidePopups();
    }
  }
  public MultiTouchDrawable getDraggableObjectAtPoint(PointInfo pt) {
    float x = pt.getX(), y = pt.getY();
    int n = subDrawables.size();
    for (int i = n - 1; i >= 0; i--) {
      MultiTouchDrawable im = subDrawables.get(i);

      if (im.isDragable() && im.containsPoint(x, y)) {
        return im.getDraggableObjectAtPoint(pt);
      }
    }

    if (this.containsPoint(pt.getX(), pt.getY())) {
      return this;
    }

    return null;
  }
 /**
  * set relative position to super object if set
  *
  * @param xPos relative x position
  * @param yPos relative y position
  */
 public void setRelativePosition(float relX, float relY) {
   this.relX = relX;
   this.relY = relY;
   if (superDrawable != null) {
     superDrawable.recalculatePositions();
   }
   onRelativePositionUpdate();
 }
 /**
  * tries to bring the current element to the front.
  *
  * @return true, if this element could be brought to front
  */
 public boolean bringToFront() {
   if (superDrawable != null) {
     superDrawable.bringSubDrawableToFront(this);
     return true;
   } else {
     Logger.d("we can't bring ourselfs to front, because we are not attached to a super drawable");
   }
   return false;
 }
  /**
   * constructor with superDrawable
   *
   * @param context
   * @param superDrawable super Drawable Object
   */
  public MultiTouchDrawable(Context context, MultiTouchDrawable superDrawable) {
    id = counter++;

    this.ctx = context;
    this.superDrawable = superDrawable;

    this.resources = context.getResources();
    subDrawables = new ArrayList<MultiTouchDrawable>();

    superDrawable.addSubDrawable(this);
  }
  protected PointF getAbsolutePositionOfSubobject(MultiTouchDrawable subobject) {

    float xBeforeRotate = this.minX + subobject.getRelativeX() * scaleX;
    float yBeforeRotate = this.minY + subobject.getRelativeY() * scaleY;

    float radius =
        (float)
            Math.sqrt(
                Math.pow(Math.abs(centerX - xBeforeRotate), 2)
                    + Math.pow(Math.abs(centerY - yBeforeRotate), 2));

    float angleBeforeRotate = (float) Math.atan2(yBeforeRotate - centerY, xBeforeRotate - centerX);

    float newAngle = angle + angleBeforeRotate;

    float newY = (float) (centerY + radius * Math.sin(newAngle));
    float newX = (float) (centerX + radius * Math.cos(newAngle));

    // Move the drawable according to it's pivot point if one is set
    if (subobject.isCustomPivotUsed()) {
      if (subobject.angle == 0.0f) {
        newX -= subobject.getPivotXRelativeToCenter() * subobject.scaleX;
        newY -= subobject.getPivotYRelativeToCenter() * subobject.scaleY;
      } else {
        PointF pivotPosition = subobject.getPivotPointPositionConsideringScalingAndAngle();

        newX -= pivotPosition.x;
        newY -= pivotPosition.y;
      }
    }

    return new PointF(newX, newY);
  }
  /** Return whether or not the given screen coords are inside this image */
  public boolean containsPoint(float scrnX, float scrnY) {
    // If this is a subdrawable, then don't let the controller think this is
    // the item to be dragged. Otherwise non-draggable subdrawables will not
    // allow to drag the superdrawable when the user's finger is exactly on
    // them. FIXME: Is this the right place to do this?
    // if (this.hasSuperDrawable())
    // return false;
    // else
    // FIXME: need to correctly account for image rotation
    boolean inside = (scrnX >= minX && scrnX <= maxX && scrnY >= minY && scrnY <= maxY);

    if (inside) return true;

    Iterator<MultiTouchDrawable> it = this.subDrawables.iterator();
    while (it.hasNext()) {
      MultiTouchDrawable sub = it.next();
      if (sub.containsPoint(scrnX, scrnY)) {
        return true;
      }
    }

    return false;
  }
 public void addSubDrawable(MultiTouchDrawable subObject) {
   subDrawables.add(subObject);
   subObject.refresher = this.refresher;
   this.setPos(centerX, centerY, scaleX, scaleY, angle, false);
 }
  /** Set the position and scale of an image in screen coordinates */
  protected boolean setPos(
      float centerX,
      float centerY,
      float scaleX,
      float scaleY,
      float angle,
      int flags,
      boolean isDraggedOrPinched) {

    // Reset the scale if the object is not scalable (otherwise the ws and
    // hs calculations below are not 100% accurate and will result in a
    // slight but noticable scaling of non-scalable objects
    if (!isScalable()) {
      scaleX = 1.0f;
      scaleY = 1.0f;
    }

    // Reset the angle if the drawable is not rotatable (for the same reason
    // as above)
    if (!isRotatable()) {
      this.setAngle(0.0f);
    }

    float ws = (width / 2) * scaleX, hs = (height / 2) * scaleY;
    float newMinX = centerX - ws,
        newMinY = centerY - hs,
        newMaxX = centerX + ws,
        newMaxY = centerY + hs;

    float scaleXChange = 1.0f, scaleYChange = 1.0f, angleChange = 0.0f;

    // Min and max values need to be set when item is dragged or scaled
    if ((flags & FLAG_FORCEXY) != 0
        || this.isDragable()
        || (flags & FLAG_FORCESCALE) != 0
        || this.isScalable()) {
      this.minX = newMinX;
      this.minY = newMinY;
      this.maxX = newMaxX;
      this.maxY = newMaxY;
    }

    if ((flags & FLAG_FORCEXY) != 0 || this.isDragable()) {

      this.centerX = centerX;
      this.centerY = centerY;
    }

    if ((flags & FLAG_FORCESCALE) != 0 || this.isScalable()) {
      scaleXChange = scaleX / this.scaleX;
      scaleYChange = scaleY / this.scaleY;
      this.setScale(scaleX, scaleY);
    }

    if ((flags & FLAG_FORCEROTATE) != 0 || this.isRotatable()) {
      angleChange = angle - this.angle;
      // this.angle = angle;
      this.setAngle(angle);
    }

    if (isDraggedOrPinched && this.hasSuperDrawable()) {
      PointF relativePosition = getRelativePositionToSuperobject();
      // we do not want to use setRelativePosition, because this will
      // overwrite the angle
      // this.setRelativePosition(relativePosition.x, relativePosition.y);
      this.setRelativePosition(relativePosition);
    }

    // Iterate through the subobjects and change their position
    for (MultiTouchDrawable subobject : subDrawables) {
      PointF absolutePosition = this.getAbsolutePositionOfSubobject(subobject);
      subobject.setPos(
          absolutePosition.x,
          absolutePosition.y,
          subobject.scaleX * scaleXChange,
          subobject.scaleY * scaleYChange,
          subobject.angle + angleChange,
          FLAG_FORCEXY,
          false);
    }

    return true;
  }
 /** Called by activity's onPause() method to free memory used for loading the images */
 public void unload() {
   for (MultiTouchDrawable sub : subDrawables) {
     sub.unload();
   }
 }