package com.applang.components; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; import java.io.StringWriter; import java.io.Writer; import java.util.Date; import java.util.Properties; import javax.swing.AbstractButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.JToolBar; import javax.swing.SwingUtilities; import org.gjt.sp.jedit.View; import org.gjt.sp.jedit.textarea.TextArea; import com.applang.Dialogs; import com.applang.PromptDirective; import com.applang.berichtsheft.BerichtsheftActivity; import com.applang.berichtsheft.BerichtsheftApp; import com.applang.berichtsheft.plugin.BerichtsheftPlugin; import com.applang.components.DataView.DataModel; import android.app.AlertDialog; import android.content.ContentUris; import android.content.ContentValues; import android.net.Uri; import android.util.Log; import static com.applang.Util.*; import static com.applang.Util1.*; import static com.applang.Util2.*; import static com.applang.SwingUtil.*; import static com.applang.PluginUtils.*; public class DataManager extends ActionPanel { private static final String TAG = DataManager.class.getSimpleName(); public static final int TABLE = 0; public static final int FORM = 1; public static final int TEXT = 2; public static final int SCRIPT = 3; private Properties props; private String name; public String getName() { return name; } public DataManager(View view, Properties props, String name, MouseListener clickListener) { super(createTextComponent(), view, name, 2); this.props = props; this.name = name; if (clickListener != null) this.clickListener = clickListener; uriString = this.props.getProperty("uri", ""); setDataView(); dataView.setUriString(uriString); initialize(dataView.getUriString()); } public static class DataPanel extends JPanel { @Override public String toString() { Writer writer = write(new StringWriter(), identity(this)); return writer.toString(); } private DataManager dataManager; public DataManager getDataManager() { return dataManager; } public DataPanel(DataManager dataManager) { super(new BorderLayout()); this.dataManager = dataManager; JPanel gutter = new JPanel(); gutter.setName("gutter"); add(gutter, BorderLayout.WEST); } public JComponent getGutter() { return findFirstComponent(this, "gutter"); } } public void installConstellation(String config) { index = -1; panes = new DataPanel[0]; String[] constellation = config.split(","); for (String s : constellation) { int star = toInt(-1, s); if (star > -1) createAnotherPane(++index, star == TABLE); } props.getProperty("constellation", config); featureFile(name, "data-manage", props); } public void modifyConstellation(final Component component) { JSplitPane splitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, component); switch (panes.length) { case 1: JComponent pane = getPane(1); if (splitPane.getTopComponent() == null) splitPane.setTopComponent(pane); else splitPane.setBottomComponent(pane); break; case 2: Component c = null; if (containsComponent((Container)splitPane.getTopComponent(), component)) { c = splitPane.getTopComponent(); splitPane.setTopComponent(null); } else if (containsComponent((Container)splitPane.getBottomComponent(), component)) { c = splitPane.getBottomComponent(); splitPane.setBottomComponent(null); } if (c != null) { onMouseClickIn((Container) c, clickListener, false); panes = arrayslice(panes, panes[0].equals(c) ? 1 : 0, 1); index = 0; JToolBar bar = findFirstComponent(panes[index], getName()); for (int i = 0; i < bar.getComponentCount(); i++) { AbstractButton btn = (AbstractButton) bar.getComponent(i); CustomAction act = (CustomAction) btn.getAction(); ActionType type = (ActionType)act.getType(); if (ActionType.DATABASE.equals(type)) { addButton(bar, ActionType.TOGGLE1.index(), new ManagerAction(ActionType.TOGGLE1, isTableView(index)), 0, i + 1); break; } } } break; } splitPane.validate(); splitPane.repaint(); } private MouseListener clickListener = new MouseAdapter() { @Override public void mouseClicked(MouseEvent ev) { final Component component = (Component) ev.getSource(); boolean controlKeyPressed = isCtrlKeyHeld(ev); println((controlKeyPressed ? "C + " : "") + "mouseClick", identity(component)); if (controlKeyPressed) { modifyConstellation(component); } } }; private DataPanel[] panes = new DataPanel[0]; public int getPanesCount() { return panes.length; } public JComponent getPane(int index) { while (panes.length <= index) { boolean tableView = index > 0 ? isTableView(index - 1) : true; createAnotherPane(panes.length, tableView); } return panes[index]; } private void createAnotherPane(int index, boolean tableView) { panes = arrayextend(panes, false, new DataPanel(this)); JToolBar bar = northToolBar(panes[index], BorderLayout.SOUTH); bar.setName(getName()); this.index = index; toggleView(tableView); if (panes.length > 1) { bar = findFirstComponent(panes[0], getName()); removeButton(bar, ActionType.TOGGLE1.index()); } } private int index = -1; public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public void setIndexBy(Component component) { component = findAncestor(component, new Predicate<Component>() { public boolean apply(Component c) { if (c instanceof DataPanel) { for (index = 0; index < panes.length; index++) if (panes[index].equals(c)) return true; } return false; } }); if (component == null) throw new RuntimeException(TAG + " is out of order"); } public boolean isTableView(int index) { return containsComponent(panes[index], getTableComponent()); } private void onMouseClickIn(Container container, MouseListener mouseListener, boolean addRemove) { for (final Component c : DoubleFeature.focusRequestComponents(container)) { JComponent jc = (JComponent) (c instanceof TextArea ? ((TextArea) c).getPainter() : c); if (addRemove) jc.addMouseListener(mouseListener); else jc.removeMouseListener(mouseListener); } } private void viewSwitch(Function<Void> func, Object...params) { int index = this.index; try { onMouseClickIn(panes[index], clickListener, false); func.apply(params); } finally { onMouseClickIn(panes[index], clickListener, true); } } private void setDataView() { dataView = new DataView(); Component component = findFirstComponent(dataView, BorderLayout.SOUTH); if (component != null) remove(component); JTable table = dataView.getTable(); table.getTableHeader().setName(DoubleFeature.REQUEST); table.setFillsViewportHeight(true); addNamePart(table, DoubleFeature.FOCUS); dataView.showSqlBox(false); } private DataView dataView = null; public JComponent getTableComponent() { return (JComponent) dataView; } private static ITextComponent createTextComponent() { TextToggle textToggle = new TextToggle().createBufferedTextArea(null, null); textToggle.getTextEdit().installSpellChecker(); return textToggle; } private DataForm dataForm = null; private void setDataForm() { final BerichtsheftActivity context = BerichtsheftActivity.getInstance((JFrame) getView()); iComponent = new IComponent() { public Component getUIComponent() { if (container == null) { // lazy initialization dataForm = new DataForm(context, DataManager.this, dataView.getDataConfiguration().getProjectionModel(), props.getProperty("layout")); container = new JScrollPane(dataForm.getContainer()); printContainer("panel", container, false); } return container; } private Container container = null; }; } public Container getFormComponent() { return (Container) iComponent.getUIComponent(); } private Container installTools(boolean tableView) { JToolBar bar = findFirstComponent(panes[index], getName()); bar.removeAll(); addButton(bar, ActionType.DATABASE.index(), new ManagerAction(ActionType.DATABASE), index); if (panes.length == 1) addButton(bar, ActionType.TOGGLE1.index(), new ManagerAction(ActionType.TOGGLE1, tableView), index); bar.revalidate(); return bar; } private Container installMoreTools() { Container bar = installTools(false); addButton(bar, ActionType.FIRST.index(), new ManagerAction(ActionType.FIRST), index); addButton(bar, ActionType.PREVIOUS.index(), new ManagerAction(ActionType.PREVIOUS), index); addButton(bar, ActionType.NEXT.index(), new ManagerAction(ActionType.NEXT), index); addButton(bar, ActionType.LAST.index(), new ManagerAction(ActionType.LAST), index); if (getTextToggle() != null) addToggle(bar, ActionType.TOGGLE2.index(), new ManagerAction(ActionType.TOGGLE2), index); return bar; } private void toggleView(boolean tableView) { try { viewToggler.perform(tableView, objects()); } catch (Exception e) { Log.e(TAG, "toggleView", e); } } private Job<Boolean> viewToggler = new Job<Boolean>() { public void perform(Boolean tableView, Object[] parms) throws Exception { Object[] params = objects(_null()); if (tableView) { installTools(true); params[0] = getTableComponent(); } else { installMoreTools(); params[0] = getFormComponent(); } viewSwitch( new Function<Void>() { public Void apply(Object... params) { Component center = (Component) params[0]; replaceCenterComponent(center, panes[index]); return null; } }, params); printContainer(String.format("panes[%d]", index), panes[index], DIAG_OFF); if (!tableView && getTextToggle() != null) try { scriptToggler.perform(_script() == TEXT, objects()); } catch(Exception e) { Log.e(TAG, "layoutToggler", e); }; featureFile(name, "data-manage", props); } }; private int _script() { String text = props.getProperty("text"); if ("script".equals(text)) return SCRIPT; else return TEXT; } protected Job<Boolean> scriptToggler = new Job<Boolean>() { public void perform(Boolean isText, Object[] parms) throws Exception { props.setProperty("text", isText ? "document" : "script"); String text = getText(); viewSwitch( new Function<Void>() { public Void apply(Object... params) { Boolean textView = (Boolean) params[0]; getTextToggle().toggle(textView, null); return null; } }, isText); setText(text); featureFile(name, "data-manage", props); } }; class ManagerAction extends CustomAction { public ManagerAction(ActionType type, Object...params) { super(type); if (type.equals(ActionType.TOGGLE1)) { boolean tableView = param_Boolean(true, 0, params); putValue(NAME, type.name(tableView ? 2 : 1)); } else if (type.equals(ActionType.TOGGLE2)) { putValue(NAME, type.name(_script() == TEXT ? 2 : 1)); } } public ManagerAction(String text) { super(text); } @Override protected void action_Performed(ActionEvent ae) { setIndexBy((Component) ae.getSource()); ActionType type = (ActionType)getType(); switch (type) { case DATABASE: updateOnRequest(); dbFilePath = chooseDatabase(dbFilePath); break; case CALENDAR: // dateString = pickDate(dateString); // date.requestFocusInWindow(); break; case FIRST: case PREVIOUS: case NEXT: case LAST: browse(type); break; case PICK: // finder.keyLine(); // Long time = toTime(dateString, DatePicker.calendarFormat); // if (time != null) { // pkRow = finder.pointer(time, getTitle()); // browse(type); // } break; case DATE: // date.requestFocusInWindow(); break; case TITLE: // comboBoxes[0].requestFocusInWindow(); break; case INSERT: break; case DELETE: // deleteSelection(); break; case STRUCT: break; case TOGGLE1: toggle(this, viewToggler); break; case TOGGLE2: toggle(this, scriptToggler); break; default: return; } listenToClick(ae); } public void listenToClick(ActionEvent ae) { MouseEvent ev = new MouseEvent((Component) ae.getSource(), ae.getID(), ae.getWhen(), ae.getModifiers(), 0, 0, 1, false); clickListener.mouseClicked(ev); } } @Override protected String chooseDatabase(String dbString) { Container container = getView(); final View view = container instanceof View ? (View) container : null; viewSwitch( new Function<Void>() { public Void apply(Object... params) { if (dataView.configureData(view, false)) { String dbString = dataView.getUriString(); props.setProperty("uri", dbString); featureFile(name, "data-manage", props); initialize(dbString); } return null; } }); return dataView.getUriString(); } private void initialize(String dbString) { refresh(); JComponent parent = (JComponent) getParent(); if (parent != null) setWindowTitle(this, String.format( "Database : %s", trimPath(dbString, parent.getWidth() / 2, parent.getFont(), parent))); } private void clear() { setText(""); enableBrowseActions(); } public void enableBrowseActions() { enableAction(ActionType.FIRST.index(), pkRow > 0); enableAction(ActionType.PREVIOUS.index(), pkRow > 0); enableAction(ActionType.NEXT.index(), pkRow < lastRow() && pkRow > -1); enableAction(ActionType.LAST.index(), pkRow < lastRow() && pkRow > -1); } private DataModel dataModel() { if (dataView == null) return null; else return (DataModel) dataView.getTable().getModel(); } private int convertRowIndexToModel(int rowIndex) { return dataView.getTable().convertRowIndexToModel(rowIndex); } private DataAdapter dataAdapter = null; private String uriString = null; private BidiMultiMap projection = null; private String sortOrder = null; private Object pk = null; public int pkColumn = -1, pkRow = -1; private boolean createAdapter() { DataConfiguration dataConfig = dataView.getDataConfiguration(); String dbPath = dataConfig.getPath(); String flavor = dataConfig.getFlavor(); projection = dataConfig.getProjection(); sortOrder = dataConfig.getSortOrder(); uriString = stringValueOf(dataConfig.getUri()); dataAdapter = new DataAdapter(flavor, new File(dbPath), uriString); ValMap info = dataAdapter.info; if (info.size() > 0) { pk = info.get("PRIMARY_KEY"); pkColumn = info.getList("name").indexOf(pk); if (pkColumn < 0) { BerichtsheftPlugin.consoleMessage("dataview.no-primary-key.message"); } } else { BerichtsheftPlugin.consoleMessage("dataview.no-info-for-table.message", dataAdapter.getTableName()); pkColumn = -1; } return pkColumn > -1; } public Object pkValue() { if (pkRow < 0) return null; int rowIndex = convertRowIndexToModel(pkRow); Object[] row = dataModel().getValues(false, rowIndex); return row[pkColumn]; } public int lastRow() { DataModel model = dataModel(); if (model == null) return -1; else return model.getRowCount() - 1; } public void setRow(Object pkValue) { DataModel model = dataModel(); for (int i = 0; i < model.getRowCount(); i++) { Object[] row = dataModel().getValues(false, i); if (row[pkColumn].equals(pkValue)) { pkRow = i; break; } } } public void refresh() { if (!noRefresh) { clear(); if (createAdapter()) { dataView.populate(dataAdapter, null, null, sortOrder); setDataForm(); for (index = 0; index < panes.length; index++) toggleView(isTableView(index)); browse(ActionType.PICK); } } } @Override protected void updateOnRequest() { Object item = getItem(); if (isItemValid(item)) { save(item, false); } } private boolean browseFree = true; public void browse(ActionType direct) { if (!browseFree) return; try { browseFree = false; updateOnRequest(); String text = ""; if (pkColumn > -1) { switch (direct) { case FIRST: pkRow = 0; break; case PREVIOUS: pkRow--; break; case NEXT: pkRow++; break; case LAST: pkRow = lastRow(); break; default: pkRow = Math.min(Math.max(0, pkRow), lastRow()); break; } enableBrowseActions(); if (direct != ActionType.DELETE) { int[] rows = dataView.getTable().getSelectedRows(); if (rows.length > 0 && direct == ActionType.PICK) rows = ints(rows[0], rows[rows.length - 1]); else rows = ints(pkRow); selectRowAndScrollToVisible(dataView.getTable(), rows); } Object[] rec = (Object[]) select(); if (rec != null && dataForm != null) { dataForm.setContent(rec); } else clear(); } setText(text); } finally { browseFree = true; } } /* private JPopupMenu tablePopup = newPopupMenu( objects(ActionType.DELETE.description(), new ManagerAction(ActionType.DELETE)) ); private ListSelectionListener tableSelectionListener = new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; int sel = dataView.getTable().getSelectedRow(); if (sel > -1) { pkRow = sel; browse(ActionType.PICK); } } }; private void deleteSelection() { updateOnRequest(); int[] rows = dataView.getTable().getSelectedRows(); if (isAvailable(0, rows)) { boolean refresh = false; try { begin_delete(); for (int i = rows.length - 1; i > -1; i--) { pkRow = rows[i]; browse(ActionType.DELETE); Boolean done = do_delete(getLongItem()); if (done != null) refresh |= done; } } catch (Throwable e) {} finally { end_delete(); } if (refresh) refresh(); } } */ @Override public Object select(Object...args) { args = reduceDepth(args); boolean pkValue = notAvailable(0, args); String dateString = param(null, 0, args); String pattern = param(null, 1, args); if (pkValue || notNullOrEmpty(dateString) || bausteinEditing()) try { Long time = toTime(dateString, DatePicker.calendarFormat); long[] interval = dayInterval(time, 1); Object[][] result = dataAdapter.query(uriString, toStrings(projection.getKeys()), whereClause(pkValue), pkValue ? strings("" + pkValue()) : (bausteinEditing() ? strings(pattern) : strings("" + interval[0], "" + (interval[1] - 1), pattern)), sortOrder); if (isAvailable(0, result)) { return result[0]; } } catch (Exception e) { handleException(e); } return null; } private boolean bausteinEditing() { // TODO Auto-generated method stub return false; } private String whereClause(boolean pkValue) { if (pkValue) return pk + "=?"; else return bausteinEditing() ? "created is null and title = ?" : "created between ? and ? and title like ?"; } public static String shortFormat = "%s %s"; @Override protected String toString(Object item) { Object[] rec = (Object[]) item; return String.format(shortFormat, rec[1], rec[0]); } @Override protected String toLongString(Object item) { Object[] rec = (Object[]) item; return String.format(noteFormat2, rec[1], rec[0], rec[2]); } @Override public boolean isItemValid(Object item) { Object[] rec = (Object[]) item; Date date = parseDate(stringValueOf(rec[0])); return date != null && notNullOrEmpty(rec[1]); } @Override protected Object getItem() { return objects(getDate(), getTitle()); } private Object getTitle() { // TODO Auto-generated method stub return null; } private Object getDate() { // TODO Auto-generated method stub return null; } @Override protected Object getLongItem() { return objects(getDate(), getTitle(), getText()); } @Override protected void updateItem(boolean update, Object...args) { Object[] rec = reduceDepth(args); String text = getText(); if (isAvailable(0, rec)) updateOrAdd(false, text, rec); else if (update) updateOrAdd(false, text, (Object[]) getItem()); else { rec = (Object[]) select(getItem()); if (isAvailable(2, rec)) setText(rec[2].toString()); } } private long updateOrAdd(boolean add, String text, Object...rec) { long time = toTime(rec[0].toString(), DatePicker.calendarFormat); ValMap info = dataAdapter.info; BidiMultiMap map = new BidiMultiMap(); map.putValue("note", text); map.putValue("modified", now()); ContentValues values = contentValues(info, map.getKeys(), map.getValues().toArray()); if (add) { values.putNull(pk.toString()); values.put("created", time); values.put("title", rec[1].toString()); Uri uri = dataAdapter.insert(uriString, values); return ContentUris.parseId(uri); } else { rec = (Object[]) select(rec); return dataAdapter.update(appendId(uriString, (Long) rec[3]), values); } } @Override protected boolean addItem(boolean refresh, Object item) { Object[] rec = (Object[]) item; String text = getText(); long id = updateOrAdd(true, text, rec); if (refresh) refresh(); return id > -1; } @Override protected boolean removeItem(Object item) { boolean done = false; Object[] rec = (Object[]) select(item); if (rec != null) { done = 0 < dataAdapter.delete(appendId(uriString, (Long) rec[3])); } if (done) { clear(); refresh(); } return done; } @Override public void setText(String text) { TextToggle textBums = getTextToggle(); if (textBums != null) { updateText(textBums, text); textBums.getTextEdit().undoManager.discardAllEdits(); } } public static String noteFormat1 = enclose(FOLD_MARKER[0], " %s\n%s\n", FOLD_MARKER[1]); public static String noteFormat2 = enclose(FOLD_MARKER[0], " %s '%s'\n%s\n", FOLD_MARKER[1]); public static String formatDate(int kind, long time) { switch (kind) { case 2: return DatePicker.weekDate(weekInterval(new Date(time), 1)); case 3: return DatePicker.monthDate(monthInterval(new Date(time), 1)); default: return com.applang.Util.formatDate(time, DatePicker.calendarFormat); } } public Date parseDate(String dateString) { try { return new Date(toTime(dateString, DatePicker.calendarFormat)); } catch (Exception e) { return null; } } public static void askConstellation(Job<String> job, Object...params) { AlertDialog.behavior = Behavior.setTimeout(AlertDialog.behavior, true); String[] values = strings("1,0"); PromptDirective.prompt( new BerichtsheftActivity(job, arraycast(values, objects())), Dialogs.DIALOG_TEXT_ENTRY, "DataManager", "constellation", values); } public static void main(String...args) { underTest = param("true", 0, args).equals("true"); BerichtsheftApp.loadSettings(); final Properties props = new Properties(); askConstellation( new Job<String>() { public void perform(String result, Object[] parms) throws Exception { AlertDialog.behavior = Behavior.setTimeout(AlertDialog.behavior, false); if (result != null) { props.setProperty("constellation", result); manage(Behavior.EXIT_ON_CLOSE, props); } else System.exit(0); } }); } public static Dimension viewportSize = new Dimension(600,200); public static void manage(int behavior, Properties props) { final DataManager dm = new DataManager(null, props, "Data manager", null); dm.installConstellation(props.getProperty("constellation")); showFrame(null, dm.getName(), new UIFunction() { public Component[] apply(final Component comp, Object[] parms) { dm.getTableComponent().setPreferredSize(viewportSize); dm.getFormComponent().setPreferredSize(viewportSize); JSplitPane splitPane = splitPane(JSplitPane.VERTICAL_SPLIT); splitPane.setTopComponent(dm.getPane(0)); if (dm.getPanesCount() > 1) { splitPane.setBottomComponent(dm.getPane(1)); } return components(splitPane); } }, new UIFunction() { public Component[] apply(Component comp, Object[] parms) { // Container frame = (JFrame)comp; // printContainer("frame", frame, true); return null; } }, null, behavior); } }