sun/swing/SwingLazyValue.js
sun/swing/UIAction.js
sun/text/resources/FormatData.js
-sun/text/resources/FormatData_en.js
+sun/text/resources/en/FormatData_en.js
sun/util/calendar/AbstractCalendar.js
sun/util/calendar/BaseCalendar.js
sun/util/calendar/CalendarDate.js
import jalview.api.AlignmentViewPanel;
//from JalviewLite imports import jalview.api.FeatureRenderer;
import jalview.api.FeatureSettingsControllerI;
+import jalview.api.FeatureSettingsModelI;
import jalview.api.SplitContainerI;
import jalview.api.ViewStyleI;
import jalview.api.analysis.SimilarityParamsI;
import java.awt.print.PageFormat;
import java.awt.print.PrinterJob;
import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
*/
@SuppressWarnings("serial")
public class AlignFrame extends GAlignFrame
- implements AlignFrameI, DropTargetListener,
- IProgressIndicator, AlignViewControllerGuiI, ColourChangeListener
+ implements AlignFrameI, DropTargetListener, IProgressIndicator,
+ AlignViewControllerGuiI, ColourChangeListener
{
public static final int DEFAULT_WIDTH = 700;
/**
* Current filename for this alignment
*/
- private String fileName = null;
+ String fileName = null;
- private File fileObject;
+ File fileObject;
/**
* Creates a new AlignFrame object with specific width and height.
addAlignmentPanel(alignPanel, newPanel);
// setBackground(Color.white); // BH 2019
-
+
if (!Jalview.isHeadlessMode())
{
progressBar = new ProgressBar(this.statusPanel, this.statusBar);
+ // JalviewJS options
statusPanel.setVisible(Jalview.getInstance().getShowStatus());
alignFrameMenuBar.setVisible(Jalview.getInstance().getAllowMenuBar());
}
calculateTree.addActionListener(new ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
if (Desktop.getDesktopPane() != null)
{
this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
- PropertyChangeListener serviceListener = (Platform.isJS() ? null
- : addServiceListeners());
- addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+ if (!Platform.isJS())
{
- @Override
- public void internalFrameClosed(
- javax.swing.event.InternalFrameEvent evt)
- {
- // System.out.println("deregistering discoverer listener");
- if (serviceListener != null)
- {
- Desktop.getInstance().removeJalviewPropertyChangeListener(
- "services", serviceListener);
- }
- closeMenuItem_actionPerformed(true);
- }
- });
-
+ addServiceListeners();
+ }
setGUINucleotide();
}
new ViewSetProvider()
{
+
@Override
public AlignmentPanel[] getAllAlignmentPanels()
{
}, selviews, new ItemListener()
{
+
@Override
public void itemStateChanged(ItemEvent e)
{
}
addFocusListener(new FocusAdapter()
{
+
@Override
public void focusGained(FocusEvent e)
{
{
addKeyListener(new KeyAdapter()
{
+
@Override
public void keyPressed(KeyEvent evt)
{
{
case KeyEvent.VK_ESCAPE: // escape key
- deselectAllSequenceMenuItem_actionPerformed(null);
-
+ // alignPanel.deselectAllSequences();
+ alignPanel.deselectAllSequences();
break;
case KeyEvent.VK_DOWN:
}
}
+
@Override
public void keyReleased(KeyEvent evt)
{
}
/* Set up intrinsic listeners for dynamically generated GUI bits. */
- private PropertyChangeListener addServiceListeners()
+ private void addServiceListeners()
{
- PropertyChangeListener serviceListener = new PropertyChangeListener()
+ final java.beans.PropertyChangeListener thisListener;
+ Desktop.getInstance().addJalviewPropertyChangeListener("services",
+ thisListener = new java.beans.PropertyChangeListener()
+ {
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // // System.out.println("Discoverer property change.");
+ // if (evt.getPropertyName().equals("services"))
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+
+
+ @Override
+ public void run()
+ {
+ System.err.println(
+ "Rebuild WS Menu for service change");
+ BuildWebServiceMenu();
+ }
+
+ });
+ }
+ }
+ });
+ addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
{
+
@Override
- public void propertyChange(PropertyChangeEvent evt)
+ public void internalFrameClosed(
+ javax.swing.event.InternalFrameEvent evt)
{
- {
- SwingUtilities.invokeLater(new Runnable()
- {
-
- @Override
- public void run()
- {
- System.err.println("Rebuild WS Menu for service change");
- BuildWebServiceMenu();
- }
- });
- }
+ // System.out.println("deregistering discoverer listener");
+ Desktop.getInstance().removeJalviewPropertyChangeListener(
+ "services", thisListener);
+ closeMenuItem_actionPerformed(true);
}
- };
-
- Desktop.getInstance().addJalviewPropertyChangeListener("services",
- serviceListener);
+ });
// Finally, build the menu once to get current service state
new Thread(new Runnable()
{
+
@Override
public void run()
{
BuildWebServiceMenu();
}
}).start();
- return serviceListener;
}
/**
* operation that affects the data in the current view (selection changed,
* etc) to update the menus to reflect the new state.
*/
+
@Override
public void setMenusForViewport()
{
scaleLeft.setVisible(av.getWrapAlignment());
scaleRight.setVisible(av.getWrapAlignment());
annotationPanelMenuItem.setState(av.isShowAnnotation());
- /*
- * Show/hide annotations only enabled if annotation panel is shown
- */
- syncAnnotationMenuItems();
-
+ // Show/hide annotations only enabled if annotation panel is shown
+ syncAnnotationMenuItems(av.isShowAnnotation());
viewBoxesMenuItem.setSelected(av.getShowBoxes());
viewTextMenuItem.setSelected(av.getShowText());
showNonconservedMenuItem.setSelected(av.getShowUnconserved());
applyToAllGroups.setState(av.getColourAppliesToAllGroups());
showNpFeatsMenuitem.setSelected(av.isShowNPFeats());
showDbRefsMenuitem.setSelected(av.isShowDBRefs());
- autoCalculate.setSelected(av.getAutoCalculateConsensusAndConservation());
+ autoCalculate
+ .setSelected(av.getAutoCalculateConsensusAndConservation());
sortByTree.setSelected(av.sortByTree);
listenToViewSelections.setSelected(av.followSelection);
*
* @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
*/
+
@Override
public void setProgressBar(String message, long id)
{
progressBar.setProgressBar(message, id);
}
+
@Override
public void registerHandler(final long id,
final IProgressIndicatorHandler handler)
*
* @return true if any progress bars are still active
*/
+
@Override
public boolean operationInProgress()
{
* will cause the status bar to be hidden, with possibly undesirable flicker
* of the screen layout.
*/
+
@Override
public void setStatus(String text)
{
return alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer();
}
+
@Override
public void fetchSequence_actionPerformed()
{
new SequenceFetcher(this);
}
+
@Override
public void addFromFile_actionPerformed(ActionEvent e)
{
Desktop.getInstance().inputLocalFileMenuItem_actionPerformed(viewport);
}
+
@Override
public void reload_actionPerformed(ActionEvent e)
{
final FeatureSettings nfs = newframe.featureSettings;
SwingUtilities.invokeLater(new Runnable()
{
+
@Override
public void run()
{
}
}
+
@Override
public void addFromText_actionPerformed(ActionEvent e)
{
.inputTextboxMenuItem_actionPerformed(viewport.getAlignPanel());
}
+
@Override
public void addFromURL_actionPerformed(ActionEvent e)
{
Desktop.getInstance().inputURLMenuItem_actionPerformed(viewport);
}
+
@Override
public void save_actionPerformed(ActionEvent e)
{
* Saves the alignment to a file with a name chosen by the user, if necessary
* warning if a file would be overwritten
*/
+
@Override
public void saveAs_actionPerformed()
{
String shortName = title;
if (shortName.indexOf(File.separatorChar) > -1)
{
- shortName = shortName.substring(
- shortName.lastIndexOf(File.separatorChar) + 1);
+ shortName = shortName
+ .substring(shortName.lastIndexOf(File.separatorChar) + 1);
}
- lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file, shortName);
-
+ lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file,
+ shortName);
+
statusBar.setText(MessageManager.formatMessage(
"label.successfully_saved_to_file_in_format", new Object[]
{ fileName, format }));
-
+
return;
}
AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
Runnable cancelAction = new Runnable()
{
+
@Override
public void run()
{
};
Runnable outputAction = new Runnable()
{
+
@Override
public void run()
{
BackupFiles backupfiles = doBackup ? new BackupFiles(file) : null;
try
{
- String tempFilePath = doBackup ? backupfiles.getTempFilePath() : file;
- PrintWriter out = new PrintWriter(
- new FileWriter(tempFilePath));
+ String tempFilePath = doBackup ? backupfiles.getTempFilePath()
+ : file;
+ PrintWriter out = new PrintWriter(new FileWriter(tempFilePath));
out.print(output);
out.close();
AlignFrame.this.setTitle(file);
statusBar.setText(MessageManager.formatMessage(
- "label.successfully_saved_to_file_in_format", new Object[]
- { fileName, format.getName() }));
+ "label.successfully_saved_to_file_in_format",
+ new Object[]
+ { fileName, format.getName() }));
lastSaveSuccessful = true;
} catch (Exception ex)
{
*
* @param fileFormatName
*/
+
@Override
protected void outputText_actionPerformed(String fileFormatName)
{
AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
Runnable outputAction = new Runnable()
{
+
@Override
public void run()
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void htmlMenuItem_actionPerformed(ActionEvent e)
{
htmlSVG.exportHTML(null);
}
+
@Override
public void bioJSMenuItem_actionPerformed(ActionEvent e)
{
*
* @param f
*/
+
@Override
public void createPNG(File f)
{
*
* @param f
*/
+
@Override
public void createEPS(File f)
{
*
* @param f
*/
+
@Override
public void createSVG(File f)
{
alignPanel.makeAlignmentImage(TYPE.SVG, f);
}
+
@Override
public void pageSetup_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void printMenuItem_actionPerformed(ActionEvent e)
{
thread.start();
}
+
@Override
public void exportFeatures_actionPerformed(ActionEvent e)
{
new AnnotationExporter(alignPanel).exportFeatures();
}
+
@Override
public void exportAnnotations_actionPerformed(ActionEvent e)
{
new AnnotationExporter(alignPanel).exportAnnotations();
}
+
@Override
public void associatedData_actionPerformed(ActionEvent e)
{
final JalviewFileChooser chooser = new JalviewFileChooser(
jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
chooser.setFileView(new JalviewFileView());
- chooser.setDialogTitle(
- MessageManager.getString("label.load_jalview_annotations"));
- chooser.setToolTipText(
- MessageManager.getString("label.load_jalview_annotations"));
+ String tooltip = MessageManager
+ .getString("label.load_jalview_annotations");
+ chooser.setDialogTitle(tooltip);
+ chooser.setToolTipText(tooltip);
chooser.setResponseHandler(0, new Runnable()
{
+
@Override
public void run()
{
*
* @param closeAllTabs
*/
+
@Override
public void closeMenuItem_actionPerformed(boolean closeAllTabs)
{
if (closeAllTabs)
{
+ // JalviewJS-develop only
+ //
+ // if (featureSettings != null && featureSettings.isOpen())
+ // {
+ // featureSettings.close();
+ // featureSettings = null;
+ // }
/*
* this will raise an INTERNAL_FRAME_CLOSED event and this method will
* be called recursively, with the frame now in 'closed' state
}
}
+
@Override
public void addHistoryItem(CommandI command)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void undoMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void redoMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void copy_actionPerformed()
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void pasteNew_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void pasteThis_actionPerformed(ActionEvent e)
{
newGraphGroups.add(q, null);
}
newGraphGroups.set(newann.graphGroup,
- new Integer(++fgroup));
+ Integer.valueOf(++fgroup));
}
newann.graphGroup = newGraphGroups.get(newann.graphGroup)
.intValue();
newGraphGroups.add(q, null);
}
newGraphGroups.set(newann.graphGroup,
- new Integer(++fgroup));
+ Integer.valueOf(++fgroup));
}
newann.graphGroup = newGraphGroups.get(newann.graphGroup)
.intValue();
}
+
@Override
protected void expand_newalign(ActionEvent e)
{
/**
* Action Cut (delete and copy) the selected region
*/
+
@Override
protected void cut_actionPerformed()
{
/**
* Performs menu option to Delete the currently selected region
*/
+
@Override
protected void delete_actionPerformed()
{
return;
}
- Runnable okAction = new Runnable()
- {
- @Override
- public void run()
- {
- SequenceI[] cut = sg.getSequences()
- .toArray(new SequenceI[sg.getSize()]);
-
- addHistoryItem(new EditCommand(
- MessageManager.getString("label.cut_sequences"), Action.CUT,
- cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
- viewport.getAlignment()));
-
- viewport.setSelectionGroup(null);
- viewport.sendSelection();
- viewport.getAlignment().deleteGroup(sg);
-
- viewport.firePropertyChange("alignment", null,
- viewport.getAlignment().getSequences());
- if (viewport.getAlignment().getHeight() < 1)
- {
- try
- {
- AlignFrame.this.setClosed(true);
- } catch (Exception ex)
- {
- }
- }
- }};
+ Runnable okAction = new Runnable()
+ {
+
+ @Override
+ public void run()
+ {
+ SequenceI[] cut = sg.getSequences()
+ .toArray(new SequenceI[sg.getSize()]);
+
+ addHistoryItem(new EditCommand(
+ MessageManager.getString("label.cut_sequences"), Action.CUT,
+ cut, sg.getStartRes(),
+ sg.getEndRes() - sg.getStartRes() + 1,
+ viewport.getAlignment()));
+
+ viewport.setSelectionGroup(null);
+ viewport.sendSelection();
+ viewport.getAlignment().deleteGroup(sg);
+
+ viewport.firePropertyChange("alignment", null,
+ viewport.getAlignment().getSequences());
+ if (viewport.getAlignment().getHeight() < 1)
+ {
+ try
+ {
+ AlignFrame.this.setClosed(true);
+ } catch (Exception ex)
+ {
+ }
+ }
+ }
+ };
/*
* If the cut affects all sequences, prompt for confirmation
*/
- boolean wholeHeight = sg.getSize() == viewport.getAlignment().getHeight();
+ boolean wholeHeight = sg.getSize() == viewport.getAlignment()
+ .getHeight();
boolean wholeWidth = (((sg.getEndRes() - sg.getStartRes())
+ 1) == viewport.getAlignment().getWidth()) ? true : false;
- if (wholeHeight && wholeWidth)
- {
- JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane());
- dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
- Object[] options = new Object[] { MessageManager.getString("action.ok"),
- MessageManager.getString("action.cancel") };
- dialog.showDialog(MessageManager.getString("warn.delete_all"),
- MessageManager.getString("label.delete_all"),
- JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
- options, options[0]);
- } else
- {
- okAction.run();
- }
+ if (wholeHeight && wholeWidth)
+ {
+ JvOptionPane dialog = JvOptionPane
+ .newOptionDialog(Desktop.getDesktopPane());
+ dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
+ Object[] options = new Object[] {
+ MessageManager.getString("action.ok"),
+ MessageManager.getString("action.cancel") };
+ dialog.showDialog(MessageManager.getString("warn.delete_all"),
+ MessageManager.getString("label.delete_all"),
+ JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
+ options, options[0]);
+ }
+ else
+ {
+ okAction.run();
+ }
}
/**
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void deleteGroups_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
{
- SequenceGroup sg = new SequenceGroup(
- viewport.getAlignment().getSequences());
-
- sg.setEndRes(viewport.getAlignment().getWidth() - 1);
- viewport.setSelectionGroup(sg);
- viewport.isSelectionGroupChanged(true);
- viewport.sendSelection();
- // JAL-2034 - should delegate to
- // alignPanel to decide if overview needs
- // updating.
- alignPanel.paintAlignment(false, false);
- PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+ // SequenceGroup sg = new SequenceGroup(
+ // viewport.getAlignment().getSequences());
+ //
+ // sg.setEndRes(viewport.getAlignment().getWidth() - 1);
+ // viewport.setSelectionGroup(sg);
+ // viewport.isSelectionGroupChanged(true);
+ // viewport.sendSelection();
+ // // JAL-2034 - should delegate to
+ // // alignPanel to decide if overview needs
+ // // updating.
+ // alignPanel.paintAlignment(false, false);
+ // PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+ alignPanel.selectAllSequences();
}
/**
* @param e
* DOCUMENT ME!
*/
+
@Override
public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
{
- if (viewport.cursorMode)
- {
- alignPanel.getSeqPanel().keyboardNo1 = null;
- alignPanel.getSeqPanel().keyboardNo2 = null;
- }
- viewport.setSelectionGroup(null);
- viewport.getColumnSelection().clear();
- viewport.setSelectionGroup(null);
- alignPanel.getIdPanel().getIdCanvas().searchResults = null;
- // JAL-2034 - should delegate to
- // alignPanel to decide if overview needs
- // updating.
- alignPanel.paintAlignment(false, false);
- PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
- viewport.sendSelection();
+ alignPanel.deselectAllSequences();
+ // if (viewport.cursorMode)
+ // {
+ // alignPanel.getSeqPanel().keyboardNo1 = null;
+ // alignPanel.getSeqPanel().keyboardNo2 = null;
+ // }
+ // viewport.setSelectionGroup(null);
+ // viewport.getColumnSelection().clear();
+ // viewport.setSelectionGroup(null);
+ // alignPanel.getIdPanel().getIdCanvas().searchResults = null;
+ // // JAL-2034 - should delegate to
+ // // alignPanel to decide if overview needs
+ // // updating.
+ // alignPanel.paintAlignment(false, false);
+ // PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+ // viewport.sendSelection();
}
/**
* @param e
* DOCUMENT ME!
*/
+
@Override
public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
{
if (sg == null)
{
- selectAllSequenceMenuItem_actionPerformed(null);
+ alignPanel.selectAllSequences();
return;
}
viewport.sendSelection();
}
+
@Override
public void invertColSel_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void remove2RightMenuItem_actionPerformed(ActionEvent e)
{
column, viewport.getAlignment());
}
- setStatus(MessageManager
- .formatMessage("label.removed_columns", new String[]
+ setStatus(MessageManager.formatMessage("label.removed_columns",
+ new String[]
{ Integer.valueOf(trimRegion.getSize()).toString() }));
addHistoryItem(trimRegion);
* @param e
* DOCUMENT ME!
*/
+
@Override
public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
{
addHistoryItem(removeGapCols);
- setStatus(MessageManager
- .formatMessage("label.removed_empty_columns", new Object[]
+ setStatus(MessageManager.formatMessage("label.removed_empty_columns",
+ new Object[]
{ Integer.valueOf(removeGapCols.getSize()).toString() }));
// This is to maintain viewport position on first residue
* @param e
* DOCUMENT ME!
*/
+
@Override
public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void padGapsMenuitem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void findMenuItem_actionPerformed(ActionEvent e)
{
/**
* Create a new view of the current alignment.
*/
+
@Override
public void newView_actionPerformed(ActionEvent e)
{
if (viewport.getViewName() == null)
{
- viewport.setViewName(MessageManager
- .getString("label.view_name_original"));
+ viewport.setViewName(
+ MessageManager.getString("label.view_name_original"));
}
/*
/**
* Explode tabbed views into separate windows.
*/
+
@Override
public void expandViews_actionPerformed(ActionEvent e)
{
/**
* Gather views in separate windows back into a tabbed presentation.
*/
+
@Override
public void gatherViews_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void font_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void seqLimit_actionPerformed(ActionEvent e)
{
alignPanel.paintAlignment(true, false);
}
+
@Override
public void idRightAlign_actionPerformed(ActionEvent e)
{
alignPanel.paintAlignment(false, false);
}
+
@Override
public void centreColumnLabels_actionPerformed(ActionEvent e)
{
*
* @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
*/
+
@Override
protected void followHighlight_actionPerformed()
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void colourTextMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void wrapMenuItem_actionPerformed(ActionEvent e)
{
alignPanel.updateLayout();
}
+
@Override
public void showAllSeqs_actionPerformed(ActionEvent e)
{
viewport.showAllHiddenSeqs();
}
+
@Override
public void showAllColumns_actionPerformed(ActionEvent e)
{
viewport.sendSelection();
}
+
@Override
public void hideSelSequences_actionPerformed(ActionEvent e)
{
* jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
* event.ActionEvent)
*/
+
@Override
public void hideAllButSelection_actionPerformed(ActionEvent e)
{
* jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
* .ActionEvent)
*/
+
@Override
public void hideAllSelection_actionPerformed(ActionEvent e)
{
* jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
* ActionEvent)
*/
+
@Override
public void showAllhidden_actionPerformed(ActionEvent e)
{
viewport.sendSelection();
}
+
@Override
public void hideSelColumns_actionPerformed(ActionEvent e)
{
viewport.sendSelection();
}
+
@Override
public void hiddenMarkers_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void scaleAbove_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void scaleLeft_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void scaleRight_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void viewTextMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
{
public FeatureSettings featureSettings;
+
@Override
public FeatureSettingsControllerI getFeatureSettingsUI()
{
return featureSettings;
}
+
@Override
public void featureSettings_actionPerformed(ActionEvent e)
{
+ showFeatureSettingsUI();
+ }
+
+
+ public FeatureSettingsControllerI showFeatureSettingsUI()
+ {
if (featureSettings != null)
{
- featureSettings.close();
+ // featureSettings.closeOldSettings();
featureSettings = null;
}
if (!showSeqFeatures.isSelected())
showSeqFeatures_actionPerformed(null);
}
featureSettings = new FeatureSettings(this);
+ return featureSettings;
}
/**
* @param evt
* DOCUMENT ME!
*/
+
@Override
public void showSeqFeatures_actionPerformed(ActionEvent evt)
{
*
* @param e
*/
+
@Override
public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
{
final boolean setVisible = annotationPanelMenuItem.isSelected();
viewport.setShowAnnotation(setVisible);
- syncAnnotationMenuItems();
+ syncAnnotationMenuItems(setVisible);
alignPanel.updateLayout();
}
- private void syncAnnotationMenuItems()
+ private void syncAnnotationMenuItems(boolean setVisible)
{
- final boolean setVisible = annotationPanelMenuItem.isSelected();
showAllSeqAnnotations.setEnabled(setVisible);
hideAllSeqAnnotations.setEnabled(setVisible);
showAllAlAnnotations.setEnabled(setVisible);
hideAllAlAnnotations.setEnabled(setVisible);
}
-
+
@Override
public void alignmentProperties()
{
JLabel textLabel = new JLabel();
textLabel.setText(content);
textLabel.setBackground(Color.WHITE);
-
+
pane = new JPanel(new BorderLayout());
((JPanel) pane).setOpaque(true);
pane.setBackground(Color.WHITE);
* @param e
* DOCUMENT ME!
*/
+
@Override
public void overviewMenuItem_actionPerformed(ActionEvent e)
{
frame.addInternalFrameListener(
new javax.swing.event.InternalFrameAdapter()
{
+
@Override
public void internalFrameClosed(
javax.swing.event.InternalFrameEvent evt)
alignPanel.setOverviewPanel(overview);
}
+
@Override
public void textColour_actionPerformed()
{
* CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
* ()[0])); }
*/
+
@Override
public void annotationColour_actionPerformed()
{
new AnnotationColourChooser(viewport, alignPanel);
}
+
@Override
public void annotationColumn_actionPerformed(ActionEvent e)
{
*
* @param selected
*/
+
@Override
public void applyToAllGroups_actionPerformed(boolean selected)
{
* @param name
* the name (not the menu item label!) of the colour scheme
*/
+
@Override
public void changeColour_actionPerformed(String name)
{
* otherwise set the chosen colour scheme (or null for 'None')
*/
ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(name,
- viewport,
- viewport.getAlignment(), viewport.getHiddenRepSequences());
+ viewport, viewport.getAlignment(),
+ viewport.getHiddenRepSequences());
changeColour(cs);
}
*
* @param cs
*/
+
@Override
public void changeColour(ColourSchemeI cs)
{
/**
* Show the PID threshold slider panel
*/
+
@Override
protected void modifyPID_actionPerformed()
{
/**
* Show the Conservation slider panel
*/
+
@Override
protected void modifyConservation_actionPerformed()
{
/**
* Action on selecting or deselecting (Colour) By Conservation
*/
+
@Override
public void conservationMenuItem_actionPerformed(boolean selected)
{
/**
* Action on selecting or deselecting (Colour) Above PID Threshold
*/
+
@Override
public void abovePIDThreshold_actionPerformed(boolean selected)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void sortIDMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void sortLengthMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void sortGroupMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
{
}
}
+
@Override
public void autoCalculate_actionPerformed(ActionEvent e)
{
- viewport.setAutoCalculateConsensusAndConservation(autoCalculate.isSelected());
+ viewport.setAutoCalculateConsensusAndConservation(
+ autoCalculate.isSelected());
if (viewport.getAutoCalculateConsensusAndConservation())
{
viewport.firePropertyChange("alignment", null,
}
}
+
@Override
public void sortByTreeOption_actionPerformed(ActionEvent e)
{
viewport.sortByTree = sortByTree.isSelected();
}
+
@Override
protected void listenToViewSelections_actionPerformed(ActionEvent e)
{
sort.add(item);
item.addActionListener(new java.awt.event.ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
sort.add(item);
item.addActionListener(new java.awt.event.ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
* rebuilding in subsequence calls.
*
*/
+
@Override
public void buildSortByAnnotationScoresMenu()
{
Enumeration<String> labels = scoreSorts.keys();
while (labels.hasMoreElements())
{
- addSortByAnnotScoreMenuItem(sortByAnnotScore,
- labels.nextElement());
+ addSortByAnnotScoreMenuItem(sortByAnnotScore, labels.nextElement());
}
sortByAnnotScore.setVisible(scoreSorts.size() > 0);
scoreSorts.clear();
* closed, and adjust the tree leaf to sequence mapping when the alignment is
* modified.
*/
+
@Override
public void buildTreeSortMenu()
{
final JMenuItem item = new JMenuItem(tp.getTitle());
item.addActionListener(new java.awt.event.ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
* @param e
* DOCUMENT ME!
*/
+
@Override
protected void loadTreeMenuItem_actionPerformed(ActionEvent e)
{
chooser.setToolTipText(
MessageManager.getString("label.load_tree_file"));
- chooser.setResponseHandler(0,new Runnable()
+ chooser.setResponseHandler(0, new Runnable()
{
+
@Override
public void run()
{
viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
} catch (Exception ex)
{
- JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), ex.getMessage(),
+ JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
+ ex.getMessage(),
MessageManager
.getString("label.problem_reading_tree_file"),
JvOptionPane.WARNING_MESSAGE);
buildingMenu = true;
new Thread(new Runnable()
{
+
@Override
public void run()
{
final JMenu dismenu = new JMenu("Protein Disorder");
// JAL-940 - only show secondary structure prediction services from
// the legacy server
- Hashtable<String, Vector<ServiceHandle>> services = Discoverer
+ Hashtable<String, Vector<ServiceHandle>> ds = Discoverer
.getInstance().getServices();
if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
// &&
- services != null && (services.size() > 0))
+ ds != null && (ds.size() > 0))
{
// TODO: refactor to allow list of AbstractName/Handler bindings to
// be
// No MSAWS used any more:
// Vector msaws = null; // (Vector)
// Discoverer.services.get("MsaWS");
- Vector<ServiceHandle> secstrpr = services.get("SecStrPred");
+ Vector<ServiceHandle> secstrpr = ds.get("SecStrPred");
if (secstrpr != null)
{
// Add any secondary structure prediction services
for (int i = 0, j = secstrpr.size(); i < j; i++)
{
- final ext.vamsas.ServiceHandle sh = secstrpr
- .get(i);
+ final ext.vamsas.ServiceHandle sh = secstrpr.get(i);
jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
.getServiceClient(sh);
int p = secstrmenu.getItemCount();
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
+
@Override
public void run()
{
* JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
* AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
*
- * @Override public void actionPerformed(ActionEvent e) {
+ * public void actionPerformed(ActionEvent e) {
* jalview.datamodel.AlignmentView
* .testSelectionViews(af.viewport.getAlignment(),
* af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
JMenuItem xtype = new JMenuItem(source);
xtype.addActionListener(new ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
* Construct and display a new frame containing the translation of this
* frame's DNA sequences to their aligned protein (amino acid) equivalents.
*/
+
@Override
public void showTranslation_actionPerformed(GeneticCodeI codeTable)
{
final String errorTitle = MessageManager
.getString("label.implementation_error")
+ MessageManager.getString("label.translation_failed");
- JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msg, errorTitle,
- JvOptionPane.ERROR_MESSAGE);
+ JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msg,
+ errorTitle, JvOptionPane.ERROR_MESSAGE);
return;
}
if (al == null || al.getHeight() == 0)
"label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
final String errorTitle = MessageManager
.getString("label.translation_failed");
- JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msg, errorTitle,
- JvOptionPane.WARNING_MESSAGE);
+ JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msg,
+ errorTitle, JvOptionPane.WARNING_MESSAGE);
}
else
{
}
+
@Override
public void refreshFeatureUI(boolean enableIfNecessary)
{
}
+
@Override
public void dragEnter(DropTargetDragEvent evt)
{
}
+
@Override
public void dragExit(DropTargetEvent evt)
{
}
+
@Override
public void dragOver(DropTargetDragEvent evt)
{
}
+
@Override
public void dropActionChanged(DropTargetDragEvent evt)
{
}
+
@Override
public void drop(DropTargetDropEvent evt)
{
evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
Transferable t = evt.getTransferable();
- final AlignFrame thisaf = this;
final List<Object> files = new ArrayList<>();
List<DataSourceType> protocols = new ArrayList<>();
try
{
Desktop.transferFromDropTarget(files, protocols, evt, t);
- } catch (Exception e)
- {
- e.printStackTrace();
- }
- if (files != null)
+ if (files.size() > 0)
{
new Thread(new Runnable()
{
+
@Override
public void run()
{
+ loadDroppedFiles(files, protocols, evt, t);
+ }
+ }).start();
+ }
+ } catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ protected void loadDroppedFiles(List<Object> files,
+ List<DataSourceType> protocols, DropTargetDropEvent evt,
+ Transferable t)
+ {
try
{
// check to see if any of these files have names matching sequences
if (protocol == DataSourceType.FILE)
{
File fl;
- if (file instanceof File) {
+ if (file instanceof File)
+ {
fl = (File) file;
Platform.cacheFileData(fl);
- } else {
+ }
+ else
+ {
fl = new File(fileName);
}
pdbfn = fl.getName();
int assocfiles = 0;
if (filesmatched.size() > 0)
{
- boolean autoAssociate = Cache
- .getDefault(Preferences.AUTOASSOCIATE_PDBANDSEQS, false);
+ boolean autoAssociate = Cache.getDefault(
+ Preferences.AUTOASSOCIATE_PDBANDSEQS, false);
if (!autoAssociate)
{
String msg = MessageManager.formatMessage(
.toString() });
String ttl = MessageManager.getString(
"label.automatically_associate_structure_files_by_name");
- int choice = JvOptionPane.showConfirmDialog(thisaf, msg,
- ttl, JvOptionPane.YES_NO_OPTION);
+ int choice = JvOptionPane.showConfirmDialog(this, msg, ttl,
+ JvOptionPane.YES_NO_OPTION);
autoAssociate = choice == JvOptionPane.YES_OPTION;
}
if (autoAssociate)
}
if (filesnotmatched.size() > 0)
{
- if (assocfiles > 0 && (Cache.getDefault(
- "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
- || JvOptionPane.showConfirmDialog(thisaf,
+ if (assocfiles > 0 && (Cache
+ .getDefault("AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
+ || JvOptionPane.showConfirmDialog(this,
"<html>" + MessageManager.formatMessage(
"label.ignore_unmatched_dropped_files_info",
new Object[]
ex.printStackTrace();
}
}
- }).start();
- }
- }
/**
* Attempt to load a "dropped" file or URL string, by testing in turn for
{
if (parseFeaturesFile(file, sourceType))
{
+ SplitFrame splitFrame = (SplitFrame) getSplitViewContainer();
+ if (splitFrame != null)
+ {
+ splitFrame.repaint();
+ }
+ else
+ {
alignPanel.paintAlignment(true, true);
}
}
+ }
else
{
new FileLoader().loadFile(viewport, file, sourceType, format);
}
/**
+ * Change the display state for the given feature groups -- Added by BH from
+ * JalviewLite
+ *
+ * @param groups
+ * list of group strings
+ * @param state
+ * visible or invisible
+ */
+
+ public void setFeatureGroupState(String[] groups, boolean state)
+ {
+ jalview.api.FeatureRenderer fr = null;
+ viewport.setShowSequenceFeatures(true);
+ if (alignPanel != null
+ && (fr = alignPanel.getFeatureRenderer()) != null)
+ {
+
+ fr.setGroupVisibility(Arrays.asList(groups), state);
+ alignPanel.getSeqPanel().seqCanvas.repaint();
+ if (alignPanel.overviewPanel != null)
+ {
+ alignPanel.overviewPanel.updateOverviewImage();
+ }
+ }
+ }
+
+ /**
* Method invoked by the ChangeListener on the tabbed pane, in other words
* when a different tabbed pane is selected by the user or programmatically.
*/
+
@Override
public void tabSelectionChanged(int index)
{
viewport = alignPanel.av;
avc.setViewportAndAlignmentPanel(viewport, alignPanel);
setMenusFromViewport(viewport);
+ if (featureSettings != null// && featureSettings.isOpen()
+ && featureSettings.fr.getViewport() != viewport)
+ {
+ if (viewport.isShowSequenceFeatures())
+ {
+ // refresh the featureSettings to reflect UI change
+ showFeatureSettingsUI();
+ }
+ else
+ {
+ // close feature settings for this view.
+ featureSettings.close();
+ }
+ }
+
}
/*
/**
* On right mouse click on view tab, prompt for and set new view name.
*/
+
@Override
public void tabbedPane_mousePressed(MouseEvent e)
{
}
/**
- * Change the display state for the given feature groups -- Added by BH from
- * JalviewLite
- *
- * @param groups
- * list of group strings
- * @param state
- * visible or invisible
- */
- public void setFeatureGroupState(String[] groups, boolean state)
- {
- jalview.api.FeatureRenderer fr = null;
- viewport.setShowSequenceFeatures(true);
- if (alignPanel != null
- && (fr = alignPanel.getFeatureRenderer()) != null)
- {
-
- fr.setGroupVisibility(Arrays.asList(groups), state);
- alignPanel.getSeqPanel().seqCanvas.repaint();
- if (alignPanel.overviewPanel != null)
- {
- alignPanel.overviewPanel.updateOverviewImage();
- }
- }
- }
-
- /**
* Open the dialog for regex description parsing.
*/
+
@Override
protected void extractScores_actionPerformed(ActionEvent e)
{
* jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
* )
*/
+
@Override
protected void showDbRefs_actionPerformed(ActionEvent e)
{
* @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
* ActionEvent)
*/
+
@Override
protected void showNpFeats_actionPerformed(ActionEvent e)
{
Cache.getDefault(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES, true));
trimrs.addActionListener(new ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
fetchr.addActionListener(new ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
new Thread(new Runnable()
{
+
@Override
public void run()
{
alignPanel.alignFrame.featureSettings, isNucleotide);
dbRefFetcher.addListener(new FetchFinishedListenerI()
{
+
@Override
public void finished()
{
+ //
+ // for (FeatureSettingsModelI srcSettings : dbRefFetcher
+ // .getFeatureSettingsModels())
+ // {
+ //
+ // alignPanel.av.mergeFeaturesStyle(srcSettings);
+ // }
AlignFrame.this.setMenusForViewport();
}
});
rfetch.add(fetchr);
new Thread(new Runnable()
{
+
@Override
public void run()
{
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
+
@Override
public void run()
{
- String[] dbclasses = jalview.ws.SequenceFetcher.getInstance()
- .getNonAlignmentSources();
+ jalview.ws.SequenceFetcher
+ sf = jalview.ws.SequenceFetcher.getInstance();
+ String[] dbclasses = sf.getNonAlignmentSources();
List<DbSourceProxy> otherdb;
JMenu dfetch = new JMenu();
JMenu ifetch = new JMenu();
int dbi = 0;
for (String dbclass : dbclasses)
{
- otherdb = jalview.ws.SequenceFetcher.getInstance()
- .getSourceProxy(dbclass);
+ otherdb = sf.getSourceProxy(dbclass);
// add a single entry for this class, or submenu allowing 'fetch
// all' or pick one
if (otherdb == null || otherdb.size() < 1)
if (otherdb.size() == 1)
{
DbSourceProxy src = otherdb.get(0);
- DbSourceProxy[] dassource = new DbSourceProxy[] {
- src };
+ DbSourceProxy[] dassource = new DbSourceProxy[] { src };
fetchr = new JMenuItem(src.getDbSource());
fetchr.addActionListener(new ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
new Thread(new Runnable()
{
+
@Override
public void run()
{
dbRefFetcher
.addListener(new FetchFinishedListenerI()
{
+
@Override
public void finished()
{
+ FeatureSettingsModelI srcSettings = dassource[0]
+ .getFeatureColourScheme();
+ // alignPanel.av.mergeFeaturesStyle(
+ // srcSettings);
AlignFrame.this.setMenusForViewport();
}
});
{ src.getDbSource() }));
fetchr.addActionListener(new ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
new Thread(new Runnable()
{
+
@Override
public void run()
{
dbRefFetcher
.addListener(new FetchFinishedListenerI()
{
+
@Override
public void finished()
{
fetchr.addActionListener(new ActionListener()
{
+
@Override
public void actionPerformed(ActionEvent e)
{
new Thread(new Runnable()
{
+
@Override
public void run()
{
dbRefFetcher
.addListener(new FetchFinishedListenerI()
{
+
@Override
public void finished()
{
/**
* Left justify the whole alignment.
*/
+
@Override
protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
{
/**
* Right justify the whole alignment.
*/
+
@Override
protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
{
viewport.firePropertyChange("alignment", null, al);
}
+
@Override
public void setShowSeqFeatures(boolean b)
{
* jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
* awt.event.ActionEvent)
*/
+
@Override
protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
{
* jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
* .ActionEvent)
*/
+
@Override
protected void showGroupConsensus_actionPerformed(ActionEvent e)
{
* jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
* .event.ActionEvent)
*/
+
@Override
protected void showGroupConservation_actionPerformed(ActionEvent e)
{
* jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
* .event.ActionEvent)
*/
+
@Override
protected void showConsensusHistogram_actionPerformed(ActionEvent e)
{
* jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
* .event.ActionEvent)
*/
+
@Override
protected void showSequenceLogo_actionPerformed(ActionEvent e)
{
alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
}
+
@Override
protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
{
alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
}
+
@Override
protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
{
* jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
* .event.ActionEvent)
*/
+
@Override
protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
{
}
}
+
@Override
protected void createGroup_actionPerformed(ActionEvent e)
{
}
}
+
@Override
protected void unGroup_actionPerformed(ActionEvent e)
{
* @param forAlignment
* update non-sequence-related annotations
*/
+
@Override
protected void setAnnotationsVisibility(boolean visible,
boolean forSequences, boolean forAlignment)
/**
* Store selected annotation sort order for the view and repaint.
*/
+
@Override
protected void sortAnnotations_actionPerformed()
{
*
* @param show
*/
+
@Override
protected void showComplement_actionPerformed(boolean show)
{
* Generate the reverse (optionally complemented) of the selected sequences,
* and add them to the alignment
*/
+
@Override
protected void showReverse_actionPerformed(boolean complement)
{
* AlignFrame is set as currentAlignFrame in Desktop, to allow the script to
* be targeted at this alignment.
*/
+
@Override
protected void runGroovy_actionPerformed()
{
return false;
}
+
@Override
protected void selectHighlightedColumns_actionPerformed(
ActionEvent actionEvent)
}
}
+
@Override
protected void loadVcf_actionPerformed()
{
final AlignFrame us = this;
chooser.setResponseHandler(0, new Runnable()
{
+
@Override
public void run()
{
}
+ private Rectangle lastFeatureSettingsBounds = null;
+
+
+ public void setFeatureSettingsGeometry(Rectangle bounds)
+ {
+ lastFeatureSettingsBounds = bounds;
+ }
+
+
+ public Rectangle getFeatureSettingsGeometry()
+ {
+ return lastFeatureSettingsBounds;
+ }
+
+
+ public void scrollTo(int row, int column)
+ {
+ alignPanel.getSeqPanel().scrollTo(row, column);
+ }
+
+
+ public void scrollToRow(int row)
+ {
+ alignPanel.getSeqPanel().scrollToRow(row);
+ }
+
+
+ public void scrollToColumn(int column)
+ {
+ alignPanel.getSeqPanel().scrollToColumn(column);
+ }
+
/**
* BH 2019 from JalviewLite
*
{
jalview.api.FeatureRenderer fr = null;
if (alignPanel != null
- && (fr = alignPanel
- .getFeatureRenderer()) != null)
+ && (fr = alignPanel.getFeatureRenderer()) != null)
{
List<String> gps = fr.getGroups(visible);
String[] _gps = gps.toArray(new String[gps.size()]);
return null;
}
- public void scrollTo(int row, int column)
- {
- alignPanel.getSeqPanel().scrollTo(row, column);
- }
-
- public void scrollToRow(int row)
- {
- alignPanel.getSeqPanel().scrollToRow(row);
- }
-
- public void scrollToColumn(int column)
- {
- alignPanel.getSeqPanel().scrollToColumn(column);
- }
-
/**
*
* @return list of feature groups on the view
private boolean holdRepaint = false;
+ /**
+ * Checked by SeqCanvas when painting
+ *
+ * @return
+ */
public boolean getHoldRepaint()
{
return holdRepaint;
}
+ /**
+ * Called specifically by Jalview2xml when loading a JPV file
+ *
+ * @param b
+ */
public void setHoldRepaint(boolean b)
{
if (holdRepaint == b)
super.repaint();
}
+ public void selectAllSequences()
+ {
+ selectSequences(av.getAlignment().getSequences());
+ }
+
+ public void deselectAllSequences()
+ {
+ if (av.cursorMode)
+ {
+ getSeqPanel().keyboardNo1 = null;
+ getSeqPanel().keyboardNo2 = null;
+ }
+ av.setSelectionGroup(null);
+ av.getColumnSelection().clear();
+ av.setSelectionGroup(null);
+ getIdPanel().getIdCanvas().searchResults = null;
+ av.sendSelection();
+ // JAL-2034 - should delegate to
+ // alignPanel to decide if overview needs
+ // updating.
+ paintAlignment(false, false);
+ PaintRefresher.Refresh(this, av.getSequenceSetId());
+ }
+
+ public void selectSequences(List<SequenceI> seqs)
+ {
+ SequenceGroup sg = new SequenceGroup(seqs);
+ sg.setEndRes(av.getAlignment().getWidth() - 1);
+ av.setSelectionGroup(sg);
+ av.isSelectionGroupChanged(true);
+ av.sendSelection();
+ // JAL-2034 - should delegate to
+ // alignPanel to decide if overview needs
+ // updating.
+ paintAlignment(false, false);
+ PaintRefresher.Refresh(this, av.getSequenceSetId());
+ }
+
}
entry.setType(PDBEntry.Type.FILE);
entry.setFile(fileName);
sequence.getDatasetSequence().addPDBId(entry);
- Desktop.getInstance().getStructureSelectionManager()
+ Desktop.getStructureSelectionManager()
.registerPDBEntry(entry);
return entry;
}
getIdentifiersOrgData();
- checkURLLinks();
+ if (Jalview.isInteractive())
+ {
+ // disabled for SeqCanvasTest
+ checkURLLinks();
- // Spawn a thread that shows the splashscreen
+ // Spawn a thread that shows the splashscreen
- SwingUtilities.invokeLater(new Runnable()
- {
- @Override
- public void run()
+ SwingUtilities.invokeLater(new Runnable()
{
- new SplashScreen();
- }
- });
+ @Override
+ public void run()
+ {
+ new SplashScreen();
+ }
+ });
- // Thread off a new instance of the file chooser - this reduces the time
- // it
- // takes to open it later on.
- new Thread(new Runnable()
- {
- @Override
- public void run()
+ // Thread off a new instance of the file chooser - this reduces the
+ // time
+ // it
+ // takes to open it later on.
+ new Thread(new Runnable()
{
- Cache.log.debug("Filechooser init thread started.");
- String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
- JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
- fileFormat);
- Cache.log.debug("Filechooser init thread finished.");
- }
- }).start();
- // Add the service change listener
- changeSupport.addJalviewPropertyChangeListener("services",
- new PropertyChangeListener()
- {
-
- @Override
- public void propertyChange(PropertyChangeEvent evt)
+ @Override
+ public void run()
+ {
+ Cache.log.debug("Filechooser init thread started.");
+ String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
+ JalviewFileChooser.forRead(
+ Cache.getProperty("LAST_DIRECTORY"), fileFormat);
+ Cache.log.debug("Filechooser init thread finished.");
+ }
+ }).start();
+ // Add the service change listener
+ changeSupport.addJalviewPropertyChangeListener("services",
+ new PropertyChangeListener()
{
- Cache.log.debug("Firing service changed event for "
- + evt.getNewValue());
- JalviewServicesChanged(evt);
- }
- });
-
- }
+ @Override
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ Cache.log.debug("Firing service changed event for "
+ + evt.getNewValue());
+ JalviewServicesChanged(evt);
+ }
- this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
+ });
- this.addWindowListener(new WindowAdapter()
- {
- @Override
- public void windowClosing(WindowEvent evt)
- {
- quit();
}
- });
- MouseAdapter ma;
- this.addMouseListener(ma = new MouseAdapter()
- {
- @Override
- public void mousePressed(MouseEvent evt)
+ this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
+
+ this.addWindowListener(new WindowAdapter()
{
- if (evt.isPopupTrigger()) // Mac
+ @Override
+ public void windowClosing(WindowEvent evt)
{
- showPasteMenu(evt.getX(), evt.getY());
+ quit();
}
- }
+ });
- @Override
- public void mouseReleased(MouseEvent evt)
+ MouseAdapter ma;
+ this.addMouseListener(ma = new MouseAdapter()
{
- if (evt.isPopupTrigger()) // Windows
+ @Override
+ public void mousePressed(MouseEvent evt)
{
- showPasteMenu(evt.getX(), evt.getY());
+ if (evt.isPopupTrigger()) // Mac
+ {
+ showPasteMenu(evt.getX(), evt.getY());
+ }
}
- }
- });
- desktopPane.addMouseListener(ma);
+
+ @Override
+ public void mouseReleased(MouseEvent evt)
+ {
+ if (evt.isPopupTrigger()) // Windows
+ {
+ showPasteMenu(evt.getX(), evt.getY());
+ }
+ }
+ });
+ desktopPane.addMouseListener(ma);
+ }
} catch (Throwable t)
{
t.printStackTrace();
}
+
}
/**
package javajs.async;
+import java.awt.Toolkit;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
try {
URL url = getInstance()._getURLFromPath(path, true);
if (url == null && !zipOnly) {
- url = Assets.class.getResource(path);
+ url = Assets.class.getClassLoader().getResource(path);
}
if (url != null)
return url.openStream();
--- /dev/null
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+ using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+
+In particular, the Mandarin character 秘 (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+ public static JSThread 秘thisThread;
+
+----------------------------------
+
+
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using
+older source code, and there are some missing methods. For the most part, this is
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the
+distribution. Or (even more preferably) you can do that via a patch submission.
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example,
+that one can call in JavaScript
+
+ new java.util.Hashtable()
+
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+ public void print(int b);
+ public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified"
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple:
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float),
+$D (double), $B (byte) $Z (boolean), and $H (short).
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+ For example: $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+ public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+ Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+ setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+
+becomes
+
+ setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So
+
+ public JLabel(String text) {...}
+
+becomes:
+
+ Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass
+can both have the same field name, such as
+
+ boolean visible;
+
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+ this.visible = super.visible;
+
+while in SwingJS we will see:
+
+ this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private
+array in the class (and are not accessible by reflection). Private parameterless
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not
+possible to cherry-pick methods to be unqualified; only full packages, classes or
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol)
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code"
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript.
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem.
+
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation.
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is,
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif.
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler
+and do not appear in the JavaScript by their names.
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm"
+(like older versions of Java). So, like the older Java schema, the JavaScript
+equivalents of both applets and applications will share all of their static
+fields and methods. This includes java.lang.System.
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with
+multiple applets present, we must be sure to only have static references
+that are "final" or specifically meant to be shared in a JavaScript
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example,
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+ ThreadGroup g = Thread.currentThread().threadGroup();
+ /**
+ * @j2sNative g._myvar = x;
+ *
+ */
+ {
+ myvar = x;
+ }
+}
+
+public String getMyVar() {
+ ThreadGroup g = Thread.currentThread().threadGroup();
+ /**
+ * @j2sNative return g._myvar || null;
+ *
+ */
+ {
+ return myvar;
+ }
+}
+
+ in Java will get and set x the same in JavaScript and in Java.
+
+
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of
+myclass.xxx in Java (and JavaScript).
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project.
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent --
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results.
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical
+user interface. The issue here is complex but workable. In Java there are two background
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to
+Java or the browser by the operating system. UI classes provide a consistent "look and feel"
+for these native objects, rendering them onto the native window canvas and handling all
+user-generated events. They paint the borders, the backgrounds, the highlights, of every
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes.
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel")
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified.
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their
+own individual native peers and thus automatically took on the look and feel provided by the OS.
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this
+design choice posed a huge headache for Swing class developers.
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and
+instead to require that all components be Swing components.
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of
+javax.swing.JComponent. So far, we have found no problem with this.
+
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications.
+
+All work with files should either use Path or File objects exclusively.
+These objects, after a file is read or checked for existence, will already
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists.
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it
+was actually written or if it already exists prior to writing in SwingJS.
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS
+=====================================================================
+
+See below for a full discussion.
+
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+========================================================================
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+==========================================================================
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well.
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively.
+Thus, Java security checking is no longer necessary, and
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private.
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway,
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS
+=====================================================================
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order.
+From the HashMap documentation for Java 8:
+
+ This class makes no guarantees as to the order of the map; in particular, it does not
+ guarantee that the order will remain constant over time.
+
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+ [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java.
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java.
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus,
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not.
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()).
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript.
+That is, it will be JavaScript typeof "string", not typeof "object".
+
+The reason for this design is that several classes in the Java core use toString()
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object.
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use
+
+ return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects.
+Thus:
+ "xxx" == "xxx"
+ "xxx" == "xx" + "x"
+ "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+ "xxx" != new String("xxx")
+ "xxx" != "xx" + a.toString()
+ "xxx" != "xx" + a
+ new String("xxx") != new String("xxx")
+ "xx" + a != new String("xxx")
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+ assert("xxx" == "xx" + "x");
+ assert("xxx" == ("xx" + a).intern());
+ assert("xxx" === new String("xxx").intern());
+
+and both of these do as well:
+
+ assert(new String("xxx") != "xxx");
+ assert(new String("xxx") != new String("xxx"));
+
+But the following two fail to assert true:
+
+ assert("xxx" != "xx" + a);
+ assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String
+concatentaion as a String object, not a literal.
+
+Most of us know not to generally use == with Strings unless they are explicitly interned.
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence,
+in which case, use new String(). This will work in Java and in JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+ var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem.
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+ You may find some situations where auto-generated names will contain the dollar sign,
+ but your variable names should always avoid using it. A similar convention
+ exists for the underscore character; while it's technically legal to begin your
+ variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+ if the above convention is followed.
+
+2) Fields that have the same Java name as a method are not an issue.
+
+3) Fields that have a Java name with $ that matches a transpiled method name,
+ such as toString$, will need to be refactored in Java to not have that name collision.
+
+4) Fields in a subclass that have the same name as private fields in a superclass
+ represent a name collision, because the superclass method needs to call its private
+ field even if invoked from a subclass. The solution was to modify the subclass field
+ name using one or more prepended $.
+
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled
+ name, not the Java name. This could require some j2sNative adjustment
+ to strip the $... parameters from the name if that is needed.
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+ do not contain "_". This is because the transpiler converts "." to "_" when
+ creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+ 2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+ int newLength = lineBuf.length * 2;
+ if (newLength < 0) {
+ newLength = Integer.MAX_VALUE;
+ }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080,
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact,
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+ try {
+ return (sa[i] == sa[i]);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return false;
+ }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort
+of trap anyway, if you ask me.
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}.
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+ b = new JButton("FileOpenDialog");
+ b.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ JFileChooser fc = new JFileChooser();
+ Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+ // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+ // JavaScript will exit with NaN immediately, and then call back with its actual value
+ // asynchronously.
+ }
+
+ });
+
+ public void onDialogReturn(int value) {
+ if (value != Math.floor(value))
+ return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+ // If we are here, the dialog has closed, in both Java and JavaScript.
+ System.out.println("int value is " + value);
+ }
+
+
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ Object val = event.getNewValue();
+ String name = event.getPropertyName();
+ System.out.println(name);
+ switch (event.getSource().getClass().getName()) {
+ case "javax.swing.JOptionPane":
+ switch (name) {
+ case "inputValue":
+ onDialogReturn(val);
+ return;
+ case "value":
+ if (val instanceof Integer)
+ onDialogReturn(((Integer) val).intValue());
+ else
+ onDialogReturn(val);
+ return;
+ }
+ break;
+ case "javax.swing.ColorChooserDialog":
+ switch (name) {
+ case "SelectedColor":
+ onDialogReturn(val);
+ return;
+ }
+ break;
+ case "javax.swing.JFileChooser":
+ switch (name) {
+ case "SelectedFile":
+ File file = (File) val;
+ byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+ null);
+ onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+ return;
+ }
+ break;
+ }
+ System.out.println(
+ event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+ }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog.
+An example class can be found in the SwingJS distribution as
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening.
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls.
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of
+the AsyncDisplay object:
+
+// Original:
+//
+// private void promptQuit() {
+// int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+// switch (sel) {
+// case JOptionPane.YES_OPTION:
+// resultsTab.clean();
+// seqs.dispose();
+// if (fromMain) {
+// System.exit(0);
+// }
+// break;
+// }
+// }
+
+ private void promptQuitAsync() {
+ new AsyncDialog(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int sel = ((AsyncDialog)e.getSource()).getOption();
+ switch (sel) {
+ case JOptionPane.YES_OPTION:
+ resultsTab.clean();
+ seqs.dispose();
+ if (fromMain) {
+ System.exit(0);
+ }
+ break;
+ }
+ }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+ }
+
+Very simple!
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ * var putYourJavaScriptCodeHere
+ *
+ */
+
+ Note that if you follow that directly with a {...} block, then
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+
+
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented.
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser.
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel.
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a
+component. This probably will not work in SwingJS.
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because
+different operating systems (Mac OS vs Windows vs HTML5) will have
+different edge sizes on their frames. If you want to ensure a component size,
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0).
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example,
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object,
+the transpiler can leave a method name unqualified. The default situation for this is a class name
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery
+object directly using the following interface:
+
+ private interface api {
+
+ interface js extends JQueryObject {
+
+ abstract js j2sCB(Object options);
+
+ abstract Object[] j2sCB(String method);
+
+ abstract Object[] j2sCB(String method, Object o);
+
+ abstract Object[] j2sCB(String method, int i);
+
+ abstract int j2sCB(String OPTION, String name);
+
+ }
+ }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development.
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js,
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS.
+Specifically in SwingJS, any call to component.getGraphics() or
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(),
+particularly when it is called outside the context of a paint(Graphics)
+call from the system.
+
+If you see your graphics scrolling down the page with each repaint,
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+ this.paint(getGraphics())
+
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+ Graphics g = getGraphics();
+ paint(g);
+ g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+ Shape oldClip = Graphics.getClip();
+ Graphics.setClip(newClip);
+ ...
+ Graphics.setClip(oldClip);
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will
+not be exact, and composite (drawn) fonts will not be supported.
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by
+creating a <div> containing that text, placing it in an obscure location on
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+
+
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+ java.awt.Toolkit
+ java.awt.GraphicsEnvironment
+
+
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented.
+
+
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing.
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow.
+References to such objects have been removed, but clearly there must be
+some connection to similar DOM objects, even for "light-weight" components.
+
+
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+ Thread.sleep()
+
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+
+However, java.lang.Thread itself is implemented and used extensively.
+
+Methods thread.start() and thread.run() both work fine.
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for
+example, one can use javax.swing.Timer instead. That is fully implemented.
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ task.run();
+ }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple
+
+ while(!interrupted()){
+ wait()
+ ...
+ }
+
+action through an asynchronous function run1(mode). For example:
+
+ protected void run1(int mode) {
+ try {
+ while (true)
+ switch (mode) {
+ case INIT:
+ // once-through stuff here
+ mode = LOOP;
+ break;
+ case LOOP:
+ if (!doDispatch || isInterrupted()) {
+ mode = DONE;
+ } else {
+ Runnable r = new Runnable() {
+ public void run() {
+ // put the loop code here
+ }
+ };
+ dispatchAndReturn(r);
+ if (isJS)
+ return;
+ }
+ break;
+ // add more cases as needed
+ case DONE:
+ // finish up here
+ if (isInterrupted())
+ return;
+ // or here
+ break;
+ }
+ } finally {
+ // stuff here to be executed after each loop in JS or at the end in Java
+ }
+ }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+ However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+ images be preloaded in the static block of the applet if it is necessary that they be available in init().
+ This is only an issue if you are trying to access the pixel buffer of the image in JavaScript.
+
+- Applet.getImage(path, name) will return null if the image does not exist.
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+ -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed
+in and not fully tested (07/2019).
+
+Both classes present significant issues for JavaScript, as they are based in
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+ When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField (JavaScript <input type="text">)
+JTextArea (JavaScript <textarea>)
+JTextPane (JavaScript <div>)
+JEditorPane (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented.
+
+The JS document model does not allow two text fields to address the same underlying document.
+
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity.
+
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
-20200601093857
+20200607232230
--- /dev/null
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+ using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+
+In particular, the Mandarin character 秘 (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+ public static JSThread 秘thisThread;
+
+----------------------------------
+
+
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using
+older source code, and there are some missing methods. For the most part, this is
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the
+distribution. Or (even more preferably) you can do that via a patch submission.
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example,
+that one can call in JavaScript
+
+ new java.util.Hashtable()
+
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+ public void print(int b);
+ public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified"
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple:
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float),
+$D (double), $B (byte) $Z (boolean), and $H (short).
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+ For example: $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+ public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+ Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+ setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+
+becomes
+
+ setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So
+
+ public JLabel(String text) {...}
+
+becomes:
+
+ Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass
+can both have the same field name, such as
+
+ boolean visible;
+
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+ this.visible = super.visible;
+
+while in SwingJS we will see:
+
+ this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private
+array in the class (and are not accessible by reflection). Private parameterless
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not
+possible to cherry-pick methods to be unqualified; only full packages, classes or
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol)
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code"
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript.
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem.
+
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation.
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is,
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif.
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler
+and do not appear in the JavaScript by their names.
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm"
+(like older versions of Java). So, like the older Java schema, the JavaScript
+equivalents of both applets and applications will share all of their static
+fields and methods. This includes java.lang.System.
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with
+multiple applets present, we must be sure to only have static references
+that are "final" or specifically meant to be shared in a JavaScript
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example,
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+ ThreadGroup g = Thread.currentThread().threadGroup();
+ /**
+ * @j2sNative g._myvar = x;
+ *
+ */
+ {
+ myvar = x;
+ }
+}
+
+public String getMyVar() {
+ ThreadGroup g = Thread.currentThread().threadGroup();
+ /**
+ * @j2sNative return g._myvar || null;
+ *
+ */
+ {
+ return myvar;
+ }
+}
+
+ in Java will get and set x the same in JavaScript and in Java.
+
+
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of
+myclass.xxx in Java (and JavaScript).
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project.
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent --
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results.
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical
+user interface. The issue here is complex but workable. In Java there are two background
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to
+Java or the browser by the operating system. UI classes provide a consistent "look and feel"
+for these native objects, rendering them onto the native window canvas and handling all
+user-generated events. They paint the borders, the backgrounds, the highlights, of every
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes.
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel")
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified.
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their
+own individual native peers and thus automatically took on the look and feel provided by the OS.
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this
+design choice posed a huge headache for Swing class developers.
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and
+instead to require that all components be Swing components.
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of
+javax.swing.JComponent. So far, we have found no problem with this.
+
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications.
+
+All work with files should either use Path or File objects exclusively.
+These objects, after a file is read or checked for existence, will already
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists.
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it
+was actually written or if it already exists prior to writing in SwingJS.
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS
+=====================================================================
+
+See below for a full discussion.
+
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+========================================================================
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+==========================================================================
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well.
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively.
+Thus, Java security checking is no longer necessary, and
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private.
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway,
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS
+=====================================================================
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order.
+From the HashMap documentation for Java 8:
+
+ This class makes no guarantees as to the order of the map; in particular, it does not
+ guarantee that the order will remain constant over time.
+
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+ [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java.
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java.
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus,
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not.
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()).
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript.
+That is, it will be JavaScript typeof "string", not typeof "object".
+
+The reason for this design is that several classes in the Java core use toString()
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object.
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use
+
+ return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects.
+Thus:
+ "xxx" == "xxx"
+ "xxx" == "xx" + "x"
+ "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+ "xxx" != new String("xxx")
+ "xxx" != "xx" + a.toString()
+ "xxx" != "xx" + a
+ new String("xxx") != new String("xxx")
+ "xx" + a != new String("xxx")
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+ assert("xxx" == "xx" + "x");
+ assert("xxx" == ("xx" + a).intern());
+ assert("xxx" === new String("xxx").intern());
+
+and both of these do as well:
+
+ assert(new String("xxx") != "xxx");
+ assert(new String("xxx") != new String("xxx"));
+
+But the following two fail to assert true:
+
+ assert("xxx" != "xx" + a);
+ assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String
+concatentaion as a String object, not a literal.
+
+Most of us know not to generally use == with Strings unless they are explicitly interned.
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence,
+in which case, use new String(). This will work in Java and in JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+ var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem.
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+ You may find some situations where auto-generated names will contain the dollar sign,
+ but your variable names should always avoid using it. A similar convention
+ exists for the underscore character; while it's technically legal to begin your
+ variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+ if the above convention is followed.
+
+2) Fields that have the same Java name as a method are not an issue.
+
+3) Fields that have a Java name with $ that matches a transpiled method name,
+ such as toString$, will need to be refactored in Java to not have that name collision.
+
+4) Fields in a subclass that have the same name as private fields in a superclass
+ represent a name collision, because the superclass method needs to call its private
+ field even if invoked from a subclass. The solution was to modify the subclass field
+ name using one or more prepended $.
+
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled
+ name, not the Java name. This could require some j2sNative adjustment
+ to strip the $... parameters from the name if that is needed.
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+ do not contain "_". This is because the transpiler converts "." to "_" when
+ creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+ 2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+ int newLength = lineBuf.length * 2;
+ if (newLength < 0) {
+ newLength = Integer.MAX_VALUE;
+ }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080,
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact,
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+ try {
+ return (sa[i] == sa[i]);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return false;
+ }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort
+of trap anyway, if you ask me.
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}.
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+ b = new JButton("FileOpenDialog");
+ b.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ JFileChooser fc = new JFileChooser();
+ Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+ // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+ // JavaScript will exit with NaN immediately, and then call back with its actual value
+ // asynchronously.
+ }
+
+ });
+
+ public void onDialogReturn(int value) {
+ if (value != Math.floor(value))
+ return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+ // If we are here, the dialog has closed, in both Java and JavaScript.
+ System.out.println("int value is " + value);
+ }
+
+
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ Object val = event.getNewValue();
+ String name = event.getPropertyName();
+ System.out.println(name);
+ switch (event.getSource().getClass().getName()) {
+ case "javax.swing.JOptionPane":
+ switch (name) {
+ case "inputValue":
+ onDialogReturn(val);
+ return;
+ case "value":
+ if (val instanceof Integer)
+ onDialogReturn(((Integer) val).intValue());
+ else
+ onDialogReturn(val);
+ return;
+ }
+ break;
+ case "javax.swing.ColorChooserDialog":
+ switch (name) {
+ case "SelectedColor":
+ onDialogReturn(val);
+ return;
+ }
+ break;
+ case "javax.swing.JFileChooser":
+ switch (name) {
+ case "SelectedFile":
+ File file = (File) val;
+ byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+ null);
+ onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+ return;
+ }
+ break;
+ }
+ System.out.println(
+ event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+ }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog.
+An example class can be found in the SwingJS distribution as
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening.
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls.
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of
+the AsyncDisplay object:
+
+// Original:
+//
+// private void promptQuit() {
+// int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+// switch (sel) {
+// case JOptionPane.YES_OPTION:
+// resultsTab.clean();
+// seqs.dispose();
+// if (fromMain) {
+// System.exit(0);
+// }
+// break;
+// }
+// }
+
+ private void promptQuitAsync() {
+ new AsyncDialog(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int sel = ((AsyncDialog)e.getSource()).getOption();
+ switch (sel) {
+ case JOptionPane.YES_OPTION:
+ resultsTab.clean();
+ seqs.dispose();
+ if (fromMain) {
+ System.exit(0);
+ }
+ break;
+ }
+ }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+ }
+
+Very simple!
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ * var putYourJavaScriptCodeHere
+ *
+ */
+
+ Note that if you follow that directly with a {...} block, then
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+
+
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented.
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser.
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel.
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a
+component. This probably will not work in SwingJS.
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because
+different operating systems (Mac OS vs Windows vs HTML5) will have
+different edge sizes on their frames. If you want to ensure a component size,
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0).
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example,
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object,
+the transpiler can leave a method name unqualified. The default situation for this is a class name
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery
+object directly using the following interface:
+
+ private interface api {
+
+ interface js extends JQueryObject {
+
+ abstract js j2sCB(Object options);
+
+ abstract Object[] j2sCB(String method);
+
+ abstract Object[] j2sCB(String method, Object o);
+
+ abstract Object[] j2sCB(String method, int i);
+
+ abstract int j2sCB(String OPTION, String name);
+
+ }
+ }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development.
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js,
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS.
+Specifically in SwingJS, any call to component.getGraphics() or
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(),
+particularly when it is called outside the context of a paint(Graphics)
+call from the system.
+
+If you see your graphics scrolling down the page with each repaint,
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+ this.paint(getGraphics())
+
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+ Graphics g = getGraphics();
+ paint(g);
+ g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+ Shape oldClip = Graphics.getClip();
+ Graphics.setClip(newClip);
+ ...
+ Graphics.setClip(oldClip);
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will
+not be exact, and composite (drawn) fonts will not be supported.
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by
+creating a <div> containing that text, placing it in an obscure location on
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+
+
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+ java.awt.Toolkit
+ java.awt.GraphicsEnvironment
+
+
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented.
+
+
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing.
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow.
+References to such objects have been removed, but clearly there must be
+some connection to similar DOM objects, even for "light-weight" components.
+
+
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+ Thread.sleep()
+
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+
+However, java.lang.Thread itself is implemented and used extensively.
+
+Methods thread.start() and thread.run() both work fine.
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for
+example, one can use javax.swing.Timer instead. That is fully implemented.
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ task.run();
+ }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple
+
+ while(!interrupted()){
+ wait()
+ ...
+ }
+
+action through an asynchronous function run1(mode). For example:
+
+ protected void run1(int mode) {
+ try {
+ while (true)
+ switch (mode) {
+ case INIT:
+ // once-through stuff here
+ mode = LOOP;
+ break;
+ case LOOP:
+ if (!doDispatch || isInterrupted()) {
+ mode = DONE;
+ } else {
+ Runnable r = new Runnable() {
+ public void run() {
+ // put the loop code here
+ }
+ };
+ dispatchAndReturn(r);
+ if (isJS)
+ return;
+ }
+ break;
+ // add more cases as needed
+ case DONE:
+ // finish up here
+ if (isInterrupted())
+ return;
+ // or here
+ break;
+ }
+ } finally {
+ // stuff here to be executed after each loop in JS or at the end in Java
+ }
+ }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+ However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+ images be preloaded in the static block of the applet if it is necessary that they be available in init().
+ This is only an issue if you are trying to access the pixel buffer of the image in JavaScript.
+
+- Applet.getImage(path, name) will return null if the image does not exist.
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+ -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed
+in and not fully tested (07/2019).
+
+Both classes present significant issues for JavaScript, as they are based in
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+ When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField (JavaScript <input type="text">)
+JTextArea (JavaScript <textarea>)
+JTextPane (JavaScript <div>)
+JEditorPane (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented.
+
+The JS document model does not allow two text fields to address the same underlying document.
+
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity.
+
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
-20200528154316
+20200607231708
--- /dev/null
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+ using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+
+In particular, the Mandarin character 秘 (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+ public static JSThread 秘thisThread;
+
+----------------------------------
+
+
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using
+older source code, and there are some missing methods. For the most part, this is
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the
+distribution. Or (even more preferably) you can do that via a patch submission.
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example,
+that one can call in JavaScript
+
+ new java.util.Hashtable()
+
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+ public void print(int b);
+ public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified"
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple:
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float),
+$D (double), $B (byte) $Z (boolean), and $H (short).
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+ For example: $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+ public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+ Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+ setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+
+becomes
+
+ setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So
+
+ public JLabel(String text) {...}
+
+becomes:
+
+ Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass
+can both have the same field name, such as
+
+ boolean visible;
+
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+ this.visible = super.visible;
+
+while in SwingJS we will see:
+
+ this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private
+array in the class (and are not accessible by reflection). Private parameterless
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not
+possible to cherry-pick methods to be unqualified; only full packages, classes or
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol)
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code"
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript.
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem.
+
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation.
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is,
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif.
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler
+and do not appear in the JavaScript by their names.
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm"
+(like older versions of Java). So, like the older Java schema, the JavaScript
+equivalents of both applets and applications will share all of their static
+fields and methods. This includes java.lang.System.
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with
+multiple applets present, we must be sure to only have static references
+that are "final" or specifically meant to be shared in a JavaScript
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example,
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+ ThreadGroup g = Thread.currentThread().threadGroup();
+ /**
+ * @j2sNative g._myvar = x;
+ *
+ */
+ {
+ myvar = x;
+ }
+}
+
+public String getMyVar() {
+ ThreadGroup g = Thread.currentThread().threadGroup();
+ /**
+ * @j2sNative return g._myvar || null;
+ *
+ */
+ {
+ return myvar;
+ }
+}
+
+ in Java will get and set x the same in JavaScript and in Java.
+
+
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of
+myclass.xxx in Java (and JavaScript).
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project.
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent --
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results.
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical
+user interface. The issue here is complex but workable. In Java there are two background
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to
+Java or the browser by the operating system. UI classes provide a consistent "look and feel"
+for these native objects, rendering them onto the native window canvas and handling all
+user-generated events. They paint the borders, the backgrounds, the highlights, of every
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes.
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel")
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified.
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their
+own individual native peers and thus automatically took on the look and feel provided by the OS.
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this
+design choice posed a huge headache for Swing class developers.
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and
+instead to require that all components be Swing components.
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of
+javax.swing.JComponent. So far, we have found no problem with this.
+
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications.
+
+All work with files should either use Path or File objects exclusively.
+These objects, after a file is read or checked for existence, will already
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists.
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it
+was actually written or if it already exists prior to writing in SwingJS.
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS
+=====================================================================
+
+See below for a full discussion.
+
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+========================================================================
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+==========================================================================
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well.
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively.
+Thus, Java security checking is no longer necessary, and
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private.
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway,
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS
+=====================================================================
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order.
+From the HashMap documentation for Java 8:
+
+ This class makes no guarantees as to the order of the map; in particular, it does not
+ guarantee that the order will remain constant over time.
+
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+ [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java.
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java.
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus,
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not.
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()).
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript.
+That is, it will be JavaScript typeof "string", not typeof "object".
+
+The reason for this design is that several classes in the Java core use toString()
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object.
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use
+
+ return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects.
+Thus:
+ "xxx" == "xxx"
+ "xxx" == "xx" + "x"
+ "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+ "xxx" != new String("xxx")
+ "xxx" != "xx" + a.toString()
+ "xxx" != "xx" + a
+ new String("xxx") != new String("xxx")
+ "xx" + a != new String("xxx")
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+ assert("xxx" == "xx" + "x");
+ assert("xxx" == ("xx" + a).intern());
+ assert("xxx" === new String("xxx").intern());
+
+and both of these do as well:
+
+ assert(new String("xxx") != "xxx");
+ assert(new String("xxx") != new String("xxx"));
+
+But the following two fail to assert true:
+
+ assert("xxx" != "xx" + a);
+ assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String
+concatentaion as a String object, not a literal.
+
+Most of us know not to generally use == with Strings unless they are explicitly interned.
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence,
+in which case, use new String(). This will work in Java and in JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+ var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem.
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+ You may find some situations where auto-generated names will contain the dollar sign,
+ but your variable names should always avoid using it. A similar convention
+ exists for the underscore character; while it's technically legal to begin your
+ variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+ if the above convention is followed.
+
+2) Fields that have the same Java name as a method are not an issue.
+
+3) Fields that have a Java name with $ that matches a transpiled method name,
+ such as toString$, will need to be refactored in Java to not have that name collision.
+
+4) Fields in a subclass that have the same name as private fields in a superclass
+ represent a name collision, because the superclass method needs to call its private
+ field even if invoked from a subclass. The solution was to modify the subclass field
+ name using one or more prepended $.
+
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled
+ name, not the Java name. This could require some j2sNative adjustment
+ to strip the $... parameters from the name if that is needed.
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+ do not contain "_". This is because the transpiler converts "." to "_" when
+ creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+ 2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+ int newLength = lineBuf.length * 2;
+ if (newLength < 0) {
+ newLength = Integer.MAX_VALUE;
+ }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080,
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact,
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+ try {
+ return (sa[i] == sa[i]);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return false;
+ }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort
+of trap anyway, if you ask me.
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}.
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+ b = new JButton("FileOpenDialog");
+ b.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ JFileChooser fc = new JFileChooser();
+ Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+ // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+ // JavaScript will exit with NaN immediately, and then call back with its actual value
+ // asynchronously.
+ }
+
+ });
+
+ public void onDialogReturn(int value) {
+ if (value != Math.floor(value))
+ return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+ // If we are here, the dialog has closed, in both Java and JavaScript.
+ System.out.println("int value is " + value);
+ }
+
+
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ Object val = event.getNewValue();
+ String name = event.getPropertyName();
+ System.out.println(name);
+ switch (event.getSource().getClass().getName()) {
+ case "javax.swing.JOptionPane":
+ switch (name) {
+ case "inputValue":
+ onDialogReturn(val);
+ return;
+ case "value":
+ if (val instanceof Integer)
+ onDialogReturn(((Integer) val).intValue());
+ else
+ onDialogReturn(val);
+ return;
+ }
+ break;
+ case "javax.swing.ColorChooserDialog":
+ switch (name) {
+ case "SelectedColor":
+ onDialogReturn(val);
+ return;
+ }
+ break;
+ case "javax.swing.JFileChooser":
+ switch (name) {
+ case "SelectedFile":
+ File file = (File) val;
+ byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+ null);
+ onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+ return;
+ }
+ break;
+ }
+ System.out.println(
+ event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+ }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog.
+An example class can be found in the SwingJS distribution as
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening.
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls.
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of
+the AsyncDisplay object:
+
+// Original:
+//
+// private void promptQuit() {
+// int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+// switch (sel) {
+// case JOptionPane.YES_OPTION:
+// resultsTab.clean();
+// seqs.dispose();
+// if (fromMain) {
+// System.exit(0);
+// }
+// break;
+// }
+// }
+
+ private void promptQuitAsync() {
+ new AsyncDialog(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int sel = ((AsyncDialog)e.getSource()).getOption();
+ switch (sel) {
+ case JOptionPane.YES_OPTION:
+ resultsTab.clean();
+ seqs.dispose();
+ if (fromMain) {
+ System.exit(0);
+ }
+ break;
+ }
+ }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+ }
+
+Very simple!
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ * var putYourJavaScriptCodeHere
+ *
+ */
+
+ Note that if you follow that directly with a {...} block, then
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+
+
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented.
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser.
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel.
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a
+component. This probably will not work in SwingJS.
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because
+different operating systems (Mac OS vs Windows vs HTML5) will have
+different edge sizes on their frames. If you want to ensure a component size,
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0).
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example,
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object,
+the transpiler can leave a method name unqualified. The default situation for this is a class name
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery
+object directly using the following interface:
+
+ private interface api {
+
+ interface js extends JQueryObject {
+
+ abstract js j2sCB(Object options);
+
+ abstract Object[] j2sCB(String method);
+
+ abstract Object[] j2sCB(String method, Object o);
+
+ abstract Object[] j2sCB(String method, int i);
+
+ abstract int j2sCB(String OPTION, String name);
+
+ }
+ }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development.
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js,
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS.
+Specifically in SwingJS, any call to component.getGraphics() or
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(),
+particularly when it is called outside the context of a paint(Graphics)
+call from the system.
+
+If you see your graphics scrolling down the page with each repaint,
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+ this.paint(getGraphics())
+
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+ Graphics g = getGraphics();
+ paint(g);
+ g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+ Shape oldClip = Graphics.getClip();
+ Graphics.setClip(newClip);
+ ...
+ Graphics.setClip(oldClip);
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will
+not be exact, and composite (drawn) fonts will not be supported.
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by
+creating a <div> containing that text, placing it in an obscure location on
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+
+
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+ java.awt.Toolkit
+ java.awt.GraphicsEnvironment
+
+
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented.
+
+
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing.
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow.
+References to such objects have been removed, but clearly there must be
+some connection to similar DOM objects, even for "light-weight" components.
+
+
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+ Thread.sleep()
+
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+
+However, java.lang.Thread itself is implemented and used extensively.
+
+Methods thread.start() and thread.run() both work fine.
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for
+example, one can use javax.swing.Timer instead. That is fully implemented.
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ task.run();
+ }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple
+
+ while(!interrupted()){
+ wait()
+ ...
+ }
+
+action through an asynchronous function run1(mode). For example:
+
+ protected void run1(int mode) {
+ try {
+ while (true)
+ switch (mode) {
+ case INIT:
+ // once-through stuff here
+ mode = LOOP;
+ break;
+ case LOOP:
+ if (!doDispatch || isInterrupted()) {
+ mode = DONE;
+ } else {
+ Runnable r = new Runnable() {
+ public void run() {
+ // put the loop code here
+ }
+ };
+ dispatchAndReturn(r);
+ if (isJS)
+ return;
+ }
+ break;
+ // add more cases as needed
+ case DONE:
+ // finish up here
+ if (isInterrupted())
+ return;
+ // or here
+ break;
+ }
+ } finally {
+ // stuff here to be executed after each loop in JS or at the end in Java
+ }
+ }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+ However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+ images be preloaded in the static block of the applet if it is necessary that they be available in init().
+ This is only an issue if you are trying to access the pixel buffer of the image in JavaScript.
+
+- Applet.getImage(path, name) will return null if the image does not exist.
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+ -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed
+in and not fully tested (07/2019).
+
+Both classes present significant issues for JavaScript, as they are based in
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+ When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField (JavaScript <input type="text">)
+JTextArea (JavaScript <textarea>)
+JTextPane (JavaScript <div>)
+JEditorPane (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented.
+
+The JS document model does not allow two text fields to address the same underlying document.
+
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity.
+
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
-20200601093857
+20200607232230
int scrollpos = 60;
af.getViewport().hideColumns(30, 50);
af.alignPanel.setScrollValues(scrollpos, 5);
-
- af.paintImmediately(af.getBounds());
assertEquals(ranges.getEndRes(), oldres + scrollpos);
// scroll to position within hidden columns, still sets endres to oldres +