/*******************************************************************************
* Copyright 2014 Rafael Garcia Moreno.
*
* 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.bladecoder.engineeditor.ui;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.HorizontalGroup;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ScreenUtils;
import com.bladecoder.engine.model.Scene;
import com.bladecoder.engine.model.World;
import com.bladecoder.engineeditor.Ctx;
import com.bladecoder.engineeditor.common.EditorLogger;
import com.bladecoder.engineeditor.common.ElementUtils;
import com.bladecoder.engineeditor.common.Message;
import com.bladecoder.engineeditor.model.Project;
import com.bladecoder.engineeditor.ui.panels.CellRenderer;
import com.bladecoder.engineeditor.ui.panels.EditModelDialog;
import com.bladecoder.engineeditor.ui.panels.ModelList;
import com.bladecoder.engineeditor.undo.UndoDeleteScene;
import com.bladecoder.engineeditor.undo.UndoOp;
public class SceneList extends ModelList<World, Scene> {
private ImageButton initBtn;
private SelectBox<String> chapters;
private HashMap<String, TextureRegion> bgIconCache = new HashMap<String, TextureRegion>();
private boolean disposeBgCache = false;
public SceneList(Skin skin) {
super(skin, true);
HorizontalGroup chapterPanel = new HorizontalGroup();
chapters = new SelectBox<String>(skin);
chapters.setFillParent(true);
chapterPanel.addActor(new Label("CHAPTER ", skin, "big"));
chapterPanel.addActor(chapters);
clearChildren();
add(chapterPanel).expandX().fillX();
row();
add(toolbar).expandX().fillX();
row().fill();
add(container).expandY().fill();
initBtn = new ImageButton(skin);
toolbar.addToolBarButton(initBtn, "ic_check", "Set init scene", "Set init scene");
initBtn.setDisabled(true);
list.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
int pos = list.getSelectedIndex();
if (pos == -1) {
Ctx.project.setSelectedScene(null);
} else {
Scene s = list.getItems().get(pos);
Ctx.project.setSelectedScene(s);
}
toolbar.disableEdit(pos == -1);
initBtn.setDisabled(pos == -1);
}
});
list.setCellRenderer(listCellRenderer);
initBtn.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setDefault();
}
});
chapters.addListener(chapterListener);
chapters.getSelection().setProgrammaticChangeEvents(false);
Ctx.project.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
EditorLogger.debug(evt.getPropertyName() + " NEW:" + evt.getNewValue() + " OLD:" + evt.getOldValue());
if (evt.getPropertyName().equals(Project.CHAPTER_PROPERTY)) {
addChapters();
} else if (evt.getPropertyName().equals(Project.NOTIFY_ELEMENT_DELETED)) {
if (evt.getNewValue() instanceof World) {
addChapters();
} else if (evt.getNewValue() instanceof Scene) {
addElements(World.getInstance(),
Arrays.asList(World.getInstance().getScenes().values().toArray(new Scene[0])));
}
} else if (evt.getPropertyName().equals(Project.NOTIFY_PROJECT_LOADED)) {
toolbar.disableCreate(Ctx.project.getProjectDir() == null);
disposeBgCache = true;
addChapters();
} else if (evt.getPropertyName().equals(Project.NOTIFY_ELEMENT_CREATED)) {
if (evt.getNewValue() instanceof Scene && !(evt.getSource() instanceof EditSceneDialog)) {
addElements(World.getInstance(),
Arrays.asList(World.getInstance().getScenes().values().toArray(new Scene[0])));
}
}
}
});
}
ChangeListener chapterListener = new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
String selChapter = (String) chapters.getSelected();
if (selChapter != null && !selChapter.equals(Ctx.project.getChapter().getId())) {
// Save the project when changing chapter
try {
Ctx.project.saveProject();
} catch (IOException e1) {
Message.showMsgDialog(getStage(), "Error saving project", e1.getMessage());
EditorLogger.error(e1.getMessage());
}
try {
if (selChapter != null)
Ctx.project.loadChapter(selChapter);
String init = Ctx.project.getEditorConfig().getProperty("project.selectedScene",
World.getInstance().getInitScene());
addElements(World.getInstance(),
Arrays.asList(World.getInstance().getScenes().values().toArray(new Scene[0])));
if (init != null) {
Scene s = World.getInstance().getScenes().get(init);
if(s == null && World.getInstance().getInitScene() != null)
s = World.getInstance().getScenes().get(World.getInstance().getInitScene());
if (s != null) {
int indexOf = list.getItems().indexOf(s, true);
list.setSelectedIndex(indexOf);
}
}
} catch (IOException e1) {
EditorLogger.printStackTrace(e1);
}
}
}
};
public void addChapters() {
String[] nl = Ctx.project.getChapter().getChapters();
Array<String> array = new Array<String>();
for (int i = 0; i < nl.length; i++) {
array.add(nl[i]);
}
chapters.setItems(array);
String init = Ctx.project.getEditorConfig().getProperty("project.selectedChapter",
World.getInstance().getInitChapter());
if (init != null) {
if(array.contains(init, false)) {
chapters.setSelected(init);
} else if(array.size > 0){
chapters.setSelected(Ctx.project.getChapter().getInitChapter());
}
}
chapterListener.changed(null, null);
invalidate();
}
private void setDefault() {
int pos = list.getSelectedIndex();
if (pos == -1)
return;
String id = list.getItems().get(pos).getId();
World.getInstance().setInitScene(id);
Ctx.project.setModified();
}
@Override
protected void delete() {
Scene s = removeSelected();
parent.getScenes().remove(s.getId());
// delete init_scene attr if the scene to delete is the chapter
// init_scene
if (parent.getInitScene() != null && parent.getInitScene().equals(s.getId())) {
if (parent.getScenes().size() > 0)
parent.setInitScene(parent.getScenes().values().iterator().next().getId());
else
parent.setInitScene(null);
}
// TRANSLATIONS
Ctx.project.getI18N().putTranslationsInElement(s);
// UNDO
UndoOp undoOp = new UndoDeleteScene(s);
Ctx.project.getUndoStack().add(undoOp);
Ctx.project.setModified();
}
@Override
protected void copy() {
Scene e = list.getSelected();
if (e == null)
return;
clipboard = (Scene) ElementUtils.cloneElement(e);
toolbar.disablePaste(false);
// TRANSLATIONS
Ctx.project.getI18N().putTranslationsInElement(clipboard);
}
@Override
protected void paste() {
Scene newElement = (Scene) ElementUtils.cloneElement(clipboard);
newElement.setId(ElementUtils.getCheckedId(newElement.getId(),
World.getInstance().getScenes().keySet().toArray(new String[0])));
int pos = list.getSelectedIndex() + 1;
list.getItems().insert(pos, newElement);
World.getInstance().addScene(newElement);
Ctx.project.getI18N().extractStrings(newElement);
if (parent.getInitScene() == null) {
parent.setInitScene(newElement.getId());
}
list.setSelectedIndex(pos);
list.invalidateHierarchy();
Ctx.project.setModified();
}
@Override
protected EditModelDialog<World, Scene> getEditElementDialogInstance(Scene e) {
EditSceneDialog dialog = new EditSceneDialog(skin, parent, e);
if (e != null) {
dialog.setListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
Ctx.project.setSelectedScene(list.getSelected());
}
});
}
return dialog;
}
public TextureRegion getBgIcon(String atlas, String region) {
// check here for dispose instead in project loading because the opengl
// context lost in new project thread
if (disposeBgCache) {
dispose();
disposeBgCache = false;
}
String s = atlas + "#" + region;
TextureRegion icon = bgIconCache.get(s);
if (icon == null) {
Batch batch = getStage().getBatch();
batch.end();
try {
icon = createBgIcon(atlas, region);
} catch(Exception e) {
}
if (icon != null) {
bgIconCache.put(s, icon);
} else {
EditorLogger.error("Error creating Background icon: " + atlas + "." + region);
}
batch.begin();
}
return icon;
}
public void dispose() {
for (TextureRegion r : bgIconCache.values())
r.getTexture().dispose();
bgIconCache.clear();
}
private TextureRegion createBgIcon(String atlas, String region) {
TextureAtlas a = new TextureAtlas(Gdx.files
.absolute(Ctx.project.getProjectPath() + "/" + Project.ATLASES_PATH + "/1/" + atlas + ".atlas"));
AtlasRegion r = a.findRegion(region);
if (r == null) {
a.dispose();
return null;
}
FrameBuffer fbo = new FrameBuffer(Format.RGBA8888, 200, (int) (r.getRegionHeight() * 200f / r.getRegionWidth()),
false);
SpriteBatch fboBatch = new SpriteBatch();
fboBatch.setColor(Color.WHITE);
OrthographicCamera camera = new OrthographicCamera();
camera.setToOrtho(false, fbo.getWidth(), fbo.getHeight());
fboBatch.setProjectionMatrix(camera.combined);
Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST);
fbo.begin();
fboBatch.begin();
fboBatch.draw(r, 0, 0, fbo.getWidth(), fbo.getHeight());
fboBatch.end();
TextureRegion tex = ScreenUtils.getFrameBufferTexture(0, 0, fbo.getWidth(), fbo.getHeight());
// tex.flip(false, true);
fbo.end();
Gdx.gl.glEnable(GL20.GL_SCISSOR_TEST);
fbo.dispose();
a.dispose();
fboBatch.dispose();
return tex;
}
// -------------------------------------------------------------------------
// ListCellRenderer
// -------------------------------------------------------------------------
private final CellRenderer<Scene> listCellRenderer = new CellRenderer<Scene>() {
@Override
protected String getCellTitle(Scene e) {
String name = e.getId();
// TODO SET INIT SCENE
String init = World.getInstance().getInitScene();
if (name.equals(init))
name += " <init>";
return name;
}
@Override
protected String getCellSubTitle(Scene e) {
return e.getBackgroundAtlas();
}
@Override
public TextureRegion getCellImage(Scene e) {
String atlas = e.getBackgroundAtlas();
String region = e.getBackgroundRegionId();
TextureRegion r = null;
if (atlas != null && region != null && !atlas.isEmpty() && !region.isEmpty()) {
r = getBgIcon(atlas, region);
}
if (r == null)
r = Ctx.assetManager.getIcon("ic_no_scene");
return r;
}
@Override
protected boolean hasSubtitle() {
return true;
}
@Override
protected boolean hasImage() {
return true;
}
};
}