Java Code Examples for docking.widgets.OptionDialog#OPTION_ONE

The following examples show how to use docking.widgets.OptionDialog#OPTION_ONE . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: MergeManagerProvider.java    From ghidra with Apache License 2.0 6 votes vote down vote up
void cancelCallback(boolean force) {

		boolean cancel = force;
		if (!force) {
			int choice =
				OptionDialog.showYesNoDialogWithNoAsDefaultButton(null, "Confirm Cancel Merge",
					"Warning!  Cancel causes the entire merge process to be canceled.\n" +
						"Do you want to cancel the Merge Process?");
			cancel = choice == OptionDialog.OPTION_ONE;
		}

		if (cancel) {
			wasCanceled = true;
			MergeManager mergeManager = plugin.getMergeManager();
			if (mergeManager != null) {
				mergeManager.cancel();
			}
		}
	}
 
Example 2
Source File: ProjectInfoDialog.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private void convertToIndexedFilesystem() {
	if (filesAreOpen()) {
		Msg.showInfo(getClass(), getComponent(),
			"Cannot Convert/Upgrade Project Storage with Open Files",
			"Before your project can be converted, you must close\n" +
				"files in running tools.");
		return;
	}

	RepositoryAdapter rep = project.getRepository();
	if (rep != null) {
		rep.disconnect();
	}

	if (OptionDialog.showOptionDialog(getComponent(), "Confirm Convert/Upgrade Project Storage",
		"Convert/Upgrade Project Storage to latest Indexed Filesystem ?\n \n" +
			"WARNING!  Once converted a project may no longer be opened by\n" +
			"any version of Ghidra older than version 6.1.",
		"Convert", OptionDialog.WARNING_MESSAGE) == OptionDialog.OPTION_ONE) {

		ProjectLocator projectLocator = project.getProjectLocator();
		FileActionManager actionMgr = plugin.getFileActionManager();
		actionMgr.closeProject(false);

		// put the conversion in a task
		ConvertProjectStorageTask task = new ConvertProjectStorageTask(projectLocator);
		new TaskLauncher(task, getComponent(), 500);

		// block until task completes
		if (task.getStatus()) {
			close();
			actionMgr.openProject(projectLocator);
			plugin.getProjectActionManager().showProjectInfo();
		}
	}

}
 
Example 3
Source File: DataTypeManagerHandler.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private boolean acquireSaveLock(UndoableDomainObject undoableDomainObject) {
	if (!undoableDomainObject.lock(null)) {
		String title = "Save " + CONTENT_NAME + " (Busy)";
		StringBuilder buf = new StringBuilder();
		buf.append("The " + CONTENT_NAME + " is currently being modified by \n");
		buf.append("the following actions:\n ");
		Transaction t = undoableDomainObject.getCurrentTransaction();
		List<String> list = t.getOpenSubTransactions();
		Iterator<String> it = list.iterator();
		while (it.hasNext()) {
			buf.append("\n     ");
			buf.append(it.next());
		}
		buf.append("\n \n");
		buf.append(
			"WARNING! The above task(s) should be cancelled before attempting a Save.\n");
		buf.append("Only proceed if unable to cancel them.\n \n");
		buf.append(
			"If you continue, all changes made by these tasks, as well as any other overlapping task,\n");
		buf.append(
			"will be LOST and subsequent transaction errors may occur while these tasks remain active.\n \n");

		int result = OptionDialog.showOptionDialog(tool.getToolFrame(), title, buf.toString(),
			"Save Archive!", OptionDialog.WARNING_MESSAGE);

		if (result == OptionDialog.OPTION_ONE) {
			undoableDomainObject.forceLock(true, "Save Archive");
			return true;
		}
		return false;
	}
	return true;
}
 
