Tag Archives: programming

Creating an OPM GEF Editor – Part 25: “Smart” Multi-line Text Figure

Previous tutorial: Creating an OPM GEF Editor – Part 24: “Smart” Multi-line Text Figure

One of the problems with visual programming languages is layout – you can stay endless hours just arranging the figures in the diagram without any real changes in the program, just to “make it look better”. This is a complete waste of time. For textual languages, it is a standard practice to have code auto-formatters that format the code every time you save. There are even some extremists who consider badly formatted code as having errors (if you are interested in the subject, there are programs like CheckStyle and Sonar to check coding standards, among others. If you use a good another tool and find it helpful, please leave me a note).

Anyway, my solution to this problem is simple: make the layout part of the language, so that it is not “secondary information”. This means that the developer cannot change the layout without changing the meaning of the program. And this also means that the language UI has to be very smart. For example, an OPP process is displayed as an ellipse, with the name of the process inside the ellipse. This could be shown in a number of ways:

Capture2 Capture1 capture4 Capture3

So the developer has a problem – he has to decide how to resize his ellipse in the “best way” possible (which will definitely be different from the “best way” of all other programmers).

My solution: don’t let the programmer decide (at least not directly), but have each model element decide on its size, based on pre-defined configuration. It took me some days to get this to work, and I had to dig and debug the GEF Logic example a couple of times, but finally I managed. I needed to do the following things: 1. calculate how large is the text; 2. calculate the desired width of the the text using a pre-determined width to height ratio (See my question on ux stackexchange regarding this ration); and 3. calculate the final size of the text after resizing it to the calculated width. The last step is needed because the text may actually end narrower because we only want to have new lines when there is a break in the text (spaces, commas, etc.). Draw2d has a component that knows how to do this (called TextFlow) but I couldn’t manage to find a way to calculate the height… I tried to use getPreferredSize() on the TextFlow after setting its size, bounds, etc, but didn’t work. And then I found it: I had to pass -1 as the desired height to the getPreferredSize() method so it calculated it alone!

So now the solution is simple: calculate the size of the text in one line (using the TextUtilities class). Calculate the area that is used by the text and from this derive the desired width, using the width to height ratio. And last, ask the FlowPage to calculate it’s preferred size using this width and a height of -1. I created a new class that does just this:

package com.vainolo.draw2d.extras;

/*******************************************************************************
 * Copyright (c) 2012 Arieh 'Vainolo' Bibliowicz
 * You can use this code for educational purposes. For any other uses
 * please contact me: vainolo@gmail.com
 *******************************************************************************/

import org.eclipse.draw2d.TextUtilities;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.text.FlowPage;
import org.eclipse.draw2d.text.ParagraphTextLayout;
import org.eclipse.draw2d.text.TextFlow;

import com.vainolo.draw2d.extras.translate.JDraw2dToDraw2dTranslations;
import com.vainolo.jdraw2d.HorizontalAlignment;

/**
 * A figure with a {@link TextFlow} that "smartly" calculates its preferred size,
 * using a provided width to height ratio.
 */
public class SmartLabelFigure extends FlowPage {
  private final TextFlow textFlow;
  private double ratio;

  /**
   * Create a new smart label with the given width to height ratio (width = ratio * height)
   *
   * @param ratio
   *          ratio to use when calculating the smart size of the label.
   */
  public SmartLabelFigure(double ratio) {
    super();
    this.ratio = ratio;

    textFlow = new TextFlow();
    textFlow.setLayoutManager(new ParagraphTextLayout(textFlow, ParagraphTextLayout.WORD_WRAP_HARD));
    add(textFlow);

  }

  public void setText(String text) {
    textFlow.setText(text);
  }

  public String getText() {
    return textFlow.getText();
  }

  public void setRatio(double ratio) {
    this.ratio = ratio;
  }

  public double getRatio() {
    return ratio;
  }

  /**
   * Calculate the best size for this label using the class's width to height ratio.
   * This is done by calculating the area that the text would occupy if it was in only
   * one line, then calculate a new width that would give the same area using the
   * width to height ratio, and finally it sends this width to the {@link FlowPage}'s
   * {@link FlowPage#getPreferredSize(int, int)} method which calculates the real
   * height using line breaks.
   *
   * @return A close match to the size that this figure should have to match
   *         the required width to height ratio.
   */
  public Dimension calculateSize() {
    Dimension lineDimensions = TextUtilities.INSTANCE.getStringExtents(textFlow.getText(), getFont());
    double area = lineDimensions.width() * lineDimensions.height();
    double width = Math.sqrt(area / ratio) * ratio;
    invalidate();
    return getPreferredSize((int) width, -1);
  }

