package org.basex.gui.view;
import static org.basex.core.Text.*;
import java.awt.Window;
import org.basex.core.Context;
import org.basex.data.Data;
import org.basex.data.Nodes;
import org.basex.gui.GUI;
import org.basex.gui.dialog.Dialog;
import org.basex.util.Array;
import org.basex.util.Performance;
import org.basex.util.Token;
import org.basex.util.Util;
/**
* This class serves as a container for all existing views. The observer pattern
* is used to inform all views on user interactions.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class ViewNotifier {
/** Large database size (minimum database size to show warning). */
private static final long LARGEDB = 200000000;
/** Maximum history size. */
public static final int MAXHIST = 20;
/** History pointer. */
public int hist;
/** Reference to main window. */
final GUI gui;
/** Zoomed rectangle history. */
private final Nodes[] marked = new Nodes[MAXHIST];
/** Zoomed rectangle history. */
private final Nodes[] cont = new Nodes[MAXHIST];
/** Command history. */
private final String[] queries = new String[MAXHIST];
/** Attached views. */
private View[] view = new View[0];
/** Maximum history value. */
private int maxhist;
/**
* Constructor.
* @param main reference to the main window
*/
public ViewNotifier(final GUI main) {
gui = main;
}
/**
* Adds a new view.
* @param v view to be added
*/
void add(final View v) {
view = Array.add(view, v);
}
/**
* Notifies all views of a data reference change.
*/
public void init() {
final Context ctx = gui.context;
final Data data = ctx.data();
if(data != null) {
cont[0] = ctx.current();
marked[0] = new Nodes(data);
// if a large database is opened, the user is asked if complex
/// visualizations should be closed first
final long fs = data.meta.dbsize();
boolean close = false;
for(final View v : view) close |= v.visible() && v.db();
if(close && fs > LARGEDB && Dialog.confirm(gui,
Util.info(H_LARGE_DB, Performance.format(fs)))) {
for(final View v : view) if(v.visible() && v.db()) v.visible(false);
}
} else {
// close all dialogs (except help) together with database
for(final Window w : gui.getOwnedWindows()) {
if(!(w.isVisible() && w instanceof Dialog)) continue;
final Dialog d = (Dialog) w;
if(!d.isModal()) ((Dialog) w).cancel();
}
}
gui.context.focused = -1;
hist = 0;
maxhist = 0;
for(final View v : view) v.refreshInit();
gui.layoutViews();
gui.setTitle(data != null ? data.meta.name : null);
}
/**
* Notifies all views of a focus change.
* @param pre focused pre value
* @param vw the calling view
*/
public void focus(final int pre, final View vw) {
if(gui.context.focused == pre) return;
gui.context.focused = pre;
for(final View v : view) if(v != vw && v.visible()) v.refreshFocus();
if(pre != -1) {
gui.status.setText(Token.string(ViewData.path(gui.context.data(), pre)));
}
}
/**
* Notifies all views of a selection change.
* @param mark marked nodes
* @param vw the calling view
*/
public void mark(final Nodes mark, final View vw) {
final Context context = gui.context;
context.marked = mark;
for(final View v : view) if(v != vw && v.visible()) v.refreshMark();
gui.filter.setEnabled(mark.size() != 0);
gui.refreshControls();
}
/**
* Notifies all views of a selection change.
* The mode flag determines what happens:
* <ul>
* <li>0: set currently focused node as marked node</li>
* <li>1: add currently focused node</li>
* <li>2: toggle currently focused node</li>
* </ul>
* @param mode mark mode
* @param vw the calling view
*/
public void mark(final int mode, final View vw) {
final int f = gui.context.focused;
if(f == -1) return;
final Context context = gui.context;
Nodes nodes = context.marked;
if(mode == 0) {
nodes = new Nodes(f, context.data());
} else if(mode == 1) {
nodes.union(new int[] { f });
} else {
nodes.toggle(f);
}
mark(nodes, vw);
}
/**
* Moves around in the internal history and notifies all views of
* a context change.
* @param forward move forward or backward
*/
public void hist(final boolean forward) {
final Context context = gui.context;
// browse back/forward
final String query;
if(forward) {
if(hist == maxhist) return;
query = queries[++hist];
} else {
if(hist == 0) return;
marked[hist] = context.marked;
query = queries[--hist];
}
context.set(cont[hist], marked[hist]);
gui.input.setText(query);
for(final View v : view) if(v.visible()) v.refreshContext(forward, false);
gui.refreshControls();
}
/**
* Notifies all views of a context change.
* @param nodes new context set
* @param quick quick switch
* @param vw the calling view
*/
public void context(final Nodes nodes, final boolean quick, final View vw) {
final Context context = gui.context;
final Nodes n = new Nodes(new int[0], context.data(), context.marked.ftpos);
if(!cont[hist].sameAs(quick ? context.current() : context.marked)) {
checkHist();
if(!quick) {
// add new entry
final String in = gui.input.getText();
queries[hist] = in;
marked[hist] = context.marked;
cont[++hist] = nodes;
queries[hist] = in;
marked[hist] = n;
} else {
// check if current node set has already been cached
// add new entry
queries[hist] = "";
marked[hist] = new Nodes(context.data());
cont[++hist] = context.current();
}
maxhist = hist;
}
context.set(nodes, n);
for(final View v : view) if(v != vw && v.visible())
v.refreshContext(true, quick);
gui.refreshControls();
}
/**
* Notifies all views of updates in the data structure.
*/
public void update() {
final Context context = gui.context;
final Data data = context.data();
if(data == null) return;
hist = 0;
maxhist = 0;
context.marked = new Nodes(data);
for(final View v : view) if(v.visible()) v.refreshUpdate();
gui.refreshControls();
}
/**
* Notifies all views of layout changes.
*/
public void layout() {
for(final View v : view) v.refreshLayout();
}
/**
* Returns the last or next query string.
* @param back back/forward flag
* @return query string
*/
public String tooltip(final boolean back) {
return back ? hist > 0 ? hist > 1 ? queries[hist - 2] : "" : null :
hist < maxhist ? queries[hist + 1] : null;
}
// PRIVATE METHODS ==========================================================
/**
* Checks the history data arrays.
*/
private void checkHist() {
final int hl = queries.length;
if(hist + 1 == hl) {
Array.move(queries, 1, 0, hl - 1);
Array.move(cont, 1, 0, hl - 1);
Array.move(marked, 1, 0, hl - 1);
--hist;
}
}
}