/*
* Copyright 2017 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.tools;
import pixelitor.Composition;
import pixelitor.gui.ImageComponent;
import pixelitor.gui.ImageComponents;
import pixelitor.selection.Selection;
import pixelitor.selection.SelectionActions;
import pixelitor.selection.SelectionBuilder;
import pixelitor.selection.SelectionInteraction;
import pixelitor.selection.SelectionType;
import pixelitor.utils.ImageSwitchListener;
import pixelitor.utils.Messages;
import pixelitor.utils.debug.DebugNode;
import javax.swing.*;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
/**
* The selection tool
*/
public class SelectionTool extends Tool implements ImageSwitchListener {
public static final String HELP_TEXT = "Click and drag to select an area. Hold SPACE down to move the entire selection. Shift-drag adds to an existing selection, Alt-drag removes from it, Shift+Alt intersects.";
public static final String POLY_HELP_TEXT = "Polygonal selection: Click to add points, double-click (or right-click) to finish the selection.";
private JComboBox<SelectionType> typeCombo;
private JComboBox<SelectionInteraction> interactionCombo;
private boolean altMeansSubtract = false;
private SelectionInteraction originalSelectionInteraction;
private SelectionBuilder selectionBuilder;
private boolean polygonal = false;
SelectionTool() {
super('m', "Selection", "selection_tool_icon.png",
HELP_TEXT,
Cursor.getDefaultCursor(), false, true, false, ClipStrategy.INTERNAL_FRAME);
spaceDragBehavior = true;
ImageComponents.addImageSwitchListener(this);
}
@Override
public void initSettingsPanel() {
typeCombo = new JComboBox<>(SelectionType.values());
typeCombo.addActionListener(e -> {
stopBuildingSelection();
polygonal = typeCombo.getSelectedItem() == SelectionType.POLYGONAL_LASSO;
if (polygonal) {
Messages.showStatusMessage(POLY_HELP_TEXT);
} else {
Messages.showStatusMessage("Selection Tool: " + HELP_TEXT);
}
});
settingsPanel.addWithLabel("Type:", typeCombo, "selectionTypeCombo");
settingsPanel.addSeparator();
interactionCombo = new JComboBox<>(SelectionInteraction.values());
settingsPanel.addWithLabel("New Selection:", interactionCombo, "selectionInteractionCombo");
settingsPanel.addSeparator();
settingsPanel.addButton(SelectionActions.getTraceWithBrush());
settingsPanel.addButton(SelectionActions.getTraceWithEraser());
settingsPanel.addButton(SelectionActions.getCropAction());
}
@Override
public void mousePressed(MouseEvent e, ImageComponent ic) {
if (polygonal) {
return; // ignore mouse pressed
}
setupInteractionWithKeyModifiers(e);
SelectionType selectionType = (SelectionType) typeCombo.getSelectedItem();
SelectionInteraction selectionInteraction = (SelectionInteraction) interactionCombo.getSelectedItem();
Composition comp = ic.getComp();
selectionBuilder = new SelectionBuilder(selectionType, selectionInteraction, comp);
}
@Override
public void mouseDragged(MouseEvent e, ImageComponent ic) {
if (polygonal) {
return; // ignore mouseDragged
}
boolean altDown = e.isAltDown();
boolean startFromCenter = (!altMeansSubtract) && altDown;
if (!altDown) {
altMeansSubtract = false;
}
userDrag.setStartFromCenter(startFromCenter);
selectionBuilder.updateSelection(userDrag);
}
@Override
public void mouseReleased(MouseEvent e, ImageComponent ic) {
// TODO
if (userDrag.isClick() && !polygonal) { // will be handled by mouseClicked
return;
}
Composition comp = ic.getComp();
Selection selection = comp.getBuiltSelection();
if (selection == null && !polygonal) {
System.err.println("SelectionTool::mouseReleased: no built selection");
return;
}
if (polygonal) {
PMouseEvent pe = new PMouseEvent(e, ic);
if (selectionBuilder == null) {
setupInteractionWithKeyModifiers(e);
SelectionType selectionType = (SelectionType) typeCombo.getSelectedItem();
SelectionInteraction selectionInteraction = (SelectionInteraction) interactionCombo.getSelectedItem();
selectionBuilder = new SelectionBuilder(selectionType, selectionInteraction, comp);
selectionBuilder.updateSelection(pe);
restoreInteraction();
} else {
selectionBuilder.updateSelection(pe);
if (SwingUtilities.isRightMouseButton(e)) {
selectionBuilder.combineShapes();
stopBuildingSelection();
}
}
} else {
restoreInteraction();
boolean startFromCenter = (!altMeansSubtract) && e.isAltDown();
userDrag.setStartFromCenter(startFromCenter);
selectionBuilder.updateSelection(userDrag);
selectionBuilder.combineShapes();
stopBuildingSelection();
}
altMeansSubtract = false;
}
@Override
public boolean dispatchMouseClicked(MouseEvent e, ImageComponent ic) {
if (polygonal) {
if (selectionBuilder != null && e.getClickCount() > 1) {
// finish polygonal for double-click
PMouseEvent pe = new PMouseEvent(e, ic);
selectionBuilder.updateSelection(pe);
selectionBuilder.combineShapes();
stopBuildingSelection();
return false;
} else {
// ignore otherwise: will be handled in mouse released
return false;
}
}
super.dispatchMouseClicked(e, ic);
deselect(ic, true);
altMeansSubtract = false;
return false;
}
private static void deselect(ImageComponent ic, boolean addToHistory) {
Composition comp = ic.getComp();
if (comp.hasSelection()) {
comp.deselect(addToHistory);
}
}
@Override
public boolean arrowKeyPressed(ArrowKey key) {
ImageComponent ic = ImageComponents.getActiveIC();
if (ic != null) {
Composition comp = ic.getComp();
Selection selection = comp.getSelection();
if (selection != null) {
selection.nudge(key.getTransform());
return true;
}
}
return false;
}
private void setupInteractionWithKeyModifiers(MouseEvent e) {
boolean shiftDown = e.isShiftDown();
boolean altDown = e.isAltDown();
altMeansSubtract = altDown;
if (shiftDown || altDown) {
originalSelectionInteraction = (SelectionInteraction) interactionCombo.getSelectedItem();
if (shiftDown) {
if (altDown) {
interactionCombo.setSelectedItem(SelectionInteraction.INTERSECT);
} else {
interactionCombo.setSelectedItem(SelectionInteraction.ADD);
}
} else if (altDown) {
interactionCombo.setSelectedItem(SelectionInteraction.SUBTRACT);
}
}
}
private void restoreInteraction() {
if (originalSelectionInteraction != null) {
interactionCombo.setSelectedItem(originalSelectionInteraction);
originalSelectionInteraction = null;
}
}
@Override
protected void toolEnded() {
super.toolEnded();
stopBuildingSelection();
}
@Override
public void noOpenImageAnymore() {
// ignore
}
@Override
public void newImageOpened(Composition comp) {
stopBuildingSelection();
}
@Override
public void activeImageHasChanged(ImageComponent oldIC, ImageComponent newIC) {
stopBuildingSelection();
}
private void stopBuildingSelection() {
if (selectionBuilder != null) {
selectionBuilder.cancelIfNotFinished();
selectionBuilder = null;
}
}
@Override
public String getStateInfo() {
Object type = typeCombo.getSelectedItem();
Object interaction = interactionCombo.getSelectedItem();
return "type = " + type + ", interaction = " + interaction;
}
@Override
public DebugNode getDebugNode() {
DebugNode node = super.getDebugNode();
node.addStringChild("Type", typeCombo.getSelectedItem().toString());
node.addStringChild("Interaction", interactionCombo.getSelectedItem().toString());
return node;
}
}