From a2f56e0692e09f2b19af46aad421a84ec0bbcb0c Mon Sep 17 00:00:00 2001 From: benfry Date: Sat, 16 Apr 2011 16:11:49 +0000 Subject: [PATCH] find/replace fixes from Peter N Lewis, new Quaqua to fix checkboxes in popups, focus fix --- app/src/processing/app/Editor.java | 26 +- app/src/processing/app/FindReplace.java | 295 +++++++++++------- .../processing/app/syntax/JEditTextArea.java | 200 +++++++----- todo.txt | 29 +- 4 files changed, 336 insertions(+), 214 deletions(-) diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 0117e2d43..127b75633 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -716,14 +716,34 @@ public abstract class Editor extends JFrame implements RunnerListener { item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (find != null) { - //find.find(true); - //FindReplace find = new FindReplace(Editor.this); //.show(); - find.find(true); + find.findNext(); } } }); menu.add(item); + item = Base.newJMenuItemShift("Find Previous", 'G'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (find != null) { + find.findPrevious(); + } + } + }); + menu.add(item); + + // For Arduino and Mac, this should be command-E, but that currently conflicts with Export Applet + item = new JMenuItem("Use Selection for Find"); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (find == null) { + find = new FindReplace(Editor.this); + } + find.setFindText( getSelectedText() ); + } + }); + menu.add(item); + return menu; } diff --git a/app/src/processing/app/FindReplace.java b/app/src/processing/app/FindReplace.java index 9cc0726d2..328e7bdc4 100644 --- a/app/src/processing/app/FindReplace.java +++ b/app/src/processing/app/FindReplace.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2004-10 Ben Fry and Casey Reas + Copyright (c) 2004-11 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology This program is free software; you can redistribute it and/or modify @@ -32,8 +32,10 @@ import javax.swing.*; */ public class FindReplace extends JFrame implements ActionListener { - static final int BIG = 13; + static final int EDGE = Base.isMacOS() ? 20 : 13; static final int SMALL = 6; + // 12 is correct for Mac, other numbers may be required for other platforms + static final int BUTTON_GAP = 12; Editor editor; @@ -45,13 +47,14 @@ public class FindReplace extends JFrame implements ActionListener { JButton replaceButton; JButton replaceAllButton; JButton replaceFindButton; + JButton previousButton; JButton findButton; JCheckBox ignoreCaseBox; static boolean ignoreCase = true; - /// true when there's something selected in the editor - boolean found; + JCheckBox wrapAroundBox; + static boolean wrapAround = true; public FindReplace(Editor editor) { @@ -63,20 +66,63 @@ public class FindReplace extends JFrame implements ActionListener { pain.setLayout(null); JLabel findLabel = new JLabel("Find:"); - Dimension d0 = findLabel.getPreferredSize(); JLabel replaceLabel = new JLabel("Replace with:"); - Dimension d1 = replaceLabel.getPreferredSize(); + Dimension labelDimension = replaceLabel.getPreferredSize(); pain.add(findLabel); pain.add(replaceLabel); - pain.add(findField = new JTextField(20)); - pain.add(replaceField = new JTextField(20)); - Dimension d2 = findField.getPreferredSize(); + pain.add(findField = new JTextField()); + pain.add(replaceField = new JTextField()); + int fieldHeight = findField.getPreferredSize().height; if (findString != null) findField.setText(findString); if (replaceString != null) replaceField.setText(replaceString); + ignoreCaseBox = new JCheckBox("Ignore Case"); + ignoreCaseBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ignoreCase = ignoreCaseBox.isSelected(); + } + }); + ignoreCaseBox.setSelected(ignoreCase); + pain.add(ignoreCaseBox); + + wrapAroundBox = new JCheckBox("Wrap Around"); + wrapAroundBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + wrapAround = wrapAroundBox.isSelected(); + } + }); + wrapAroundBox.setSelected(wrapAround); + pain.add(wrapAroundBox); + + JPanel buttons = new JPanel(); + buttons.setLayout(new FlowLayout(FlowLayout.CENTER,BUTTON_GAP, 0)); + + // ordering is different on mac versus pc + if (Base.isMacOS()) { + buttons.add(replaceAllButton = new JButton("Replace All")); + buttons.add(replaceButton = new JButton("Replace")); + buttons.add(replaceFindButton = new JButton("Replace & Find")); + buttons.add(previousButton = new JButton("Previous")); + buttons.add(findButton = new JButton("Find")); + + } else { + buttons.add(findButton = new JButton("Find")); + buttons.add(previousButton = new JButton("Previous")); // is this the right position for non-Mac? + buttons.add(replaceFindButton = new JButton("Replace & Find")); + buttons.add(replaceButton = new JButton("Replace")); + buttons.add(replaceAllButton = new JButton("Replace All")); + } + pain.add(buttons); + + // to fix ugliness.. normally macosx java 1.3 puts an + // ugly white border around this object, so turn it off. + if (Base.isMacOS()) { + buttons.setBorder(null); + } + /* findField.addFocusListener(new FocusListener() { public void focusGained(FocusEvent e) { @@ -92,91 +138,74 @@ public class FindReplace extends JFrame implements ActionListener { }); */ - // +1 since it's better to tend downwards - int yoff = (1 + d2.height - d1.height) / 2; + Dimension buttonsDimension = buttons.getPreferredSize(); + int visibleButtonWidth = buttonsDimension.width - 2 * BUTTON_GAP; + int fieldWidth = visibleButtonWidth - (labelDimension.width + SMALL); - findLabel.setBounds(BIG + (d1.width-d0.width) + yoff, BIG, - d1.width, d1.height); - replaceLabel.setBounds(BIG, BIG + d2.height + SMALL + yoff, - d1.width, d1.height); + // +1 since it's better to tend downwards + int yoff = (1 + fieldHeight - labelDimension.height) / 2; - ignoreCaseBox = new JCheckBox("Ignore Case"); - ignoreCaseBox.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ignoreCase = ignoreCaseBox.isSelected(); - } - }); - ignoreCaseBox.setSelected(ignoreCase); - pain.add(ignoreCaseBox); + int ypos = EDGE; - // + int labelWidth = findLabel.getPreferredSize().width; + findLabel.setBounds(EDGE + (labelDimension.width-labelWidth), ypos + yoff, // + yoff was added to the wrong field + labelWidth, labelDimension.height); + findField.setBounds(EDGE + labelDimension.width + SMALL, ypos, + fieldWidth, fieldHeight); - JPanel buttons = new JPanel(); - buttons.setLayout(new FlowLayout()); + ypos += fieldHeight + SMALL; - // ordering is different on mac versus pc - if (Base.isMacOS()) { - buttons.add(replaceAllButton = new JButton("Replace All")); - buttons.add(replaceButton = new JButton("Replace")); - buttons.add(replaceFindButton = new JButton("Replace & Find")); - buttons.add(findButton = new JButton("Find")); + labelWidth = replaceLabel.getPreferredSize().width; + replaceLabel.setBounds(EDGE + (labelDimension.width-labelWidth), ypos + yoff, + labelWidth, labelDimension.height); + replaceField.setBounds(EDGE + labelDimension.width + SMALL, ypos, + fieldWidth, fieldHeight); - } else { - buttons.add(findButton = new JButton("Find")); - buttons.add(replaceFindButton = new JButton("Replace & Find")); - buttons.add(replaceButton = new JButton("Replace")); - buttons.add(replaceAllButton = new JButton("Replace All")); - } - pain.add(buttons); + ypos += fieldHeight + SMALL; - // to fix ugliness.. normally macosx java 1.3 puts an - // ugly white border around this object, so turn it off. - if (Base.isMacOS()) { - buttons.setBorder(null); - } + ignoreCaseBox.setBounds(EDGE + labelDimension.width + SMALL, + ypos, + (fieldWidth-SMALL)/2, fieldHeight); - Dimension d3 = buttons.getPreferredSize(); - //buttons.setBounds(BIG, BIG + d2.height*2 + SMALL + BIG, - buttons.setBounds(BIG, BIG + d2.height*3 + SMALL*2 + BIG, - d3.width, d3.height); + wrapAroundBox.setBounds(EDGE + labelDimension.width + SMALL + (fieldWidth-SMALL)/2 + SMALL, + ypos, + (fieldWidth-SMALL)/2, fieldHeight); - // + ypos += fieldHeight + SMALL; - findField.setBounds(BIG + d1.width + SMALL, BIG, - d3.width - (d1.width + SMALL), d2.height); - replaceField.setBounds(BIG + d1.width + SMALL, BIG + d2.height + SMALL, - d3.width - (d1.width + SMALL), d2.height); + buttons.setBounds(EDGE-BUTTON_GAP, ypos, + buttonsDimension.width, buttonsDimension.height); - ignoreCaseBox.setBounds(BIG + d1.width + SMALL, - BIG + d2.height*2 + SMALL*2, - d3.width, d2.height); + ypos += buttonsDimension.height + EDGE; - // +// Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + + int wide = visibleButtonWidth + EDGE*2; + int high = ypos; // butt.y + butt.height + EDGE*2 + SMALL; + + pack(); + Insets insets = getInsets(); +// System.out.println("Insets = " + insets); + setSize(wide + insets.left + insets.right,high + insets.top + insets.bottom); + + setLocationRelativeTo( null ); // center + // setBounds((screen.width - wide) / 2, (screen.height - high) / 2, wide, high); replaceButton.addActionListener(this); replaceAllButton.addActionListener(this); replaceFindButton.addActionListener(this); findButton.addActionListener(this); + previousButton.addActionListener(this); // you mustn't replace what you haven't found, my son - replaceButton.setEnabled(false); - replaceFindButton.setEnabled(false); - - // so that typing will go straight to this field - //findField.requestFocus(); + // semantics of replace are "replace the current selection with the replace field" + // so whether we have found before or not is irrelevent + // replaceButton.setEnabled(false); + // replaceFindButton.setEnabled(false); // make the find button the blinky default getRootPane().setDefaultButton(findButton); - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - - int wide = d3.width + BIG*2; - Rectangle butt = buttons.getBounds(); // how big is your butt? - int high = butt.y + butt.height + BIG*2 + SMALL; - - setBounds((screen.width - wide) / 2, - (screen.height - high) / 2, wide, high); - setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { @@ -227,11 +256,13 @@ public class FindReplace extends JFrame implements ActionListener { Object source = e.getSource(); if (source == findButton) { - find(true); + findNext(); + + } else if (source == previousButton) { + findPrevious(); } else if (source == replaceFindButton) { - replace(); - find(true); + replaceAndFindNext(); } else if (source == replaceButton) { replace(); @@ -242,50 +273,52 @@ public class FindReplace extends JFrame implements ActionListener { } - // look for the next instance of the find string - // to be found later than the current caret selection - + // look for the next instance of the find string to be found // once found, select it (and go to that line) - - public void find(boolean wrap) { - // in case search len is zero, - // otherwise replace all will go into an infinite loop - found = false; - + private boolean find(boolean wrap, boolean backwards) { String search = findField.getText(); //System.out.println("finding for " + search + " " + findString); // this will catch "find next" being called when no search yet - if (search.length() == 0) return; + if (search.length() == 0) return false; String text = editor.getText(); if (ignoreCase) { - search = search.toLowerCase(); + search = search.toLowerCase(); text = text.toLowerCase(); } - //int selectionStart = editor.textarea.getSelectionStart(); - int selectionEnd = editor.getSelectionStop(); + int nextIndex; + if (!backwards) { + //int selectionStart = editor.textarea.getSelectionStart(); + int selectionEnd = editor.getSelectionStop(); - int nextIndex = text.indexOf(search, selectionEnd); - if (nextIndex == -1) { - if (wrap) { + nextIndex = text.indexOf(search, selectionEnd); + if (wrap && nextIndex == -1) { // if wrapping, a second chance is ok, start from beginning nextIndex = text.indexOf(search, 0); } + } else { + //int selectionStart = editor.textarea.getSelectionStart(); + int selectionStart = editor.getSelectionStart()-1; - if (nextIndex == -1) { - found = false; - replaceButton.setEnabled(false); - replaceFindButton.setEnabled(false); - //Toolkit.getDefaultToolkit().beep(); - return; + if ( selectionStart >= 0 ) { + nextIndex = text.lastIndexOf(search, selectionStart); + } else { + nextIndex = -1; + } + if (wrap && nextIndex == -1) { + // if wrapping, a second chance is ok, start from the end + nextIndex = text.lastIndexOf(search); } } - found = true; - replaceButton.setEnabled(true); - replaceFindButton.setEnabled(true); - editor.setSelection(nextIndex, nextIndex + search.length()); + + if (nextIndex != -1) { + editor.setSelection(nextIndex, nextIndex + search.length()); + } else { + //Toolkit.getDefaultToolkit().beep(); + } + return nextIndex != -1; } @@ -294,26 +327,18 @@ public class FindReplace extends JFrame implements ActionListener { * replacement text field. */ public void replace() { - if (!found) return; // don't replace if nothing found - - // check to see if the document has wrapped around - // otherwise this will cause an infinite loop - String sel = editor.getSelectedText(); - if (sel.equals(replaceField.getText())) { - found = false; - replaceButton.setEnabled(false); - replaceFindButton.setEnabled(false); - return; - } - editor.setSelectedText(replaceField.getText()); - //editor.setSketchModified(true); - //editor.sketch.setCurrentModified(true); editor.getSketch().setModified(true); // TODO is this necessary? + } - // don't allow a double replace - replaceButton.setEnabled(false); - replaceFindButton.setEnabled(false); + + /** + * Replace the current selection with whatever's in the + * replacement text field, and then find the next match + */ + public void replaceAndFindNext() { + replace(); + findNext(); } @@ -325,9 +350,37 @@ public class FindReplace extends JFrame implements ActionListener { // move to the beginning editor.setSelection(0, 0); - do { - find(false); - replace(); - } while (found); + boolean foundAtLeastOne = false; + while ( true ) { + if ( find(false,false) ) { + foundAtLeastOne = true; + replace(); + } else { + break; + } + } + if ( !foundAtLeastOne ) { + Toolkit.getDefaultToolkit().beep(); + } + } + + + public void setFindText( String t ) { + findField.setText( t ); + findString = t; + } + + + public void findNext() { + if ( !find( wrapAround, false ) ) { + Toolkit.getDefaultToolkit().beep(); + } + } + + + public void findPrevious() { + if ( !find( wrapAround, true ) ) { + Toolkit.getDefaultToolkit().beep(); + } } } diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index f5051547d..41b69c4c2 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -833,6 +833,23 @@ public class JEditTextArea extends JComponent return lineElement.getEndOffset(); } + /** + * Returns the start offset of the line after this line, or the end of + * this line if there is no next line. + * @param line The line + * @return The end offset of the specified line, or -1 if the line is + * invalid. + */ + public int getLineSelectionStopOffset(int line) + { + Element lineElement = document.getDefaultRootElement() + .getElement(line); + if(lineElement == null) + return -1; + else + return Math.min(lineElement.getEndOffset(),getDocumentLength()); + } + /** * Returns the length of the specified line. * @param line The line @@ -1143,7 +1160,7 @@ public class JEditTextArea extends JComponent { throw new IllegalArgumentException("Bounds out of" + " range: " + newStart + "," + - newEnd); + newEnd + " [" + getDocumentLength() + "]"); } // If the new position is the same as the old, we don't @@ -1200,6 +1217,64 @@ public class JEditTextArea extends JComponent // getLineOfOffset(end)); } } + + private enum CharacterKinds { + Word, + Whitespace, + Other + } + + private CharacterKinds CharacterKind( char ch, String noWordSep ) + { + if ( Character.isLetterOrDigit(ch) || ch=='_' || noWordSep.indexOf(ch) != -1 ) + return CharacterKinds.Word; + else if ( Character.isWhitespace(ch) ) + return CharacterKinds.Whitespace; + else + return CharacterKinds.Other; + } + + protected void setNewSelectionWord( int line, int offset ) + { + if (getLineLength(line) == 0) { + newSelectionStart = getLineStartOffset(line); + newSelectionEnd = newSelectionStart; + return; + } + + String noWordSep = (String)document.getProperty("noWordSep"); + if(noWordSep == null) + noWordSep = ""; + + String lineText = getLineText(line); + + int wordStart = 0; + int wordEnd = lineText.length(); + + char ch = lineText.charAt(Math.max(0,offset - 1)); + + CharacterKinds thisWord = CharacterKind(ch,noWordSep); + + for(int i = offset - 1; i >= 0; i--) { + ch = lineText.charAt(i); + if(CharacterKind(ch,noWordSep) != thisWord) { + wordStart = i + 1; + break; + } + } + + for(int i = offset; i < lineText.length(); i++) { + ch = lineText.charAt(i); + if(CharacterKind(ch,noWordSep) != thisWord) { + wordEnd = i; + break; + } + } + int lineStart = getLineStartOffset(line); + + newSelectionStart = lineStart + wordStart; + newSelectionEnd = lineStart + wordEnd; + } /** @@ -1833,6 +1908,14 @@ public class JEditTextArea extends JComponent protected int selectionEndLine; protected boolean biasLeft; + protected int newSelectionStart; // hack to get around lack of multiple returns in Java + protected int newSelectionEnd; + + protected boolean selectWord; + protected boolean selectLine; + protected int selectionAncorStart; + protected int selectionAncorEnd; + protected int bracketPosition; protected int bracketLine; @@ -2171,9 +2254,26 @@ public class JEditTextArea extends JComponent { if (popup != null && popup.isVisible()) return; + if ( !selectWord && !selectLine ) { setSelectionRectangular((evt.getModifiers() & InputEvent.CTRL_MASK) != 0); select(getMarkPosition(),xyToOffset(evt.getX(),evt.getY())); + } else { + int line = yToLine(evt.getY()); + if ( selectWord ) { + setNewSelectionWord( line, xToOffset(line,evt.getX()) ); + } else { + newSelectionStart = getLineStartOffset(line); + newSelectionEnd = getLineSelectionStopOffset(line); + } + if ( newSelectionStart < selectionAncorStart ) { + select(newSelectionStart,selectionAncorEnd); + } else if ( newSelectionEnd > selectionAncorEnd ) { + select(selectionAncorStart,newSelectionEnd); + } else { + select(newSelectionStart,newSelectionEnd); + } + } } public void mouseMoved(MouseEvent evt) {} @@ -2202,7 +2302,7 @@ public class JEditTextArea extends JComponent { public void mousePressed(MouseEvent evt) { - try { +// try { // requestFocus(); // // Focus events not fired sometimes? // setCaretVisible(true); @@ -2214,7 +2314,10 @@ public class JEditTextArea extends JComponent // the problem, though it's not clear why the wrong Document data was // being using regardless of the focusedComponent. // if (focusedComponent != JEditTextArea.this) return; - if (!hasFocus()) return; + if (!hasFocus()) { + requestFocusInWindow(); + return; + } // isPopupTrigger wasn't working for danh on windows boolean trigger = (evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0; @@ -2231,6 +2334,9 @@ public class JEditTextArea extends JComponent int offset = xToOffset(line,evt.getX()); int dot = getLineStartOffset(line) + offset; + selectLine = false; + selectWord = false; + switch(evt.getClickCount()) { case 1: @@ -2251,11 +2357,11 @@ public class JEditTextArea extends JComponent doTripleClick(evt,line,offset,dot); break; } - } catch (ArrayIndexOutOfBoundsException aioobe) { - aioobe.printStackTrace(); - int line = yToLine(evt.getY()); - System.out.println("line is " + line + ", line count is " + getLineCount()); - } +// } catch (ArrayIndexOutOfBoundsException aioobe) { +// aioobe.printStackTrace(); +// int line = yToLine(evt.getY()); +// System.out.println("line is " + line + ", line count is " + getLineCount()); +// } } @@ -2294,74 +2400,11 @@ public class JEditTextArea extends JComponent bl.printStackTrace(); } - String noWordSep = (String)document.getProperty("noWordSep"); - if(noWordSep == null) - noWordSep = ""; - - // Ok, it's not a bracket... select the word - String lineText = getLineText(line); - - int wordStart = 0; - int wordEnd = lineText.length(); - - char ch = lineText.charAt(Math.max(0,offset - 1)); - - // special case for whitespace (fry 0122, bug #348) - // this is really nasty.. turns out that double-clicking any non-letter - // or digit char gets lumped together.. sooo, this quickly gets messy, - // because really it needs to check whether the chars are of the same - // type.. so a double space or double - might be grouped together, - // but what about a +=1? do + and - get grouped but not the 1? blech, - // coming back to this later. it's not a difficult fix, just a - // time-consuming one to track down all the proper cases. - /* - if (ch == ' ') { - //System.out.println("yeehaa"); - - for(int i = offset - 1; i >= 0; i--) { - if (lineText.charAt(i) == ' ') { - wordStart = i; - } else { - break; - } - } - for(int i = offset; i < lineText.length(); i++) { - if (lineText.charAt(i) == ' ') { - wordEnd = i + 1; - } else { - break; - } - } - - } else { - */ - - // If the user clicked on a non-letter char, - // we select the surrounding non-letters - boolean selectNoLetter = (!Character.isLetterOrDigit(ch) - && noWordSep.indexOf(ch) == -1); - - for(int i = offset - 1; i >= 0; i--) { - ch = lineText.charAt(i); - if (selectNoLetter ^ (!Character.isLetterOrDigit(ch) && - noWordSep.indexOf(ch) == -1)) { - wordStart = i + 1; - break; - } - } - - for(int i = offset; i < lineText.length(); i++) { - ch = lineText.charAt(i); - if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) && - noWordSep.indexOf(ch) == -1)) { - wordEnd = i; - break; - } - } - //} - - int lineStart = getLineStartOffset(line); - select(lineStart + wordStart,lineStart + wordEnd); + setNewSelectionWord( line, offset ); + select(newSelectionStart,newSelectionEnd); + selectWord = true; + selectionAncorStart = selectionStart; + selectionAncorEnd = selectionEnd; /* String lineText = getLineText(line); @@ -2377,7 +2420,10 @@ public class JEditTextArea extends JComponent private void doTripleClick(MouseEvent evt, int line, int offset, int dot) { - select(getLineStartOffset(line),getLineStopOffset(line)-1); + selectLine = true; + select(getLineStartOffset(line),getLineSelectionStopOffset(line)); + selectionAncorStart = selectionStart; + selectionAncorEnd = selectionEnd; } } diff --git a/todo.txt b/todo.txt index 4bb9059da..12e478f17 100644 --- a/todo.txt +++ b/todo.txt @@ -1,27 +1,23 @@ 0196 - -_ checkbox in mode menu smashing into things -_ remove opengl2 for 1.5 and examples - - -_ colors for 2.0 -_ nurbs or other arch stuff for 2.0? - -_ changing number of screens between run causes things to show up off-screen -_ so when running, check to make sure that things are out of the area +X upgrade to Quaqua 7.3.4 on OS X +X fixes checkbox in mode menu smashing into things +C build a new version of the reference _ help casey re-export all applets for the site _ remove .java files, use one central loading.gif and core.jar? _ use a custom applet.html template for the export _ the tool could copy this into the folder just before exporting -_ build a new version of the reference + +_ changing number of screens between run causes things to show up off-screen +_ so when running, check to make sure that things are out of the area _ PDE change indicator partially broken _ http://code.google.com/p/processing/issues/detail?id=620 from peter n lewis -_ Use Selection For Find -_ http://code.google.com/p/processing/issues/detail?id=571 +Fixes from Peter N Lewis incorporated for the next release (0196 or 1.5, whichever comes first). +X Use Selection For Find +X http://code.google.com/p/processing/issues/detail?id=571 _ Double and Triple click and drag behaviour _ http://code.google.com/p/processing/issues/detail?id=576 _ double-clicking whitespace selects adjacent chars @@ -33,6 +29,13 @@ _ Find Previous and FindReplace dialog cleanup _ http://code.google.com/p/processing/issues/detail?id=580 _ bugs 67, 68, 69 (below) cover more of this +_ remove opengl2 for 1.5 and examples + + +2.0 +_ colors for 2.0 +_ nurbs or other arch stuff for 2.0? + _ Internationalization _ http://code.google.com/p/processing/issues/detail?id=593