/* (c) Morten Sickel 2010 - 2011 Licenced under GNU GPL v3 or later */ // $Id: WaterfallApplet.java 762 2011-05-31 07:50:58Z radioecology $ // TODO : Initiering av scrollbar - må hindre at plottet hopper til slutten etter å ha blitt lest inn OK // TODO : End of data set vises når det ikke skulle ha vært vist ifm scrolling. OK // TODO: BUG: Vil ikke scrolle helt til slutten med scrollbar - hopper tilbake til posisjonen den hadde før scroll. import java.applet.AppletContext; import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.swing.*; //import java.util.Date; public class WaterfallApplet extends JApplet { private Waterfall waterfall; private JScrollBar scroller; public static JLabel timestamp; public static JLabel infolabel; static JFrame f; AppletContext appletContext; public static String codebase; public static JButton BtMeta, BtLink, BtSaveHit, BtPrev, BtNext, BtUp, BtDn; public static JToggleButton BtRunwf,BtSpeed; private static final String NEXT_PAGE = "Next"; private static final String PREVIOUS_PAGE = "Prev"; private static final String NEXT_PAGE_AC = "SHOW NEXT PAGE"; private static final String PREV_PAGE_AC = "SHOW PREV PAGE"; private Timer updateScroller; private static final String DOWN_ARROW="D"; private static final String DOWN_ARROW_AC="MOVE_MARKER_DOWN"; private static final String UP_ARROW="U"; private static final String UP_ARROW_AC="MOVE_MARKER_UP"; private long lastscroll=0; // Time of last scroller movement JComboBox CBDataSet; JTextField TFMax; private String unit,selectedsystem="",selectedproject=""; JCheckBox CbLog; Waterfall wf; Legend legend; private boolean fetchall=false; public long getlastscroll(){ return lastscroll; } public void resetlastscroll(){ // for use when a scroll event has been finished lastscroll=0; } public void setScrollmax(int max){ scroller.setMaximum(max); scroller.setVisibleAmount(wf.getH()); } public void setScrollvalue(int val){ scroller.setValue(val); } public void getScrollvalue(){ scroller.getValue(); } public int wftotal(){ if(!(wf==null)){ return wf.getTotal(); }else{ return 1; } } public int wffollower(){ if(!(wf==null)){ return wf.getFollower(); }else{ return 1; } } public void scrollto(){ //System.out.println("-->"+scroller.getValue()); if(scroller.getValue()>0){ wf.drawnew(scroller.getValue(),false); } } public WaterfallApplet () { updateScroller = new Timer(200, new UpdateScroller(this)); updateScroller.start(); } /* Reads parameters from the html file; unit . if set to ch, the waterfallplot is shown with channels as units along the x-axis. In other cases the unit is keV dataset: Name of a dataset to be opened at startup id; id of a spectrum that is to be marked at startup. Id must be within the selected dataset, */ public void init() { System.out.println("WaterfallApplet svn $Revision: 762 $ loaded"); codebase=this.getCodeBase().toString(); unit= getParameter("unit"); unit=unit==null?"keV":unit; buildUI(); String parameter=getParameter("dataset"); if(!(parameter==null)){ selectedproject=""; selectedsystem=""; fetchall=true; // Must load all sets loadCBDataSet(fetchsetlist()); fetchall=false; // Do not want all sets CBDataSet.setSelectedItem(parameter); String selected=(String)CBDataSet.getSelectedItem(); if(selected.equals(parameter)){ startwaterfall(); } } parameter=getParameter("id"); if(!(parameter==null || parameter.equals(""))){ try{ int marked=Integer.valueOf(parameter.trim()).intValue(); wf.setmarked(marked); wf.drawnew(marked+200,true); } catch (NumberFormatException e){e.printStackTrace();} } } public String[] fetchsetlist(){ String [] datasets=new String[500]; // TODO - use a more sensible data type try { String ajaxstring="/ajaxserver.php?a=listsets"; if(fetchall){ ajaxstring+="&fetchall=fetchall"; } if(!(selectedsystem.equals(""))){ ajaxstring+="&system="+URLEncoder.encode(selectedsystem,"utf8"); } if(!(selectedproject.equals(""))){ ajaxstring+="&project="+URLEncoder.encode(selectedproject,"utf8"); } //System.out.println(ajaxstring); URL ajaxserver = new URL(WaterfallApplet.codebase+ajaxstring ); URLConnection conn=ajaxserver.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); int i =0; String decodedString; while ((decodedString = in.readLine()) != null) { datasets[i++] = decodedString; //System.out.println(decodedString); } in.close(); } catch (MalformedURLException e) {e.printStackTrace();} catch(IOException e){e.printStackTrace();} return datasets; } private void loadCBDataSet(String[] list){ CBDataSet.removeAllItems(); for (int i=0;i0?"&inttime="+Integer.toString(wf.getInttime()):""; appletContext.showDocument(new URL(WaterfallApplet.codebase+url),"specter"); }catch (IOException IOe) {IOe.printStackTrace();} } }); BtMeta=new JButton("Data info"); FileInfoViewer fileInfoViewer= new FileInfoViewer(); BtMeta.addActionListener(fileInfoViewer); BtMeta.setEnabled(false); JButton BtSetMax=new JButton("Set"); LegendSetter setLegend=new LegendSetter(); BtSetMax.addActionListener(setLegend); BtSaveHit=new JButton("Save hit"); BtSaveHit.setEnabled(false); BtSaveHit.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { try{ URL ajaxserver = new URL(WaterfallApplet.codebase+"/ajaxserver.php?a=savehit&rawdataid=" +Integer.toString(wf.getMarked())+"&fileid="+Integer.toString(wf.getDatasetid()) ); URLConnection conn=ajaxserver.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); in.close(); } catch (MalformedURLException SaveHitMUe) { SaveHitMUe.printStackTrace();} catch(IOException SaveHitIOe){SaveHitIOe.printStackTrace();} }; }); CbLog= new JCheckBox("Log"); MarkAdjuster markAdjuster=new MarkAdjuster(); java.net.URL upurl=this.getClass().getResource("up_arrow.png"); BtUp = new JButton(new ImageIcon(upurl)); BtUp.setEnabled(false); BtUp.setActionCommand(UP_ARROW_AC); BtUp.addActionListener(markAdjuster); java.net.URL dnurl=this.getClass().getResource("dn_arrow.png"); BtDn = new JButton(new ImageIcon(dnurl)); BtDn.setEnabled(false); BtDn.setActionCommand(DOWN_ARROW_AC); BtDn.addActionListener(markAdjuster); gbc.gridx=0;gbc.gridy=0;gbc.gridwidth=2;gbc.weightx=0.5; Pnorth.add(BtSaveHit,gbc); gbc.gridx=2;gbc.gridy=0;gbc.gridwidth=2;gbc.weightx=0.5; Pnorth.add(BtLink,gbc); gbc.gridx=4;gbc.gridy=0;gbc.gridwidth=1;gbc.weightx=0.5; Pnorth.add(BtUp,gbc); gbc.gridx=5;gbc.gridy=0;gbc.gridwidth=1;gbc.weightx=0.5; Pnorth.add(BtDn,gbc); gbc.gridx=6;gbc.gridy=0;gbc.gridwidth=2;gbc.weightx=0.5; Pnorth.add(new JLabel(" Max value :"),gbc); gbc.gridx=10;gbc.gridy=0;gbc.gridwidth=1;gbc.weightx=0.5; Pnorth.add(BtSetMax,gbc); gbc.gridx=0;gbc.gridy=1;gbc.gridwidth=4; Pnorth.add(timestamp,gbc); gbc.gridx=4;gbc.gridy=1;gbc.gridwidth=3; Pnorth.add(infolabel=new JLabel(" "),gbc); gbc.gridx=7;gbc.gridy=1;gbc.gridwidth=3; Pnorth.add(TFMax,gbc); gbc.gridx=10;gbc.gridy=1; Pnorth.add(CbLog,gbc); Pnorth.doLayout(); add("North", Pnorth); BtRunwf = new JToggleButton("Run"); BtRunwf.setEnabled(false); BtRunwf.setPreferredSize(new Dimension(70, 25)); BtRunwf.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { wf.toggleActive(); wf.endrepeats=0; }; }); BtSpeed = new JToggleButton("Fast"); BtSpeed.setPreferredSize(new Dimension(70, 25)); BtSpeed.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ //wf.readtimer.setDelay(BtSpeed.isSelected()?Waterfall.HISPEED:Waterfall.LOWSPEED); wf.hispeed(BtSpeed.isSelected()); }; }); add("Center", wf); DataPager dataPager=new DataPager(); BtNext = new JButton(NEXT_PAGE); BtNext.setEnabled(false); BtNext.setActionCommand(NEXT_PAGE_AC); BtNext.addActionListener(dataPager); BtPrev = new JButton(PREVIOUS_PAGE); BtPrev.setEnabled(false); BtPrev.setActionCommand(PREV_PAGE_AC); BtPrev.addActionListener(dataPager); JPanel Psouth=new JPanel(new GridBagLayout()); DataSetDialog DSD=new DataSetDialog(); JButton BtReload = new JButton("Reload datasets"); BtReload.addActionListener(DSD); int x=0,y=0; gbc.gridx=x++;gbc.gridy=y;gbc.gridwidth=1; Psouth.add(CBDataSet,gbc); gbc.gridx=x++;gbc.gridy=y;gbc.gridwidth=1; Psouth.add(BtLoad,gbc); gbc.gridx=x++;gbc.gridy=y;gbc.gridwidth=1; Psouth.add(BtRunwf,gbc); gbc.gridx=x++;gbc.gridy=y;gbc.gridwidth=1; Psouth.add(BtSpeed,gbc); x=0; gbc.gridx=x++;gbc.gridy=++y;gbc.gridwidth=1; Psouth.add(BtReload,gbc); gbc.gridx=x++;gbc.gridy=y;gbc.gridwidth=1; Psouth.add(BtMeta,gbc); gbc.gridx=x++;gbc.gridy=y;gbc.gridwidth=1; Psouth.add(BtPrev,gbc); gbc.gridx=x++;gbc.gridy=y;gbc.gridwidth=1; Psouth.add(BtNext,gbc); Psouth.setPreferredSize(new Dimension(300, 70)); Pnorth.setPreferredSize(new Dimension(300, 60)); Psouth.doLayout(); add("South",Psouth); invalidate(); } // This class shows a dialog box for reloading of datasets private class DataSetDialog implements ActionListener{ private JFrame fr; private void initDialog(){ fr=new JFrame(); JPanel pan = new JPanel(new GridBagLayout()); // GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets=new Insets(2,2,0,2); gbc.gridx=0;gbc.gridy=0;gbc.gridwidth=1; String[] nullstring={""}; final JComboBox CbProject=new JComboBox(nullstring); pan.add(new JLabel("Projects :"),gbc); gbc.gridx=1;gbc.gridy=0;gbc.gridwidth=2; pan.add(CbProject,gbc); final JComboBox CbSystem=new JComboBox(nullstring); gbc.gridx=0;gbc.gridy=1;gbc.gridwidth=1; pan.add(new JLabel("Systems :"),gbc); gbc.gridx=1;gbc.gridy=1;gbc.gridwidth=2; pan.add(CbSystem,gbc); gbc.gridx=0;gbc.gridy=2;gbc.gridwidth=1; final JCheckBox CbAll=new JCheckBox("Show all"); pan.add(CbAll,gbc); JButton BtOK=new JButton("OK"); BtOK.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ selectedsystem=""+CbSystem.getSelectedItem(); selectedproject=""+CbProject.getSelectedItem(); fetchall=CbAll.isSelected(); loadCBDataSet(fetchsetlist()); fr.setVisible(false); }; }); JButton BtCancel=new JButton("Cancel"); BtCancel.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ fr.setVisible(false); }; }); gbc.gridx=1;gbc.gridy=2;gbc.gridwidth=1; pan.add(BtOK,gbc); gbc.gridx=2;gbc.gridy=2;gbc.gridwidth=1; pan.add(BtCancel,gbc); pan.doLayout(); fr.add(pan); fr.pack(); fr.setLocationRelativeTo(null); try{ URL ajaxserver = new URL(WaterfallApplet.codebase+"ajaxserver.php?a=listsystems&listtype=text"); URLConnection conn=ajaxserver.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String item,title=""; int i=0; while ((item = in.readLine()) != null) { if(item.equals(":===:")){ title = in.readLine(); item = in.readLine(); // the next item } if(title.equals("system")){CbSystem.addItem(item);} if(title.equals("project")){CbProject.addItem(item);} } in.close(); } catch (MalformedURLException e) {e.printStackTrace();} catch(IOException e){e.printStackTrace();} } public void actionPerformed(ActionEvent e){ if (fr==null){initDialog();} fr.setVisible(true); }; } private void startwaterfall(){ try { URL ajaxserver = new URL(WaterfallApplet.codebase+"/ajaxserver.php?a=getstart&dataset="+CBDataSet.getSelectedItem()+"&offset="+Integer.toString(wf.getH()) ); URLConnection conn=ajaxserver.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); wf.setDatasetid(Integer.valueOf(in.readLine().trim()).intValue()); int startid=Integer.valueOf(in.readLine().trim()).intValue(); String calibdata=in.readLine(); calibdata=calibdata.isEmpty()?in.readLine():calibdata; wf.calibrate(calibdata); wf.drawnew(startid,true); // The end of the first screenfull of data wf.removeMarked(); in.close(); infolabel.setText(""); BtRunwf.setEnabled(wf.getDatasetid()>0); BtPrev.setEnabled(wf.getDatasetid()>0); BtNext.setEnabled(wf.getDatasetid()>0); BtMeta.setEnabled(wf.getDatasetid()>0); } catch (MalformedURLException rde) {rde.printStackTrace();} catch(IOException rde){rde.printStackTrace();} } private class DataLoader implements ActionListener{ public void actionPerformed(ActionEvent e){ startwaterfall(); }; } private class MarkAdjuster implements ActionListener{ // Moves the mark one spectre up or down // Thanks to http://www.java-forums.org/java-applets/18142-closing-popup-jframe-applet.html // where I was looking for something else but came across how to write a common event handler public void actionPerformed(ActionEvent e){ try { String command=e.getActionCommand(); int mark=wf.getMarked(); mark=wf.nextMark(mark,command==DOWN_ARROW_AC); wf.setmarked(mark); } catch(WaterfallException wfe) {System.out.println("Kan ikke justere posisjon");} }; } private class DataPager implements ActionListener{ // Tells the waterfall to go one page up or down public void actionPerformed(ActionEvent e){ String command=e.getActionCommand(); System.out.println('<'+command+'>'); int delta=300; // a bit less than the height, makes it easier to keep the overview delta=command==NEXT_PAGE_AC?delta:-1*delta; int index=wf.getIndex(); wf.drawnew(index+delta,true); }; } private class FileInfoViewer implements ActionListener { // Fetches info on the current dataset, displays it verbatim in a dialogbox. public void actionPerformed(ActionEvent e) { String info=""; try{ URL ajaxserver = new URL(WaterfallApplet.codebase+"/ajaxserver.php?a=fileinfo&fileid="+Integer.toString(wf.getDatasetid()) ); URLConnection conn=ajaxserver.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); info=in.readLine(); in.close(); } catch (MalformedURLException SaveHitMUe) { SaveHitMUe.printStackTrace();} catch(IOException SaveHitIOe){SaveHitIOe.printStackTrace();} int n = JOptionPane.showConfirmDialog( f,info,"Current dataset", JOptionPane.YES_NO_OPTION); if(n == JOptionPane.YES_OPTION){ // Opens up a web page for details /editing of the information try{ appletContext=getAppletContext(); appletContext.showDocument(new URL(WaterfallApplet.codebase+"data.php?table=datafile&id="+Integer.toString(wf.getDatasetid())),"datawindow"); }catch (IOException IOe) {IOe.printStackTrace();} } } } private class LegendSetter implements ActionListener // Sets the scale and log/lin status of the legend { public void actionPerformed(ActionEvent e){ float oldmax=legend.max; try{ legend.max=Float.valueOf(TFMax.getText().trim()).floatValue(); if (legend.max<0){ legend.max=legend.DEFAULTMAX; TFMax.setText(Float.toString(legend.max)); } }catch (NumberFormatException numexp) { TFMax.setText(Float.toString(legend.max)); } if (legend.max != oldmax || legend.getLog()!=CbLog.isSelected()){ // Will not redraw the plot if nothing is changed legend.setLog(CbLog.isSelected()); legend.repaint(); if(wf.getDatasetid()>0){ // No reason to redraw if scale is changed before any data are loaded wf.drawnew(wf.getIndex(),true); } } /* This reloads the dataset from the server. May have backup store in an array. More mem use on the client, less network traffic and less load on the data base server. */ }; } public static void main(String s[]) { f = new JFrame("Waterfall plot"); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} }); WaterfallApplet wfa = new WaterfallApplet(); wfa.buildUI(); f.add("Center", wfa); f.pack(); f.setVisible(true); } public void paintComponent(Graphics g){ g.fillRect(0,0,500,300); } } class UpdateScroller implements ActionListener{ /* Helper class for timed updates */ private WaterfallApplet wfa; public UpdateScroller(WaterfallApplet initwf){ wfa=initwf; } public void actionPerformed(ActionEvent e){ wfa.setScrollmax(wfa.wftotal()); long diff=System.currentTimeMillis()-wfa.getlastscroll(); if(diff>700){ // the scroller has been left alone for 0.7 secs - time for action if(diff>1000){ // adjusts the scroller to whereever the plot is wfa.setScrollvalue(wfa.wftotal()-wfa.wffollower()); wfa.resetlastscroll(); }else{ wfa.scrollto(); // fetches the new data set wfa.resetlastscroll(); // System.out.println(System.currentTimeMillis()-wfa.getlastscroll()); } } } }