Example 4
Source File: CreateArchiveAction.java    From ghidra with Apache License 2.0 5 votes vote down vote up
@Override
public void actionPerformed(ActionContext context) {
	DataTypeArchiveGTree gTree = plugin.getProvider().getGTree();
	ArchiveFileChooser fileChooser = new ArchiveFileChooser(gTree);
	fileChooser.setApproveButtonText("Create Archive");
	fileChooser.setApproveButtonToolTipText("Create Archive");
	fileChooser.setTitle("Create Archive");

	Msg.trace(this, "Showing filechooser to get new archive name...");
	File file = fileChooser.promptUserForFile("New_Archive");
	if (file == null) {
		Msg.trace(this, "No new archive filename chosen by user - not performing action");
		return;
	}

	Msg.trace(this, "User picked file: " + file.getAbsolutePath());

	if (file.exists()) {
		Msg.trace(this, "Need to overwrite--showing dialog");
		if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(gTree,
			"Overwrite Existing File?",
			"Do you want to overwrite existing file\n" +
				file.getAbsolutePath()) != OptionDialog.OPTION_ONE) {
			Msg.trace(this, "\tdo not overwrite was chosen");
			return;
		}
		Msg.trace(this, "\toverwriting file!");
		file.delete();
	}
	Archive newArchive = plugin.getDataTypeManagerHandler().createArchive(file);
	if (newArchive != null) {
		Msg.trace(this, "Created new archive: " + newArchive.getName());
		selectNewArchive(newArchive, gTree);
	}
}
 
Example 5
Source File: ArchiveUtils.java    From ghidra with Apache License 2.0 5 votes vote down vote up
static File getFile(Component component, FileArchive archive) {
	ArchiveFileChooser fileChooser = new ArchiveFileChooser(component);
	String archiveName = archive.getName();
	File file = fileChooser.promptUserForFile(archiveName);

	if (file == null) {
		return null;
	}
	if (file.equals(archive.getFile())) {
		return file;
	}

	if (archive.getArchiveManager().isInUse(file)) {
		Msg.showInfo(ArchiveUtils.class, component, "Cannot Perform Save As",
			"Cannot save archive to " + file.getName() + "\nbecause " + file.getName() +
				" is in use.");
		return null;
	}

	if (file.exists()) {
		if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(component,
			"Overwrite Existing File?", "Do you want to overwrite existing file\n" +
				file.getAbsolutePath()) != OptionDialog.OPTION_ONE) {
			return null;
		}

		if (!deleteArchiveFile(file)) {
			Msg.showError(log, component, "Error Deleting File",
				"Could not delete file " + file.getAbsolutePath());

			return null;
		}
	}

	return file;
}
 
Example 6
Source File: ProjectDataDeleteTask.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private boolean confirmDeleteFiles() {

		int fileCount = filesToDelete.size();
		String files = fileCount != 1 ? fileCount + " files" : " 1 file";
		if (!foldersToDelete.isEmpty()) {
			int folderCount = foldersToDelete.size();
			files += folderCount > 1 ? " and " + folderCount + " folders" : " and 1 folder";
		}

		StringBuilder buffy = new StringBuilder("<html>");
		buffy.append("<div style='font-size: 110%; text-align: center;'>");
		buffy.append("<span>");
		buffy.append("Are you sure you want to <u>permanently</u> delete " + files + "?");
		buffy.append("</span><br><br>");

		buffy.append("<span style='color: red;'>");
		buffy.append("There is no undo operation!");
		buffy.append("</span>");
		buffy.append("</div>");

		/*
		msg +=
			"<div style='margin-bottom: 20pt; font-size: 110%; text-align: center; color: red;'>Are you sure you want to permanently delete these files?</div>" +
				"<div style='margin-bottom: 20pt; text-align: center; font-weight: bold;'>There is no undo operation</div>";
				*/

		int choice = OptionDialog.showOptionDialog(parentComponent, "Confirm Delete Files?",
			buffy.toString(), "Delete Files", OptionDialog.QUESTION_MESSAGE, "Cancel");
		return choice == OptionDialog.OPTION_ONE;
	}
 
Example 7
Source File: ProjectDataDeleteTask.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private boolean confirmUserSkipFailedPreprocessedFiles() {

		String msg = "<html><div style='margin-bottom: 20pt; text-align: center;'>There were " +
			failPreprocessCheckedOut.size() +
			" versioned and checked-out files in the requested set of files that cannot be deleted.</div>" +
			"<div style='margin-bottom: 20pt; text-align: center;'>Skip these files and continue or cancel delete operation?</div>";

		if (OptionDialog.showOptionDialog(parentComponent, "Continue and Skip Problem Files?", msg,
			"Skip and Continue") != OptionDialog.OPTION_ONE) {
			return false;
		}

		return true;
	}
 
