Files
forge/forge-gui-mobile/src/forge/toolbox/FFileChooser.java
drdev e1457ad744 Make FDialogs more mobile-friendly
Fix Concede so dialog shown
2015-03-07 16:49:33 +00:00

452 lines
17 KiB
Java

package forge.toolbox;
import java.io.File;
import java.io.FilenameFilter;
import org.apache.commons.lang3.StringUtils;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
import forge.Forge;
import forge.Graphics;
import forge.assets.FSkinColor;
import forge.assets.FSkinFont;
import forge.assets.FSkinImage;
import forge.menu.FMenuItem;
import forge.menu.FPopupMenu;
import forge.toolbox.FEvent;
import forge.toolbox.FEvent.FEventHandler;
import forge.util.Callback;
import forge.util.FileUtil;
import forge.util.Utils;
public class FFileChooser extends FDialog {
private static final float BACK_ICON_THICKNESS = Utils.scale(2);
public enum ChoiceType {
OpenFile,
SaveFile,
GetDirectory
}
public static void show(String title0, ChoiceType choiceType0, Callback<String> callback0) {
show(title0, choiceType0, "", "", callback0);
}
public static void show(String title0, ChoiceType choiceType0, String defaultFilename0, Callback<String> callback0) {
show(title0, choiceType0, defaultFilename0, "", callback0);
}
public static void show(String title0, ChoiceType choiceType0, String defaultFilename0, String baseDir0, Callback<String> callback0) {
FFileChooser dialog = new FFileChooser(title0, choiceType0, defaultFilename0, baseDir0, callback0);
dialog.show();
}
private final ChoiceType choiceType;
private final String baseDir;
private final Callback<String> callback;
private final FList<File> lstFiles = add(new FileList());
private final FTextField txtFilename = add(new FilenameField());
private FFileChooser(String title0, ChoiceType choiceType0, String defaultFilename0, String baseDir0, Callback<String> callback0) {
super(title0, 3);
choiceType = choiceType0;
if (choiceType == ChoiceType.GetDirectory) {
if (defaultFilename0.endsWith(File.separator)) { //if getting directory, don't end with a slash
defaultFilename0 = defaultFilename0.substring(0, defaultFilename0.length() - 1);
}
}
txtFilename.setFont(FSkinFont.get(12));
txtFilename.setText(defaultFilename0);
txtFilename.setChangedHandler(new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
refreshFileList();
}
});
initButton(0, "OK", new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
activateSelectedFile(true);
}
});
initButton(1, "New Folder", new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
final File dir = getCurrentDir();
if (dir == null) {
FOptionPane.showErrorDialog("Cannot add new folder to invalid folder.", "Invalid Folder");
return;
}
FOptionPane.showInputDialog("Enter name for new folder:", "New Folder", new Callback<String>() {
@Override
public void run(String result) {
if (StringUtils.isEmpty(result)) { return; }
try {
File newDir = new File(dir, result);
if (newDir.mkdirs()) {
txtFilename.setText(newDir.getAbsolutePath());
refreshFileList();
return;
}
}
catch (Exception e) {
e.printStackTrace();
}
FOptionPane.showErrorDialog("\"" + result + "\" is not a valid folder name.", "Invalid Name");
}
});
}
});
initButton(2, "Cancel", new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
hide();
}
});
baseDir = baseDir0;
callback = callback0;
refreshFileList();
}
@Override
protected float layoutAndGetHeight(float width, float maxHeight) {
float padding = FOptionPane.PADDING;
float w = width - 2 * padding;
float fieldHeight = txtFilename.getHeight();
float listHeight = maxHeight - fieldHeight - 2 * padding;
float x = padding;
float y = padding;
txtFilename.setBounds(x, y, w, fieldHeight);
y += fieldHeight + padding;
lstFiles.setBounds(x, y, w, listHeight);
return maxHeight;
}
private File getCurrentDir() {
String filename = getSelectedFilename();
File dir = new File(filename);
if (!dir.exists() || !dir.isDirectory()) {
int idx = filename.lastIndexOf(File.separatorChar);
if (idx != -1) {
dir = new File(filename.substring(0, idx));
}
}
if (dir.exists() && dir.isDirectory()) {
if (choiceType == ChoiceType.GetDirectory) {
dir = dir.getParentFile(); //show parent folder's files and select folder
}
return dir;
}
return null;
}
private void refreshFileList() {
File dir = getCurrentDir();
if (dir != null) {
setFileListForDir(dir);
}
else {
lstFiles.setListData(File.listRoots());
}
scrollSelectionIntoView();
}
private void setFileListForDir(File dir) {
FilenameFilter filter = null;
if (choiceType == ChoiceType.GetDirectory) {
//don't list files if getting directory
filter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir, name).isDirectory();
}
};
}
lstFiles.setListData(dir.listFiles(filter));
}
private void scrollSelectionIntoView() {
String filename = getSelectedFilename();
for (int i = 0; i < lstFiles.getCount(); i++) {
if (lstFiles.getItemAt(i).getAbsolutePath().equals(filename)) {
lstFiles.scrollIntoView(i);
return;
}
}
}
private String getSelectedFilename() {
return baseDir + txtFilename.getText();
}
private void activateSelectedFile(boolean fromOkButton) {
String filename = getSelectedFilename();
File file = new File(filename);
boolean returnDirectory = (choiceType == ChoiceType.GetDirectory);
if (file.exists() && file.isDirectory() && (!fromOkButton || !returnDirectory)) {
//if directory activated not from OK button, open it within dialog
setFileListForDir(file);
if (lstFiles.getCount() > 0) { //select first item if any
txtFilename.setText(lstFiles.getItemAt(0).getAbsolutePath());
}
else {
txtFilename.setText(file.getAbsolutePath() + File.separator); //indicate no selection in directory
}
scrollSelectionIntoView();
return;
}
//validate return value
if (returnDirectory) {
if (!file.exists() || !file.isDirectory()) {
FOptionPane.showErrorDialog("No folder exists with the selected path.", "Invalid Folder");
return;
}
}
else {
if ((!file.exists() && choiceType == ChoiceType.OpenFile) || file.isDirectory()) {
FOptionPane.showErrorDialog("No file exists with the selected path.", "Invalid File");
return;
}
}
hide();
if (returnDirectory) {
filename += File.separator; //re-append separator if returning directory
}
callback.run(filename);
}
private void back() {
int idx = txtFilename.getText().lastIndexOf(File.separatorChar);
if (idx != -1) {
txtFilename.setText(txtFilename.getText().substring(0, idx));
refreshFileList();
}
}
private void renameFile(final File file) {
final File dir = file.getParentFile();
if (dir == null) {
FOptionPane.showErrorDialog("Cannot rename file in invalid folder.", "Invalid Folder");
return;
}
String message, title;
if (file.isDirectory()) {
message = "Enter new name for folder:";
title = "Rename Folder";
}
else {
message = "Enter new name for file:";
title = "Rename File";
}
FOptionPane.showInputDialog(message, title, null, file.getName(), new Callback<String>() {
@Override
public void run(String result) {
if (StringUtils.isEmpty(result)) { return; }
try {
File newFile = new File(dir, result);
if (file.renameTo(newFile)) {
txtFilename.setText(newFile.getAbsolutePath());
refreshFileList();
return;
}
}
catch (Exception e) {
e.printStackTrace();
}
FOptionPane.showErrorDialog("\"" + result + "\" is not a valid name.", "Invalid Name");
}
});
}
private void deleteFile(final Integer index, final File file) {
FOptionPane.showConfirmDialog("Are you sure you wish to proceed with delete? This action cannot be undone.",
"Delete " + (file.isDirectory() ? "Folder" : "File"), "Delete", "Cancel", new Callback<Boolean>() {
@Override
public void run(Boolean result) {
if (result) {
try {
if (FileUtil.deleteDirectory(file)) { //this will ensure directory or file deleted
lstFiles.removeItem(file);
if (lstFiles.getCount() > index) { //select next item if possible
txtFilename.setText(lstFiles.getItemAt(index).getAbsolutePath());
}
else if (lstFiles.getCount() > 0) { //select new last item otherwise
txtFilename.setText(lstFiles.getItemAt(lstFiles.getCount() - 1).getAbsolutePath());
}
else {
File dir = getCurrentDir();
if (dir != null) {
txtFilename.setText(dir.getAbsolutePath() + File.separator); //indicate no selection in directory
}
}
return;
}
}
catch (Exception ex) {
ex.printStackTrace();
}
FOptionPane.showErrorDialog("Could not delete file.");
}
}
});
}
private class FilenameField extends FTextField {
@Override
public boolean tap(float x, float y, int count) {
if (x < getLeftPadding()) { //handle tapping on back icon
back();
return true;
}
return super.tap(x, y, count);
}
@Override
protected float getLeftPadding() {
return getHeight();
}
@Override
public void draw(Graphics g) {
super.draw(g);
//draw back icon
float w = getHeight();
float h = w;
float x = w * 0.35f;
float y = h / 2;
float offsetX = w / 8;
float offsetY = h / 6;
g.drawLine(BACK_ICON_THICKNESS, FORE_COLOR, x + offsetX, y - offsetY, x - offsetX, y);
g.drawLine(BACK_ICON_THICKNESS, FORE_COLOR, x - offsetX, y, x + offsetX, y + offsetY);
x += w * 0.3f;
g.drawLine(BACK_ICON_THICKNESS, FORE_COLOR, x + offsetX, y - offsetY, x - offsetX, y);
g.drawLine(BACK_ICON_THICKNESS, FORE_COLOR, x - offsetX, y, x + offsetX, y + offsetY);
}
}
private class FileList extends FList<File> {
private FileList() {
setListItemRenderer(new ListItemRenderer<File>() {
private int prevTapIndex = -1;
@Override
public float getItemHeight() {
return FChoiceList.DEFAULT_ITEM_HEIGHT;
}
@Override
public boolean tap(Integer index, File value, float x, float y, int count) {
if (count == 2 && index == prevTapIndex) {
activateSelectedFile(false);
return true;
}
txtFilename.setText(value.getAbsolutePath());
prevTapIndex = index;
if (value.isDirectory() && x < getItemHeight() + 2 * FList.PADDING) {
activateSelectedFile(false); //open folders if icon single tapped
}
return true;
}
@Override
public boolean showMenu(final Integer index, final File value, FDisplayObject owner, float x, float y) {
txtFilename.setText(value.getAbsolutePath());
FPopupMenu menu = new FPopupMenu() {
@Override
protected void buildMenu() {
String suffix = value.isDirectory() ? " Folder" : " File";
addItem(new FMenuItem("Rename" + suffix, FSkinImage.EDIT,
new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
renameFile(value);
}
}));
addItem(new FMenuItem("Delete" + suffix, FSkinImage.DELETE,
new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
deleteFile(index, value);
}
}));
}
};
menu.show(owner, x, y);
return true;
}
@Override
public void drawValue(Graphics g, Integer index, File value, FSkinFont font, FSkinColor foreColor, FSkinColor backColor, boolean pressed, float x, float y, float w, float h) {
if (value.isDirectory()) {
float iconSize = h;
g.drawImage(FSkinImage.FOLDER, x, y + (h - iconSize) / 2, iconSize, iconSize);
x += iconSize + FList.PADDING;
}
g.drawText(value.getName(), font, foreColor, x, y, w, h, false, HAlignment.LEFT, true);
}
});
}
@Override
protected void drawBackground(Graphics g) {
//draw no background
}
@Override
public void drawOverlay(Graphics g) {
super.drawOverlay(g);
g.drawRect(1.5f, FChoiceList.BORDER_COLOR, 0, 0, getWidth(), getHeight());
}
@Override
protected FSkinColor getItemFillColor(int index) {
if (getItemAt(index).getAbsolutePath().equals(getSelectedFilename())) {
return FChoiceList.SEL_COLOR; //don't show SEL_COLOR if in multi-select mode
}
if (index % 2 == 1) {
return FChoiceList.ALT_ITEM_COLOR;
}
return FChoiceList.ITEM_COLOR;
}
@Override
protected boolean drawLineSeparators() {
return false;
}
}
@Override
public boolean keyDown(int keyCode) {
switch (keyCode) {
case Keys.ENTER:
activateSelectedFile(false);
return true;
case Keys.ESCAPE:
if (Forge.endKeyInput()) { return true; }
break; //let FDialog handle it
case Keys.BACK:
case Keys.BACKSPACE:
back();
return true;
}
return super.keyDown(keyCode);
}
}