/*
* Copyright 2016 Igor Maznitsa.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.igormaznitsa.mindmap.swing.panel.ui;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.igormaznitsa.mindmap.model.Topic;
import com.igormaznitsa.mindmap.plugins.MindMapPluginRegistry;
import com.igormaznitsa.mindmap.plugins.api.AttributePlugin;
import com.igormaznitsa.mindmap.plugins.api.VisualAttributePlugin;
import com.igormaznitsa.mindmap.swing.panel.MindMapPanelConfig;
import com.igormaznitsa.mindmap.swing.panel.ui.gfx.MMGraphics;
import com.igormaznitsa.mindmap.plugins.api.Renderable;
import com.igormaznitsa.mindmap.plugins.api.CodeSnippetProcessor;
public class VisualAttributeImageBlock {
private final Rectangle2D bounds = new Rectangle2D.Double();
private final Topic model;
private boolean contentPresented;
private VisualItem[] items = null;
private static final class VisualItem {
private static final VisualItem[] EMPTY = new VisualItem[0];
private final VisualAttributeImageBlock parent;
private Renderable image;
private final VisualAttributePlugin plugin;
private final int relx;
private int rely;
private int width;
private int height;
private double lastScale = -1.0d;
VisualItem(@Nonnull VisualAttributeImageBlock parent, @Nonnull final VisualItem item) {
this.parent = parent;
this.plugin = item.plugin;
this.relx = item.relx;
this.rely = item.rely;
this.width = item.width;
this.height = item.height;
this.lastScale = item.lastScale;
this.image = item.image;
}
VisualItem(@Nonnull final VisualAttributeImageBlock parent, @Nonnull final MindMapPanelConfig cfg, final int x, final int y, @Nonnull final VisualAttributePlugin plugin) {
this.parent = parent;
this.relx = x;
this.rely = y;
this.plugin = plugin;
updateImage(cfg);
}
@Nonnull
VisualAttributePlugin getPlugin() {
return this.plugin;
}
void toHCenter(final int maxHeight) {
this.rely = (maxHeight - this.height) / 2;
}
int getWidth() {
return this.width;
}
int getHeight() {
return this.height;
}
void updateImage(@Nonnull final MindMapPanelConfig config) {
final double scale = config.getScale();
if (this.image == null || Double.compare(this.lastScale, scale) != 0) {
this.image = this.plugin.getScaledImage(config, this.parent.model);
this.lastScale = scale;
if (this.image == null) {
this.width = 0;
this.height = 0;
} else {
this.width = image.getWidth(scale);
this.height = image.getHeight(scale);
}
}
}
boolean isVisible() {
return this.image != null;
}
boolean containsPoint(final int relativeX, final int relativeY) {
return relativeX >= this.relx && relativeY >= this.rely && relativeX < this.relx + this.width && relativeY < this.rely + this.height;
}
void draw(@Nonnull final MMGraphics gfx, @Nonnull final MindMapPanelConfig cfg, final int basex, final int basey) {
if (this.isVisible()) {
this.image.renderAt(gfx, cfg, basex + this.relx, basey + this.rely);
}
}
}
public VisualAttributeImageBlock(@Nonnull final VisualAttributeImageBlock orig) {
this.bounds.setRect(orig.bounds);
this.model = orig.model;
this.contentPresented = orig.contentPresented;
if (orig.items == null){
this.items = null;
} else {
this.items = new VisualItem[orig.items.length];
for(int i=0;i<orig.items.length;i++){
this.items[i] = new VisualItem(this, orig.items[i]);
}
}
}
public VisualAttributeImageBlock(@Nonnull final Topic model) {
this.model = model;
}
public void setCoordOffset(final double x, final double y) {
this.bounds.setRect(x, y, this.bounds.getWidth(), this.bounds.getHeight());
}
public void updateSize(@Nonnull final MMGraphics gfx, @Nonnull final MindMapPanelConfig cfg) {
final List<VisualAttributePlugin> pluginsFromRegistry = MindMapPluginRegistry.getInstance().findFor(VisualAttributePlugin.class);
int x = 0;
int maxheight = 0;
if (pluginsFromRegistry.isEmpty()) {
this.items = VisualItem.EMPTY;
} else {
final List<VisualAttributePlugin> detectedPlugins = new ArrayList<VisualAttributePlugin>();
final Map<String, String> attributes = this.model.getAttributes();
final Map<String, String> codeSnippets = this.model.getCodeSnippets();
for (final VisualAttributePlugin p : pluginsFromRegistry) {
final String attributeKey = p.getAttributeKey();
if (!AttributePlugin.NULL_ATTRIBUTE.equals(attributeKey) && attributes.containsKey(attributeKey)) {
detectedPlugins.add(p);
} else if (p instanceof CodeSnippetProcessor && this.model.doesContainCodeSnippetForAnyLanguage(((CodeSnippetProcessor)p).getProcessingLanguageNames())){
detectedPlugins.add(p);
}
}
int index = 0;
this.items = this.items == null || this.items.length != detectedPlugins.size() ? new VisualItem[detectedPlugins.size()] : this.items;
for (final VisualAttributePlugin plugin : detectedPlugins) {
VisualItem item = this.items[index];
if (item == null || item.getPlugin() == plugin) {
item = new VisualItem(this, cfg, x, 0, plugin);
this.items[index] = item;
} else {
item.updateImage(cfg);
}
x += item.getWidth();
maxheight = Math.max(maxheight, item.getHeight());
index++;
}
for (final VisualItem i : this.items) {
i.toHCenter(maxheight);
}
}
this.bounds.setRect(0d, 0d, x, maxheight);
}
public boolean mayHaveContent() {
return this.items == null || this.items.length > 0;
}
public void paint(@Nonnull final MMGraphics gfx, @Nonnull final MindMapPanelConfig cfg) {
if (this.items == null) {
updateSize(gfx, cfg);
}
int offsetX = (int) Math.round(this.bounds.getX());
final int offsetY = (int) Math.round(this.bounds.getY());
for (final VisualItem i : this.items) {
if (i.isVisible()) {
i.draw(gfx, cfg, offsetX, offsetY);
}
}
}
@Nullable
public VisualAttributePlugin findPluginForPoint(final double x, final double y) {
VisualAttributePlugin result = null;
if (this.items != null && this.bounds.contains(x, y)) {
final int px = (int) Math.round(x - this.bounds.getX());
final int py = (int) Math.round(y - this.bounds.getY());
for (final VisualItem i : this.items) {
if (i.isVisible() && i.containsPoint(px, py)) {
result = i.getPlugin();
break;
}
}
}
return result;
}
@Nonnull
public Rectangle2D getBounds() {
return this.bounds;
}
}