/*
* Copyright 2016 Laszlo Balazs-Csiki
*
* This file is part of Pixelitor. Pixelitor is free software: you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License, version 3 as published by the Free
* Software Foundation.
*
* Pixelitor is 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 Pixelitor. If not, see <http://www.gnu.org/licenses/>.
*/
package pixelitor.layers;
import pixelitor.Composition;
import pixelitor.gui.ImageComponents;
import javax.swing.*;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* The GUI container for LayerButton objects.
* Each ImageComponent has its own LayersPanel instance.
*/
public class LayersPanel extends JLayeredPane {
private final List<LayerButton> layerButtons = new ArrayList<>();
private final ButtonGroup buttonGroup = new ButtonGroup();
private final DragReorderHandler dragReorderHandler;
private LayerButton draggedButton = null;
public LayersPanel() {
dragReorderHandler = new DragReorderHandler(this);
}
public void addLayerButton(LayerButton button, int newLayerIndex) {
buttonGroup.add(Objects.requireNonNull(button));
layerButtons.add(newLayerIndex, button);
add(button, JLayeredPane.DEFAULT_LAYER);
// the new layer always becomes the selected layer
button.setUserInteraction(false); // this selection should not go into history
button.setSelected(true);
button.setUserInteraction(true);
revalidate();
repaint();
button.addDragReorderHandler(dragReorderHandler);
}
public void deleteLayerButton(LayerButton button) {
buttonGroup.remove(button);
layerButtons.remove(button);
remove(button);
revalidate();
repaint();
button.removeDragReorderHandler(dragReorderHandler);
}
public void changeLayerOrderInTheGUI(int oldIndex, int newIndex) {
LayerButton button = layerButtons.remove(oldIndex);
layerButtons.add(newIndex, button);
revalidate();
}
/**
* @param firstDragUpdate true if called for the first time during this drag
*/
public void updateDrag(LayerButton newDraggedButton, int dragY, boolean firstDragUpdate) {
assert newDraggedButton != null;
if (firstDragUpdate) {
// put it into the drag layer so that it is always visible
// (removing and adding works on Java 7, but not on Java 8, setLayer is fine on both)
setLayer(newDraggedButton, JLayeredPane.DRAG_LAYER);
this.draggedButton = newDraggedButton;
}
swapIfNecessary(dragY);
}
/**
* Override doLayout() so that when the whole window is resized, the
* layer buttons are still laid out correctly
*/
@Override
public void doLayout() {
int parentHeight = getHeight();
// assumes that all buttons have the same height
int buttonHeight = layerButtons.get(0).getPreferredSize().height;
int numButtons = layerButtons.size();
for (int i = 0; i < numButtons; i++) {
LayerButton button = layerButtons.get(i);
int y = parentHeight - (i + 1) * buttonHeight;
if (button != draggedButton) {
button.setSize(getWidth(), buttonHeight);
button.setLocation(0, y);
}
button.setStaticY(y);
}
}
/**
* Change the order of buttons while dragging
*/
private void swapIfNecessary(int dragY) {
int staticY = draggedButton.getStaticY();
int deltaY = dragY - staticY;
int buttonHeight = draggedButton.getPreferredSize().height; // all buttons have the same height
int halfHeight = buttonHeight / 2;
if (deltaY > 0) { // dragging downwards
if (deltaY < halfHeight) {
return;
} else {
int draggedIndex = layerButtons.indexOf(draggedButton);
if (draggedIndex > 0) {
int indexBellow = draggedIndex - 1;
Collections.swap(layerButtons, indexBellow, draggedIndex);
}
}
} else { // dragging upwards
if (deltaY > -halfHeight) {
return;
} else {
int draggedIndex = layerButtons.indexOf(draggedButton);
if (draggedIndex < layerButtons.size() - 1) {
int indexAbove = draggedIndex + 1;
Collections.swap(layerButtons, indexAbove, draggedIndex);
}
}
}
}
// drag finished, put the last dragged back to the default JLayeredPane layer
public void dragFinished() {
if (draggedButton != null) {
setLayer(draggedButton, JLayeredPane.DEFAULT_LAYER);
draggedButton.dragFinished(layerButtons.indexOf(draggedButton)); // notify the composition
} else {
throw new IllegalStateException();
}
draggedButton = null;
doLayout();
// notify the raise/lower layer menu items
Composition comp = ImageComponents.getActiveCompOrNull();
LayerMoveAction.INSTANCE_UP.enableDisable(comp);
LayerMoveAction.INSTANCE_DOWN.enableDisable(comp);
}
@Override
public Dimension getPreferredSize() {
// assumes that all buttons have the same height
int buttonHeight = layerButtons.get(0).getPreferredSize().height;
int numButtons = layerButtons.size();
int allButtonsHeight = numButtons * buttonHeight;
return new Dimension(10, allButtonsHeight);
}
}