Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.appletgui

File AppletJmol.java

 

Coverage histogram

../../img/srcFileCovDistChart0.png
56% of files have more coverage

Code metrics

82
219
23
2
726
548
72
0.33
9.52
11.5
3.13

Classes

Class Line # Actions
AppletJmol 67 210 69
0.00%
AppletJmol.RenderPanel 646 9 3
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.appletgui;
22   
23    import java.awt.BorderLayout;
24    import java.awt.CheckboxMenuItem;
25    import java.awt.Color;
26    import java.awt.Dimension;
27    import java.awt.Font;
28    import java.awt.Frame;
29    import java.awt.Graphics;
30    import java.awt.Menu;
31    import java.awt.MenuBar;
32    import java.awt.MenuItem;
33    import java.awt.Panel;
34    import java.awt.TextArea;
35    import java.awt.TextField;
36    import java.awt.event.ActionEvent;
37    import java.awt.event.ActionListener;
38    import java.awt.event.ItemEvent;
39    import java.awt.event.ItemListener;
40    import java.awt.event.KeyEvent;
41    import java.awt.event.KeyListener;
42    import java.awt.event.WindowAdapter;
43    import java.awt.event.WindowEvent;
44    import java.util.ArrayList;
45    import java.util.List;
46    import java.util.Vector;
47   
48    import jalview.bin.JalviewLite;
49    import jalview.datamodel.AlignmentI;
50    import jalview.datamodel.PDBEntry;
51    import jalview.datamodel.SequenceI;
52    import jalview.io.DataSourceType;
53    import jalview.io.FileParse;
54    import jalview.io.StructureFile;
55    import jalview.schemes.BuriedColourScheme;
56    import jalview.schemes.HelixColourScheme;
57    import jalview.schemes.HydrophobicColourScheme;
58    import jalview.schemes.PurinePyrimidineColourScheme;
59    import jalview.schemes.StrandColourScheme;
60    import jalview.schemes.TaylorColourScheme;
61    import jalview.schemes.TurnColourScheme;
62    import jalview.schemes.UserColourScheme;
63    import jalview.schemes.ZappoColourScheme;
64    import jalview.structure.StructureSelectionManager;
65    import jalview.util.MessageManager;
66   
 
