/*
* This file is part of the Illarion project.
*
* Copyright © 2015 - Illarion e.V.
*
* Illarion is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Illarion 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.
*/
package illarion.mapedit.gui;
import illarion.mapedit.Lang;
import illarion.mapedit.MapEditor;
import illarion.mapedit.data.*;
import illarion.mapedit.events.*;
import illarion.mapedit.events.map.MapPositionEvent;
import illarion.mapedit.events.map.RepaintRequestEvent;
import illarion.mapedit.events.menu.*;
import illarion.mapedit.history.HistoryManager;
import illarion.mapedit.history.ItemPlacedAction;
import illarion.mapedit.render.RendererManager;
import illarion.mapedit.util.SwingLocation;
import javolution.util.FastTable;
import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.annotation.AnnotationProcessor;
import org.bushe.swing.event.annotation.EventSubscriber;
import org.bushe.swing.event.annotation.EventTopicSubscriber;
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.List;
/**
* This class should contain the model, and control the view, nicely separated from each other.
*
* @author Tim
*/
public class GuiController extends WindowAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(GuiController.class);
@Nonnull
private final MainFrame mainFrame;
@Nonnull
private final List<Map> maps;
@Nonnull
private final HistoryManager historyManager;
@Nonnull
private final AnnotationChecker annotationChecker;
@Nonnull
private final HelpDialog helpDialog;
@Nullable
private Map selected;
private boolean started;
private boolean notSaved;
@Nullable
private MapSelection clipboard;
public GuiController() {
AnnotationProcessor.process(this);
mainFrame = new MainFrame(this);
historyManager = new HistoryManager();
annotationChecker = new AnnotationChecker();
maps = new FastTable<>();
notSaved = false;
helpDialog = new HelpDialog(mainFrame);
}
@Nonnull
public AnnotationChecker getAnnotationChecker() {
return annotationChecker;
}
public void start() {
if (started) {
throw new IllegalStateException("The Controller can't be started twice");
}
started = true;
startGui();
}
@Nonnull
public List<Map> getMaps() {
return maps;
}
public boolean isMapLoaded() {
return !maps.isEmpty();
}
/**
* This method starts up the gui.
*/
private void startGui() {
SwingUtilities.invokeLater(() -> {
SubstanceLookAndFeel.setSkin(MapEditorConfig.getInstance().getLookAndFeel());
mainFrame.initialize(this);
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
SplashScreen.getInstance().setVisible(false);
});
}
public void initialize() {
}
@Nullable
public Map getSelected() {
return selected;
}
public void addMap(@Nullable Map map) {
if (!maps.contains(map) && (map != null)) {
maps.add(map);
if (maps.size() == 1) {
selected = map;
}
}
EventBus.publish(new UpdateMapListEvent(maps, maps.size() - 1));
EventBus.publish(new RepaintRequestEvent());
}
private void removeMap(int index) {
if (index < maps.size()) {
Map map = maps.remove(index);
setSelectedMap(map);
}
}
private void setSelectedMap(Map map) {
if (selected == map) {
if (maps.isEmpty()) {
selected = null;
} else {
selected = maps.get(0);
}
}
EventBus.publish(new UpdateMapListEvent(maps, maps.indexOf(selected)));
EventBus.publish(new RepaintRequestEvent());
}
public void removeMap(@Nullable Map map) {
if (maps.contains(map) && (map != null)) {
maps.remove(map);
setSelectedMap(map);
}
}
@Nonnull
public HistoryManager getHistoryManager() {
return historyManager;
}
public void setSaved(boolean saved) {
notSaved = !saved;
}
@Override
public void windowClosing(WindowEvent e) {
if (notSaved) {
if (MapDialogs.isShowSaveDialog()) {
onMapSave(new MapSaveEvent());
}
}
LOGGER.debug("Closing window.");
MapEditor.exit();
}
@Override
public void windowClosed(WindowEvent e) {
LOGGER.debug("Exit");
System.exit(0);
}
@EventSubscriber
public void onMapNew(MapNewEvent e) {
addMap(MapDialogs.showNewMapDialog(mainFrame));
}
@EventSubscriber
public void onMapSave(MapSaveEvent e) {
for (Map map : maps) {
try {
if (map != null) {
MapIO.saveMap(map);
}
} catch (IOException ex) {
LOGGER.warn("Can't save map", ex);
JOptionPane.showMessageDialog(MainFrame.getInstance(), Lang.getMsg("gui.error.SaveMap"),
Lang.getMsg("gui.error"), JOptionPane.ERROR_MESSAGE);
}
}
notSaved = false;
EventBus.publish(new UpdateMapListEvent(maps, maps.indexOf(selected)));
}
@EventSubscriber
public void onMapSelected(@Nonnull MapSelectedEvent e) {
if ((e.getIndex() < 0) || (e.getIndex() >= maps.size())) {
selected = null;
} else {
selected = maps.get(e.getIndex());
int x = SwingLocation.displayCoordinateX(selected.getX(), selected.getY(), 0);
int y = SwingLocation.displayCoordinateY(selected.getX(), selected.getY(), 0);
RendererManager manager = mainFrame.getRendererManager();
manager.setZoom(1f);
manager.setSelectedLevel(selected.getZ());
manager.setTranslationX(-x);
manager.setTranslationY(-y);
manager.setDefaultTranslationX(-x);
manager.setDefaultTranslationY(-y);
}
EventBus.publish(new RepaintRequestEvent());
}
@EventSubscriber
public void onMapLoaded(@Nonnull MapLoadedEvent e) {
if (!maps.contains(e.getMap())) {
addMap(e.getMap());
}
}
@EventSubscriber
public void onMapOpen(@Nonnull MapOpenEvent e) {
MapIO.loadMap(e.getPath(), e.getName());
}
@EventTopicSubscriber(topic = GlobalActionEvents.CLOSE_MAP)
public void onMapClosed(String topic, ActionEvent event) {
removeMap(selected);
}
@EventSubscriber
public void onMapCloseAtIndex(@Nonnull CloseMapEvent e) {
removeMap(e.getIndex());
}
@EventSubscriber
public void onCopyClipboard(@Nonnull ClipboardCopyEvent e) {
if (getSelected() != null) {
clipboard = getSelected().copySelectedTiles();
}
}
@EventSubscriber
public void onCutClipboard(@Nonnull ClipboardCutEvent e) {
if ((getSelected() != null) && !annotationChecker.isAnnotatedFill(getSelected())) {
clipboard = getSelected().cutSelectedTiles();
EventBus.publish(new RepaintRequestEvent());
setSaved(false);
}
}
@EventSubscriber
public void onPasteClipboard(@Nonnull PasteEvent e) {
EventBus.publish(new DidPasteEvent());
if ((getSelected() != null) && (clipboard != null) &&
!annotationChecker.isAnnotated(e.getX(), e.getY(), getSelected(), clipboard)) {
getSelected().pasteTiles(e.getX(), e.getY(), clipboard);
EventBus.publish(new RepaintRequestEvent());
setSaved(false);
}
}
@EventSubscriber
public void onItemRemove(@Nonnull ItemRemoveEvent e) {
if (getSelected() != null) {
ItemPlacedAction historyAction = getSelected().removeItemOnActiveTile(e.getIndex());
if (historyAction != null) {
historyManager.addEntry(historyAction);
setSaved(false);
}
EventBus.publish(new RepaintRequestEvent());
EventBus.publish(new ItemsUpdatedEvent(getSelected().getItemsOnActiveTile()));
}
}
@EventSubscriber
public void onItemReplace(@Nonnull ItemReplaceEvent e) {
if (getSelected() != null) {
getSelected().replaceItemOnActiveTile(e.getIndex(), e.getNewIndex());
EventBus.publish(new RepaintRequestEvent());
EventBus.publish(new ItemsUpdatedEvent(getSelected().getItemsOnActiveTile()));
}
}
@EventSubscriber
public void onMapPosition(@Nonnull MapPositionEvent e) {
if (getSelected() != null) {
getSelected().setMapPosition(e.getMapX(), e.getMapY());
EventBus.publish(new RepaintRequestEvent());
}
}
@EventSubscriber
public void onItemDataAnnotation(@Nonnull TileAnnotationEvent e) {
if (getSelected() == null) {
return;
}
MapTile tile = getSelected().getActiveTile();
if (tile != null) {
tile.setAnnotation(e.getText());
EventBus.publish(new RepaintRequestEvent());
}
}
@EventSubscriber
public void onShowHelpDialog(ShowHelpDialogEvent e) {
helpDialog.setVisible(true);
}
}