studio.ui.StudioWindow Maven / Gradle / Ivy
package studio.ui;
import kx.K4Exception;
import kx.KMessage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit;
import studio.core.Studio;
import studio.kdb.*;
import studio.kdb.config.ActionOnExit;
import studio.ui.action.ConnectionStats;
import studio.ui.action.QPadImport;
import studio.ui.action.QueryResult;
import studio.ui.action.WorkspaceSaver;
import studio.ui.chart.Chart;
import studio.ui.dndtabbedpane.DragEvent;
import studio.ui.dndtabbedpane.DraggableTabbedPane;
import studio.ui.rstextarea.ConvertTabsToSpacesAction;
import studio.ui.rstextarea.FindReplaceAction;
import studio.ui.rstextarea.RSTextAreaFactory;
import studio.ui.search.SearchPanel;
import studio.ui.statusbar.MainStatusBar;
import studio.utils.BrowserLaunch;
import studio.utils.Content;
import studio.utils.HistoricalList;
import studio.utils.LineEnding;
import studio.utils.log4j.EnvConfig;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import javax.swing.table.TableModel;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.*;
import static javax.swing.JSplitPane.VERTICAL_SPLIT;
import static studio.ui.EscapeDialog.DialogResult.ACCEPTED;
import static studio.ui.EscapeDialog.DialogResult.CANCELLED;
public class StudioWindow extends JFrame implements WindowListener {
private static final Logger log = LogManager.getLogger();
private static final Action editorUndoAction;
private static final Action editorRedoAction;
private static final Action editorCutAction;
private static final Action editorCopyAction;
private static final Action editorPasteAction;
private static final Action editorSelectAllAction;
private static final Action editorFindAction;
private static final Action editorReplaceAction;
private static final Action editorConvertTabsToSpacesAction;
static {
// Action name will be used for text in menu items. Kit's actions have internal names.
// We will create new actions for menu/toolbar and use kit's actions as action itself.
editorCopyAction = RSTextAreaFactory.getAction(RSTextAreaFactory.rstaCopyAsStyledTextAction);
editorCutAction = RSTextAreaFactory.getAction(RSTextAreaFactory.rstaCutAsStyledTextAction);
editorPasteAction = RSTextAreaFactory.getAction(RSyntaxTextAreaEditorKit.pasteAction);
editorSelectAllAction = RSTextAreaFactory.getAction(RSyntaxTextAreaEditorKit.selectAllAction);
editorUndoAction = RSTextAreaFactory.getAction(RSyntaxTextAreaEditorKit.rtaUndoAction);
editorRedoAction = RSTextAreaFactory.getAction(RSyntaxTextAreaEditorKit.rtaRedoAction);
editorFindAction = RSTextAreaFactory.getAction(FindReplaceAction.findAction);
editorReplaceAction = RSTextAreaFactory.getAction(FindReplaceAction.replaceAction);
editorConvertTabsToSpacesAction = RSTextAreaFactory.getAction(ConvertTabsToSpacesAction.action);
}
private boolean loading = true;
private JComboBox comboServer;
private JTextField txtServer;
private String lastQuery = null;
private JToolBar toolbar;
private EditorsPanel rootEditorsPanel;
private EditorTab editor; // should be NotNull
private JSplitPane splitpane;
private JPanel topPanel;
private MainStatusBar mainStatusBar;
private DraggableTabbedPane tabbedPane;
private SearchPanel editorSearchPanel;
private SearchPanel resultSearchPanel;
private ServerList serverList;
private UserAction arrangeAllAction;
private UserAction closeFileAction;
private UserAction closeTabAction;
private UserAction cleanAction;
private UserAction openFileAction;
private UserAction openInExcel;
private UserAction codeKxComAction;
private UserAction serverListAction;
private UserAction serverHistoryAction;
private UserAction newWindowAction;
private UserAction newTabAction;
private UserAction saveFileAction;
private UserAction saveAllFilesAction;
private UserAction saveAsFileAction;
private UserAction exportAction;
private UserAction chartAction;
private Action undoAction;
private Action redoAction;
private Action cutAction;
private Action copyAction;
private Action pasteAction;
private Action selectAllAction;
private Action findAction;
private Action replaceAction;
private Action convertTabsToSpacesAction;
private UserAction stopAction;
private UserAction executeAction;
private UserAction executeCurrentLineAction;
private UserAction refreshAction;
private UserAction aboutAction;
private UserAction exitAction;
private UserAction settingsAction;
private UserAction toggleDividerOrientationAction;
private UserAction minMaxDividerAction;
private UserAction importFromQPadAction;
private UserAction connectionStatsAction;
private UserAction editServerAction;
private UserAction addServerAction;
private UserAction removeServerAction;
private UserAction toggleCommaFormatAction;
private UserAction findInResultAction;
private UserAction nextEditorTabAction;
private UserAction prevEditorTabAction;
private UserAction[] lineEndingActions;
private UserAction wordWrapAction;
private UserAction splitEditorRight;
private UserAction splitEditorDown;
private static int studioWindowNameIndex = 0;
private int editorTabbedPaneNameIndex = 0;
private int editorNameIndex = 0;
private int resultNameIndex = 0;
private static List allWindows = new ArrayList<>();
private static StudioWindow activeWindow = null;
private final List serverHistory;
public final static int menuShortcutKeyMask = java.awt.Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
private final static Cursor textCursor = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
private final static Cursor waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
private final static int MAX_SERVERS_TO_CLONE = 20;
private static final Config CONFIG = Config.getInstance();
public int nextEditorNameIndex() {
return editorNameIndex++;
}
public int nextResultNameIndex() {
return resultNameIndex++;
}
public int nextEditorTabbedPaneNameIndex() {
return editorTabbedPaneNameIndex++;
}
private String getCaption() {
StringBuilder caption = new StringBuilder();
caption.append(editor.getTitle());
if (editor.isModified()) caption.append(" (not saved)");
Server server = editor.getServer();
if (server != Server.NO_SERVER) {
caption.append(" @ ");
String fullName = server.getFullName();
if (fullName.length()>0) caption.append(fullName);
else caption.append(server.getHost()).append(":").append(server.getPort());
}
return caption.toString();
}
public void refreshFrameTitle() {
StringBuilder frameTitleBuilder = new StringBuilder(getCaption());
frameTitleBuilder.append(" ");
frameTitleBuilder.append("Studio for kdb+ ").append(Lm.version);
String env = EnvConfig.getEnvironment();
if (env != null) frameTitleBuilder.append(" [").append(env).append("]");
String frameTitle = frameTitleBuilder.toString();
if (frameTitle.equals(getTitle())) return;
setTitle(frameTitle);
refreshAllMenus();
}
private void setActionsEnabled(boolean value, Action... actions) {
for (Action action: actions) {
if (action != null) {
action.setEnabled(value);
}
}
}
public boolean isQueryRunning() {
return editor.getQueryExecutor().running();
}
public void refreshActionState() {
RSyntaxTextArea textArea = editor.getTextArea();
Server server = editor.getServer();
editServerAction.setEnabled(server != Server.NO_SERVER);
removeServerAction.setEnabled(server != Server.NO_SERVER);
undoAction.setEnabled(textArea.canUndo());
redoAction.setEnabled(textArea.canRedo());
wordWrapAction.setSelected(CONFIG.getBoolean(Config.RSTA_WORD_WRAP));
for (LineEnding lineEnding: LineEnding.values() ) {
lineEndingActions[lineEnding.ordinal()].setSelected(editor.getLineEnding() == lineEnding);
}
boolean queryRunning = isQueryRunning();
stopAction.setEnabled(queryRunning);
executeAction.setEnabled(!queryRunning);
executeCurrentLineAction.setEnabled(!queryRunning);
refreshAction.setEnabled(lastQuery != null && !queryRunning);
TabPanel tab = getSelectedResultPane();
if (tab == null) {
setActionsEnabled(false, exportAction, chartAction, openInExcel, refreshAction);
} else {
exportAction.setEnabled(tab.isTable());
chartAction.setEnabled(tab.getType() == TabPanel.ResultType.TABLE);
openInExcel.setEnabled(tab.isTable());
refreshAction.setEnabled(true);
tab.refreshActionState();
}
}
private void exportAsExcel(final String filename) {
new ExcelExporter().exportTableX(this,getSelectedTable(),new File(filename),false);
}
private void exportAsDelimited(final TableModel model,final String filename,final char delimiter) {
UIManager.put("ProgressMonitor.progressText","Studio for kdb+");
final ProgressMonitor pm = new ProgressMonitor(this,"Exporting data to " + filename,
"0% complete",0,100);
pm.setMillisToDecideToPopup(100);
pm.setMillisToPopup(100);
pm.setProgress(0);
Runnable runner = () -> {
if (filename != null) {
String lineSeparator = System.getProperty("line.separator");
try (BufferedWriter fw = new BufferedWriter(new FileWriter(filename))) {
for(int col = 0; col < model.getColumnCount(); col++) {
if (col > 0)
fw.write(delimiter);
fw.write(model.getColumnName(col));
}
fw.write(lineSeparator);
int maxRow = model.getRowCount();
for(int r = 1; r <= maxRow; r++) {
for (int col = 0;col < model.getColumnCount();col++) {
if (col > 0) fw.write(delimiter);
K.KBase o = (K.KBase) model.getValueAt(r - 1,col);
if (!o.isNull())
fw.write(o.toString(KFormatContext.NO_TYPE));
}
fw.write(lineSeparator);
if (pm.isCanceled()) break;
int progress = (100 * r) / maxRow;
String note = "" + progress + "% complete";
SwingUtilities.invokeLater( () -> {
pm.setProgress(progress);
pm.setNote(note);
} );
}
}
catch (IOException e) {
log.error("Error in writing to file {}", filename, e);
}
finally {
pm.close();
}
}
};
Thread t = new Thread(runner,"Exporter");
t.setPriority(Thread.MIN_PRIORITY);
t.start();
}
private void exportAsXml(final TableModel model,final String filename) {
UIManager.put("ProgressMonitor.progressText","Studio for kdb+");
final ProgressMonitor pm = new ProgressMonitor(this,"Exporting data to " + filename,
"0% complete",0,100);
pm.setMillisToDecideToPopup(100);
pm.setMillisToPopup(100);
pm.setProgress(0);
Runnable runner = () -> {
if (filename != null) {
String lineSeparator = System.getProperty("line.separator");;
try (BufferedWriter fw = new BufferedWriter(new FileWriter(filename))) {
fw.write("");
int maxRow = model.getRowCount();
fw.write(lineSeparator);
String[] columns = new String[model.getColumnCount()];
for (int col = 0; col < model.getColumnCount(); col++)
columns[col] = model.getColumnName(col);
for (int r = 1; r <= maxRow; r++) {
fw.write("");
for (int col = 0; col < columns.length; col++) {
fw.write("<" + columns[col] + ">");
K.KBase o = (K.KBase) model.getValueAt(r - 1,col);
if (!o.isNull())
fw.write(o.toString(KFormatContext.NO_TYPE));
fw.write("" + columns[col] + ">");
}
fw.write(" ");
fw.write(lineSeparator);
if (pm.isCanceled()) break;
int progress = (100 * r) / maxRow;
String note = "" + progress + "% complete";
SwingUtilities.invokeLater(() -> {
pm.setProgress(progress);
pm.setNote(note);
});
}
fw.write(" ");
}
catch (IOException e) {
log.error("Error in writing to file {}", filename, e);
}
finally {
pm.close();
}
}
};
Thread t = new Thread(runner, "Exporter");
t.setPriority(Thread.MIN_PRIORITY);
t.start();
}
private void exportAsTxt(String filename) {
exportAsDelimited(getSelectedTable().getModel(),filename,'\t');
}
private void exportAsCSV(String filename) {
exportAsDelimited(getSelectedTable().getModel(),filename,',');
}
private void export() {
if (getSelectedTable() == null) return;
File file = FileChooser.chooseFile(this, Config.EXPORT_FILE_CHOOSER, JFileChooser.SAVE_DIALOG, "Export result set as",
null,
new FileNameExtensionFilter("csv (Comma delimited)", "csv"),
new FileNameExtensionFilter("txt (Tab delimited)", "txt"),
new FileNameExtensionFilter("xml", "xml"),
new FileNameExtensionFilter("xls (Microsoft Excel)", "xls"));
if (file == null) return;
try {
String filename = file.getAbsolutePath();
if (filename.endsWith(".xls"))
exportAsExcel(filename);
else if (filename.endsWith(".csv"))
exportAsCSV(filename);
else if (filename.endsWith(".txt"))
exportAsTxt(filename);
else if (filename.endsWith(".xml"))
exportAsXml(getSelectedTable().getModel(),filename);
else
StudioOptionPane.showWarning(this,
"You did not specify what format to export the file as.\n Cancelling data export",
"Warning");
} catch (Exception e) {
StudioOptionPane.showError(this,
"Error",
"An error occurred whilst writing the export file.\n Details are: " + e.getMessage());
}
}
public void newFile() {
if (!EditorsPanel.checkAndSaveTab(editor)) return;
editor.loadFile(null);
}
private void openFile() {
File file = FileChooser.chooseFile(this, Config.OPEN_FILE_CHOOSER, JFileChooser.OPEN_DIALOG, null, null,
new FileNameExtensionFilter("q script", "q"));
if (file == null) return;
String filename = file.getAbsolutePath();
addTab(filename);
addToMruFiles(filename);
}
public void loadMRUFile(String filename) {
if (!EditorsPanel.checkAndSaveTab(editor)) return;
editor.loadFile(filename);
addToMruFiles(filename);
EditorsPanel.refreshEditorTitle(editor);
refreshAllMenus();
}
public void addToMruFiles(String filename) {
if (filename == null)
return;
Vector v = new Vector();
v.add(filename);
String[] mru = CONFIG.getMRUFiles();
for (int i = 0;i < mru.length;i++)
if (!v.contains(mru[i]))
v.add(mru[i]);
CONFIG.saveMRUFiles((String[]) v.toArray(new String[0]));
refreshAllMenus();
}
public static void executeAll(EditorsPanel.EditorTabAction action) {
for (StudioWindow studioWindow: allWindows) {
studioWindow.execute(action);
}
}
public boolean execute(EditorsPanel.EditorTabAction action) {
return rootEditorsPanel.execute(action);
}
private static void saveAll() {
executeAll(editorTab -> editorTab.saveFileOnDisk(false));
}
private void arrangeAll() {
int noWins = allWindows.size();
Iterator windowIterator = allWindows.iterator();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int noRows = Math.min(noWins, 3);
int height = screenSize.height / noRows;
for (int row = 0;row < noRows;row++) {
int noCols = (noWins / 3);
if ((row == 0) && ((noWins % 3) > 0))
noCols++;
else if ((row == 1) && ((noWins % 3) > 1))
noCols++;
int width = screenSize.width / noCols;
for (int col = 0;col < noCols;col++) {
StudioWindow window = windowIterator.next();
window.setSize(width,height);
window.setLocation(col * width,((noRows - 1) - row) * height);
ensureDeiconified(window);
}
}
}
public void setServer(Server server) {
editor.setServer(server);
if (!loading) {
CONFIG.addServerToHistory(server);
serverHistory.add(server);
EditorsPanel.refreshEditorTitle(editor);
refreshServer();
}
}
private void initActions() {
cleanAction = UserAction.create("Clean", Util.NEW_DOCUMENT_ICON, "Clean editor script", KeyEvent.VK_N,
null, e -> newFile());
arrangeAllAction = UserAction.create(I18n.getString("ArrangeAll"), "Arrange all windows on screen",
KeyEvent.VK_A, null, e -> arrangeAll());
minMaxDividerAction = UserAction.create(I18n.getString("MaximizeEditorPane"), "Maximize editor pane",
KeyEvent.VK_M, KeyStroke.getKeyStroke(KeyEvent.VK_M, menuShortcutKeyMask),
e -> minMaxDivider());
toggleDividerOrientationAction = UserAction.create(I18n.getString("ToggleDividerOrientation"),
"Toggle the window divider's orientation", KeyEvent.VK_C, null, e -> toggleDividerOrientation());
closeTabAction = UserAction.create("Close Tab", "Close current tab", KeyEvent.VK_W,
KeyStroke.getKeyStroke(KeyEvent.VK_W, menuShortcutKeyMask), e -> editor.getEditorsPanel().closeTab(editor));
closeFileAction = UserAction.create("Close Window", "Close current window (close all tabs)",
KeyEvent.VK_C, null, e -> close());
openFileAction = UserAction.create(I18n.getString("Open"), Util.FOLDER_ICON, "Open a script", KeyEvent.VK_O,
KeyStroke.getKeyStroke(KeyEvent.VK_O, menuShortcutKeyMask), e -> openFile());
newWindowAction = UserAction.create(I18n.getString("NewWindow"), "Open a new window",
KeyEvent.VK_N, KeyStroke.getKeyStroke(KeyEvent.VK_N, menuShortcutKeyMask | InputEvent.SHIFT_MASK),
e -> new StudioWindow(editor.getServer(), null) );
newTabAction = UserAction.create("New Tab", "Open a new tab", KeyEvent.VK_T,
KeyStroke.getKeyStroke(KeyEvent.VK_N, menuShortcutKeyMask),
e -> addTab(null));
serverListAction = UserAction.create(I18n.getString("ServerList"), Util.TEXT_TREE_ICON, "Show server list",
KeyEvent.VK_L, KeyStroke.getKeyStroke(KeyEvent.VK_L, menuShortcutKeyMask | InputEvent.SHIFT_MASK),
e -> showServerList(false));
serverHistoryAction = UserAction.create("Server History", "Recent selected servers", KeyEvent.VK_R,
KeyStroke.getKeyStroke(KeyEvent.VK_R, menuShortcutKeyMask | InputEvent.SHIFT_MASK),
e -> showServerList(true));
importFromQPadAction = UserAction.create("Import Servers from QPad...", "Import from Servers.cfg",
KeyEvent.VK_I, null, e -> QPadImport.doImport(this));
connectionStatsAction = UserAction.create("Get Connection Statistics", "Details about all connections for all opened tabs",
KeyEvent.VK_S, null, e -> ConnectionStats.getStats(this));
editServerAction = UserAction.create(I18n.getString("Edit"), Util.SERVER_INFORMATION_ICON, "Edit the server details",
KeyEvent.VK_E, null, e -> {
EditServerForm f = new EditServerForm(this, editor.getServer());
f.alignAndShow();
if (f.getResult() == ACCEPTED) {
if (stopAction.isEnabled())
stopAction.actionPerformed(e);
CONFIG.removeServer(editor.getServer());
Server newServer = f.getServer();
CONFIG.addServer(newServer);
setServer(newServer);
refreshAll();
}
});
addServerAction = UserAction.create(I18n.getString("Add"), Util.ADD_SERVER_ICON, "Configure a new server",
KeyEvent.VK_A, null, e -> {
AddServerForm f = new AddServerForm(this);
f.alignAndShow();
if (f.getResult() == ACCEPTED) {
Server s = f.getServer();
CONFIG.addServer(s);
setServer(s);
refreshAll();
}
});
removeServerAction = UserAction.create(I18n.getString("Remove"), Util.DELETE_SERVER_ICON, "Remove this server",
KeyEvent.VK_R, null, e -> {
int choice = JOptionPane.showOptionDialog(this,
"Remove server " + editor.getServer().getFullName() + " from list?",
"Remove server?",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
Util.QUESTION_ICON,
null, // use standard button titles
null); // no default selection
if (choice == 0) {
CONFIG.removeServer(editor.getServer());
Server[] servers = CONFIG.getServers();
if (servers.length > 0)
setServer(servers[0]);
refreshAll();
}
});
saveFileAction = UserAction.create(I18n.getString("Save"), Util.DISKS_ICON, "Save the script",
KeyEvent.VK_S, KeyStroke.getKeyStroke(KeyEvent.VK_S, menuShortcutKeyMask),
e -> EditorsPanel.saveEditor(editor));
saveAllFilesAction = UserAction.create("Save All...", "Save all files",
KeyEvent.VK_L, KeyStroke.getKeyStroke(KeyEvent.VK_S, menuShortcutKeyMask | InputEvent.SHIFT_MASK),
e -> saveAll());
saveAsFileAction = UserAction.create(I18n.getString("SaveAs"), Util.SAVE_AS_ICON, "Save script as",
KeyEvent.VK_A, null, e -> EditorsPanel.saveAsFile(editor));
exportAction = UserAction.create(I18n.getString("Export"), Util.EXPORT_ICON, "Export result set",
KeyEvent.VK_E, null, e -> export());
chartAction = UserAction.create(I18n.getString("Chart"), Util.CHART_ICON, "Chart current data set",
KeyEvent.VK_E, null, e -> new Chart((KTableModel) getSelectedTable().getModel()));
stopAction = UserAction.create(I18n.getString("Stop"), Util.STOP_ICON, "Stop the query",
KeyEvent.VK_S, null, e -> editor.getQueryExecutor().cancel());
openInExcel = UserAction.create(I18n.getString("OpenInExcel"), Util.EXCEL_ICON, "Open in Excel",
KeyEvent.VK_O, null, e -> {
try {
File file = File.createTempFile("studioExport", ".xlsx");
new ExcelExporter().exportTableX(this, getSelectedTable(), file, true);
} catch (IOException ex) {
log.error("Failed to create temporary file", ex);
StudioOptionPane.showError(this, "Failed to Open in Excel " + ex.getMessage(),"Error");
}
});
executeAction = UserAction.create(I18n.getString("Execute"), Util.TABLE_SQL_RUN_ICON, "Execute the full or highlighted text as a query",
KeyEvent.VK_E, KeyStroke.getKeyStroke(KeyEvent.VK_E, menuShortcutKeyMask), e -> executeQuery());
executeCurrentLineAction = UserAction.create(I18n.getString("ExecuteCurrentLine"), Util.RUN_ICON, "Execute the current line as a query",
KeyEvent.VK_ENTER, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, menuShortcutKeyMask), e -> executeQueryCurrentLine());
refreshAction = UserAction.create(I18n.getString("Refresh"), Util.REFRESH_ICON, "Refresh the result set",
KeyEvent.VK_R, KeyStroke.getKeyStroke(KeyEvent.VK_Y, menuShortcutKeyMask | InputEvent.SHIFT_MASK), e -> refreshQuery());
toggleCommaFormatAction = UserAction.create("Toggle Comma Format", Util. COMMA_ICON, "Add/remove thousands separator in selected result",
KeyEvent.VK_J, KeyStroke.getKeyStroke(KeyEvent.VK_J, menuShortcutKeyMask),
e -> {
TabPanel tab = getSelectedResultPane();
if (tab != null) tab.toggleCommaFormatting();
});
findInResultAction = UserAction.create("Find in Result", Util.FIND_ICON, "Find in result tab",
KeyEvent.VK_F, KeyStroke.getKeyStroke(KeyEvent.VK_F, menuShortcutKeyMask | InputEvent.SHIFT_MASK),
e -> resultSearchPanel.setVisible(true) );
aboutAction = UserAction.create(I18n.getString("About"), Util.ABOUT_ICON, "About Studio for kdb+",
KeyEvent.VK_E, null, e -> about());
exitAction = UserAction.create(I18n.getString("Exit"), "Close this window",
KeyEvent.VK_X, e -> quit());
settingsAction = UserAction.create("Settings", "Settings",
KeyEvent.VK_S, null, e -> settings());
codeKxComAction = UserAction.create("code.kx.com", Util.TEXT_ICON, "Open code.kx.com",
KeyEvent.VK_C, null, e -> {
try {
BrowserLaunch.openURL("http://code.kx.com/q/");
} catch (Exception ex) {
StudioOptionPane.showError("Error attempting to launch web browser:\n" + ex.getLocalizedMessage(), "Error");
}
});
copyAction = UserAction.create(I18n.getString("Copy"), Util.COPY_ICON, "Copy the selected text to the clipboard",
KeyEvent.VK_C, KeyStroke.getKeyStroke(KeyEvent.VK_C,menuShortcutKeyMask), editorCopyAction);
cutAction = UserAction.create(I18n.getString("Cut"), Util.CUT_ICON, "Cut the selected text",
KeyEvent.VK_T, KeyStroke.getKeyStroke(KeyEvent.VK_X,menuShortcutKeyMask), editorCutAction);
pasteAction = UserAction.create(I18n.getString("Paste"), Util.PASTE_ICON, "Paste text from the clipboard",
KeyEvent.VK_P, KeyStroke.getKeyStroke(KeyEvent.VK_V,menuShortcutKeyMask), editorPasteAction);
findAction = UserAction.create(I18n.getString("Find"), Util.FIND_ICON, "Find text in the document",
KeyEvent.VK_F, KeyStroke.getKeyStroke(KeyEvent.VK_F,menuShortcutKeyMask), editorFindAction);
replaceAction = UserAction.create(I18n.getString("Replace"), Util.REPLACE_ICON, "Replace text in the document",
KeyEvent.VK_R, KeyStroke.getKeyStroke(KeyEvent.VK_R,menuShortcutKeyMask), editorReplaceAction);
convertTabsToSpacesAction = UserAction.create("Convert tabs to spaces", editorConvertTabsToSpacesAction);
selectAllAction = UserAction.create(I18n.getString("SelectAll"), "Select all text in the document",
KeyEvent.VK_A, KeyStroke.getKeyStroke(KeyEvent.VK_A,menuShortcutKeyMask), editorSelectAllAction);
undoAction = UserAction.create(I18n.getString("Undo"), Util.UNDO_ICON, "Undo the last change to the document",
KeyEvent.VK_U, KeyStroke.getKeyStroke(KeyEvent.VK_Z,menuShortcutKeyMask), editorUndoAction);
redoAction = UserAction.create(I18n.getString("Redo"), Util.REDO_ICON, "Redo the last change to the document",
KeyEvent.VK_R, KeyStroke.getKeyStroke(KeyEvent.VK_Y,menuShortcutKeyMask), editorRedoAction);
nextEditorTabAction = UserAction.create("Next tab",
"Select next editor tab", KeyEvent.VK_N,
Util.MAC_OS_X ? KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, menuShortcutKeyMask | InputEvent.ALT_MASK ) :
KeyStroke.getKeyStroke(KeyEvent.VK_TAB, menuShortcutKeyMask),
e -> editor.getEditorsPanel().selectNextTab(true));
prevEditorTabAction = UserAction.create("Previous tab",
"Select previous editor tab", KeyEvent.VK_P,
Util.MAC_OS_X ? KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, menuShortcutKeyMask | InputEvent.ALT_MASK ) :
KeyStroke.getKeyStroke(KeyEvent.VK_TAB, menuShortcutKeyMask | InputEvent.SHIFT_MASK),
e -> editor.getEditorsPanel().selectNextTab(false));
lineEndingActions = new UserAction[LineEnding.values().length];
for(LineEnding lineEnding: LineEnding.values()) {
UserAction action = UserAction.create(lineEnding.getDescription(),
e -> {
editor.setLineEnding(lineEnding);
refreshActionState();
} );
lineEndingActions[lineEnding.ordinal()] = action;
}
wordWrapAction = UserAction.create("Word wrap", "Word wrap for all tabs",
KeyEvent.VK_W, KeyStroke.getKeyStroke(KeyEvent.VK_W, menuShortcutKeyMask | InputEvent.SHIFT_MASK),
e -> toggleWordWrap());
splitEditorRight = UserAction.create("Split right", "Split vertically",
KeyEvent.VK_R, null,
e-> editor.getEditorsPanel().split(false));
splitEditorDown = UserAction.create("Split down", "Split horizontally",
KeyEvent.VK_D, null,
e-> editor.getEditorsPanel().split(true));
}
public UserAction getSplitAction(boolean vertically) {
return vertically ? splitEditorDown : splitEditorRight;
}
public static void settings() {
SettingsDialog dialog = new SettingsDialog(activeWindow);
dialog.alignAndShow();
if (dialog.getResult() == CANCELLED) return;
dialog.saveSettings();
}
private void toggleWordWrap() {
boolean value = CONFIG.getBoolean(Config.RSTA_WORD_WRAP);
CONFIG.setBoolean(Config.RSTA_WORD_WRAP, !value);
refreshEditorsSettings();
refreshActionState();
}
public static void refreshServerSettingsForAllWindows() {
for (StudioWindow window: allWindows) {
window.refreshServer();
}
}
public static void refreshEditorsSettings() {
executeAll(editorTab -> {
RSyntaxTextArea editor = editorTab.getTextArea();
editor.setHighlightCurrentLine(CONFIG.getBoolean(Config.RSTA_HIGHLIGHT_CURRENT_LINE));
editor.setAnimateBracketMatching(CONFIG.getBoolean(Config.RSTA_ANIMATE_BRACKET_MATCHING));
editor.setLineWrap(CONFIG.getBoolean(Config.RSTA_WORD_WRAP));
editor.setFont(CONFIG.getFont(Config.FONT_EDITOR));
editor.setTabSize(CONFIG.getInt(Config.EDITOR_TAB_SIZE));
editor.setTabsEmulated(CONFIG.getBoolean(Config.EDITOR_TAB_EMULATED));
return true;
});
}
public static void refreshResultSettings() {
long doubleClickTimeout = CONFIG.getInt(Config.EMULATED_DOUBLE_CLICK_TIMEOUT);
for (StudioWindow window: allWindows) {
int count = window.tabbedPane.getTabCount();
for (int index=0; index {
if (editorTab.isModified()) {
if (!EditorsPanel.checkAndSaveTab(editorTab)) {
return false;
}
if (editorTab.isModified()) {
if (action == ActionOnExit.CLOSE_ANONYMOUS_NOT_SAVED && editorTab.getFilename()==null) {
editorTab.getEditorsPanel().closeTab(editorTab);
}
}
}
return true;
});
if (!complete) return;
}
}
} finally {
if (allWindows.size() > 0) {
activeWindow.toFront();
}
WorkspaceSaver.setEnabled(true);
}
WorkspaceSaver.save(getWorkspace());
CONFIG.exit();
}
public void close() {
// If this is the last window, we need to properly persist workspace
if (allWindows.size() == 1) {
quit();
} else {
boolean result = execute(editorTab -> editorTab.getEditorsPanel().closeTab(editorTab));
if (!result) return;
// closing the last tab would trigger this code again
if (allWindows.contains(this)) {
allWindows.remove(this);
dispose();
refreshAllMenus();
}
}
}
public static void refreshAll() {
for (StudioWindow window: allWindows) {
window.refreshMenu();
window.refreshServerList();
}
}
private void addToMenu(JMenu menu, Action... actions) {
for (Action action: actions) {
if (action == null) {
menu.addSeparator();
} else {
if (action.getValue(Action.SMALL_ICON) == null) {
action.putValue(Action.SMALL_ICON, Util.BLANK_ICON);
}
menu.add(action);
}
}
}
private void setNamesInMenu(JMenu menu) {
menu.setName(menu.getText());
for (int index = 0; index < menu.getMenuComponentCount(); index++) {
Component c = menu.getMenuComponent(index);
if (c instanceof JMenuItem) {
JMenuItem menuItem = (JMenuItem) c;
menuItem.setName(menuItem.getText());
}
if (c instanceof JMenu) {
setNamesInMenu((JMenu) c);
}
}
}
private void setNamesInMenu(JMenuBar menuBar) {
for (int index = 0; index < menuBar.getMenuCount(); index++) {
setNamesInMenu(menuBar.getMenu(index));
}
}
private static void refreshAllMenus() {
for(StudioWindow window: allWindows) {
window.refreshMenu();
}
}
private JMenu openMRUMenu, cloneMenu, windowMenu;
private int windowMenuWindowIndex;
private void refreshMenu() {
openMRUMenu.removeAll();
String[] mru = CONFIG.getMRUFiles();
String mnems = "123456789";
for (int i = 0; i < mru.length; i++) {
final String filename = mru[i];
JMenuItem item = new JMenuItem("" + (i + 1) + " " + filename);
if (i loadMRUFile(filename));
openMRUMenu.add(item);
}
cloneMenu.removeAll();
Server[] servers = CONFIG.getServers();
int count = Math.min(MAX_SERVERS_TO_CLONE, servers.length);
for (int i = 0; i < count; i++) {
final Server s = servers[i];
JMenuItem item = new JMenuItem(s.getFullName());
item.addActionListener(e -> {
Server clone = s.newName("Clone of " + s.getName()) ;
EditServerForm f = new EditServerForm(StudioWindow.this,clone);
f.alignAndShow();
if (f.getResult() == ACCEPTED) {
clone = f.getServer();
CONFIG.addServer(clone);
setServer(clone);
refreshAll();
}
});
cloneMenu.add(item);
}
for (int index=windowMenu.getMenuComponentCount()-1; index>=windowMenuWindowIndex; index--) {
windowMenu.remove(index);
}
count = allWindows.size();
UserAction[] windowMenuActions = new UserAction[count];
if (count > 0) {
windowMenu.addSeparator();
for (int index = 0; index < count; index++) {
StudioWindow window = allWindows.get(index);
windowMenuActions[index] = UserAction.create("" + (index + 1) + " " + window.getCaption(),
window == this ? Util.CHECK_ICON : Util.BLANK_ICON, "", 0 , null,
e -> ensureDeiconified(window));
}
addToMenu(windowMenu, windowMenuActions);
}
}
private void createMenuBar() {
openMRUMenu = new JMenu("Open Recent");
openMRUMenu.setIcon(Util.BLANK_ICON);
cloneMenu = new JMenu(I18n.getString("Clone"));
cloneMenu.setIcon(Util.DATA_COPY_ICON);
windowMenu = new JMenu(I18n.getString("Window"));
windowMenu.setMnemonic(KeyEvent.VK_W);
JMenuBar menubar = new JMenuBar();
JMenu menu = new JMenu(I18n.getString("File"));
menu.setMnemonic(KeyEvent.VK_F);
addToMenu(menu, newWindowAction, newTabAction, openFileAction);
menu.add(openMRUMenu);
addToMenu(menu,saveFileAction, saveAsFileAction, saveAllFilesAction, closeTabAction, closeFileAction, null);
if (!Studio.hasMacOSSystemMenu()) {
addToMenu(menu, settingsAction);
}
addToMenu(menu, openInExcel, exportAction, chartAction);
if (!Studio.hasMacOSSystemMenu()) {
addToMenu(menu, null, exitAction);
}
menubar.add(menu);
menu = new JMenu(I18n.getString("Edit"));
menu.setMnemonic(KeyEvent.VK_E);
addToMenu(menu, undoAction, redoAction, null, cutAction, copyAction, pasteAction, null);
menu.add(new JCheckBoxMenuItem(wordWrapAction));
JMenu lineEndingSubMenu = new JMenu("Line Ending");
lineEndingSubMenu.setIcon(Util.BLANK_ICON);
for (Action action: lineEndingActions) {
lineEndingSubMenu.add(new JCheckBoxMenuItem(action));
}
menu.add(lineEndingSubMenu);
addToMenu(menu, cleanAction, selectAllAction, null, findAction, replaceAction, convertTabsToSpacesAction);
menubar.add(menu);
menu = new JMenu(I18n.getString("Server"));
menu.setMnemonic(KeyEvent.VK_S);
addToMenu(menu, addServerAction, editServerAction, removeServerAction);
menu.add(cloneMenu);
addToMenu(menu, null, serverListAction, serverHistoryAction, importFromQPadAction, null, connectionStatsAction);
menubar.add(menu);
menu = new JMenu(I18n.getString("Query"));
menu.setMnemonic(KeyEvent.VK_Q);
addToMenu(menu, executeCurrentLineAction, executeAction, stopAction, refreshAction,
toggleCommaFormatAction, findInResultAction);
menubar.add(menu);
//Window menu
addToMenu(windowMenu, splitEditorRight, splitEditorDown, null, minMaxDividerAction, toggleDividerOrientationAction,
arrangeAllAction, nextEditorTabAction, prevEditorTabAction);
windowMenuWindowIndex = windowMenu.getMenuComponentCount();
menubar.add(windowMenu);
menu = new JMenu(I18n.getString("Help"));
menu.setMnemonic(KeyEvent.VK_H);
menu.add(new JMenuItem(codeKxComAction));
if (!Studio.hasMacOSSystemMenu())
menu.add(new JMenuItem(aboutAction));
menubar.add(menu);
setNamesInMenu(menubar);
setJMenuBar(menubar);
}
private void ensureDeiconified(JFrame f) {
int state = f.getExtendedState();
state = state & ~Frame.ICONIFIED;
f.setExtendedState(state);
f.show();
}
private void selectConnectionString() {
String connection = txtServer.getText().trim();
if (connection.length() == 0) return;
Server server = editor.getServer();
if (server != Server.NO_SERVER && server.getConnectionString().equals(connection)) return;
try {
setServer(CONFIG.getServerByConnectionString(connection));
refreshServer();
} catch (IllegalArgumentException e) {
refreshConnectionText();
}
}
private void showServerList(boolean selectHistory) {
if (serverList == null) {
serverList = new ServerList(this);
}
Rectangle bounds = Config.getInstance().getBounds(Config.SERVER_LIST_BOUNDS);
serverList.setBounds(bounds);
serverList.updateServerTree(CONFIG.getServerTree(), editor.getServer());
serverList.updateServerHistory(serverHistory);
serverList.selectHistoryTab(selectHistory);
serverList.setVisible(true);
bounds = serverList.getBounds();
CONFIG.setBounds(Config.SERVER_LIST_BOUNDS, bounds);
Server selectedServer = serverList.getSelectedServer();
if (selectedServer == null || selectedServer.equals(editor.getServer())) return;
setServer(selectedServer);
refreshAllMenus();
refreshServerListAllWindows();
}
private void selectServerName() {
String selection = comboServer.getSelectedItem().toString();
if(! CONFIG.getServerNames().contains(selection)) return;
setServer(CONFIG.getServer(selection));
refreshServer();
}
private void refreshConnectionText() {
Server server = editor.getServer();
if (server == Server.NO_SERVER) {
txtServer.setText("");
txtServer.setToolTipText("Select connection details");
} else {
txtServer.setText(server.getConnectionString());
txtServer.setToolTipText(server.getConnectionStringWithPwd());
}
}
public static void refreshComboServerVisibility() {
for (StudioWindow window: allWindows) {
window.comboServer.setVisible(CONFIG.getBoolean(Config.SHOW_SERVER_COMBOBOX));
}
}
public static void refreshServerListAllWindows() {
for (StudioWindow window: allWindows) {
window.refreshServerList();
}
}
private void refreshServerList() {
Server server = editor.getServer();
Collection names = CONFIG.getServerNames();
String name = server == Server.NO_SERVER ? "" : server.getFullName();
if (!names.contains(name)) {
List newNames = new ArrayList<>();
newNames.add(name);
newNames.addAll(names);
names = newNames;
}
comboServer.setModel(new DefaultComboBoxModel<>(names.toArray(new String[0])));
comboServer.setSelectedItem(name);
}
private void refreshServer() {
Server server = editor.getServer();
String name = server == Server.NO_SERVER ? "" : server.getFullName();
comboServer.setSelectedItem(name);
refreshConnectionText();
refreshActionState();
}
private void initToolbar() {
comboServer = new JComboBox<>();
comboServer.setVisible(CONFIG.getBoolean(Config.SHOW_SERVER_COMBOBOX));
comboServer.setName("serverDropDown");
comboServer.setToolTipText("Select the server context");
comboServer.addActionListener(e->selectServerName());
// Cut the width if it is too wide.
comboServer.setMinimumSize(new Dimension(0, 0));
txtServer = new JTextField(32);
txtServer.setName("serverEntryTextField");
txtServer.addActionListener(e -> {
selectConnectionString();
editor.getTextArea().requestFocus();
});
txtServer.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
selectConnectionString();
}
});
refreshServerList();
refreshServer();
toolbar.add(new JLabel(I18n.getString("Server")));
toolbar.add(comboServer);
toolbar.add(txtServer);
Action[] actions = new Action[] {
serverListAction, null,
stopAction, executeAction, refreshAction, null, openFileAction, saveFileAction, saveAsFileAction, null,
openInExcel, null, exportAction, null, chartAction, null, undoAction, redoAction, null,
cutAction, copyAction, pasteAction, null, findAction, replaceAction, null, codeKxComAction };
for (Action action: actions) {
if (action == null) {
toolbar.addSeparator();
} else {
JButton button = toolbar.add(action);
button.setFocusable(false);
button.setMnemonic(KeyEvent.VK_UNDEFINED);
String name = (String) action.getValue(Action.NAME);
button.setName("toolbar" + name);
}
}
refreshActionState();
}
private int dividerLastPosition; // updated from property change listener
private void minMaxDivider(){
//BasicSplitPaneDivider divider = ((BasicSplitPaneUI)splitpane.getUI()).getDivider();
//((JButton)divider.getComponent(0)).doClick();
//((JButton)divider.getComponent(1)).doClick();
if(splitpane.getDividerLocation()>=splitpane.getMaximumDividerLocation()){
// Minimize editor pane
splitpane.getTopComponent().setMinimumSize(new Dimension());
splitpane.getBottomComponent().setMinimumSize(null);
splitpane.setDividerLocation(0.);
splitpane.setResizeWeight(0.);
}
else if(splitpane.getDividerLocation()<=splitpane.getMinimumDividerLocation()){
// Restore editor pane
splitpane.getTopComponent().setMinimumSize(null);
splitpane.getBottomComponent().setMinimumSize(null);
splitpane.setResizeWeight(0.);
// Could probably catch resize edge-cases etc in pce too
if(dividerLastPosition>=splitpane.getMaximumDividerLocation()||dividerLastPosition<=splitpane.getMinimumDividerLocation())
dividerLastPosition=splitpane.getMaximumDividerLocation()/2;
splitpane.setDividerLocation(dividerLastPosition);
}
else{
// Maximize editor pane
splitpane.getBottomComponent().setMinimumSize(new Dimension());
splitpane.getTopComponent().setMinimumSize(null);
splitpane.setDividerLocation(splitpane.getOrientation()==VERTICAL_SPLIT?splitpane.getHeight()-splitpane.getDividerSize():splitpane.getWidth()-splitpane.getDividerSize());
splitpane.setResizeWeight(1.);
}
}
private void toggleDividerOrientation() {
if (splitpane.getOrientation() == JSplitPane.VERTICAL_SPLIT) {
splitpane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
tabbedPane.setTabPlacement(JTabbedPane.LEFT);
} else {
splitpane.setOrientation(JSplitPane.VERTICAL_SPLIT);
tabbedPane.setTabPlacement(JTabbedPane.TOP);
}
int count = tabbedPane.getTabCount();
for (int index = 0; index(CONFIG.getServerHistoryDepth(),
CONFIG.getServerHistory());
initActions();
createMenuBar();
toolbar = createToolbar();
editorSearchPanel = new SearchPanel( () -> editor.getPane() );
mainStatusBar = new MainStatusBar();
tabbedPane = initResultPane();
resultSearchPanel = initResultSearchPanel();
// We need to have some editor initialize to prevent NPE
editor = new EditorTab(this);
initToolbar();
topPanel = new JPanel(new BorderLayout());
rootEditorsPanel = new EditorsPanel(this, workspaceWindow);
List editors = rootEditorsPanel.getAllEditors(true);
boolean first = true;
for (EditorTab editor: editors) {
if (first) {
updateEditor(editor);
first = false;
} else {
editor.getEditorsPanel().setInFocusTabbedEditors(false);
}
}
topPanel.add(editorSearchPanel, BorderLayout.NORTH);
topPanel.setMinimumSize(new Dimension(0,0));
JPanel bottomPanel = new JPanel(new BorderLayout());
bottomPanel.add(tabbedPane, BorderLayout.CENTER);
bottomPanel.add(resultSearchPanel, BorderLayout.NORTH);
bottomPanel.setMinimumSize(new Dimension(0,0));
splitpane = initSplitPane(topPanel, bottomPanel);
topPanel.add(rootEditorsPanel, BorderLayout.CENTER);
initFrame(workspaceWindow.getLocation(), toolbar, splitpane, mainStatusBar);
splitpane.setDividerLocation(workspaceWindow.getResultDividerLocation());
rootEditorsPanel.loadDividerLocation(workspaceWindow);
loading = false;
EditorsPanel.refreshEditorTitle(editor);
refreshServer();
refreshAllMenus();
}
private Toolbar createToolbar() {
Toolbar toolbar = new Toolbar();
toolbar.setLayout(new BoxLayout(toolbar, BoxLayout.X_AXIS));
toolbar.setFloatable(false);
toolbar.setBorder(BorderFactory.createEmptyBorder(0,2,0,1));
return toolbar;
}
private DraggableTabbedPane initResultPane() {
DraggableTabbedPane tabbedPane = new DraggableTabbedPane("Result", JTabbedPane.TOP);
tabbedPane.setName("ResultTabbedPane");
ClosableTabbedPane.makeCloseable(tabbedPane, new ClosableTabbedPane.PinTabAction() {
@Override
public boolean close(int index, boolean force) {
if (force || !isPinned(index)) {
tabbedPane.removeTabAt(index);
}
return true;
}
@Override
public boolean isPinned(int index) {
TabPanel panel = (TabPanel)tabbedPane.getComponentAt(index);
return panel.isPinned();
}
@Override
public void setPinned(int index, boolean pinned) {
TabPanel panel = (TabPanel)tabbedPane.getComponentAt(index);
panel.setPinned(pinned);
}
});
tabbedPane.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
refreshResultTab();
}
});
tabbedPane.putClientProperty(StudioWindow.class, this);
tabbedPane.addDragListener( evt -> resultTabDragged(evt));
tabbedPane.addChangeListener(e -> {
TabPanel tabPanel = getSelectedResultPane();
if (tabPanel != null) tabPanel.refreshActionState();
});
return tabbedPane;
}
private SearchPanel initResultSearchPanel() {
SearchPanel resultSearchPanel = new SearchPanel(() -> {
if (tabbedPane.getTabCount() == 0) return null;
TabPanel resultTab = getSelectedResultPane();
EditorPane editorPane = resultTab.getEditor();
if (editorPane != null) return editorPane;
return resultTab.getGrid(); // the Grid or null
});
resultSearchPanel.setReplaceVisible(false);
resultSearchPanel.setName("ResultSearchPanel");
return resultSearchPanel;
}
private JSplitPane initSplitPane(JComponent top, JComponent bottom) {
JSplitPane splitpane = new JSplitPane();
splitpane.setTopComponent(top);
splitpane.setBottomComponent(bottom);
splitpane.setOneTouchExpandable(true);
splitpane.setOrientation(JSplitPane.VERTICAL_SPLIT);
if (splitpane.getUI() instanceof BasicSplitPaneUI) {
Component divider = ((BasicSplitPaneUI) splitpane.getUI()).getDivider();
divider.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent event) {
if (event.getClickCount() == 2)
toggleDividerOrientation();
}
});
}
splitpane.setContinuousLayout(true);
splitpane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY,new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent pce){
String s = splitpane.getDividerLocation()>=splitpane.getMaximumDividerLocation() ?
I18n.getString("MinimizeEditorPane")
: splitpane.getDividerLocation()<=splitpane.getMinimumDividerLocation() ?
I18n.getString("RestoreEditorPane"):
I18n.getString("MaximizeEditorPane");
minMaxDividerAction.putValue(Action.SHORT_DESCRIPTION,s);
minMaxDividerAction.putValue(Action.NAME,s);
if(splitpane.getDividerLocation()splitpane.getMinimumDividerLocation())
dividerLastPosition=splitpane.getDividerLocation();
}
});
dividerLastPosition = splitpane.getDividerLocation();
return splitpane;
}
private void initFrame(Rectangle location, JComponent toolbar, JComponent central, JComponent statusBar) {
addWindowFocusListener(new WindowFocusListener() {
@Override
public void windowGainedFocus(WindowEvent e) {
if (StudioWindow.activeWindow == StudioWindow.this) return;
log.info("Window focus is changed from {} to {} ", StudioWindow.activeWindow.getCaption(), StudioWindow.this.getCaption());
if (StudioWindow.allWindows.contains(StudioWindow.activeWindow)) {
StudioWindow.activeWindow.editor.getEditorsPanel().setInFocusTabbedEditors(false);
}
editor.getEditorsPanel().setInFocusTabbedEditors(true);
StudioWindow.activeWindow = StudioWindow.this;
}
@Override
public void windowLostFocus(WindowEvent e) {
}
});
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.add(toolbar, BorderLayout.NORTH);
contentPane.add(central, BorderLayout.CENTER);
contentPane.add(statusBar, BorderLayout.SOUTH);
setContentPane(contentPane);
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addWindowListener(this);
if (Util.fitToScreen(location)) {
setBounds(location);
} else {
//@TODO: should be defaulted in the Workspace.TopWindow
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setSize((int) (0.8 * screenSize.width), (int) (0.8 * screenSize.height));
setLocation(((int) Math.max(0, (screenSize.width - getWidth()) / 2.0)),
(int) (Math.max(0, (screenSize.height - getHeight()) / 2.0)));
}
setIconImage(Util.LOGO_ICON.getImage());
setVisible(true);
}
public SearchPanel getEditorSearchPanel() {
return editorSearchPanel;
}
public SearchPanel getResultSearchPanel() {
return resultSearchPanel;
}
public static void loadWorkspace(Workspace workspace) {
for (Workspace.TopWindow window: workspace.getWindows()) {
new StudioWindow(window);
}
int index = workspace.getSelectedWindow();
if (index >= 0 && index < allWindows.size()) {
allWindows.get(index).toFront();
}
if (allWindows.size() == 0) {
new StudioWindow(Server.NO_SERVER, null);
}
for (StudioWindow window: allWindows) {
window.refreshFrameTitle();
}
}
public void refreshQuery() {
executeK4Query(lastQuery);
}
public void executeQueryCurrentLine() {
executeQuery(getCurrentLineEditorText(editor.getTextArea()));
}
public void executeQuery() {
executeQuery(getEditorText(editor.getTextArea()));
}
private void executeQuery(String text) {
if (text == null) {
return;
}
text = text.trim();
if (text.length() == 0) {
log.info("Nothing to execute - got empty string");
return;
}
executeK4Query(text);
lastQuery = text;
}
public MainStatusBar getMainStatusBar() {
return mainStatusBar;
}
public Server getServer() {
return editor.getServer();
}
private String getEditorText(JTextComponent editor) {
String text = editor.getSelectedText();
if (text != null) return text;
Config.ExecAllOption option = CONFIG.getExecAllOption();
if (option == Config.ExecAllOption.Ignore) {
log.info("Nothing is selected. Ignore execution of the whole script");
return null;
}
if (option == Config.ExecAllOption.Execute) {
return editor.getText();
}
//Ask
int result = StudioOptionPane.showYesNoDialog(this, "Nothing is selected. Execute the whole script?",
"Execute All?");
if (result == JOptionPane.YES_OPTION ) {
return editor.getText();
}
return null;
}
private String getCurrentLineEditorText(JTextComponent editor) {
String newLine = "\n";
String text = null;
try {
int pos = editor.getCaretPosition();
int max = editor.getDocument().getLength();
if ((max > pos) && (!editor.getText(pos,1).equals("\n"))) {
String toeol = editor.getText(pos,max - pos);
int eol = toeol.indexOf('\n');
if (eol > 0)
pos = pos + eol;
else
pos = max;
}
text = editor.getText(0,pos);
int lrPos = text.lastIndexOf(newLine);
if (lrPos >= 0) {
lrPos += newLine.length(); // found it so skip it
text = text.substring(lrPos,pos).trim();
}
}
catch (BadLocationException e) {
}
if (text != null) {
text = text.trim();
if (text.length() == 0)
text = null;
}
return text;
}
private JTable getSelectedTable() {
TabPanel tab = (TabPanel) tabbedPane.getSelectedComponent();
if (tab == null) return null;
QGrid grid = tab.getGrid();
if (grid == null) return null;
return grid.getTable();
}
private void executeK4Query(String text) {
executeK4Query(new K.KCharacterVector(text), text);
}
void executeK4Query(K.KBase query, String queryText) {
if (editor.getServer() == Server.NO_SERVER) {
log.info("Server is not set. Can't execute the query");
return;
}
editor.getTextArea().setCursor(waitCursor);
editor.getPane().setEditorStatus("Executing: " + queryText);
editor.getQueryExecutor().execute(query, queryText);
editor.getPane().startClock();
refreshActionState();
}
private TabPanel getResultPane(int index) {
return (TabPanel)tabbedPane.getComponentAt(index);
}
private TabPanel getSelectedResultPane() {
return (TabPanel) tabbedPane.getSelectedComponent();
}
public void addResultTab(QueryResult queryResult, String tooltip) {
TabPanel tab = new TabPanel(this, queryResult);
tab.addInto(tabbedPane, getTooltipText(tooltip, queryResult.getKMessage()));
}
private static String getTooltipText(String tooltip, KMessage message) {
StringBuilder res = new StringBuilder();
res.append("").append(tooltip);
if (message != null) {
res.append("
Bytes sent: ").append(message.getBytesSent());
res.append("
Bytes received: ").append(message.getBytesReceived());
K.KTimestamp started = message.getStarted();
K.KTimestamp finished = message.getFinished();
if (! started.isNull()) {
res.append("
Query sent at ").append(started);
}
if (! finished.isNull()) {
res.append("
Result received at ").append(finished);
}
if (!started.isNull() && !finished.isNull()) {
res.append("
Duration: ").append((finished.toLong() - started.toLong()) / 1_000_000).append("ms");
}
}
res.append("");
return res.toString();
}
// if the query is cancelled execTime=-1, result and error are null's
public static void queryExecutionComplete(EditorTab editor, QueryResult queryResult) {
editor.getPane().stopClock();
JTextComponent textArea = editor.getTextArea();
textArea.setCursor(textCursor);
Throwable error = queryResult.getError();
if (queryResult.isComplete()) {
long execTime = queryResult.getExecutionTimeInMS();
editor.getPane().setEditorStatus("Last execution time: " + (execTime > 0 ? "" + execTime : "<1") + " ms");
} else {
editor.getPane().setEditorStatus("Last query was cancelled");
}
StudioWindow window = editor.getStudioWindow();
if (error == null || error instanceof K4Exception) {
try {
if (queryResult.isComplete()) {
window.addResultTab(queryResult, "Executed at server: " + queryResult.getServer().getDescription(true) );
}
error = null;
} catch (Throwable exc) {
error = new RuntimeException("Error during result rendering", exc);
log.error("Error during result rendering", exc);
}
}
if (error != null) {
String message = error.getMessage();
if ((message == null) || (message.length() == 0))
message = "No message with exception. Exception is " + error;
StudioOptionPane.showError(editor.getPane(),
"\nAn unexpected error occurred whilst communicating with " +
editor.getServer().getConnectionString() +
"\n\nError detail is\n\n" + message + "\n\n",
"Studio for kdb+");
}
window.refreshActionState();
}
public static Workspace getWorkspace() {
Workspace workspace = new Workspace();
Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
for (StudioWindow window : allWindows) {
Workspace.TopWindow workspaceWindow = workspace.addWindow(window == activeWindow);
workspaceWindow.setResultDividerLocation(Util.getDividerLocation(window.splitpane));
workspaceWindow.setLocation(window.getBounds());
window.rootEditorsPanel.getWorkspace(workspaceWindow);
}
return workspace;
}
public void windowClosing(WindowEvent e) {
close();
}
public void windowClosed(WindowEvent e) {
}
public void windowOpened(WindowEvent e) {
}
// ctrl-alt spacebar to minimize window
public void windowIconified(WindowEvent e) {
}
public void windowDeiconified(WindowEvent e) {
}
public void windowActivated(WindowEvent e) {
}
public void windowDeactivated(WindowEvent e) {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy