test ModpackManager

This commit is contained in:
huangyuhui 2016-01-23 16:52:40 +08:00
parent 102205de40
commit 08301bed8d
120 changed files with 8363 additions and 169 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;
/**

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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> {

View File

@ -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;

View File

@ -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 + "/";

View File

@ -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;
/**

View File

@ -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);
}
}

View 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;

View File

@ -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;

View File

@ -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) {
}

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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");

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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);
/**

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -346,7 +346,9 @@
</Component>
<Component class="javax.swing.JButton" name="btnExportModpack">
<Properties>
<Property name="text" type="java.lang.String" value="&#x5bfc;&#x51fa;&#x6574;&#x5408;&#x5305;"/>
<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(&quot;{key}&quot;)"/>
</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="&#x5bfc;&#x5165;&#x6574;&#x5408;&#x5305;"/>
<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(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnImportModpackActionPerformed"/>

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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(&quot;{key}&quot;)"/>
</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="&#x8981;&#x5bfc;&#x51fa;&#x7684;&#x6e38;&#x620f;&#x7248;&#x672c;"/>
</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="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JCheckBox" name="chkSave">
<Properties>
<Property name="text" type="java.lang.String" value="&#x5141;&#x8bb8;&#x5bfc;&#x51fa;&#x5b58;&#x6863;"/>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkSaveItemStateChanged"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.utils;
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
import java.util.Map;
import rx.Observable;

View File

@ -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;

View File

@ -17,6 +17,8 @@
*/
package org.jackhuang.hellominecraft.utils;
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
/**
*
* @author huangyuhui

View File

@ -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;

View File

@ -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));

View File

@ -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));
}

View File

@ -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;
/**
*

View File

@ -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;
/**
*

View File

@ -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;
/**

View File

@ -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;
/**

View File

@ -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;
/**

View File

@ -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);
}
}

View File

@ -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;
/**
*

View File

@ -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;
/**
*

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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 &quot;summary&quot; 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);
}

View File

@ -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
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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!");
}
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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