GanttDiagramMainBlock.java

/* ========================================================================
 * PlantUML : a free UML diagram generator
 * ========================================================================
 *
 * (C) Copyright 2009-2024, Arnaud Roques
 *
 * Project Info:  https://plantuml.com
 *
 * If you like this project or if you find it useful, you can support us at:
 *
 * https://plantuml.com/patreon (only 1$ per month!)
 * https://plantuml.com/paypal
 *
 * This file is part of PlantUML.
 *
 * PlantUML is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PlantUML distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 *
 *
 * Original Author:  Arnaud Roques
 *
 *
 */
package net.sourceforge.plantuml.project;

import net.sourceforge.plantuml.crash.CrashImage;
import net.sourceforge.plantuml.crash.ReportLog;
import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.color.HColor;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.shape.AbstractTextBlock;
import net.sourceforge.plantuml.klimt.shape.URectangle;
import net.sourceforge.plantuml.log.Logme;
import net.sourceforge.plantuml.project.core.Resource;
import net.sourceforge.plantuml.project.core.Task;
import net.sourceforge.plantuml.project.data.DisplayConfigData;
import net.sourceforge.plantuml.project.data.GanttModelData;
import net.sourceforge.plantuml.project.data.TaskDrawRegistryData;
import net.sourceforge.plantuml.project.data.TimeBoundsData;
import net.sourceforge.plantuml.project.data.TimelineStyleData;
import net.sourceforge.plantuml.project.draw.ResourceDraw;
import net.sourceforge.plantuml.project.draw.TaskDraw;
import net.sourceforge.plantuml.project.draw.header.TimeHeader;
import net.sourceforge.plantuml.project.time.TimePoint;
import net.sourceforge.plantuml.project.timescale.TimeScale;
import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleSignatureBasic;

public class GanttDiagramMainBlock extends AbstractTextBlock {

	private final GanttDiagram diagram;
	private final TimeHeader timeHeader;
	private final GanttLayout layout;
	private final GanttModelData modelData;
	private final TimeBoundsData timeBounds;

	private final DisplayConfigData displayConfig;
	private final TimelineStyleData timelineStyle;
	private final TaskDrawRegistryData drawRegistry;

	public GanttDiagramMainBlock(TimeBoundsData timeBounds, GanttModelData modelData, TaskDrawRegistryData drawRegistry,
			DisplayConfigData displayConfig, TimelineStyleData timelineStyle, GanttDiagram diagram,
			TimeHeader timeHeader, StringBounder stringBounder) {
		this.diagram = diagram;
		this.timeBounds = timeBounds;
		this.modelData = modelData;
		this.drawRegistry = drawRegistry;
		this.displayConfig = displayConfig;
		this.timelineStyle = timelineStyle;
		this.timeHeader = timeHeader;
		this.layout = new GanttLayout(modelData, displayConfig, timeBounds, timelineStyle, drawRegistry, stringBounder,
				timeHeader);
	}

	@Override
	public void drawU(UGraphic ug) {
		try {
			final UGraphic ugOrig = ug;

			if (displayConfig.getLabelStrategy().titleInFirstColumn())
				ug = ug.apply(UTranslate.dx(layout.getTitlesWidth()));

			final Style style = StyleSignatureBasic.of(SName.root, SName.element, SName.ganttDiagram, SName.timeline)
					.getMergedStyle(timelineStyle.getSkinParam().getCurrentStyleBuilder());

			final HColor back = style.value(PName.BackGroundColor).asColor(timelineStyle.getColorSet());
			if (back.isTransparent() == false) {

				final double fullWidth = layout.getTitlesWidth() + layout.getBarsWidth();

				final URectangle rect1 = URectangle.build(fullWidth, layout.getHeaderHeight());
				ug.apply(back.bg()).draw(rect1);
				if (displayConfig.isShowFootbox()) {
					final URectangle rect2 = URectangle.build(fullWidth, layout.getFooterHeight());
					ug.apply(back.bg()).apply(UTranslate.dy(drawRegistry.getTotalHeightWithoutFooter())).draw(rect2);
				}
			}

			timeHeader.drawTimeHeader(ug, drawRegistry.getTotalHeightWithoutFooter());

			drawConstraints(ug, timeHeader.getTimeScale());
			drawTasksRect(ug);
			drawTasksTitle(ugOrig, layout.getTitlesWidth(), layout.getBarsWidth());

			if (displayConfig.isHideResourceFootbox() == false)
				drawResources(ug);

			if (displayConfig.isShowFootbox())
				timeHeader.drawTimeFooter(ug.apply(UTranslate.dy(drawRegistry.getTotalHeightWithoutFooter())));

		} catch (Throwable e) {
			Logme.error(e);

			final ReportLog report = new ReportLog();
			report.anErrorHasOccured(e, diagram.getFlashData());

			report.addProperties();
			report.addEmptyLine();
			report.youShouldSendThisDiagram();

			final CrashImage image = new CrashImage(e, diagram.getFlashData(), report);
			image.drawU(ug);

		}

	}

	public XDimension2D calculateDimension(StringBounder stringBounder) {
		return layout.calculateDimension();
	}

	private void drawTasksRect(UGraphic ug) {
		for (Task task : modelData.getTasks()) {
			if (timeBounds.isHidden(task))
				continue;

			final TaskDraw draw = drawRegistry.getTaskDraw(task);
			final UTranslate move = UTranslate.dy(draw.getY(ug.getStringBounder()).getCurrentValue());
			draw.drawU(ug.apply(move));
		}
	}

	private void drawConstraints(final UGraphic ug, TimeScale timeScale) {
		for (GanttConstraint constraint : modelData.getConstraints()) {
			if (timeBounds.getPrintStart() != null
					&& constraint.isHidden(TimePoint.ofStartOfDay(timeBounds.getMinDay()),
							TimePoint.ofEndOfDayMinusOneSecond(timeBounds.getMaxDay())))
				continue;

			constraint.getUDrawable(timeScale, drawRegistry).drawU(ug);
		}

	}

	private void drawTasksTitle(UGraphic ug, double colTitles, double colBars) {
		for (Task task : modelData.getTasks()) {
			if (timeBounds.isHidden(task))
				continue;

			final TaskDraw draw = drawRegistry.getTaskDraw(task);
			final UTranslate move = UTranslate.dy(draw.getY(ug.getStringBounder()).getCurrentValue());
			draw.drawTitle(ug.apply(move), displayConfig.getLabelStrategy(), colTitles, colBars);
		}
	}

	private void drawResources(UGraphic ug) {
		for (Resource res : modelData.getResources()) {
			final ResourceDraw draw = res.getResourceDraw();
			final UTranslate move = UTranslate.dy(draw.getY());
			draw.drawU(ug.apply(move));
		}
	}

}