Example 8
Source File: ParseDialog.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private void delete() {
	ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem();
	if (item.isUserDefined) {
		if (OptionDialog.showOptionDialog(getComponent(), "Delete Profile?",
			"Are you sure you want to delete profile " + item.getName(), "Delete",
			OptionDialog.QUESTION_MESSAGE) == OptionDialog.OPTION_ONE) {
			item.file.delete();
			itemList.remove(item);
			comboModel.removeElement(item);
		}
	}
}
 
Example 9
Source File: ExporterDialog.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private boolean tryExport(TaskMonitor monitor) {
	Exporter exporter = getSelectedExporter();

	exporter.setExporterServiceProvider(tool);
	DomainObject dobj = getDomainObject(monitor);
	if (dobj == null) {
		return false;
	}
	ProgramSelection selection = getApplicableProgramSeletion();
	File outputFile = getSelectedOutputFile();

	try {
		if (outputFile.exists() &&
			OptionDialog.showOptionDialog(getComponent(), "Overwrite Existing File?",
				"The file " + outputFile + " already exists.\nDo you want to overwrite it?",
				"Overwrite", OptionDialog.QUESTION_MESSAGE) != OptionDialog.OPTION_ONE) {
			return false;
		}
		if (options != null) {
			exporter.setOptions(options);
		}
		boolean success = exporter.export(outputFile, dobj, selection, monitor);
		displaySummaryResults(exporter, dobj);
		return success;
	}
	catch (Exception e) {
		Msg.error(this, "Exception exporting", e);
		SystemUtilities.runSwingLater(() -> setStatusText(
			"Exception exporting: " + e.getMessage() + ".  If null, see log for details."));
	}
	return false;
}
 
Example 10
Source File: GhidraScriptComponentProvider.java    From ghidra with Apache License 2.0 5 votes vote down vote up
void deleteScript() {
	ResourceFile script = getSelectedScript();
	if (script == null) {
		return;
	}
	ResourceFile directory = script.getParentFile();

	Path path = GhidraScriptUtil.getScriptPath(directory);
	if (path == null || path.isReadOnly()) {
		Msg.showWarn(getClass(), getComponent(), getName(),
			"Unable to delete scripts in '" + directory + "'.");
		return;
	}

	int result = OptionDialog.showYesNoDialog(getComponent(), getName(),
		"Are you sure you want to delete script '" + script.getName() + "'?");
	if (result == OptionDialog.OPTION_ONE) {
		if (removeScript(script)) {
			GhidraScriptProvider provider = GhidraScriptUtil.getProvider(script);
			if (provider.deleteScript(script)) {
				restoreSelection(script);
			}
			else {
				Msg.showInfo(getClass(), getComponent(), getName(),
					"Unable to delete script '" + script.getName() + "'" + "\n" +
						"Please verify the file permissions.");
			}
		}
	}
}
 
Example 11
Source File: ColumnFilterDialog.java    From ghidra with Apache License 2.0 5 votes vote down vote up
/**
 * Ask the user if they want to apply the filter changes before closing. If they agree, then
 * apply the filter.
 *
 * @return true if they don't cancel, false if they answer yes or no.
 */
private boolean promptToApplyFilter() {
	int choice = OptionDialog.showOptionDialog(null, "Unapplied Changes",
		"You have unapplied changes that will be lost!\n" +
			"Do you want to apply them before you exit?",
		"Apply Changes", "Discard Changes", OptionDialog.QUESTION_MESSAGE);

	if (choice == OptionDialog.OPTION_ONE) {
		applyFilter();
	}
	return choice != OptionDialog.CANCEL_OPTION;
}
 
Example 12
Source File: DefaultProject.java    From ghidra with Apache License 2.0 5 votes vote down vote up
/**
 * Creates a ProjectLock and attempts to lock it. This handles the case
 * where the project was previously locked.
 * 
 * @param locator the project locator
 * @param allowInteractiveForce if true, when a lock cannot be obtained, the
 *            user will be prompted
 * @return A locked ProjectLock
 * @throws ProjectLockException if lock failed
 */