67    public class AppletJmol extends EmbmenuFrame implements
68    // StructureListener,
69    KeyListener, ActionListener, ItemListener
70   
71    {
72    Menu fileMenu = new Menu(MessageManager.getString("action.file"));
73   
74    Menu viewMenu = new Menu(MessageManager.getString("action.view"));
75   
76    Menu coloursMenu = new Menu(MessageManager.getString("action.colour"));
77   
78    Menu chainMenu = new Menu(MessageManager.getString("action.show_chain"));
79   
80    Menu helpMenu = new Menu(MessageManager.getString("action.help"));
81   
82    MenuItem mappingMenuItem = new MenuItem(
83    MessageManager.getString("label.view_mapping"));
84   
85    CheckboxMenuItem seqColour = new CheckboxMenuItem(
86    MessageManager.getString("action.by_sequence"), true);
87   
88    CheckboxMenuItem jmolColour = new CheckboxMenuItem(
89    MessageManager.getString("action.using_jmol"), false);
90   
91    MenuItem chain = new MenuItem(
92    MessageManager.getString("action.by_chain"));
93   
94    MenuItem charge = new MenuItem(
95    MessageManager.getString("label.charge_cysteine"));
96   
97    MenuItem zappo = new MenuItem(
98    MessageManager.getString("label.colourScheme_zappo"));
99   
100    MenuItem taylor = new MenuItem(
101    MessageManager.getString("label.colourScheme_taylor"));
102   
103    MenuItem hydro = new MenuItem(
104    MessageManager.getString("label.colourScheme_hydrophobic"));
105   
106    MenuItem helix = new MenuItem(
107    MessageManager.getString("label.colourScheme_helixpropensity"));
108   
109    MenuItem strand = new MenuItem(
110    MessageManager.getString("label.colourScheme_strandpropensity"));
111   
112    MenuItem turn = new MenuItem(
113    MessageManager.getString("label.colourScheme_turnpropensity"));
114   
115    MenuItem buried = new MenuItem(
116    MessageManager.getString("label.colourScheme_buriedindex"));
117   
118    MenuItem purinepyrimidine = new MenuItem(
119    MessageManager.getString("label.colourScheme_purine/pyrimidine"));
120   
121    MenuItem user = new MenuItem(
122    MessageManager.getString("label.user_defined_colours"));
123   
124    MenuItem jmolHelp = new MenuItem(
125    MessageManager.getString("label.jmol_help"));
126   
127    Panel scriptWindow;
128   
129    TextField inputLine;
130   
131    TextArea history;
132   
133    RenderPanel renderPanel;
134   
135    AlignmentPanel ap;
136   
137    List<AlignmentPanel> _aps = new ArrayList<>(); // remove? never
138    // added to
139   
140    String fileLoadingError;
141   
142    boolean loadedInline;
143   
144    // boolean colourBySequence = true;
145   
146    FeatureRenderer fr = null;
147   
148    AppletJmolBinding jmb;
149   
150    /**
151    * datasource protocol for access to PDBEntry
152    */
153    String protocol = null;
154   
155    /**
156    * Load a bunch of pdb entries associated with sequences in the alignment and
157    * display them - aligning them if necessary.
158    *
159    * @param pdbentries
160    * each pdb file (at least one needed)
161    * @param boundseqs
162    * each set of sequences for each pdb file (must match number of pdb
163    * files)
164    * @param boundchains
165    * the target pdb chain corresponding with each sequence associated
166    * with each pdb file (may be null at any level)
167    * @param align
168    * true/false
169    * @param ap
170    * associated alignment
171    * @param protocol
172    * how to get pdb data
173    */
 
174  0 toggle public AppletJmol(PDBEntry[] pdbentries, SequenceI[][] boundseqs,
175    String[][] boundchains, boolean align, AlignmentPanel ap,
176    String protocol)
177    {
178  0 throw new Error(MessageManager.getString("error.not_yet_implemented"));
179    }
180   
 
181  0 toggle public AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
182    AlignmentPanel ap, DataSourceType protocol)
183    {
184  0 this.ap = ap;
185  0 jmb = new AppletJmolBinding(this, ap.getStructureSelectionManager(),
186    new PDBEntry[]
187    { pdbentry }, new SequenceI[][] { seq }, protocol);
188  0 jmb.setColourBySequence(true);
189  0 if (pdbentry.getId() == null || pdbentry.getId().length() < 1)
190    {
191  0 if (protocol == DataSourceType.PASTE)
192    {
193  0 pdbentry.setId(
194  0 "PASTED PDB" + (chains == null ? "_" : chains.toString()));
195    }
196    else
197    {
198  0 pdbentry.setId(pdbentry.getFile());
199    }
200    }
201   
202  0 if (JalviewLite.debug)
203    {
204  0 System.err
205    .println("AppletJmol: PDB ID is '" + pdbentry.getId() + "'");
206    }
207   
208  0 String alreadyMapped = StructureSelectionManager
209    .getStructureSelectionManager(ap.av.applet)
210    .alreadyMappedToFile(pdbentry.getId());
211  0 StructureFile reader = null;
212  0 if (alreadyMapped != null)
213    {
214  0 reader = StructureSelectionManager
215    .getStructureSelectionManager(ap.av.applet)
216    .setMapping(seq, chains, pdbentry.getFile(), protocol, null);
217    // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW?
218    // FOR NOW, LETS JUST OPEN A NEW WINDOW
219    }
220  0 MenuBar menuBar = new MenuBar();
221  0 menuBar.add(fileMenu);
222  0 fileMenu.add(mappingMenuItem);
223  0 menuBar.add(viewMenu);
224  0 mappingMenuItem.addActionListener(this);
225  0 viewMenu.add(chainMenu);
226  0 menuBar.add(coloursMenu);
227  0 menuBar.add(helpMenu);
228   
229  0 charge.addActionListener(this);
230  0 hydro.addActionListener(this);
231  0 chain.addActionListener(this);
232  0 seqColour.addItemListener(this);
233  0 jmolColour.addItemListener(this);
234  0 zappo.addActionListener(this);
235  0 taylor.addActionListener(this);
236  0 helix.addActionListener(this);
237  0 strand.addActionListener(this);
238  0 turn.addActionListener(this);
239  0 buried.addActionListener(this);
240  0 purinepyrimidine.addActionListener(this);
241  0 user.addActionListener(this);
242   
243  0 jmolHelp.addActionListener(this);
244   
245  0 coloursMenu.add(seqColour);
246  0 coloursMenu.add(chain);
247  0 coloursMenu.add(charge);
248  0 coloursMenu.add(zappo);
249  0 coloursMenu.add(taylor);
250  0 coloursMenu.add(hydro);
251  0 coloursMenu.add(helix);
252  0 coloursMenu.add(strand);
253  0 coloursMenu.add(turn);
254  0 coloursMenu.add(buried);
255  0 coloursMenu.add(purinepyrimidine);
256  0 coloursMenu.add(user);
257  0 coloursMenu.add(jmolColour);
258  0 helpMenu.add(jmolHelp);
259  0 this.setLayout(new BorderLayout());
260   
261  0 setMenuBar(menuBar);
262   
263  0 renderPanel = new RenderPanel();
264  0 embedMenuIfNeeded(renderPanel);
265  0 this.add(renderPanel, BorderLayout.CENTER);
266  0 scriptWindow = new Panel();
267  0 scriptWindow.setVisible(false);
268    // this.add(scriptWindow, BorderLayout.SOUTH);
269   
270  0 try
271    {
272  0 jmb.allocateViewer(renderPanel, true,
273    ap.av.applet.getName() + "_jmol_",
274    ap.av.applet.getDocumentBase(), ap.av.applet.getCodeBase(),
275    "-applet", scriptWindow, null);
276    } catch (Exception e)
277    {
278  0 System.err.println(
279    "Couldn't create a jmol viewer. Args to allocate viewer were:\nDocumentBase="
280    + ap.av.applet.getDocumentBase() + "\nCodebase="
281    + ap.av.applet.getCodeBase());
282  0 e.printStackTrace();
283  0 dispose();
284  0 return;
285    }
286    // jmb.newJmolPopup(true, "Jmol", true);
287   
288  0 this.addWindowListener(new WindowAdapter()
289    {
 
290  0 toggle @Override
291    public void windowClosing(WindowEvent evt)
292    {
293  0 closeViewer();
294    }
295    });
296  0 pdbentry.setProperty("protocol", protocol);
297  0 if (pdbentry.getFile() != null)
298   
299    {
300    // import structure data from pdbentry.getFile based on given protocol
301  0 if (protocol == DataSourceType.PASTE)
302    {
303    // TODO: JAL-623 : correctly record file contents for matching up later
304    // pdbentry.getProperty().put("pdbfilehash",""+pdbentry.getFile().hashCode());
305  0 loadInline(pdbentry.getFile());
306    }
307  0 else if (protocol == DataSourceType.FILE
308    || protocol == DataSourceType.URL)
309    {
310  0 jmb.jmolViewer.openFile(pdbentry.getFile());
311    }
312    else
313    {
314    // probably CLASSLOADER based datasource..
315    // Try and get a reader on the datasource, and pass that to Jmol
316  0 try
317    {
318  0 java.io.Reader freader = null;
319  0 if (reader != null)
320    {
321  0 if (jalview.bin.JalviewLite.debug)
322    {
323  0 System.err.println(
324    "AppletJmol:Trying to reuse existing PDBfile IO parser.");
325    }
326    // re-use the one we opened earlier
327  0 freader = reader.getReader();
328    }
329  0 if (freader == null)
330    {
331  0 if (jalview.bin.JalviewLite.debug)
332    {
333  0 System.err.println(
334    "AppletJmol:Creating new PDBfile IO parser.");
335    }
336  0 FileParse fp = new FileParse(pdbentry.getFile(), protocol);
337  0 fp.mark();
338    // reader = new mc_view.PDBfile(fp);
339    // could set ID, etc.
340    // if (!reader.isValid())
341    // {
342    // throw new Exception("Invalid datasource.
343    // "+reader.getWarningMessage());
344    // }
345    // fp.reset();
346  0 freader = fp.getReader();
347    }
348  0 if (freader == null)
349    {
350  0 throw new Exception(MessageManager.getString(
351    "exception.invalid_datasource_couldnt_obtain_reader"));
352    }
353  0 jmb.jmolViewer.openReader(pdbentry.getFile(), pdbentry.getId(),
354    freader);
355    } catch (Exception e)
356    {
357    // give up!
358  0 System.err.println("Couldn't access pdbentry id="
359    + pdbentry.getId() + " and file=" + pdbentry.getFile()
360    + " using protocol=" + protocol);
361  0 e.printStackTrace();
362    }
363    }
364    }
365   
366  0 jalview.bin.JalviewLite.addFrame(this, jmb.getViewerTitle(), 400, 400);
367    }
368   
 