  public void setHorizontalAlignment(HorizontalAlignment hAlignment) {
    setHorizontalAligment(JDraw2dToDraw2dTranslations.translateHorizontalAlignment(hAlignment));
  }
}

(Note: I’m using here some special classes that I am creating in order to port the draw2d project outsize of eclipse. I’ll write
more about this in a future post).

So there it is, now the programmer can’t resize the figures, not matter how hard he tries. And the size is calculated automatically.
Using a ratio of 2, the example above looks something like this:

Capture

And here is another example:

Capture5

The code for this class (and more goodies to come) can be found here and my new jdraw2d library (in progress) can be found here.

Update: the FlowPage stores a kind of “cache” of the last 3 saved layouts, so my implementation didn’t give the correct results sometime. Thankfully,
invalidate() clears this cache. The code was updated to mach this.

Enhanced by Zemanta

Programmers are Problem Solvers

This post was in my “drafts” for some time, and yesterday after the “final class” panel of the Reversim 2013 summit, I decided it was time to close it up.

It all started while trying to explain to my wife what I’m doing in my PhD research. Some background: my wife is a Chemical Engineer, works in quality and reliability of semiconductors, and is very, VERY smart and tough. She is the kind of person who always asks the difficult questions that make you think, and think, and think. So I sat there, trying to explain to her that I am creating a visual programming language based on the visual modeling methodology that my advisor created (the Object-Process Methodology, aka OPM).

“So why is this language better? Everyone programs in C/C++/Java, etc. Why a visual language?” she asked. And really, why do we need another programming language? what problems will this language solve? but before this, what problems do we have? I had to think this one out a bit. Why am I doing this (“Just for the fun” is not an acceptable answer, although it is one of the reasons)? This made me think… about what is a programmer.

So, what is a good programmer? A good programmer is someone who knows how to solve a problem. By problem I mean requirements/desires/expectations/bugs/needs/whatever. A programmer knows how to take a problem definition and analyze it. He divides it into small pieces and tries to find which of them are trivial and which are complex. He knows how to zoom into the parts of the system that need this zoom-in, but knows when to stay on the broad level when not needed (Thanks to Harel for this idea). And he knows his toolbox; He not only learns Scala/Python/List/Ruby “just for the fun”, but because this usually helps him find better solutions to the problems he has (or to future problems). He is not expected to carry all of his tools around (because it’s too much weight to carry), uses the common tools to solve the common problems, and searches, learns and uses special tools when the occasion arrives. He reads RSSs and listens to podcasts because they maintain him updated with new and developing technologies that may later help him solving problems faster/better. He does TDD not because it is the latest fad, but because he sees the value of TDD in his problem-solving process. And when he sees no value in TDD, he doesn’t do TDD. Same for pair programming, BDD, and other buzzwords. He knows enough about the business model of his company to prioritize between maintenance, new features, algorithm improvements, refactoring, and others (Thanks to Yaki for pointing this out). He uses continuous integration and deployment because he understands its value.

And he is pragmatic and passionate. Pragmatic because otherwise he would never stop researching new technologies and improving his solutions, because they can always be improved. And passionate because otherwise he wouldn’t invest his time in new technologies, new platforms, new paradigms, new tools.

This, IMHO, is not a good programmer. It is a GREAT programmer. It’s the kind of programmer I would love to work with and to become one day. And it is very hard to be one. It takes time, it takes patience, it takes energy. And that is why he needs passion.

So how it this connected tho why I’m developing a new programming language? Because I think in some cases (not all, but many of them) visual programming can help us create a better solution to our problems. This is what I think, and I don’t have any proof. But “just for the fun” is also part of the answer. But I can sleep with that.

And once again, thanks to Ran and all the people and companies who made the Reversim Summit 2013 possible.

Creating an OPM GEF Editor – Part 24: Showing Feedback to the User

Previous tutorial: Creating an OPM GEF Editor – Part 23: Drag & Drop from the Palette

Giving feedback to the user is very important in graphical editors which may have lots of information and we would like to minimize the amount of mistakes the user does. One example of feedback is coloring a figure when it is the target of an “add” operations – for example, color the Object to which a State is being added. The user can see very fast the change in the object and act upon the change (validate/cancel).

