blob: 3164a60a0427c4d63052c0c3aee8177f13430694 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2005, 2014 IBM Corporation, Ericsson
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM - Initial API and implementation
* Bernd Hufmann - Updated for TMF
**********************************************************************/
package org.eclipse.tracecompass.tmf.ui.views.uml2sd.core;
import java.util.Comparator;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.drawings.IGC;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.ISDPreferences;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.preferences.SDViewPref;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.SortAsyncForBackward;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.util.SortAsyncMessageComparator;
/**
* A AsyncMessage is a asynchronous message which appear at two different event occurrences on each lifeline ends (sender
* and receiver).<br>
* <br>
* <br>
* Usage example:
*
* <pre>
* Frame frame;
* Lifeline lifeLine1;
* Lifeline lifeLine2;
*
* AsyncMessage message = new AsyncMessage();
* // Create a new event occurrence on each lifeline
* lifeline1.getNewOccurrenceIndex();
* lifeline2.getNewOccurrenceIndex();
* // Set the message sender and receiver
* message.setStartLifeline(lifeLine1);
* message.setEndLifline(lifeline2);
* message.setName(&quot;Message label&quot;);
* // add the message to the frame
* frame.addMessage(message);
* </pre>
*
* @see Lifeline Lifeline for more event occurence details
* @version 1.0
* @author sveyrier
*/
public class AsyncMessage extends BaseMessage implements ITimeRange {
// ------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------
/**
* The grahNode ID constant
*/
public static final String ASYNC_MESS_TAG = "AsyncMessage"; //$NON-NLS-1$
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
/**
* Flag whether message has time information or not.
*/
private boolean fHasTime = false;
/**
* The time when the message begin
*/
private ITmfTimestamp fEndTime = TmfTimestamp.fromSeconds(0);
/**
* The time when the message end
*/
private ITmfTimestamp fStartTime = TmfTimestamp.fromSeconds(0);
/**
* The associated message.
*/
protected AsyncMessageReturn fMessageReturn = null;
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Default constructor
*/
public AsyncMessage() {
setColorPrefId(ISDPreferences.PREF_ASYNC_MESS);
}
// ------------------------------------------------------------------------
// Methods
// ------------------------------------------------------------------------
@Override
public int getX() {
int x = super.getX(true);
int activationWidth = Metrics.EXECUTION_OCCURRENCE_WIDTH / 2;
if ((getStartLifeline() != null) && (getEndLifeline() != null) && (getStartLifeline().getX() > getEndLifeline().getX())) {
activationWidth = -activationWidth;
}
if (isMessageStartInActivation(getStartOccurrence())) {
x = x + activationWidth;
}
return x;
}
@Override
public int getY() {
if ((getStartLifeline() != null) && (getEndLifeline() != null)) {
return getEndLifeline().getY() + getEndLifeline().getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * getStartOccurrence();
}
return super.getY();
}
@Override
public int getWidth() {
int width = super.getWidth(true);
int activationWidth = Metrics.EXECUTION_OCCURRENCE_WIDTH / 2;
if ((getStartLifeline() != null) && (getEndLifeline() != null) && (getStartLifeline().getX() > getEndLifeline().getX())) {
activationWidth = -activationWidth;
}
if (isMessageStartInActivation(getStartOccurrence())) {
width = width - activationWidth;
}
if (isMessageEndInActivation(getEndOccurrence())) {
width = width - activationWidth;
}
return width;
}
@Override
public int getHeight() {
if ((getStartLifeline() != null) && (getEndLifeline() != null)) {
return (getEndLifeline().getY() + getEndLifeline().getHeight() + (Metrics.getMessageFontHeigth() + Metrics.getMessagesSpacing()) * getEndOccurrence()) - getY();
}
return super.getHeight();
}
/**
* Set the message return associated with this message.
*
* @param message the message return to associate
*/
protected void setMessageReturn(AsyncMessageReturn message) {
fMessageReturn = message;
}
/**
* Set the event occurrence attached to this message for its end lifeline
*
* @param occurrence the event occurrence to set
*/
@Override
public void setEndOccurrence(int occurrence) {
super.setEndOccurrence(occurrence);
if (getStartLifeline() == null) {
setStartOccurrence(occurrence);
}
informFrame(getEndLifeline(), occurrence);
}
/**
* Informs the given lifeline about the maximum occurrence if applicable.
*
* @param lifeLine
* Concerned lifeline
* @param occurrence
* Occurrence number
*/
protected void informFrame(Lifeline lifeLine, int occurrence) {
if ((lifeLine != null) && (lifeLine.getFrame() != null) && (lifeLine.getFrame().getMaxEventOccurrence() < occurrence)) {
lifeLine.getFrame().setMaxEventOccurrence(occurrence);
}
}
/**
* Set the event occurrence attached to this message for its start lifeline
*
* @param occurrence the event occurrence to set
*/
@Override
public void setStartOccurrence(int occurrence) {
super.setStartOccurrence(occurrence);
if (getEndLifeline() == null) {
setEndOccurrence(getStartOccurrence());
}
informFrame(getStartLifeline(), occurrence);
}
/**
* Set the lifeLine which has sent the message.<br>
* A new EventOccurence will be create on this lifeLine.<br>
*
* @param lifeline the message sender
*/
public void autoSetStartLifeline(Lifeline lifeline) {
lifeline.getNewEventOccurrence();
setStartLifeline(lifeline);
}
/**
* Set the lifeLine which has received the message.<br>
* A new EventOccurence will be create on this lifeLine.<br>
*
* @param lifeline the message receiver
*/
public void autoSetEndLifeline(Lifeline lifeline) {
lifeline.getNewEventOccurrence();
setEndLifeline(lifeline);
}
@Override
public void setStartLifeline(Lifeline lifeline) {
super.setStartLifeline(lifeline);
setStartOccurrence(getStartLifeline().getEventOccurrence());
if (getEndLifeline() == null) {
setEndOccurrence(getStartOccurrence());
}
}
@Override
public void setEndLifeline(Lifeline lifeline) {
super.setEndLifeline(lifeline);
setEventOccurrence(getEndLifeline().getEventOccurrence());
}
/**
* Returns true if the point C is on the segment defined with the point A and B
*
* @param xA point A x coordinate
* @param yA point A y coordinate
* @param xB point B x coordinate
* @param yB point B y coordinate
* @param xC point C x coordinate
* @param yC point C y coordinate
* @return Return true if the point C is on the segment defined with the point A and B, else otherwise
*/
protected boolean isNearSegment(int xA, int yA, int xB, int yB, int xC, int yC) {
if ((xA > xB) && (xC > xA)) {
return false;
}
if ((xA < xB) && (xC > xB)) {
return false;
}
if ((xA < xB) && (xC < xA)) {
return false;
}
if ((xA > xB) && (xC < xB)) {
return false;
}
double distAB = Math.sqrt((xB - xA) * (xB - xA) + (yB - yA) * (yB - yA));
double scalar = ((xB - xA) * (xC - xA) + (yB - yA) * (yC - yA)) / distAB;
double distAC = Math.sqrt((xC - xA) * (xC - xA) + (yC - yA) * (yC - yA));
double distToSegment = Math.sqrt(Math.abs(distAC * distAC - scalar * scalar));
return (distToSegment <= Metrics.MESSAGE_SELECTION_TOLERANCE);
}
@Override
public boolean contains(int x, int y) {
// Is it a self message?
if (getStartLifeline() == getEndLifeline()) {
return super.contains(x, y);
}
if (isNearSegment(getX(), getY(), getX() + getWidth(), getY() + getHeight(), x, y)) {
return true;
}
int messageMaxWidth = Metrics.swimmingLaneWidth() - Metrics.EXECUTION_OCCURRENCE_WIDTH;
int nameWidth = getName().length() * Metrics.getAverageCharWidth();
if (getName().length() * Metrics.getAverageCharWidth() > messageMaxWidth) {
if (GraphNode.contains(getX(), getY() - Metrics.MESSAGES_NAME_SPACING - Metrics.getMessageFontHeigth(), messageMaxWidth, Metrics.getMessageFontHeigth(), x, y)) {
return true;
}
} else {
if (GraphNode.contains(getX() + (messageMaxWidth - nameWidth) / 2, getY() + getHeight() / 2 - Metrics.MESSAGES_NAME_SPACING - Metrics.getMessageFontHeigth(), nameWidth, Metrics.getMessageFontHeigth(), x, y)) {
return true;
}
}
return false;
}
/**
* Draws the asynchronous message using giving graphical context.
*
* @param context A graphical context to draw in.
*/
protected void drawAsyncMessage(IGC context) {
if (getStartLifeline() != null && getEndLifeline() != null && getStartLifeline() == getEndLifeline() && (getStartOccurrence() != getEndOccurrence())) {
int x = getX();
int y = getY();
int height = getHeight();
int tempx = 0;
boolean startInActivation = isMessageStartInActivation(getStartOccurrence());
boolean endInActivation = isMessageEndInActivation(getEndOccurrence());
if (endInActivation && !startInActivation) {
tempx = Metrics.EXECUTION_OCCURRENCE_WIDTH / 2;
}
if (startInActivation && !endInActivation) {
tempx = -Metrics.EXECUTION_OCCURRENCE_WIDTH / 2;
}
int tempy = Metrics.INTERNAL_MESSAGE_WIDTH / 2;
if (getHeight() <= Metrics.INTERNAL_MESSAGE_WIDTH) {
tempy = getHeight() / 2;
}
context.drawLine(x, y, x + Metrics.INTERNAL_MESSAGE_WIDTH / 2, y);
context.drawLine(x + Metrics.INTERNAL_MESSAGE_WIDTH, y + tempy, x + Metrics.INTERNAL_MESSAGE_WIDTH, y + height - tempy);
context.drawLine(x + tempx, y + height, x + Metrics.INTERNAL_MESSAGE_WIDTH / 2, y + height);
Double xt = Double.valueOf(Math.cos(0.75) * 7);
Double yt = Double.valueOf(Math.sin(0.75) * 7);
context.drawLine(x + xt.intValue() + tempx, y + height + yt.intValue(), x + tempx, y + height);
context.drawArc(x, y, Metrics.INTERNAL_MESSAGE_WIDTH, 2 * tempy, 0, 90);
context.drawArc(x, y + height, Metrics.INTERNAL_MESSAGE_WIDTH, -2 * tempy, 0, -90);
context.drawLine(x + xt.intValue() + tempx, y + height - yt.intValue(), x + tempx, y + height);
context.drawTextTruncated(getName(), x + Metrics.INTERNAL_MESSAGE_WIDTH + Metrics.INTERNAL_MESSAGE_V_MARGIN, y, Metrics.swimmingLaneWidth() - Metrics.EXECUTION_OCCURRENCE_WIDTH + -Metrics.INTERNAL_MESSAGE_WIDTH,
+Metrics.MESSAGES_NAME_SPACING - Metrics.getMessageFontHeigth(), !isSelected());
} else {
super.draw(context);
}
}
@Override
public void draw(IGC context) {
if (!isVisible()) {
return;
}
ISDPreferences pref = SDViewPref.getInstance();
// Draw it selected?
if (isSelected() && (getStartLifeline() != null && getEndLifeline() != null && getStartLifeline() == getEndLifeline() && (getStartOccurrence() != getEndOccurrence()))) {
/*
* Draw it twice First time, bigger inverting selection colors Second time, regular drawing using selection
* colors This create the highlight effect
*/
context.setForeground(pref.getBackGroundColorSelection());
context.setLineWidth(Metrics.SELECTION_LINE_WIDTH);
drawAsyncMessage(context);
context.setBackground(pref.getBackGroundColorSelection());
context.setForeground(pref.getForeGroundColorSelection());
// Second drawing is done after the else
} else {
context.setBackground(pref.getBackGroundColor(getColorPrefId()));
context.setForeground(pref.getForeGroundColor(getColorPrefId()));
}
if (hasFocus()) {
context.setDrawTextWithFocusStyle(true);
}
context.setLineWidth(Metrics.NORMAL_LINE_WIDTH);
drawAsyncMessage(context);
if (hasFocus()) {
context.setDrawTextWithFocusStyle(false);
}
}
/**
* Set the time when the message end
*
* @param time the time when the message end
*/
public void setEndTime(ITmfTimestamp time) {
fEndTime = time;
fHasTime = true;
if (getStartLifeline() != null && getStartLifeline().getFrame() != null) {
getStartLifeline().getFrame().setHasTimeInfo(true);
} else if (getEndLifeline() != null && getEndLifeline().getFrame() != null) {
getEndLifeline().getFrame().setHasTimeInfo(true);
}
}
/**
* Set the time when the message start
*
* @param time the time when the message start
*/
public void setStartTime(ITmfTimestamp time) {
fStartTime = time;
fHasTime = true;
if (getStartLifeline() != null && getStartLifeline().getFrame() != null) {
getStartLifeline().getFrame().setHasTimeInfo(true);
} else if (getEndLifeline() != null && getEndLifeline().getFrame() != null) {
getEndLifeline().getFrame().setHasTimeInfo(true);
}
}
@Override
public ITmfTimestamp getEndTime() {
return fEndTime;
}
@Override
public ITmfTimestamp getStartTime() {
return fStartTime;
}
@Override
public boolean hasTimeInfo() {
return fHasTime;
}
/**
* @return message return instance or null
*/
public AsyncMessageReturn getMessageReturn() {
return fMessageReturn;
}
@Override
public boolean isVisible(int x, int y, int width, int height) {
int toDrawY = getY();
int toDrawHeight = getHeight();
if ((toDrawY > y + height + Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth()) && (toDrawY + toDrawHeight > y + height + Metrics.MESSAGES_NAME_SPACING + Metrics.getMessageFontHeigth())) {
return false;
}
if (toDrawY < y && (toDrawY + toDrawHeight < y)) {
return false;
}
return super.isVisible(x, y, width, height);
}
@Override
public Comparator<GraphNode> getComparator() {
return new SortAsyncMessageComparator();
}
@Override
public String getArrayId() {
return ASYNC_MESS_TAG;
}
@Override
public Comparator<GraphNode> getBackComparator() {
return new SortAsyncForBackward();
}
@Override
public boolean positiveDistanceToPoint(int x, int y) {
int mY = getY();
int mH = getHeight();
return ((mY > y) || (mY + mH > y));
}
}