private ProjectLock getProjectLock(ProjectLocator locator, boolean allowInteractiveForce) {
	ProjectLock lock = new ProjectLock(locator);
	if (lock.lock()) {
		return lock;
	}

	// in headless mode, just spit out an error
	if (!allowInteractiveForce || SystemUtilities.isInHeadlessMode()) {
		return null;
	}

	String projectStr = "Project: " + HTMLUtilities.escapeHTML(locator.getLocation()) +
		System.getProperty("file.separator") + HTMLUtilities.escapeHTML(locator.getName());
	String lockInformation = lock.getExistingLockFileInformation();
	if (!lock.canForceLock()) {
		Msg.showInfo(getClass(), null, "Project Locked",
			"<html>Project is locked. You have another instance of Ghidra<br>" +
				"already running with this project open (locally or remotely).<br><br>" +
				projectStr + "<br><br>" + "Lock information: " + lockInformation);
		return null;
	}

	int userChoice = OptionDialog.showOptionDialog(null, "Project Locked - Delete Lock?",
		"<html>Project is locked. You may have another instance of Ghidra<br>" +
			"already running with this project opened (locally or remotely).<br>" + projectStr +
			"<br><br>" + "If this is not the case, you can delete the lock file:  <br><b>" +
			locator.getProjectLockFile().getAbsolutePath() + "</b>.<br><br>" +
			"Lock information: " + lockInformation,
		"Delete Lock", OptionDialog.QUESTION_MESSAGE);
	if (userChoice == OptionDialog.OPTION_ONE) { // Delete Lock
		if (lock.forceLock()) {
			return lock;
		}

		Msg.showError(this, null, "Error", "Attempt to force lock failed! " + locator);
	}
	return null;
}
 
Example 13
Source File: ToolServicesImpl.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private File chooseToolFile(ToolTemplate tool) {
	GhidraFileChooser fileChooser = getFileChooser();

	File exportFile = null;
	while (exportFile == null) {
		exportFile = fileChooser.getSelectedFile(); // show the chooser
		if (exportFile == null) {
			return null; // user cancelled
		}

		Preferences.setProperty(Preferences.LAST_TOOL_EXPORT_DIRECTORY, exportFile.getParent());
		if (!exportFile.getName().endsWith(ToolUtils.TOOL_EXTENSION)) {
			exportFile = new File(exportFile.getAbsolutePath() + ToolUtils.TOOL_EXTENSION);
		}

		if (exportFile.exists()) {
			int result = OptionDialog.showOptionDialog(null, "Overwrite?",
				"Overwrite existing file: '" + exportFile.getName() + "'?", "Overwrite",
				OptionDialog.QUESTION_MESSAGE);
			if (result != OptionDialog.OPTION_ONE) {
				exportFile = null; // user chose not to overwrite
			}
		}
	}

	return exportFile;
}
 
Example 14
Source File: RemoveInvalidArchiveFromProgramAction.java    From ghidra with Apache License 2.0 5 votes vote down vote up
@Override
public void actionPerformed(ActionContext context) {
	GTree gtree = (GTree) context.getContextObject();

	TreePath[] selectionPaths = gtree.getSelectionPaths();
	if (selectionPaths.length != 1) {
		return;
	}
	Object pathComponent = selectionPaths[0].getLastPathComponent();

	// our isValidContext() ensures this cast is save
	InvalidArchiveNode invalidArchiveNode = (InvalidArchiveNode) pathComponent;

	if (OptionDialog.showOptionDialog(gtree, "Confirm Remove Invalid Archive(s)",
		"<html><b>Are you sure you want to delete archive: " +
			HTMLUtilities.escapeHTML(invalidArchiveNode.getName()) +
			" from the program?<br><br>" +
			"<font color=\"red\">(WARNING: This action will disassociate " +
			"all datatypes in the program from this archive.)</font></b>",
		"Yes", OptionDialog.QUESTION_MESSAGE) != OptionDialog.OPTION_ONE) {
		return;
	}

	Archive archive = invalidArchiveNode.getArchive();
	DataTypeManagerHandler dataTypeManagerHandler = plugin.getDataTypeManagerHandler();
	dataTypeManagerHandler.removeInvalidArchive((InvalidFileArchive) archive);
}
 