369  0 toggle public void loadInline(String string)
370    {
371  0 loadedInline = true;
372  0 jmb.loadInline(string);
373    }
374   
 
375  0 toggle void setChainMenuItems(List<String> chains)
376    {
377  0 chainMenu.removeAll();
378   
379  0 MenuItem menuItem = new MenuItem(MessageManager.getString("label.all"));
380  0 menuItem.addActionListener(this);
381   
382  0 chainMenu.add(menuItem);
383   
384  0 CheckboxMenuItem menuItemCB;
385  0 for (String ch : chains)
386    {
387  0 menuItemCB = new CheckboxMenuItem(ch, true);
388  0 menuItemCB.addItemListener(this);
389  0 chainMenu.add(menuItemCB);
390    }
391    }
392   
393    boolean allChainsSelected = false;
394   
 
395  0 toggle void centerViewer()
396    {
397  0 Vector<String> toshow = new Vector<>();
398  0 for (int i = 0; i < chainMenu.getItemCount(); i++)
399    {
400  0 if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
401    {
402  0 CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
403  0 if (item.getState())
404    {
405  0 toshow.addElement(item.getLabel());
406    }
407    }
408    }
409  0 jmb.showChains(toshow);
410    }
411   
 
412  0 toggle void closeViewer()
413    {
414  0 jmb.closeViewer(true);
415  0 jmb = null;
416  0 this.setVisible(false);
417    }
418   
 