This is one of the places where the investment in GEF gives its fruits. Adding feedback seemed so simple that I thought is wouldn’t work. Feedback is handled by the EditPolicy in charge of creating new model elements, in my case, OPMContainerXYLayoutPolicy. Two functions were overriden to provide feedback: showLayoutTargetFeedback and eraseLayoutTargetFeedback. Both of these functions are called automatically by the GEF framework. To show how this works, I decided to paint the target figure a light blue color and to add a Donald Duck icon that moves with the cursor while you can apply the policy. This is the code that implements this functionality:

  @Override
  protected void showLayoutTargetFeedback(Request request) {
    if(getHostFigure() instanceof OPMThingFigure) {
      OPMThingFigure figure = (OPMThingFigure) getHostFigure();
      figure.setBackgroundColor(ColorConstants.lightBlue);
      figure.setOpaque(true);
      // Adding a new ellipse somewhere in the screen:
      if(!request.getExtendedData().containsKey("feedbackFigure")) {
        IFigure feedbackFigure =
            new ImageFigure(ImageDescriptor.createFromFile(this.getClass(), "../icons/dd.png").createImage());
        feedbackFigure.setLocation(((DropRequest) request).getLocation());
        feedbackFigure.setSize(feedbackFigure.getPreferredSize());
        addFeedback(feedbackFigure);
        request.getExtendedData().put("feedbackFigure", feedbackFigure);
      } else {
        IFigure feedbackFigure = (IFigure) request.getExtendedData().remove("feedbackFigure");
        feedbackFigure.setLocation(((DropRequest) request).getLocation());
        request.getExtendedData().put("feedbackFigure", feedbackFigure);
      }
    }
  }

  @Override
  protected void eraseLayoutTargetFeedback(Request request) {
    if(getHostFigure() instanceof OPMThingFigure) {
      OPMThingFigure figure = (OPMThingFigure) getHostFigure();
      figure.setBackgroundColor(ColorConstants.white);
      figure.setOpaque(false);
      IFigure feedbackFigure = (IFigure) request.getExtendedData().remove("feedbackFigure");
      removeFeedback(feedbackFigure);
    }
  }

And this is how it looks (thanks to Screencast-O-Matic):

feedbackvideo

Cool, Ah? As usual, the source code for this example (as part of the whole project) can be found here.

Next Tutorial: Creating an OPM GEF Editor – Part 25: “Smart” Multi-line Text Figure

Enhanced by Zemanta

GEF Edit Policy Reference Card

I have a bad memory. No matter what happens I forget things. To cope with this, I try to write down everything I learn for future reference. So after developing a couple of GEF editors I think I can write down a small table of GEF edit policies, what each policy does, where they can be installed and how they work. This table will be updated as I learn more things.

LayoutEditPolicy
Used to create and move elements inside a container. This policy can also be used to create feedback before the user performs the operation

SelectionEditPolicy
Used to give feedback or perform special actions when an EditPart is selected

ComponentEditPolicy
Used to delete a NodeEditPart (nodes in the editor)

ConnectionEditPolicy
Used to delete a ConnectionEditPart (links in the editor)

ContainerEditPart
Used to create elements inside a container.

DirectEditPolicy
Used to show “direct edit” of a property inside the GraphicalEditPart. This is the policy used to create an editable textbox inside a model element to edit one of the element’s properties – usually its name.

Enhanced by Zemanta

Temporary Solutions Usually Become Permanent

There’s a saying that temporary solutions usually become permanent. And there is no better place validate this than version control systems. I noticed this comment today in the Eclipse SWT framework:

	/*
	* TEMPORARY CODE. When a graphics object is
	* created and the device parameter is null,
	* the current Display is used. This presents
	* a problem because SWT graphics does not
	* reference classes in SWT widgets. The correct
	* fix is to remove this feature. Unfortunately,
	* too many application programs rely on this
	* feature.
	*/
	protected static Device CurrentDevice;
	protected static Runnable DeviceFinder;
	static {
		try {
			Class.forName ("org.eclipse.swt.widgets.Display"); //$NON-NLS-1$
		} catch (ClassNotFoundException e) {}
	}

/*
* TEMPORARY CODE.
*/
static synchronized Device getDevice () {
	if (DeviceFinder != null) DeviceFinder.run();
	Device device = CurrentDevice;
	CurrentDevice = null;
	return device;
}

I decided to check when this “temporary” code came to existence, and found it was added about 12 years ago (June 2001 to be exact). Since then, it hasn’t changed. And neither has the comment.

And on a related subject, I am not sure if I would have endeavored in this search if git and github didn’t exist. Since they were created, the world is definitely a better place.

Enhanced by Zemanta