Example 15
Source File: OpenDomainFileTask.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private boolean isRecoveryOK(final DomainFile dfile)
		throws InterruptedException, InvocationTargetException {
	final boolean[] recoverFile = new boolean[] { false };
	if (dfile.isInWritableProject() && dfile.canRecover()) {
		Runnable r = () -> {
			int option = OptionDialog.showYesNoDialog(null, "Crash Recovery Data Found",
				"<html>" + HTMLUtilities.escapeHTML(dfile.getName()) + " has crash data.<br>" +
					"Would you like to recover unsaved changes?");
			recoverFile[0] = (option == OptionDialog.OPTION_ONE);
		};
		SwingUtilities.invokeAndWait(r);
	}
	return recoverFile[0];
}
 
Example 16
Source File: DataTypesProvider.java    From ghidra with Apache License 2.0 4 votes vote down vote up
private boolean isOkToLock() {
	return (OptionDialog.showYesNoDialog(archiveGTree, "Open Archive for Edit?",
		"Archive file is not modifiable.\nDo you want to open for edit?") == OptionDialog.OPTION_ONE);
}
 
Example 17
Source File: PdbParser.java    From ghidra with Apache License 2.0 4 votes vote down vote up
/**
 * Parse the PDB file, enforcing pre-conditions and post-conditions.
 *
 * @throws IOException If an I/O error occurs
 * @throws PdbException  if there was a problem during processing
 */
public void parse() throws IOException, PdbException {

	checkPdbLoaded();
	checkFileType();
	checkOSCompatibility();

	if (!forceAnalysis && !programAttributes.isProgramAnalyzed()) {
		throw new PdbException("Before loading a PDB, you must first analyze the program.");
	}

	processPdbContents(false);

	// The below code only applies when we are processing .pdb (not .pdb.xml) files
	if (!isXML) {

		try {//give thread sometime to spin up...
			Thread.sleep(1000);
		}
		catch (Exception e) {
			// don't care
		}

		if (hasErrors()) {
			throw new PdbException(getErrorAndWarningMessages());
		}

		if (hasWarnings()) {
			if (SystemUtilities.isInHeadlessMode()) {
				throw new PdbException(
					getErrorAndWarningMessages() + "..  Skipping PDB processing.");
			}
			int option = OptionDialog.showYesNoDialog(null, "Continue Loading PDB?",
				getErrorAndWarningMessages() + "\n " + "\nContinue anyway?" + "\n " +
					"\nPlease note: Invalid disassembly may be produced!");
			if (option == OptionDialog.OPTION_ONE) {
				cleanup();
				processPdbContents(true);//attempt without validation...
			}
			else {
				throw new PdbException(getErrorAndWarningMessages());
			}
		}
	}
	else { // only for .pdb.xml files.
		verifyPdbSignature();
	}
	parsed = true;
}
 
Example 18
Source File: ProjectActionManager.java    From ghidra with Apache License 2.0 4 votes vote down vote up
private void changePassword() {
	RepositoryAdapter repository = activeProject.getRepository();
	if (repository == null) {
		return;
	}
	PasswordChangeDialog dlg = null;
	char[] pwd = null;
	try {
		repository.connect();

		ServerInfo info = repository.getServerInfo();

		if (OptionDialog.OPTION_ONE != OptionDialog.showOptionDialog(tool.getToolFrame(),
			"Confirm Password Change",
			"You are about to change your repository server password for:\n" + info +
				"\n \nThis password is used when connecting to project\n" +
				"repositories associated with this server",
			"Continue", OptionDialog.WARNING_MESSAGE)) {
			return;
		}

		dlg = new PasswordChangeDialog("Change Password", "Repository Server",
			repository.getServerInfo().getServerName(), repository.getServer().getUser());
		tool.showDialog(dlg);
		pwd = dlg.getPassword();
		if (pwd != null) {
			repository.getServer().setPassword(
				HashUtilities.getSaltedHash(HashUtilities.SHA256_ALGORITHM, pwd));
			Msg.showInfo(getClass(), tool.getToolFrame(), "Password Changed",
				"Password was changed successfully");
		}
	}
	catch (IOException e) {
		ClientUtil.handleException(repository, e, "Password Change", tool.getToolFrame());
	}
	finally {
		if (pwd != null) {
			Arrays.fill(pwd, ' ');
		}
		if (dlg != null) {
			dlg.dispose();
		}
	}
}
 