419  0 toggle @Override
420    public void actionPerformed(ActionEvent evt)
421    {
422  0 if (evt.getSource() == mappingMenuItem)
423    {
424  0 jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer(
425    false, null);
426  0 Frame frame = new Frame();
427  0 frame.add(cap);
428   
429  0 StringBuffer sb = new StringBuffer();
430  0 try
431    {
432  0 cap.setText(jmb.printMappings());
433    } catch (OutOfMemoryError ex)
434    {
435  0 frame.dispose();
436  0 System.err.println(
437    "Out of memory when trying to create dialog box with sequence-structure mapping.");
438  0 return;
439    }
440  0 jalview.bin.JalviewLite.addFrame(frame,
441    MessageManager.getString("label.pdb_sequence_mapping"), 550,
442    600);
443    }
444  0 else if (evt.getSource() == charge)
445    {
446  0 setEnabled(charge);
447  0 jmb.colourByCharge();
448    }
449   
450  0 else if (evt.getSource() == chain)
451    {
452  0 setEnabled(chain);
453  0 jmb.colourByChain();
454    }
455  0 else if (evt.getSource() == zappo)
456    {
457  0 setEnabled(zappo);
458  0 jmb.colourByJalviewColourScheme(new ZappoColourScheme());
459    }
460  0 else if (evt.getSource() == taylor)
461    {
462  0 setEnabled(taylor);
463  0 jmb.colourByJalviewColourScheme(new TaylorColourScheme());
464    }
465  0 else if (evt.getSource() == hydro)
466    {
467  0 setEnabled(hydro);
468  0 jmb.colourByJalviewColourScheme(new HydrophobicColourScheme());
469    }
470  0 else if (evt.getSource() == helix)
471    {
472  0 setEnabled(helix);
473  0 jmb.colourByJalviewColourScheme(new HelixColourScheme());
474    }
475  0 else if (evt.getSource() == strand)
476    {
477  0 setEnabled(strand);
478  0 jmb.colourByJalviewColourScheme(new StrandColourScheme());
479    }
480  0 else if (evt.getSource() == turn)
481    {
482  0 setEnabled(turn);
483  0 jmb.colourByJalviewColourScheme(new TurnColourScheme());
484    }
485  0 else if (evt.getSource() == buried)
486    {
487  0 setEnabled(buried);
488  0 jmb.colourByJalviewColourScheme(new BuriedColourScheme());
489    }
490  0 else if (evt.getSource() == purinepyrimidine)
491    {
492  0 jmb.colourByJalviewColourScheme(new PurinePyrimidineColourScheme());
493    }
494  0 else if (evt.getSource() == user)
495    {
496  0 setEnabled(user);
497  0 new UserDefinedColours(this);
498    }
499  0 else if (evt.getSource() == jmolHelp)
500    {
501  0 try
502    {
503  0 ap.av.applet.getAppletContext()
504    .showDocument(new java.net.URL(
505    "http://jmol.sourceforge.net/docs/JmolUserGuide/"),
506    "jmolHelp");
507    } catch (java.net.MalformedURLException ex)
508    {
509    }
510    }
511    else
512    {
513  0 allChainsSelected = true;
514  0 for (int i = 0; i < chainMenu.getItemCount(); i++)
515    {
516  0 if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
517    {
518  0 ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
519    }
520    }
521   
522  0 centerViewer();
523  0 allChainsSelected = false;
524    }
525    }
526   
527    /**
528    * tick or untick the seqColour menu entry or jmoColour entry depending upon
529    * if it was selected or not.
530    *
531    * @param itm
532    */
 
