package org.geogebra.desktop.gui.view.consprotocol; /* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. */ import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Graphics; import java.awt.HeadlessException; import java.awt.Image; import java.awt.Point; import java.awt.dnd.DragSource; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import javax.swing.AbstractAction; import javax.swing.AbstractCellEditor; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTable.PrintMode; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.TableColumnModelEvent; import javax.swing.event.TableColumnModelListener; import javax.swing.event.TableModelEvent; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.geogebra.common.GeoGebraConstants; import org.geogebra.common.awt.GPoint; import org.geogebra.common.gui.SetLabels; import org.geogebra.common.gui.view.consprotocol.ConstructionProtocolView; import org.geogebra.common.javax.swing.table.GAbstractTableModel; import org.geogebra.common.jre.util.Base64; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.algos.ConstructionElement; import org.geogebra.common.kernel.geos.GProperty; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.main.App; import org.geogebra.common.main.GeoGebraColorConstants; import org.geogebra.common.main.settings.AbstractSettings; import org.geogebra.common.main.settings.ConstructionProtocolSettings; import org.geogebra.common.main.settings.SettingListener; import org.geogebra.common.util.FileExtensions; import org.geogebra.common.util.StringUtil; import org.geogebra.common.util.lang.Unicode; import org.geogebra.desktop.awt.GColorD; import org.geogebra.desktop.export.ConstructionProtocolExportDialog; import org.geogebra.desktop.gui.GuiManagerD; import org.geogebra.desktop.gui.TitlePanel; import org.geogebra.desktop.gui.view.algebra.InputPanelD; import org.geogebra.desktop.javax.swing.GImageIconD; import org.geogebra.desktop.javax.swing.table.GAbstractTableModelD; import org.geogebra.desktop.main.AppD; import org.geogebra.desktop.main.LocalizationD; import org.geogebra.desktop.plugin.GgbAPID; import org.geogebra.desktop.util.GuiResourcesD; public class ConstructionProtocolViewD extends ConstructionProtocolView implements Printable, SettingListener, SetLabels { static Color COLOR_STEP_HIGHLIGHT = AppD.COLOR_SELECTION; private static Color COLOR_DRAG_HIGHLIGHT = new Color(250, 250, 200); private static Color COLOR_DROP_HIGHLIGHT = Color.lightGray; public JTable table; // public JPanel cpPanel; private TableColumn[] tableColumns; // private AbstractAction printPreviewAction, exportHtmlAction; // for drag & drop private boolean dragging = false; int dragIndex = -1; // dragged construction index private int dropIndex = -1; // public ConstructionProtocolNavigationD protNavBar; // navigation bar of // protocol window private ConstructionProtocolViewD view = this; public JScrollPane scrollPane; private ConstructionProtocolStyleBar helperBar; private AbstractAction exportHtmlAction, printPreviewAction; private LocalizationD loc; public ConstructionProtocolViewD(final AppD app) { // cpPanel = new JPanel(new BorderLayout()); this.app = app; this.loc = app.getLocalization(); kernel = app.getKernel(); data = new ConstructionTableDataD(this); useColors = true; addIcons = false; table = new JTable(); table.setAutoCreateColumnsFromModel(false); table.setModel(((GAbstractTableModelD) data.getImpl()).getImpl()); table.setRowSelectionAllowed(true); table.setGridColor(Color.lightGray); table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); // header JTableHeader header = table.getTableHeader(); // header.setUpdateTableInRealTime(true); header.setReorderingAllowed(false); // init model ConstructionTableCellRenderer renderer; HeaderRenderer headerRend = new HeaderRenderer(); tableColumns = new TableColumn[data.columns.length]; for (int k = 0; k < data.columns.length; k++) { renderer = new ConstructionTableCellRenderer(); renderer.setHorizontalAlignment(data.columns[k].getAlignment()); tableColumns[k] = new TableColumn(k, data.columns[k].getPreferredWidth(), renderer, null); tableColumns[k].setMinWidth(data.columns[k].getMinWidth()); tableColumns[k].setHeaderRenderer(headerRend); if (data.columns[k].getInitShow()) { table.addColumn(tableColumns[k]); } if ("Caption".equals(data.columns[k].getTitle())) { tableColumns[k] .setCellEditor(new ConstructionTableCellEditor()); } } // first column "No." should have fixed width tableColumns[0].setMaxWidth(tableColumns[0].getMinWidth()); table.getColumnModel().addColumnModelListener( ((ConstructionTableDataD) data).new ColumnMovementListener()); scrollPane = new JScrollPane(table); scrollPane.getViewport().setBackground(Color.white); // cpPanel.add(scrollPane, BorderLayout.CENTER); // clicking ConstructionMouseListener ml = new ConstructionMouseListener(); table.addMouseListener(ml); table.addMouseMotionListener(ml); header.addMouseListener(ml); // double clicking, right-click menu scrollPane.addMouseListener(ml); // right-click menu // keys ConstructionKeyListener keyListener = new ConstructionKeyListener(); table.addKeyListener(keyListener); app.getGuiManager().registerConstructionProtocolView(this); // navigation bar // protNavBar = new ConstructionProtocolNavigationD(app); // protNavBar.register(this); // protNavBar.setPlayButtonVisible(false); // protNavBar.setConsProtButtonVisible(false); // this.cpPanel.add(protNavBar.getImpl(), BorderLayout.SOUTH); // org.geogebra.desktop.util.Util.addKeyListenerToAll(protNavBar.getImpl(), // keyListener); initGUI(); initActions(); ConstructionProtocolSettings cps = app.getSettings() .getConstructionProtocol(); settingsChanged(cps); cps.addListener(this); } public App getApp() { return app; } public JScrollPane getCpPanel() { return scrollPane; } public int getConstructionIndex(int row) { return data.getConstructionIndex(row); } public AppD getApplication() { return (AppD) app; } public void unregisterNavigationBar(ConstructionProtocolNavigationD nb) { navigationBars.remove(nb); ((ConstructionTableDataD) data).detachView(); // only done if there are // no more navigation // bars } public void initProtocol() { if (!isViewAttached) { ((ConstructionTableDataD) data).initView(); } } protected void repaintScrollpane() { scrollPane.repaint(); } /** * inits GUI with labels of current language */ public void initGUI() { // setTitle(loc.getMenu("ConstructionProtocol")); scrollPane.setFont(((AppD) app).getPlainFont()); // setMenuBar(); getStyleBar().setLabels(); // set header values (language may have changed) for (int k = 0; k < tableColumns.length; k++) { tableColumns[k] .setHeaderValue(data.columns[k].getTranslatedTitle()); } table.updateUI(); table.setFont(((AppD) app).getPlainFont()); ((ConstructionTableDataD) data).updateAll(); getStyleBar().reinit(); // protNavBar.updateIcons(); } public void setUseColors(boolean flag) { useColors = flag; ((ConstructionTableDataD) data).updateAll(); } public void setAddIcons(boolean flag) { addIcons = flag; ((ConstructionTableDataD) data).updateAll(); } public boolean getAddIcons() { return addIcons; } public void update() { ((ConstructionTableDataD) data).updateAll(); } public TableColumn[] getTableColumns() { return tableColumns; } public boolean getUseColors() { return useColors; } public boolean isColumnInModel(TableColumn col) { boolean ret = false; TableColumnModel model = table.getColumnModel(); int size = model.getColumnCount(); for (int i = 0; i < size; ++i) { if (model.getColumn(i) == col) { ret = true; break; } } return ret; } /** * shows this dialog centered on screen */ public void setVisible(boolean flag) { if (flag) { ((ConstructionTableDataD) data).attachView(); } else { ((ConstructionTableDataD) data).detachView(); } scrollPane.setVisible(flag); } @Override public void scrollToConstructionStep() { int rowCount = table.getRowCount(); if (rowCount == 0) { return; } int step = kernel.getConstructionStep(); int row = 0; for (int i = Math.max(step, 0); i < rowCount; i++) { if (data.getConstructionIndex(i) <= step) { row = i; } else { break; } } table.setRowSelectionInterval(row, row); table.repaint(); } public JTable getTable() { return table; } public AbstractAction getExportHtmlAction() { return exportHtmlAction; } public AbstractAction getPrintPreviewAction() { return printPreviewAction; } /** * @return The style bar for this view. */ public ConstructionProtocolStyleBar getStyleBar() { if (helperBar == null) { helperBar = newConstructionProtocolHelperBar(); } return helperBar; } /** * @return new Construction Protocol style bar */ protected ConstructionProtocolStyleBar newConstructionProtocolHelperBar() { return new ConstructionProtocolStyleBar(this, (AppD) app); } private void initActions() { exportHtmlAction = new AbstractAction(loc.getMenu("ExportAsWebpage") + " (" + FileExtensions.HTML + ") ...") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { app.setWaitCursor(); Thread runner = new Thread() { @Override public void run() { JDialog d = new ConstructionProtocolExportDialog(view); d.setVisible(true); } }; runner.start(); app.setDefaultCursor(); } }; printPreviewAction = new AbstractAction( loc.getMenu("Print") + Unicode.ellipsis, ((AppD) app) .getScaledIcon(GuiResourcesD.DOCUMENT_PRINT_PREVIEW)) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { app.setWaitCursor(); Thread runner = new Thread() { @Override public void run() { try { Construction cons = app.getKernel() .getConstruction(); getTable().print(JTable.PrintMode.FIT_WIDTH, new MessageFormat(tableHeader(cons)), new MessageFormat("{0}"), // page numbering /* showPrintDialog */true, /* attr */null, /* interactive */true /* , */ /* service *//* null */); // service must be omitted for Java version 1.5.0 } catch (HeadlessException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } catch (PrinterException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } } // This may be too long. FIXME private String tableHeader(Construction cons) { TitlePanel tp = new TitlePanel((AppD) app); String author = tp.loadAuthor(); String title = cons.getTitle(); String date = tp.configureDate(cons.getDate()); if ("".equals(title)) { title = loc.getMenu("UntitledConstruction"); } if ("".equals(author)) { return title + " (" + date + ")"; } return author + ": " + title + " (" + date + ")"; } }; runner.start(); app.setDefaultCursor(); } }; } class ConstructionKeyListener extends KeyAdapter { @Override public void keyPressed(KeyEvent event) { // SPECIAL KEYS int keyCode = event.getKeyCode(); switch (keyCode) { default: // do nothing break; case KeyEvent.VK_DELETE: ConstructionElement ce = kernel .getConstructionElement(kernel.getConstructionStep()); if (ce != null) { ce.remove(); app.storeUndoInfo(); } break; case KeyEvent.VK_UP: case KeyEvent.VK_RIGHT: previousStep(); scrollToConstructionStep(); break; case KeyEvent.VK_DOWN: case KeyEvent.VK_LEFT: nextStep(); scrollToConstructionStep(); break; case KeyEvent.VK_HOME: case KeyEvent.VK_PAGE_UP: setConstructionStep(-1); scrollToConstructionStep(); break; case KeyEvent.VK_END: case KeyEvent.VK_PAGE_DOWN: setConstructionStep(kernel.getLastConstructionStep()); scrollToConstructionStep(); break; } } } class ConstructionMouseListener implements MouseListener, MouseMotionListener { // smallest and larges possible construction index for dragging private int minIndex, maxIndex; @Override public void mouseClicked(MouseEvent e) { Object ob = e.getSource(); if (ob == table) { Point origin = e.getPoint(); GPoint mouseCoords = new GPoint(e.getPoint().x, e.getPoint().y); int row = table.rowAtPoint(origin); if (row < 0) { return; } // right click if (AppD.isRightClick(e)) { GeoElement geo = ((ConstructionTableDataD) data) .getGeoElement(row); ArrayList<GeoElement> temp = new ArrayList<GeoElement>(); temp.add(geo); ((GuiManagerD) app.getGuiManager()).showPopupMenu(temp, table, mouseCoords); } else { // left click if (e.getClickCount() == 1) { // click on breakpoint column? int column = table.columnAtPoint(origin); String colName = table.getColumnName(column); // if ("Breakpoint".equals(colName)) { if ("H".equals(colName)) { RowData rd = data.getRow(row); GeoElement geo = rd.getGeo(); boolean newVal = !geo.isConsProtocolBreakpoint(); geo.setConsProtocolBreakpoint(newVal); // update only current row rd.updateAll(); if (kernel.getConstruction().showOnlyBreakpoints() && !newVal) { data.remove(geo); } /* * // update geo and all siblings GeoElement [] * siblings = geo.getSiblings(); if (siblings != * null) { data.updateAll(); } else { // update only * current row rd.updateAll(); } * * // no longer a breakpoint: hide it if * (kernel.showOnlyBreakpoints() && !newVal) { if * (siblings == null) data.remove(geo); else { for * (int i=0; i < siblings.length; i++) { * data.remove(siblings[i]); } } } */ } } // double click if (e.getClickCount() == 2) { data.setConstructionStepForRow(row); table.repaint(); } } } else if (ob == table.getTableHeader() && (e.getClickCount() == 2)) { setConstructionStep(-1); table.repaint(); } else if ((e.getClickCount() == 1) && (AppD.isRightClick(e)) && ((ob == table.getTableHeader()) || (ob == scrollPane))) { ConstructionProtocolContextMenu contextMenu = new ConstructionProtocolContextMenu( (AppD) app); contextMenu.show(view.scrollPane, e.getPoint().x, e.getPoint().y); } } @Override public void mousePressed(MouseEvent e) { if (e.getSource() != table) { return; } int row = table.rowAtPoint(e.getPoint()); if (row >= 0) { // init drag GeoElement geo = ((ConstructionTableDataD) data) .getGeoElement(row); dragIndex = geo.getConstructionIndex(); minIndex = geo.getMinConstructionIndex(); maxIndex = geo.getMaxConstructionIndex(); } } @Override public void mouseReleased(MouseEvent e) { if (e.getSource() != table) { return; } if (!dragging) { table.repaint(); return; } // drop int row = table.rowAtPoint(e.getPoint()); if (row >= 0) { dropIndex = data.getConstructionIndex(row); boolean kernelChanged = ((ConstructionTableDataD) data) .moveInConstructionList(dragIndex, dropIndex); if (kernelChanged) { app.storeUndoInfo(); } } // reinit vars dragging = false; dragIndex = -1; dropIndex = -1; table.setCursor(Cursor.getDefaultCursor()); table.repaint(); } @Override public void mouseDragged(MouseEvent e) { if (e.getSource() != table || dragIndex == -1) { return; } int row = table.rowAtPoint(e.getPoint()); int index = (row < 0) ? -1 : data.getConstructionIndex(row); // drop possible if (minIndex <= index && index <= maxIndex) { table.setCursor(DragSource.DefaultMoveDrop); } // drop impossible else { table.setCursor(DragSource.DefaultMoveNoDrop); } if (index != dropIndex) { dragging = true; dropIndex = index; table.repaint(); } } @Override public void mouseMoved(MouseEvent e) { // do nothing } @Override public void mouseEntered(MouseEvent e) { // do nothing } @Override public void mouseExited(MouseEvent arg0) { // do nothing } } public class ColumnKeeper implements ActionListener { protected TableColumn column; protected ColumnData colData; private boolean isBreakPointColumn; public ColumnKeeper(TableColumn column, ColumnData colData) { this.column = column; this.colData = colData; isBreakPointColumn = colData.getTitle().equals("Breakpoint"); } @Override public void actionPerformed(ActionEvent e) { // JCheckBoxMenuItem item = (JCheckBoxMenuItem) e.getSource(); TableColumnModel model = table.getColumnModel(); if (!colData.isVisible()) { colData.setVisible(true); model.addColumn(column); // column is added at right end of model // move column to its default place int lastPos = model.getColumnCount() - 1; int pos = data.getColumnNumber(colData); if (pos >= 0 && pos < lastPos) { model.moveColumn(lastPos, pos); } scrollPane.setSize( scrollPane.getWidth() + column.getPreferredWidth(), scrollPane.getHeight()); // show breakPointColumn => show all lines if (isBreakPointColumn) { kernel.getConstruction().setShowOnlyBreakpoints(false); // cbShowOnlyBreakpoints.setSelected(false); } } else { colData.setVisible(false); model.removeColumn(column); // setSize(getWidth() - column.getWidth(), getHeight()); // setSize(view.getWidth(), getHeight()); } table.tableChanged(new TableModelEvent( ((GAbstractTableModelD) data.getImpl()).getImpl())); // reinit view to update possible breakpoint changes ((ConstructionTableDataD) data).initView(); SwingUtilities.updateComponentTreeUI(view.scrollPane); } } class ConstructionTableCellEditor extends AbstractCellEditor implements TableCellEditor { private static final long serialVersionUID = 1L; InputPanelD inputPanel; GeoElement geo; @Override public boolean stopCellEditing() { super.stopCellEditing(); // data.updateAll(); return true; } @Override public Object getCellEditorValue() { return inputPanel.getText(); } @Override public Component getTableCellEditorComponent(JTable table1, Object value, boolean isSelected, int rowIndex, int columnIndex) { geo = ((ConstructionTableDataD) data).getGeoElement(rowIndex); String val = geo .getCaptionDescription(StringTemplate.defaultTemplate); inputPanel = new InputPanelD("", (AppD) app, 20, false); inputPanel.setText(val); inputPanel.setEnabled(true); inputPanel.setVisible(true); return inputPanel; } } class ConstructionTableCellRenderer extends DefaultTableCellRenderer { /** * */ private static final long serialVersionUID = -9165858653728142643L; private JCheckBox cbTemp = new JCheckBox(); private JLabel iTemp = new JLabel(); public ConstructionTableCellRenderer() { setOpaque(true); setVerticalAlignment(TOP); } @Override public Component getTableCellRendererComponent(JTable table1, Object value, boolean isSelected, boolean hasFocus, int row, int column) { // Boolean value: show as checkbox boolean isBoolean = value instanceof Boolean; boolean isImage = value instanceof ImageIcon; Component comp = isBoolean ? cbTemp : (Component) this; if (isBoolean) { comp = cbTemp; } else if (isImage) { comp = iTemp; } else { comp = this; } int step = kernel.getConstructionStep(); RowData rd = data.getRow(row); int index = rd.getGeo().getConstructionIndex(); if (useColors) { comp.setForeground( GColorD.getAwtColor(rd.getGeo().getAlgebraColor())); } else { comp.setForeground(Color.black); } if (index == step) { // current construction step background color comp.setBackground(COLOR_STEP_HIGHLIGHT); } else if (index < step) { comp.setBackground(Color.white); } else { comp.setForeground(Color.gray); comp.setBackground(Color.white); } // set background color if (dragging) { if (index == dragIndex) { // drag & drop background color comp.setBackground(COLOR_DRAG_HIGHLIGHT); } else if (index == dropIndex) { // drag & drop background color comp.setBackground(COLOR_DROP_HIGHLIGHT); } } comp.setFont(table1.getFont()); if (isBoolean) { cbTemp.setSelected(((Boolean) value).booleanValue()); cbTemp.setEnabled(true); return cbTemp; } if (isImage) { /* * Scaling does not work yet. I wonder why. Image miniImage = * ((ImageIcon) value).getImage().getScaledInstance(16,16,0); * ImageIcon miniIcon = new ImageIcon(); * miniIcon.setImage(miniImage); iTemp.setIcon((ImageIcon) * value); iTemp.setHorizontalAlignment(JLabel.CENTER); * iTemp.setMaximumSize(new Dimension(16,16)); return iTemp; */ iTemp.setIcon((ImageIcon) value); return iTemp; } setText((value == null) ? "" : value.toString()); return this; } } class HeaderRenderer extends JLabel implements TableCellRenderer { /** * */ private static final long serialVersionUID = 8149210120003929698L; public HeaderRenderer() { setOpaque(true); // setForeground(UIManager.getColor("TableHeader.foreground")); // setBackground(UIManager.getColor("TableHeader.background")); // setBorder(UIManager.getBorder("TableHeader.cellBorder")); // better for Macs? setForeground(Color.black); setBackground(GColorD.getAwtColor( GeoGebraColorConstants.TABLE_BACKGROUND_COLOR_HEADER)); // setBorder(BorderFactory.createBevelBorder(0)); setBorder(BorderFactory.createMatteBorder(0, 0, 1, 1, GColorD .getAwtColor(GeoGebraColorConstants.TABLE_GRID_COLOR))); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { setFont(table.getFont()); setText((value == null) ? "" : " " + value.toString()); if ((row == -1) && (column == 0)) { // No. column header int width_header = getPreferredSize().width + 2; int width_numbers = 25; if (data.getRowCount() > 0) { TableCellRenderer r = table .getCellRenderer(data.getRowCount() - 1, 0); Component c = r.getTableCellRendererComponent(table, ((ConstructionTableDataD) data) .getValueAt(data.getRowCount() - 1, 0), false, false, data.getRowCount() - 1, 0); // width = Math.max(width, c.getPreferredSize().width +2); width_numbers = Math.max(width_numbers, c.getPreferredSize().width + 2); } // tableColumns[0].setMaxWidth(width); tableColumns[0].setMinWidth(width_numbers); tableColumns[0] .setMaxWidth(Math.max(width_numbers, width_header)); // tableColumns[0].setPreferredWidth(width); } return this; } } public class ConstructionTableDataD extends ConstructionTableData { protected MyGAbstractTableModel ctDataImpl; protected ConstructionTableData ctData = this; public ConstructionTableDataD(SetLabels gui) { super(gui); ctDataImpl = new MyGAbstractTableModel(); // rowList = new ArrayList<RowData>(); // geoMap = new HashMap<GeoElement, RowData>(); } @Override public GAbstractTableModel getImpl() { return ctDataImpl; } boolean moveInConstructionList(int fromIndex, int toIndex) { // kernel.detach(this); boolean changed = kernel.moveInConstructionList(fromIndex, toIndex); // kernel.attach(this); // reorder rows in this view ConstructionElement ce = kernel.getConstructionElement(toIndex); GeoElementND[] geos = ce.getGeoElements(); for (int i = 0; i < geos.length; ++i) { remove(geos[i].toGeoElement()); add(geos[i].toGeoElement()); } return changed; } public GeoElement getGeoElement(int row) { return rowList.get(row).getGeo(); } @Override public void repaintView() { repaintScrollpane(); } private Color getColorAt(int nRow, int nCol) { try { if (useColors) { return GColorD.getAwtColor( rowList.get(nRow).getGeo().getAlgebraColor()); } return Color.black; } catch (Exception e) { return Color.black; } } public Object getValueAt(int nRow, int nCol) { if (nRow < 0 || nRow >= getRowCount()) { return ""; } switch (nCol) { case 0: return rowList.get(nRow).getIndex() + ""; case 1: return rowList.get(nRow).getName(); case 2: GImageIconD toolbarIcon = (GImageIconD) rowList.get(nRow) .getToolbarIcon(); return (toolbarIcon == null) ? null : toolbarIcon.getImpl(); case 3: return rowList.get(nRow).getDescription(); case 4: return rowList.get(nRow).getDefinition(); case 5: return rowList.get(nRow).getAlgebra(); case 7: return Boolean.valueOf(rowList.get(nRow).getCPVisible()); case 6: return rowList.get(nRow).getCaption(); } return ""; } // no html code but plain text public String getPlainTextAt(int nRow, int nCol) { if (nRow < 0 || nRow >= getRowCount()) { return ""; } switch (nCol) { case 0: return "" + (rowList.get(nRow).getGeo().getConstructionIndex() + 1); case 1: return ""; case 2: return rowList.get(nRow).getGeo().getNameDescription(); case 3: return rowList.get(nRow).getGeo().getDefinitionDescription( StringTemplate.defaultTemplate); case 4: return rowList.get(nRow).getGeo() .getDefinition(StringTemplate.defaultTemplate); case 5: return rowList.get(nRow).getGeo() .getAlgebraDescriptionDefault(); case 7: return rowList.get(nRow).getCPVisible() + ""; } return ""; } // html code without <html> tags public String getPlainHTMLAt(int nRow, int nCol) { /* * Only one toolbar should be displayed for each step, even if * multiple substeps are present in a step (i.e. more rows). For * that, we calculate the index for the current and the previous row * and check if they are equal. */ int index; int prevIndex; index = (nRow < 0) ? -1 : data.getConstructionIndex(nRow); prevIndex = (nRow < 1) ? -1 : data.getConstructionIndex(nRow - 1); if (nRow < 0 || nRow >= getRowCount()) { return ""; } switch (nCol) { case 0: return "" + (rowList.get(nRow).getGeo().getConstructionIndex() + 1); case 1: return rowList.get(nRow).getGeo().getNameDescriptionHTML(false, false); case 2: { // Displaying toolbar icons in the list on demand. int m; // Markus' idea to find the correct icon: // 1) check if an object has a parent algorithm: GeoElement ge = rowList.get(nRow).getGeo(); if (ge.getParentAlgorithm() != null) { // 2) if it has a parent algorithm and its modeID returned // is > -1, then use this one: m = ge.getParentAlgorithm().getRelatedModeID(); } // 3) otherwise use the modeID of the GeoElement itself: else { m = rowList.get(nRow).getGeo().getRelatedModeID(); } if (m == -1 || index == prevIndex) { return ""; } ImageIcon icon = ((AppD) app).getModeIcon(m); Image img1 = icon.getImage(); BufferedImage img2 = toBufferedImage(img1); String base64 = GgbAPID.base64encode(img2, 72); return "<img src=\"data:image/png;base64," + base64 + "\">"; } case 3: return rowList.get(nRow).getGeo().getDescriptionHTML(false); case 4: return rowList.get(nRow).getGeo().getDefinitionHTML(false); case 5: return rowList.get(nRow).getGeo() .getAlgebraDescriptionHTMLDefault(); case 6: return rowList.get(nRow).getGeo().getCaptionDescriptionHTML( false, StringTemplate.defaultTemplate); case 7: return rowList.get(nRow).getCPVisible() + ""; } return ""; } /* * The following code has been copy-pasted from * http://forums.sun.com/thread.jspa?threadID=5330345, posted by _Matt_. * Its purpose is to convert an icon to a format which can be copied to * the file system. */ public BufferedImage toBufferedImage(Image i) { if (i instanceof BufferedImage) { return (BufferedImage) i; } Image img; img = new ImageIcon(i).getImage(); BufferedImage b; b = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics g = b.createGraphics(); g.drawImage(img, 0, 0, null); g.dispose(); return b; } public void repaint() { table.repaint(); } @Override public void updateAll() { if (isNotifyUpdateCalled()) { return; } int size = rowList.size(); int toolbarIconHeight = 0; // If displaying toolbarIcon is set, row height must be at least 32 // + 1: if (isColumnInModel(tableColumns[1])) { toolbarIconHeight = 32 + 1; /* * FIXME: The cell content is not aligned vertically centered. I * don't think it is possible to easily solve this because JTable * does not offer a convenient way for vertical alignment of the * cell content. Probably * http://articles.techrepublic.com.com/5100-10878_11-5032692.html * may help, or to use the same technique which is introduced in * GeoGebraCAS. */ } for (int i = 0; i < size; ++i) { RowData row = rowList.get(i); row.updateAll(); // it seems there isn't fit to content (automatic) row height in // JTable, // so we use the most frequent option, 2 lines of text in a row // (this is still better than 1 lines of text in a row) if (row.getIncludesIndex()) { table.setRowHeight(i, Math.max((table.getFont().getSize() * 2 + 16), toolbarIconHeight)); } else { table.setRowHeight(i, Math.max((table.getFont().getSize() * 2 + 12), toolbarIconHeight)); } } ctDataImpl.fireTableRowsUpdated(0, size - 1); } @Override protected void fireTableRowsUpdated(int rowNumber, int rowNumber2) { ctDataImpl.fireTableRowsUpdated(rowNumber, rowNumber2); } @Override final public void updateVisualStyle(GeoElement geo, GProperty prop) { update(geo); } @Override final public void updateAuxiliaryObject(GeoElement geo) { // update(geo); } private class ColumnMovementListener implements TableColumnModelListener { @Override public void columnAdded(TableColumnModelEvent e) { columnsCount++; } @Override public void columnRemoved(TableColumnModelEvent e) { columnsCount--; } @Override public void columnMarginChanged(ChangeEvent e) { // only handle removed / added } @Override public void columnMoved(TableColumnModelEvent e) { // only handle removed / added } @Override public void columnSelectionChanged(ListSelectionEvent e) { // only handle removed / added } } @Override public void reset() { repaint(); } public void setValueAt(Object value, int row, int col) { if ((this.columns[col].getTitle()).equals("Caption")) { data.getRow(row).getGeo().setCaption(value.toString()); data.getRow(row).getGeo().update(); // updateAll(); kernel.notifyRepaint(); } } @Override public int getViewID() { return App.VIEW_CONSTRUCTION_PROTOCOL; } @Override public boolean hasFocus() { return false; } @Override public boolean isShowing() { return false; } public class MyGAbstractTableModel extends GAbstractTableModelD { @Override public int getRowCount() { return ctData.getRowCount(); } @Override public Object getValueAt(int nRow, int nCol) { return ((ConstructionTableDataD) ctData).getValueAt(nRow, nCol); } @Override public int getColumnCount() { return ctData.getColumnCount(); } @Override public boolean isCellEditable(int nRow, int nCol) { return ctData.isCellEditable(nCol); } @Override public void setValueAt(Object value, int row, int col) { ((ConstructionTableDataD) ctData).setValueAt(value, row, col); } } @Override public void fireTableRowsDeleted(int firstRow, int lastRow) { ctDataImpl.fireTableRowsDeleted(firstRow, lastRow); } @Override public void fireTableRowsInserted(int firstRow, int lastRow) { ctDataImpl.fireTableRowsInserted(firstRow, lastRow); } } /************ * PRINTING * ************/ @Override public int print(Graphics graphics, PageFormat pageFormat, int pageIndex0) throws PrinterException { int pageIndex = ((AppD)kernel.getApplication()).getPrintPreview().adjustIndex(pageIndex0); if (!isViewAttached) { data.clearView(); data.notifyAddAll(kernel.getConstruction().getStep()); JFrame tempFrame = new JFrame(); tempFrame.add(this.scrollPane); tempFrame.pack(); } int r = table.getPrintable(PrintMode.FIT_WIDTH, null, null) .print(graphics, pageFormat, pageIndex); return r; } /* * public int print(Graphics pg, PageFormat pageFormat, int pageIndex) * throws PrinterException { Application.debug("CPView-print"); if * (pageIndex >= maxNumPage) return NO_SUCH_PAGE; * * pg.translate((int) pageFormat.getImageableX(), (int) * pageFormat.getImageableY()); int wPage = (int) * pageFormat.getImageableWidth(); int hPage = (int) * pageFormat.getImageableHeight(); pg.setClip(0, 0, wPage, hPage); * * // construction title int y = 0; Font titleFont = * table.getFont().deriveFont(Font.BOLD, table.getFont().getSize() + 2); * pg.setFont(titleFont); pg.setColor(Color.black); // Font fn = * pg.getFont(); FontMetrics fm = pg.getFontMetrics(); * * // title Construction cons = kernel.getConstruction(); String title = * cons.getTitle(); if (!"".equals(title)) { y += fm.getAscent(); * pg.drawString(title, 0, y); } * * // construction author and date String author = cons.getAuthor(); String * date = cons.getDate(); String line = null; if (!"".equals(author)) { line * = author; } if (!"".equals(date)) { if (line == null) line = date; else * line = line + " - " + date; } if (line != null) { * pg.setFont(table.getFont()); // fn = pg.getFont(); fm = * pg.getFontMetrics(); y += fm.getHeight(); pg.drawString(line, 0, y); } * * y += 20; // space between title and table headers * * Font headerFont = table.getFont().deriveFont(Font.BOLD); * pg.setFont(headerFont); fm = pg.getFontMetrics(); * * TableColumnModel colModel = table.getColumnModel(); int nColumns = * colModel.getColumnCount(); int x[] = new int[nColumns]; x[0] = 0; * * int h = fm.getAscent(); y += h; // add ascent of header font because of * baseline * * int nRow, nCol; for (nCol = 0; nCol < nColumns; nCol++) { TableColumn tk * = colModel.getColumn(nCol); int width = tk.getWidth(); // only print * column if there is enough space for it // if (x[nCol] + width > wPage) { * // nColumns = nCol; // break; // } if (nCol + 1 < nColumns) x[nCol + 1] = * x[nCol] + width; title = (String) tk.getIdentifier(); * pg.drawString(title, x[nCol], y); } * * Font tableFont = table.getFont(); pg.setFont(tableFont); fm = * pg.getFontMetrics(); * * int header = y; h = fm.getHeight(); int rowH = Math.max(h, 10); int * rowPerPage = (hPage - header) / rowH; maxNumPage = Math.max( (int) * Math.ceil(table.getRowCount() / (double) rowPerPage), 1); * * // TableModel tblModel = table.getModel(); int iniRow = pageIndex * * rowPerPage; int endRow = Math.min(table.getRowCount(), iniRow + * rowPerPage); int yAdd, maxYadd = 0; * * for (nRow = iniRow; nRow < endRow; nRow++) { y = y + h + maxYadd; // * maxYadd is additional space for indices of // last line maxYadd = 0; for * (nCol = 0; nCol < nColumns; nCol++) { int col = * table.getColumnModel().getColumn(nCol) .getModelIndex(); // Object obj = * data.getValueAt(nRow, col); // String str = obj.toString(); // * pg.drawString(str, x[nCol], y); String str = data.getPlainTextAt(nRow, * col); pg.setColor(data.getColorAt(nRow, col)); yAdd = * Drawable.drawIndexedString(app, (Graphics2D) pg, str, x[nCol], y).y; if * (yAdd > maxYadd) maxYadd = yAdd; } } * * return PAGE_EXISTS; } */ /*************** * HTML export * ***************/ /** * Returns a html representation of the construction protocol. * * @param imgBase64 * : image file to be included */ public String getHTML(String imgBase64) { StringBuilder sb = new StringBuilder(); boolean icon_column; // Let's be W3C compliant: sb.append( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"); sb.append("\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"); sb.append( "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">"); sb.append("<head>\n"); sb.append("<title>"); sb.append(StringUtil.toHTMLString(GeoGebraConstants.APPLICATION_NAME)); sb.append(" - "); sb.append(loc.getMenu("ConstructionProtocol")); sb.append("</title>\n"); sb.append("<meta keywords = \""); sb.append(StringUtil.toHTMLString(GeoGebraConstants.APPLICATION_NAME)); sb.append(" export\">"); sb.append( "<style type=\"text/css\"><!--body { font-family:Arial,Helvetica,sans-serif; margin-left:40px }--></style>"); sb.append( "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">"); sb.append("</head>\n"); sb.append("<body>\n"); // header with title Construction cons = kernel.getConstruction(); String title = cons.getTitle(); if (!"".equals(title)) { sb.append("<h1>"); sb.append(StringUtil.toHTMLString(title)); sb.append("</h1>\n"); } // header with author and date String author = cons.getAuthor(); String date = cons.getDate(); String line = null; if (!"".equals(author)) { line = author; } if (!"".equals(date)) { if (line == null) { line = date; } else { line = line + " - " + date; } } if (line != null) { sb.append("<h3>"); sb.append(StringUtil.toHTMLString(line)); sb.append("</h3>\n"); } // include image file if (imgBase64 != null) { sb.append("<p>\n"); sb.append("<img src=\"data:image/png;base64,"); sb.append(imgBase64); sb.append("\" alt=\""); sb.append(StringUtil .toHTMLString(GeoGebraConstants.APPLICATION_NAME)); sb.append(' '); sb.append(StringUtil.toHTMLString(loc.getMenu("DrawingPad"))); sb.append("\" border=\"1\">\n"); sb.append("</p>\n"); } // table sb.append("<table border=\"1\">\n"); // table headers sb.append("<tr>\n"); TableColumnModel colModel = table.getColumnModel(); int nColumns = colModel.getColumnCount(); for (int nCol = 0; nCol < nColumns; nCol++) { // toolbar icon will only be inserted on request icon_column = table.getColumnName(nCol).equals("ToolbarIcon"); if ((icon_column && addIcons) || !icon_column) { TableColumn tk = colModel.getColumn(nCol); title = (String) tk.getIdentifier(); sb.append("<th>"); sb.append(StringUtil.toHTMLString(title)); sb.append("</th>\n"); } } sb.append("</tr>\n"); // table rows int endRow = table.getRowCount(); for (int nRow = 0; nRow < endRow; nRow++) { sb.append("<tr valign=\"baseline\">\n"); for (int nCol = 0; nCol < nColumns; nCol++) { // toolbar icon will only be inserted on request icon_column = table.getColumnName(nCol).equals("ToolbarIcon"); if ((icon_column && addIcons) || !icon_column) { int col = table.getColumnModel().getColumn(nCol) .getModelIndex(); String str = StringUtil .toHTMLString(((ConstructionTableDataD) data) .getPlainHTMLAt(nRow, col), false); sb.append("<td>"); if ("".equals(str)) { sb.append(" "); // space } else { Color color = ((ConstructionTableDataD) data) .getColorAt(nRow, col); if (color != Color.black) { sb.append("<span style=\"color:#"); sb.append(StringUtil.toHexString( (byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue())); sb.append("\">"); sb.append(str); sb.append("</span>"); } else { sb.append(str); } } sb.append("</td>\n"); } } sb.append("</tr>\n"); } sb.append("</table>\n"); // footer sb.append( ((GuiManagerD) app.getGuiManager()).getCreatedWithHTML()); // append base64 string so that file can be reloaded with File -> Open sb.append( "\n<!-- Base64 string so that this file can be opened in GeoGebra with File -> Open -->"); sb.append("\n<applet style=\"display:none\">"); sb.append("\n<param name=\"ggbBase64\" value=\""); appendBase64((AppD) app, sb); sb.append("\">\n<applet>"); sb.append("\n</body>"); sb.append("\n</html>"); return sb.toString(); } /** * @param app * app * @param sb * sb * @return .ggb file encoded as base64 string */ public static boolean appendBase64(AppD app, StringBuilder sb) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { app.getXMLio().writeGeoGebraFile(baos, false); sb.append(Base64.encodeToString(baos.toByteArray(), false)); } catch (IOException e) { e.printStackTrace(); return false; } return true; } /* * public void add(GeoElement geo) { // TODO Auto-generated method stub } * * public void remove(GeoElement geo) { // TODO Auto-generated method stub } * * public void rename(GeoElement geo) { // TODO Auto-generated method stub } * * public void update(GeoElement geo) { // TODO Auto-generated method stub * data.update(geo); } * * public void updateAuxiliaryObject(GeoElement geo) { // TODO * Auto-generated method stub } * * public void repaintView() { // TODO Auto-generated method stub * data.repaintView(); } * * public void reset() { // TODO Auto-generated method stub } * * public void clearView() { // TODO Auto-generated method stub } * * public void setMode(int mode) { // TODO Auto-generated method stub } * * public void attachView() { kernel.notifyAddAll(this); * kernel.attach(this); } */ @Override public void settingsChanged(AbstractSettings settings) { ConstructionProtocolSettings cps = (ConstructionProtocolSettings) settings; boolean gcv[] = cps.getColsVisibility(); if (gcv != null) { if (gcv.length > 0) { setColsVisibility(gcv); } } update(); ((ConstructionTableDataD) getData()).initView(); repaintScrollpane(); } private void setColsVisibility(boolean[] colsVisibility) { TableColumnModel model = table.getColumnModel(); int k = Math.min(colsVisibility.length, data.columns.length); for (int i = 0; i < k; i++) { TableColumn column = getTableColumns()[i]; model.removeColumn(column); if (colsVisibility[i]) { model.addColumn(column); } // else { // model.removeColumn(column); // } data.columns[i].setVisible(colsVisibility[i]); } } @Override public void setLabels() { this.initGUI(); } }