Example 19
Source File: ParseDialog.java    From ghidra with Apache License 2.0 4 votes vote down vote up
private void saveAs(ComboBoxItem item) {

		InputDialog d = new InputDialog("Enter Profile Name", "Profile Name");
		plugin.getTool().showDialog(d, getComponent());

		String name = d.getValue();
		if (name != null && name.length() > 0) {
			if (!name.endsWith(FILE_EXTENSION)) {
				name = name + FILE_EXTENSION;
			}
			ResourceFile file = new ResourceFile(parentUserFile, name);
			if (file.equals(item.file)) {
				save(item);
				return;
			}

			if (file.exists()) {
				if (OptionDialog.showOptionDialog(rootPanel, "Overwrite Existing File?",
					"The file " + file.getAbsolutePath() +
						" already exists.\nDo you want to overwrite it?",
					"Yes", OptionDialog.QUESTION_MESSAGE) != OptionDialog.OPTION_ONE) {
					return;
				}
				file.delete();
			}
			ComboBoxItem newItem = new ComboBoxItem(file, true);
			if (itemList.contains(newItem)) {
				itemList.remove(newItem);
				comboModel.removeElement(newItem);
			}
			int index = Collections.binarySearch(itemList, newItem, comparator);
			if (index < 0) {
				index = -index - 1;
			}
			itemList.add(index, newItem);
			saveAsInProgress = true;
			writeProfile(newItem.file);
			newItem.isChanged = false;
			item.isChanged = false;
			try {
				comboModel.insertElementAt(newItem, index);
				comboBox.setSelectedIndex(index);
			}
			finally {
				saveAsInProgress = false;
			}
			setActionsEnabled();
		}
	}
 
Example 20
Source File: GhidraScript.java    From ghidra with Apache License 2.0 3 votes vote down vote up
/**
 * Returns a boolean value, using the String parameters for guidance. The actual behavior of
 * the method depends on your environment, which can be GUI or headless.
 * <p>
 * Regardless of environment -- if script arguments have been set, this method will use the
 * next argument in the array and advance the array index so the next call to an ask method
 * will get the next argument.  If there are no script arguments and a .properties file
 * sharing the same base name as the Ghidra Script exists (i.e., Script1.properties for
 * Script1.java), then this method will then look there for the String value to return.
 * The method will look in the .properties file by searching for a property name that is a
 * space-separated concatenation of the input String parameters (title + " " + question).
 * If that property name exists and its value represents a valid boolean value, then the
 * .properties value will be used in the following way:
 * <ol>
 * 		<li>In the GUI environment, this method displays a popup dialog that prompts the user
 * 			with a yes/no dialog with the specified title and question. Returns true if the user
 * 			selects "yes" to the question or false if the user selects "no".</li>
 * 		<li>In the headless environment, if a .properties file sharing the same base name as the
 * 			Ghidra Script exists (i.e., Script1.properties for Script1.java), then this method
 * 			looks there for the boolean value to return. The method will look in the .properties
 * 			file by searching for a property name that is a space-separated concatenation of the
 * 			String parameters (title + " " + question). If that property name exists and its
 * 			value represents a valid boolean value (either 'true' or 'false', case insensitive),
 * 			then that value	is returned. Otherwise, an Exception is thrown if there is an
 * 			invalid or missing .properties value.</li>
 * </ol>
 * 
 *
 * @param title the title of the dialog (in GUI mode) or the first part of the variable name
 * 			(in headless mode)
 * @param question the question to display to the user (in GUI mode) or the second part of the
 * 			variable name (in headless mode)
 * @return true if the user selects "yes" to the question (in GUI mode) or "true" (in headless
 * 			mode)
 * @throws IllegalArgumentException if in headless mode, there was a missing or invalid boolean
 * 			specified in the .properties file
 */
public boolean askYesNo(String title, String question) {

	String key = join(title, question);
	Boolean existingValue = loadAskValue(this::parseBoolean, key);
	if (isRunningHeadless()) {
		return existingValue;
	}

	return OptionDialog.showYesNoDialog(null, title, question) == OptionDialog.OPTION_ONE;
}