package org.basex.gui.view.table;
import static org.basex.gui.GUIConstants.*;
import static org.basex.gui.layout.BaseXKeys.*;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import javax.swing.SwingUtilities;
import org.basex.core.Context;
import org.basex.data.Data;
import org.basex.data.Nodes;
import org.basex.gui.GUIProp;
import org.basex.gui.layout.BaseXBar;
import org.basex.gui.layout.BaseXPopup;
import org.basex.gui.view.View;
import org.basex.gui.view.ViewNotifier;
import org.basex.util.Performance;
import org.basex.util.list.IntList;
/**
* This view creates a flat table view on the database contents.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class TableView extends View implements Runnable {
/** Zoom table. */
private static final double[] ZOOM = {
1, .99, .98, .97, 1, 1.03, 1.05, .9, .8, .6, .35, .18, .13, .09, .05, .03
};
/** Table data. */
final TableData tdata;
/** Table header. */
private final TableHeader header;
/** Table content area. */
private final TableContent content;
/** Table scrollbar. */
private final BaseXBar scroll;
/**
* Default constructor.
* @param man view manager
*/
public TableView(final ViewNotifier man) {
super(TABLEVIEW, man);
tdata = new TableData(gui.context, gui.gprop);
layout(new BorderLayout());
header = new TableHeader(this);
add(header, BorderLayout.NORTH);
scroll = new BaseXBar(this);
content = new TableContent(tdata, scroll);
add(content, BorderLayout.CENTER);
new BaseXPopup(this, POPUP);
}
@Override
public void refreshInit() {
tdata.rootRows = null;
tdata.rows = null;
final Data data = gui.context.data();
if(!visible() || data == null || !data.meta.pathindex) return;
tdata.init(data);
refreshContext(true, false);
}
@Override
public void refreshContext(final boolean more, final boolean quick) {
if(tdata.cols.length == 0) return;
tdata.context(false);
scroll.pos(0);
if(tdata.rows == null) return;
if(quick) {
scroll.height(tdata.rows.size() * tdata.rowH(1));
focus();
repaint();
} else {
if(!more) tdata.resetFilter();
gui.updating = true;
new Thread(this).start();
}
}
@Override
public void refreshFocus() {
if(!visible() || tdata.rows == null) return;
repaint();
}
@Override
public void refreshMark() {
if(!visible() || tdata.rows == null) return;
final Context context = gui.context;
final Nodes marked = context.marked;
if(marked.size() != 0) {
final int p = tdata.getRoot(context.data(), marked.list[0]);
if(p != -1) setPos(p);
}
repaint();
}
@Override
public void refreshLayout() {
if(!visible() || tdata.rows == null) return;
scroll.height(tdata.rows.size() * tdata.rowH(1));
refreshContext(false, true);
}
@Override
public void refreshUpdate() {
refreshContext(false, true);
}
@Override
public boolean visible() {
return gui.gprop.is(GUIProp.SHOWTABLE);
}
@Override
public void visible(final boolean v) {
gui.gprop.set(GUIProp.SHOWTABLE, v);
}
@Override
protected boolean db() {
return true;
}
@Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
if(tdata.rows == null && visible()) refreshInit();
}
@Override
public void run() {
/* Current zoom step. */
int zoomstep = ZOOM.length;
while(--zoomstep >= 0) {
scroll.height(tdata.rows.size() * tdata.rowH(ZOOM[zoomstep]));
repaint();
Performance.sleep(25);
}
gui.updating = false;
focus();
}
/**
* Sets scrollbar position for the specified pre value.
* @param pre pre value
*/
private void setPos(final int pre) {
final int off = getOff(pre);
if(off == -1) return;
final int h = getHeight() - header.getHeight() - 2 * tdata.rowH;
final int y = (off - 1) * tdata.rowH;
final int s = scroll.pos();
if(y < s || y > s + h) scroll.pos(y);
}
/**
* Returns list offset for specified pre value.
* @param pre pre value
* @return offset
*/
private int getOff(final int pre) {
final int ns = tdata.rows.size();
for(int n = 0; n < ns; ++n) {
if(tdata.rows.get(n) == pre) return n;
}
return -1;
}
@Override
public void mouseMoved(final MouseEvent e) {
super.mouseMoved(e);
if(!visible() || tdata.rows == null) return;
tdata.mouseX = e.getX();
tdata.mouseY = e.getY();
focus();
}
/**
* Finds the current focus.
*/
private void focus() {
final int y = tdata.mouseY - header.getHeight() + scroll.pos();
final int l = y / tdata.rowH;
final boolean valid = y >= 0 && l < tdata.rows.size();
final Data data = gui.context.data();
int focused = -1;
if(valid) {
final int pre = tdata.rows.get(l);
final TableIterator it = new TableIterator(data, tdata);
final int c = tdata.column(getWidth() - BaseXBar.SIZE, tdata.mouseX);
it.init(pre);
while(it.more()) {
if(it.col == c) {
focused = it.pre;
break;
}
}
}
gui.notify.focus(focused, this);
content.repaint();
final String str = content.focusedString;
gui.cursor(valid && str != null && str.length() <= data.meta.maxlen ?
CURSORHAND : CURSORARROW);
}
@Override
public void mouseExited(final MouseEvent e) {
gui.cursor(CURSORARROW);
gui.notify.focus(-1, null);
}
@Override
public void mousePressed(final MouseEvent e) {
final int pre = gui.context.focused;
if(pre == -1) return;
super.mousePressed(e);
final Context context = gui.context;
final Data data = gui.context.data();
if(tdata.rows == null) return;
if(e.getY() < header.getHeight()) return;
if(SwingUtilities.isLeftMouseButton(e)) {
if(e.getClickCount() == 1) {
final int c = tdata.column(getWidth() - BaseXBar.SIZE, e.getX());
final String str = content.focusedString;
if(str == null || str.length() > data.meta.maxlen) return;
if(!e.isShiftDown()) tdata.resetFilter();
tdata.cols[c].filter = str;
query();
//repaint();
} else {
Nodes nodes = context.marked;
if(getCursor() == CURSORARROW) {
nodes = new Nodes(tdata.getRoot(nodes.data, pre), nodes.data);
}
gui.notify.context(nodes, false, null);
}
} else {
if(pre != -1) {
final TableIterator it = new TableIterator(data, tdata);
final int c = tdata.column(getWidth() - BaseXBar.SIZE, e.getX());
it.init(pre);
while(it.more()) {
if(it.col == c) {
gui.notify.mark(new Nodes(it.pre, data), null);
return;
}
}
}
}
}
/**
* Performs a table query.
*/
void query() {
final String query = tdata.find();
if(query != null) gui.xquery(query, false);
}
@Override
public void mouseWheelMoved(final MouseWheelEvent e) {
if(tdata.rows == null) return;
scroll.pos(scroll.pos() + e.getUnitsToScroll() * tdata.rowH);
mouseMoved(e);
repaint();
}
@Override
public void keyPressed(final KeyEvent e) {
super.keyPressed(e);
if(tdata.rows == null) return;
final int lines = (getHeight() - header.getHeight()) / tdata.rowH;
final int oldPre = tdata.getRoot(gui.context.data(), gui.context.focused);
int pre = oldPre;
final IntList rows = tdata.rows;
if(LINESTART.is(e)) {
pre = rows.get(0);
} else if(LINEEND.is(e)) {
pre = rows.get(rows.size() - 1);
} else if(PREVLINE.is(e)) {
pre = rows.get(Math.max(0, getOff(pre) - 1));
} else if(NEXTLINE.is(e)) {
pre = rows.get(Math.min(rows.size() - 1, getOff(pre) + 1));
} else if(PREVPAGE.is(e)) {
pre = rows.get(Math.max(0, getOff(pre) - lines));
} else if(NEXTPAGE.is(e)) {
pre = rows.get(Math.min(rows.size() - 1, getOff(pre) + lines));
}
if(pre != oldPre) {
setPos(pre);
gui.notify.focus(pre, null);
}
}
}