diff --git a/app/Editor.java b/app/Editor.java index ef8077522..956cfd556 100644 --- a/app/Editor.java +++ b/app/Editor.java @@ -653,7 +653,7 @@ public class Editor extends JFrame }); menu.add(item); - item = newJMenuItem("Color Picker", 'P', false); + item = new JMenuItem("Color Picker"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { @@ -665,6 +665,18 @@ public class Editor extends JFrame }); menu.add(item); + item = new JMenuItem("Format for Discourse"); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + new DiscourseFormat(Editor.this).show(); + } + }); + } + }); + menu.add(item); + item = new JMenuItem("Archive Sketch"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { diff --git a/app/syntax/TextAreaPainter.java b/app/syntax/TextAreaPainter.java index fb7eeeef3..b1e4ccc82 100644 --- a/app/syntax/TextAreaPainter.java +++ b/app/syntax/TextAreaPainter.java @@ -475,6 +475,42 @@ public class TextAreaPainter extends JComponent implements TabExpander Token currentLineTokens; Segment currentLine; + /** + * Accessor used by tools that want to hook in and grab the formatting. + */ + public int getCurrentLineIndex() { + return currentLineIndex; + } + + /** + * Accessor used by tools that want to hook in and grab the formatting. + */ + public void setCurrentLineIndex(int what) { + currentLineIndex = what; + } + + /** + * Accessor used by tools that want to hook in and grab the formatting. + */ + public Token getCurrentLineTokens() { + return currentLineTokens; + } + + /** + * Accessor used by tools that want to hook in and grab the formatting. + */ + public void setCurrentLineTokens(Token tokens) { + currentLineTokens = tokens; + } + + /** + * Accessor used by tools that want to hook in and grab the formatting. + */ + public Segment getCurrentLine() { + return currentLine; + } + + // protected members protected JEditTextArea textArea; diff --git a/app/tools/DiscourseFormat.java b/app/tools/DiscourseFormat.java new file mode 100644 index 000000000..6c8754264 --- /dev/null +++ b/app/tools/DiscourseFormat.java @@ -0,0 +1,391 @@ +/* + * Original code by owd, http://usuarios.iponet.es/imoreta + * + * Notes from the original source: + * Discourse.java This is a dirty-mix source. + * NOTE that: No macs and no keyboard. Unreliable source. + * Only format processing code using fontMetrics. + * It works under my windows XP + PentiumIV + Processing 0091. + */ + +package processing.app.tools; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.text.Segment; + +import processing.app.*; +import processing.app.syntax.*; +import processing.core.PApplet; + + +public class DiscourseFormat /*extends JPanel implements WindowListener*/ { + + //static final String WINDOW_TITLE = "Code ready: processing.org/discourse"; + static final String WINDOW_TITLE = "Format for Discourse"; + + // p5 icon for the window + static Image icon; + + Editor editor; + JEditTextArea textarea; + + // Parent editor JTextArea + JEditTextArea parent; + + // False listener (no NullPointerException at processKeyEvent, + // but gives other problems like the § on the Tab) + DiscourseListener listener; + + JFrame frame; + + // One window only (if window exists, update()) + //static boolean active = false; + + //Discourse.formatDiscourse(textarea); + + /** + * Creates a new window with the formated (YaBB tags) sketchcode + * from the actual Processing Tab ready to send to the processing discourse + * web (copy & paste) + */ + public DiscourseFormat(Editor editor) { + //super(new GridBagLayout()); + + this.editor = editor; + this.parent = editor.textarea; + + textarea = new JEditTextArea(new PdeTextAreaDefaults()); + textarea.setRightClickPopup(new DiscourseTextAreaPopup()); + textarea.setTokenMarker(new PdeKeywords()); + textarea.setHorizontalOffset(6); + + //GridBagConstraints c = new GridBagConstraints(); + //c.fill = GridBagConstraints.BOTH; + //c.weightx = 1.0; + //c.weighty = 1.0; + //add(textarea, c); + + textarea.setEditable(false); + + //frame.addWindowListener(this); + //listener = new DiscourseListener(textarea); + wlistener = parent.editorListener; + + //Make sure we have nice window decorations. + //Sure... false, false... + //JFrame.setDefaultLookAndFeelDecorated(false); + + // Create and set up the window. + frame = new JFrame(WINDOW_TITLE); + frame.setSize(500, 500); + + // set the window icon + try { + icon = Base.getImage("icon.gif", frame); + frame.setIconImage(icon); + } catch (Exception e) { } // fail silently, no big whup + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + // Create and set up the content pane. + //JComponent newContentPane = new Discourse(); + //newContentPane.setOpaque(true); //content panes must be opaque + //frame.setContentPane(newContentPane); + Container pain = frame.getContentPane(); + pain.setLayout(new BorderLayout()); + pain.add(textarea, BorderLayout.CENTER); + + frame.setResizable(true); + + // Display the window + frame.pack(); + //frame.setVisible(true); + } + + + public void show() { + // Format and render sketchcode + + // [code] tag cancels other tags, using [quote] + StringBuffer cf = new StringBuffer("[quote] \n \n"); + + // Line by line + for (int i = 0; i < parent.getLineCount(); i++) { + cf.append(formatCode(i)); + } + + cf.append("\n [/quote]"); + + // Send the text to the textarea + textarea.setText(cf.toString()); + textarea.select(0, 0); + + frame.show(); + } + + + /* + // Update contents + public static void update() { + // + preliminars(); + frame.toFront(); + } + */ + + /** Read parent textarea */ + /* + public static void formatDiscourse(JEditTextArea parentTxa) { + //String code = parent.getText(0, textarea.getDocumentLength()); + parent = parentTxa; + if (Discourse.active) { + // Discourse window exists + Discourse.update(); + } else { + // Creates a new discourse window + Discourse.createAndShowGUI(); + } + } + */ + + /* + // Returns a string from a char + static String character(char ch) { + return String.valueOf(ch); + //Character Ch = new Character(ch); + //return Ch.toString(); + } + */ + + + // A terrible headache... + public String formatCode(int line) { + StringBuffer cf = new StringBuffer(); + + // Segment + Segment lineSegment = new Segment(); + + TextAreaPainter painter = parent.getPainter(); + TokenMarker tokenMarker = parent.getTokenMarker(); + + // Use painter's cached info for speed + FontMetrics fm = painter.getFontMetrics(); + + // Jump empty lines + // if (parent.getLineLength(line) == 0) cf.concat(character('\n')); + // return cf; + + // get line text from parent textarea + parent.getLineText(line, lineSegment); + + char[] segmentArray = lineSegment.array; + int limit = lineSegment.getEndIndex(); + int segmentOffset = lineSegment.offset; + int segmentCount = lineSegment.count; + int width = 0; //parent.getHorizontalOffset(); + + int x = 0; //parent.getHorizontalOffset(); + + // If syntax coloring is disabled, do simple translation + if (tokenMarker == null) { + for (int j = 0; j < segmentCount; j++) { + char c = segmentArray[j + segmentOffset]; + cf = cf.append(c); //concat(character(c)); + int charWidth; + if (c == '\t') { + charWidth = (int) painter.nextTabStop(width, j) - width; + } else { + charWidth = fm.charWidth(c); + } + width += charWidth; + } + + } else { + // If syntax coloring is enabled, we have to do this + // because tokens can vary in width + Token tokens; + if ((painter.getCurrentLineIndex() == line) && + (painter.getCurrentLineTokens() != null)) { + tokens = painter.getCurrentLineTokens(); + + } else { + painter.setCurrentLineIndex(line); + //painter.currentLineIndex = line; + painter.setCurrentLineTokens(tokenMarker.markTokens(lineSegment, line)); + tokens = painter.getCurrentLineTokens(); + } + + int offset = 0; + Toolkit toolkit = painter.getToolkit(); + Font defaultFont = painter.getFont(); + SyntaxStyle[] styles = painter.getStyles(); + + for (;;) { + byte id = tokens.id; + if (id == Token.END) { + char c = segmentArray[segmentOffset + offset]; + if (segmentOffset + offset < limit) { + cf.append(c); + } else { + cf.append('\n'); + } + return cf.toString(); + } + if (id == Token.NULL) { + fm = painter.getFontMetrics(); + } else { + // Place open tags [] + //cf.append("[color=" + color() + "]"); + cf.append("[color=#"); + cf.append(PApplet.hex(styles[id].getColor().getRGB() & 0xFFFFFF, 6)); + cf.append("]"); + + if (styles[id].isBold()) + cf.append("[b]"); + + fm = styles[id].getFontMetrics(defaultFont); + } + int length = tokens.length; + + for (int j = 0; j < length; j++) { + char c = segmentArray[segmentOffset + offset + j]; + cf.append(c); + // Place close tags [/] + if (j == (length - 1) && id != Token.NULL && styles[id].isBold()) + cf.append("[/b]"); + if (j == (length - 1) && id != Token.NULL) + cf.append("[/color]"); + int charWidth; + if (c == '\t') { + charWidth = (int) painter + .nextTabStop(width, offset + j) + - width; + } else { + charWidth = fm.charWidth(c); + } + width += charWidth; + } + offset += length; + tokens = tokens.next; + } + } + return cf.toString(); + } + + + /* Return a string [#rrggbb] from color */ + //public static String color(Color rgbColor) { + /* + int r = rgbColor.getRed(); + int g = rgbColor.getGreen(); + int b = rgbColor.getBlue(); + + String rx = r >= 16 ? Integer.toHexString(r) : "0" + + Integer.toHexString(r); + String gx = g >= 16 ? Integer.toHexString(g) : "0" + + Integer.toHexString(g); + String bx = b >= 16 ? Integer.toHexString(b) : "0" + + Integer.toHexString(b); + rx = rx.toUpperCase(); + gx = gx.toUpperCase(); + bx = bx.toUpperCase(); + + return "#" + rx + gx + bx; + */ + + /* + return "#" + + PApplet.hex(rgbColor.getRed(), 2) + + PApplet.hex(rgbColor.getGreen(), 2) + + PApplet.hex(rgbColor.getBlue(), 2); + */ + //return "#" + PApplet.hex(rgbColor.getRGB() & 0xFFFFFF, 6); + //} + + /* + // M*erda... voids, voids, voids... Really needed? + // Eclipse says YES (?) + public void windowOpened(WindowEvent e) { + } + + public void windowIconified(WindowEvent e) { + } + + public void windowDeiconified(WindowEvent e) { + } + + public void windowActivated(WindowEvent e) { + } + + public void windowDeactivated(WindowEvent e) { + } + + public void windowClosing(WindowEvent arg0) { + } + */ + + + /** + * Returns the discourse popup menu. Another features can be added: format + * selected text with a determinated tag (I'm thinking about [url]selected + * text[/url]) + */ + class DiscourseTextAreaPopup extends JPopupMenu { + //protected ReferenceKeys referenceItems = new ReferenceKeys(); + //String currentDir = System.getProperty("user.dir"); + //String referenceFile = null; + //JMenuItem cutItem, copyItem; + JMenuItem copyItem; + //JMenuItem referenceItem; + + public DiscourseTextAreaPopup() { + JMenuItem item; + + copyItem = new JMenuItem("Copy"); + copyItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + textarea.copy(); + } + }); + this.add(copyItem); + + item = new JMenuItem("Select All"); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + textarea.selectAll(); + } + }); + this.add(item); + } + + // if no text is selected, disable copy menu item + public void show(Component component, int x, int y) { + if (textarea.isSelectionActive()) { + copyItem.setEnabled(true); + + } else { + copyItem.setEnabled(false); + } + super.show(component, x, y); + } + } + + + /* + // A false listener (use the mouse) + public class DiscourseListener { + + public DiscourseListener(JEditTextArea thisTextarea) { + // I'm a... I know this gives peoblems, but all this code + // is a funny hacking experiment + thisTextarea.editorListener = parent.editorListener; + } + + public boolean keyPressed(KeyEvent event) { + System.out.println("Is your mouse lone some tonight..."); + return false; + } + } + */ +} \ No newline at end of file diff --git a/todo.txt b/todo.txt index 0fb6233c9..26617e0ce 100644 --- a/todo.txt +++ b/todo.txt @@ -4,6 +4,8 @@ X undoing to the code's original state won't unset it as "modified" X http://dev.processing.org/bugs/show_bug.cgi?id=248 X internal color picker X could be a separate window that's always around if needed +o why was i allowed to save changes to the rgbcube example? +o wasn't, just that the error didn't come through. yay osx! _ check on whether linux is running properly _ probably threading issue, 98 doesn't have any trouble @@ -32,12 +34,6 @@ _ http://developer.apple.com/qa/qa2005/qa1295.html _ find/build universal version of jogl _ find/build universal version of rxtx -_ need method for showing prefs for the tools -_ add pref to handle date vs. increment on archive sketch - -_ why was i allowed to save changes to the rgbcube example? -_ wasn't, just that the error didn't come through. yay osx! - _ when a conflicting library is found, need to report it _ altho prolly only when it's actually different (md5hash it?) @@ -545,17 +541,24 @@ _ http://dev.processing.org/bugs/show_bug.cgi?id=88 TOOLS / General + +_ need method for showing prefs for the tools +_ add pref to handle date vs. increment on archive sketch _ make dynamically loaded plugins and "tools" menu _ http://dev.processing.org/bugs/show_bug.cgi?id=124 _ break out beautify as its own plugin _ tools api: -_ setup() -> run when p5 is first launched +_ init() -> run when p5 is first launched _ isAvailable() -> true/false whether the option should be dimmed +_ skip this for now, not sending events to tools all the time _ show() -> open the window or run the tool _ is there a better name than show() since not all will be visible? _ maybe it's just a run() method launched via invokeLater()? +_ hide() -> hide the tool's frame _ getDefaultShortcut() -> returns the default shortcut this tools wants +_ store this in the data file instead _ needs to be able to get current sketch code +_ look at the pretty-formatting code to see what else needs to be in api o getMenu() -> return non-null the tool is a submenu o no, bad idea.. don't want a zillion submenus on things _ need a proper means to handle command keys for tools