mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-04-12 18:30:26 +08:00
test ModpackManager
This commit is contained in:
parent
102205de40
commit
08301bed8d
@ -27,7 +27,7 @@ import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.views.LogWindow;
|
||||
import org.jackhuang.hellominecraft.launcher.utils.MinecraftCrashAdvicer;
|
||||
|
@ -32,7 +32,7 @@ import javax.net.ssl.X509TrustManager;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.api.PluginManager;
|
||||
import org.jackhuang.hellominecraft.launcher.core.launch.GameLauncher;
|
||||
import org.jackhuang.hellominecraft.launcher.utils.CrashReporter;
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.launcher.api;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.settings.DefaultPlugin;
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,7 @@ import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
|
||||
|
@ -22,7 +22,7 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftAssetService;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.Task;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
|
@ -24,7 +24,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftAssetService;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.IDownloadProvider;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
|
@ -31,7 +31,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
|
||||
public class PropertyMap extends HashMap<String, Property> {
|
||||
|
||||
|
@ -29,7 +29,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.NetUtils;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
|
||||
|
@ -23,9 +23,8 @@ import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.launch.GameLauncher;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.IMinecraftLibrary;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
@ -119,6 +118,19 @@ public class MinecraftDownloadService extends IMinecraftDownloadService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean downloadMinecraftJarTo(String id, File mvt) {
|
||||
String vurl = service.getDownloadType().getProvider().getVersionsDownloadURL() + id + "/";
|
||||
if (TaskWindow.getInstance()
|
||||
.addTask(new FileDownloadTask(vurl + id + ".jar", IOUtils.tryGetCanonicalFile(mvt)).setTag(id + ".jar"))
|
||||
.start())
|
||||
return true;
|
||||
else {
|
||||
mvt.delete();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean downloadMinecraftVersionJson(String id) {
|
||||
String vurl = service.getDownloadType().getProvider().getVersionsDownloadURL() + id + "/";
|
||||
|
@ -17,7 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.launcher.core.download;
|
||||
|
||||
import org.jackhuang.hellominecraft.launcher.core.installers.InstallerType;
|
||||
import org.jackhuang.hellominecraft.launcher.core.installers.InstallerVersionList;
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,7 @@ public class PackMinecraftInstaller {
|
||||
file.mkdirs();
|
||||
for (String src1 : src)
|
||||
Compressor.unzip(new File(src1), file);
|
||||
Compressor.zip(file, dest);
|
||||
Compressor.zip(file.getAbsolutePath(), dest.getAbsolutePath());
|
||||
FileUtils.deleteDirectory(file);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import java.io.InputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
|
||||
import org.jackhuang.hellominecraft.launcher.core.installers.InstallerVersionList.InstallerVersion;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.Task;
|
||||
|
@ -21,7 +21,7 @@ import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.Task;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.communication.PreviousResult;
|
||||
|
@ -24,7 +24,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.Launcher;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.auth.UserProfileProvider;
|
||||
@ -132,7 +132,7 @@ public abstract class AbstractMinecraftLoader implements IMinecraftLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> makeLaunchingCommand() {
|
||||
public List<String> makeLaunchingCommand() throws GameException {
|
||||
HMCLog.log("*** Make shell command ***");
|
||||
|
||||
ArrayList<String> res = new ArrayList<>();
|
||||
@ -191,7 +191,7 @@ public abstract class AbstractMinecraftLoader implements IMinecraftLoader {
|
||||
*
|
||||
* @param list the command list you shoud edit.
|
||||
*/
|
||||
protected abstract void makeSelf(List<String> list);
|
||||
protected abstract void makeSelf(List<String> list) throws GameException;
|
||||
|
||||
protected void appendJVMArgs(List<String> list) {
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ package org.jackhuang.hellominecraft.launcher.core.launch;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.auth.IAuthenticator;
|
||||
import org.jackhuang.hellominecraft.launcher.core.auth.LoginInfo;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.DownloadLibraryJob;
|
||||
|
@ -27,7 +27,7 @@ import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.List;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.api.PluginManager;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.auth.IAuthenticator;
|
||||
@ -174,13 +174,10 @@ public class GameLauncher {
|
||||
}
|
||||
writer.write(StrUtils.makeCommand(str));
|
||||
writer.close();
|
||||
if (!isWin)
|
||||
try {
|
||||
Runtime.getRuntime().exec("chmod +x " + IOUtils.tryGetCanonicalFilePath(f));
|
||||
} catch (IOException e) {
|
||||
HMCLog.warn("Failed to give sh file permission.", e);
|
||||
MessageBox.Show(C.i18n("launch.failed_sh_permission"));
|
||||
}
|
||||
if (!f.setExecutable(true)) {
|
||||
HMCLog.warn("Failed to give launcher permission.");
|
||||
MessageBox.Show(C.i18n("launch.failed_sh_permission"));
|
||||
}
|
||||
|
||||
HMCLog.log("Command: " + StrUtils.parseParams("", str, " "));
|
||||
return f;
|
||||
|
@ -28,7 +28,7 @@ import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Pack200;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.DownloadLibraryJob;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.download.FileDownloadTask;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
@ -50,23 +50,25 @@ public class LibraryDownloadTask extends FileDownloadTask {
|
||||
@Override
|
||||
public void executeTask() throws Throwable {
|
||||
File packFile = new File(job.path.getParentFile(), job.path.getName() + ".pack.xz");
|
||||
if (job.name.contains("typesafe")) {
|
||||
download(new URL(job.url + ".pack.xz"), packFile);
|
||||
unpackLibrary(job.path, packFile);
|
||||
packFile.delete();
|
||||
} else {
|
||||
if (job.name.startsWith("net.minecraftforge:forge:")) {
|
||||
String[] s = job.name.split(":");
|
||||
if (s.length == 3)
|
||||
job.url = "http://files.minecraftforge.net/maven/net/minecraftforge/forge/" + s[2] + "/forge-" + s[2] + "-universal.jar";
|
||||
}
|
||||
if (job.name.startsWith("com.mumfrey:liteloader:")) {
|
||||
String[] s = job.name.split(":");
|
||||
if (s.length == 3 && s[2].length() > 3)
|
||||
job.url = "http://dl.liteloader.com/versions/com/mumfrey/liteloader/" + s[2].substring(0, s[2].length() - 3) + "/liteloader-" + s[2] + ".jar";
|
||||
}
|
||||
download(new URL(job.url), job.path);
|
||||
/*
|
||||
* if (job.name.contains("typesafe")) {
|
||||
* download(new URL(job.url + ".pack.xz"), packFile);
|
||||
* unpackLibrary(job.path, packFile);
|
||||
* packFile.delete();
|
||||
* } else {
|
||||
*/
|
||||
if (job.name.startsWith("net.minecraftforge:forge:")) {
|
||||
String[] s = job.name.split(":");
|
||||
if (s.length == 3)
|
||||
job.url = "http://files.minecraftforge.net/maven/net/minecraftforge/forge/" + s[2] + "/forge-" + s[2] + "-universal.jar";
|
||||
}
|
||||
if (job.name.startsWith("com.mumfrey:liteloader:")) {
|
||||
String[] s = job.name.split(":");
|
||||
if (s.length == 3 && s[2].length() > 3)
|
||||
job.url = "http://dl.liteloader.com/versions/com/mumfrey/liteloader/" + s[2].substring(0, s[2].length() - 3) + "/liteloader-" + s[2] + ".jar";
|
||||
}
|
||||
download(new URL(job.url), job.path);
|
||||
//}
|
||||
}
|
||||
|
||||
void download(URL url, File filePath) throws Throwable {
|
||||
|
@ -24,7 +24,7 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.Main;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.auth.UserProfileProvider;
|
||||
@ -54,14 +54,17 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void makeSelf(List<String> res) {
|
||||
protected void makeSelf(List<String> res) throws GameException {
|
||||
String library = options.isCanceledWrapper() ? "" : "-cp=";
|
||||
for (MinecraftLibrary l : version.libraries) {
|
||||
l.init();
|
||||
if (l.allow() && !l.isRequiredToUnzip())
|
||||
library += l.getFilePath(gameDir).getAbsolutePath() + File.pathSeparator;
|
||||
}
|
||||
library += IOUtils.tryGetCanonicalFilePath(version.getJar(service.baseDirectory())) + File.pathSeparator;
|
||||
File f = version.getJar(service.baseDirectory());
|
||||
if (!f.exists())
|
||||
throw new GameException("Minecraft jar does not exists");
|
||||
library += IOUtils.tryGetCanonicalFilePath(f) + File.pathSeparator;
|
||||
library = library.substring(0, library.length() - File.pathSeparator.length());
|
||||
if (options.isCanceledWrapper())
|
||||
res.add("-cp");
|
||||
|
@ -25,7 +25,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftModService;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
|
||||
import org.jackhuang.hellominecraft.launcher.core.ModInfo;
|
||||
|
@ -22,14 +22,20 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.FileSystemException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftProvider;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.system.Compressor;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.ZipEngine;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftVersionRequest;
|
||||
|
||||
/**
|
||||
* A mod pack(*.zip) includes these things:
|
||||
@ -51,8 +57,8 @@ import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
*/
|
||||
public final class ModpackManager {
|
||||
|
||||
public static void install(File input, File installFolder, String id) throws IOException, FileAlreadyExistsException {
|
||||
File versions = new File(installFolder, "versions");
|
||||
public static void install(File input, IMinecraftService service, String id) throws IOException, FileAlreadyExistsException {
|
||||
File versions = new File(service.baseDirectory(), "versions");
|
||||
File oldFile = new File(versions, "minecraft"), newFile = null;
|
||||
if (oldFile.exists()) {
|
||||
newFile = new File(versions, "minecraft-" + System.currentTimeMillis());
|
||||
@ -64,19 +70,29 @@ public final class ModpackManager {
|
||||
}
|
||||
|
||||
try {
|
||||
AtomicBoolean b = new AtomicBoolean(false);
|
||||
AtomicInteger b = new AtomicInteger(0);
|
||||
HMCLog.log("Decompressing modpack");
|
||||
Compressor.unzip(input, versions, t -> {
|
||||
if (t.equals("minecraft/pack.json"))
|
||||
b.set(true);
|
||||
b.incrementAndGet();
|
||||
if (t.equals("minecraft/pack.jar"))
|
||||
b.incrementAndGet();
|
||||
return true;
|
||||
});
|
||||
if (!b.get())
|
||||
throw new FileNotFoundException("the mod pack is not in a correct format.");
|
||||
if (b.get() < 2)
|
||||
throw new FileNotFoundException("the mod pack is in an incorrect format.");
|
||||
File nowFile = new File(versions, id);
|
||||
oldFile.renameTo(nowFile);
|
||||
|
||||
new File(nowFile, "pack.json").renameTo(new File(nowFile, id + ".json"));
|
||||
File json = new File(nowFile, "pack.json");
|
||||
MinecraftVersion mv = C.gson.fromJson(FileUtils.readFileToString(json), MinecraftVersion.class);
|
||||
if (mv.jar == null)
|
||||
throw new FileSystemException("the mod pack is in an incorrect format, pack.json does not have attribute 'jar'.");
|
||||
service.download().downloadMinecraftJarTo(mv.jar, new File(nowFile, id + ".jar"));
|
||||
mv.jar = null;
|
||||
FileUtils.writeStringToFile(json, C.gsonPrettyPrinting.toJson(mv));
|
||||
json.renameTo(new File(nowFile, id + ".json"));
|
||||
|
||||
} finally {
|
||||
FileUtils.deleteDirectoryQuietly(oldFile);
|
||||
if (newFile != null)
|
||||
@ -94,31 +110,34 @@ public final class ModpackManager {
|
||||
*
|
||||
* @throws IOException if create tmp directory failed
|
||||
*/
|
||||
public static void export(File output, IMinecraftProvider provider, String version) throws IOException, GameException {
|
||||
File tmp = new File(System.getProperty("java.io.tmpdir"), "hmcl-modpack");
|
||||
tmp.mkdirs();
|
||||
|
||||
File root = new File(tmp, "minecraft");
|
||||
|
||||
HMCLog.log("Copying files from game directory.");
|
||||
FileUtils.copyDirectory(provider.getRunDirectory(version), root);
|
||||
File pack = new File(root, "pack.json");
|
||||
MinecraftVersion mv = provider.getVersionById(version).resolve(provider);
|
||||
public static void export(File output, IMinecraftProvider provider, String version, List<String> blacklist) throws IOException, GameException {
|
||||
ArrayList<String> b = new ArrayList<>(Arrays.asList(new String[] { "usernamecache.json", "asm", "logs", "backups", "versions", "assets", "usercache.json", "libraries", "crash-reports", "launcher_profiles.json", "NVIDIA", "TCNodeTracker" }));
|
||||
if (blacklist != null)
|
||||
b.addAll(blacklist);
|
||||
HMCLog.log("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
ZipEngine zip = null;
|
||||
try {
|
||||
FileUtils.writeStringToFile(pack, C.gsonPrettyPrinting.toJson(mv));
|
||||
String[] blacklist = { "usernamecache.json", "asm", "logs", "backups", "versions", "assets", "usercache.json", "libraries", "crash-reports", "launcher_profiles.json", "NVIDIA", "TCNodeTracker" };
|
||||
HMCLog.log("Removing files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
for (String s : blacklist) {
|
||||
File f = new File(root, s);
|
||||
if (f.isFile())
|
||||
f.delete();
|
||||
else if (f.isDirectory())
|
||||
FileUtils.deleteDirectory(f);
|
||||
}
|
||||
HMCLog.log("Compressing game files");
|
||||
Compressor.zip(tmp, output);
|
||||
zip = new ZipEngine(output);
|
||||
zip.putDirectory(provider.getRunDirectory(version), (String x, Boolean y) -> {
|
||||
for (String s : b)
|
||||
if (y) {
|
||||
if (x.startsWith(s + "/"))
|
||||
return null;
|
||||
} else if (x.equals(s))
|
||||
return null;
|
||||
return "minecraft/" + x;
|
||||
});
|
||||
|
||||
MinecraftVersion mv = provider.getVersionById(version).resolve(provider);
|
||||
mv.runDir = "version";
|
||||
MinecraftVersionRequest r = MinecraftVersionRequest.minecraftVersion(provider.getMinecraftJar(version));
|
||||
if (r.type != MinecraftVersionRequest.OK)
|
||||
throw new FileSystemException("Cannot read vanilla version, " + MinecraftVersionRequest.getResponse(r));
|
||||
mv.jar = r.version;
|
||||
zip.putTextFile(C.gsonPrettyPrinting.toJson(mv), "minecraft/pack.json");
|
||||
} finally {
|
||||
FileUtils.deleteDirectory(tmp);
|
||||
if (zip != null)
|
||||
zip.closeFile();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.launcher.core.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.DownloadLibraryJob;
|
||||
@ -38,6 +39,8 @@ public abstract class IMinecraftDownloadService extends IMinecraftBasicService {
|
||||
|
||||
public abstract boolean downloadMinecraftJar(String id);
|
||||
|
||||
public abstract boolean downloadMinecraftJarTo(String id, File f);
|
||||
|
||||
public abstract boolean downloadMinecraftVersionJson(String id);
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftProvider;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
|
||||
@ -36,6 +36,7 @@ import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.download.FileDownloadTask;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.functions.Consumer;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
|
||||
@ -232,7 +233,10 @@ public class MinecraftVersionManager extends IMinecraftProvider {
|
||||
|
||||
@Override
|
||||
public File getMinecraftJar(String id) {
|
||||
return versions.get(id).getJar(service.baseDirectory());
|
||||
if (versions.containsKey(id))
|
||||
return versions.get(id).getJar(service.baseDirectory());
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -242,7 +246,7 @@ public class MinecraftVersionManager extends IMinecraftProvider {
|
||||
|
||||
@Override
|
||||
public MinecraftVersion getVersionById(String id) {
|
||||
return id == null ? null : versions.get(id);
|
||||
return StrUtils.isBlank(id) ? null : versions.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,7 +20,7 @@ package org.jackhuang.hellominecraft.launcher.settings;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.Main;
|
||||
import org.jackhuang.hellominecraft.launcher.api.PluginManager;
|
||||
import org.jackhuang.hellominecraft.launcher.core.LauncherVisibility;
|
||||
@ -115,7 +115,7 @@ public final class Profile {
|
||||
|
||||
public String getSelectedVersion() {
|
||||
String v = selectedMinecraftVersion;
|
||||
if (v == null) {
|
||||
if (StrUtils.isBlank(v) || service.version().getVersionById(v) == null) {
|
||||
v = service.version().getOneVersion().id;
|
||||
if (v != null)
|
||||
setSelectedMinecraftVersion(v);
|
||||
|
@ -26,7 +26,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.Main;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.DownloadType;
|
||||
import org.jackhuang.hellominecraft.utils.CollectionUtils;
|
||||
|
@ -24,7 +24,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.Main;
|
||||
import org.jackhuang.hellominecraft.launcher.settings.Settings;
|
||||
import org.jackhuang.hellominecraft.utils.NetUtils;
|
||||
|
@ -35,7 +35,7 @@ import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Pack200;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.MCUtils;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.Task;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
|
@ -21,7 +21,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.download.FileDownloadTask;
|
||||
import org.jackhuang.hellominecraft.utils.ArrayUtils;
|
||||
|
@ -19,7 +19,7 @@ package org.jackhuang.hellominecraft.launcher.views;
|
||||
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
|
@ -346,7 +346,9 @@
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="btnExportModpack">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="导出整合包"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/jackhuang/hellominecraft/launcher/I18N.properties" key="settings.modpack.save.task" replaceFormat="C.i18n("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnExportModpackActionPerformed"/>
|
||||
@ -354,7 +356,9 @@
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="btnImportModpack">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="导入整合包"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/jackhuang/hellominecraft/launcher/I18N.properties" key="settings.modpack.install.task" replaceFormat="C.i18n("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnImportModpackActionPerformed"/>
|
||||
|
@ -32,7 +32,9 @@ import java.awt.event.ItemEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.JFileChooser;
|
||||
@ -45,7 +47,7 @@ import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.LauncherVisibility;
|
||||
import org.jackhuang.hellominecraft.launcher.settings.Profile;
|
||||
@ -56,6 +58,8 @@ import org.jackhuang.hellominecraft.launcher.core.installers.InstallerType;
|
||||
import org.jackhuang.hellominecraft.launcher.core.mod.ModpackManager;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.GameDirType;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.launcher.views.modpack.ModpackInitializationPanel;
|
||||
import org.jackhuang.hellominecraft.launcher.views.modpack.ModpackWizard;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskRunnable;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
import org.jackhuang.hellominecraft.utils.Event;
|
||||
@ -64,8 +68,10 @@ import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftVersionRequest;
|
||||
import org.jackhuang.hellominecraft.utils.system.OS;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.Java;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.WizardDisplayer;
|
||||
import rx.Observable;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
@ -403,14 +409,14 @@ public final class GameSettingsPanel extends AnimatedPanel implements DropTarget
|
||||
}
|
||||
});
|
||||
|
||||
btnExportModpack.setText("导出整合包");
|
||||
btnExportModpack.setText(C.i18n("settings.modpack.save.task")); // NOI18N
|
||||
btnExportModpack.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
btnExportModpackActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
btnImportModpack.setText("导入整合包");
|
||||
btnImportModpack.setText(C.i18n("settings.modpack.install.task")); // NOI18N
|
||||
btnImportModpack.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
btnImportModpackActionPerformed(evt);
|
||||
@ -914,6 +920,7 @@ public final class GameSettingsPanel extends AnimatedPanel implements DropTarget
|
||||
// <editor-fold defaultstate="collapsed" desc="UI Events">
|
||||
private void cboProfilesItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboProfilesItemStateChanged
|
||||
if (!isLoading) {
|
||||
Settings.getInstance().setLast((String) cboProfiles.getSelectedItem());
|
||||
if (getProfile().service().version().getVersionCount() <= 0)
|
||||
versionChanged(null);
|
||||
prepare(getProfile());
|
||||
@ -1139,13 +1146,14 @@ public final class GameSettingsPanel extends AnimatedPanel implements DropTarget
|
||||
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||
fc.setDialogTitle(C.i18n("settings.modpack.choose"));
|
||||
fc.setMultiSelectionEnabled(false);
|
||||
fc.setFileFilter(new FileNameExtensionFilter(C.i18n("settings.modpack"), ".zip"));
|
||||
fc.setFileFilter(new FileNameExtensionFilter(C.i18n("settings.modpack"), "zip"));
|
||||
fc.showOpenDialog(this);
|
||||
if (fc.getSelectedFile() == null)
|
||||
return;
|
||||
TaskWindow.getInstance().addTask(new TaskRunnable(C.i18n("settings.modpack"), () -> {
|
||||
String suggestedModpackId = JOptionPane.showInputDialog("Please enter your favourite game name", FileUtils.getBaseName(fc.getSelectedFile().getName()));
|
||||
TaskWindow.getInstance().addTask(new TaskRunnable(C.i18n("settings.modpack.install.task"), () -> {
|
||||
try {
|
||||
ModpackManager.install(fc.getSelectedFile(), getProfile().getCanonicalGameDirFile(), fc.getSelectedFile().getName());
|
||||
ModpackManager.install(fc.getSelectedFile(), getProfile().service(), suggestedModpackId);
|
||||
} catch (IOException ex) {
|
||||
MessageBox.Show(C.i18n("settings.modpack.install_error"));
|
||||
HMCLog.err("Failed to install modpack", ex);
|
||||
@ -1154,22 +1162,20 @@ public final class GameSettingsPanel extends AnimatedPanel implements DropTarget
|
||||
}//GEN-LAST:event_btnImportModpackActionPerformed
|
||||
|
||||
private void btnExportModpackActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExportModpackActionPerformed
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||
fc.setDialogTitle(C.i18n("settings.modpack.save"));
|
||||
fc.setMultiSelectionEnabled(false);
|
||||
fc.setFileFilter(new FileNameExtensionFilter(C.i18n("settings.modpack"), ".zip"));
|
||||
fc.showSaveDialog(this);
|
||||
if (fc.getSelectedFile() == null)
|
||||
return;
|
||||
TaskWindow.getInstance().addTask(new TaskRunnable(C.i18n("settings.modpack"), () -> {
|
||||
try {
|
||||
ModpackManager.export(fc.getSelectedFile(), getProfile().service().version(), getProfile().getSelectedVersion());
|
||||
} catch (IOException | GameException ex) {
|
||||
MessageBox.Show(C.i18n("settings.modpack.export_error"));
|
||||
HMCLog.err("Failed to export modpack", ex);
|
||||
}
|
||||
})).start();
|
||||
Map settings = (Map) WizardDisplayer.showWizard(new ModpackWizard(getProfile().service().version()).createWizard());
|
||||
if (settings != null)
|
||||
TaskWindow.getInstance().addTask(new TaskRunnable(C.i18n("settings.modpack.save.task"),
|
||||
() -> {
|
||||
try {
|
||||
ModpackManager.export(new File((String) settings.get(ModpackInitializationPanel.KEY_MODPACK_LOCATION)),
|
||||
getProfile().service().version(),
|
||||
(String) settings.get(ModpackInitializationPanel.KEY_GAME_VERSION),
|
||||
((Boolean) settings.get(ModpackInitializationPanel.KEY_SAVE) == false) ? Arrays.asList("saves") : null);
|
||||
} catch (IOException | GameException ex) {
|
||||
MessageBox.Show(C.i18n("settings.modpack.export_error"));
|
||||
HMCLog.err("Failed to export modpack", ex);
|
||||
}
|
||||
})).start();
|
||||
}//GEN-LAST:event_btnExportModpackActionPerformed
|
||||
|
||||
// </editor-fold>
|
||||
|
@ -23,7 +23,7 @@ import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.settings.Settings;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.DownloadType;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
|
@ -41,7 +41,7 @@ import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.Main;
|
||||
import org.jackhuang.hellominecraft.launcher.settings.Settings;
|
||||
import org.jackhuang.hellominecraft.launcher.core.auth.IAuthenticator;
|
||||
|
@ -28,7 +28,7 @@ import java.util.List;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.launch.DefaultGameLauncher;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.auth.IAuthenticator;
|
||||
|
@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="lblModpackLocation" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="175" max="32767" attributes="0"/>
|
||||
<Component id="cboModpackLocation" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="txtModpackLocation" max="32767" attributes="0"/>
|
||||
<Component id="cboGameVersion" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="lblGameVersion" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="chkSave" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="lblModpackLocation" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cboModpackLocation" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="txtModpackLocation" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
|
||||
<Component id="lblGameVersion" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="cboGameVersion" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="chkSave" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="133" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="lblModpackLocation">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/jackhuang/hellominecraft/launcher/I18N.properties" key="settings.modpack.save" replaceFormat="C.i18n("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="txtModpackLocation">
|
||||
<Events>
|
||||
<EventHandler event="caretUpdate" listener="javax.swing.event.CaretListener" parameters="javax.swing.event.CaretEvent" handler="txtModpackLocationCaretUpdate"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="cboModpackLocation">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="..."/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cboModpackLocationActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lblGameVersion">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="要导出的游戏版本"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="cboGameVersion">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboGameVersionItemStateChanged"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="chkSave">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="允许导出存档"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkSaveItemStateChanged"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.launcher.views.modpack;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardController;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class ModpackInitializationPanel extends javax.swing.JPanel {
|
||||
|
||||
public static final String KEY_GAME_VERSION = "gameVersion";
|
||||
public static final String KEY_MODPACK_LOCATION = "modpackLocation";
|
||||
public static final String KEY_SAVE = "save";
|
||||
|
||||
private final WizardController controller;
|
||||
private final Map wizardData;
|
||||
|
||||
/**
|
||||
* Creates new form ModpackInitializationPanel
|
||||
*/
|
||||
public ModpackInitializationPanel(WizardController controller, Map wizardData, String[] versions) {
|
||||
initComponents();
|
||||
|
||||
this.controller = controller;
|
||||
this.wizardData = wizardData;
|
||||
wizardData.put(KEY_GAME_VERSION, versions);
|
||||
|
||||
wizardData.put(KEY_SAVE, false);
|
||||
|
||||
configureComboContents();
|
||||
controller.setProblem("Not a valid file location");
|
||||
|
||||
controller.setForwardNavigationMode(WizardController.MODE_CAN_FINISH);
|
||||
}
|
||||
|
||||
private void configureComboContents() {
|
||||
String[] versions = (String[]) wizardData.get(KEY_GAME_VERSION);
|
||||
cboGameVersion.setModel(new DefaultComboBoxModel<>(versions));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
lblModpackLocation = new javax.swing.JLabel();
|
||||
txtModpackLocation = new javax.swing.JTextField();
|
||||
cboModpackLocation = new javax.swing.JButton();
|
||||
lblGameVersion = new javax.swing.JLabel();
|
||||
cboGameVersion = new javax.swing.JComboBox<>();
|
||||
chkSave = new javax.swing.JCheckBox();
|
||||
|
||||
lblModpackLocation.setText(C.i18n("settings.modpack.save")); // NOI18N
|
||||
|
||||
txtModpackLocation.addCaretListener(new javax.swing.event.CaretListener() {
|
||||
public void caretUpdate(javax.swing.event.CaretEvent evt) {
|
||||
txtModpackLocationCaretUpdate(evt);
|
||||
}
|
||||
});
|
||||
|
||||
cboModpackLocation.setText("...");
|
||||
cboModpackLocation.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cboModpackLocationActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
lblGameVersion.setText("要导出的游戏版本");
|
||||
|
||||
cboGameVersion.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
cboGameVersionItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
chkSave.setText("允许导出存档");
|
||||
chkSave.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
chkSaveItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(lblModpackLocation)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 175, Short.MAX_VALUE)
|
||||
.addComponent(cboModpackLocation))
|
||||
.addComponent(txtModpackLocation)
|
||||
.addComponent(cboGameVersion, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(lblGameVersion)
|
||||
.addComponent(chkSave))
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(lblModpackLocation)
|
||||
.addComponent(cboModpackLocation))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(txtModpackLocation, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(29, 29, 29)
|
||||
.addComponent(lblGameVersion)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(cboGameVersion, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(chkSave)
|
||||
.addContainerGap(133, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void cboModpackLocationActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cboModpackLocationActionPerformed
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||
fc.setDialogTitle(C.i18n("settings.modpack.save"));
|
||||
fc.setMultiSelectionEnabled(false);
|
||||
fc.setFileFilter(new FileNameExtensionFilter(C.i18n("settings.modpack") + "(*.zip)", "zip"));
|
||||
fc.showSaveDialog(this);
|
||||
if (fc.getSelectedFile() != null)
|
||||
txtModpackLocation.setText(fc.getSelectedFile().getAbsolutePath());
|
||||
}//GEN-LAST:event_cboModpackLocationActionPerformed
|
||||
|
||||
private void txtModpackLocationCaretUpdate(javax.swing.event.CaretEvent evt) {//GEN-FIRST:event_txtModpackLocationCaretUpdate
|
||||
wizardData.put(KEY_MODPACK_LOCATION, txtModpackLocation.getText());
|
||||
|
||||
if (txtModpackLocation.getText().trim().isEmpty())
|
||||
controller.setProblem("Please choose a location!!!");
|
||||
else
|
||||
controller.setProblem(null);
|
||||
}//GEN-LAST:event_txtModpackLocationCaretUpdate
|
||||
|
||||
private void chkSaveItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_chkSaveItemStateChanged
|
||||
wizardData.put(KEY_SAVE, chkSave.isSelected());
|
||||
}//GEN-LAST:event_chkSaveItemStateChanged
|
||||
|
||||
private void cboGameVersionItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboGameVersionItemStateChanged
|
||||
wizardData.put(KEY_GAME_VERSION, cboGameVersion.getSelectedItem());
|
||||
}//GEN-LAST:event_cboGameVersionItemStateChanged
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JComboBox<String> cboGameVersion;
|
||||
private javax.swing.JButton cboModpackLocation;
|
||||
private javax.swing.JCheckBox chkSave;
|
||||
private javax.swing.JLabel lblGameVersion;
|
||||
private javax.swing.JLabel lblModpackLocation;
|
||||
private javax.swing.JTextField txtModpackLocation;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.launcher.views.modpack;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftProvider;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardBranchController;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardController;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardPanelProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class ModpackWizard extends WizardBranchController {
|
||||
|
||||
public ModpackWizard(IMinecraftProvider provider) {
|
||||
super(new WizardPanelProvider("Modpack Wizard", new String[] { "Settings" }, new String[] { "Select location, version and allow version" }) {
|
||||
|
||||
@Override
|
||||
protected JComponent createPanel(WizardController controller, String id, Map settings) {
|
||||
switch (indexOfStep(id)) {
|
||||
case 0:
|
||||
String[] s = new String[provider.getVersionCount()];
|
||||
Iterator<MinecraftVersion> it = provider.getVersions().iterator();
|
||||
for (int i = 0; i < s.length; i++)
|
||||
s[i] = it.next().id;
|
||||
return new ModpackInitializationPanel(controller, settings, s);
|
||||
default:
|
||||
throw new IllegalArgumentException(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WizardPanelProvider getPanelProviderForStep(String step, Map settings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import java.util.ResourceBundle;
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import java.util.Map;
|
||||
import rx.Observable;
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
|
@ -17,6 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
|
@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils;
|
||||
package org.jackhuang.hellominecraft.utils.logging;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.logging.logger.Logger;
|
||||
|
@ -28,6 +28,7 @@ import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
import org.jackhuang.hellominecraft.utils.functions.Predicate;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import org.jackhuang.hellominecraft.utils.functions.BiFunction;
|
||||
|
||||
/**
|
||||
* 文件压缩/解压类
|
||||
@ -37,20 +38,21 @@ import java.util.zip.ZipInputStream;
|
||||
public class Compressor {
|
||||
|
||||
public static void zip(String sourceDir, String zipFile) throws IOException {
|
||||
zip(new File(sourceDir), new File(zipFile));
|
||||
zip(new File(sourceDir), new File(zipFile), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能:把 sourceDir 目录下的所有文件进行 zip 格式的压缩,保存为指定 zip 文件
|
||||
*
|
||||
* @param sourceDir 源文件夹
|
||||
* @param zipFile 压缩生成的zip文件路径。
|
||||
* @param sourceDir 源文件夹
|
||||
* @param zipFile 压缩生成的zip文件路径。
|
||||
* @param pathNameCallback callback(pathName, isDirectory) returns your
|
||||
* modified pathName
|
||||
*
|
||||
* @throws java.io.IOException 压缩失败或无法读取
|
||||
*/
|
||||
public static void zip(File sourceDir, File zipFile) throws IOException {
|
||||
FileOutputStream os;
|
||||
os = new FileOutputStream(zipFile);
|
||||
public static void zip(File sourceDir, File zipFile, BiFunction<String, Boolean, String> pathNameCallback) throws IOException {
|
||||
FileOutputStream os = new FileOutputStream(zipFile);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(os);
|
||||
try (ZipOutputStream zos = new ZipOutputStream(bos)) {
|
||||
String basePath;
|
||||
@ -58,22 +60,46 @@ public class Compressor {
|
||||
basePath = sourceDir.getPath();
|
||||
else//直接压缩单个文件时,取父目录
|
||||
basePath = sourceDir.getParent();
|
||||
zipFile(sourceDir, basePath, zos);
|
||||
zipFile(sourceDir, basePath, zos, pathNameCallback);
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能:把 sourceDir 目录下的所有文件进行 zip 格式的压缩,保存为指定 zip 文件
|
||||
*
|
||||
* @param sourceDir 源文件夹
|
||||
* @param zipFile 压缩生成的zip文件路径。
|
||||
* @param pathNameCallback callback(pathName, isDirectory) returns your
|
||||
* modified pathName
|
||||
*
|
||||
* @throws java.io.IOException 压缩失败或无法读取
|
||||
*/
|
||||
public static ZipOutputStream zipContinuing(File sourceDir, File zipFile, BiFunction<String, Boolean, String> pathNameCallback) throws IOException {
|
||||
FileOutputStream os = new FileOutputStream(zipFile);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(os);
|
||||
try (ZipOutputStream zos = new ZipOutputStream(bos)) {
|
||||
String basePath;
|
||||
if (sourceDir.isDirectory())
|
||||
basePath = sourceDir.getPath();
|
||||
else//直接压缩单个文件时,取父目录
|
||||
basePath = sourceDir.getParent();
|
||||
zipFile(sourceDir, basePath, zos, pathNameCallback);
|
||||
return zos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件压缩成zip文件
|
||||
*
|
||||
* @param source zip文件路径
|
||||
* @param basePath 待压缩文件根目录
|
||||
* @param zos zip文件的os
|
||||
*
|
||||
* @param callback if the file is allowed to be zipped.
|
||||
* @param source zip文件路径
|
||||
* @param basePath 待压缩文件根目录
|
||||
* @param zos zip文件的os
|
||||
* @param pathNameCallback callback(pathName, isDirectory) returns your
|
||||
* modified pathName, null if you dont want this file zipped
|
||||
*/
|
||||
private static void zipFile(File source, String basePath,
|
||||
ZipOutputStream zos) throws IOException {
|
||||
ZipOutputStream zos, BiFunction<String, Boolean, String> pathNameCallback) throws IOException {
|
||||
File[] files;
|
||||
if (source.isDirectory())
|
||||
files = source.listFiles();
|
||||
@ -88,10 +114,18 @@ public class Compressor {
|
||||
if (file.isDirectory()) {
|
||||
pathName = file.getPath().substring(basePath.length() + 1)
|
||||
+ "/";
|
||||
if (pathNameCallback != null)
|
||||
pathName = pathNameCallback.apply(pathName, true);
|
||||
if (pathName == null)
|
||||
continue;
|
||||
zos.putNextEntry(new ZipEntry(pathName));
|
||||
zipFile(file, basePath, zos);
|
||||
zipFile(file, basePath, zos, pathNameCallback);
|
||||
} else {
|
||||
pathName = file.getPath().substring(basePath.length() + 1);
|
||||
if (pathNameCallback != null)
|
||||
pathName = pathNameCallback.apply(pathName, true);
|
||||
if (pathName == null)
|
||||
continue;
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
zos.putNextEntry(new ZipEntry(pathName));
|
||||
|
@ -27,7 +27,7 @@ import java.io.OutputStream;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.NetUtils;
|
||||
|
||||
/**
|
||||
@ -321,6 +321,13 @@ public class FileUtils {
|
||||
return filename.substring(index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file name without extensions.
|
||||
*
|
||||
* @param filename
|
||||
*
|
||||
* @return the file name without extensions
|
||||
*/
|
||||
public static String getBaseName(String filename) {
|
||||
return removeExtension(getName(filename));
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -23,7 +23,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -21,7 +21,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.StringTokenizer;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ package org.jackhuang.hellominecraft.utils.system;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.EventHandler;
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils.system;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
import org.jackhuang.hellominecraft.utils.functions.BiFunction;
|
||||
|
||||
/**
|
||||
* Non thread-safe
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class ZipEngine {
|
||||
|
||||
byte[] buf = new byte[1024];
|
||||
ZipOutputStream zos;
|
||||
|
||||
public ZipEngine(File f) throws IOException {
|
||||
FileOutputStream os = new FileOutputStream(f);
|
||||
zos = new ZipOutputStream(new BufferedOutputStream(os));
|
||||
}
|
||||
|
||||
public void closeFile() throws IOException {
|
||||
zos.closeEntry();
|
||||
zos.close();
|
||||
}
|
||||
|
||||
public void putDirectory(String sourceDir) throws IOException {
|
||||
putDirectory(new File(sourceDir), null);
|
||||
}
|
||||
|
||||
public void putDirectory(File sourceDir) throws IOException {
|
||||
putDirectory(sourceDir, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能:把 sourceDir 目录下的所有文件进行 zip 格式的压缩,保存为指定 zip 文件
|
||||
*
|
||||
* @param sourceDir 源文件夹
|
||||
* @param zipFile 压缩生成的zip文件路径。
|
||||
* @param pathNameCallback callback(pathName, isDirectory) returns your
|
||||
* modified pathName
|
||||
*
|
||||
* @throws java.io.IOException 压缩失败或无法读取
|
||||
*/
|
||||
public void putDirectory(File sourceDir, BiFunction<String, Boolean, String> pathNameCallback) throws IOException {
|
||||
putDirectoryImpl(sourceDir, sourceDir.isDirectory() ? sourceDir.getPath() : sourceDir.getParent(), pathNameCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件压缩成zip文件
|
||||
*
|
||||
* @param source zip文件路径
|
||||
* @param basePath 待压缩文件根目录
|
||||
* @param zos zip文件的os
|
||||
* @param pathNameCallback callback(pathName, isDirectory) returns your
|
||||
* modified pathName, null if you dont want this file zipped
|
||||
*/
|
||||
private void putDirectoryImpl(File source, String basePath, BiFunction<String, Boolean, String> pathNameCallback) throws IOException {
|
||||
File[] files;
|
||||
if (source.isDirectory())
|
||||
files = source.listFiles();
|
||||
else {
|
||||
files = new File[1];
|
||||
files[0] = source;
|
||||
}
|
||||
String pathName;//存相对路径(相对于待压缩的根目录)
|
||||
for (File file : files)
|
||||
if (file.isDirectory()) {
|
||||
pathName = file.getPath().substring(basePath.length() + 1)
|
||||
+ "/";
|
||||
if (pathNameCallback != null)
|
||||
pathName = pathNameCallback.apply(pathName, true);
|
||||
if (pathName == null)
|
||||
continue;
|
||||
zos.putNextEntry(new ZipEntry(pathName));
|
||||
putDirectoryImpl(file, basePath, pathNameCallback);
|
||||
} else {
|
||||
pathName = file.getPath().substring(basePath.length() + 1);
|
||||
if (pathNameCallback != null)
|
||||
pathName = pathNameCallback.apply(pathName, true);
|
||||
if (pathName == null)
|
||||
continue;
|
||||
putFile(file, pathName);
|
||||
}
|
||||
}
|
||||
|
||||
public void putFile(File file, String pathName) throws IOException {
|
||||
putStream(new FileInputStream(file), pathName);
|
||||
}
|
||||
|
||||
public void putStream(InputStream is, String pathName) throws IOException {
|
||||
int length;
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
zos.putNextEntry(new ZipEntry(pathName));
|
||||
while ((length = bis.read(buf)) > 0)
|
||||
zos.write(buf, 0, length);
|
||||
}
|
||||
|
||||
public void putTextFile(String text, String pathName) throws IOException {
|
||||
putTextFile(text, "UTF-8", pathName);
|
||||
}
|
||||
|
||||
public void putTextFile(String text, String encoding, String pathName) throws IOException {
|
||||
putStream(new ByteArrayInputStream(text.getBytes(encoding)), pathName);
|
||||
}
|
||||
|
||||
}
|
@ -19,7 +19,7 @@ package org.jackhuang.hellominecraft.utils.tasks;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -24,7 +24,7 @@ import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -21,7 +21,7 @@ import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
|
@ -25,7 +25,7 @@ import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.Task;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.communication.PreviousResult;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.communication.PreviousResultRegistrar;
|
||||
|
@ -21,7 +21,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskInfo;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.communication.PreviousResult;
|
||||
import org.jackhuang.hellominecraft.utils.EventHandler;
|
||||
|
@ -22,7 +22,7 @@ import java.io.IOException;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.ArrayUtils;
|
||||
import org.jackhuang.hellominecraft.utils.NetUtils;
|
||||
|
||||
@ -151,7 +151,7 @@ public class MinecraftVersionRequest {
|
||||
|
||||
public static MinecraftVersionRequest minecraftVersion(File file) {
|
||||
MinecraftVersionRequest r = new MinecraftVersionRequest();
|
||||
if (!file.exists()) {
|
||||
if (file == null || !file.exists()) {
|
||||
r.type = MinecraftVersionRequest.NOT_FOUND;
|
||||
return r;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import javax.swing.text.Document;
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.Level;
|
||||
import org.jackhuang.hellominecraft.utils.functions.NonFunction;
|
||||
import org.jackhuang.hellominecraft.utils.DoubleOutputStream;
|
||||
|
@ -34,7 +34,7 @@ import javax.swing.JTable;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.functions.NonFunction;
|
||||
|
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* The contents of this file are subject to the terms of the Common Development
|
||||
* and Distribution License (the License). You may not use this file except in
|
||||
* compliance with the License.
|
||||
* You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
* or http://www.netbeans.org/cddl.txt.
|
||||
* When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
* and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
* If applicable, add the following below the CDDL Header, with the fields
|
||||
* enclosed by brackets [] replaced by your own identifying information:
|
||||
* "Portions Copyrighted [year] [name of copyright owner]"
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.api;
|
||||
|
||||
import java.awt.Container;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import javax.swing.Action;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.displayer.WizardDisplayerImpl;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.Wizard;
|
||||
|
||||
/**
|
||||
* <h2>Displaying Wizards</h2>
|
||||
* Factory which can display a <code>Wizard</code> in a dialog onscreen or in an
|
||||
* ad-hoc
|
||||
* container. Usage:
|
||||
* <pre>
|
||||
* Wizard wizard = WizardPage.createWizard (new Class[] {WizardPageSubclass1.class,
|
||||
* WizardPageSubclass2.class, WizardPageSubclass3.class},
|
||||
* new MyWizardResultProducer();
|
||||
* WizardDisplayer.showWizard (wizard);
|
||||
* </pre>
|
||||
* Alternately you can implement <code>WizardPanelProvider</code> instead of
|
||||
* <code>WizardPage</code> to provide the panels of the wizard.
|
||||
* <p>
|
||||
* To use a <code>Wizard</code> in a <code>JInternalFrame</code> or similar, use
|
||||
* <code>WizardDisplayer.installInContainer()</code>. You will need to implement
|
||||
* <code>WizardResultReceiver</code> which will me notified when the wizard
|
||||
* is finished or cancelled, to close the internal frame or whatever UI is
|
||||
* showing the wizard.
|
||||
* <p>
|
||||
* <h2>Customizing the default implementation</h2>
|
||||
* The image on the left panel of the default implementation can be customized
|
||||
* in the following ways:
|
||||
* <ul>
|
||||
* <li>Put an instance of <code>java.awt.image.BufferedImage</code> into
|
||||
* UIManager with the key <code>wizard.sidebar.image</code>, i.e.
|
||||
* <pre>
|
||||
* BufferedImage img = ImageIO.read (getClass().getResource ("MySideImage.png");
|
||||
* UIManager.put ("wizard.sidebar.image", img);
|
||||
* </pre>
|
||||
* </li>
|
||||
* <li>Use the system property <code>wizard.sidebar.image</code> to set a path
|
||||
* within a JAR on the classpath to the image. The image must be visible
|
||||
* to the classloader which loads <code>WizardDisplayer</code>, so this
|
||||
* may not work in environments which manage the classpath. i.e.
|
||||
* <pre>
|
||||
* System.setProperty ("wizard.sidebar.image", "com/foo/myapp/MySideImage.png");
|
||||
* </pre>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Providing a custom WizardDisplayer:</h2>
|
||||
* The implementation of <code>WizardDisplayer</code> is pluggable. While the
|
||||
* default implementation should be adequate for most cases, it is possible
|
||||
* that in some cases one might want to completely replace the UI, buttons,
|
||||
* etc. with custom UI code. To do that:
|
||||
* <ul>
|
||||
* <li>If the NetBeans Lookup library (<code>org.openide.util.Lookup</code>
|
||||
* is on the classpath, the default implementation will be found in
|
||||
* the default lookup (i.e. META-INF/services, same as
|
||||
* JDK 6's ServiceLoader)</li>
|
||||
* <li>If Lookup is not available or not found, <code>WizardDisplayer</code>
|
||||
* will check the system
|
||||
* property <code>WizardDisplayer.default</code> for a fully qualified
|
||||
* class name of a subclass of <code>WizardDisplayer</code>.
|
||||
* </li>
|
||||
* <li>If no other implementation of <code>WizardDisplayer</code> is found
|
||||
* by the above methods, the default implementation contained in this
|
||||
* library will be used.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public abstract class WizardDisplayer {
|
||||
|
||||
protected WizardDisplayer() {
|
||||
}
|
||||
private static final String SYSPROP_KEY = "WizardDisplayer.default";
|
||||
|
||||
/**
|
||||
* Display a wizard in a dialog, using the default implementation of
|
||||
* WizardDisplayer.
|
||||
*
|
||||
* @param wizard The wizard to show. Must not be null
|
||||
* @param rect The rectangle on screen for the wizard, may be
|
||||
* null for default size
|
||||
* @param help An action to invoke if the user presses the help
|
||||
* button
|
||||
* @param initialProperties are the initial values for properties to be
|
||||
* shown
|
||||
* and entered in the wizard. May be null.
|
||||
*/
|
||||
public static Object showWizard(Wizard wizard, Rectangle rect, Action help, Map initialProperties) {
|
||||
// assert nonBuggyWizard (wizard);
|
||||
// validate it
|
||||
nonBuggyWizard(wizard);
|
||||
|
||||
WizardDisplayer defaultInstance = getDefault();
|
||||
|
||||
return defaultInstance.show(wizard, rect, help, initialProperties);
|
||||
}
|
||||
|
||||
private static WizardDisplayer getDefault() {
|
||||
return new WizardDisplayerImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a wizard with default window placement and no Help button
|
||||
*/
|
||||
public static Object showWizard(Wizard wizard) {
|
||||
return showWizard(wizard, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a wizard with default window placement, showing the help button,
|
||||
* which will invoke the passed action.
|
||||
*
|
||||
* @param wizard The wizard to show
|
||||
* @param help An action to invoke if the user presses the help button
|
||||
*
|
||||
* @return The result of Wizard.finish()
|
||||
*/
|
||||
public static Object showWizard(Wizard wizard, Action help) {
|
||||
return showWizard(wizard, null, help, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a wizard in the passed location on screen with no help button
|
||||
*
|
||||
* @param wizard The wizard to show
|
||||
* @param r The rectangle on screen for the wizard
|
||||
*
|
||||
* @return The result of Wizard.finish()
|
||||
*/
|
||||
public static Object showWizard(Wizard wizard, Rectangle r) {
|
||||
return showWizard(wizard, r, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a wizard.
|
||||
*
|
||||
* @param wizard the Wizard to show
|
||||
* @param r the bounding rectangle for the wizard dialog on
|
||||
* screen, null means "computed from first panel
|
||||
* size"
|
||||
* @param help An action to be called if the Help button is
|
||||
* pressed
|
||||
* @param initialProperties are used to set initial values for screens
|
||||
* within the wizard.
|
||||
* This may be null.
|
||||
*
|
||||
* @return Whatever object the wizard returns from its <code>finish()</code>
|
||||
* method, if the Wizard was completed by the user.
|
||||
*/
|
||||
protected abstract Object show(Wizard wizard, Rectangle r, Action help, Map initialProperties);
|
||||
|
||||
/**
|
||||
* Install a panel representing a Wizard in a user-supplied container
|
||||
* with a user-supplied layout constraint.
|
||||
*
|
||||
* @param c The container the wizard panel should be added
|
||||
* to. May not
|
||||
* be null.
|
||||
* @param layoutConstraint The argument to use when adding the wizard's
|
||||
* ui component to the container. May be null.
|
||||
* @param helpAction An action that should be invoked when the help
|
||||
* button
|
||||
* is clicked (if null, no help button will be displayed)
|
||||
* @param initialProperties A set of properties that should be pre-set upon
|
||||
* entering the wizard. May be null.
|
||||
* @param receiver An object which will be called when the Finish
|
||||
* or
|
||||
* Cancel buttons are pressed. May not be null.
|
||||
*/
|
||||
public static void installInContainer(Container c, Object layoutConstraint,
|
||||
Wizard awizard,
|
||||
Action helpAction, Map initialProperties,
|
||||
WizardResultReceiver receiver) {
|
||||
getDefault().install(c, layoutConstraint, awizard, helpAction,
|
||||
initialProperties, receiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance implementation of installInContainer().
|
||||
*/
|
||||
protected abstract void install(Container c, Object layoutConstraint,
|
||||
Wizard awizard, Action helpAction, Map initialProperties,
|
||||
WizardResultReceiver receiver);
|
||||
|
||||
private static boolean nonBuggyWizard(Wizard wizard) {
|
||||
String[] s = wizard.getAllSteps();
|
||||
// assert new HashSet(Arrays.asList(s)).size() == s.length;
|
||||
// for JDK 1.4.2: replace assert with runtime exception
|
||||
if (new HashSet(Arrays.asList(s)).size() != s.length)
|
||||
throw new RuntimeException("steps are duplicated: " + Arrays.asList(s));
|
||||
if (s.length == 1 && Wizard.UNDETERMINED_STEP.equals(s[0]))
|
||||
// assert false : "Only ID may not be UNDETERMINED_ID"; //NOI18N
|
||||
throw new RuntimeException("Only ID may not be UNDETERMINED_ID");
|
||||
for (int i = 0; i < s.length; i++)
|
||||
if (Wizard.UNDETERMINED_STEP.equals(s[i]) && i != s.length - 1)
|
||||
// assert false : "UNDETERMINED_ID may only be last element in" + //NOI18N
|
||||
// " ids array " + Arrays.asList(s); //NOI18N
|
||||
throw new RuntimeException("UNDETERMINED_ID may only be last element in"
|
||||
+ //NOI18N
|
||||
" ids array " + Arrays.asList(s)); //NOI18N)
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* The contents of this file are subject to the terms of the Common Development
|
||||
* and Distribution License (the License). You may not use this file except in
|
||||
* compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
* or http://www.netbeans.org/cddl.txt.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
* and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
* If applicable, add the following below the CDDL Header, with the fields
|
||||
* enclosed by brackets [] replaced by your own identifying information:
|
||||
* "Portions Copyrighted [year] [name of copyright owner]"
|
||||
*
|
||||
* The Original Software is NetBeans. The Initial Developer of the Original
|
||||
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
|
||||
* Microsystems, Inc. All Rights Reserved.
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Object which is called when the wizard is completed or cancelled. Only
|
||||
* useful if you want to call WizardDisplayer.installInContainer() to install
|
||||
* a wizard in a custom container (such as a JInternalDialog) - this class
|
||||
* is a callback to notify the caller that the Finish or Cancel button has
|
||||
* been pressed.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public interface WizardResultReceiver {
|
||||
/**
|
||||
* Called when the wizard has been completed, providing whatever object
|
||||
* the wizard created as its result.
|
||||
* @param wizardResult The object created by Wizard.finish()
|
||||
*/
|
||||
void finished (Object wizardResult);
|
||||
/**
|
||||
* Called when the wizard has been cancelled.
|
||||
* @param settings The settings that were gathered thus far in the
|
||||
* wizard
|
||||
*/
|
||||
void cancelled (Map settings);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.api.displayer;
|
||||
|
||||
import java.awt.Container;
|
||||
|
||||
/**
|
||||
* Interface for providing the UI for instructions displayed in a wizard.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public interface InstructionsPanel {
|
||||
/**
|
||||
* Get the component that will actually display the instructions.
|
||||
* Note that this component should have a layout manager that can position
|
||||
* a single component in a location that will not obscure the instructions,
|
||||
* for showing a progress bar.
|
||||
*
|
||||
* This method should always return the same component.
|
||||
*
|
||||
* @return A component that can listen to the wizard, display the steps
|
||||
* in that wizard, notice and update when they change, and optionally
|
||||
* highlight the current step.
|
||||
*/
|
||||
public Container getComponent();
|
||||
/**
|
||||
* Set whether or not the panel is in the summary page at the end of a
|
||||
* wizard (in which case it should add a "summary" item to its
|
||||
* list of steps and highlight that).
|
||||
*
|
||||
* @param val Whether or not the wizard has navigated to a summary page.
|
||||
*/
|
||||
public void setInSummaryPage (boolean val);
|
||||
}
|
@ -0,0 +1,652 @@
|
||||
/*
|
||||
* NavButtonManager.java created on Dec 9, 2006
|
||||
*
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.api.displayer;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.WizardDisplayer;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.modules.MergeMap;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.modules.NbBridge;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.DeferredWizardResult;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.Summary;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.Wizard;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardException;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardObserver;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardPage;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardPanel;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardPanelNavResult;
|
||||
|
||||
/**
|
||||
* Manage the button state and interaction with the wizard.
|
||||
* <p>
|
||||
* <b><i><font color="red">This class is NOT AN API CLASS. There is no
|
||||
* commitment that it will remain backward compatible or even exist in the
|
||||
* future. The API of this library is in the packages
|
||||
* <code>org.netbeans.api.wizard</code>
|
||||
* and <code>org.netbeans.spi.wizard</code></font></i></b>.
|
||||
*
|
||||
* @author stanley@stanleyknutson.com
|
||||
*/
|
||||
public class NavButtonManager implements ActionListener {
|
||||
|
||||
static final String NAME_NEXT = "next";
|
||||
|
||||
static final String NAME_PREV = "prev";
|
||||
|
||||
static final String NAME_FINISH = "finish";
|
||||
|
||||
static final String NAME_CANCEL = "cancel";
|
||||
|
||||
static final String NAME_CLOSE = "close";
|
||||
|
||||
/**
|
||||
* Prefix for the name in deferredStatus
|
||||
*/
|
||||
static final String DEFERRED_FAILED = "FAILED_";
|
||||
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(NavButtonManager.class.getName());
|
||||
|
||||
JButton next = null;
|
||||
|
||||
JButton prev = null;
|
||||
|
||||
JButton finish = null;
|
||||
|
||||
JButton cancel = null;
|
||||
|
||||
JButton help = null;
|
||||
|
||||
JPanel buttons = null;
|
||||
|
||||
// container can be JDialog or JFrame
|
||||
private Window window;
|
||||
|
||||
WizardDisplayerImpl parent;
|
||||
|
||||
String closeString = NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Close"); // NOI18N
|
||||
|
||||
boolean suppressMessageDialog = false;
|
||||
/**
|
||||
* Deferred status of not null means we are waiting for a deferred result to
|
||||
* be completed as part of the handling for some button Value of the
|
||||
* deferredStatus is the NAME_* constant that triggered the deferred
|
||||
* operation.
|
||||
*/
|
||||
String deferredStatus = null;
|
||||
|
||||
NavButtonManager(WizardDisplayerImpl impl) {
|
||||
parent = impl;
|
||||
}
|
||||
|
||||
protected void buildButtons(Action helpAction) {
|
||||
|
||||
next = new JButton(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Next_>")); // NOI18N
|
||||
next.setName(NAME_NEXT);
|
||||
next.setMnemonic(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Next_mnemonic").charAt(0)); // NOI18N
|
||||
|
||||
prev = new JButton(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "<_Prev")); // NOI18N
|
||||
prev.setName(NAME_PREV);
|
||||
prev.setMnemonic(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Prev_mnemonic").charAt(0)); // NOI18N
|
||||
|
||||
finish = new JButton(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Finish")); // NOI18N
|
||||
finish.setName(NAME_FINISH);
|
||||
finish.setMnemonic(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Finish_mnemonic").charAt(0)); // NOI18N
|
||||
|
||||
cancel = new JButton(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Cancel")); // NOI18N
|
||||
cancel.setName(NAME_CANCEL);
|
||||
cancel.setMnemonic(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Cancel_mnemonic").charAt(0)); // NOI18N
|
||||
|
||||
help = new JButton();
|
||||
if (helpAction != null) {
|
||||
help.setAction(helpAction);
|
||||
if (helpAction.getValue(Action.NAME) == null) {
|
||||
help.setText(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Help")); // NOI18N
|
||||
help.setMnemonic(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Help_mnemonic").charAt(0)); // NOI18N
|
||||
}
|
||||
} else {
|
||||
help.setText(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Help")); // NOI18N
|
||||
help.setMnemonic(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Help_mnemonic").charAt(0)); // NOI18N
|
||||
}
|
||||
|
||||
next.setDefaultCapable(true);
|
||||
prev.setDefaultCapable(true);
|
||||
|
||||
help.setVisible(helpAction != null);
|
||||
|
||||
// Use standard default-button-last order on Aqua L&F
|
||||
final boolean aqua = "Aqua".equals(UIManager.getLookAndFeel().getID()); // NOI18N
|
||||
|
||||
buttons = new JPanel() {
|
||||
public void doLayout() {
|
||||
Insets ins = getInsets();
|
||||
JButton b = aqua ? finish : cancel;
|
||||
|
||||
Dimension n = b.getPreferredSize();
|
||||
int y = ((getHeight() - (ins.top + ins.bottom)) / 2) - (n.height / 2);
|
||||
int gap = 5;
|
||||
int x = getWidth() - (12 + ins.right + n.width);
|
||||
|
||||
b.setBounds(x, y, n.width, n.height);
|
||||
|
||||
b = aqua ? next : finish;
|
||||
n = b.getPreferredSize();
|
||||
x -= n.width + gap;
|
||||
b.setBounds(x, y, n.width, n.height);
|
||||
|
||||
b = aqua ? prev : next;
|
||||
n = b.getPreferredSize();
|
||||
x -= n.width + gap;
|
||||
b.setBounds(x, y, n.width, n.height);
|
||||
|
||||
b = aqua ? cancel : prev;
|
||||
n = b.getPreferredSize();
|
||||
x -= n.width + gap;
|
||||
b.setBounds(x, y, n.width, n.height);
|
||||
|
||||
b = help;
|
||||
n = b.getPreferredSize();
|
||||
x -= n.width + (gap * 2);
|
||||
b.setBounds(x, y, n.width, n.height);
|
||||
}
|
||||
};
|
||||
buttons.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, UIManager
|
||||
.getColor("textText"))); // NOI18N
|
||||
|
||||
buttons.add(prev);
|
||||
buttons.add(next);
|
||||
buttons.add(finish);
|
||||
buttons.add(cancel);
|
||||
buttons.add(help);
|
||||
|
||||
next.addActionListener(this);
|
||||
prev.addActionListener(this);
|
||||
finish.addActionListener(this);
|
||||
cancel.addActionListener(this);
|
||||
}
|
||||
|
||||
void connectListener() {
|
||||
NavWizardObserver l = new NavWizardObserver();
|
||||
Wizard wizard = parent.getWizard();
|
||||
l.stepsChanged(wizard);
|
||||
l.navigabilityChanged(wizard);
|
||||
l.selectionChanged(wizard);
|
||||
wizard.addWizardObserver(l);
|
||||
}
|
||||
|
||||
private void configureNavigationButtons(final Wizard wizard, final JButton prev,
|
||||
final JButton next, final JButton finish) {
|
||||
final String nextStep = wizard.getNextStep();
|
||||
final int fwdNavMode = wizard.getForwardNavigationMode();
|
||||
|
||||
WizardDisplayerImpl.checkLegalNavMode(fwdNavMode);
|
||||
|
||||
final String problem = wizard.getProblem();
|
||||
|
||||
final boolean isDeferredResult = deferredStatus != null;
|
||||
|
||||
final boolean canContinue = (fwdNavMode & Wizard.MODE_CAN_CONTINUE) != 0 && !isDeferredResult;
|
||||
final boolean canFinish = (fwdNavMode & Wizard.MODE_CAN_FINISH) != 0 && !isDeferredResult;
|
||||
final boolean enableFinish = canFinish && problem == null && !isDeferredResult;
|
||||
final boolean enableNext = nextStep != null && canContinue && problem == null && !isDeferredResult;
|
||||
final boolean enablePrevious = wizard.getPreviousStep() != null && !isDeferredResult;
|
||||
|
||||
final Runnable runnable = new Runnable() {
|
||||
|
||||
public void run() {
|
||||
next.setEnabled(enableNext);
|
||||
prev.setEnabled(enablePrevious);
|
||||
finish.setEnabled(enableFinish);
|
||||
JRootPane root = next.getRootPane();
|
||||
if (root != null)
|
||||
if (next.isEnabled())
|
||||
root.setDefaultButton(next);
|
||||
else if (finish.isEnabled())
|
||||
root.setDefaultButton(finish);
|
||||
else if (prev.isEnabled())
|
||||
root.setDefaultButton(prev);
|
||||
else
|
||||
root.setDefaultButton(null);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
if (EventQueue.isDispatchThread())
|
||||
runnable.run();
|
||||
else
|
||||
EventQueue.invokeLater(runnable);
|
||||
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
|
||||
JButton button = (JButton) event.getSource();
|
||||
|
||||
String name = button.getName();
|
||||
|
||||
if (NAME_CANCEL.equals(name)) {
|
||||
processCancel(event, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// probably an error status
|
||||
if (deferredStatus != null) {
|
||||
deferredResultFinished(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NAME_NEXT.equals(name))
|
||||
processNext();
|
||||
else if (NAME_PREV.equals(name))
|
||||
processPrev();
|
||||
else if (NAME_FINISH.equals(name))
|
||||
processFinish(event);
|
||||
else if (NAME_CLOSE.equals(name))
|
||||
processClose(event);
|
||||
// else ignore, we don't know it
|
||||
|
||||
parent.updateProblem();
|
||||
}
|
||||
|
||||
void deferredResultFailed(final boolean canGoBack) {
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
if (!canGoBack)
|
||||
getCancel().setText(closeString);
|
||||
getPrev().setEnabled(true);
|
||||
getNext().setEnabled(false);
|
||||
getCancel().setEnabled(true);
|
||||
getFinish().setEnabled(false);
|
||||
|
||||
if (NAME_CLOSE.equals(deferredStatus)) {
|
||||
// no action
|
||||
} else
|
||||
deferredStatus = DEFERRED_FAILED + deferredStatus;
|
||||
}
|
||||
};
|
||||
if (EventQueue.isDispatchThread())
|
||||
runnable.run();
|
||||
else
|
||||
EventQueue.invokeLater(runnable);
|
||||
}
|
||||
|
||||
void deferredResultFinished(Object o) {
|
||||
String name = deferredStatus;
|
||||
deferredStatus = null;
|
||||
|
||||
configureNavigationButtons(parent.getWizard(), prev, next, finish);
|
||||
|
||||
if (name.startsWith(DEFERRED_FAILED)) {
|
||||
// Cancel clicked after a deferred failure
|
||||
if (o instanceof ActionEvent) {
|
||||
JButton button = (JButton) ((ActionEvent) o).getSource();
|
||||
name = button.getName();
|
||||
if (NAME_CANCEL.equals(name)) {
|
||||
processCancel(o instanceof ActionEvent ? (ActionEvent) o
|
||||
: null, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// in failed state, so we always reload the current step's screen
|
||||
String currentStep = parent.getCurrentStep();
|
||||
parent.navigateTo(currentStep);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NAME_NEXT.equals(name))
|
||||
processNextProceed(o);
|
||||
else if (NAME_PREV.equals(name))
|
||||
processPrevProceed(o);
|
||||
else if (NAME_CANCEL.equals(name))
|
||||
processCancel(o instanceof ActionEvent ? (ActionEvent) o
|
||||
: null, false);
|
||||
else if (NAME_FINISH.equals(name))
|
||||
// allowFinish on the "down" click of the finish button
|
||||
processFinishProceed(o);
|
||||
else if (NAME_CLOSE.equals(name)) {
|
||||
// the "up" click of the finish button: wizard.finish was a deferred result
|
||||
Window dlg = getWindow();
|
||||
dlg.setVisible(false);
|
||||
dlg.dispose();
|
||||
}
|
||||
// else ignore, we don't know it
|
||||
|
||||
parent.updateProblem();
|
||||
}
|
||||
|
||||
protected void processNext() {
|
||||
WizardPanel panel = parent.getCurrentWizardPanel();
|
||||
Wizard wizard = parent.getWizard();
|
||||
MergeMap settings = parent.getSettings();
|
||||
|
||||
WizardPanelNavResult proceed = WizardPanelNavResult.PROCEED;
|
||||
if (panel != null) {
|
||||
String currentStep = parent.getCurrentStep();
|
||||
proceed = panel.allowNext(currentStep, settings, wizard);
|
||||
if (proceed.isDeferredComputation()) {
|
||||
deferredStatus = NAME_NEXT;
|
||||
configureNavigationButtons(wizard, prev, next, finish);
|
||||
parent.handleDeferredWizardResult(proceed, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
processNextProceed(proceed);
|
||||
}
|
||||
|
||||
protected void processNextProceed(Object result) {
|
||||
Wizard wizard = parent.getWizard();
|
||||
MergeMap settings = parent.getSettings();
|
||||
|
||||
if (WizardPanelNavResult.REMAIN_ON_PAGE.equals(result))
|
||||
// leave current panel displayed, assume problem is being shown
|
||||
return;
|
||||
// ignore other results
|
||||
|
||||
String nextId = wizard.getNextStep();
|
||||
settings.push(nextId);
|
||||
parent.navigateTo(nextId);
|
||||
parent.setInSummary(false);
|
||||
}
|
||||
|
||||
protected void processPrev() {
|
||||
WizardPanel panel = parent.getCurrentWizardPanel();
|
||||
Wizard wizard = parent.getWizard();
|
||||
MergeMap settings = parent.getSettings();
|
||||
|
||||
WizardPanelNavResult proceed = WizardPanelNavResult.PROCEED;
|
||||
if (panel != null) {
|
||||
String currentStep = parent.getCurrentStep();
|
||||
proceed = panel.allowBack(currentStep, settings, wizard);
|
||||
if (proceed.isDeferredComputation()) {
|
||||
deferredStatus = NAME_PREV;
|
||||
parent.handleDeferredWizardResult(proceed, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
processPrevProceed(proceed);
|
||||
}
|
||||
|
||||
protected void processPrevProceed(Object result) {
|
||||
Wizard wizard = parent.getWizard();
|
||||
MergeMap settings = parent.getSettings();
|
||||
|
||||
if (WizardPanelNavResult.REMAIN_ON_PAGE.equals(result))
|
||||
// leave current panel displayed, assume problem is being shown
|
||||
return;
|
||||
// ignore other results
|
||||
|
||||
String prevId = wizard.getPreviousStep();
|
||||
settings.popAndCalve();
|
||||
parent.setDeferredResult(null);
|
||||
parent.navigateTo(prevId);
|
||||
parent.setInSummary(false);
|
||||
}
|
||||
|
||||
protected void processFinish(ActionEvent event) {
|
||||
WizardPanel panel = parent.getCurrentWizardPanel();
|
||||
Wizard wizard = parent.getWizard();
|
||||
MergeMap settings = parent.getSettings();
|
||||
|
||||
WizardPanelNavResult proceed = WizardPanelNavResult.PROCEED;
|
||||
if (panel != null) {
|
||||
String currentStep = parent.getCurrentStep();
|
||||
proceed = panel.allowFinish(currentStep, settings, wizard);
|
||||
if (proceed.isDeferredComputation()) {
|
||||
deferredStatus = NAME_FINISH;
|
||||
parent.handleDeferredWizardResult((DeferredWizardResult) proceed, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
processFinishProceed(proceed);
|
||||
}
|
||||
|
||||
protected void processFinishProceed(Object result) {
|
||||
Wizard wizard = parent.getWizard();
|
||||
MergeMap settings = parent.getSettings();
|
||||
|
||||
if (WizardPanelNavResult.REMAIN_ON_PAGE.equals(result))
|
||||
// leave current panel displayed, assume problem is being shown
|
||||
return;
|
||||
try {
|
||||
Object o = wizard.finish(settings);
|
||||
// System.err.println("WIZARD FINISH GOT ME A " + o);
|
||||
|
||||
boolean closeWindow = true;
|
||||
|
||||
if (o instanceof DeferredWizardResult) {
|
||||
final DeferredWizardResult r = (DeferredWizardResult) o;
|
||||
finish.setEnabled(false);
|
||||
cancel.setEnabled(r.canAbort());
|
||||
prev.setEnabled(false);
|
||||
next.setEnabled(false);
|
||||
|
||||
// the button still says "cancel"
|
||||
deferredStatus = NAME_CANCEL;
|
||||
// deferredStatus = NAME_CLOSE;
|
||||
parent.handleDeferredWizardResult(r, true);
|
||||
|
||||
closeWindow = false;
|
||||
} else if (o instanceof Summary) {
|
||||
parent.handleSummary((Summary) o);
|
||||
parent.setWizardResult(((Summary) o).getResult());
|
||||
// setSummaryShowingMode will be called
|
||||
// need to share code with NavProgress.finished code path
|
||||
closeWindow = false;
|
||||
} else
|
||||
parent.setWizardResult(o);
|
||||
|
||||
if (closeWindow)
|
||||
// do cancel processing as well
|
||||
processCancel(null, false);
|
||||
} catch (WizardException we) {
|
||||
if (!suppressMessageDialog)
|
||||
JOptionPane.showMessageDialog(next, we.getLocalizedMessage());
|
||||
String id = we.getStepToReturnTo();
|
||||
String curr = settings.currID();
|
||||
try {
|
||||
while (id != null && !id.equals(curr))
|
||||
curr = settings.popAndCalve();
|
||||
settings.push(id);
|
||||
parent.navigateTo(id);
|
||||
return;
|
||||
} catch (NoSuchElementException ex) {
|
||||
IllegalStateException e = new IllegalStateException("Exception "
|
||||
+ // NOI18N
|
||||
"said to return to " + id + " but no such "
|
||||
+ // NOI18N
|
||||
"step found"); // NOI18N
|
||||
e.initCause(ex);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void processCancel(ActionEvent event, boolean reallyCancel) {
|
||||
DeferredWizardResult deferredResult = parent.getDeferredResult();
|
||||
if (deferredResult != null && deferredResult.canAbort())
|
||||
deferredResult.abort();
|
||||
Wizard wizard = parent.getWizard();
|
||||
MergeMap settings = parent.getSettings();
|
||||
|
||||
// System.err.println("ProcessCancel " + reallyCancel + " receiver " + parent.receiver);
|
||||
boolean closeWindow = false;
|
||||
|
||||
if (reallyCancel && parent.cancel()) {
|
||||
// System.err.println("DO CANCEL");
|
||||
logger.fine("calling wizard cancel method on " + wizard);
|
||||
wizard.cancel(settings);
|
||||
return;
|
||||
}
|
||||
|
||||
closeWindow = reallyCancel ? wizard.cancel(settings) : parent.receiver == null;
|
||||
|
||||
// if we have the event (allowFinish was not deferred) then be very sure to close the proper dialog
|
||||
if (closeWindow) {
|
||||
Window win = event != null ? (Window) ((JComponent) event.getSource()).getTopLevelAncestor()
|
||||
: getWindow();
|
||||
win.setVisible(false);
|
||||
win.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected void processClose(ActionEvent event) {
|
||||
Window win = (Window) ((JComponent) event.getSource()).getTopLevelAncestor();
|
||||
win.setVisible(false);
|
||||
win.dispose();
|
||||
}
|
||||
|
||||
void updateButtons() {
|
||||
Wizard wizard = parent.getWizard();
|
||||
if (!wizard.isBusy())
|
||||
configureNavigationButtons(wizard, prev, next, finish);
|
||||
}
|
||||
|
||||
void setSummaryShowingMode() {
|
||||
next.setEnabled(false);
|
||||
prev.setEnabled(false);
|
||||
cancel.setEnabled(true);
|
||||
finish.setEnabled(false);
|
||||
if (window != null && parent.receiver == null && window instanceof JDialog)
|
||||
((JDialog) window).getRootPane().setDefaultButton(cancel);
|
||||
|
||||
cancel.setText(closeString); // NOI18N
|
||||
cancel.setMnemonic(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/api/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Close_mnemonic").charAt(0)); // NOI18N
|
||||
cancel.setName(NAME_CLOSE);
|
||||
deferredStatus = null; // ?? should summary be different
|
||||
}
|
||||
|
||||
void setWindow(Window dlg) {
|
||||
this.window = dlg;
|
||||
}
|
||||
|
||||
public JPanel getButtons() {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
public JButton getCancel() {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
public String getCloseString() {
|
||||
return closeString;
|
||||
}
|
||||
|
||||
public Window getWindow() {
|
||||
return window;
|
||||
}
|
||||
|
||||
public JButton getFinish() {
|
||||
return finish;
|
||||
}
|
||||
|
||||
public JButton getHelp() {
|
||||
return help;
|
||||
}
|
||||
|
||||
public JButton getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
public WizardDisplayerImpl getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public JButton getPrev() {
|
||||
return prev;
|
||||
}
|
||||
|
||||
public void initializeNavigation() {
|
||||
Wizard wizard = parent.getWizard();
|
||||
prev.setEnabled(false);
|
||||
next.setEnabled(wizard.getNextStep() != null);
|
||||
int fwdNavMode = wizard.getForwardNavigationMode();
|
||||
WizardDisplayerImpl.checkLegalNavMode(fwdNavMode);
|
||||
|
||||
finish.setEnabled((fwdNavMode & Wizard.MODE_CAN_FINISH) != 0);
|
||||
|
||||
connectListener();
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
/**
|
||||
* Listener for wizard changages that affect button state
|
||||
*/
|
||||
class NavWizardObserver implements WizardObserver {
|
||||
|
||||
boolean wasBusy = false;
|
||||
|
||||
public void stepsChanged(Wizard wizard) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public void navigabilityChanged(final Wizard wizard) {
|
||||
final Runnable runnable = new Runnable() {
|
||||
|
||||
public void run() {
|
||||
if (wizard.isBusy()) {
|
||||
next.setEnabled(false);
|
||||
prev.setEnabled(false);
|
||||
finish.setEnabled(false);
|
||||
cancel.setEnabled(false);
|
||||
parent.getOuterPanel().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
wasBusy = true;
|
||||
return;
|
||||
} else if (wasBusy) {
|
||||
cancel.setEnabled(true);
|
||||
parent.getOuterPanel().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
configureNavigationButtons(wizard, prev, next, finish);
|
||||
|
||||
parent.updateProblem();
|
||||
|
||||
}
|
||||
};
|
||||
if (EventQueue.isDispatchThread())
|
||||
runnable.run();
|
||||
else
|
||||
EventQueue.invokeLater(runnable);
|
||||
}
|
||||
|
||||
public void selectionChanged(Wizard wizard) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.api.displayer;
|
||||
|
||||
import java.awt.Container;
|
||||
import java.awt.EventQueue;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.WizardDisplayer;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.modules.InstructionsPanelImpl;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.modules.NbBridge;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.ResultProgressHandle;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.Summary;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardPage;
|
||||
|
||||
/**
|
||||
* Show progress bar for deferred results, with a label showing percent done and
|
||||
* progress bar.
|
||||
*
|
||||
* <p>
|
||||
* <b><i><font color="red">This class is NOT AN API CLASS. There is no
|
||||
* commitment that it will remain backward compatible or even exist in the
|
||||
* future. The API of this library is in the packages
|
||||
* <code>org.netbeans.api.wizard</code>
|
||||
* and <code>org.netbeans.spi.wizard</code></font></i></b>.
|
||||
*
|
||||
* @author stanley@stanleyknutson.com
|
||||
*/
|
||||
public class NavProgress implements ResultProgressHandle {
|
||||
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(NavProgress.class.getName());
|
||||
|
||||
JProgressBar progressBar = new JProgressBar();
|
||||
|
||||
JLabel lbl = new JLabel();
|
||||
|
||||
JLabel busy = new JLabel();
|
||||
|
||||
WizardDisplayerImpl parent;
|
||||
|
||||
String failMessage = null;
|
||||
|
||||
boolean isUseBusy = false;
|
||||
|
||||
Container ipanel = null;
|
||||
|
||||
boolean isInitialized = false;
|
||||
|
||||
/**
|
||||
* isRunning is true until finished or failed is called
|
||||
*/
|
||||
boolean isRunning = true;
|
||||
|
||||
NavProgress(WizardDisplayerImpl impl, boolean useBusy) {
|
||||
this.parent = impl;
|
||||
isUseBusy = useBusy;
|
||||
}
|
||||
|
||||
public void addProgressComponents(Container panel) {
|
||||
panel.add(lbl);
|
||||
if (isUseBusy) {
|
||||
ensureBusyInitialized();
|
||||
panel.add(busy);
|
||||
} else
|
||||
panel.add(progressBar);
|
||||
isInitialized = true;
|
||||
ipanel = panel;
|
||||
}
|
||||
|
||||
public void setProgress(final String description, final int currentStep, final int totalSteps) {
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
lbl.setText(description == null ? " " : description); // NOI18N
|
||||
setProgress(currentStep, totalSteps);
|
||||
}
|
||||
};
|
||||
invoke(r);
|
||||
}
|
||||
|
||||
public void setProgress(final int currentStep, final int totalSteps) {
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
if (totalSteps == -1)
|
||||
progressBar.setIndeterminate(true);
|
||||
else {
|
||||
if (currentStep > totalSteps || currentStep < 0) {
|
||||
if (currentStep == -1 && totalSteps == -1)
|
||||
return;
|
||||
throw new IllegalArgumentException("Bad step values: " // NOI18N
|
||||
+ currentStep + " out of " + totalSteps); // NOI18N
|
||||
}
|
||||
progressBar.setIndeterminate(false);
|
||||
progressBar.setMaximum(totalSteps);
|
||||
progressBar.setValue(currentStep);
|
||||
}
|
||||
|
||||
setUseBusy(false);
|
||||
}
|
||||
};
|
||||
invoke(r);
|
||||
}
|
||||
|
||||
public void setBusy(final String description) {
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
lbl.setText(description == null ? " " : description); // NOI18N
|
||||
|
||||
progressBar.setIndeterminate(true);
|
||||
|
||||
setUseBusy(true);
|
||||
}
|
||||
};
|
||||
invoke(r);
|
||||
}
|
||||
|
||||
protected void setUseBusy(boolean useBusy) {
|
||||
if (isInitialized)
|
||||
if (useBusy && (!isUseBusy)) {
|
||||
ipanel.remove(progressBar);
|
||||
ensureBusyInitialized();
|
||||
ipanel.add(busy);
|
||||
ipanel.invalidate();
|
||||
} else if (!useBusy && isUseBusy) {
|
||||
ipanel.remove(busy);
|
||||
ipanel.add(progressBar);
|
||||
ipanel.invalidate();
|
||||
}
|
||||
isUseBusy = useBusy;
|
||||
}
|
||||
|
||||
private void ensureBusyInitialized() {
|
||||
if (busy.getIcon() == null) {
|
||||
URL url = getClass().getResource("busy.gif");
|
||||
Icon icon = new ImageIcon(url);
|
||||
busy.setIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
private void invoke(Runnable r) {
|
||||
if (EventQueue.isDispatchThread())
|
||||
r.run();
|
||||
else
|
||||
try {
|
||||
EventQueue.invokeAndWait(r);
|
||||
} catch (InvocationTargetException ex) {
|
||||
ex.printStackTrace();
|
||||
logger.severe("Error invoking operation " + ex.getClass().getName() + " " + ex.getMessage());
|
||||
} catch (InterruptedException ex) {
|
||||
logger.severe("Error invoking operation " + ex.getClass().getName() + " " + ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void finished(final Object o) {
|
||||
isRunning = false;
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
if (o instanceof Summary) {
|
||||
Summary summary = (Summary) o;
|
||||
parent.handleSummary(summary);
|
||||
parent.setWizardResult(summary.getResult());
|
||||
} else if (parent.getDeferredResult() != null) {
|
||||
parent.setWizardResult(o);
|
||||
|
||||
// handle result based on which button was pushed
|
||||
parent.getButtonManager().deferredResultFinished(o);
|
||||
}
|
||||
}
|
||||
};
|
||||
invoke(r);
|
||||
}
|
||||
|
||||
public void failed(final String message, final boolean canGoBack) {
|
||||
failMessage = message;
|
||||
isRunning = false;
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
// cheap word wrap
|
||||
JLabel comp = new JLabel("<html><body>" + message); // NOI18N
|
||||
comp.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
parent.setCurrentWizardPanel(comp);
|
||||
parent.getTtlLabel().setText(
|
||||
NbBridge
|
||||
.getString("org/netbeans/api/wizard/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Failed")); // NOI18N
|
||||
NavButtonManager bm = parent.getButtonManager();
|
||||
bm.deferredResultFailed(canGoBack);
|
||||
}
|
||||
};
|
||||
invoke(r);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
}
|
@ -0,0 +1,681 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
|
||||
Written by Stanley@StanleyKnutson.com based on code from Tim B.
|
||||
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.api.displayer;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.ComponentOrientation;
|
||||
import java.awt.Container;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Font;
|
||||
import java.awt.Frame;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.RootPaneContainer;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.WindowConstants;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.WizardDisplayer;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.WizardResultReceiver;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.modules.InstructionsPanelImpl;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.modules.MergeMap;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.modules.NbBridge;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.DeferredWizardResult;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.ResultProgressHandle;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.Summary;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.Wizard;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardPanel;
|
||||
|
||||
/**
|
||||
* Default implementation of WizardDisplayer.
|
||||
* <b><i><font color="red">This class is NOT AN API CLASS. There is no
|
||||
* commitment that it will remain backward compatible or even exist in the
|
||||
* future. The API of this library is in the packages <code>org.netbeans.api.wizard</code>
|
||||
* and <code>org.netbeans.spi.wizard</code></font></i></b>. <p>Use
|
||||
* <code>WizardDisplayer.showWizard()</code> or its other static methods to
|
||||
* display wizards in a way which will continue to work over time.
|
||||
* @author stanley@StanleyKnutson.com
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public class WizardDisplayerImpl extends WizardDisplayer
|
||||
{
|
||||
|
||||
|
||||
ResultProgressHandle progress = null;
|
||||
|
||||
JLabel ttlLabel = null;
|
||||
|
||||
JPanel ttlPanel = null;
|
||||
|
||||
Wizard wizard = null;
|
||||
|
||||
JPanel outerPanel = null;
|
||||
|
||||
NavButtonManager buttonManager = null;
|
||||
|
||||
InstructionsPanel instructions = null;
|
||||
|
||||
MergeMap settings = null;
|
||||
|
||||
JPanel inner = null;
|
||||
|
||||
JLabel problem = null;
|
||||
|
||||
Object wizardResult = null;
|
||||
|
||||
WizardResultReceiver receiver = null;
|
||||
|
||||
/**
|
||||
* WizardPanel is the panel returned as the panel to display. Often a
|
||||
* subclass of WizardPanel
|
||||
*/
|
||||
JComponent wizardPanel = null;
|
||||
|
||||
boolean inSummary = false;
|
||||
|
||||
DeferredWizardResult deferredResult = null;
|
||||
|
||||
/**
|
||||
* Default constructor used by WizardDisplayer static methods.
|
||||
*
|
||||
*/
|
||||
public WizardDisplayerImpl()
|
||||
{
|
||||
}
|
||||
|
||||
protected void buildStepTitle()
|
||||
{
|
||||
ttlLabel = new JLabel(wizard.getStepDescription(wizard.getAllSteps()[0]));
|
||||
ttlLabel.setBorder(BorderFactory.createCompoundBorder(BorderFactory
|
||||
.createEmptyBorder(5, 5, 12, 5), BorderFactory.createMatteBorder(0, 0, 1, 0, UIManager
|
||||
.getColor("textText")))); // NOI18N
|
||||
ttlPanel = new JPanel()
|
||||
{
|
||||
public void doLayout()
|
||||
{
|
||||
Dimension d = ttlLabel.getPreferredSize();
|
||||
if (ttlLabel.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
|
||||
{
|
||||
ttlLabel.setBounds(getWidth() - d.width, 0, getWidth(), d.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
ttlLabel.setBounds(0, 0, getWidth(), d.height);
|
||||
}
|
||||
}
|
||||
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
return ttlLabel.getPreferredSize();
|
||||
}
|
||||
};
|
||||
ttlPanel.add(ttlLabel);
|
||||
Font f = ttlLabel.getFont();
|
||||
if (f == null)
|
||||
{
|
||||
f = UIManager.getFont("controlFont"); // NOI18N
|
||||
}
|
||||
if (f != null)
|
||||
{
|
||||
f = f.deriveFont(Font.BOLD);
|
||||
ttlLabel.setFont(f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a wizard
|
||||
*
|
||||
* @param awizard is the wizard to be displayed
|
||||
* @param bounds for display, may be null for default of 0,0,400,600.
|
||||
* @param helpAction
|
||||
* @param initialProperties - initial values for the map
|
||||
* @return value of the 'finish' processing
|
||||
* @see org.netbeans.api.wizard.WizardDisplayer#show(org.netbeans.spi.wizard.Wizard, java.awt.Rectangle, javax.swing.Action, java.util.Map)
|
||||
*/
|
||||
private JPanel createOuterPanel(final Wizard awizard, Rectangle bounds, Action helpAction,
|
||||
Map initialProperties)
|
||||
{
|
||||
|
||||
this.wizard = awizard;
|
||||
|
||||
outerPanel = new JPanel();
|
||||
|
||||
// apply default size
|
||||
// we don't enforce any maximum size
|
||||
if (bounds == null)
|
||||
{
|
||||
bounds = new Rectangle(0,0, 600,400);
|
||||
}
|
||||
|
||||
if (wizard.getAllSteps().length == 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Wizard has no steps"); // NOI18N
|
||||
}
|
||||
|
||||
// initialize the ttl* stuff
|
||||
buildStepTitle();
|
||||
|
||||
buttonManager = new NavButtonManager(this);
|
||||
|
||||
outerPanel.setLayout(new BorderLayout());
|
||||
|
||||
Action kbdCancel = new AbstractAction() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JButton b = buttonManager.getCancel();
|
||||
if (b.isEnabled()) {
|
||||
b.doClick();
|
||||
}
|
||||
}
|
||||
};
|
||||
outerPanel.getInputMap(outerPanel.WHEN_IN_FOCUSED_WINDOW).put(
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel"); //NOI18N
|
||||
outerPanel.getActionMap().put("cancel", kbdCancel); //NOI18N
|
||||
|
||||
instructions = createInstructionsPanel();
|
||||
|
||||
buttonManager.buildButtons(helpAction);
|
||||
|
||||
inner = new JPanel();
|
||||
inner.setLayout(new BorderLayout());
|
||||
inner.add(ttlPanel, BorderLayout.NORTH);
|
||||
|
||||
problem = new JLabel(" ");
|
||||
Color fg = UIManager.getColor("nb.errorColor"); // NOI18N
|
||||
problem.setForeground(fg == null ? Color.BLUE : fg);
|
||||
inner.add(problem, BorderLayout.SOUTH);
|
||||
problem.setPreferredSize(new Dimension(20, 20));
|
||||
|
||||
outerPanel.add(instructions.getComponent(), BorderLayout.WEST);
|
||||
outerPanel.add(buttonManager.getButtons(), BorderLayout.SOUTH);
|
||||
outerPanel.add(inner, BorderLayout.CENTER);
|
||||
|
||||
String first = wizard.getAllSteps()[0];
|
||||
settings = new MergeMap(first);
|
||||
|
||||
// introduce the initial properties as if they had been set on page 1
|
||||
// even though they may be defaults for page 2
|
||||
if (initialProperties != null)
|
||||
{
|
||||
settings.putAll(initialProperties);
|
||||
}
|
||||
|
||||
wizardPanel = wizard.navigatingTo(first, settings);
|
||||
String desc = wizard.getLongDescription (first);
|
||||
if (desc != null) {
|
||||
ttlLabel.setText (desc);
|
||||
}
|
||||
|
||||
inner.add(wizardPanel, BorderLayout.CENTER);
|
||||
|
||||
buttonManager.initializeNavigation();
|
||||
return outerPanel;
|
||||
}
|
||||
|
||||
protected InstructionsPanel createInstructionsPanel() {
|
||||
return new InstructionsPanelImpl (wizard);
|
||||
}
|
||||
|
||||
public void install (Container c, Object layoutConstraint, Wizard awizard,
|
||||
Action helpAction, Map initialProperties, WizardResultReceiver receiver) {
|
||||
JPanel pnl = createOuterPanel (awizard, new Rectangle(), helpAction, initialProperties);
|
||||
if (layoutConstraint != null) {
|
||||
if (c instanceof RootPaneContainer) {
|
||||
((RootPaneContainer) c).getContentPane().add (pnl, layoutConstraint);
|
||||
} else {
|
||||
c.add (pnl, layoutConstraint);
|
||||
}
|
||||
} else {
|
||||
if (c instanceof RootPaneContainer) {
|
||||
((RootPaneContainer) c).getContentPane().add (pnl);
|
||||
} else {
|
||||
c.add (pnl);
|
||||
}
|
||||
}
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
private static boolean warned;
|
||||
public Object show(final Wizard awizard, Rectangle bounds, Action helpAction,
|
||||
Map initialProperties) {
|
||||
if (!EventQueue.isDispatchThread() && !warned) {
|
||||
Logger.getLogger(WizardDisplayerImpl.class.getName()).log(Level.WARNING,
|
||||
"show() should be called from the AWT Event Thread. This "
|
||||
+ "call may deadlock - c.f. "
|
||||
+ "http://java.net/jira/browse/WIZARD-33", new Throwable());
|
||||
warned = true;
|
||||
}
|
||||
createOuterPanel (awizard, bounds, helpAction, initialProperties);
|
||||
Object result = showInDialog(bounds);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected JDialog createDialog()
|
||||
{
|
||||
JDialog dlg;
|
||||
Object o = findLikelyOwnerWindow();
|
||||
if (o instanceof Frame)
|
||||
{
|
||||
dlg = new JDialog((Frame) o);
|
||||
}
|
||||
else if (o instanceof Dialog)
|
||||
{
|
||||
dlg = new JDialog((Dialog) o);
|
||||
}
|
||||
else
|
||||
{
|
||||
dlg = new JDialog();
|
||||
}
|
||||
return dlg;
|
||||
}
|
||||
|
||||
protected Object showInDialog(Rectangle bounds)
|
||||
{
|
||||
// TODO: add flag for "showInFrame"
|
||||
|
||||
JDialog dlg = createDialog();
|
||||
|
||||
buttonManager.setWindow(dlg);
|
||||
|
||||
dlg.setTitle(wizard.getTitle());
|
||||
|
||||
dlg.getContentPane().setLayout(new BorderLayout());
|
||||
dlg.getContentPane().add(outerPanel, BorderLayout.CENTER);
|
||||
if (bounds != null)
|
||||
{
|
||||
dlg.setBounds(bounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
dlg.pack();
|
||||
}
|
||||
dlg.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
dlg.addWindowListener(new WindowAdapter()
|
||||
{
|
||||
public void windowClosing(WindowEvent e)
|
||||
{
|
||||
if (!(e.getWindow() instanceof JDialog)) {
|
||||
return;
|
||||
}
|
||||
JDialog dlg = (JDialog) e.getWindow();
|
||||
boolean dontClose = false;
|
||||
if (!wizard.isBusy())
|
||||
{
|
||||
DeferredWizardResult defResult;
|
||||
synchronized(WizardDisplayerImpl.this) {
|
||||
defResult = deferredResult;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (defResult != null && defResult.canAbort())
|
||||
{
|
||||
defResult.abort();
|
||||
}
|
||||
else if (defResult != null && !defResult.canAbort())
|
||||
{
|
||||
dontClose = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!dontClose && wizard.cancel(settings))
|
||||
{
|
||||
dlg.setVisible(false);
|
||||
dlg.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
if (bounds == null) {
|
||||
// XXX get screen insets?
|
||||
int x = (d.width - dlg.getWidth()) / 2;
|
||||
int y = (d.height - dlg.getHeight()) / 2;
|
||||
dlg.setLocation(x, y);
|
||||
}
|
||||
|
||||
dlg.setModal(true);
|
||||
dlg.getRootPane().setDefaultButton(buttonManager.getNext());
|
||||
dlg.setVisible(true);
|
||||
|
||||
return wizardResult;
|
||||
}
|
||||
|
||||
private Window findLikelyOwnerWindow()
|
||||
{
|
||||
return KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current wizard panel, or null if the currently displayed page
|
||||
* is not a WizardPanel.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public WizardPanel getCurrentWizardPanel()
|
||||
{
|
||||
JComponent comp = wizardPanel;
|
||||
if (comp instanceof WizardPanel)
|
||||
{
|
||||
return (WizardPanel) comp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCurrentStep()
|
||||
{
|
||||
return settings.currID();
|
||||
}
|
||||
|
||||
// available in the package only
|
||||
static void checkLegalNavMode(int i)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case Wizard.MODE_CAN_CONTINUE:
|
||||
case Wizard.MODE_CAN_CONTINUE_OR_FINISH:
|
||||
case Wizard.MODE_CAN_FINISH:
|
||||
return;
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal forward " + // NOI18N
|
||||
"navigation mode: " + i); // NOI18N
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* private static final class LDlg extends JDialog { public LDlg() {
|
||||
* } public LDlg (Frame frame) { super (frame); }
|
||||
*
|
||||
* public LDlg (Dialog dlg) { super (dlg); }
|
||||
*
|
||||
* public void setVisible (boolean val) { if (!val) { Thread.dumpStack(); }
|
||||
* super.setVisible (val); } }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set the currently displayed panel.
|
||||
* @parm comp is can be anything - it is not required to be a WizardPage or WizardPanel
|
||||
* */
|
||||
public void setCurrentWizardPanel(JComponent comp)
|
||||
{
|
||||
inner.add(comp, BorderLayout.CENTER);
|
||||
inner.remove(wizardPanel);
|
||||
wizardPanel = comp;
|
||||
inner.invalidate();
|
||||
inner.revalidate();
|
||||
inner.repaint();
|
||||
comp.requestFocus();
|
||||
if (!inSummary)
|
||||
{
|
||||
buttonManager.updateButtons();
|
||||
}
|
||||
}
|
||||
|
||||
void handleSummary(Summary summary)
|
||||
{
|
||||
inSummary = true;
|
||||
instructions.setInSummaryPage(true);
|
||||
JComponent summaryComp = (JComponent) summary.getSummaryComponent(); // XXX
|
||||
if (summaryComp.getBorder() != null)
|
||||
{
|
||||
CompoundBorder b = new CompoundBorder(new EmptyBorder(5, 5, 5, 5), summaryComp
|
||||
.getBorder());
|
||||
summaryComp.setBorder(b);
|
||||
}
|
||||
setCurrentWizardPanel((JComponent) summaryComp); // XXX
|
||||
ttlLabel.setText(NbBridge.getString("org/netbeans/api/wizard/Bundle", // NOI18N
|
||||
WizardDisplayer.class, "Summary")); // NOI18N
|
||||
getButtonManager().setSummaryShowingMode();
|
||||
summaryComp.requestFocus();
|
||||
|
||||
}
|
||||
|
||||
protected ResultProgressHandle createProgressDisplay (boolean isUseBusy)
|
||||
{
|
||||
return new NavProgress(this, isUseBusy);
|
||||
}
|
||||
|
||||
void handleDeferredWizardResult(final DeferredWizardResult r, final boolean inSummary)
|
||||
{
|
||||
synchronized (this) {
|
||||
deferredResult = r;
|
||||
}
|
||||
wizardPanel.setEnabled(false);
|
||||
progress = createProgressDisplay(r.isUseBusy());
|
||||
Container inst = instructions.getComponent();
|
||||
progress.addProgressComponents(inst);
|
||||
inst.invalidate();
|
||||
if (inst instanceof JComponent) {
|
||||
((JComponent)inst).revalidate();
|
||||
}
|
||||
inst.repaint();
|
||||
Runnable run = new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
if (!EventQueue.isDispatchThread())
|
||||
{
|
||||
try
|
||||
{
|
||||
EventQueue.invokeLater (new Runnable() {
|
||||
public void run() {
|
||||
buttonManager.getWindow()
|
||||
.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
}
|
||||
});
|
||||
r.start(settings, progress);
|
||||
if (progress.isRunning())
|
||||
{
|
||||
progress.failed("Start method did not inidicate " +
|
||||
"failure or finished in " + r, false);
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
EventQueue.invokeAndWait(this);
|
||||
}
|
||||
catch (InvocationTargetException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
return;
|
||||
}
|
||||
catch (InterruptedException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
buttonManager.getWindow().setCursor(Cursor.getDefaultCursor());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
synchronized (this) {
|
||||
deferredResult = null;
|
||||
}
|
||||
buttonManager.getCancel().setEnabled(true);
|
||||
Container inst = instructions.getComponent();
|
||||
inst.removeAll();
|
||||
inst.invalidate();
|
||||
if (inst instanceof JComponent) {
|
||||
((JComponent)instructions).revalidate();
|
||||
}
|
||||
inst.repaint();
|
||||
}
|
||||
}
|
||||
};
|
||||
Thread runner = new Thread(run, "Wizard Background Result Thread " + r); // NOI18N
|
||||
runner.start();
|
||||
}
|
||||
|
||||
public void navigateTo(String id)
|
||||
{
|
||||
JComponent comp = wizard.navigatingTo(id, getSettings());
|
||||
String description = wizard.getLongDescription (id);
|
||||
if (description == null) {
|
||||
description = wizard.getStepDescription (id);
|
||||
}
|
||||
getTtlLabel().setText(description);
|
||||
setCurrentWizardPanel(comp);
|
||||
}
|
||||
|
||||
public NavButtonManager getButtonManager()
|
||||
{
|
||||
return buttonManager;
|
||||
}
|
||||
|
||||
public synchronized DeferredWizardResult getDeferredResult()
|
||||
{
|
||||
return deferredResult;
|
||||
}
|
||||
|
||||
public InstructionsPanel getInstructions()
|
||||
{
|
||||
return instructions;
|
||||
}
|
||||
|
||||
public boolean isInSummary()
|
||||
{
|
||||
return inSummary;
|
||||
}
|
||||
|
||||
public void setInSummary(final boolean state)
|
||||
{
|
||||
inSummary = state;
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
instructions.setInSummaryPage(state);
|
||||
}
|
||||
};
|
||||
if (EventQueue.isDispatchThread()) {
|
||||
r.run();
|
||||
} else {
|
||||
EventQueue.invokeLater (r);
|
||||
}
|
||||
}
|
||||
|
||||
public JPanel getOuterPanel()
|
||||
{
|
||||
return outerPanel;
|
||||
}
|
||||
|
||||
public MergeMap getSettings()
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
|
||||
public JLabel getTtlLabel()
|
||||
{
|
||||
return ttlLabel;
|
||||
}
|
||||
|
||||
public JPanel getTtlPanel()
|
||||
{
|
||||
return ttlPanel;
|
||||
}
|
||||
|
||||
public Wizard getWizard()
|
||||
{
|
||||
return wizard;
|
||||
}
|
||||
|
||||
public JComponent getWizardPanel()
|
||||
{
|
||||
return wizardPanel;
|
||||
}
|
||||
|
||||
public Object getWizardResult()
|
||||
{
|
||||
return wizardResult;
|
||||
}
|
||||
|
||||
public void setWizardResult(Object wizardResult)
|
||||
{
|
||||
this.wizardResult = wizardResult;
|
||||
if (receiver != null) {
|
||||
receiver.finished(wizardResult);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void setDeferredResult(DeferredWizardResult deferredResult)
|
||||
{
|
||||
this.deferredResult = deferredResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will only be called if there is a WizardResultReceiver - i.e. if the
|
||||
* wizard is being displayed in some kind of custom container. Return
|
||||
* true to indicate we should not try to close the parent window.
|
||||
*/
|
||||
boolean cancel() {
|
||||
boolean result = receiver != null;
|
||||
if (result) {
|
||||
receiver.cancelled(settings);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void updateProblem()
|
||||
{
|
||||
String prob = wizard.getProblem();
|
||||
problem.setText(prob == null ? " " : prob); // NOI18N
|
||||
if (prob != null && prob.trim().length() == 0)
|
||||
{
|
||||
// Issue 3 - provide ability to disable next w/o
|
||||
// showing the error line
|
||||
prob = null;
|
||||
}
|
||||
Border b = prob == null ? BorderFactory.createEmptyBorder(1, 0, 0, 0) : BorderFactory
|
||||
.createMatteBorder(1, 0, 0, 0, problem.getForeground());
|
||||
|
||||
Border b1 = BorderFactory.createCompoundBorder(BorderFactory
|
||||
.createEmptyBorder(0, 12, 0, 12), b);
|
||||
|
||||
problem.setBorder(b1);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,485 @@
|
||||
/*
|
||||
* The contents of this file are subject to the terms of the Common Development
|
||||
* and Distribution License (the License). You may not use this file except in
|
||||
* compliance with the License.
|
||||
* You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
* or http://www.netbeans.org/cddl.txt.
|
||||
* When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
* and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
* If applicable, add the following below the CDDL Header, with the fields
|
||||
* enclosed by brackets [] replaced by your own identifying information:
|
||||
* "Portions Copyrighted [year] [name of copyright owner]"
|
||||
*/
|
||||
/*
|
||||
* InstructionsPanel.java
|
||||
*
|
||||
* Created on March 4, 2005, 8:59 PM
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.modules;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.IllegalComponentStateException;
|
||||
import java.awt.Insets;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
import java.util.Arrays;
|
||||
import javax.accessibility.Accessible;
|
||||
import javax.accessibility.AccessibleContext;
|
||||
import javax.accessibility.AccessibleRole;
|
||||
import javax.accessibility.AccessibleState;
|
||||
import javax.accessibility.AccessibleStateSet;
|
||||
import javax.accessibility.AccessibleText;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.CellRendererPane;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JEditorPane;
|
||||
import javax.swing.UIManager;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.displayer.InstructionsPanel;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.Wizard;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.WizardObserver;
|
||||
|
||||
/**
|
||||
* A panel that displays a background image and optionally instructions
|
||||
* from a wizard, tracking the selected panel and showing that in bold.
|
||||
* <p/>
|
||||
* <b><i><font color="red">This class is NOT AN API CLASS. There is no
|
||||
* commitment that it will remain backward compatible or even exist in the
|
||||
* future. The API of this library is in the packages
|
||||
* <code>org.netbeans.api.wizard</code>
|
||||
* and <code>org.netbeans.spi.wizard</code></font></i></b>.
|
||||
* <p/>
|
||||
* There is currently a single use-case for subclassing this - a navigation
|
||||
* panel that wants to display a different image for each step.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public class InstructionsPanelImpl extends JComponent implements WizardObserver, Accessible, InstructionsPanel {
|
||||
|
||||
private final BufferedImage img;
|
||||
private final Wizard wizard;
|
||||
private static final int MARGIN = 5;
|
||||
|
||||
public InstructionsPanelImpl(Wizard wiz) {
|
||||
this(null, wiz);
|
||||
Font f = UIManager.getFont("Tree.font"); //NOI18N
|
||||
if (f != null)
|
||||
setFont(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wizard this panel is monitoring.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected final Wizard getWizard() {
|
||||
return wizard;
|
||||
}
|
||||
|
||||
public final Container getComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to start listening to the wizard when added to a container
|
||||
*/
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
wizard.addWizardObserver(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to stop listening to the wizard when removed from a container
|
||||
*/
|
||||
public void removeNotify() {
|
||||
wizard.removeWizardObserver(this);
|
||||
super.removeNotify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image to be displayed. Note that unpredictable behavior
|
||||
* may result if all images returned from this method are not the
|
||||
* same size. Override to display a different wizard depending on the
|
||||
* step.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected BufferedImage getImage() { //for unit test
|
||||
return img;
|
||||
}
|
||||
|
||||
public InstructionsPanelImpl(BufferedImage img, Wizard wizard) {
|
||||
if (img == null)
|
||||
//In the event of classloader issues, also have a way to get
|
||||
//the image from UIManager - slightly more portable for large
|
||||
//apps
|
||||
img = (BufferedImage) UIManager.get("wizard.sidebar.image"); //NOI18N
|
||||
|
||||
String imgStr = System.getProperty("wizard.sidebar.image"); //NOI18N
|
||||
//image has not been loaded and user wishes to supply their own image
|
||||
if (img == null && imgStr != null) {
|
||||
//get an URL, works for jars
|
||||
ClassLoader cl = this.getClass().getClassLoader();
|
||||
URL url = cl.getResource(imgStr);
|
||||
//successfully parsed the URL
|
||||
if (url != null)
|
||||
try {
|
||||
img = ImageIO.read(url);
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Could not load wizard image "
|
||||
+ //NOI18N
|
||||
ioe.getMessage());
|
||||
System.setProperty("wizard.sidebar.image", null); //NOI18N
|
||||
img = null; //error loading img, set to null to use default
|
||||
}
|
||||
else { //URL was not successfully parsed, set img to null to use default
|
||||
System.err.println("Bad URL for wizard image " + imgStr); //NOI18N
|
||||
System.setProperty("wizard.sidebar.image", null); //NOI18N
|
||||
img = null;
|
||||
}
|
||||
}
|
||||
if (img == null)
|
||||
try {
|
||||
img = ImageIO.read(InstructionsPanelImpl.class.getResourceAsStream(
|
||||
"defaultWizard.png")); //NOI18N
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
this.img = img;
|
||||
this.wizard = wizard;
|
||||
}
|
||||
|
||||
public boolean isOpaque() {
|
||||
return img != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the background image for this component, or fills the
|
||||
* background with a color if no image present.
|
||||
*
|
||||
* @param g A Graphic2D to paint into
|
||||
* @param x The x coordinate of the area that should contain the image
|
||||
* @param y The y coordinate of the area that should contain the image
|
||||
* @param w The width of the area that should contain the image
|
||||
* @param h The height of the area that should contain the image
|
||||
*/
|
||||
protected void paintImage(Graphics2D g, int x, int y, int w, int h) {
|
||||
BufferedImage image = getImage();
|
||||
if (image != null)
|
||||
g.drawImage(image, x, y, w, h, this);
|
||||
else {
|
||||
Color c = g.getColor();
|
||||
g.setColor(Color.WHITE);
|
||||
g.fillRect(x, y, w, h);
|
||||
g.setColor(c);
|
||||
}
|
||||
}
|
||||
|
||||
String[] steps = new String[0];
|
||||
|
||||
public final void paintComponent(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
Font f = getFont() != null ? getFont() : UIManager.getFont("controlFont"); //NOI18N
|
||||
FontMetrics fm = g.getFontMetrics(f);
|
||||
Insets ins = getInsets();
|
||||
int dx = ins.left;
|
||||
int dy = ins.top;
|
||||
int w = getWidth() - (ins.left + ins.right);
|
||||
int hh = getHeight() - (ins.top + ins.bottom);
|
||||
paintImage(g2d, dx, dy, w, hh);
|
||||
String currentStep = wizard.getCurrentStep();
|
||||
if (!inSummaryPage)
|
||||
//Don't fetch step list if in summary page, there will
|
||||
//only be the base ones
|
||||
steps = wizard.getAllSteps();
|
||||
String steps[] = this.steps;
|
||||
if (inSummaryPage) {
|
||||
String summaryStep = NbBridge.getString(
|
||||
"org/jackhuang/hellominecraft/utils/views/wizard/modules/Bundle", //NOI18N
|
||||
InstructionsPanelImpl.class, "Summary"); //NOI18N
|
||||
String[] nue = new String[steps.length + 1];
|
||||
System.arraycopy(steps, 0, nue, 0, steps.length);
|
||||
nue[nue.length - 1] = summaryStep;
|
||||
steps = nue;
|
||||
}
|
||||
int y = fm.getMaxAscent() + ins.top + MARGIN;
|
||||
int x = ins.left + MARGIN;
|
||||
int h = fm.getMaxAscent() + fm.getMaxDescent() + 3;
|
||||
|
||||
Font boldFont = f.deriveFont(Font.BOLD);
|
||||
|
||||
g.setFont(boldFont);
|
||||
g.drawString(NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/modules/Bundle", //NOI18N
|
||||
InstructionsPanelImpl.class, "Steps"), x, y); //NOI18N
|
||||
|
||||
int underlineY = ins.top + MARGIN + fm.getAscent() + 3;
|
||||
g.drawLine(x, underlineY, x + (getWidth() - (x + ins.left + MARGIN)),
|
||||
underlineY);
|
||||
|
||||
int bottom = getComponentCount() == 0 ? getHeight() - getInsets().bottom
|
||||
: getHeight() - getInsets().bottom - getComponents()[0].getPreferredSize().height;
|
||||
|
||||
y += h + 10;
|
||||
int first = 0;
|
||||
int stop = steps.length;
|
||||
String elipsis = NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/modules/Bundle", //NOI18N
|
||||
InstructionsPanelImpl.class, "elipsis"); //NOI18N
|
||||
boolean wontFit = y + (h * (steps.length)) > getHeight();
|
||||
if (wontFit) {
|
||||
//try to center the current step
|
||||
int availHeight = bottom - y;
|
||||
int willFit = availHeight / h;
|
||||
int currStepIndex = Arrays.asList(steps).indexOf(currentStep);
|
||||
int rangeStart = Math.max(0, currStepIndex - (willFit / 2));
|
||||
int rangeEnd = Math.min(rangeStart + willFit, steps.length);
|
||||
if (rangeStart + willFit > steps.length) {
|
||||
//Don't scroll off if there's room
|
||||
rangeStart = steps.length - willFit;
|
||||
rangeEnd = steps.length;
|
||||
}
|
||||
steps = (String[]) steps.clone();
|
||||
if (rangeStart != 0) {
|
||||
steps[rangeStart] = elipsis;
|
||||
first = rangeStart;
|
||||
}
|
||||
if (rangeEnd != steps.length && rangeEnd > 0) {
|
||||
steps[rangeEnd - 1] = elipsis;
|
||||
stop = rangeEnd;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* if (wontFit) {
|
||||
* int currStepIndex = Arrays.asList (steps).indexOf(currentStep);
|
||||
* if (currStepIndex != -1) { //shouldn't happen
|
||||
* steps = (String[]) steps.clone();
|
||||
* first = Math.max (0, currStepIndex - 2);
|
||||
* if (first != 0) {
|
||||
* if (y + ((currStepIndex - first) * h) > getHeight()) {
|
||||
* //Best effort to keep current step on screen
|
||||
* first++;
|
||||
* }
|
||||
* if (first != currStepIndex) {
|
||||
* steps[first] = elipsis;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* if (y + ((stop - first) * h) > bottom) {
|
||||
* int avail = bottom - y;
|
||||
* int willFit = avail / h;
|
||||
* int last = first + willFit - 1;
|
||||
* if (last < steps.length - 1) {
|
||||
* steps[last] = elipsis;
|
||||
* stop = last + 1;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
g.setFont(getFont());
|
||||
g.setColor(getForeground());
|
||||
|
||||
for (int i = first; i < stop; i++) {
|
||||
boolean isUndetermined = Wizard.UNDETERMINED_STEP.equals(steps[i]);
|
||||
boolean canOnlyFinish = wizard.getForwardNavigationMode()
|
||||
== Wizard.MODE_CAN_FINISH;
|
||||
if (isUndetermined && canOnlyFinish)
|
||||
break;
|
||||
String curr;
|
||||
if (!elipsis.equals(steps[i]))
|
||||
if (inSummaryPage && i == this.steps.length)
|
||||
curr = (i + 1) + ". " + steps[i];
|
||||
else
|
||||
curr = (i + 1) + ". " + (isUndetermined
|
||||
? NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/modules/Bundle", //NOI18N
|
||||
InstructionsPanelImpl.class, "elipsis")
|
||||
: //NOI18N
|
||||
steps[i].equals(elipsis) ? elipsis
|
||||
: wizard.getStepDescription(steps[i])); //NOI18N
|
||||
else
|
||||
curr = elipsis;
|
||||
if (curr != null) {
|
||||
boolean selected = (steps[i].equals(currentStep) && !inSummaryPage)
|
||||
|| (inSummaryPage && i == steps.length - 1);
|
||||
if (selected)
|
||||
g.setFont(boldFont);
|
||||
|
||||
int width = fm.stringWidth(curr);
|
||||
while (width > getWidth() - (ins.left + ins.right) && curr.length() > 5)
|
||||
curr = curr.substring(0, curr.length() - 5)
|
||||
+ NbBridge.getString(
|
||||
"org/jackhuang/hellominecraft/utils/views/wizard/modules/Bundle", //NOI18N
|
||||
InstructionsPanelImpl.class, "elipsis"); //NOI18N
|
||||
|
||||
g.drawString(curr, x, y);
|
||||
if (selected)
|
||||
g.setFont(f);
|
||||
y += h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int historicWidth = Integer.MIN_VALUE;
|
||||
|
||||
public final Dimension getPreferredSize() {
|
||||
Font f = getFont() != null ? getFont()
|
||||
: UIManager.getFont("controlFont"); //NOI18N
|
||||
|
||||
Graphics g = getGraphics();
|
||||
if (g == null)
|
||||
g = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics();
|
||||
f = f.deriveFont(Font.BOLD);
|
||||
FontMetrics fm = g.getFontMetrics(f);
|
||||
Insets ins = getInsets();
|
||||
int h = fm.getHeight();
|
||||
|
||||
String[] steps = wizard.getAllSteps();
|
||||
int w = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < steps.length; i++) {
|
||||
String desc = i + ". " + (Wizard.UNDETERMINED_STEP.equals(steps[i])
|
||||
? NbBridge.getString("org/jackhuang/hellominecraft/utils/views/wizard/modules/Bundle", //NOI18N
|
||||
InstructionsPanelImpl.class, "elipsis")
|
||||
: //NOI18N
|
||||
wizard.getStepDescription(steps[i]));
|
||||
if (desc != null)
|
||||
w = Math.max(w, fm.stringWidth(desc) + MARGIN);
|
||||
}
|
||||
if (Integer.MIN_VALUE == w)
|
||||
w = 250;
|
||||
BufferedImage img = getImage();
|
||||
if (img != null)
|
||||
w = Math.max(w, img.getWidth());
|
||||
//Make sure we can grow but not shrink
|
||||
w = Math.max(w, historicWidth);
|
||||
historicWidth = w;
|
||||
return new Dimension(w, ins.top + ins.bottom + ((h + 3) * steps.length));
|
||||
}
|
||||
|
||||
private boolean inSummaryPage;
|
||||
|
||||
public void setInSummaryPage(boolean val) {
|
||||
this.inSummaryPage = val;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public final Dimension getMinimumSize() {
|
||||
return getPreferredSize();
|
||||
}
|
||||
|
||||
public void stepsChanged(Wizard wizard) {
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void navigabilityChanged(Wizard wizard) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
public void selectionChanged(Wizard wizard) {
|
||||
repaint();
|
||||
}
|
||||
|
||||
public final void doLayout() {
|
||||
Component[] c = getComponents();
|
||||
Insets ins = getInsets();
|
||||
int y = getHeight() - (MARGIN + ins.bottom);
|
||||
int x = MARGIN + ins.left;
|
||||
int w = getWidth() - ((MARGIN * 2) + ins.left + ins.right);
|
||||
if (w < 0)
|
||||
w = 0;
|
||||
for (int i = c.length - 1; i >= 0; i--) {
|
||||
Dimension d = c[i].getPreferredSize();
|
||||
c[i].setBounds(x, y - d.height, w, d.height);
|
||||
y -= d.height;
|
||||
}
|
||||
}
|
||||
|
||||
public final AccessibleContext getAccessibleContext() {
|
||||
return new ACI(this);
|
||||
}
|
||||
|
||||
private static final class ACI extends AccessibleContext {
|
||||
|
||||
private final Wizard wizard;
|
||||
private final InstructionsPanelImpl panel;
|
||||
|
||||
public ACI(InstructionsPanelImpl pnl) {
|
||||
this.wizard = pnl.wizard;
|
||||
panel = pnl;
|
||||
if (pnl.getParent() instanceof Accessible)
|
||||
setAccessibleParent((Accessible) pnl.getParent());
|
||||
setAccessibleName(NbBridge.getString(
|
||||
"org/jackhuang/hellominecraft/utils/views/wizard/modules/Bundle", //NOI18N
|
||||
InstructionsPanelImpl.class, "ACN_InstructionsPanel")); //NOI18N
|
||||
setAccessibleDescription(NbBridge.getString(
|
||||
"org/jackhuang/hellominecraft/utils/views/wizard/modules/Bundle", //NOI18N
|
||||
InstructionsPanelImpl.class, "ACSD_InstructionsPanel")); //NOI18N
|
||||
}
|
||||
|
||||
JEditorPane pane;
|
||||
|
||||
public AccessibleText getAccessibleText() {
|
||||
if (pane == null) {
|
||||
//Cheat just a bit here - will do for now - the text is
|
||||
//there, more or less where it should be, and a screen
|
||||
//reader should be able to find it; exact bounds don't
|
||||
//make much difference
|
||||
pane = new JEditorPane();
|
||||
pane.setBounds(panel.getBounds());
|
||||
pane.getAccessibleContext().getAccessibleText();
|
||||
pane.setFont(panel.getFont());
|
||||
CellRendererPane cell = new CellRendererPane();
|
||||
cell.add(pane);
|
||||
}
|
||||
pane.setText(getText());
|
||||
pane.selectAll();
|
||||
pane.validate();
|
||||
return pane.getAccessibleContext().getAccessibleText();
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
String[] s = wizard.getAllSteps();
|
||||
for (int i = 0; i < s.length; i++) {
|
||||
sb.append(wizard.getStepDescription(s[i]));
|
||||
sb.append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public AccessibleRole getAccessibleRole() {
|
||||
return AccessibleRole.LIST;
|
||||
}
|
||||
|
||||
public AccessibleStateSet getAccessibleStateSet() {
|
||||
AccessibleState[] states = new AccessibleState[] {
|
||||
AccessibleState.VISIBLE,
|
||||
AccessibleState.OPAQUE,
|
||||
AccessibleState.SHOWING,
|
||||
AccessibleState.MULTI_LINE, };
|
||||
return new AccessibleStateSet(states);
|
||||
}
|
||||
|
||||
public int getAccessibleIndexInParent() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getAccessibleChildrenCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Accessible getAccessibleChild(int i) {
|
||||
throw new IndexOutOfBoundsException("" + i);
|
||||
}
|
||||
|
||||
public Locale getLocale() throws IllegalComponentStateException {
|
||||
return Locale.getDefault();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,290 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
/*
|
||||
* MergeMap.java
|
||||
*
|
||||
* Created on February 22, 2005, 4:06 PM
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.modules;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* A map which proxies a collection of sub-maps each of which has a
|
||||
* unique id. Submaps can be added or removed en banc. Values from
|
||||
* removed maps are retained; if push ("someKnownId") happens, the
|
||||
* values previously added to the map while that ID was active reappear.
|
||||
* <p>
|
||||
* This allows us to implement backward/forward semantics for wizards,
|
||||
* in which each pane (identified with a unique ID) can add its own
|
||||
* settings to the settings map, but if the user presses the Back
|
||||
* button, the settings from the formerly active pane can disappear -
|
||||
* but if the user moves forward again, they are not lost.
|
||||
* <p>
|
||||
* Calling remove("someKeyBelongingToAnEarlierId") will completely
|
||||
* remove that value; calling put ("someKeyBelongingToAnEarlierId", "newValue")
|
||||
* replaces the earler value permanently.
|
||||
* <p>
|
||||
* <b><i><font color="red">This class is NOT AN API CLASS. There is no
|
||||
* commitment that it will remain backward compatible or even exist in the
|
||||
* future. The API of this library is in the packages <code>org.netbeans.api.wizard</code>
|
||||
* and <code>org.netbeans.spi.wizard</code></font></i></b>.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public class MergeMap implements Map {
|
||||
private Stack order = new Stack();
|
||||
private Map id2map = new HashMap();
|
||||
|
||||
/** Creates a new instance of MergeMap */
|
||||
public MergeMap(String currID) {
|
||||
push (currID);
|
||||
}
|
||||
|
||||
private static final String BASE = "__BASE"; //NOI18N
|
||||
/**
|
||||
* Creates a MergeMap with a set of key/value pairs that are
|
||||
* always there (they came from a legacy wizard - used for bridging the
|
||||
* old NetBeans wizards API and this one - some bridged wizards will
|
||||
* have a first panel that gathered some settings using the old APIs
|
||||
* framework, and we need to inject them here.
|
||||
*/
|
||||
public MergeMap(String currId, Map everpresent) {
|
||||
order.push(BASE);
|
||||
id2map.put (BASE, everpresent);
|
||||
push (currId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to a different ID (meaning add a new named map to proxy which can be
|
||||
* calved off if necessary).
|
||||
*/
|
||||
public Map push (String id) {
|
||||
// assert !order.contains(id) : id + " already present"; //NOI18N
|
||||
if (order.contains(id)) {
|
||||
throw new RuntimeException (id + " already present"); //NOI18N
|
||||
}
|
||||
// assert !order.contains(id) : id + " already present"; //NOI18N
|
||||
if (!order.isEmpty() && id.equals(order.peek())) {
|
||||
return (Map) id2map.get(id);
|
||||
}
|
||||
Map result = (Map) id2map.get(id);
|
||||
if (result == null) {
|
||||
result = new HashMap();
|
||||
id2map.put (id, result);
|
||||
}
|
||||
order.push (id);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID of the current sub-map being written into.
|
||||
*/
|
||||
public String currID() {
|
||||
return (String) order.peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the current sub-map. Removes all of its settings from the
|
||||
* MergedMap, but if push() is called with the returned value, the
|
||||
* values associated with the ID being removed will be restored.
|
||||
*/
|
||||
public String popAndCalve() {
|
||||
if (order.size() == 0) {
|
||||
throw new NoSuchElementException ("Cannot back out past first " + //NOI18N
|
||||
"entry"); //NOI18N
|
||||
}
|
||||
//Get the current map
|
||||
String result = (String) order.peek();
|
||||
Map curr = (Map) id2map.get (result);
|
||||
order.pop();
|
||||
|
||||
//Though unlikely, it is possible that a later step in a wizard
|
||||
//overwrote a key/value pair from a previous step of the wizard.
|
||||
//We do not want to revert that write, so iterate all the keys
|
||||
//we're removing, and if any of them are in steps lower on the
|
||||
//stack, change those lower steps values to whatever was written
|
||||
//into the map we're calving off
|
||||
|
||||
Set keysForCurr = curr.keySet();
|
||||
for (Iterator i=orderIterator(); i.hasNext();) {
|
||||
Map other = (Map) id2map.get(i.next());
|
||||
for (Iterator j=curr.keySet().iterator(); j.hasNext();) {
|
||||
Object key = j.next();
|
||||
if (other.containsKey(key)) {
|
||||
other.put (key, curr.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object obj) {
|
||||
for (Iterator i = orderIterator(); i.hasNext();) {
|
||||
Map curr = (Map) id2map.get(i.next());
|
||||
if (curr.containsKey(obj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsValue(Object obj) {
|
||||
for (Iterator i = orderIterator(); i.hasNext();) {
|
||||
Map curr = (Map) id2map.get(i.next());
|
||||
if (curr.containsValue(obj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public java.util.Set entrySet() {
|
||||
HashSet result = new HashSet();
|
||||
for (Iterator i = orderIterator(); i.hasNext();) {
|
||||
Map curr = (Map) id2map.get(i.next());
|
||||
result.addAll (curr.entrySet());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Object get(Object obj) {
|
||||
for (Iterator i = orderIterator(); i.hasNext();) {
|
||||
String id = (String) i.next();
|
||||
Map curr = (Map) id2map.get(id);
|
||||
Object result = curr.get(obj);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
public Set keySet() {
|
||||
HashSet result = new HashSet();
|
||||
for (Iterator i = orderIterator(); i.hasNext();) {
|
||||
Map curr = (Map) id2map.get(i.next());
|
||||
result.addAll (curr.keySet());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Object put(Object obj, Object obj1) {
|
||||
Map curr = (Map) id2map.get (order.peek());
|
||||
return curr.put (obj, obj1);
|
||||
}
|
||||
|
||||
public void putAll(Map map) {
|
||||
Map curr = (Map) id2map.get (order.peek());
|
||||
curr.putAll (map);
|
||||
}
|
||||
|
||||
private Object doRemove(Object obj) {
|
||||
Map curr = (Map) id2map.get (order.peek());
|
||||
Object result = curr.remove (obj);
|
||||
if (result == null) {
|
||||
for (Iterator i = orderIterator(); i.hasNext();) {
|
||||
curr = (Map) id2map.get(i.next());
|
||||
result = curr.remove (obj);
|
||||
if (result != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Object remove(Object obj) {
|
||||
//Ensure we remove any duplicates in upper arrays
|
||||
Object result = get(obj);
|
||||
while (get(obj) != null) {
|
||||
doRemove (obj);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
//using keySet() prunes duplicates
|
||||
return keySet().size();
|
||||
}
|
||||
|
||||
public Collection values() {
|
||||
HashSet result = new HashSet();
|
||||
Set keys = keySet();
|
||||
for (Iterator i = keys.iterator(); i.hasNext();) {
|
||||
result.add (get(i.next()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Iterator orderIterator() {
|
||||
return new ReverseIterator(order);
|
||||
}
|
||||
|
||||
private static final class ReverseIterator implements Iterator {
|
||||
private int pos;
|
||||
private List l;
|
||||
public ReverseIterator (Stack s) {
|
||||
pos = s.size()-1;
|
||||
l = new ArrayList(s);
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return pos != -1;
|
||||
}
|
||||
|
||||
public Object next() {
|
||||
if (pos < 0) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
Object result = l.get(pos);
|
||||
pos--;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (Iterator i = keySet().iterator(); i.hasNext();) {
|
||||
Object key = (Object) i.next();
|
||||
sb.append ('[');
|
||||
sb.append (key);
|
||||
sb.append('=');
|
||||
sb.append(get(key));
|
||||
sb.append(']');
|
||||
if (i.hasNext()) sb.append (',');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* The contents of this file are subject to the terms of the Common Development
|
||||
* and Distribution License (the License). You may not use this file except in
|
||||
* compliance with the License.
|
||||
* You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
* or http://www.netbeans.org/cddl.txt.
|
||||
* When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
* and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
* If applicable, add the following below the CDDL Header, with the fields
|
||||
* enclosed by brackets [] replaced by your own identifying information:
|
||||
* "Portions Copyrighted [year] [name of copyright owner]"
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.modules;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Non API class for accessing a few things in NetBeans via reflection.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public final class NbBridge {
|
||||
|
||||
private NbBridge() {
|
||||
}
|
||||
|
||||
public static String getString(String path, Class callerType, String key) {
|
||||
return getStringViaResourceBundle(path, key);
|
||||
}
|
||||
|
||||
private static String getStringViaResourceBundle(String path, String key) {
|
||||
return ResourceBundle.getBundle(path).getString(key);
|
||||
}
|
||||
}
|
@ -0,0 +1,349 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
|
||||
/*
|
||||
* BranchingWizard.java
|
||||
*
|
||||
* Created on March 4, 2005, 10:56 PM
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A Wizard with indeterminate branches. The actual branch decision-making
|
||||
* is done by the WizardBranchController passed to the constructor.
|
||||
* <p/>
|
||||
* Wizards with arbitrary numbers of branches can be handled by a
|
||||
* WizardBranchController by returning wizards created by
|
||||
* another WizardBranchController's <code>createWizard()</code> method.
|
||||
* <p/>
|
||||
* One important point: There should be no duplicate IDs between steps of
|
||||
* this wizard.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
final class BranchingWizard implements WizardImplementation {
|
||||
private final List listenerList = Collections.synchronizedList (
|
||||
new LinkedList());
|
||||
|
||||
private final WizardBranchController brancher;
|
||||
final WizardImplementation initialSteps;
|
||||
|
||||
private WizardImplementation subsequentSteps;
|
||||
private WizardImplementation activeWizard;
|
||||
private WL wl;
|
||||
|
||||
private String currStep;
|
||||
private Map wizardData;
|
||||
|
||||
public BranchingWizard(WizardBranchController brancher) {
|
||||
this.brancher = brancher;
|
||||
initialSteps = new SimpleWizard(brancher.getBase(), true);
|
||||
setCurrent(initialSteps);
|
||||
}
|
||||
|
||||
protected final WizardImplementation createSecondary(Map settings) {
|
||||
Wizard wiz = brancher.getWizardForStep(currStep, settings);
|
||||
return wiz == null ? null : wiz.impl;
|
||||
}
|
||||
|
||||
private void checkForSecondary() {
|
||||
if (wizardData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
WizardImplementation newSecondary = createSecondary(wizardData);
|
||||
|
||||
/*
|
||||
* johnflournoy 7/20/07
|
||||
* check for secondary should be adding the secondary to the activeWizard
|
||||
* not the initial wizard. Adding it to the initial wizard was breaking
|
||||
* multiple branching - to accomplish this created a new method:
|
||||
* setSecondary()
|
||||
*/
|
||||
if (activeWizard instanceof BranchingWizard) {
|
||||
((BranchingWizard) activeWizard).setSecondary(newSecondary);
|
||||
} else {
|
||||
this.setSecondary(newSecondary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the secondary for this <code>BranchingWizard</code>.
|
||||
* @param newSecondary is a WizardImplementation.
|
||||
*/
|
||||
private void setSecondary(WizardImplementation newSecondary) {
|
||||
/* johnflournoy added additional condition: secondary != this */
|
||||
if ((((subsequentSteps == null) != (newSecondary == null))
|
||||
|| (subsequentSteps != null && !subsequentSteps.equals(newSecondary)))
|
||||
&& !this.equals(newSecondary)) {
|
||||
|
||||
/*
|
||||
* johnflournoy: only set the subsequent steps if it
|
||||
* this wizard owns the current step.
|
||||
*/
|
||||
if (Arrays.asList(initialSteps.getAllSteps()).contains(currStep)) {
|
||||
subsequentSteps = newSecondary;
|
||||
fireStepsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getForwardNavigationMode() {
|
||||
return activeWizard.getForwardNavigationMode();
|
||||
}
|
||||
|
||||
private void setCurrent(WizardImplementation wizard) {
|
||||
if (activeWizard == wizard) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wizard == null) {
|
||||
throw new NullPointerException("Can't set current wizard to null");
|
||||
}
|
||||
|
||||
if ((activeWizard != null) && (wl != null)) {
|
||||
activeWizard.removeWizardObserver(wl);
|
||||
}
|
||||
|
||||
activeWizard = wizard;
|
||||
|
||||
if (wl == null) {
|
||||
wl = new WL();
|
||||
}
|
||||
|
||||
activeWizard.addWizardObserver(wl);
|
||||
}
|
||||
|
||||
public final boolean isBusy() {
|
||||
return activeWizard.isBusy();
|
||||
}
|
||||
|
||||
public final Object finish(Map settings) throws WizardException {
|
||||
try {
|
||||
Object result = activeWizard.finish(settings);
|
||||
initialSteps.removeWizardObserver(wl);
|
||||
//Can be null, we allow bail-out with finish mid-wizard now
|
||||
if (subsequentSteps != null) {
|
||||
subsequentSteps.removeWizardObserver(wl);
|
||||
}
|
||||
return result;
|
||||
} catch (WizardException we) {
|
||||
if (we.getStepToReturnTo() != null) {
|
||||
initialSteps.addWizardObserver(wl);
|
||||
//Can be null, we allow bail-out with finish mid-wizard now
|
||||
if (subsequentSteps != null) {
|
||||
subsequentSteps.addWizardObserver(wl);
|
||||
}
|
||||
}
|
||||
throw we;
|
||||
}
|
||||
}
|
||||
|
||||
public final String[] getAllSteps() {
|
||||
String[] result;
|
||||
if (subsequentSteps == null) {
|
||||
String[] bsteps = initialSteps.getAllSteps();
|
||||
result = new String[bsteps.length + 1];
|
||||
System.arraycopy(bsteps, 0, result, 0, bsteps.length);
|
||||
result[result.length - 1] = UNDETERMINED_STEP;
|
||||
} else {
|
||||
String[] bsteps = initialSteps.getAllSteps();
|
||||
String[] csteps = subsequentSteps.getAllSteps();
|
||||
result = new String[bsteps.length + csteps.length];
|
||||
System.arraycopy(bsteps, 0, result, 0, bsteps.length);
|
||||
System.arraycopy(csteps, 0, result, bsteps.length, csteps.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getCurrentStep() {
|
||||
return currStep;
|
||||
}
|
||||
|
||||
public final String getNextStep() {
|
||||
String result;
|
||||
if (currStep == null) {
|
||||
result = getAllSteps()[0];
|
||||
} else {
|
||||
String[] steps = getAllSteps();
|
||||
int idx = Arrays.asList(steps).indexOf(currStep);
|
||||
if (idx == -1) {
|
||||
throw new IllegalStateException("Current step not in" + //NOI18N
|
||||
" available steps: " + currStep + " not in " + //NOI18N
|
||||
Arrays.asList(steps));
|
||||
} else {
|
||||
if (idx == steps.length - 1) {
|
||||
if (subsequentSteps == null) {
|
||||
result = UNDETERMINED_STEP;
|
||||
} else {
|
||||
result = subsequentSteps.getNextStep();
|
||||
}
|
||||
} else {
|
||||
WizardImplementation w = ownerOf(currStep);
|
||||
if (w == initialSteps && idx == initialSteps.getAllSteps().length - 1) {
|
||||
checkForSecondary();
|
||||
if (subsequentSteps != null) {
|
||||
result = subsequentSteps.getAllSteps()[0];
|
||||
} else {
|
||||
result = UNDETERMINED_STEP;
|
||||
}
|
||||
} else {
|
||||
result = w.getNextStep();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return getProblem() == null ? result : UNDETERMINED_STEP.equals(result) ? result : null;
|
||||
}
|
||||
|
||||
public final String getPreviousStep() {
|
||||
if (activeWizard == subsequentSteps && subsequentSteps.getAllSteps()[0].equals(currStep)) {
|
||||
return initialSteps.getAllSteps()[initialSteps.getAllSteps().length - 1];
|
||||
} else {
|
||||
return activeWizard.getPreviousStep();
|
||||
}
|
||||
}
|
||||
|
||||
public final String getProblem() {
|
||||
return activeWizard.getProblem();
|
||||
}
|
||||
|
||||
public final String getStepDescription(String id) {
|
||||
WizardImplementation w = ownerOf(id);
|
||||
if (w == null) {
|
||||
return null;
|
||||
}
|
||||
return w.getStepDescription(id);
|
||||
}
|
||||
|
||||
public final String getLongDescription(String id) {
|
||||
WizardImplementation w = ownerOf(id);
|
||||
if (w == null) {
|
||||
return null;
|
||||
}
|
||||
return w.getLongDescription(id);
|
||||
}
|
||||
|
||||
private WizardImplementation ownerOf(String id) {
|
||||
if (UNDETERMINED_STEP.equals(id)) {
|
||||
checkForSecondary();
|
||||
return subsequentSteps;
|
||||
}
|
||||
if (Arrays.asList(initialSteps.getAllSteps()).contains(id)) {
|
||||
return initialSteps;
|
||||
} else {
|
||||
/*
|
||||
* johnflournoy
|
||||
* need to check an existing subsequentsteps to see if
|
||||
* we can find the owner of "id", otherwise we were losing
|
||||
* a wizard if we had multiple branches and we backed up to an
|
||||
* earlier wizard and then went down the same path again.
|
||||
*/
|
||||
if (subsequentSteps != null) {
|
||||
if (!Arrays.asList(subsequentSteps.getAllSteps()).contains(id)) {
|
||||
checkForSecondary();
|
||||
}
|
||||
} else {
|
||||
checkForSecondary();
|
||||
}
|
||||
|
||||
return subsequentSteps;
|
||||
}
|
||||
}
|
||||
|
||||
public final String getTitle() {
|
||||
return activeWizard.getTitle();
|
||||
}
|
||||
|
||||
public final JComponent navigatingTo(String id, Map settings) {
|
||||
if (id == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
currStep = id;
|
||||
wizardData = settings;
|
||||
|
||||
WizardImplementation impl = ownerOf (id);
|
||||
if (impl == null) {
|
||||
throw new NullPointerException ("No owning WizardImplementation for" +
|
||||
" id " + id);
|
||||
}
|
||||
setCurrent(impl);
|
||||
|
||||
return activeWizard.navigatingTo(id, settings);
|
||||
}
|
||||
|
||||
public final void removeWizardObserver (WizardObserver observer) {
|
||||
listenerList.remove(observer);
|
||||
}
|
||||
|
||||
public final void addWizardObserver (WizardObserver observer) {
|
||||
listenerList.add(observer);
|
||||
}
|
||||
|
||||
private void fireStepsChanged() {
|
||||
WizardObserver[] listeners = (WizardObserver[])
|
||||
listenerList.toArray (new WizardObserver[0]);
|
||||
|
||||
for (int i = listeners.length - 1; i >= 0; i --) {
|
||||
WizardObserver l = (WizardObserver) listeners[i];
|
||||
l.stepsChanged(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireNavigabilityChanged() {
|
||||
checkForSecondary();
|
||||
|
||||
WizardObserver[] listeners = (WizardObserver[])
|
||||
listenerList.toArray (new WizardObserver[0]);
|
||||
|
||||
for (int i = listeners.length - 1; i >= 0; i --) {
|
||||
WizardObserver l = (WizardObserver) listeners[i];
|
||||
l.navigabilityChanged(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireSelectionChanged() {
|
||||
WizardObserver[] listeners = (WizardObserver[])
|
||||
listenerList.toArray (new WizardObserver[0]);
|
||||
|
||||
for (int i = listeners.length - 1; i >= 0; i --) {
|
||||
WizardObserver l = (WizardObserver) listeners[i];
|
||||
l.selectionChanged(null);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean cancel(Map settings) {
|
||||
return activeWizard == null ? true : activeWizard.cancel(settings);
|
||||
}
|
||||
|
||||
private class WL implements WizardObserver {
|
||||
public void stepsChanged(Wizard wizard) {
|
||||
fireStepsChanged();
|
||||
}
|
||||
|
||||
public void navigabilityChanged(Wizard wizard) {
|
||||
fireNavigabilityChanged();
|
||||
}
|
||||
|
||||
public void selectionChanged(Wizard wizard) {
|
||||
fireSelectionChanged();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
|
||||
/*
|
||||
* DeferredWizardResult.java
|
||||
*
|
||||
* Created on September 24, 2006, 3:42 AM
|
||||
*
|
||||
* To change this template, choose Tools | Template Manager
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Object which can be returned from
|
||||
* <code>WizardPage.WizardResultProducer.finish()</code>
|
||||
* or <code>WizardPanelProvider.finish()</code>. A DeferredWizardResult does
|
||||
* not immediately calculate its result; it is used for cases where some
|
||||
* time consuming work needs to be performed to compute the result (such as
|
||||
* creating files on disk), and a progress bar should be shown until the work
|
||||
* is completed.
|
||||
* @see org.jackhuang.hellominecraft.utils.views.wizard.spi.ResultProgressHandle
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public abstract class DeferredWizardResult {
|
||||
private final boolean canAbort;
|
||||
private final boolean useBusy;
|
||||
/**
|
||||
* Creates a new instance of DeferredWizardResult which cannot be
|
||||
* aborted and shows a progress bar.
|
||||
*/
|
||||
public DeferredWizardResult() {
|
||||
useBusy = false;
|
||||
canAbort = false;
|
||||
}
|
||||
|
||||
/** Creates a new instance of DeferredWizardResult which may or may not
|
||||
* be able to be aborted.
|
||||
* @param canAbort determine if background computation can be aborted by
|
||||
* calling the <code>abort()</code> method
|
||||
*/
|
||||
public DeferredWizardResult (boolean canAbort) {
|
||||
this.canAbort = canAbort;
|
||||
this.useBusy = false;
|
||||
}
|
||||
|
||||
/** Creates a new instance of DeferredWizardResult which may or may not
|
||||
* be able to be aborted, and which may simply disable the wizard's UI
|
||||
* instead of showing a progress bar while the background work runs.
|
||||
*
|
||||
* @param canAbort
|
||||
* @param useBusy
|
||||
*/
|
||||
public DeferredWizardResult (boolean canAbort, boolean useBusy) {
|
||||
this.canAbort = canAbort;
|
||||
this.useBusy = useBusy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begin computing the result. This method is called on a background
|
||||
* thread, not the AWT event thread, and computation can immediately begin.
|
||||
* Use the progress handle to set progress as the work progresses.
|
||||
*
|
||||
* IMPORTANT: This method MUST call either progress.finished with the result,
|
||||
* or progress.failed with an error message. If this method returns without
|
||||
* calling either of those methods, it will be assumed to have failed.
|
||||
*
|
||||
* @param settings The settings gathered over the course of the wizard
|
||||
* @param progress A handle which can be used to affect the progress bar.
|
||||
*/
|
||||
public abstract void start (Map settings, ResultProgressHandle progress);
|
||||
|
||||
/**
|
||||
* If true, the background thread can be aborted. If it is possible to
|
||||
* abort, then the UI may allow the dialog to be closed while the result
|
||||
* is being computed.
|
||||
*/
|
||||
public final boolean canAbort() {
|
||||
return canAbort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort computation of the result. This method will usually be called on
|
||||
* the event thread, after <code>start()<code> has been called, and before
|
||||
* <code>finished()</code> has been called on the <code>ResultProgressHandle</code>
|
||||
* that is passed to <code>start()</code>, for example, if the user clicks
|
||||
* the close button on the dialog showing the wizard while the result is
|
||||
* being computed.
|
||||
* <p>
|
||||
* <b>This method does <i>nothing</i> by default</b> - it is left empty so
|
||||
* that people who do not want to support aborting background work do not
|
||||
* have to override it. It is up to the implementor
|
||||
* to set a flag or otherwise notify the background thread to halt
|
||||
* computation. A simple method for doing so is as follows:
|
||||
* <pre>
|
||||
* volatile Thread thread;
|
||||
* public void start (Map settings, ResultProgressHandle handle) {
|
||||
* try {
|
||||
* synchronized (this) {
|
||||
* thread = Thread.currentThread();
|
||||
* }
|
||||
*
|
||||
* //do the background computation, update progress. Every so often,
|
||||
* //check Thread.interrupted() and exit if true
|
||||
* } finally {
|
||||
* synchronized (this) {
|
||||
* thread = null;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* public synchronized void abort() {
|
||||
* if (thread != null) thread.interrupt();
|
||||
* }
|
||||
* </pre>
|
||||
* or you can use a <code>volatile boolean</code> flag that you set in
|
||||
* <code>abort()</code> and periodically check in the body of <code>start()</code>.
|
||||
*
|
||||
*/
|
||||
public void abort() {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the UI should be completely disabled while the background
|
||||
* work is running (i.e. you do not want a progress bar, you just want all
|
||||
* navigation disabled [note on some window managers, the user will still
|
||||
* be able to click the dialog's window drag-bar close button, so you still
|
||||
* should override abort() to stop computation if possible]).
|
||||
*
|
||||
* @return true if no progress bar should be displayed and the UI should
|
||||
* just disable itself
|
||||
*/
|
||||
public final boolean isUseBusy()
|
||||
{
|
||||
return useBusy;
|
||||
}
|
||||
}
|
@ -0,0 +1,439 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
|
||||
/*
|
||||
* GenericListener.java
|
||||
*
|
||||
* Created on October 5, 2004, 12:36 AM
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.*;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Arrays;
|
||||
import java.util.EventObject;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.*;
|
||||
import javax.swing.tree.*;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
|
||||
/**
|
||||
* A listener that can listen to just about any standard swing component
|
||||
* that accepts user input and notify the panel that it needs to
|
||||
* validate its contents.
|
||||
*
|
||||
* If you use subclasses of the swing components, you will also need to subclass
|
||||
* this listener and override at least the methods isProbablyContainer, attachTo and detachFrom.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
final class GenericListener
|
||||
implements ActionListener, PropertyChangeListener, ItemListener,
|
||||
ContainerListener, DocumentListener, ChangeListener,
|
||||
ListSelectionListener, TreeSelectionListener, TableModelListener {
|
||||
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(GenericListener.class.getName());
|
||||
|
||||
private final WizardPage wizardPage;
|
||||
|
||||
private boolean ignoreEvents;
|
||||
|
||||
/**
|
||||
* Set of components that we're listening to models of, so we can look
|
||||
* up the component from the model as needed
|
||||
*/
|
||||
private Set listenedTo = new HashSet();
|
||||
|
||||
private final WizardPage.CustomComponentListener extListener;
|
||||
private final WizardPage.CustomComponentNotifier extNotifier;
|
||||
public GenericListener(WizardPage wizardPage, WizardPage.CustomComponentListener l,
|
||||
WizardPage.CustomComponentNotifier n) {
|
||||
this.extListener = l;
|
||||
this.extNotifier = n;
|
||||
if ((extListener == null) != (extNotifier == null)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
// assert wizardPage != null : "WizardPage may not be null"; // NOI18N
|
||||
if (wizardPage == null) {
|
||||
throw new IllegalArgumentException("WizardPage may not be null"); // NOI18N)
|
||||
}
|
||||
this.wizardPage = wizardPage;
|
||||
wizardPage.addContainerListener(this);
|
||||
}
|
||||
|
||||
public GenericListener (WizardPage page) {
|
||||
this (page, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given component is likely to be a container such the each
|
||||
* component within the container should be be considered as a user input.
|
||||
*
|
||||
* @param c
|
||||
* @return true if the component children should have this listener added.
|
||||
*/
|
||||
protected boolean isProbablyAContainer (Component c) {
|
||||
boolean result = extListener != null ? extListener.isContainer(c) : false;
|
||||
if (!result) {
|
||||
boolean isSwing = isSwingClass(c);
|
||||
if (isSwing) {
|
||||
result = c instanceof JPanel || c instanceof JSplitPane || c instanceof
|
||||
JToolBar || c instanceof JViewport || c instanceof JScrollPane ||
|
||||
c instanceof JFrame || c instanceof JRootPane || c instanceof
|
||||
Window || c instanceof Frame || c instanceof Dialog ||
|
||||
c instanceof JTabbedPane || c instanceof JInternalFrame ||
|
||||
c instanceof JDesktopPane || c instanceof JLayeredPane ||
|
||||
c instanceof Box;
|
||||
} else {
|
||||
result = c instanceof Container;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given component is likely to be a swing primitive or a subclass.
|
||||
* The default implmentation here just checks for the package of the component to be "javax.swing"
|
||||
* If you use subclasses of swing components, you will need to override this method
|
||||
* to get proper behavior.
|
||||
*
|
||||
* @param c
|
||||
* @return true if the component should be examined more closely (see isProbablyAContainer)
|
||||
*/
|
||||
protected boolean isSwingClass (Component c)
|
||||
{
|
||||
String packageName = c.getClass().getPackage().getName();
|
||||
boolean swing = packageName.equals ("javax.swing"); //NOI18N
|
||||
return swing;
|
||||
}
|
||||
|
||||
protected void attachTo(Component jc) {
|
||||
if (extListener != null && extListener.accept (jc)) {
|
||||
extListener.startListeningTo(jc, extNotifier);
|
||||
listenedTo.add (jc);
|
||||
if (wizardPage.getMapKeyFor(jc) != null) {
|
||||
wizardPage.maybeUpdateMap(jc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isProbablyAContainer(jc)) {
|
||||
attachToHierarchyOf((Container) jc);
|
||||
} else if (jc instanceof JList) {
|
||||
listenedTo.add(jc);
|
||||
((JList) jc).addListSelectionListener(this);
|
||||
} else if (jc instanceof JComboBox) {
|
||||
((JComboBox) jc).addActionListener(this);
|
||||
} else if (jc instanceof JTree) {
|
||||
listenedTo.add(jc);
|
||||
((JTree) jc).getSelectionModel().addTreeSelectionListener(this);
|
||||
} else if (jc instanceof JToggleButton) {
|
||||
((AbstractButton) jc).addItemListener(this);
|
||||
} else if (jc instanceof JFormattedTextField) { //JFormattedTextField must be tested before JTextCompoent
|
||||
jc.addPropertyChangeListener("value", this);
|
||||
} else if (jc instanceof JTextComponent) {
|
||||
listenedTo.add(jc);
|
||||
((JTextComponent) jc).getDocument().addDocumentListener(this);
|
||||
} else if (jc instanceof JColorChooser) {
|
||||
listenedTo.add(jc);
|
||||
((JColorChooser) jc).getSelectionModel().addChangeListener(this);
|
||||
} else if (jc instanceof JSpinner) {
|
||||
((JSpinner) jc).addChangeListener(this);
|
||||
} else if (jc instanceof JSlider) {
|
||||
((JSlider) jc).addChangeListener(this);
|
||||
} else if (jc instanceof JTable) {
|
||||
listenedTo.add(jc);
|
||||
((JTable) jc).getSelectionModel().addListSelectionListener(this);
|
||||
} else {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("Don't know how to listen to a " + // NOI18N
|
||||
jc.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (accept(jc) && !(jc instanceof JPanel)) {
|
||||
jc.addPropertyChangeListener("name", this);
|
||||
if (wizardPage.getMapKeyFor(jc) != null) {
|
||||
wizardPage.maybeUpdateMap(jc);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isLoggable(Level.FINE) && accept(jc)) {
|
||||
logger.fine("Begin listening to " + jc); // NOI18N
|
||||
}
|
||||
}
|
||||
|
||||
protected void detachFrom(Component jc) {
|
||||
listenedTo.remove(jc);
|
||||
if (extListener != null && extListener.accept (jc)) {
|
||||
extListener.stopListeningTo(jc);
|
||||
}
|
||||
if (isProbablyAContainer(jc)) {
|
||||
detachFromHierarchyOf((Container) jc);
|
||||
} else if (jc instanceof JList) {
|
||||
((JList) jc).removeListSelectionListener(this);
|
||||
} else if (jc instanceof JComboBox) {
|
||||
((JComboBox) jc).removeActionListener(this);
|
||||
} else if (jc instanceof JTree) {
|
||||
((JTree) jc).getSelectionModel().removeTreeSelectionListener(this);
|
||||
} else if (jc instanceof JToggleButton) {
|
||||
((AbstractButton) jc).removeActionListener(this);
|
||||
} else if (jc instanceof JTextComponent) {
|
||||
} else if (jc instanceof JFormattedTextField) { //JFormattedTextField must be tested before JTextCompoent
|
||||
jc.removePropertyChangeListener("value", this);
|
||||
((JTextComponent) jc).getDocument().removeDocumentListener(this);
|
||||
} else if (jc instanceof JColorChooser) {
|
||||
((JColorChooser) jc).getSelectionModel().removeChangeListener(this);
|
||||
} else if (jc instanceof JSpinner) {
|
||||
((JSpinner) jc).removeChangeListener(this);
|
||||
} else if (jc instanceof JSlider) {
|
||||
((JSlider) jc).removeChangeListener(this);
|
||||
} else if (jc instanceof JTable) {
|
||||
((JTable) jc).getSelectionModel().removeListSelectionListener(this);
|
||||
}
|
||||
|
||||
if (accept(jc) && !(jc instanceof JPanel)) {
|
||||
jc.removePropertyChangeListener("name", this);
|
||||
Object key = wizardPage.getMapKeyFor(jc);
|
||||
|
||||
if (key != null) {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("Named component removed from hierarchy: " + // NOI18N
|
||||
key + ". Removing any corresponding " + // NOI18N
|
||||
"value from the wizard settings map."); // NOI18N
|
||||
}
|
||||
|
||||
wizardPage.removeFromMap(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isLoggable(Level.FINE) && accept(jc)) {
|
||||
logger.fine("Stop listening to " + jc); // NOI18N
|
||||
}
|
||||
}
|
||||
|
||||
private void detachFromHierarchyOf(Container container) {
|
||||
container.removeContainerListener(this);
|
||||
Component[] components = container.getComponents();
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
detachFrom(components[i]); // Will callback recursively any nested JPanels
|
||||
}
|
||||
}
|
||||
|
||||
void attachToHierarchyOf(Container container) {
|
||||
if (!Arrays.asList (container.getContainerListeners()).contains(this)) {
|
||||
container.addContainerListener(this);
|
||||
}
|
||||
Component[] components = container.getComponents();
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
attachTo(components[i]); // Will recursively add any child components in
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean accept(Component jc) {
|
||||
if (extListener != null && extListener.accept(jc)) {
|
||||
return true;
|
||||
}
|
||||
if (!(jc instanceof JComponent)) {
|
||||
return false;
|
||||
}
|
||||
if (jc instanceof TableCellEditor || jc instanceof TreeCellEditor ||
|
||||
SwingUtilities.getAncestorOfClass(JTable.class, jc) != null ||
|
||||
SwingUtilities.getAncestorOfClass(JTree.class, jc) != null ||
|
||||
SwingUtilities.getAncestorOfClass(JList.class, jc) != null){
|
||||
//Don't listen to cell editors, we can end up listening to them
|
||||
//multiple times, and the tree/table model will give us the event
|
||||
//we need
|
||||
return false;
|
||||
}
|
||||
return isProbablyAContainer (jc) ||
|
||||
jc instanceof JList ||
|
||||
jc instanceof JComboBox ||
|
||||
jc instanceof JTree ||
|
||||
jc instanceof JToggleButton || //covers toggle, radio, checkbox
|
||||
jc instanceof JTextComponent ||
|
||||
jc instanceof JColorChooser ||
|
||||
jc instanceof JSpinner ||
|
||||
jc instanceof JSlider;
|
||||
}
|
||||
|
||||
void setIgnoreEvents(boolean val) {
|
||||
ignoreEvents = val;
|
||||
}
|
||||
|
||||
private void fire(Object e) {
|
||||
if (!ignoreEvents) {
|
||||
setIgnoreEvents(true);
|
||||
try {
|
||||
//XXX this could be prettier...
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("Event received: " + e); // NOI18N
|
||||
}
|
||||
if (e instanceof EventObject && ((EventObject) e).getSource() instanceof Component) {
|
||||
wizardPage.userInputReceived((Component) ((EventObject) e).getSource(), e);
|
||||
} else if (e instanceof TreeSelectionEvent) {
|
||||
logger.fine("Looking for a tree for a tree selection event"); // NOI18N
|
||||
TreeSelectionModel mdl = (TreeSelectionModel) ((TreeSelectionEvent) e).getSource();
|
||||
for (Iterator i = listenedTo.iterator(); i.hasNext();) {
|
||||
Object o = i.next();
|
||||
if (o instanceof JTree && ((JTree) o).getSelectionModel() == mdl) {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine(" found it: " + o); // NOI18N
|
||||
}
|
||||
wizardPage.userInputReceived((Component) o, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (e instanceof DocumentEvent) {
|
||||
logger.fine("Looking for a JTextComponent for a DocumentEvent"); // NOI18N
|
||||
Document document = ((DocumentEvent) e).getDocument();
|
||||
for (Iterator i = listenedTo.iterator(); i.hasNext();) {
|
||||
Object o = i.next();
|
||||
if (o instanceof JTextComponent && ((JTextComponent) o).getDocument() == document) {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine(" found it: " + o); // NOI18N
|
||||
}
|
||||
wizardPage.userInputReceived((Component) o, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (e instanceof ListSelectionEvent) {
|
||||
logger.fine("Looking for a JList or JTable for a ListSelectionEvent"); // NOI18N
|
||||
ListSelectionModel model = (ListSelectionModel) ((ListSelectionEvent) e).getSource();
|
||||
for (Iterator i = listenedTo.iterator(); i.hasNext();) {
|
||||
Object o = i.next();
|
||||
if (o instanceof JList && ((JList) o).getSelectionModel() == model) {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine(" found it: " + o); // NOI18N
|
||||
}
|
||||
wizardPage.userInputReceived((Component) o, e);
|
||||
break;
|
||||
} else if (o instanceof JTable && ((JTable) o).getSelectionModel() == model) {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine(" found it: " + o); // NOI18N
|
||||
}
|
||||
wizardPage.userInputReceived((Component) o, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wizardPage.userInputReceived(null, e);
|
||||
}
|
||||
} finally {
|
||||
setIgnoreEvents(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
fire(e);
|
||||
}
|
||||
|
||||
public void propertyChange(PropertyChangeEvent e) {
|
||||
if (e.getSource() instanceof JComponent && "name".equals(e.getPropertyName())) {
|
||||
// Note - most components do NOT fire a property change on
|
||||
// setName(), but it is possible for this to be done intentionally
|
||||
if (e.getOldValue() instanceof String) {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("Name of component changed from " + e.getOldValue() + // NOI18N
|
||||
" to " + e.getNewValue() + ". Removing any values for " + // NOI18N
|
||||
e.getOldValue() + " from the wizard data map"); // NOI18N
|
||||
}
|
||||
wizardPage.removeFromMap(e.getOldValue());
|
||||
}
|
||||
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("Possibly update map for renamed component " + // NOI18N
|
||||
e.getSource());
|
||||
}
|
||||
|
||||
} else if (e.getSource() instanceof JFormattedTextField && "value".equals(e.getPropertyName())) {
|
||||
fire(e);
|
||||
wizardPage.maybeUpdateMap((JComponent) e.getSource());
|
||||
}
|
||||
}
|
||||
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
fire(e);
|
||||
}
|
||||
|
||||
public void componentAdded(ContainerEvent e) {
|
||||
// if (extListener != null && extListener.accept(e.getChild())) {
|
||||
// extListener.startListeningTo(e.getChild(), extNotifier);
|
||||
// listenedTo.add (e.getChild());
|
||||
// } else if (accept(e.getChild())) {
|
||||
if (accept (e.getChild())) {
|
||||
attachTo(e.getChild());
|
||||
}
|
||||
}
|
||||
|
||||
public void componentRemoved(ContainerEvent e) {
|
||||
if (extListener != null && extListener.accept (e.getChild())) {
|
||||
extListener.stopListeningTo (e.getChild());
|
||||
listenedTo.remove (e.getChild());
|
||||
} else if (accept(e.getChild())) {
|
||||
detachFrom(e.getChild());
|
||||
}
|
||||
}
|
||||
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
fire(e);
|
||||
}
|
||||
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
fire(e);
|
||||
}
|
||||
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
fire(e);
|
||||
}
|
||||
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
fire(e);
|
||||
}
|
||||
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
fire(e);
|
||||
}
|
||||
|
||||
public void valueChanged(TreeSelectionEvent e) {
|
||||
fire(e);
|
||||
}
|
||||
|
||||
public void tableChanged(TableModelEvent e) {
|
||||
fire(e);
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.awt.Container;
|
||||
|
||||
/**
|
||||
* A controller for the progress bar shown in the user interface. Used in
|
||||
* conjunction with <code>DeferredWizardResult</code> for cases where at
|
||||
* the conclusion of the wizard, the work to create the final wizard result
|
||||
* will take a while and needs to happen on a background thread.
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public interface ResultProgressHandle {
|
||||
|
||||
/**
|
||||
* Set the current position and total number of steps. Note it is
|
||||
* inadvisable to be holding any locks when calling this method, as it
|
||||
* may immediately update the GUI using
|
||||
* <code>EventQueue.invokeAndWait()</code>.
|
||||
*
|
||||
* @param currentStep the current step in the progress of computing the
|
||||
* result.
|
||||
* @param totalSteps the total number of steps. Must be greater than
|
||||
* or equal to currentStep.
|
||||
*/
|
||||
public abstract void setProgress (int currentStep, int totalSteps);
|
||||
|
||||
/**
|
||||
* Set the current position and total number of steps, and description
|
||||
* of what the computation is doing. Note it is
|
||||
* inadvisable to be holding any locks when calling this method, as it
|
||||
* may immediately update the GUI using
|
||||
* <code>EventQueue.invokeAndWait()</code>.
|
||||
* @param description Text to describe what is being done, which can
|
||||
* be displayed in the UI.
|
||||
* @param currentStep the current step in the progress of computing the
|
||||
* result.
|
||||
* @param totalSteps the total number of steps. Must be greater than
|
||||
* or equal to currentStep.
|
||||
*/
|
||||
public abstract void setProgress (String description, int currentStep, int totalSteps);
|
||||
|
||||
/**
|
||||
* Set the status as "busy" - a rotating icon will be displayed instead
|
||||
* of a percent complete progress bar.
|
||||
*
|
||||
* Note it is inadvisable to be holding any locks when calling this method, as it
|
||||
* may immediately update the GUI using
|
||||
* <code>EventQueue.invokeAndWait()</code>.
|
||||
* @param description Text to describe what is being done, which can
|
||||
* be displayed in the UI.
|
||||
*/
|
||||
public abstract void setBusy (String description);
|
||||
|
||||
/**
|
||||
* Call this method when the computation is complete, and pass in the
|
||||
* final result of the computation. The method doing the computation
|
||||
* (<code>DeferredWizardResult.start()</code> or something it
|
||||
* called) should exit immediately after calling this method. If the
|
||||
* <code>failed()</code> method is called after this method has been
|
||||
* called, a runtime exception may be thrown.
|
||||
* @param result the Object which was computed, if any.
|
||||
*/
|
||||
public abstract void finished(Object result);
|
||||
/**
|
||||
* Call this method if computation fails. The message may be some text
|
||||
* describing what went wrong, or null if no description.
|
||||
* @param message The text to display to the user. The method
|
||||
* doing the computation (<code>DeferredWizardResult.start()</code> or something it
|
||||
* called). If the <code>finished()</code> method is called after this
|
||||
* method has been called, a runtime exception may be thrown.
|
||||
* should exit immediately after calling this method.
|
||||
* It is A description of what went wrong, or null.
|
||||
* @param canNavigateBack whether or not the Prev button should be
|
||||
* enabled.
|
||||
*/
|
||||
public abstract void failed (String message, boolean canNavigateBack);
|
||||
|
||||
/**
|
||||
* Add the component to show for the progress display to the instructions panel.
|
||||
*/
|
||||
public abstract void addProgressComponents (Container panel);
|
||||
|
||||
/**
|
||||
* Returns true if the computation is still running, i.e., if neither finished or failed have been called.
|
||||
*
|
||||
* @return true if there is no result yet.
|
||||
*/
|
||||
public boolean isRunning();
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* The contents of this file are subject to the terms of the Common Development
|
||||
* and Distribution License (the License). You may not use this file except in
|
||||
* compliance with the License.
|
||||
* You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
* or http://www.netbeans.org/cddl.txt.
|
||||
* When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
* and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
* If applicable, add the following below the CDDL Header, with the fields
|
||||
* enclosed by brackets [] replaced by your own identifying information:
|
||||
* "Portions Copyrighted [year] [name of copyright owner]"
|
||||
*/
|
||||
/*
|
||||
* SimpleWizard.java
|
||||
*
|
||||
* Created on February 22, 2005, 2:33 PM
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
* A simple implementation of Wizard for use in wizards which have a
|
||||
* straightforward set of steps with no branching. To use, implement the
|
||||
* simplified interface SimpleWizard.Info and pass that to the constructor.
|
||||
*
|
||||
* @see SimpleWizardInfo
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
final class SimpleWizard implements WizardImplementation {
|
||||
|
||||
private final List listenerList
|
||||
= Collections.synchronizedList(new LinkedList());
|
||||
private final Map ids2panels = new HashMap();
|
||||
|
||||
final SimpleWizardInfo info;
|
||||
|
||||
private String currID = null;
|
||||
private boolean subwizard;
|
||||
|
||||
public SimpleWizard(WizardPanelProvider prov) {
|
||||
this(new SimpleWizardInfo(prov), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of SimpleWizard
|
||||
*/
|
||||
public SimpleWizard(SimpleWizardInfo info) {
|
||||
this.info = info;
|
||||
info.setWizard(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of SimpleWizard
|
||||
*/
|
||||
public SimpleWizard(SimpleWizardInfo info, boolean subwizard) {
|
||||
this.info = info;
|
||||
this.subwizard = subwizard;
|
||||
info.setWizard(this);
|
||||
}
|
||||
|
||||
public void addWizardObserver(WizardObserver observer) {
|
||||
listenerList.add(observer);
|
||||
}
|
||||
|
||||
public void removeWizardObserver(WizardObserver observer) {
|
||||
listenerList.remove(observer);
|
||||
}
|
||||
|
||||
public int getForwardNavigationMode() {
|
||||
int result = info.getFwdNavMode();
|
||||
if (!subwizard && ((result & WizardController.MODE_CAN_CONTINUE) != 0) && isLastStep())
|
||||
result = WizardController.MODE_CAN_FINISH;
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean isLastStep() {
|
||||
String[] steps = info.getSteps();
|
||||
return currID != null && steps.length > 0 && currID.equals(steps[steps.length - 1]);
|
||||
}
|
||||
|
||||
public String[] getAllSteps() {
|
||||
String[] allSteps = info.getSteps();
|
||||
String[] result = new String[allSteps.length];
|
||||
//Defensive copy
|
||||
System.arraycopy(allSteps, 0, result, 0, allSteps.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getStepDescription(String id) {
|
||||
int idx = Arrays.asList(info.getSteps()).indexOf(id);
|
||||
if (idx == -1)
|
||||
throw new IllegalArgumentException("Undefined id: " + id);
|
||||
return info.getDescriptions()[idx];
|
||||
}
|
||||
|
||||
public String getLongDescription(String id) {
|
||||
return info.getLongDescription(id);
|
||||
}
|
||||
|
||||
public JComponent navigatingTo(String id, Map settings) {
|
||||
// assert SwingUtilities.isEventDispatchThread();
|
||||
|
||||
// if info.getSteps() does not yet contain the ID, then create it
|
||||
JComponent result = (JComponent) ids2panels.get(id);
|
||||
currID = id;
|
||||
if (result == null) {
|
||||
result = info.createPanel(id, settings);
|
||||
ids2panels.put(id, result);
|
||||
} else {
|
||||
info.update();
|
||||
info.recycleExistingPanel(id, settings, result);
|
||||
}
|
||||
fireSelectionChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getCurrentStep() {
|
||||
return currID;
|
||||
}
|
||||
|
||||
public String getNextStep() {
|
||||
if (!info.isValid())
|
||||
return null;
|
||||
if ((info.getFwdNavMode() & WizardController.MODE_CAN_CONTINUE) == 0)
|
||||
return null;
|
||||
|
||||
int idx = currentStepIndex();
|
||||
if (idx < info.getSteps().length - 1)
|
||||
return info.getSteps()[idx + 1];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPreviousStep() {
|
||||
int idx = currentStepIndex();
|
||||
if (idx < info.getSteps().length && idx > 0)
|
||||
return info.getSteps()[idx - 1];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
int currentStepIndex() {
|
||||
int idx = 0;
|
||||
if (currID != null)
|
||||
idx = Arrays.asList(info.getSteps()).indexOf(currID);
|
||||
return idx;
|
||||
}
|
||||
|
||||
void fireNavigability() {
|
||||
WizardObserver[] listeners = (WizardObserver[]) listenerList.toArray(new WizardObserver[0]);
|
||||
|
||||
for (int i = listeners.length - 1; i >= 0; i--) {
|
||||
WizardObserver l = (WizardObserver) listeners[i];
|
||||
l.navigabilityChanged(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireSelectionChanged() {
|
||||
WizardObserver[] listeners = (WizardObserver[]) listenerList.toArray(new WizardObserver[0]);
|
||||
|
||||
for (int i = listeners.length - 1; i >= 0; i--) {
|
||||
WizardObserver l = (WizardObserver) listeners[i];
|
||||
l.selectionChanged(null);
|
||||
}
|
||||
}
|
||||
|
||||
public Object finish(Map settings) throws WizardException {
|
||||
return info.finish(settings);
|
||||
}
|
||||
|
||||
public boolean cancel(Map settings) {
|
||||
return info.cancel(settings);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return info.getTitle();
|
||||
}
|
||||
|
||||
public String getProblem() {
|
||||
return info.getProblem();
|
||||
}
|
||||
|
||||
public boolean isBusy() {
|
||||
return info.isBusy();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return info.hashCode() ^ 17;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof SimpleWizard)
|
||||
return ((SimpleWizard) o).info.equals(info);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "SimpleWizard for " + info;
|
||||
}
|
||||
}
|
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* The contents of this file are subject to the terms of the Common Development
|
||||
* and Distribution License (the License). You may not use this file except in
|
||||
* compliance with the License.
|
||||
* You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
* or http://www.netbeans.org/cddl.txt.
|
||||
* When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
* and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
* If applicable, add the following below the CDDL Header, with the fields
|
||||
* enclosed by brackets [] replaced by your own identifying information:
|
||||
* "Portions Copyrighted [year] [name of copyright owner]"
|
||||
*/
|
||||
|
||||
/*
|
||||
* SimpleWizardInfo.java
|
||||
*
|
||||
* Created on March 4, 2005, 9:46 PM
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
|
||||
/**
|
||||
* Provides information about a simple wizard. Wraps a
|
||||
* WizardPanelProvider and provides a connection to the instance of
|
||||
* SimpleWizard created for it, acting as the WizardController for
|
||||
* calls to WizardPanelProvider.createPanel().
|
||||
*/
|
||||
final class SimpleWizardInfo implements WizardControllerImplementation {
|
||||
|
||||
private WeakReference wizard = null;
|
||||
private final String[] descriptions;
|
||||
private final String[] steps;
|
||||
final int[] navModeByPanel;
|
||||
private String problem = null;
|
||||
private final String title;
|
||||
private final WizardPanelProvider provider;
|
||||
private boolean busy = false;
|
||||
|
||||
SimpleWizardInfo(WizardPanelProvider provider) {
|
||||
this(provider.title, provider.steps, provider.descriptions, provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of Info, which will provide panels for a simple,
|
||||
* non-branching wizard, passing a localized title, a list of steps
|
||||
* and descriptions.
|
||||
*/
|
||||
protected SimpleWizardInfo(String title, String[] steps, String[] descriptions, WizardPanelProvider provider) {
|
||||
if (steps == null)
|
||||
throw new NullPointerException("Null steps");
|
||||
if (descriptions == null)
|
||||
throw new NullPointerException("Null descriptions");
|
||||
this.steps = steps;
|
||||
this.descriptions = descriptions;
|
||||
if (new HashSet(Arrays.asList(steps)).size() < steps.length)
|
||||
throw new IllegalArgumentException("Duplicate ID: " + Arrays.asList(steps));
|
||||
if (descriptions.length != steps.length)
|
||||
if (steps.length != descriptions.length + 1 && !WizardImplementation.UNDETERMINED_STEP.equals(steps[steps.length - 1]))
|
||||
throw new IllegalArgumentException("Steps and descriptions "
|
||||
+ "array lengths not equal: " + Arrays.asList(steps) + ":"
|
||||
+ Arrays.asList(descriptions));
|
||||
navModeByPanel = new int[steps.length];
|
||||
Arrays.fill(navModeByPanel, -1);
|
||||
this.title = title;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
final void setWizard(SimpleWizard wizard) {
|
||||
this.wizard = new WeakReference(wizard);
|
||||
}
|
||||
|
||||
final SimpleWizard getWizard() {
|
||||
return wizard != null ? (SimpleWizard) wizard.get() : null;
|
||||
}
|
||||
|
||||
final SimpleWizard createWizard() {
|
||||
return new SimpleWizard(this);
|
||||
}
|
||||
|
||||
//pkg private for unit tests
|
||||
final WizardController controller = new WizardController(this);
|
||||
|
||||
/**
|
||||
* Create a panel that represents a named step in the wizard.
|
||||
* This method will be called exactly <i>once</i> in the life of
|
||||
* a wizard. The panel should retain the passed settings Map, and
|
||||
* add/remove values from it as the user enters information, calling
|
||||
* <code>setProblem()</code> and <code>setCanFinish()</code> as
|
||||
* appropriate in response to user input.
|
||||
*
|
||||
* @param id The name of the step, as supplied in the constructor
|
||||
* @param settings A Map containing settings from earlier steps in
|
||||
* the wizard
|
||||
*
|
||||
* @return A JComponent
|
||||
*/
|
||||
protected JComponent createPanel(String id, Map settings) {
|
||||
try {
|
||||
JComponent result = provider.createPanel(controller, id, settings);
|
||||
if (result instanceof WizardPage) {
|
||||
((WizardPage) result).setController(controller);
|
||||
((WizardPage) result).setWizardDataMap(settings);
|
||||
}
|
||||
return result;
|
||||
} catch (RuntimeException re) {
|
||||
JTextArea jta = new JTextArea();
|
||||
jta.setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, Color.RED));
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
PrintStream str = new PrintStream(buf);
|
||||
re.printStackTrace(str);
|
||||
jta.setText(new String(buf.toByteArray()));
|
||||
setProblem(re.getLocalizedMessage());
|
||||
return new JScrollPane(jta);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate whatever object (if any) the wizard creates from its
|
||||
* gathered data.
|
||||
*/
|
||||
protected Object finish(Map settings) throws WizardException {
|
||||
//XXX fixme
|
||||
// assert canFinish();
|
||||
|
||||
// SKNUTSON: the "canFinish" behavior is not working
|
||||
// instead, panels must implement the WizardPanel interface
|
||||
// and have allowFinish return false
|
||||
// if ( ! canFinish())
|
||||
// {
|
||||
// throw new RuntimeException ("Can't finish right now");
|
||||
// }
|
||||
return provider.finish(settings);
|
||||
}
|
||||
|
||||
public String getLongDescription(String id) {
|
||||
return provider.getLongDescription(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* The method provides a chance to call setProblem() or setCanFinish() when
|
||||
* the user re-navigates to a panel they've already seen - in the case
|
||||
* that the user pressed the Previous button and then the Next button.
|
||||
* <p>
|
||||
* The default implementation does nothing, which is sufficient for
|
||||
* most implementations. If whether this panel is valid or not could
|
||||
* have changed because of changed data from a previous panel,
|
||||
* you may want to override this method to ensure validity and currNavMode
|
||||
* are set correctly.
|
||||
* <p>
|
||||
* This method will <i>not</i> be called when a panel is first instantiated
|
||||
* -
|
||||
* <code>createPanel()</code> is expected to set validity and currNavMode
|
||||
* appropriately.
|
||||
* <p>
|
||||
* The settings Map passed to this method will always be the same
|
||||
* Settings map instance that was passed to <code>createPanel()</code>
|
||||
* when the panel was created.
|
||||
*/
|
||||
protected void recycleExistingPanel(String id, Map settings, JComponent panel) {
|
||||
provider.recycle(id, controller, settings, panel);
|
||||
}
|
||||
|
||||
private int index() {
|
||||
SimpleWizard wizard = getWizard();
|
||||
if (wizard != null)
|
||||
return wizard.currentStepIndex();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final void setBusy(boolean value) {
|
||||
if (value != busy) {
|
||||
busy = value;
|
||||
fire();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not the contents of this panel are valid. When
|
||||
* user-entered information in a panel changes, call this method as
|
||||
* appropriate.
|
||||
*/
|
||||
public final void setProblem(String value) {
|
||||
this.problem = value;
|
||||
int idx = index();
|
||||
provider.setKnownProblem(problem, idx);
|
||||
fire();
|
||||
}
|
||||
|
||||
private int currNavMode = WizardController.MODE_CAN_CONTINUE;
|
||||
|
||||
/**
|
||||
* Set whether or not the Finish button should be enabled. Neither
|
||||
* the Finish nor Next buttons will be enabled if setProblem has
|
||||
* been called with non-null.
|
||||
* <p>
|
||||
* Legal values are: WizardController.MODE_CAN_CONTINUE,
|
||||
* WizardController.MODE_CAN_FINISH or
|
||||
* WizardController.MODE_CAN_CONTINUE_OR_FINISH.
|
||||
* <p>
|
||||
* This method is used to set what means of forward navigation should
|
||||
* be available if the current panel is in a valid state (problem is
|
||||
* null). It is <i>not</i> a way to disable both the next button
|
||||
* and the finish button, only a way to choose either or both.
|
||||
*
|
||||
* @param value The forward navigation mode
|
||||
*
|
||||
* @see setProblem
|
||||
*/
|
||||
public final void setForwardNavigationMode(int value) {
|
||||
switch (value) {
|
||||
case WizardController.MODE_CAN_CONTINUE:
|
||||
case WizardController.MODE_CAN_FINISH:
|
||||
case WizardController.MODE_CAN_CONTINUE_OR_FINISH:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(Integer.toString(value));
|
||||
}
|
||||
if (currNavMode != value) {
|
||||
currNavMode = value;
|
||||
fire();
|
||||
}
|
||||
navModeByPanel[index()] = value;
|
||||
}
|
||||
|
||||
public final int getFwdNavMode() {
|
||||
return currNavMode;
|
||||
}
|
||||
|
||||
final String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
final void update() {
|
||||
int idx = index();
|
||||
boolean change = navModeByPanel[idx] != -1 && currNavMode != navModeByPanel[idx];
|
||||
setProblem(provider.getKnownProblem(idx));
|
||||
currNavMode = navModeByPanel[idx] == -1 ? WizardController.MODE_CAN_CONTINUE : navModeByPanel[idx];
|
||||
if (change)
|
||||
fire();
|
||||
}
|
||||
|
||||
final void fire() {
|
||||
WizardImplementation wiz = getWizard();
|
||||
if (wiz != null)
|
||||
getWizard().fireNavigability();
|
||||
}
|
||||
|
||||
final boolean isValid() {
|
||||
return getProblem() == null;
|
||||
}
|
||||
|
||||
final boolean canFinish() {
|
||||
return isValid() && (currNavMode != -1 && (currNavMode
|
||||
& WizardController.MODE_CAN_FINISH) != 0);
|
||||
}
|
||||
|
||||
final boolean canContinue() {
|
||||
return isValid() && (currNavMode == -1 || (currNavMode
|
||||
& WizardController.MODE_CAN_CONTINUE) != 0);
|
||||
}
|
||||
|
||||
String[] getDescriptions() {
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
String[] getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
// lookup the step by name
|
||||
boolean containsStep(String name) {
|
||||
for (int i = 0; i < steps.length; i++) {
|
||||
String step = steps[i];
|
||||
if (name.equals(step))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final String getProblem() {
|
||||
return problem;
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
return busy;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o != null && o.getClass() == getClass()) {
|
||||
SimpleWizardInfo info = (SimpleWizardInfo) o;
|
||||
|
||||
// assert info.descriptions != null : "Info.descriptions == null";
|
||||
// assert info.steps != null : "Info.steps == null";
|
||||
if (info.descriptions == null || info.steps == null)
|
||||
throw new RuntimeException("Invalid info object");
|
||||
|
||||
return Arrays.equals(info.descriptions, descriptions)
|
||||
&& Arrays.equals(info.steps, steps)
|
||||
&& info.title == title;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 0;
|
||||
for (int i = 0; i < steps.length; i++)
|
||||
result += (steps[i].hashCode() * (i + 1)) ^ 31;
|
||||
return result + (title == null ? 0 : title.hashCode());
|
||||
}
|
||||
|
||||
boolean cancel(Map settings) {
|
||||
return provider.cancel(settings);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "SimpleWizardInfo@" + System.identityHashCode(this) + " for "
|
||||
+ provider;
|
||||
}
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
/*
|
||||
* Summary.java
|
||||
*
|
||||
* Created on September 24, 2006, 4:05 AM
|
||||
*
|
||||
* To change this template, choose Tools | Template Manager
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Font;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
* Object which may be returned from <code>WizardPage.WizardResultProducer.finish()</code>
|
||||
* or <code>WizardPanelProvider.finish()</code>, or passed to
|
||||
* <code>DeferredWizardResult.ResultProgressHandle.finish()</code>. If an
|
||||
* instance of <code>Summary</code> is used, then the UI should, rather
|
||||
* than disappearing, show the component provided by the <code>Summary</code>
|
||||
* object. Convenience constructors are provided for plain text and list style
|
||||
* views.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public class Summary {
|
||||
private final Component comp;
|
||||
private Object result;
|
||||
|
||||
//constructors package private - only unit tests should be able to subclass
|
||||
//Summary
|
||||
|
||||
Summary(String text, Object result) {
|
||||
//XXX this is creating components off the AWT thread - needs to change
|
||||
//to use invokeAndWait where appropriate
|
||||
if (text == null) {
|
||||
throw new NullPointerException ("Text is null"); //NOI18N
|
||||
}
|
||||
if (text.trim().length() == 0) {
|
||||
throw new IllegalArgumentException ("Text is empty or all " + //NOI18N
|
||||
"whitespace"); //NOI18N
|
||||
}
|
||||
this.result = result;
|
||||
JTextArea jta = new JTextArea();
|
||||
jta.setText (text);
|
||||
jta.setWrapStyleWord(true);
|
||||
jta.setLineWrap(true);
|
||||
jta.getCaret().setBlinkRate(0);
|
||||
jta.setEditable(false);
|
||||
jta.getCaret().setVisible(true);
|
||||
Font f = UIManager.getFont ("Label.font");
|
||||
if (f != null) { //may be on old GTK L&F, etc.
|
||||
jta.setFont (f);
|
||||
}
|
||||
comp = new JScrollPane (jta);
|
||||
}
|
||||
|
||||
Summary(String[] items, Object result) {
|
||||
if (items == null) {
|
||||
throw new NullPointerException ("Items array null"); //NOI18N
|
||||
}
|
||||
if (items.length == 0) {
|
||||
throw new IllegalArgumentException ("Items array empty"); //NOI18N
|
||||
}
|
||||
this.result = result;
|
||||
JList list = new JList(items);
|
||||
comp = new JScrollPane (list);
|
||||
}
|
||||
|
||||
Summary(Component comp, Object result) {
|
||||
this.result = result;
|
||||
this.comp = comp;
|
||||
if (comp == null) {
|
||||
throw new NullPointerException ("Null component"); //NOI18N
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>Summary</code> object that will display the passed
|
||||
* <code>String</code>s in a <code>JList</code> or similar.
|
||||
* @param items A non-null list of one or more Strings to be displayed
|
||||
* @param result The result that should be returned when the Wizard is
|
||||
* closed
|
||||
* @return the requested <code>Summary</code> object
|
||||
*/
|
||||
public static Summary create (String[] items, Object result) {
|
||||
return new Summary (items, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>Summary</code> object that will display the passed component.
|
||||
* @param comp A custom component to show on the summary page after the
|
||||
* Wizard has been completed
|
||||
* @param result The result that should be returned when the <code>Wizard</code> is
|
||||
* closed
|
||||
* @return the requested <code>Summary</code> object
|
||||
*/
|
||||
public static Summary create (Component comp, Object result) {
|
||||
return new Summary (comp, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>Summary</code> object which will display the
|
||||
* passed <code>String</code> in a text component of some sort.
|
||||
* @param text The text to display - must be non-null, greater than zero
|
||||
* length and not completely whitespace
|
||||
* @param result The result that should be returned when the Wizard is
|
||||
* closed
|
||||
* @return the requested <code>Summary</code> object
|
||||
*/
|
||||
public static Summary create (String text, Object result) {
|
||||
return new Summary (text, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component that will display the summary information.
|
||||
* @return an appropriate component, the type of which may differ depending
|
||||
* on the factory method used to create this component
|
||||
*/
|
||||
public Component getSummaryComponent() {
|
||||
return comp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object that represents the actual result of whatever the <code>Wizard</code>
|
||||
* that created this <code>Summary</code> object computes. Note this method may not
|
||||
* return another instance of <code>Summary</code> or an instance of
|
||||
* <code>DeferredWizardResult</code>.
|
||||
* @return the object passed to the factory method that created this
|
||||
* Summary object, or null.
|
||||
*/
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
final class Util {
|
||||
private Util(){}
|
||||
|
||||
/**
|
||||
* Get an array of step ids from an array of WizardPages
|
||||
*/
|
||||
static String[] getSteps(WizardPage[] pages) {
|
||||
String[] result = new String[pages.length];
|
||||
|
||||
Set uniqueNames = new HashSet(pages.length);
|
||||
for (int i = 0; i < pages.length; i++) {
|
||||
result[i] = pages[i].id();
|
||||
if (result[i] == null || uniqueNames.contains(result[i])) {
|
||||
result[i] = uniquify (getIDFromStaticMethod(pages[i].getClass()),
|
||||
uniqueNames);
|
||||
pages[i].id = result[i];
|
||||
}
|
||||
uniqueNames.add (result[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static String uniquify (String s, Set /* <String> */ used) {
|
||||
String test = s;
|
||||
if (test != null) {
|
||||
int ix = 0;
|
||||
while (used.contains(test)) {
|
||||
test = s + "_" + ix++;
|
||||
}
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of descriptions from an array of WizardPages
|
||||
*/
|
||||
static String[] getDescriptions(WizardPage[] pages) {
|
||||
String[] result = new String[pages.length];
|
||||
|
||||
for (int i = 0; i < pages.length; i++) {
|
||||
result[i] = pages[i].description();
|
||||
if (result[i] == null) {
|
||||
result[i] = getDescriptionFromStaticMethod (pages[i].getClass());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static String getIDFromStaticMethod (Class clazz) {
|
||||
// System.err.println("GetID by method for " + clazz);
|
||||
String result = null;
|
||||
try {
|
||||
Method m = clazz.getDeclaredMethod("getStep", new Class[] {});
|
||||
// assert m.getReturnType() == String.class;
|
||||
result = (String) m.invoke(clazz, (Object[]) null);
|
||||
if (result == null) {
|
||||
throw new NullPointerException ("getStep may not return null");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
//do nothing
|
||||
}
|
||||
return result == null ? clazz.getName() : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of steps by looking for a static method getID() on each
|
||||
* class object passed
|
||||
*/
|
||||
static String[] getSteps(Class[] pages) {
|
||||
if (pages == null) {
|
||||
throw new NullPointerException("Null array of classes"); //NOI18N
|
||||
}
|
||||
|
||||
String[] result = new String[pages.length];
|
||||
|
||||
Set used = new HashSet (pages.length);
|
||||
for (int i = 0; i < pages.length; i++) {
|
||||
if (pages[i] == null) {
|
||||
throw new NullPointerException("Null at " + i + " in array " + //NOI18N
|
||||
"of panel classes"); //NOI18N
|
||||
}
|
||||
|
||||
if (!WizardPage.class.isAssignableFrom(pages[i])) {
|
||||
throw new IllegalArgumentException(pages[i] +
|
||||
" is not a subclass of WizardPage"); //NOI18N
|
||||
}
|
||||
result[i] = uniquify (getIDFromStaticMethod (pages[i]), used);
|
||||
if (result[i] == null) {
|
||||
result[i] = pages[i].getName();
|
||||
}
|
||||
}
|
||||
// System.err.println("Returning " + Arrays.asList(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
// /** Determine if a default constructor is present for a class */
|
||||
// private static boolean hasDefaultConstructor (Class clazz) {
|
||||
// try {
|
||||
// Constructor c = clazz.getConstructor(new Class[0]);
|
||||
// return c != null;
|
||||
// } catch (Exception e) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Get an array of descriptions by looking for the static method
|
||||
* getDescription() on each passed class object
|
||||
*/
|
||||
static String[] getDescriptions(Class[] pages) {
|
||||
String[] result = new String[pages.length];
|
||||
|
||||
for (int i = 0; i < pages.length; i++) {
|
||||
result[i] = getDescriptionFromStaticMethod(pages[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static String getDescriptionFromStaticMethod(Class clazz) {
|
||||
String result = null;
|
||||
Method m;
|
||||
try {
|
||||
m = clazz.getDeclaredMethod("getDescription", (Class[]) null); //NOI18N
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Could not find or access " + //NOI18N
|
||||
"public static String " + clazz.getName() + //NOI18N
|
||||
".getDescription() - make sure it exists"); //NOI18N
|
||||
}
|
||||
|
||||
if (m.getReturnType() != String.class) {
|
||||
throw new IllegalArgumentException("getStep has wrong " //NOI18N
|
||||
+ " return type: " + m.getReturnType() + " on " + //NOI18N
|
||||
clazz);
|
||||
}
|
||||
|
||||
if (!Modifier.isStatic(m.getModifiers())) {
|
||||
throw new IllegalArgumentException("getStep is not " + //NOI18N
|
||||
"static on " + clazz); //NOI18N
|
||||
}
|
||||
|
||||
try {
|
||||
m.setAccessible(true);
|
||||
result= (String) m.invoke(null, (Object[]) null);
|
||||
} catch (InvocationTargetException ite) {
|
||||
throw new IllegalArgumentException("Could not invoke " + //NOI18N
|
||||
"public static String " + clazz.getName() + //NOI18N
|
||||
".getDescription() - make sure it exists."); //NOI18N
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new IllegalArgumentException("Could not invoke " + //NOI18N
|
||||
"public static String " + clazz.getName() + //NOI18N
|
||||
".getDescription() - make sure it exists."); //NOI18N
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,364 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JComponent;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.WizardDisplayer;
|
||||
|
||||
/**
|
||||
* Encapsulates the logic and state of a Wizard. A Wizard gathers information
|
||||
* into a Map, and then performs some action with that information when the
|
||||
* user clicks Finish. To display a wizard, pass it to one of the methods
|
||||
* on <code>WizardDisplayer.getDefault()</code>.
|
||||
* <p>
|
||||
* A Wizard is a series of one or more steps represented
|
||||
* by panels in the user interface. Each step is identified by a unique String ID.
|
||||
* Panels are created, usually on-demand, as the user navigates through
|
||||
* the UI of the wizard. Panels typically listen on components they contain
|
||||
* and put values into the Map where the wizard gathers data. Note that if the
|
||||
* user navigates <i>backward</i>, data entered on pages after the current one
|
||||
* disappears from the Map.
|
||||
* <p>
|
||||
* To create a Wizard, you do not implement or instantiate this class directly,
|
||||
* but rather, use one of the convenience classes in this package. There are
|
||||
* three:
|
||||
* <ul>
|
||||
* <li><code>WizardPage</code> - use or subclass WizardPage, and pass an array
|
||||
* of instances, or an array of the <code>Class</code> objects of your subclasses
|
||||
* to <code>WizardPage.createWizard()</code>. This class offers the added
|
||||
* convenience that standard Swing components will be listened to automatically,
|
||||
* and if their Name property is set, the value from the component will be
|
||||
* automatically put into the settings map.
|
||||
* </li>
|
||||
*
|
||||
* <li><code>WizardPanelProvider</code> - subclass this to create a Wizard
|
||||
* with a fixed set of steps. You provide a set of unique ID strings to the
|
||||
* constructor, for all of the steps in the wizard, and override
|
||||
* createPanel() to create the GUI component that should be displayed for
|
||||
* each step - it will be called on demand as the user moves through the
|
||||
* wizard</li>
|
||||
*
|
||||
* <li><code>WizardBranchController</code> - this is for creating complex
|
||||
* wizards with decision points after which the future steps change, depending
|
||||
* on what the user chooses. Create a simple wizard using WizardPage or
|
||||
* WizardPanelProvider to represent the initial steps.
|
||||
* Then override <code>getWizardForStep()</code> or
|
||||
* <code>getPanelProviderForStep()</code> to return a different Wizard to
|
||||
* represent the remaining steps at any point where the set of future steps
|
||||
* changes. You can have as many branch points as you want, simply by
|
||||
* using WizardBranchController to create the wizards for different decision
|
||||
* points.
|
||||
* <p>
|
||||
* In other words, a wizard with a different set of panels (or number of steps)
|
||||
* depending on the user's decision is really three wizards composed into one -
|
||||
* one wizard that provides the initial set of steps, and then two others, one
|
||||
* or the other of which will actually provide the steps/panels after the
|
||||
* decision point (the Wizards are created on demand, for efficiency, so if
|
||||
* the user never changes his or her mind at the decision point, only two
|
||||
* of the three Wizards are ever actually created).
|
||||
* </li></ul>
|
||||
*
|
||||
* @see org.jackhuang.hellominecraft.utils.views.wizard.api.WizardDisplayer
|
||||
* @see WizardPage
|
||||
* @see WizardPanelProvider
|
||||
* @see WizardBranchController
|
||||
*
|
||||
* @author Timothy Boudreau
|
||||
*/
|
||||
public final class Wizard {
|
||||
/**
|
||||
* Constant that can be returned by <code>getForwardNavigationMode()</code>
|
||||
* to indicate that the Next button can be enabled (or the Finish button
|
||||
* if the current panel is the last one in the wizard).
|
||||
*/
|
||||
public static final int MODE_CAN_CONTINUE =
|
||||
WizardController.MODE_CAN_CONTINUE;
|
||||
|
||||
/**
|
||||
* Constant that can be returned by <code>getForwardNavigationMode</code> to indicate
|
||||
* that the Finish button can be enabled if the problem string is null.
|
||||
*/
|
||||
public static final int MODE_CAN_FINISH =
|
||||
WizardController.MODE_CAN_FINISH;
|
||||
/**
|
||||
* Constant that can be returned by <code>getForwardNavigationMode</code> to indicate
|
||||
* that both the Finish and Next buttons can be enabled if the problem
|
||||
* string is null. This value is a bitmask - i.e.
|
||||
* <code>MODE_CAN_CONTINUE_OR_FINISH == MODE_CAN_CONTINUE |
|
||||
* MODE_CAN_FINISH</code>
|
||||
*/
|
||||
public static final int MODE_CAN_CONTINUE_OR_FINISH =
|
||||
WizardController.MODE_CAN_CONTINUE_OR_FINISH;
|
||||
|
||||
/**
|
||||
* Special panel ID key indicating a branch point in the wizard,
|
||||
* after which the next step(s) are unknown.
|
||||
*/
|
||||
public static final String UNDETERMINED_STEP = "_#UndeterminedStep";
|
||||
|
||||
|
||||
final WizardImplementation impl; //package private for unit tests
|
||||
|
||||
/** Creates a new instance of Wizard */
|
||||
Wizard(WizardImplementation impl) {
|
||||
this.impl = impl;
|
||||
if (impl == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the wizard that the user is navigating to a different panel,
|
||||
* as identified by the passed <code>id</code>.
|
||||
* @param id The id of the panel being navigated to
|
||||
* @param wizardData The data gathered thus far as the user has progressed
|
||||
* through the wizard. The contents of this map should not contain any
|
||||
* key/values that were assigned on future panels, if the user is
|
||||
* navigating backward.
|
||||
* @return The component that should be shown for step <code>id</code>
|
||||
* of the <code>Wizard</code>
|
||||
*/
|
||||
public JComponent navigatingTo(String id, Map wizardData) {
|
||||
return impl.navigatingTo(id, wizardData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current step the wizard is on, as determined by the most recent
|
||||
* call to <code>navigatingTo()</code>.
|
||||
*/
|
||||
public String getCurrentStep() {
|
||||
return impl.getCurrentStep();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id of the step that comes after current step returned by
|
||||
* <code>getCurrentStep()</code>.
|
||||
* @return Null if this is the last step of the wizard;
|
||||
* <code>UNDETERMINED_STEP</code> if this is a branch point and the
|
||||
* user yet needs to do some interaction with the UI of the current
|
||||
* panel to trigger computation of the id of the next step; otherwise,
|
||||
* the unique id of the next step.
|
||||
*/
|
||||
public String getNextStep() {
|
||||
return impl.getNextStep();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id of the preceding step to the current one as returned by
|
||||
* <code>getCurrentStep()</code>, or null if the current step is the
|
||||
* first page of the wizard.
|
||||
* @return the id of the previous step or null
|
||||
*/
|
||||
public String getPreviousStep() {
|
||||
return impl.getPreviousStep();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the problem string that should be displayed to the user.
|
||||
* @return A string describing what the user needs to do to enable
|
||||
* the Next or Finish buttons, or null if the buttons may be enabled
|
||||
*/
|
||||
public String getProblem() {
|
||||
return impl.getProblem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string IDs of all known steps in this wizard, terminating
|
||||
* with <code>UNDETERMINED_STEP</code> if subsequent steps of the
|
||||
* wizard depend on the user's interaction beyond that point.
|
||||
* @return an array of strings which may individually be passed to
|
||||
* <code>navigatingTo</code> to change the current step of the wizard
|
||||
*/
|
||||
public String[] getAllSteps() {
|
||||
return impl.getAllSteps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long description for this panel. The long description should be
|
||||
* used in preference to the short description in the top of a wizard
|
||||
* panel in the UI, if it returns non-null.
|
||||
* @param stepId The ID of the step for which a description is requested
|
||||
* @return A more detailed localized description or null
|
||||
*/
|
||||
public String getLongDescription(String stepId) {
|
||||
return impl.getLongDescription (stepId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a localized String description of the step for the passed id,
|
||||
* which may be displayed in the UI of the wizard.
|
||||
* @param id A step id among those returned by <code>getAllSteps()</code>
|
||||
*/
|
||||
public String getStepDescription(String id) {
|
||||
return impl.getStepDescription(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user has clicked the finish button. This method
|
||||
* computes whatever the result of the wizard is.
|
||||
* @param settings The complete set of key-value pairs gathered by the
|
||||
* various panels as the user proceeded through the wizard
|
||||
* @return An implementation-dependent object that is the outcome of
|
||||
* the wizard. May be null. Special return values are instances of
|
||||
* DeferredWizardResult and Summary which will affect the behavior of
|
||||
* the UI.
|
||||
*/
|
||||
public Object finish(Map settings) throws WizardException {
|
||||
return impl.finish(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user has clicked the Cancel button in the wizard UI
|
||||
* or otherwise closed the UI component without completing the wizard.
|
||||
* @param settings The (possibly incomplete) set of key-value pairs gathered by the
|
||||
* various panels as the user proceeded through the wizard
|
||||
* @return true if the UI may indeed be closed, false if closing should
|
||||
* not be permitted
|
||||
*/
|
||||
public boolean cancel (Map settings) {
|
||||
return impl.cancel(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the Wizard that should be displayed in its dialog
|
||||
* titlebar (if any).
|
||||
* @return A localized string
|
||||
*/
|
||||
public String getTitle() {
|
||||
return impl.getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the wizard is busy doing work in a background thread and
|
||||
* all navigation controls should be disabled.
|
||||
* @return whether or not the wizard is busy
|
||||
*/
|
||||
public boolean isBusy() {
|
||||
return impl.isBusy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the navigation mode, which determines the enablement state of
|
||||
* the Next and Finish buttons.
|
||||
* @return one of the constants <code>MODE_CAN_CONTINUE</code>,
|
||||
* <code>MODE_CAN_FINISH</code>, or <code>MODE_CAN_CONTINUE_OR_FINISH</code>.
|
||||
*/
|
||||
public int getForwardNavigationMode() {
|
||||
return impl.getForwardNavigationMode();
|
||||
}
|
||||
|
||||
private volatile boolean listeningToImpl = false;
|
||||
private final List listeners = Collections.synchronizedList (
|
||||
new LinkedList());
|
||||
|
||||
private WizardObserver l = null;
|
||||
/**
|
||||
* Add a WizardObserver that will be notified of navigability and step
|
||||
* changes.
|
||||
* @param observer A WizardObserver
|
||||
*/
|
||||
public void addWizardObserver(WizardObserver observer) {
|
||||
listeners.add(observer);
|
||||
if (!listeningToImpl) {
|
||||
l = new ImplL();
|
||||
impl.addWizardObserver(l);
|
||||
listeningToImpl = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a WizardObserver.
|
||||
* @param observer A WizardObserver
|
||||
*/
|
||||
public void removeWizardObserver(WizardObserver observer) {
|
||||
listeners.remove(observer);
|
||||
if (listeningToImpl && listeners.size() == 0) {
|
||||
impl.removeWizardObserver(l);
|
||||
l = null;
|
||||
listeningToImpl = false;
|
||||
}
|
||||
}
|
||||
|
||||
private class ImplL implements WizardObserver {
|
||||
public void stepsChanged(Wizard wizard) {
|
||||
WizardObserver[] l = (WizardObserver[]) listeners.toArray(
|
||||
new WizardObserver[listeners.size()]);
|
||||
for (int i = 0; i < l.length; i++) {
|
||||
l[i].stepsChanged(Wizard.this);
|
||||
}
|
||||
}
|
||||
|
||||
public void navigabilityChanged(Wizard wizard) {
|
||||
WizardObserver[] l = (WizardObserver[]) listeners.toArray(
|
||||
new WizardObserver[listeners.size()]);
|
||||
for (int i = 0; i < l.length; i++) {
|
||||
l[i].navigabilityChanged(Wizard.this);
|
||||
}
|
||||
}
|
||||
|
||||
public void selectionChanged(Wizard wizard) {
|
||||
WizardObserver[] l = (WizardObserver[]) listeners.toArray(
|
||||
new WizardObserver[listeners.size()]);
|
||||
for (int i = 0; i < l.length; i++) {
|
||||
l[i].selectionChanged(Wizard.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return impl.hashCode() * 17;
|
||||
}
|
||||
|
||||
public boolean equals (Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
} else if (o instanceof Wizard) {
|
||||
return impl.equals (((Wizard)o).impl);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to WizardDisplayer.showWizard()
|
||||
*/
|
||||
public void show () {
|
||||
WizardDisplayer.showWizard(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to WizardDisplayer.showWizard()
|
||||
*/
|
||||
public Object show (Wizard wizard, Action help) {
|
||||
return WizardDisplayer.showWizard (wizard, help);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to WizardDisplayer.showWizard()
|
||||
*/
|
||||
public Object show (Wizard wizard, Rectangle r) {
|
||||
return WizardDisplayer.showWizard (wizard, r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to WizardDisplayer.showWizard()
|
||||
*/
|
||||
public Object show (Wizard wizard, Rectangle r, Action help) {
|
||||
return WizardDisplayer.showWizard (wizard, r, help, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
/*
|
||||
* WizardBranchController.java
|
||||
*
|
||||
* Created on March 5, 2005, 6:33 PM
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Extend this class to create wizards which have branch points in them -
|
||||
* either override <code>getWizardForStep</code> to return one or another a wizard which
|
||||
* represents the subsequent steps after a decision point, or override
|
||||
* <code>getPanelProviderForStep</code> to provide instances of <code>WizardPanelProvider</code>
|
||||
* if there are no subsequent branch points and the continuation is a
|
||||
* simple wizard.
|
||||
* <p>
|
||||
* The basic idea is to supply a base wizard for the initial steps, stopping
|
||||
* at the branch point. The panel for the branch point should put enough
|
||||
* information into the settings map that the WizardBranchController can
|
||||
* decide what to return as the remaining steps of the wizard.
|
||||
* <p>
|
||||
* The result is a <code>Wizard</code> which embeds sub-wizards; when the
|
||||
* <code>PanelProvider</code> passed to the constructor runs out of steps,
|
||||
* the master <code>Wizard</code> will try to find a sub-<code>Wizard</code>
|
||||
* by calling <code>getWizardForStep</code>. If non-null, the user seamlessly
|
||||
* continues in the returned wizard. To create <code>Wizard</code>s with
|
||||
* multiple branches, simply override <code>getWizardForStep</code> to create
|
||||
* another <code>WizardBranchController</code> and return the result of its
|
||||
* <code>createWizard</code> method.
|
||||
* <p>
|
||||
* Note that it is important to cache the instances of <code>WizardPanelProvider</code>
|
||||
* or <code>Wizard</code> which are returned here - this class's methods may
|
||||
* be called frequently to determine if the sequence of steps (the next wizard)
|
||||
* have changed.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public abstract class WizardBranchController {
|
||||
private final SimpleWizardInfo base;
|
||||
|
||||
/**
|
||||
* Create a new WizardBranchController. The <code>base</code> argument
|
||||
* provides the initial step(s) of the wizard up; when the user comes to
|
||||
* the last step of the base wizard, this WizardBranchController will be
|
||||
* asked for a wizard to provide subsequent panes. So the base wizard
|
||||
* should put some token into the settings map based on what the user
|
||||
* selects on its final pane, which the WizardBranchController can use
|
||||
* to decide what the next steps should be.
|
||||
*/
|
||||
protected WizardBranchController (WizardPanelProvider base) {
|
||||
this (new SimpleWizardInfo (base));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WizardBranchController using the passed WizardPage
|
||||
* instances as the initial pages of the wizard.
|
||||
* @param pages An array of WizardPage instances
|
||||
*/
|
||||
protected WizardBranchController (WizardPage[] pages) {
|
||||
this (WizardPage.createWizardPanelProvider(pages));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WizardBranchController using the passed WizardPage
|
||||
* as the initial page of the wizard. The initial page should
|
||||
* determine the subsequent steps of the wizard.
|
||||
* @param onlyPage An instance of WizardPage
|
||||
*/
|
||||
protected WizardBranchController (WizardPage onlyPage) {
|
||||
this (WizardPage.createWizardPanelProvider(onlyPage));
|
||||
}
|
||||
/**
|
||||
* Create a new WizardBranchController, using the passed <code>SimpleWizardInfo</code>
|
||||
* for the initial panes of the wizard.
|
||||
*/
|
||||
WizardBranchController (SimpleWizardInfo base) {
|
||||
if (base == null) throw new NullPointerException ("No base");
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wizard which represents the subsequent panes after this step.
|
||||
* The UI for the current step should have put sufficient data into the
|
||||
* settings map to decide what to return; return null if not.
|
||||
* <p>
|
||||
* The default implementation delegates to <code>getPanelProviderForStep()</code>
|
||||
* and returns a <code>Wizard</code> representing the result of that
|
||||
* call.
|
||||
* <p>
|
||||
* <b>Note:</b> This method can be called very frequently, to determine
|
||||
* if the sequence of steps has changed - so it needs to run fast.
|
||||
* Returning the same instance every time the same arguments are passed
|
||||
* is highly recommended. It will typically be called whenever a change
|
||||
* is fired by the base wizard (i.e. every call <code>setProblem()</code>
|
||||
* should generate a check to see if the navigation has changed).
|
||||
* <p>
|
||||
* Note that the wizard for the subsequent steps will be instantiated
|
||||
* as soon as it is known what the user's choice is, so the list of
|
||||
* pending steps can be updated (this does not mean that all subsequent
|
||||
* panel UI components of the wizard will be instantiated, just the
|
||||
* Wizard object itself, which will create panels on demand if they
|
||||
* have not already been created).
|
||||
*
|
||||
* @param step The current step the user is on in the wizard
|
||||
* @param settings The settings map, which previous panes of the wizard
|
||||
* have been writing information into
|
||||
*/
|
||||
protected Wizard getWizardForStep(String step, Map settings) {
|
||||
WizardPanelProvider provider = getPanelProviderForStep(step, settings);
|
||||
return provider == null ? null : provider.createWizard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to return a <code>WizardPanelProvider</code> representing the
|
||||
* steps from here to the final step of the wizard, varying the returned
|
||||
* object based on the contents of the map and the step in question.
|
||||
* The default implementation of this method throws an <code>Error</code> -
|
||||
* either override this method, or override <code>getWizardForStep()</code>
|
||||
* (in which case this method will not be called).
|
||||
* <p>
|
||||
* <b>Note:</b> This method can be called very frequently, to determine
|
||||
* if the sequence of steps has changed - so it needs to run fast.
|
||||
* Returning the same instance every time called with equivalent arguments
|
||||
* is highly recommended.
|
||||
*
|
||||
* @param step The string ID of the current step
|
||||
* @param settings The settings map, which previous panes of the wizard
|
||||
* will have written content into
|
||||
*/
|
||||
protected WizardPanelProvider getPanelProviderForStep(String step, Map settings) {
|
||||
throw new Error ("Override either createInfoForStep or " +
|
||||
"createWizardForStep");
|
||||
}
|
||||
|
||||
SimpleWizardInfo getBase() {
|
||||
return base;
|
||||
}
|
||||
|
||||
private WizardImplementation wizard = null;
|
||||
private Wizard real = null;
|
||||
/**
|
||||
* Create a Wizard to represent this branch controller. The resulting
|
||||
* Wizard instance is cached; subsequent calls to this method will return
|
||||
* the same instance.
|
||||
*/
|
||||
public final Wizard createWizard() {
|
||||
if (wizard == null) {
|
||||
wizard = new BranchingWizard (this);
|
||||
real = new Wizard (wizard);
|
||||
}
|
||||
return real;
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
/*
|
||||
* WizardController.java
|
||||
*
|
||||
* Created on March 5, 2005, 7:24 PM
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
/**
|
||||
* Controller which can be used to modify the UI state of a wizard. Passed
|
||||
* as an argument to methods of <code>WizardPanelProvider</code>. Use this
|
||||
* interface
|
||||
* to determine whether the Next/Finish buttons should be enabled, and if some
|
||||
* problem explanation text should be displayed.
|
||||
* <p>
|
||||
* If you are using {@link WizardPage WizardPage}, methods equivalent to this
|
||||
* interface are available directly on instances of <code>WizardPage</code>.
|
||||
*
|
||||
* @see WizardPanelProvider
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public final class WizardController {
|
||||
/**
|
||||
* Constant that can be passed to <code>setForwardNavigationMode</code> to indicate
|
||||
* that the Next button can be enabled if the problem string is null.
|
||||
* Value is identical to the similarly named constant on <code>Wizard</code>.
|
||||
*/
|
||||
public static final int MODE_CAN_CONTINUE = 1;
|
||||
/**
|
||||
* Constant that can be passed to <code>setForwardNavigationMode</code> to indicate
|
||||
* that the Finish button can be enabled if the problem string is null.
|
||||
* Value is identical to the similarly named constant on <code>Wizard</code>.
|
||||
*/
|
||||
public static final int MODE_CAN_FINISH = 2;
|
||||
/**
|
||||
* Constant that can be passed to <code>setForwardNavigationMode</code> to indicate
|
||||
* that both the Finish and Next buttons can be enabled if the problem
|
||||
* string is null. This value is a bitmask - i.e.
|
||||
* <code>MODE_CAN_CONTINUE_OR_FINISH == MODE_CAN_CONTINUE |
|
||||
* MODE_CAN_FINISH</code>.
|
||||
* Value is identical to the similarly named constant on
|
||||
* <code>Wizard</code>.
|
||||
*/
|
||||
public static final int MODE_CAN_CONTINUE_OR_FINISH =
|
||||
MODE_CAN_CONTINUE | MODE_CAN_FINISH;
|
||||
|
||||
private final WizardControllerImplementation impl;
|
||||
|
||||
WizardController (WizardControllerImplementation impl) {
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that there is a problem with what the user has (or has not)
|
||||
* input, such that the Next/Finish buttons should be disabled until the
|
||||
* user has made some change.
|
||||
* <p>
|
||||
* If you want to disable the Next/Finish buttons, do that by calling
|
||||
* this method with a short description of what is wrong.
|
||||
* <p>
|
||||
* Pass null to indicate there is no problem; non-null indicates there is
|
||||
* a problem - the passed string should be a localized, human-readable
|
||||
* description that assists the user in correcting the situation. It will
|
||||
* be displayed in the UI.
|
||||
*/
|
||||
public void setProblem (String value) {
|
||||
impl.setProblem (value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the forward navigation mode. This method determines whether
|
||||
* the Next button, the Finish button or both should be enabled if the
|
||||
* problem string is set to null.
|
||||
* <p>
|
||||
* On panels where, based on the UI state, the only reasonable next
|
||||
* step is to finish the wizard (even though there may be more panels
|
||||
* if the UI is in a different state), set the navigation mode to
|
||||
* MODE_CAN_FINISH, and the Finish button will be enabled, and the
|
||||
* Next button not.
|
||||
* <p>
|
||||
* On panels where, based on the UI state, the user could either continue
|
||||
* or complete the wizard at that point, set the navigation mode to
|
||||
* MODE_CAN_CONTINUE_OR_FINISH.
|
||||
* <p>
|
||||
* If the finish button should not be enabled, set the navigation mode
|
||||
* to MODE_CAN_CONTINUE. This is the default on any panel if no
|
||||
* explicit call to <code>setForwardNavigationMode()</code> has been made.
|
||||
*
|
||||
* @param navigationMode Legal values are MODE_CAN_CONTINUE,
|
||||
* MODE_CAN_FINISH or MODE_CAN_CONTINUE_OR_FINISH
|
||||
*/
|
||||
public void setForwardNavigationMode (int navigationMode) {
|
||||
impl.setForwardNavigationMode(navigationMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that some sort of background process is happening (presumably
|
||||
* a progress bar is being shown to the user) which cannot be interrupted.
|
||||
* <i>Calling this menu disables all navigation and the ability to close
|
||||
* the wizard dialog. Use this option with caution and liberal use of
|
||||
* <code>finally</code> to reenable navigation.</i>
|
||||
*/
|
||||
public void setBusy (boolean busy) {
|
||||
impl.setBusy (busy);
|
||||
}
|
||||
|
||||
WizardControllerImplementation getImpl() {
|
||||
return impl;
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
/**
|
||||
* Internal, non-public SPI for wizard controller; allows the actual WizardController
|
||||
* class to be final, so it does not imply that the API user should implement
|
||||
* it, and methods can be added safely to it if desired.
|
||||
*
|
||||
* @see WizardController
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
interface WizardControllerImplementation {
|
||||
/**
|
||||
* Indicate that there is a problem with what the user has (or has not)
|
||||
* input, such that the Next/Finish buttons should be disabled until the
|
||||
* user has made some change.
|
||||
* <p>
|
||||
* If you want to disable the Next/Finish buttons, do that by calling
|
||||
* this method with a short description of what is wrong.
|
||||
* <p>
|
||||
* Pass null to indicate there is no problem; non-null indicates there is
|
||||
* a problem - the passed string should be a localized, human-readable
|
||||
* description that assists the user in correcting the situation. It will
|
||||
* be displayed in the UI.
|
||||
*/
|
||||
void setProblem (String value);
|
||||
|
||||
/**
|
||||
* Set the forward navigation mode. This method determines whether
|
||||
* the Next button, the Finish button or both should be enabled if the
|
||||
* problem string is set to null.
|
||||
* <p>
|
||||
* On panels where, based on the UI state, the only reasonable next
|
||||
* step is to finish the wizard (even though there may be more panels
|
||||
* if the UI is in a different state), set the navigation mode to
|
||||
* MODE_CAN_FINISH, and the Finish button will be enabled, and the
|
||||
* Next button not.
|
||||
* <p>
|
||||
* On panels where, based on the UI state, the user could either continue
|
||||
* or complete the wizard at that point, set the navigation mode to
|
||||
* MODE_CAN_CONTINUE_OR_FINISH.
|
||||
* <p>
|
||||
* If the finish button should not be enabled, set the navigation mode
|
||||
* to MODE_CAN_CONTINUE. This is the default on any panel if no
|
||||
* explicit call to <code>setForwardNavigationMode()</code> has been made.
|
||||
*
|
||||
* @param navigationMode Legal values are MODE_CAN_CONTINUE,
|
||||
* MODE_CAN_FINISH or MODE_CAN_CONTINUE_OR_FINISH
|
||||
*/
|
||||
void setForwardNavigationMode (int navigationMode);
|
||||
|
||||
/**
|
||||
* Indicate that some sort of background process is happening (presumably
|
||||
* a progress bar is being shown to the user) which cannot be interrupted.
|
||||
* <i>Calling this menu disables all navigation and the ability to close
|
||||
* the wizard dialog. Use this option with caution and liberal use of
|
||||
* <code>finally</code> to reenable navigation.</i>
|
||||
*/
|
||||
void setBusy (boolean busy);
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
/*
|
||||
* WizardException.java
|
||||
*
|
||||
* Created on February 22, 2005, 3:56 PM
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
/**
|
||||
* Some arguments a user enters in a wizard may be too expensive to validate
|
||||
* as the user is going through the wizard. Therefore, Wizard.finish() throws
|
||||
* WizardException.
|
||||
* <p>
|
||||
* Exceptions of this type always have a localized message, and optionally
|
||||
* provide a step in the wizard that to return to, so that the user can
|
||||
* enter corrected information.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public final class WizardException extends Exception {
|
||||
private final String localizedMessage;
|
||||
private final String step;
|
||||
/** Creates a new instance of WizardException */
|
||||
public WizardException(String localizedMessage, String stepToReturnTo) {
|
||||
super ("wizardException");
|
||||
this.localizedMessage = localizedMessage;
|
||||
this.step = stepToReturnTo;
|
||||
}
|
||||
|
||||
public WizardException (String localizedMessage) {
|
||||
this (localizedMessage, Wizard.UNDETERMINED_STEP);
|
||||
}
|
||||
|
||||
public String getLocalizedMessage() {
|
||||
return localizedMessage;
|
||||
}
|
||||
|
||||
public String getStepToReturnTo() {
|
||||
return step;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,246 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
/*
|
||||
* Wizard.java
|
||||
*
|
||||
* Created on February 22, 2005, 2:18 PM
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
* Non-public mirror interface to the final Wizard class. A Wizard delegates to
|
||||
* its WizardImplementation for all of its functions. This interface is
|
||||
* implemented by several internal classes.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
* @see Wizard
|
||||
* @see WizardPage
|
||||
* @see WizardPanelProvider
|
||||
* @see WizardBranchController
|
||||
* @see org.netbeans.api.wizard.WizardDisplayer
|
||||
*/
|
||||
interface WizardImplementation {
|
||||
/**
|
||||
* Constant that can be returned by <code>getForwardNavigationMode</code> to indicate
|
||||
* that the Next button can be enabled if the problem string is null.
|
||||
*/
|
||||
public static final int MODE_CAN_CONTINUE =
|
||||
WizardController.MODE_CAN_CONTINUE;
|
||||
|
||||
/**
|
||||
* Constant that can be returned by <code>getForwardNavigationMode</code> to indicate
|
||||
* that the Finish button can be enabled if the problem string is null.
|
||||
*/
|
||||
public static final int MODE_CAN_FINISH =
|
||||
WizardController.MODE_CAN_FINISH;
|
||||
/**
|
||||
* Constant that can be returned by <code>getForwardNavigationMode</code> to indicate
|
||||
* that both the Finish and Next buttons can be enabled if the problem
|
||||
* string is null. This value is a bitmask - i.e.
|
||||
* <code>MODE_CAN_CONTINUE_OR_FINISH == MODE_CAN_CONTINUE |
|
||||
* MODE_CAN_FINISH</code>
|
||||
*/
|
||||
public static final int MODE_CAN_CONTINUE_OR_FINISH =
|
||||
WizardController.MODE_CAN_CONTINUE_OR_FINISH;
|
||||
|
||||
/**
|
||||
* Special panel ID key indicating a branch point in the wizard,
|
||||
* after which the next step(s) are unknown.
|
||||
*/
|
||||
public static final String UNDETERMINED_STEP = "_#UndeterminedStep";
|
||||
|
||||
/**
|
||||
* Set which step of the wizard is currently being displayed and get
|
||||
* the component for that step. This method is passed a Map into which
|
||||
* panels may put key/value pairs that represent user input. This Map
|
||||
* is what the <code>finish()</code> method will use to decide what to
|
||||
* do.
|
||||
* <p>
|
||||
* The ID passed
|
||||
* becomes the currently active step of the wizard as of this call.
|
||||
* <p>
|
||||
* If the user has already been to this step, and some key/value pairs
|
||||
* were written to the wizard data map, and the user then
|
||||
* then pressed the Back button, and later pressed Next again, the
|
||||
* wizard data map may already contain key/value pairs for this
|
||||
* step. Panels whose components
|
||||
* are affected by data entered in the map in preceding steps should
|
||||
* update their UI based on the map's current contents, at the time a
|
||||
* step is re-displayed, to ensure they are in
|
||||
* sync with any changes the user may have made on preceding panels
|
||||
* since the last time this panel was shown.
|
||||
* <p>
|
||||
* If this method is called as a result of
|
||||
* the user pressing the Back button, the wizard data map will <i>not</i>
|
||||
* contain any key/value pairs added by the subsequent panels. It
|
||||
* will only key/value pairs from
|
||||
* panels that precede this one and any written the last time this
|
||||
* panel was visited. The wizard data map shall never contain
|
||||
* keys and values from future steps in the wizard.
|
||||
* <p>
|
||||
* Implementations are expected to return the same component if
|
||||
* <code>navigatingTo()</code> is called repeatedly with the same ID.
|
||||
* Components should be constructed once, then reused for the lifetime
|
||||
* of the wizard.
|
||||
* <blockquote>
|
||||
* <b>Note:</b> The consequences of a later panel writing or deleting
|
||||
* a key/value pair that was put into the wizard data map by
|
||||
* an earlier panel are
|
||||
* undefined. Each step should use only its own unique keys, not
|
||||
* modify those from earlier steps.
|
||||
* </blockquote>
|
||||
*
|
||||
*
|
||||
* @param id The ID of the to-be-current panel
|
||||
* @param wizardData The map into which panels should write key/value pairs
|
||||
* in response to user input - the place where user data is aggregated
|
||||
* @return The UI component for this step, which should be displayed in
|
||||
* the wizard
|
||||
*/
|
||||
public JComponent navigatingTo(String id, Map wizardData);
|
||||
|
||||
/**
|
||||
* Get the String ID of the current panel.
|
||||
* If there is no current panel, return null.
|
||||
*
|
||||
* @return The unique ID of the step currently
|
||||
* presented in the UI, as determined by the last call to
|
||||
* <code>navigateTo</code>
|
||||
*/
|
||||
public String getCurrentStep();
|
||||
|
||||
/**
|
||||
* Get the String ID of the next panel. If the Next button should be
|
||||
* disabled, or this is the final step of the wizard, return null.
|
||||
*
|
||||
* @return The unique ID of the step that follows the one currently
|
||||
* presented in the UI, as determined by the last call to
|
||||
* <code>navigateTo</code>
|
||||
*/
|
||||
public String getNextStep();
|
||||
|
||||
/**
|
||||
* Get the String ID of the previous panel. If the Prev button should
|
||||
* be disabled, return null.
|
||||
* @return the String ID of the step that precedes the one currently
|
||||
* presented in this <code>Wizard</code>s UI, or null if it is either
|
||||
* the first step or the preceding step is unknown, as determined by the last call to
|
||||
* <code>navigateTo</code>
|
||||
*/
|
||||
public String getPreviousStep();
|
||||
|
||||
/**
|
||||
* Get a human readable description of the reason the Next/Finish button
|
||||
* is not enabled (i.e. "#\foo is not a legal filename").
|
||||
* @return A localized string that describes why the Next/Finish button
|
||||
* is not enabled, or null if one or the other or both should be enabled
|
||||
*/
|
||||
public String getProblem();
|
||||
|
||||
/**
|
||||
* Get String IDs for the entire list of known steps in the
|
||||
* wizard (regardless of whether
|
||||
* Finish/Next can be enabled or not). If there is a branch point in
|
||||
* the wizard and it cannot be determined what step will be next beyond
|
||||
* that point, make the final entry in the returned array the constant
|
||||
* UNDETERMINED_STEP, and fire <code>stepsChange()</code> to any listeners
|
||||
* once the later steps become known.
|
||||
* <p>
|
||||
* The return value of this method must be an array of Strings at least
|
||||
* one String in length. If length == 1, the single step ID may not be
|
||||
* UNDETERMINED_STEP; UNDETERMINED_STEP may only be the last ID, and only
|
||||
* may be used if there is more than one step.
|
||||
*
|
||||
* @return An array of strings that constitute unique IDs of each step
|
||||
* in the wizard. The returned array may not contain duplicate entries.
|
||||
*/
|
||||
public String[] getAllSteps();
|
||||
|
||||
/**
|
||||
* Get a human-readable description for a given panel, as identified by
|
||||
* the passed ID.
|
||||
*/
|
||||
public String getStepDescription(String id);
|
||||
|
||||
public String getLongDescription(String id);
|
||||
|
||||
/**
|
||||
* Add a listener for changes in the count or order of steps in this
|
||||
* wizard and for changes in Next/Previous/Finish button enablement.
|
||||
* @param listener A listener to add
|
||||
*/
|
||||
public void addWizardObserver(WizardObserver listener);
|
||||
|
||||
/**
|
||||
* Remove a listener for changes in the count or order of steps in this
|
||||
* wizard and for changes in Next/Previous/Finish button enablement.
|
||||
* @param listener A listener to remove
|
||||
*/
|
||||
public void removeWizardObserver(WizardObserver listener);
|
||||
|
||||
/**
|
||||
* Finish the wizard, (optionally) instantiating some Object and returning
|
||||
* it. For cases where the map may contain wizardData too expensive to
|
||||
* validate on the fly,
|
||||
* this method may throw a WizardException with a localized message
|
||||
* indicating the problem; that exception can indicate a step in the
|
||||
* wizard to return to to allow the user to correct the information.
|
||||
* <p>
|
||||
* No methods on a <code>Wizard</code> instance should be called after
|
||||
* that <code>Wizard</code>'s <code>finish()</code> method has been
|
||||
* called - the results are undefined.
|
||||
*
|
||||
* @param settings A map containing all of the wizardData the user has
|
||||
* entered as they traversed this wizard - presumably enough to do
|
||||
* whatever this method needs to do (if not, that's a bug in the
|
||||
* implementation of <code>Wizard</code>).
|
||||
*/
|
||||
public Object finish(Map settings) throws WizardException;
|
||||
|
||||
/** Get the title of the wizard.
|
||||
* @return A human-readable, localized title that should be displayed
|
||||
* in any dialog showing this wizard
|
||||
*/
|
||||
public String getTitle();
|
||||
|
||||
/** Determine if all navigation buttons should be disabled - if the
|
||||
* wizard is currently doing some kind of progress/background processing
|
||||
* task that cannot be interrupted.
|
||||
*/
|
||||
public boolean isBusy();
|
||||
|
||||
/**
|
||||
* Get the forward navigation mode of this wizard. This
|
||||
* determines whether the Next button, the Finish button or both should
|
||||
* be enabled, <i>if the problem string returned from <code>getProblem</code>
|
||||
* is null</i>. If the problem is set to non-null, the UI should disable
|
||||
* all forward navigation.
|
||||
* <p>
|
||||
* This method should never return any value but
|
||||
* MODE_CAN_CONTINUE, MODE_CAN_FINISH or MODE_CAN_CONTINUE_OR_FINISH -
|
||||
* it is not a mechanism for disabling all forward navigation (return
|
||||
* non null from <code>getProblem()</code> for that, or <code>true</code>
|
||||
* from <code>isBusy()</code> to temporarily disable <i>all</i> navigation).
|
||||
* <p>
|
||||
* On the final step of the wizard, this method should always return
|
||||
* MODE_CAN_FINISH.
|
||||
*/
|
||||
public int getForwardNavigationMode();
|
||||
|
||||
/**
|
||||
* Called when the user cancels the wizard.
|
||||
*/
|
||||
public boolean cancel(Map settings);
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
/**
|
||||
* Observer which can detect changes in the state of a wizard as the
|
||||
* user proceeds. Only likely to be used by implementations of
|
||||
* <code>WizardDisplayer</code>.
|
||||
*/
|
||||
public interface WizardObserver {
|
||||
/**
|
||||
* Called when the number or names of the steps of the
|
||||
* wizard changes (for example, the user made a choice in one pane which
|
||||
* affects the flow of subsequent steps).
|
||||
* @param wizard The wizard whose steps have changed
|
||||
*/
|
||||
public void stepsChanged(Wizard wizard);
|
||||
|
||||
/**
|
||||
* Called when the enablement of the next/previous/finish buttons
|
||||
* change, or the problem text changes.
|
||||
* @param wizard The wizard whose navigability has changed
|
||||
*/
|
||||
public void navigabilityChanged(Wizard wizard);
|
||||
|
||||
/**
|
||||
* Called whenever the current step changes.
|
||||
*
|
||||
* @param wizard The wizard whose current step has changed
|
||||
*/
|
||||
public void selectionChanged(Wizard wizard);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,89 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is an optional interface for panels that want to be notified when
|
||||
* the next and back buttons are pressed.
|
||||
*
|
||||
* The WizardPanelProvider is NOT required to create panels that implement
|
||||
* this interface.
|
||||
*
|
||||
* Each of these methods returns a WizardPanelNavResult that can be used to
|
||||
* indicate PROCEED or REMAIN_ON_PAGE.
|
||||
*
|
||||
* The result can also be an instance of a subclass of WizardPanelNavResult
|
||||
* that implements the <code>start</code> method to use a background thread
|
||||
* to determine if the next page can be shown.
|
||||
*
|
||||
* @author stanley@stanleyknutson.com
|
||||
*/
|
||||
public interface WizardPanel
|
||||
{
|
||||
/**
|
||||
* This method is invoked when the "next" button has been pushed,
|
||||
* to do a final validation of input (such as doing a database login).
|
||||
*
|
||||
* If this method return false, then the "next" button will not change the
|
||||
* displayed panel. Presumably some error will have been shown to the user.
|
||||
*
|
||||
* @param stepName
|
||||
* @param settings
|
||||
* @param wizard
|
||||
* @return WizardPanelNavResult.PROCEED if the "next" button should proceed,
|
||||
* WizardPanelNavResult.REMAIN_ON_PAGE if "next" should not proceed,
|
||||
* or a instance of subclass of WizardPanelResult that will do some background computation
|
||||
* and call the progress.finished method with one of those constants
|
||||
* (or call progress.failed with the error message)
|
||||
*/
|
||||
public WizardPanelNavResult allowNext (String stepName, Map settings, Wizard wizard);
|
||||
|
||||
/**
|
||||
* This method is invoked when the "back" button has been pushed,
|
||||
* to discard any data from the setings that will not been needed and for which the
|
||||
* normal "just hide that data" is not the desired behavior.
|
||||
* (See MergeMap for discussion of the "hide the data" behavior)
|
||||
*
|
||||
* If this method return false, then the "next" button will not change the
|
||||
* displayed panel. Presumably some error will have been shown to the user.
|
||||
*
|
||||
* @param stepName
|
||||
* @param settings
|
||||
* @param wizard
|
||||
* @return WizardPanelNavResult.PROCEED if the "back" button should proceed,
|
||||
* WizardPanelNavResult.REMAIN_ON_PAGE if "back" should not proceed,
|
||||
* or a instance of subclass of WizardPanelResult that will do some background computation
|
||||
* and call the progress.finished method with one of those constants.
|
||||
* (or call progress.failed with the error message)
|
||||
*/
|
||||
public WizardPanelNavResult allowBack (String stepName, Map settings, Wizard wizard);
|
||||
|
||||
/**
|
||||
* This method is invoked when the "finish" button has been pushed,
|
||||
* to allow veto of the finish action BEFORE the wizard finish method is invoked.
|
||||
*
|
||||
* If this method return false, then the "finish" button will have no effect.
|
||||
* Presumably some error will have been shown to the user.
|
||||
*
|
||||
* @param stepName
|
||||
* @param settings
|
||||
* @param wizard
|
||||
* @return WizardPanelNavResult.PROCEED if the "finish" button should proceed,
|
||||
* WizardPanelNavResult.REMAIN_ON_PAGE if "finish" should not proceed,
|
||||
* or a instance of subclass of WizardPanelResult that will do some background computation
|
||||
* and call the progress.finished method with one of those constants.
|
||||
* (or call progress.failed with the error message)
|
||||
*/
|
||||
public WizardPanelNavResult allowFinish (String stepName, Map settings, Wizard wizard);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Result class for the methods in WizardPanel.
|
||||
*
|
||||
* For immediate action, one of the two constantants PROCEED or REMAIN_ON_PAGE
|
||||
* should be returned. Otherwise an instance of a subclass should be returned
|
||||
* that computes a Boolean result.
|
||||
*
|
||||
* @author stanley@stanleyknutson.com
|
||||
*/
|
||||
public abstract class WizardPanelNavResult extends DeferredWizardResult
|
||||
{
|
||||
/**
|
||||
* value for procced to next step in the wizard.
|
||||
*/
|
||||
public static final WizardPanelNavResult PROCEED = new WPNRimmediate(true);
|
||||
/**
|
||||
* Value to remain on the current page in the wizard
|
||||
*/
|
||||
public static final WizardPanelNavResult REMAIN_ON_PAGE = new WPNRimmediate(false);
|
||||
|
||||
public WizardPanelNavResult(boolean useBusy) {
|
||||
super (false, useBusy);
|
||||
}
|
||||
|
||||
public WizardPanelNavResult(boolean useBusy, boolean canAbort) {
|
||||
super (canAbort, useBusy);
|
||||
}
|
||||
|
||||
public WizardPanelNavResult() {
|
||||
super (false, false);
|
||||
}
|
||||
|
||||
public boolean isDeferredComputation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* internal class for the constants only
|
||||
*/
|
||||
private final static class WPNRimmediate extends WizardPanelNavResult
|
||||
{
|
||||
boolean value;
|
||||
|
||||
WPNRimmediate (boolean v)
|
||||
{
|
||||
value = v;
|
||||
}
|
||||
public boolean isDeferredComputation()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean equals (Object o)
|
||||
{
|
||||
if (o instanceof WPNRimmediate && ((WPNRimmediate)o).value == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode()
|
||||
{
|
||||
return value ? 1 : 2;
|
||||
}
|
||||
|
||||
public void start(Map settings, ResultProgressHandle progress)
|
||||
{
|
||||
// Should never get here, this is supposed to be immediate!
|
||||
throw new RuntimeException("Immediate result was called as deferral!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,323 @@
|
||||
/* The contents of this file are subject to the terms of the Common Development
|
||||
and Distribution License (the License). You may not use this file except in
|
||||
compliance with the License.
|
||||
You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
or http://www.netbeans.org/cddl.txt.
|
||||
When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
If applicable, add the following below the CDDL Header, with the fields
|
||||
enclosed by brackets [] replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]" */
|
||||
/*
|
||||
* PanelProvider.java
|
||||
*
|
||||
* Created on March 5, 2005, 7:25 PM
|
||||
*/
|
||||
|
||||
package org.jackhuang.hellominecraft.utils.views.wizard.spi;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
* (Note: <code>WizardPage</code> offers somewhat simpler functionality for
|
||||
* creating a wizard than does WizardPanelProvider; the only advantage of
|
||||
* <code>WizardPanelProvider</code> is that it does not require one to
|
||||
* subclass a panel component).
|
||||
* <p>
|
||||
* A simple interface for providing a fixed set of panels for a wizard.
|
||||
* To use, simply implement <code>createPanel()</code> to create the
|
||||
* appropriate UI component for a given step (a unique String ID - one of the ones passed
|
||||
* in the constructor in the <code>steps</code> array), and implement
|
||||
* <code>finish()</code> to do whatever should be done when the wizard is
|
||||
* finished.
|
||||
* <p>
|
||||
* To control whether the Next/Finish buttons are enabled, components
|
||||
* created in <code>createPanel()</code> should call methods on the <code>
|
||||
* WizardController</code> passed. The created panels should listen on the
|
||||
* UI components they create, updating the settings Map when the user changes
|
||||
* their input.
|
||||
* <p>
|
||||
* Super-simple one-pane wizard example - if the checkbox is checked, the user
|
||||
* can continue:
|
||||
* <pre>
|
||||
* public class MyProvider extends WizardPanelProvider {
|
||||
* public MyProvider() {
|
||||
* <font color="gray">//here we pass a localized title for the wizard,
|
||||
* //the ID of the one step it will have, and a localized description
|
||||
* //the wizard can show for that one step</font>
|
||||
* super ("Click the box", "click", "Click the checkbox");
|
||||
* }
|
||||
* protected JComponent createPanel (final WizardController controller, String id, final Map settings) {
|
||||
* <font color="gray">//A quick sanity check</font>
|
||||
* assert "click".equals (id);
|
||||
* <font color="gray">//Remember this method will only be called <i>once</i> for any panel</font>
|
||||
* final JCheckBox result = new JCheckBox();
|
||||
* result.addActionListener (new ActionListener() {
|
||||
* public void actionPerformed (ActionEvent ae) {
|
||||
* <font color="gray">//Typically you want to write the result of some user
|
||||
* //action into the settings map as soon as they do it </font>
|
||||
* settings.put ("boxSelected", result.isSelected() ? Boolean.TRUE : Boolean.FALSE);
|
||||
* if (result.isSelected()) {
|
||||
* controller.setProblem(null);
|
||||
* } else {
|
||||
* controller.setProblem("The box is not checked");
|
||||
* }
|
||||
* controller.setCanFinish(true); <font color="gray">//won't matter if we called setProblem() with non-null</font>
|
||||
* }
|
||||
* });
|
||||
* return result;
|
||||
* }
|
||||
*
|
||||
* protected Object finish (Map settings) throws WizardException {
|
||||
* <font color="gray">//if we had some interesting information (Strings a user put in a
|
||||
* //text field or something, we'd generate some interesting object or
|
||||
* //create some files or something here</font>
|
||||
* return null;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public abstract class WizardPanelProvider {
|
||||
final String title;
|
||||
final String[] descriptions;
|
||||
final String[] steps;
|
||||
final String[] knownProblems;
|
||||
|
||||
/**
|
||||
* Create a WizardPanelProvider. The passed array of steps and descriptions
|
||||
* will be used as IDs and localized descriptions of the various steps in
|
||||
* the wizard. Use this constructor (which passes not title) for sub-wizards
|
||||
* used in a <code>WizardBranchController</code>, where the first pane
|
||||
* will determine the title, and the titles of the sub-wizards will never be
|
||||
* shown.
|
||||
* @param steps A set of unique IDs identifying each step of this wizard. Each
|
||||
* ID must occur only once in the array of steps.
|
||||
*
|
||||
* @param descriptions A set of human-readable descriptions corresponding
|
||||
* 1:1 with the unique IDs passed as the <code>steps</code> parameter
|
||||
*/
|
||||
protected WizardPanelProvider (String[] steps, String[] descriptions) {
|
||||
this (null, steps, descriptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WizardPanelProvider with the provided title, steps and
|
||||
* descriptions. The <code>steps</code> parameter are unique IDs of
|
||||
* panels, which will be passed to <code>createPanel</code> to create
|
||||
* panels for various steps in the wizard, as the user navigates it.
|
||||
* The <code>descriptions</code> parameter is a set of localized descriptions
|
||||
* that can appear in the Wizard to describe each step.
|
||||
* @param title A human readable title for the wizard dialog
|
||||
* @param steps An array of unique IDs for the various panels of this
|
||||
* wizard
|
||||
* @param descriptions An array of descriptions corresponding 1:1 with the
|
||||
* unique IDs. These must be human readable, localized strings.
|
||||
*/
|
||||
protected WizardPanelProvider (String title, String[] steps, String[] descriptions) {
|
||||
this.title = title;
|
||||
this.steps = steps;
|
||||
this.descriptions = descriptions;
|
||||
knownProblems = new String[steps.length];
|
||||
if (steps.length != descriptions.length) {
|
||||
throw new IllegalArgumentException ("Length of steps and" +
|
||||
" descriptions arrays do not match");
|
||||
}
|
||||
// assert validData (steps, descriptions) == null : validData (steps, descriptions);
|
||||
String v = validData (steps, descriptions);
|
||||
if (v != null)
|
||||
{
|
||||
throw new RuntimeException (v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String validData (String[] steps, String[] descriptions) {
|
||||
if (steps.length != descriptions.length) {
|
||||
return steps.length + " steps but " + descriptions.length +
|
||||
" descriptions";
|
||||
}
|
||||
for (int i=0; i < steps.length; i++) {
|
||||
if (steps[i] == null) {
|
||||
throw new NullPointerException ("Step id " + i + " is null");
|
||||
}
|
||||
if (descriptions[i] == null) {
|
||||
throw new NullPointerException ("Description " + i + " is null");
|
||||
}
|
||||
}
|
||||
if (new HashSet(Arrays.asList(steps)).size() != steps.length) {
|
||||
return "Duplicate step ids: " + Arrays.asList(steps);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor to create a WizardPanelProvider which has only
|
||||
* one step to it. Mainly useful for initial steps in a <code>WizardBranchController</code>.
|
||||
* @param title A human readable title for the wizard dialog
|
||||
* @param singleStep The unique ID of the only step this wizard has
|
||||
* @param singleDescription The human-readable description of what the user
|
||||
* should do in the one step of this one-step wizard or sub-wizard
|
||||
*/
|
||||
protected WizardPanelProvider (String title, String singleStep, String singleDescription) {
|
||||
this (title, new String[] {singleStep}, new String[] {singleDescription});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel that represents a named step in the wizard.
|
||||
* This method will be called exactly <i>once</i> in the life of
|
||||
* a wizard. The panel should retain the passed settings Map, and
|
||||
* add/remove values from it as the user enters information, calling
|
||||
* <code>setProblem()</code> and <code>setCanFinish()</code> as
|
||||
* appropriate in response to user input.
|
||||
*
|
||||
* @param controller - the object which controls whether the
|
||||
* Next/Finish buttons in the wizard are enabled, and what instructions
|
||||
* are displayed to the user if they are not
|
||||
* @param id The name of the step, one of the array of steps passed in
|
||||
* the constructor
|
||||
* @param settings A Map containing settings from earlier steps in
|
||||
* the wizard. It is safe to retain a reference to this map and put
|
||||
* values in it as the user manipulates the UI; the reference should
|
||||
* be refreshed whenever this method is called again.
|
||||
* @return A JComponent that should be displayed in the center of the
|
||||
* wizard
|
||||
*/
|
||||
protected abstract JComponent createPanel (WizardController controller, String id, Map settings);
|
||||
|
||||
/**
|
||||
* Instantiate whatever object (if any) the wizard creates from its
|
||||
* gathered data. The default implementation is a no-op that returns
|
||||
* null.
|
||||
* <p>
|
||||
* If an instance of <code>Summary</code> is returned from this method, the
|
||||
* UI shall display it on a final page and disable all navigation buttons
|
||||
* except the Close/Cancel button.
|
||||
* <p>
|
||||
* If an instance of <code>DeferredWizardResult</code> is returned from this
|
||||
* method, the UI shall display some sort of progress bar while the result
|
||||
* is computed in the background. If that <code>DeferredWizardResult</code>
|
||||
* produces a <code>Summary</code> object, that summary shall be displayed
|
||||
* as described above.
|
||||
* <p>
|
||||
* The default implementation returns the settings map it is passed.
|
||||
*
|
||||
* @param settings The settings map, now fully populated with all settings needed
|
||||
* to complete the wizard (this method will only be called if
|
||||
* <code>setProblem(null)</code> and <code>setCanFinish(true)</code> have
|
||||
* been called on the <code>WizardController</code> passed to
|
||||
* <code>createPanel()</code>.
|
||||
* @return an object composed based on what the user entered in the wizard -
|
||||
* somethingmeaningful to whatever code invoked the wizard, or null. Note
|
||||
* special handling if an instance of <code>DeferredWizardResult</code>
|
||||
* or <code>Summary</code> is returned from this method.
|
||||
*/
|
||||
protected Object finish (Map settings) throws WizardException {
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method provides a chance to call setProblem() or setCanFinish() when
|
||||
* the user re-navigates to a panel they've already seen - in the case
|
||||
* that the user pressed the Previous button and then the Next button.
|
||||
* <p>
|
||||
* The default implementation does nothing, which is sufficient for most
|
||||
* cases.
|
||||
* If whether this panel is valid or not could
|
||||
* have changed because of changed data from a previous panel, or it
|
||||
* displays data entered on previous panes which may have changed,
|
||||
* you may want to override this method to ensure validity and canFinish
|
||||
* are set correctly, and that the components have the correct text.
|
||||
* <p>
|
||||
* This method will <i>not</i> be called when a panel is first instantiated -
|
||||
* <code>createPanel()</code> is expected to set validity and canFinish
|
||||
* appropriately.
|
||||
* <p>
|
||||
* The settings Map passed to this method will always be the same
|
||||
* Settings map instance that was passed to <code>createPanel()</code>
|
||||
* when the panel was created.
|
||||
* <p>
|
||||
* If you are implementing WizardPanelProvider and some of the pages are
|
||||
* <code>WizardPage</code>s, you should call the super implementation if
|
||||
* you override this method.
|
||||
*/
|
||||
protected void recycleExistingPanel (String id, WizardController controller, Map wizardData, JComponent panel) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
void recycle (String id, WizardController controller, Map wizardData, JComponent panel) {
|
||||
if (panel instanceof WizardPage) {
|
||||
WizardPage page = (WizardPage) panel;
|
||||
page.setController(controller);
|
||||
page.setWizardDataMap(wizardData);
|
||||
page.recycle();
|
||||
}
|
||||
recycleExistingPanel (id, controller, wizardData, panel);
|
||||
}
|
||||
|
||||
private Wizard wizard;
|
||||
/**
|
||||
* Create a Wizard for this PanelProvider. The instance created by this
|
||||
* method is cached and the same instance will be returned on subsequent
|
||||
* calls.
|
||||
*/
|
||||
public final Wizard createWizard() {
|
||||
if (wizard == null) {
|
||||
wizard = new Wizard (new SimpleWizard (this));
|
||||
}
|
||||
return wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can optionally be overridden to provide a longer
|
||||
* description of a step to be shown in the top of its panel.
|
||||
* The default implementation returns null, indicating that the
|
||||
* short description should be used.
|
||||
*
|
||||
* @param stepId a unique id for one step of the wizard
|
||||
* @return An alternate description for use in the top of the wizard
|
||||
* page when this page is the current one, or null
|
||||
*/
|
||||
public String getLongDescription (String stepId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to get the index into the array of steps passed to
|
||||
* the constructor of a specific step id.
|
||||
*/
|
||||
protected final int indexOfStep (String id) {
|
||||
return Arrays.asList(steps).indexOf(id);
|
||||
}
|
||||
|
||||
void setKnownProblem (String problem, int idx) {
|
||||
//Record a problem message so we can put it back if the user does
|
||||
//prev and then next
|
||||
if (idx >= 0) { //setProblem() can be called during initialization
|
||||
knownProblems[idx] = problem;
|
||||
}
|
||||
}
|
||||
|
||||
String getKnownProblem(int idx) {
|
||||
return knownProblems[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Called if the user invokes cancel. The default impl returns
|
||||
* true.
|
||||
* @return false to abort cancellation (almost all implementations will
|
||||
* want to return true - this is really only applicable in cases such
|
||||
* as an OS installer or such).
|
||||
*/
|
||||
public boolean cancel(Map settings) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return super.toString() + " with wizard " + wizard;
|
||||
}
|
||||
}
|
@ -208,11 +208,14 @@ settings.custom=\u81ea\u5b9a\u4e49
|
||||
settings.choose_gamedir=\u9009\u62e9\u6e38\u620f\u8def\u5f84
|
||||
settings.failed_load=\u8bbe\u7f6e\u6587\u4ef6\u52a0\u8f7d\u5931\u8d25\uff0c\u53ef\u80fd\u662f\u5347\u7ea7\u4e86\u542f\u52a8\u5668\u6216\u88ab\u4eba\u5de5\u4fee\u6539\u9020\u6210\u9519\u8bef\uff0c\u662f\u5426\u6e05\u9664\uff1f
|
||||
|
||||
settings.modpack=\u6574\u5408\u5305
|
||||
settings.modpack.choose=\u9009\u62e9\u8981\u5bfc\u5165\u7684\u6e38\u620f\u6574\u5408\u5305\u6587\u4ef6
|
||||
settings.modpack.install.task=\u5bfc\u5165\u6574\u5408\u5305
|
||||
settings.modpack.install_error=\u5b89\u88c5\u5931\u8d25\uff0c\u53ef\u80fd\u662f\u6574\u5408\u5305\u683c\u5f0f\u4e0d\u6b63\u786e\u6216\u64cd\u4f5c\u6587\u4ef6\u5931\u8d25
|
||||
settings.modpack.save=\u9009\u62e9\u8981\u5bfc\u51fa\u5230\u7684\u6e38\u620f\u6574\u5408\u5305\u4f4d\u7f6e
|
||||
settings.modpack.save.task=\u5bfc\u51fa\u6574\u5408\u5305
|
||||
settings.modpack.export_error=\u5bfc\u51fa\u5931\u8d25\uff0c\u53ef\u80fd\u662f\u60a8\u7684\u6e38\u620f\u6587\u4ef6\u5939\u683c\u5f0f\u4e0d\u6b63\u786e\u6216\u64cd\u4f5c\u6587\u4ef6\u5931\u8d25
|
||||
settings.modpack=\u6574\u5408\u5305
|
||||
settings.modpack.enter_name=\u7ed9\u6e38\u620f\u8d77\u4e2a\u4f60\u559c\u6b22\u7684\u540d\u5b57
|
||||
|
||||
mods=Mod\u7ba1\u7406
|
||||
mods.choose_mod=\u9009\u62e9\u6a21\u7ec4
|
||||
@ -260,6 +263,7 @@ launcher.enable_shadow=\u542f\u7528\u7a97\u53e3\u9634\u5f71(\u91cd\u542f\u542f\u
|
||||
launcher.theme=\u4e3b\u9898
|
||||
launcher.proxy=\u4ee3\u7406
|
||||
launcher.decorated=\u542f\u7528\u7a97\u53e3\u8fb9\u6846(Linux\u4e0b\u53ef\u89e3\u51b3\u7a0b\u5e8f\u754c\u9762\u5168\u7070\u95ee\u9898)
|
||||
launcher.modpack=<html><a href="http://blog.163.com/huanghongxun2008@126/blog/static/7738046920160323812771/">\u6574\u5408\u5305\u4f5c\u8005\u5e2e\u52a9</a></html>
|
||||
|
||||
launcher.title.game=\u6e38\u620f\u8bbe\u7f6e
|
||||
launcher.title.main=\u4e3b\u9875
|
||||
@ -338,4 +342,3 @@ color.green=\u7eff\u8272
|
||||
color.orange=\u6a59\u8272
|
||||
color.dark_blue=\u6df1\u84dd\u8272
|
||||
color.purple=\u7d2b\u8272
|
||||
launcher.modpack=<html><a href="www.java.com">\u6574\u5408\u5305\u4f5c\u8005\u5e2e\u52a9</a></html>
|
||||
|
@ -208,11 +208,14 @@ settings.custom=Custom
|
||||
settings.choose_gamedir=Choose Game Directory
|
||||
settings.failed_load=Failed to load settings file. Remove it?
|
||||
|
||||
settings.modpack=Mod pack
|
||||
settings.modpack.choose=Choose a modpack zip file which you want to import.
|
||||
settings.modpack.install.task=Import the modpack
|
||||
settings.modpack.install_error=Failed to install the modpack, maybe the modpack file is incorrect or failed to manage files.
|
||||
settings.modpack.save=Choose a location which you want to export the game files to.
|
||||
settings.modpack.save.task=Export the modpack
|
||||
settings.modpack.export_error=Failed to export the modpack, maybe the format of your game directory is incorrect or failed to manage files.
|
||||
settings.modpack=Mod pack
|
||||
settings.modpack.enter_name=Give this game a name which is your favorite.
|
||||
|
||||
mods=Mods
|
||||
mods.choose_mod=Choose your mods
|
||||
@ -260,6 +263,7 @@ launcher.enable_shadow=Enable Window Shadow
|
||||
launcher.theme=Theme
|
||||
launcher.proxy=Proxy
|
||||
launcher.decorated=Enable system window border(in order to fix the problem that the ui become all gray in Linux OS)
|
||||
launcher.modpack=<html><a href="http://blog.163.com/huanghongxun2008@126/blog/static/7738046920160323812771/">Documentations for modpacks.</a></html>
|
||||
|
||||
launcher.title.game=Games
|
||||
launcher.title.main=Home
|
||||
|
@ -208,11 +208,14 @@ settings.custom=\u81ea\u5b9a\u7fa9
|
||||
settings.choose_gamedir=\u9009\u62e9\u6e38\u620f\u8def\u5f84
|
||||
settings.failed_load=\u8a2d\u5b9a\u6587\u4ef6\u52a0\u8f09\u5931\u6557\uff0c\u53ef\u80fd\u662f\u5347\u7d1a\u4e86\u555f\u52d5\u5668\u6216\u88ab\u4eba\u5de5\u4fee\u6539\u9020\u6210\u932f\u8aa4\uff0c\u662f\u5426\u6e05\u9664\uff1f
|
||||
|
||||
settings.modpack.choose=\u9078\u64c7\u8981\u5c0e\u5165\u7684\u904a\u6232\u6574\u5408\u5305\u6587\u4ef6
|
||||
settings.modpack.install_error=\u5b89\u88dd\u5931\u6557\uff0c\u53ef\u80fd\u662f\u6574\u5408\u5305\u683c\u5f0f\u4e0d\u6b63\u78ba\u6216\u64cd\u4f5c\u6587\u4ef6\u5931\u6557
|
||||
settings.modpack.save=\u9078\u64c7\u8981\u5c0e\u51fa\u5230\u7684\u904a\u6232\u6574\u5408\u5305\u4f4d\u7f6e
|
||||
settings.modpack.export_error=\u5c0e\u51fa\u5931\u6557\uff0c\u53ef\u80fd\u662f\u60a8\u7684\u904a\u6232\u6587\u4ef6\u593e\u683c\u5f0f\u4e0d\u6b63\u78ba\u6216\u64cd\u4f5c\u6587\u4ef6\u5931\u6557
|
||||
settings.modpack=\u61f6\u4eba\u5305
|
||||
settings.modpack.choose=\u9078\u64c7\u8981\u5c0e\u5165\u7684\u904a\u6232\u61f6\u4eba\u5305\u6587\u4ef6
|
||||
settings.modpack.install.task=\u5c0e\u5165\u61f6\u4eba\u5305
|
||||
settings.modpack.install_error=\u5b89\u88dd\u5931\u6557\uff0c\u53ef\u80fd\u662f\u6574\u5408\u5305\u683c\u5f0f\u4e0d\u6b63\u78ba\u6216\u64cd\u4f5c\u6587\u4ef6\u5931\u6557
|
||||
settings.modpack.save=\u9078\u64c7\u8981\u5c0e\u51fa\u5230\u7684\u904a\u6232\u61f6\u4eba\u5305\u4f4d\u7f6e
|
||||
settings.modpack.save.task=\u5c0e\u51fa\u61f6\u4eba\u5305
|
||||
settings.modpack.export_error=\u5c0e\u51fa\u5931\u6557\uff0c\u53ef\u80fd\u662f\u60a8\u7684\u904a\u6232\u6587\u4ef6\u593e\u683c\u5f0f\u4e0d\u6b63\u78ba\u6216\u64cd\u4f5c\u6587\u4ef6\u5931\u6557
|
||||
settings.modpack.enter_name=\u7d66\u904a\u6232\u8d77\u500b\u4f60\u559c\u6b61\u7684\u540d\u5b57
|
||||
|
||||
mods=Mod\u7ba1\u7406
|
||||
mods.choose_mod=\u9009\u62e9\u6a21\u7ec4
|
||||
@ -260,6 +263,7 @@ launcher.enable_shadow=\u542f\u7528\u7a97\u53e3\u9634\u5f71(\u91cd\u542f\u542f\u
|
||||
launcher.theme=\u4e3b\u9898
|
||||
launcher.proxy=\u4ee3\u7406
|
||||
launcher.decorated=\u555f\u7528\u7a97\u53e3\u908a\u6846(Linux\u4e0b\u53ef\u89e3\u6c7a\u7a0b\u5e8f\u754c\u9762\u5168\u7070\u554f\u984c)
|
||||
launcher.modpack=<html><a href="http://blog.163.com/huanghongxun2008@126/blog/static/7738046920160323812771/">\u6574\u5408\u5305\u4f5c\u8005\u5e2e\u52a9</a></html>
|
||||
|
||||
launcher.title.game=\u904a\u6232\u8a2d\u5b9a
|
||||
launcher.title.main=\u4e3b\u9801
|
||||
|
@ -208,11 +208,14 @@ settings.custom=\u81ea\u5b9a\u4e49
|
||||
settings.choose_gamedir=\u9009\u62e9\u6e38\u620f\u8def\u5f84
|
||||
settings.failed_load=\u8bbe\u7f6e\u6587\u4ef6\u52a0\u8f7d\u5931\u8d25\uff0c\u53ef\u80fd\u662f\u5347\u7ea7\u4e86\u542f\u52a8\u5668\u6216\u88ab\u4eba\u5de5\u4fee\u6539\u9020\u6210\u9519\u8bef\uff0c\u662f\u5426\u6e05\u9664\uff1f
|
||||
|
||||
settings.modpack=\u6574\u5408\u5305
|
||||
settings.modpack.choose=\u9009\u62e9\u8981\u5bfc\u5165\u7684\u6e38\u620f\u6574\u5408\u5305\u6587\u4ef6
|
||||
settings.modpack.install.task=\u5bfc\u5165\u6574\u5408\u5305
|
||||
settings.modpack.install_error=\u5b89\u88c5\u5931\u8d25\uff0c\u53ef\u80fd\u662f\u6574\u5408\u5305\u683c\u5f0f\u4e0d\u6b63\u786e\u6216\u64cd\u4f5c\u6587\u4ef6\u5931\u8d25
|
||||
settings.modpack.save=\u9009\u62e9\u8981\u5bfc\u51fa\u5230\u7684\u6e38\u620f\u6574\u5408\u5305\u4f4d\u7f6e
|
||||
settings.modpack.save.task=\u5bfc\u51fa\u6574\u5408\u5305
|
||||
settings.modpack.export_error=\u5bfc\u51fa\u5931\u8d25\uff0c\u53ef\u80fd\u662f\u60a8\u7684\u6e38\u620f\u6587\u4ef6\u5939\u683c\u5f0f\u4e0d\u6b63\u786e\u6216\u64cd\u4f5c\u6587\u4ef6\u5931\u8d25
|
||||
settings.modpack=\u6574\u5408\u5305
|
||||
settings.modpack.enter_name=\u7ed9\u6e38\u620f\u8d77\u4e2a\u4f60\u559c\u6b22\u7684\u540d\u5b57
|
||||
|
||||
mods=Mod\u7ba1\u7406
|
||||
mods.choose_mod=\u9009\u62e9\u6a21\u7ec4
|
||||
@ -257,6 +260,7 @@ launcher.enable_shadow=\u542f\u7528\u7a97\u53e3\u9634\u5f71(\u91cd\u542f\u542f\u
|
||||
launcher.theme=\u4e3b\u9898
|
||||
launcher.proxy=\u4ee3\u7406
|
||||
launcher.decorated=\u542f\u7528\u7a97\u53e3\u8fb9\u6846(Linux\u4e0b\u53ef\u89e3\u51b3\u7a0b\u5e8f\u754c\u9762\u5168\u7070\u95ee\u9898)
|
||||
launcher.modpack=<html><a href="http://blog.163.com/huanghongxun2008@126/blog/static/7738046920160323812771/">\u6574\u5408\u5305\u4f5c\u8005\u5e2e\u52a9</a></html>
|
||||
|
||||
launcher.title.game=\u6e38\u620f\u8bbe\u7f6e
|
||||
launcher.title.main=\u4e3b\u9875
|
||||
|
@ -0,0 +1,25 @@
|
||||
#The contents of this file are subject to the terms of the Common Development
|
||||
#and Distribution License (the License). You may not use this file except in
|
||||
#compliance with the License.
|
||||
# You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
#or http://www.netbeans.org/cddl.txt.
|
||||
# When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
#and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
#If applicable, add the following below the CDDL Header, with the fields
|
||||
#enclosed by brackets [] replaced by your own identifying information:
|
||||
#"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Next_>=Next >
|
||||
Next_mnemonic=N
|
||||
<_Prev=< Prev
|
||||
Prev_mnemonic=P
|
||||
Finish=Finish
|
||||
Finish_mnemonic=F
|
||||
Cancel=Cancel
|
||||
Cancel_mnemonic=C
|
||||
Help=Help
|
||||
Help_mnemonic=H
|
||||
Close=Close
|
||||
Close_mnemonic=C
|
||||
Summary=Summary
|
||||
Failed=Failed
|
@ -0,0 +1,25 @@
|
||||
#The contents of this file are subject to the terms of the Common Development
|
||||
#and Distribution License (the License). You may not use this file except in
|
||||
#compliance with the License.
|
||||
# You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
#or http://www.netbeans.org/cddl.txt.
|
||||
# When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
#and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
#If applicable, add the following below the CDDL Header, with the fields
|
||||
#enclosed by brackets [] replaced by your own identifying information:
|
||||
#"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Next_>=Weiter >
|
||||
Next_mnemonic=W
|
||||
<_Prev=< Zur\u00FCck
|
||||
Prev_mnemonic=Z
|
||||
Finish=Fertig
|
||||
Finish_mnemonic=F
|
||||
Cancel=Abbrechen
|
||||
Cancel_mnemonic=A
|
||||
Help=Hilfe
|
||||
Help_mnemonic=H
|
||||
Close=Schlie\u00DFen
|
||||
Close_mnemonic=S
|
||||
Summary=Zusammenfassung
|
||||
Failed=Fehler
|
@ -0,0 +1,30 @@
|
||||
#The contents of this file are subject to the terms of the Common Development
|
||||
#and Distribution License (the License). You may not use this file except in
|
||||
#compliance with the License.
|
||||
# You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
#or http://www.netbeans.org/cddl.txt.
|
||||
# When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
#and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
#If applicable, add the following below the CDDL Header, with the fields
|
||||
#enclosed by brackets [] replaced by your own identifying information:
|
||||
#"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Next_>=\u0395\u03C0\u03CC\u03BC\u03B5\u03BD\u03BF >
|
||||
Next_mnemonic=\u0395
|
||||
<_Prev=< \u03A0\u03C1\u03BF\u03B7\u03B3\u03BF\u03CD\u03BC\u03B5\u03BD\u03BF
|
||||
Prev_mnemonic=\u03A0
|
||||
Finish=\u03A4\u03AD\u03BB\u03BF\u03C2
|
||||
Finish_mnemonic=\u03A4
|
||||
Cancel=\u0391\u03BA\u03CD\u03C1\u03C9\u03C3\u03B7
|
||||
Cancel_mnemonic=\u0391
|
||||
Help=\u0392\u03BF\u03AE\u03B8\u03B5\u03B9\u03B1
|
||||
Help_mnemonic=\u0392
|
||||
Close=\u039A\u03BB\u03B5\u03AF\u03C3\u03B9\u03BC\u03BF
|
||||
Close_mnemonic=\u039A
|
||||
Summary=\u03A0\u03B5\u03C1\u03AF\u03BB\u03B7\u03C8\u03B7
|
||||
Failed=\u0391\u03C0\u03AD\u03C4\u03C5\u03C7\u03B5
|
||||
HELP=\u0392\u039F\u0397\u0398\u0395\u0399\u0391
|
||||
FINISH=\u03A4\u0395\u039B\u039F\u03A3
|
||||
NEXT=\u0395\u03A0\u039F\u039C\u0395\u039D\u039F
|
||||
PREV=\u03A0\u03A1\u039F\u0397\u0393\u039F\u03A5\u039C\u0395\u039D\u039F
|
||||
CANCEL=\u0391\u039A\u03A5\u03A1\u03A9\u03A3\u0397
|
@ -0,0 +1,25 @@
|
||||
#The contents of this file are subject to the terms of the Common Development
|
||||
#and Distribution License (the License). You may not use this file except in
|
||||
#compliance with the License.
|
||||
# You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
#or http://www.netbeans.org/cddl.txt.
|
||||
# When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
#and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
#If applicable, add the following below the CDDL Header, with the fields
|
||||
#enclosed by brackets [] replaced by your own identifying information:
|
||||
#"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Next_>=Suivant >
|
||||
Next_mnemonic=S
|
||||
<_Prev=< Pr\u00E9c\u00E9dent
|
||||
Prev_mnemonic=P
|
||||
Finish=Terminer
|
||||
Finish_mnemonic=T
|
||||
Cancel=Annuler
|
||||
Cancel_mnemonic=n
|
||||
Help=Aide
|
||||
Help_mnemonic=A
|
||||
Close=Fermer
|
||||
Close_mnemonic=F
|
||||
Summary=Sommaire
|
||||
Failed=\u00C9chec
|
@ -0,0 +1,25 @@
|
||||
#The contents of this file are subject to the terms of the Common Development
|
||||
#and Distribution License (the License). You may not use this file except in
|
||||
#compliance with the License.
|
||||
# You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
#or http://www.netbeans.org/cddl.txt.
|
||||
# When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
#and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
#If applicable, add the following below the CDDL Header, with the fields
|
||||
#enclosed by brackets [] replaced by your own identifying information:
|
||||
#"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Next_>=Avanti >
|
||||
Next_mnemonic=v
|
||||
<_Prev=< Indietro
|
||||
Prev_mnemonic=I
|
||||
Finish=Fine
|
||||
Finish_mnemonic=F
|
||||
Cancel=Annulla
|
||||
Cancel_mnemonic=n
|
||||
Help=Aiuto
|
||||
Help_mnemonic=A
|
||||
Close=Chiudi
|
||||
Close_mnemonic=C
|
||||
Summary=Riassunto
|
||||
Failed=Fallimento
|
@ -0,0 +1,25 @@
|
||||
#The contents of this file are subject to the terms of the Common Development
|
||||
#and Distribution License (the License). You may not use this file except in
|
||||
#compliance with the License.
|
||||
# You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
#or http://www.netbeans.org/cddl.txt.
|
||||
# When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
#and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
#If applicable, add the following below the CDDL Header, with the fields
|
||||
#enclosed by brackets [] replaced by your own identifying information:
|
||||
#"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Next_>=Volgende >
|
||||
Next_mnemonic=g
|
||||
<_Prev=< Vorige
|
||||
Prev_mnemonic=r
|
||||
Finish=Be\u00EBindigen
|
||||
Finish_mnemonic=B
|
||||
Cancel=Annuleren
|
||||
Cancel_mnemonic=A
|
||||
Help=Help
|
||||
Help_mnemonic=H
|
||||
Close=Sluiten
|
||||
Close_mnemonic=S
|
||||
Summary=Sammenvatting
|
||||
Failed=Gefaald
|
@ -0,0 +1,27 @@
|
||||
#The contents of this file are subject to the terms of the Common Development
|
||||
#and Distribution License (the License). You may not use this file except in
|
||||
#compliance with the License.
|
||||
# You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
#or http://www.netbeans.org/cddl.txt.
|
||||
# When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
#and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
#If applicable, add the following below the CDDL Header, with the fields
|
||||
#enclosed by brackets [] replaced by your own identifying information:
|
||||
#"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Next_>=Pr\u00F3ximo >
|
||||
Next_mnemonic=P
|
||||
<_Prev=< Previs\u00E3o
|
||||
Prev_mnemonic=r
|
||||
Finish=Fim
|
||||
Finish_mnemonic=F
|
||||
Cancel=Cancelar
|
||||
Cancel_mnemonic=C
|
||||
Help=Ajudar
|
||||
Help_mnemonic=A
|
||||
Close=Fechar
|
||||
Close_mnemonic=F
|
||||
Summary=Sum\u00E1rio
|
||||
|
||||
Failed=Falhou
|
||||
|
@ -0,0 +1,31 @@
|
||||
#The contents of this file are subject to the terms of the Common Development
|
||||
#and Distribution License (the License). You may not use this file except in
|
||||
#compliance with the License.
|
||||
# You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
#or http://www.netbeans.org/cddl.txt.
|
||||
# When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
#and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
#If applicable, add the following below the CDDL Header, with the fields
|
||||
#enclosed by brackets [] replaced by your own identifying information:
|
||||
#"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Next_>=N\u00E4sta >
|
||||
Next_mnemonic=N
|
||||
<_Prev=< F\u00F6reg\u00E5ende
|
||||
Prev_mnemonic=P
|
||||
Finish=Avsluta
|
||||
Finish_mnemonic=F
|
||||
Cancel=Avbryt
|
||||
Cancel_mnemonic=C
|
||||
Help=Hj\u00E4lp
|
||||
Help_mnemonic=H
|
||||
Close=St\u00E4ng
|
||||
Close_mnemonic=C
|
||||
Summary=Sammanfatning
|
||||
Failed=Misslyckades
|
||||
HELP=HJ\u00C4LP
|
||||
FINISH=AVSLUTA
|
||||
NEXT=N\u00C4STA
|
||||
PREV=F\u00D6REG\u00C5ENDE
|
||||
CANCEL=AVBRYT
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
@ -0,0 +1,18 @@
|
||||
#The contents of this file are subject to the terms of the Common Development
|
||||
#and Distribution License (the License). You may not use this file except in
|
||||
#compliance with the License.
|
||||
# You can obtain a copy of the License at http://www.netbeans.org/cddl.html
|
||||
#or http://www.netbeans.org/cddl.txt.
|
||||
# When distributing Covered Code, include this CDDL Header Notice in each file
|
||||
#and include the License file at http://www.netbeans.org/cddl.txt.
|
||||
#If applicable, add the following below the CDDL Header, with the fields
|
||||
#enclosed by brackets [] replaced by your own identifying information:
|
||||
#"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Cannot_back_out_past_first_entry=Cannot back out past first entry
|
||||
elipsis=...
|
||||
Steps=Steps
|
||||
Summary=Summary
|
||||
ACN_InstructionsPanel=Wizard Step List
|
||||
ACSD_InstructionsPanel=List of the panels in this wizard, which can change \
|
||||
based on the choices made on individual panels within it
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user