FlexGanttFX Developer Manual : 4.7 LinesManager

Introduction

A lines manager is used to control the layout of lines inside a row. Activities located on different lines do not overlap each other, except if the lines themselves overlap each other. Each line can have its own height and a location within the row. Each line can also have its own Layout . By using lines and layouts it is possible to display activities that belong to the same row in different ways (see ChartLayout, AgendaLayout, GanttLayout).

Line Count

The actual number of lines on a row is stored on the lineCount property of the row. Simply call Row.setLineCount(int) to change its value. If the line count is larger than 0 the row will call back on its line manager to figure out where each line is located, how high it is, and which activity will be placed on which row. Also the type of layout to use for each line will be retrieved from the manager.

Interface

The following table describes the interface methods.

Method Description
double getLineHeight(int lineIndex, double rowHeight);
Returns the height of the line with the given index. The height can be computed on-the-fly based on the given available row height.
int getLineIndex(A activity);
Returns the line index for the given activity. This method places activities on different lines.
Layout getLineLayout(int lineIndex);
Returns the layout for the line with the given line index. Each line can have its own layout.
double getLineLocation(int lineIndex, double rowHeight);
Returns the location of the line with the given index. The location can be computed on-the-fly based on the given available row height.

Equal Lines Manager

The EqualLinesManager can be used to equally distribute line locations and line heights on a row. Each line will have the same height and the lines will not overlap each other. While this behaviour will be provided by the manager it is still the responsibility of the application to place the activities on different rows and to specify the layout for each line. This is also the reason why the methods getLineHeight() and getLineLocation() are final while the methods getLineLayout() and getLineIndex() are not and can be overriden. 

Auto Lines Manager

The AutoLinesManager can be used to create a dynamic number of lines based on all activities inside a repository. This lines manager detects clusters of intersecting activities (start / end time intervals) and ensures that enough lines are available to place the activities in a non-overlapping way. Below you are finding the complete source code of this manager class as a case study. Please note that the manager's layout() method needs to be invoked from the outside. A good way to do this is to listen to ACTIVITY_CHANGE_FINISHED events or even more fine grained START/END_TIME_CHANGE_FINISHED events.

AutoLinesManager
/**
 * Copyright (C) 2014 Dirk Lemmermann Software & Consulting (dlsc.com)
 *
 * This file is part of FlexGanttFX.
 */
package com.flexganttfx.view.util;
import static java.util.Objects.requireNonNull;
import impl.com.flexganttfx.skin.util.Placement;
import impl.com.flexganttfx.skin.util.Resolver;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.flexganttfx.model.Activity;
import com.flexganttfx.model.ActivityRepository;
import com.flexganttfx.model.Layer;
import com.flexganttfx.model.LinesManager;
import com.flexganttfx.model.Row;
import com.flexganttfx.model.layout.EqualLinesManager;
import com.flexganttfx.view.graphics.GraphicsBase;
/**
 * A specialized {@link LinesManager} used for ensuring that activities will not
 * overlap each other. This manager will create as many inner lines as needed
 * and will calculate the placement of all activities on these lines.
 *
 * @param <R>
 *            the type of the row that will be managed
 * @param <A>
 *            the type of the activities that will be managed
 *
 * @since 1.2
 */
public class AutoLinesManager<R extends Row<?, ?, A>, A extends Activity>
		extends EqualLinesManager<R, A> {
	private Map<A, Placement<A>> placements;
	private GraphicsBase<R> graphics;
	/**
	 * Constructs a new automatic lines manager. The constructor requires a
	 * reference to the graphics view to lookup various parameters that are
	 * needed when the manager queries the activity repository of the row (e.g.
	 * the currently displayed temporal unit and the list of layers).
	 *
	 * @param row
	 *            the managed row
	 * @param graphics
	 *            the graphics view where the manager will be used
	 *
	 * @since 1.2
	 */
	public AutoLinesManager(R row, GraphicsBase<R> graphics) {
		super(row);
		this.graphics = requireNonNull(graphics);
		layout();
	}
	/**
	 * Returns the graphics view where the manager will be used.
	 *
	 * @return the graphics view
	 * @since 1.2
	 */
	public final GraphicsBase<R> getGraphics() {
		return graphics;
	}
	public final void layout() {
		R row = getRow();
		ActivityRepository<A> repository = row.getRepository();
		Instant st = repository.getEarliestTimeUsed();
		Instant et = repository.getLatestTimeUsed();
		if (st == null || et == null) {
			return;
		}
		List<A> allActivities = new ArrayList<>();
		for (Layer layer : graphics.getLayers()) {
			Iterator<A> activities = repository.getActivities(layer, st, et,
					graphics.getTimeline().getDateline()
							.getPrimaryTemporalUnit(), row.getZoneId());
			if (activities != null) {
				activities.forEachRemaining(activity -> allActivities
						.add(activity));
			}
		}
		placements = Resolver.resolve(allActivities);
		if (placements != null && !placements.isEmpty()) {
			Placement<A> p = placements.values().iterator().next();
			row.setLineCount(p.getColumnCount());
		} else {
			row.setLineCount(0);
		}
	}
	@Override
	public int getLineIndex(A activity) {
		if (placements != null) {
			Placement<A> placement = placements.get(activity);
			if (placement != null) {
				return placement.getColumnIndex();
			}
		}
		return -1;
	}
}