Subversion Repositories wimsdev

Rev

Rev 7246 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.     Sketch Elements: Chemistry molecular diagram drawing tool.
  3.  
  4.     (c) 2008 Dr. Alex M. Clark
  5.  
  6.     Released as GNUware, under the Gnu Public License (GPL)
  7.  
  8.     See www.gnu.org for details.
  9. */
  10.  
  11. package WIMSchem.ds;
  12.  
  13. import WIMSchem.*;
  14.  
  15. import java.io.*;
  16. import java.awt.*;
  17. import java.util.*;
  18. import java.awt.image.*;
  19. import java.awt.event.*;
  20. import java.awt.datatransfer.*;
  21. import java.awt.dnd.*;
  22. import javax.swing.*;
  23. import javax.swing.event.*;
  24. import javax.swing.table.*;
  25.  
  26. /*
  27.     Main window for datasheet viewing & editing.
  28. */
  29.  
  30. public class DataWindow extends JFrame implements ActionListener, WindowListener, KeyListener, TitleListener
  31. {
  32.     public static final boolean ALLOW=true; // temporary: disable DataSheet use while development is in-progress
  33.  
  34.     String filename=null;
  35.     JMenuBar menubar=null;
  36.     JSplitPane splitter=null;
  37.     DataSheet ds=null;
  38.     DataSheetCache cache=null;
  39.     DataTableModel model=null;
  40.     JTable table=null;
  41.  
  42.     ImageIcon mainIcon=null,mainLogo=null;
  43.  
  44.     int unitRowHeight,curRowMag;
  45.  
  46.     public DataWindow(String LoadFN)
  47.     {
  48.         super("WIMSchem DataSheet");
  49.  
  50.         JFrame.setDefaultLookAndFeelDecorated(false);
  51.         setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
  52.  
  53.         mainIcon=new ImageIcon(getClass().getResource("/images/MainIcon.png"));
  54.         mainLogo=new ImageIcon(getClass().getResource("/images/MainLogo.png"));
  55.  
  56.         setIconImage(mainIcon.getImage());
  57.  
  58.         filename=LoadFN;
  59.         if (filename==null)
  60.         {
  61.             ds=new DataSheet();
  62.             cache=new DataSheetCache();
  63.             ds.appendColumn("Molecule",DataSheet.COLTYPE_MOLECULE,"Molecular structure");
  64.         }
  65.         else loadDataSheet(filename);
  66.  
  67.         setLayout(new BorderLayout());
  68.  
  69.         menubar=new JMenuBar();
  70.         createMenuBar();
  71.  
  72.         model=new DataTableModel(ds,cache,this);
  73.         table=new JTable(model)
  74.         {
  75.             protected boolean processKeyBinding(KeyStroke ks,KeyEvent e,int condition,boolean pressed)
  76.             {
  77.                 if (ks.getKeyCode()==KeyEvent.VK_ESCAPE) return false;
  78.                 if ((e.getModifiersEx() & e.ALT_DOWN_MASK) != 0) return false;
  79.                 if ((e.getModifiersEx() & e.CTRL_DOWN_MASK) != 0) return false;
  80.                 return super.processKeyBinding(ks,e,condition,pressed);
  81.             }
  82.         };
  83.         table.setPreferredScrollableViewportSize(new Dimension(720,530));
  84.         table.setDefaultRenderer(Molecule.class,new TableMoleculeRenderer());
  85.         table.setDefaultEditor(Molecule.class,new TableMoleculeEditor());
  86.         table.setRowSelectionAllowed(true);
  87.         table.setColumnSelectionAllowed(true);
  88.         table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
  89.         table.addKeyListener(this);
  90.         table.setTransferHandler(new TransferTableData(table,model));
  91.  
  92.         unitRowHeight=table.getRowHeight();
  93.         updateRowHeight(2);
  94.  
  95.         add(menubar,BorderLayout.NORTH);
  96.         add(new JScrollPane(table),BorderLayout.CENTER);
  97.  
  98.         pack();
  99.  
  100.         addWindowListener(this);
  101.     }
  102.  
  103.     // ------------------ utility functions --------------------
  104.  
  105.     // loads a new datasheet and replaces the old one, without question
  106.     private void loadDataSheet(String FN)
  107.     {
  108.         DataSheet newDS=null;
  109.         FileInputStream istr=null;
  110.         try
  111.         {
  112.             istr=new FileInputStream(FN);
  113.             if (DataSheetStream.examineIsXMLDS(istr))
  114.             {
  115.                 newDS=DataSheetStream.readXML(istr);
  116.             }
  117.             else if (DataSheetStream.examineIsMDLSDF(istr))
  118.             {
  119.                 newDS=DataSheetStream.readSDF(istr);
  120.                 FN=null;
  121.             }
  122.             else
  123.             {
  124.                 String msg="["+FN+"]\n"+
  125.                            "The file does not appear to be of the XML\n"+
  126.                            "WIMSchem DataSheet format, an MDL SD file.";
  127.                 JOptionPane.showMessageDialog(null,msg,"Open Failed",JOptionPane.ERROR_MESSAGE);
  128.             }
  129.             cache=new DataSheetCache();
  130.         }
  131.         catch (IOException e)
  132.         {
  133.             e.printStackTrace();
  134.             JOptionPane.showMessageDialog(null,e.toString(),"Open Failed",JOptionPane.ERROR_MESSAGE);
  135.         }
  136.         finally
  137.         {
  138.             try {if (istr!=null) istr.close();} catch (IOException e) {}
  139.         }
  140.  
  141.         if (newDS==null)
  142.         {
  143.             ds=new DataSheet(); // blank
  144.             replaceTitle();
  145.             return;
  146.         }
  147.  
  148.         ds=newDS;
  149.         setFilename(FN);
  150.  
  151.         if (model!=null)
  152.         {
  153.             model.setDataSheet(ds,cache);
  154.             model.fireTableStructureChanged();
  155.         }
  156.     }
  157.  
  158.     // assembles the menu items, with appropriate context
  159.     private void createMenuBar()
  160.     {
  161.         boolean allKeys=true;
  162.  
  163.         JMenu menufile=new JMenu("File");
  164.         menufile.setMnemonic(KeyEvent.VK_F);
  165.         menufile.add(menuItem("New",KeyEvent.VK_N,null,key('N',InputEvent.CTRL_MASK,allKeys)));
  166.         menufile.add(menuItem("New Molecule",KeyEvent.VK_M));
  167.         menufile.add(menuItem("Open",KeyEvent.VK_O,null,key('O',InputEvent.CTRL_MASK,allKeys)));
  168.         menufile.add(menuItem("Save",KeyEvent.VK_S,null,key('S',InputEvent.CTRL_MASK,allKeys)));
  169.         menufile.add(menuItem("Save As",KeyEvent.VK_A));
  170.         JMenu menuexport=new JMenu("Export");
  171.         menuexport.setMnemonic(KeyEvent.VK_X);
  172.         menuexport.add(menuItem("as MDL SDF",KeyEvent.VK_S,null,key('S',InputEvent.CTRL_MASK+InputEvent.SHIFT_MASK,allKeys)));
  173.         menufile.add(menuexport);
  174.         menufile.addSeparator();
  175.         menufile.add(menuItem("Quit",KeyEvent.VK_Q,null,key('Q',InputEvent.CTRL_MASK,allKeys)));
  176.  
  177.         JMenu menuedit=new JMenu("Edit");
  178.         menuedit.setMnemonic(KeyEvent.VK_E);
  179.         menuedit.add(menuItem("Undo",KeyEvent.VK_U,null,key('Z',InputEvent.CTRL_MASK,allKeys)));
  180.         menuedit.add(menuItem("Redo",KeyEvent.VK_R,null,key('Z',InputEvent.CTRL_MASK+InputEvent.SHIFT_MASK,allKeys)));
  181.         menuedit.addSeparator();
  182.         menuedit.add(menuItem("Add Row",KeyEvent.VK_A,null,key(KeyEvent.VK_INSERT,0,allKeys)));
  183.         menuedit.add(menuItem("Delete Rows",KeyEvent.VK_D));
  184.         menuedit.add(menuItem("Move Rows Up",KeyEvent.VK_U,null,key(KeyEvent.VK_UP,InputEvent.ALT_MASK,allKeys)));
  185.         menuedit.add(menuItem("Move Rows Down",KeyEvent.VK_D,null,key(KeyEvent.VK_DOWN,InputEvent.ALT_MASK,allKeys)));
  186.         menuedit.addSeparator();
  187.         menuedit.add(menuItem("Copy Rows",KeyEvent.VK_C,null,key('C',InputEvent.CTRL_MASK+InputEvent.SHIFT_MASK,allKeys)));
  188.         menuedit.add(menuItem("Cut Rows",KeyEvent.VK_T,null,key('X',InputEvent.CTRL_MASK+InputEvent.SHIFT_MASK,allKeys)));
  189.         menuedit.add(menuItem("Paste Rows",KeyEvent.VK_P,null,key('V',InputEvent.CTRL_MASK+InputEvent.SHIFT_MASK,allKeys)));
  190.         menuedit.addSeparator();
  191.         menuedit.add(menuItem("Sheet Summary",KeyEvent.VK_S));
  192.         menuedit.add(menuItem("Edit Columns",KeyEvent.VK_C));
  193.  
  194.         JMenu menuview=new JMenu("View");
  195.         menuview.setMnemonic(KeyEvent.VK_V);
  196.         menuview.add(menuItem("Single Line",KeyEvent.VK_L,null,key('1',InputEvent.CTRL_MASK,allKeys)));
  197.         menuview.add(menuItem("Small Height",KeyEvent.VK_S,null,key('2',InputEvent.CTRL_MASK,allKeys)));
  198.         menuview.add(menuItem("Medium Height",KeyEvent.VK_M,null,key('3',InputEvent.CTRL_MASK,allKeys)));
  199.         menuview.add(menuItem("Large Height",KeyEvent.VK_L,null,key('4',InputEvent.CTRL_MASK,allKeys)));
  200.  
  201.         JMenu menuhelp=new JMenu("Help");
  202.         menuhelp.setMnemonic(KeyEvent.VK_H);
  203.         menuhelp.add(menuItem("About",KeyEvent.VK_A));
  204.  
  205.         menubar.removeAll();
  206.         menubar.add(menufile);
  207.         menubar.add(menuedit);
  208.         menubar.add(menuview);
  209.         menubar.add(Box.createHorizontalGlue());
  210.         menubar.add(menuhelp);
  211.     }
  212.  
  213.     private KeyStroke key(char key, int mods, boolean really) {return really ? KeyStroke.getKeyStroke(key,mods) : null;}
  214.     private KeyStroke key(int key, int mods, boolean really) {return really ? KeyStroke.getKeyStroke(key,mods) : null;}
  215.  
  216.     private JMenuItem menuItem(String txt,int key) {return menuItem(txt,key,null,null,true);}
  217.     private JMenuItem menuItem(String txt,int key,Icon icon) {return menuItem(txt,key,icon,null,true);}
  218.     private JMenuItem menuItem(String txt,int key,Icon icon,KeyStroke accel) {return menuItem(txt,key,icon,accel,true);}
  219.     private JMenuItem menuItem(String txt,int key,Icon icon,KeyStroke accel,boolean enabled)
  220.     {
  221.         JMenuItem mi=new JMenuItem(txt,key);
  222.         mi.addActionListener(this);
  223.         mi.setEnabled(enabled);
  224.         if (icon!=null) mi.setIcon(icon);
  225.         if (accel!=null) mi.setAccelerator(accel);
  226.         return mi;
  227.     }
  228.     private JRadioButtonMenuItem radioMenuItem(String txt,int key,boolean sel,ButtonGroup bg)
  229.     {
  230.         JRadioButtonMenuItem mi=new JRadioButtonMenuItem(txt,sel);
  231.         mi.addActionListener(this);
  232.         mi.setMnemonic(key);
  233.         bg.add(mi);
  234.         return mi;
  235.     }
  236.  
  237.     private void updateRowHeight(int Mag)
  238.     {
  239.         curRowMag=Mag;
  240.         table.setRowHeight(unitRowHeight*(curRowMag==2 ? 3 : curRowMag==3 ? 6 : curRowMag==4 ? 12 : 1));
  241.     }
  242.  
  243.     private void setFilename(String FN)
  244.     {
  245.         filename=FN;
  246.         replaceTitle();
  247.     }
  248.  
  249.     public void replaceTitle()
  250.     {
  251.         String title="WIMSchem DataSheet";
  252.         if (ds!=null && ds.isDirty()) title="*"+title;
  253.         if (filename!=null) title+=" - "+new File(filename).getName();
  254.         if (ds!=null && ds.getTitle().length()>0) title+=":"+ds.getTitle();
  255.         setTitle(title);
  256.     }
  257.  
  258.     private void saveCurrent()
  259.     {
  260.         if (filename==null) return;
  261.         try
  262.         {
  263.             FileOutputStream ostr=new FileOutputStream(filename);
  264.             DataSheetStream.writeXML(ostr,ds);
  265.             ostr.close();
  266.             ds.clearDirty();
  267.             replaceTitle();
  268.         }
  269.         catch (IOException e)
  270.         {
  271.             Util.errmsg("Save Failed",e.toString());
  272.         }
  273.     }
  274.  
  275.     // returns true if two datasheets have the same columns
  276.     private boolean sameColumns(DataSheet DS1,DataSheet DS2)
  277.     {
  278.         if (DS1.numCols()!=DS2.numCols()) return false;
  279.         for (int n=0;n<DS1.numCols();n++)
  280.         {
  281.             if (DS1.colName(n).compareTo(DS2.colName(n))!=0) return false;
  282.             if (DS1.colType(n)!=DS2.colType(n)) return false;
  283.             if (DS1.colDescr(n).compareTo(DS2.colDescr(n))!=0) return false;
  284.         }
  285.         return true;
  286.     }
  287.  
  288.     // ------------------ user responses --------------------
  289.  
  290.     private void fileQuit()
  291.     {
  292.         if (ds.isDirty())
  293.         {
  294.             if (JOptionPane.showConfirmDialog(null,
  295.                 "Current datasheet has been modified. Exit without saving?","Quit",
  296.                 JOptionPane.YES_NO_OPTION)!=JOptionPane.YES_OPTION) return;
  297.         }
  298.         dispose();
  299.     }
  300.  
  301.     private void fileNew()
  302.     {
  303.         new DataWindow(null).setVisible(true);
  304.     }
  305.  
  306.     private void fileNewMolecule()
  307.     {
  308.         new MainWindow(null,false).setVisible(true);
  309.     }
  310.  
  311.     private void fileOpen()
  312.     {
  313.         JFileChooser chooser=new JFileChooser(System.getenv().get("PWD"));
  314.         chooser.setDragEnabled(false);
  315.         chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
  316.         chooser.setFileFilter(new FileExtFilter("DataSheet Formats",".xml;.sdf"));
  317.         // !! chooser.setAccessory(new FileMolPreview(chooser));
  318.         if (chooser.showOpenDialog(this)!=JFileChooser.APPROVE_OPTION) return;
  319.         String newfn=chooser.getSelectedFile().getPath();
  320.  
  321.         if (ds.numRows()==0 && !ds.isDirty())
  322.             loadDataSheet(newfn); // replace blank with new thing
  323.         else
  324.             new DataWindow(newfn).setVisible(true); // pop up a new window
  325.     }
  326.  
  327.     private void fileSave()
  328.     {
  329.         if (filename==null) {fileSaveAs(); return;}
  330.         saveCurrent();
  331.     }
  332.  
  333.     private void fileSaveAs()
  334.     {
  335.         JFileChooser chooser=new JFileChooser(System.getenv().get("PWD"));
  336.         chooser.setDragEnabled(false);
  337.         chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
  338.         chooser.setFileFilter(new FileExtFilter("WIMSchem DataSheets",".xml"));
  339.         if (chooser.showSaveDialog(this)!=JFileChooser.APPROVE_OPTION) return;
  340.  
  341.         String fn=chooser.getSelectedFile().getPath();
  342.         if (chooser.getSelectedFile().getName().indexOf('.')<0) fn=fn+".xml";
  343.  
  344.         File newf=new File(fn);
  345.         if (newf.exists())
  346.         {
  347.             if (JOptionPane.showConfirmDialog(null,
  348.                 "Overwrite existing file ["+newf.getName()+"]?","Save As",
  349.                 JOptionPane.YES_NO_OPTION)!=JOptionPane.YES_OPTION) return;
  350.         }
  351.  
  352.         setFilename(fn);
  353.         saveCurrent();
  354.     }
  355.  
  356.     private void fileExportSDF()
  357.     {
  358.         JFileChooser chooser=new JFileChooser(System.getenv().get("PWD"));
  359.         chooser.setDragEnabled(false);
  360.         chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
  361.         chooser.setFileFilter(new FileExtFilter("MDL SD files",".sdf"));
  362.         if (chooser.showSaveDialog(this)!=JFileChooser.APPROVE_OPTION) return;
  363.  
  364.         String fn=chooser.getSelectedFile().getPath();
  365.         if (chooser.getSelectedFile().getName().indexOf('.')<0) fn=fn+".sdf";
  366.  
  367.         File newf=new File(fn);
  368.         if (newf.exists())
  369.         {
  370.             if (JOptionPane.showConfirmDialog(null,
  371.                 "Overwrite existing file ["+newf.getName()+"]?","Save As",
  372.                 JOptionPane.YES_NO_OPTION)!=JOptionPane.YES_OPTION) return;
  373.         }
  374.  
  375.         // !! perhaps a warning if there are multiple molecule fields?
  376.  
  377.         try
  378.         {
  379.             FileOutputStream ostr=new FileOutputStream(fn);
  380.             DataSheetStream.writeSDF(ostr,ds);
  381.             ostr.close();
  382.         }
  383.         catch (IOException e)
  384.         {
  385.             Util.errmsg("Export Failed",e.toString());
  386.         }
  387.     }
  388.  
  389.     // !! TODO: undo/redo should un-dirtify the datasheet in some cases
  390.  
  391.     private void editUndo()
  392.     {
  393.         if (!cache.canUndo()) return;
  394.  
  395.         DataSheet newds=cache.performUndo(ds);
  396.         boolean colmod=!sameColumns(ds,newds);
  397.         ds=newds;
  398.  
  399.         ds.setDirty();
  400.         replaceTitle();
  401.         model.setDataSheet(ds,cache);
  402.         if (colmod) model.fireTableStructureChanged(); else model.fireTableDataChanged();
  403.     }
  404.  
  405.     private void editRedo()
  406.     {
  407.         if (!cache.canRedo()) return;
  408.  
  409.         DataSheet newds=cache.performRedo(ds);
  410.         boolean colmod=!sameColumns(ds,newds);
  411.         ds=newds;
  412.  
  413.         ds.setDirty();
  414.         replaceTitle();
  415.         model.setDataSheet(ds,cache);
  416.         if (colmod) model.fireTableStructureChanged(); else model.fireTableDataChanged();
  417.     }
  418.  
  419.     private void editSummary()
  420.     {
  421.         DialogEditSummary edsumm=new DialogEditSummary(this,ds);
  422.         if (!edsumm.execute()) return;
  423.  
  424.         if (edsumm.resultTitle().equals(ds.getTitle()) && edsumm.resultDescr().equals(ds.getDescription())) return;
  425.  
  426.         ds.setTitle(edsumm.resultTitle());
  427.         ds.setDescription(edsumm.resultDescr());
  428.         ds.setDirty();
  429.         replaceTitle();
  430.     }
  431.  
  432.     private void editColumns()
  433.     {
  434.         DialogEditColumns edcols=new DialogEditColumns(this,ds);
  435.         if (!edcols.execute()) return;
  436.  
  437.         modifyColumns(edcols.resultOldPos(),edcols.resultNewPos(),edcols.resultName(),edcols.resultType(),edcols.resultDescr());
  438.  
  439.         model.fireTableStructureChanged();
  440.         ds.setDirty();
  441.         replaceTitle();
  442.     }
  443.  
  444.     private void addRow()
  445.     {
  446.         cache.cacheUndo(ds);
  447.         ds.appendRow();
  448.         int selcol=table.getSelectedColumn();
  449.         model.fireTableChanged(new TableModelEvent(model));
  450.  
  451.         // !! currently, the INSERT key initiates an edit event...
  452.         table.changeSelection(ds.numRows()-1,selcol<0 ? 0 : selcol,false,false);
  453.  
  454.         ds.setDirty();
  455.         replaceTitle();
  456.     }
  457.  
  458.     private void deleteRows()
  459.     {
  460.         if (table.getSelectedRowCount()==0) return;
  461.         cache.cacheUndo(ds);
  462.         int[] todel=table.getSelectedRows(); // (sorted)
  463.         for (int i=todel.length-1;i>=0;i--)
  464.         {
  465.             ds.deleteRow(i);
  466.             for (int j=i+1;j<todel.length;j++) if (todel[j]>todel[i]) todel[j]--;
  467.         }
  468.         model.fireTableChanged(new TableModelEvent(model));
  469.         ds.setDirty();
  470.         replaceTitle();
  471.     }
  472.  
  473.     private void moveRowsUp()
  474.     {
  475.         if (table.getSelectedRowCount()==0 || ds.numCols()==0) return;
  476.         int[] rows=table.getSelectedRows(); // (is sorted)
  477.         if (rows[0]==0) return; // if selected the top one, nop
  478.  
  479.         cache.cacheUndo(ds);
  480.  
  481.         for (int n=0;n<rows.length;n++) ds.moveRowUp(rows[n]);
  482.  
  483.         model.fireTableChanged(new TableModelEvent(model));
  484.         for (int n=0;n<rows.length;n++) table.changeSelection(rows[n]-1,0,n!=0,false);
  485.         table.setColumnSelectionInterval(0,ds.numCols()-1);
  486.  
  487.         ds.setDirty();
  488.         replaceTitle();
  489.     }
  490.  
  491.     private void moveRowsDown()
  492.     {
  493.         if (table.getSelectedRowCount()==0 || ds.numCols()==0) return;
  494.         int[] rows=table.getSelectedRows(); // (is sorted)
  495.         if (rows[rows.length-1]==ds.numRows()-1) return; // if selected the bottom one, nop
  496.  
  497.         cache.cacheUndo(ds);
  498.  
  499.         for (int n=rows.length-1;n>=0;n--) ds.moveRowDown(rows[n]);
  500.  
  501.         model.fireTableChanged(new TableModelEvent(model));
  502.         for (int n=0;n<rows.length;n++) table.changeSelection(rows[n]+1,0,n!=0,false);
  503.         table.setColumnSelectionInterval(0,ds.numCols()-1);
  504.  
  505.         ds.setDirty();
  506.         replaceTitle();
  507.     }
  508.  
  509.     private void copyRows()
  510.     {
  511.         if (table.getSelectedRowCount()==0) return;
  512.         int[] rn=table.getSelectedRows();
  513.  
  514.         DataSheet copy=new DataSheet();
  515.         for (int n=0;n<ds.numCols();n++)
  516.         {
  517.             copy.appendColumn(ds.colName(n),ds.colType(n),ds.colDescr(n));
  518.         }
  519.         for (int i=0;i<rn.length;i++)
  520.         {
  521.             copy.appendRow();
  522.             for (int j=0;j<ds.numCols();j++) copy.setObject(i,j,ds.getObject(rn[i],j));
  523.         }
  524.  
  525.         StringWriter sw=new StringWriter();
  526.         try
  527.         {
  528.             DataSheetStream.writeXML(new BufferedWriter(sw),copy);
  529.             Clipboard clip=Toolkit.getDefaultToolkit().getSystemClipboard();
  530.             clip.setContents(new StringSelection(sw.toString()),null);
  531.         }
  532.         catch (IOException e)
  533.         {
  534.             e.printStackTrace();
  535.             JOptionPane.showMessageDialog(null,e.toString(),"Copy Failed",JOptionPane.ERROR_MESSAGE);
  536.         }
  537.     }
  538.  
  539.     private void cutRows()
  540.     {
  541.         copyRows();
  542.         deleteRows();
  543.     }
  544.  
  545.     private void pasteRows()
  546.     {
  547.         String cliptext="";
  548.  
  549.         try
  550.         {
  551.             Clipboard clip=Toolkit.getDefaultToolkit().getSystemClipboard();
  552.             Transferable contents=clip.getContents(null);
  553.             if (contents!=null && contents.isDataFlavorSupported(DataFlavor.stringFlavor))
  554.                 cliptext=(String)contents.getTransferData(DataFlavor.stringFlavor);
  555.         }
  556.         catch (UnsupportedFlavorException e)
  557.         {
  558.             JOptionPane.showMessageDialog(null,e.toString(),"Clipboard Read Failed",JOptionPane.ERROR_MESSAGE);
  559.         }
  560.         catch (IOException e)
  561.         {
  562.             JOptionPane.showMessageDialog(null,e.toString(),"Paste Failed",JOptionPane.ERROR_MESSAGE);
  563.         }
  564.  
  565.         DataSheet paste=null;
  566.         try
  567.         {
  568.             if (DataSheetStream.examineIsXMLDS(new BufferedReader(new StringReader(cliptext))))
  569.                 paste=DataSheetStream.readXML(new BufferedReader(new StringReader(cliptext)));
  570.             else if (DataSheetStream.examineIsMDLSDF(new BufferedReader(new StringReader(cliptext))))
  571.                 paste=DataSheetStream.readSDF(new BufferedReader(new StringReader(cliptext)));
  572.         }
  573.         catch (IOException e) {e.printStackTrace(); return;}
  574.  
  575.         if (paste==null)
  576.         {
  577.             JOptionPane.showMessageDialog(null,
  578.                 "Unknown format: must be DataSheet XML or MDL SDF","Paste Failed",JOptionPane.ERROR_MESSAGE);
  579.             return;
  580.         }
  581.  
  582.         cache.cacheUndo(ds);
  583.  
  584.         // handle columns first: find mapping index for each, based on name
  585.         int[] newcolpos=new int[paste.numCols()];
  586.         for (int i=0;i<paste.numCols();i++)
  587.         {
  588.             newcolpos[i]=-1;
  589.             for (int j=0;j<ds.numCols();j++) if (ds.colName(j).compareTo(paste.colName(i))==0) {newcolpos[i]=j; break;}
  590.             if (newcolpos[i]<0) newcolpos[i]=ds.appendColumn(paste.colName(i),paste.colType(i),paste.colDescr(i));
  591.         }
  592.  
  593.         // now paste the new rows
  594.         for (int i=0;i<paste.numRows();i++)
  595.         {
  596.             int rn=ds.appendRow();
  597.             for (int j=0;j<paste.numCols();j++)
  598.             {
  599.                 int cn=newcolpos[j];
  600.                 int ptype=paste.colType(j),dtype=ds.colType(j);
  601.                 String strval="";
  602.  
  603.                 if (ptype==DataSheet.COLTYPE_MOLECULE && dtype==DataSheet.COLTYPE_MOLECULE)
  604.                 {
  605.                     ds.setMolecule(rn,cn,paste.getMolecule(i,j));
  606.                 }
  607.                 else if (ptype==DataSheet.COLTYPE_MOLECULE || dtype==DataSheet.COLTYPE_MOLECULE) {} // not possible
  608.                 else
  609.                 {
  610.                     String val="";
  611.                     if (ptype==DataSheet.COLTYPE_STRING) val=paste.getString(i,j);
  612.                     else if (ptype==DataSheet.COLTYPE_INTEGER) val=String.valueOf(paste.getInteger(i,j));
  613.                     else if (ptype==DataSheet.COLTYPE_REAL) val=String.valueOf(paste.getReal(i,j));
  614.                     else if (ptype==DataSheet.COLTYPE_BOOLEAN) val=paste.getBoolean(i,j) ? "true" : "false";
  615.  
  616.                     try
  617.                     {
  618.                         if (dtype==DataSheet.COLTYPE_STRING) ds.setString(rn,cn,val);
  619.                         else if (dtype==DataSheet.COLTYPE_INTEGER) ds.setInteger(rn,cn,new Integer(val).intValue());
  620.                         else if (dtype==DataSheet.COLTYPE_REAL) ds.setReal(rn,cn,new Double(val).doubleValue());
  621.                         else if (dtype==DataSheet.COLTYPE_BOOLEAN)
  622.                             ds.setBoolean(rn,cn,val.toLowerCase().compareTo("true")==0 ? true : false);
  623.                     }
  624.                     catch (NumberFormatException e) {} // stays null
  625.                 }
  626.             }
  627.         }
  628.  
  629.         model.fireTableChanged(new TableModelEvent(model));
  630.     }
  631.  
  632.     private void helpAbout()
  633.     {
  634.         String msg="WIMSchem v"+MainPanel.VERSION+"\n"+
  635.                    "Molecule drawing tool\n"+
  636.                    "© 2005-2008 Dr. Alex M. Clark\n"+
  637.                    "Released under the Gnu Public\n"+
  638.                    "License (GPL), see www.gnu.org\n"+
  639.                    "Home page and documentation:\n"+
  640.                    "http://sketchel.sf.net\n";
  641.         JOptionPane.showMessageDialog(null,msg,"About WIMSchem",JOptionPane.INFORMATION_MESSAGE,mainLogo);
  642.     }
  643.  
  644.     private void modifyColumns(int[] OldPos,int[] NewPos,String[] Name,int[] Type,String[] Descr)
  645.     {
  646.         int sz=OldPos.length;
  647.  
  648.         // delete those which need to be chopped out
  649.         for (int n=0;n<sz;n++) if (NewPos[n]<0)
  650.         {
  651.             ds.deleteColumn(OldPos[n]);
  652.             for (int i=0;i<sz;i++) if (OldPos[i]>OldPos[n]) OldPos[i]--;
  653.             for (int i=n;i<sz-1;i++)
  654.             {
  655.                 OldPos[i]=OldPos[i+1];
  656.                 NewPos[i]=NewPos[i+1];
  657.                 Name[i]=Name[i+1];
  658.                 Type[i]=Type[i+1];
  659.                 Descr[i]=Descr[i+1];
  660.             }
  661.             n--;
  662.             sz--;
  663.         }
  664.  
  665.         // add the new ones
  666.         for (int n=0;n<sz;n++) if (OldPos[n]<0)
  667.         {
  668.             OldPos[n]=ds.appendColumn(Name[n],Type[n],Descr[n]);
  669.         }
  670.  
  671.         // modify any existing content
  672.         for (int n=0;n<sz;n++)
  673.         {
  674.             ds.changeColumnName(OldPos[n],Name[n],Descr[n]);
  675.             ds.changeColumnType(OldPos[n],Type[n],true);
  676.         }
  677.  
  678.         // now redefine the column order
  679.         int[] reord=new int[sz];
  680.         for (int n=0;n<sz;n++) reord[NewPos[n]]=OldPos[n];
  681.         cache.cacheUndo(ds);
  682.         ds.reorderColumns(reord);
  683.     }
  684.  
  685.     // ------------------ event functions --------------------
  686.  
  687.     public void actionPerformed(ActionEvent e)
  688.     {
  689.         String cmd=e.getActionCommand();
  690.  
  691.         //System.out.println("CMD:["+cmd+"]");
  692.  
  693.         if (e.getSource()==table)
  694.         {
  695.             /* !!
  696.             String[] rc=cmd.split(",");
  697.             editMolecule(Integer.parseInt(rc[0]),Integer.parseInt(rc[1]));
  698.             */
  699.         }
  700.         else if (cmd=="Quit") fileQuit();
  701.         else if (cmd=="New") fileNew();
  702.         else if (cmd=="New Molecule") fileNewMolecule();
  703.         else if (cmd=="Open") fileOpen();
  704.         else if (cmd=="Save") fileSave();
  705.         else if (cmd=="Save As") fileSaveAs();
  706.         else if (cmd=="as MDL SDF") fileExportSDF();
  707.         else if (cmd=="Undo") editUndo();
  708.         else if (cmd=="Redo") editRedo();
  709.         else if (cmd=="Add Row") addRow();
  710.         else if (cmd=="Delete Rows") deleteRows();
  711.         else if (cmd=="Move Rows Up") moveRowsUp();
  712.         else if (cmd=="Move Rows Down") moveRowsDown();
  713.         else if (cmd=="Copy Rows") copyRows();
  714.         else if (cmd=="Cut Rows") cutRows();
  715.         else if (cmd=="Paste Rows") pasteRows();
  716.         else if (cmd=="Sheet Summary") editSummary();
  717.         else if (cmd=="Edit Columns") editColumns();
  718.         else if (cmd=="Single Line") updateRowHeight(1);
  719.         else if (cmd=="Small Height") updateRowHeight(2);
  720.         else if (cmd=="Medium Height") updateRowHeight(3);
  721.         else if (cmd=="Large Height") updateRowHeight(4);
  722.         else if (cmd=="About") helpAbout();
  723.     }
  724.  
  725.     public void windowActivated(WindowEvent e) {}
  726.     public void windowClosed(WindowEvent e) {}
  727.     public void windowClosing(WindowEvent e)
  728.     {
  729.         fileQuit();
  730.     }
  731.     public void windowDeactivated(WindowEvent e) {}
  732.     public void windowDeiconified(WindowEvent e) {}
  733.     public void windowIconified(WindowEvent e) {}
  734.     public void windowOpened(WindowEvent e) {}
  735.  
  736.     public void keyPressed(KeyEvent e) {}
  737.     public void keyReleased(KeyEvent e) {}
  738.     public void keyTyped(KeyEvent e) {}
  739.  
  740.     // Class for allowing molecule, or other table style, data to be dragged into parts of the table.
  741.  
  742.     class TransferTableData extends TransferHandler
  743.     {
  744.         JTable table;
  745.         DataTableModel model;
  746.  
  747.         public TransferTableData(JTable table,DataTableModel model)
  748.         {
  749.             this.table=table;
  750.             this.model=model;
  751.         }
  752.         public boolean canImport(TransferHandler.TransferSupport info)
  753.         {
  754.             if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) return false;
  755.             String data=null;
  756.             try
  757.             {
  758.                 data=(String)info.getTransferable().getTransferData(DataFlavor.stringFlavor);
  759.             }
  760.             catch (InvalidDnDOperationException e)
  761.             {
  762.                 // this is thrown when dragging between different processes; it means we can't actually check the
  763.                 // data here, which is suboptimal, but not the end of the world
  764.                 return true;
  765.             }
  766.             catch (UnsupportedFlavorException e) {return false;}
  767.             catch (IOException e) {}
  768.  
  769.             // now we have string data: see if it is a molecule...
  770.             try
  771.             {
  772.                 Molecule mol=MoleculeStream.readUnknown(new BufferedReader(new StringReader(data)));
  773.                 if (mol!=null)
  774.                 {
  775.                     Point pos=info.getDropLocation().getDropPoint();
  776.                     int col=table.columnAtPoint(pos);
  777.                     int row=table.rowAtPoint(pos);
  778.                     return col>=0 && row>=0 && model.getDataSheet().colType(col)==DataSheet.COLTYPE_MOLECULE;
  779.                 }
  780.             }
  781.             catch (IOException e) {}
  782.  
  783.             // see if it is a datasheet fragment
  784.             // !!
  785.  
  786.             return false;
  787.         }
  788.         public boolean importData(TransferHandler.TransferSupport info)
  789.         {
  790.             if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) return false;
  791.             String data=null;
  792.             try
  793.             {
  794.                 data=(String)info.getTransferable().getTransferData(DataFlavor.stringFlavor);
  795.             }
  796.             catch (InvalidDnDOperationException e) {return false;}
  797.             catch (UnsupportedFlavorException e) {return false;}
  798.             catch (IOException e) {}
  799.  
  800.             // now we have string data: see if it is a molecule...
  801.             try
  802.             {
  803.                 Molecule mol=MoleculeStream.readUnknown(new BufferedReader(new StringReader(data)));
  804.                 if (mol!=null)
  805.                 {
  806.                     Point pos=info.getDropLocation().getDropPoint();
  807.                     int row=table.rowAtPoint(pos);
  808.                     int col=table.columnAtPoint(pos);
  809.                     DataSheet ds=model.getDataSheet();
  810.                     if (row>=0 && col>=0 && ds.colType(col)==DataSheet.COLTYPE_MOLECULE)
  811.                     {
  812.                         model.setValueAt(mol,row,col);
  813.                         model.fireTableDataChanged();
  814.                         return true;
  815.                     }
  816.                     else return false;
  817.                 }
  818.             }
  819.             catch (IOException e) {}
  820.  
  821.             // see if it is a datasheet fragment
  822.             // !!
  823.  
  824.             return false;
  825.         }
  826.     }
  827.  
  828. }
  829.  
  830. // Used to allow other classes to notify that the title may need to change.
  831.  
  832. interface TitleListener
  833. {
  834.     public void replaceTitle();
  835. }
  836.