package org.basex.gui; import static org.basex.core.Text.*; import java.awt.*; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.data.*; import org.basex.gui.dialog.*; import org.basex.gui.layout.*; import org.basex.gui.text.TextEditor.*; import org.basex.gui.view.*; import org.basex.gui.view.editor.*; import org.basex.io.*; import org.basex.query.func.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.query.value.seq.*; import org.basex.query.value.type.*; import org.basex.util.*; import org.basex.util.list.*; /** * This enumeration encapsulates all commands that are triggered by GUI operations. * * @author BaseX Team 2005-17, BSD License * @author Christian Gruen */ public enum GUIMenuCmd implements GUICommand { /* DATABASE MENU */ /** Opens a dialog to create a new database. */ C_CREATE(NEW + DOTS, "% N", false, false) { @Override public void execute(final GUI gui) { // open file chooser for XML creation final DialogNew dialog = new DialogNew(gui); if(!dialog.ok()) return; final String in = gui.gopts.get(GUIOptions.INPUTPATH); final String db = gui.gopts.get(GUIOptions.DBNAME); DialogProgress.execute(gui, new CreateDB(db, in.isEmpty() ? null : in)); } }, /** Opens a dialog to manage databases. */ C_OPEN_MANAGE(OPEN_MANAGE + DOTS, "% M", false, false) { @Override public void execute(final GUI gui) { if(new DialogManage(gui).nodb() && BaseXDialog.confirm(gui, NEW_DB_QUESTION)) C_CREATE.execute(gui); } }, /** Shows database info. */ C_INFO(PROPERTIES + DOTS, "% D", true, false) { @Override public void execute(final GUI gui) { new DialogProps(gui); } }, /** Exports a database. */ C_EXPORT(EXPORT + DOTS, null, true, false) { @Override public void execute(final GUI gui) { final DialogExport dialog = new DialogExport(gui); if(!dialog.ok()) return; final IOFile root = new IOFile(dialog.path()); // check if existing files will be overwritten if(root.exists()) { IOFile file = null; boolean overwrite = false; final Data data = gui.context.data(); final IntList docs = data.resources.docs(); final int ds = docs.size(); for(int d = 0; d < ds; d++) { file = root.resolve(Token.string(data.text(docs.get(d), true))); if(file.exists()) { if(overwrite) { // more than one file will be overwritten; check remaining tests file = null; break; } overwrite = true; } } if(overwrite) { // show message for overwriting files or directories final String msg = file == null ? FILES_REPLACE_X : FILE_EXISTS_X; if(file == null) file = root; if(!BaseXDialog.confirm(gui, Util.info(msg, file))) return; } } DialogProgress.execute(gui, new Export(root.path())); } }, /** Closes the database. */ C_CLOSE(CLOSE, "% shift W", true, false) { @Override public void execute(final GUI gui) { gui.execute(new Close()); } }, /** Creates a new file in the editor. */ C_EDITNEW(NEW, "% T", false, false) { @Override public void execute(final GUI gui) { gui.editor.newFile(); } }, /** Opens a new file in the editor. */ C_EDITOPEN(OPEN + DOTS, "% O", false, false) { @Override public void execute(final GUI gui) { gui.editor.open(); } }, /** Reverts the current editor file. */ C_EDITREOPEN(REOPEN + DOTS, null, false, false) { @Override public void execute(final GUI gui) { gui.editor.getEditor().reopen(true); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Saves the current file in the editor. */ C_EDITSAVE(SAVE, "% S", false, false) { @Override public void execute(final GUI gui) { gui.editor.save(); } @Override public boolean enabled(final GUI gui) { final EditorArea ea = gui.editor.getEditor(); return gui.gopts.get(GUIOptions.SHOWEDITOR) && ea != null && (ea.modified() || !ea.opened()); } }, /** Saves the current editor file under a new name. */ C_EDITSAVEAS(SAVE_AS + DOTS, "% shift S", false, false) { @Override public void execute(final GUI gui) { gui.editor.saveAs(); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Closes the current editor file. */ C_EDITCLOSE(CLOSE, "% W", false, false) { @Override public void execute(final GUI gui) { gui.editor.close(null); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Jumps to the next error. */ C_NEXTERROR(NEXT_ERROR, "% PERIOD", false, false) { @Override public void execute(final GUI gui) { gui.editor.markError(true); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Adds or removes a comment. */ C_COMMENT(COMMENT, "% K", false, false) { @Override public void execute(final GUI gui) { gui.editor.getEditor().comment(); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Formats text in the editor. */ C_FORMAT(FORMAT, "% shift F", false, false) { @Override public void execute(final GUI gui) { gui.editor.getEditor().format(); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Sorts text. */ C_SORT(SORT, "% U", false, false) { @Override public void execute(final GUI gui) { gui.editor.getEditor().sort(); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Lower case. */ C_LOWERCASE(LOWER_CASE, "% shift L", false, false) { @Override public void execute(final GUI gui) { gui.editor.getEditor().toCase(Case.LOWER); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Upper case. */ C_UPPERCASE(UPPER_CASE, "% shift U", false, false) { @Override public void execute(final GUI gui) { gui.editor.getEditor().toCase(Case.UPPER); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Title case. */ C_TITLECASE(TITLE_CASE, "% shift T", false, false) { @Override public void execute(final GUI gui) { gui.editor.getEditor().toCase(Case.TITLE); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Exits the application. */ C_EXIT(EXIT, "% Q", false, false) { @Override public void execute(final GUI gui) { gui.dispose(); } }, /* EDIT COMMANDS */ /** Copies the current path to the clipboard. */ C_COPYPATH(COPY_PATH, "% shift C", true, false) { @Override public void execute(final GUI gui) { final int pre = gui.context.marked.pre(0); BaseXLayout.copy(Token.string(ViewData.path(gui.context.data(), pre))); } @Override public boolean enabled(final GUI gui) { // disallow copy of empty node set or root node final DBNodes marked = gui.context.marked; return marked != null && !marked.isEmpty(); } }, /** Copies the currently marked nodes. */ C_COPY(COPY, null, true, false) { @Override public void execute(final GUI gui) { final Context ctx = gui.context; final DBNodes n = ctx.marked; ctx.copied = new DBNodes(n.data(), n.pres()); } @Override public boolean enabled(final GUI gui) { // disallow copy of empty node set or root node return updatable(gui.context.marked); } }, /** Pastes the copied nodes. */ C_PASTE(PASTE, null, true, false) { @Override public void execute(final GUI gui) { final StringBuilder sb = new StringBuilder(); final DBNodes n = gui.context.copied; final long ns = n.size(); for(int i = 0; i < ns; ++i) { if(i > 0) sb.append(','); sb.append(openPre(n, i)); } gui.context.copied = null; gui.execute(new XQuery("insert nodes (" + sb + ") into " + openPre(gui.context.marked, 0))); } @Override public boolean enabled(final GUI gui) { final Context ctx = gui.context; // disallow copy of empty node set or root node return updatable(ctx.marked, Data.DOC) && ctx.copied != null; } }, /** Deletes the currently marked nodes. */ C_DELETE(DELETE + DOTS, null, true, false) { @Override public void execute(final GUI gui) { if(!BaseXDialog.confirm(gui, DELETE_NODES)) return; final StringBuilder sb = new StringBuilder(); final DBNodes n = gui.context.marked; final long ns = n.size(); for(int i = 0; i < ns; ++i) { if(i > 0) sb.append(','); sb.append(openPre(n, i)); } gui.context.marked = new DBNodes(n.data()); gui.context.copied = null; gui.context.focused = -1; gui.execute(new XQuery("delete nodes (" + sb + ')')); } @Override public boolean enabled(final GUI gui) { return updatable(gui.context.marked, Data.DOC); } }, /** Inserts new nodes. */ C_INSERT(NEW + DOTS, null, true, false) { @Override public void execute(final GUI gui) { final DBNodes n = gui.context.marked; final DialogInsert insert = new DialogInsert(gui); if(!insert.ok()) return; final StringList sl = insert.result; final NodeType type = ANode.type(insert.kind); String item = Token.string(type.string()) + " { " + quote(sl.get(0)) + " }"; if(type == NodeType.ATT || type == NodeType.PI) { item += " { " + quote(sl.get(1)) + " }"; } else if(type == NodeType.ELM) { item += " { () }"; } gui.context.copied = null; gui.execute(new XQuery("insert node " + item + " into " + openPre(n, 0))); } @Override public boolean enabled(final GUI gui) { return updatable(gui.context.marked, Data.ATTR, Data.PI, Data.COMM, Data.TEXT); } }, /** Opens a dialog to edit the currently marked nodes. */ C_EDIT(EDIT + DOTS, null, true, false) { @Override public void execute(final GUI gui) { final DBNodes n = gui.context.marked; final DialogEdit edit = new DialogEdit(gui, n.pre(0)); if(!edit.ok()) return; String rename = null; String replace = null; final int k = edit.kind; if(k == Data.ELEM || k == Data.PI || k == Data.ATTR) { rename = edit.result.get(0); if(k != Data.ELEM) replace = edit.result.get(1); } else { replace = edit.result.get(0); } if(rename != null) gui.execute(new XQuery("rename node " + openPre(n, 0) + " as " + quote(rename))); if(replace != null) gui.execute(new XQuery("replace value of node " + openPre(n, 0) + " with " + quote(replace))); } @Override public boolean enabled(final GUI gui) { return updatable(gui.context.marked, Data.DOC); } }, /** Filters the currently marked nodes. */ C_FILTER(FILTER_SELECTED, null, true, false) { @Override public void execute(final GUI gui) { final Context ctx = gui.context; DBNodes marked = ctx.marked; if(marked.isEmpty()) { final int pre = gui.context.focused; if(pre == -1) return; marked = new DBNodes(ctx.data(), pre); } gui.notify.context(marked, false, null); } @Override public boolean enabled(final GUI gui) { final DBNodes marked = gui.context.marked; return marked != null && !marked.isEmpty(); } }, /** Shows the XQuery view. */ C_SHOWEDITOR(EDITOR, "% E", false, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWEDITOR); gui.layoutViews(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Jumps to the currently edited file. */ C_JUMPFILE(JUMP_TO_FILE, "% J", false, false) { @Override public void execute(final GUI gui) { gui.editor.showProject(); gui.editor.jumpToFile(); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Finds files. */ C_FILESEARCH(FIND_FILES + DOTS, Prop.MAC ? "% shift H" : "% H", false, false) { @Override public void execute(final GUI gui) { gui.editor.showProject(); gui.editor.findFiles(); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } }, /** Shows the XQuery project structure. */ C_SHOWPROJECT(PROJECT, "% P", false, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWPROJECT); gui.editor.project(); } @Override public boolean enabled(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEDITOR); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWPROJECT); } }, /** Shows info. */ C_SHOWINFO(QUERY_INFO, "% I", false, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWINFO); gui.layoutViews(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWINFO); } }, /** Repository manager. */ C_PACKAGES(PACKAGES + DOTS, null, false, false) { @Override public void execute(final GUI gui) { new DialogPackages(gui); } }, /* VIEW MENU */ /** Shows the buttons. */ C_SHOWBUTTONS(BUTTONS, null, false, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWBUTTONS); gui.updateControl(gui.buttons, gui.gopts.get(GUIOptions.SHOWBUTTONS), BorderLayout.CENTER); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWBUTTONS); } }, /** Show Input Field. */ C_SHOWINPUT(INPUT_BAR, null, false, true) { @Override public void execute(final GUI gui) { gui.updateControl(gui.nav, gui.gopts.invert(GUIOptions.SHOWINPUT), BorderLayout.SOUTH); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWINPUT); } }, /** Shows the status bar. */ C_SHOWSTATUS(STATUS_BAR, null, false, true) { @Override public void execute(final GUI gui) { gui.updateControl(gui.status, gui.gopts.invert(GUIOptions.SHOWSTATUS), BorderLayout.SOUTH); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWSTATUS); } }, /** Shows the text view. */ C_SHOWRESULT(RESULT, "% R", false, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWTEXT); gui.layoutViews(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWTEXT); } }, /** Shows the map. */ C_SHOWMAP(MAP, "% 1", true, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWMAP); gui.layoutViews(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWMAP); } }, /** Shows the tree view. */ C_SHOWTREE(TREE, "% 2", true, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWTREE); gui.layoutViews(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWTREE); } }, /** Shows the tree view. */ C_SHOWFOLDER(FOLDER, "% 3", true, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWFOLDER); gui.layoutViews(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWFOLDER); } }, /** Shows the plot view. */ C_SHOWPLOT(PLOT, "% 4", true, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWPLOT); gui.layoutViews(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWPLOT); } }, /** Shows the table view. */ C_SHOWTABLE(TABLE, "% 5", true, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWTABLE); gui.layoutViews(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWTABLE); } }, /** Shows the explorer view. */ C_SHOWEXPLORE(EXPLORER, "% 6", true, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.SHOWEXPLORE); gui.layoutViews(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.SHOWEXPLORE); } }, /** Fullscreen mode. */ C_FULL(FULLSCREEN, Prop.MAC ? "% shift F" : "F11", false, true) { @Override public void execute(final GUI gui) { gui.fullscreen(); } @Override public boolean selected(final GUI gui) { return gui.fullscreen; } }, /* OPTION MENU */ /** Realtime execution on/off. */ C_RTEXEC(RT_EXECUCTION, null, false, true) { @Override public void execute(final GUI gui) { gui.gopts.invert(GUIOptions.EXECRT); gui.stop(); // refresh buttons in input bar gui.refreshControls(); // refresh editor buttons gui.editor.refreshMark(); } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.EXECRT); } }, /** Realtime filtering on/off. */ C_RTFILTER(RT_FILTERING, null, true, true) { @Override public void execute(final GUI gui) { final boolean rt = gui.gopts.invert(GUIOptions.FILTERRT); gui.stop(); // refresh buttons in input bar gui.refreshControls(); // refresh editor buttons gui.editor.refreshMark(); final Context ctx = gui.context; final boolean root = ctx.root(); final Data data = ctx.data(); if(rt) { if(root) { gui.notify.mark(new DBNodes(data), null); } else { final DBNodes mark = ctx.marked; ctx.marked = new DBNodes(data); gui.notify.context(mark, true, null); } } else { if(!root) { gui.notify.context(new DBNodes(data, 0), true, null); gui.notify.mark(ctx.current(), null); } } } @Override public boolean selected(final GUI gui) { return gui.gopts.get(GUIOptions.FILTERRT); } }, /** Color schema. */ C_COLOR(COLORS + DOTS, null, false, false) { @Override public void execute(final GUI gui) { DialogColors.show(gui); } }, /** Changes the fonts. */ C_FONTS(FONTS_D, null, false, false) { @Override public void execute(final GUI gui) { DialogFonts.show(gui); } }, /** Shows a preference dialog. */ C_PREFS(PREFERENCES + DOTS, Prop.MAC ? "% COMMA" : "% shift P", false, false) { @Override public void execute(final GUI gui) { DialogPrefs.show(gui); } }, /* HELP MENU */ /** Shows the documentation web page. */ C_HELP(HELP, "F1", false, false) { @Override public void execute(final GUI gui) { BaseXDialog.browse(gui, Prop.DOC_URL); } }, /** Opens the community web page. */ C_COMMUNITY(COMMUNITY, null, false, false) { @Override public void execute(final GUI gui) { BaseXDialog.browse(gui, Prop.COMMUNITY_URL); } }, /** Opens the update web page. */ C_UPDATES(CHECK_FOR_UPDATES, null, false, false) { @Override public void execute(final GUI gui) { BaseXDialog.browse(gui, Prop.UPDATE_URL); } }, /** Shows the "about" information. */ C_ABOUT(ABOUT + DOTS, null, false, false) { @Override public void execute(final GUI gui) { new DialogAbout(gui); } }, /* BROWSE COMMANDS */ /** Goes one step back. */ C_GOBACK(GO_BACK, "alt LEFT", true, false) { @Override public void execute(final GUI gui) { gui.notify.hist(false); } @Override public boolean enabled(final GUI gui) { return gui.notify.query(true) != null; } }, /** Goes one step forward. */ C_GOFORWARD(GO_FORWARD, "alt RIGHT", true, false) { @Override public void execute(final GUI gui) { gui.notify.hist(true); } @Override public boolean enabled(final GUI gui) { return gui.notify.query(false) != null; } }, /** Goes one level up. */ C_GOUP(GO_UP, "alt UP", true, false) { @Override public void execute(final GUI gui) { // skip operation for root context final Context ctx = gui.context; if(ctx.root()) return; // check if all nodes are document nodes boolean doc = true; final Data data = ctx.data(); for(final int pre : ctx.current().pres()) doc &= data.kind(pre) == Data.DOC; final DBNodes nodes; if(doc) { // if yes, jump to database root ctx.invalidate(); nodes = ctx.current(); } else { // otherwise, jump to parent nodes final IntList pres = new IntList(); for(final int pre : ctx.current().pres()) { final int k = data.kind(pre); pres.add(k == Data.DOC ? pre : data.parent(pre, k)); } nodes = new DBNodes(data, pres.sort().distinct().finish()); ctx.current(nodes); } gui.notify.context(nodes, false, null); } @Override public boolean enabled(final GUI gui) { return !gui.gopts.get(GUIOptions.FILTERRT) && gui.context.data() != null && !gui.context.root(); } }, /** Goes to the root node. */ C_GOHOME(GO_HOME, "alt HOME", true, false) { @Override public void execute(final GUI gui) { // skip operation for root context final Context ctx = gui.context; if(ctx.root()) return; // jump to database root ctx.invalidate(); gui.notify.context(ctx.current(), false, null); } @Override public boolean enabled(final GUI gui) { return gui.context.data() != null && !gui.context.root(); } }, /** Displays the root node in the text view. */ C_HOME(GO_HOME, null, true, false) { @Override public void execute(final GUI gui) { gui.execute(new XQuery("/")); } }; /** States if the command needs a data reference. */ private final boolean data; /** Menu label. */ private final String label; /** Key shortcut. */ private final Object key; /** Indicates if this command has two states. */ private final boolean toggle; /** Shortcut. */ private final String shortcut; /** * Constructor. * @param label label of the menu item * @param key shortcut * @param data requires a database to be opened * @param toggle indicates if this command has two states */ GUIMenuCmd(final String label, final String key, final boolean data, final boolean toggle) { this.label = label; this.key = key; this.data = data; this.toggle = toggle; shortcut = BaseXLayout.addShortcut(label, key); } @Override public boolean enabled(final GUI gui) { return !data || gui.context.data() != null; } @Override public boolean selected(final GUI gui) { return false; } @Override public final String label() { return label; } @Override public final boolean toggle() { return toggle; } @Override public String shortCut() { return shortcut; } @Override public Object shortcuts() { return key; } // STATIC METHODS =========================================================== /** * Checks if data can be updated. * @param n node instance * @param no disallowed node types * @return result of check */ private static boolean updatable(final DBNodes n, final int... no) { if(n == null || (no.length == 0 ? n.size() < 1 : n.size() != 1)) return false; final int k = n.data().kind(n.pre(0)); for(final int i : no) if(k == i) return false; return true; } /** * Returns a quoted string. * @param s string to encode * @return quoted string */ private static String quote(final String s) { return '"' + s.replaceAll("\"", """) + '"'; } /** * Returns a database function for the first node in a node set. * @param n node set * @param i offset * @return function string */ private static String openPre(final DBNodes n, final int i) { return Function._DB_OPEN_PRE.toString(Str.get(n.data().meta.name), Int.get(n.pre(i))); } }