533  0 toggle private void setEnabled(MenuItem itm)
534    {
535  0 jmolColour.setState(itm == jmolColour);
536  0 seqColour.setState(itm == seqColour);
537  0 jmb.setColourBySequence(itm == seqColour);
538    }
539   
 
540  0 toggle @Override
541    public void itemStateChanged(ItemEvent evt)
542    {
543  0 if (evt.getSource() == jmolColour)
544    {
545  0 setEnabled(jmolColour);
546  0 jmb.setColourBySequence(false);
547    }
548  0 else if (evt.getSource() == seqColour)
549    {
550  0 setEnabled(seqColour);
551  0 jmb.colourBySequence(ap);
552    }
553  0 else if (!allChainsSelected)
554    {
555  0 centerViewer();
556    }
557    }
558   
 
559  0 toggle @Override
560    public void keyPressed(KeyEvent evt)
561    {
562  0 if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
563    {
564  0 jmb.eval(inputLine.getText());
565  0 addToHistory("$ " + inputLine.getText());
566  0 inputLine.setText("");
567    }
568   
569    }
570   
 
571  0 toggle @Override
572    public void keyTyped(KeyEvent evt)
573    {
574    }
575   
 
576  0 toggle @Override
577    public void keyReleased(KeyEvent evt)
578    {
579    }
580   
 
581  0 toggle public void updateColours(Object source)
582    {
583  0 AlignmentPanel panel = (AlignmentPanel) source;
584  0 jmb.colourBySequence(panel);
585    }
586   
 
587  0 toggle public void updateTitleAndMenus()
588    {
589  0 if (jmb.hasFileLoadingError())
590    {
591  0 repaint();
592  0 return;
593    }
594  0 setChainMenuItems(jmb.getChainNames());
595  0 jmb.colourBySequence(ap);
596   
597  0 setTitle(jmb.getViewerTitle());
598    }
599   
 
600  0 toggle public void showUrl(String url)
601    {
602  0 try
603    {
604  0 ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
605    "jmolOutput");
606    } catch (java.net.MalformedURLException ex)
607    {
608    }
609    }
610   
611    Panel splitPane = null;
612   
 
613  0 toggle public void showConsole(boolean showConsole)
614    {
615  0 if (showConsole)
616    {
617  0 remove(renderPanel);
618  0 splitPane = new Panel();
619   
620  0 splitPane.setLayout(new java.awt.GridLayout(2, 1));
621  0 splitPane.add(renderPanel);
622  0 splitPane.add(scriptWindow);
623  0 scriptWindow.setVisible(true);
624  0 this.add(splitPane, BorderLayout.CENTER);
625  0 splitPane.setVisible(true);
626  0 splitPane.validate();
627    }
628    else
629    {
630  0 scriptWindow.setVisible(false);
631  0 remove(splitPane);
632  0 add(renderPanel, BorderLayout.CENTER);
633  0 splitPane = null;
634    }
635  0 validate();
636    }
637   
 
638  0 toggle public float[][] functionXY(String functionName, int x, int y)
639    {
640  0 return null;
641    }
642   
643    // /End JmolStatusListener
644    // /////////////////////////////
645   
 
646    class RenderPanel extends Panel
647    {
648    Dimension currentSize = new Dimension();
649   
 
650  0 toggle @Override
651    public void update(Graphics g)
652    {
653  0 paint(g);
654    }
655   
 
656  0 toggle @Override
657    public void paint(Graphics g)
658    {
659  0 currentSize = this.getSize();
660   
661  0 if (jmb.jmolViewer == null)
662    {
663  0 g.setColor(Color.black);
664  0 g.fillRect(0, 0, currentSize.width, currentSize.height);
665  0 g.setColor(Color.white);
666  0 g.setFont(new Font("Verdana", Font.BOLD, 14));
667  0 g.drawString(MessageManager.getString("label.retrieving_pdb_data"),
668    20, currentSize.height / 2);
669    }
670    else
671    {
672  0 jmb.jmolViewer.renderScreenImage(g, currentSize.width,
673    currentSize.height);
674    }
675    }
676    }
677   
678    /*
679    * @Override public Color getColour(int atomIndex, int pdbResNum, String
680    * chain, String pdbId) { return jmb.getColour(atomIndex, pdbResNum, chain,
681    * pdbId); }
682    *
683    * @Override public String[] getPdbFile() { return jmb.getPdbFile(); }
684    *
685    * @Override public void highlightAtom(int atomIndex, int pdbResNum, String
686    * chain, String pdbId) { jmb.highlightAtom(atomIndex, pdbResNum, chain,
687    * pdbId);
688    *
689    * }
690    *
691    * @Override public void mouseOverStructure(int atomIndex, String strInfo) {
692    * jmb.mouseOverStructure(atomIndex, strInfo);
693    *
694    * }
695    */
 
696  0 toggle public void colourByJalviewColourScheme(UserColourScheme ucs)
697    {
698  0 jmb.colourByJalviewColourScheme(ucs);
699    }
700   
 
701  0 toggle public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment)
702    {
703  0 for (int i = 0; i < _aps.size(); i++)
704    {
705  0 if (_aps.get(i).av.getAlignment() == alignment)
706    {
707  0 return (_aps.get(i));
708    }
709    }
710  0 return ap;
711    }
712   
713    /**
714    * Append the given text to the history object
715    *
716    * @param text
717    */
 
718  0 toggle public void addToHistory(String text)
719    {
720    // actually currently never initialised
721  0 if (history != null)
722    {
723  0 history.append("\n" + text);
724    }
725    }
726    }