1 /*
   2  *
   3  * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  *
   9  *   - Redistributions of source code must retain the above copyright
  10  *     notice, this list of conditions and the following disclaimer.
  11  *
  12  *   - Redistributions in binary form must reproduce the above copyright
  13  *     notice, this list of conditions and the following disclaimer in the
  14  *     documentation and/or other materials provided with the distribution.
  15  *
  16  *   - Neither the name of Oracle nor the names of its
  17  *     contributors may be used to endorse or promote products derived
  18  *     from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 
  33 
  34 import java.awt.BorderLayout;
  35 import java.awt.Color;
  36 import java.awt.Component;
  37 import java.awt.FileDialog;
  38 import java.awt.Frame;
  39 import java.awt.Graphics;
  40 import java.awt.GraphicsEnvironment;
  41 import java.awt.event.ActionEvent;
  42 import java.awt.event.ActionListener;
  43 import java.io.File;
  44 import java.io.FileInputStream;
  45 import java.io.FileOutputStream;
  46 import java.io.IOException;
  47 import java.io.ObjectInputStream;
  48 import java.io.ObjectOutput;
  49 import java.io.ObjectOutputStream;
  50 import java.lang.reflect.InvocationTargetException;
  51 import java.util.MissingResourceException;
  52 import java.util.ResourceBundle;
  53 import java.util.logging.Level;
  54 import java.util.logging.Logger;
  55 import javax.swing.AbstractAction;
  56 import javax.swing.Action;
  57 import javax.swing.Icon;
  58 import javax.swing.JButton;
  59 import javax.swing.JComboBox;
  60 import javax.swing.JFrame;
  61 import javax.swing.JMenu;
  62 import javax.swing.JMenuItem;
  63 import javax.swing.JTextPane;
  64 import javax.swing.SwingUtilities;
  65 import javax.swing.text.DefaultStyledDocument;
  66 import javax.swing.text.Document;
  67 import javax.swing.text.JTextComponent;
  68 import javax.swing.text.StyleContext;
  69 import javax.swing.text.StyledEditorKit;
  70 import javax.swing.text.TextAction;
  71 
  72 
  73 /**
  74  * Sample application using JTextPane.
  75  *
  76  * @author Timothy Prinzing
  77  */
  78 @SuppressWarnings("serial")
  79 public class Stylepad extends Notepad {
  80 
  81     private static ResourceBundle resources;
  82     private FileDialog fileDialog;
  83 
  84     private static final String[] MENUBAR_KEYS = {"file", "edit", "color",
  85         "font", "debug"};
  86     private static final String[] FONT_KEYS = {"family1", "family2", "family3",
  87         "family4", "-", "size1", "size2", "size3", "size4", "size5", "-",
  88         "bold", "italic", "underline"};
  89     private static final String[] TOOLBAR_KEYS = {"new", "open", "save", "-",
  90         "cut", "copy", "paste", "-", "bold", "italic", "underline", "-",
  91         "left", "center", "right"};
  92 
  93 
  94     static {
  95         try {
  96             properties.load(Stylepad.class.getResourceAsStream(
  97                     "resources/StylepadSystem.properties"));
  98             resources = ResourceBundle.getBundle("resources.Stylepad");
  99         } catch (MissingResourceException | IOException  mre) {
 100             System.err.println("Stylepad.properties or StylepadSystem.properties not found");
 101             System.exit(0);
 102         }
 103     }
 104 
 105     public Stylepad() {
 106         super();
 107     }
 108 
 109     public static void main(String[] args) {
 110         try {
 111             SwingUtilities.invokeAndWait(new Runnable() {
 112 
 113                 public void run() {
 114                     JFrame frame = new JFrame();
 115                     frame.setTitle(resources.getString("Title"));
 116                     frame.setBackground(Color.lightGray);
 117                     frame.getContentPane().
 118                             setLayout(new BorderLayout());
 119                     Stylepad stylepad = new Stylepad();
 120                     frame.getContentPane().add("Center", stylepad);
 121                     frame.setJMenuBar(stylepad.createMenubar());
 122                     frame.addWindowListener(new AppCloser());
 123                     frame.pack();
 124                     frame.setSize(600, 480);
 125                     frame.setVisible(true);
 126                 }
 127             });
 128         } catch (InterruptedException ex) {
 129             Logger.getLogger(Stylepad.class.getName()).log(Level.SEVERE, null,
 130                     ex);
 131         } catch (InvocationTargetException ex) {
 132             Logger.getLogger(Stylepad.class.getName()).log(Level.SEVERE, null,
 133                     ex);
 134         }
 135     }
 136 
 137     /**
 138      * Fetch the list of actions supported by this
 139      * editor.  It is implemented to return the list
 140      * of actions supported by the superclass
 141      * augmented with the actions defined locally.
 142      */
 143     @Override
 144     public Action[] getActions() {
 145         Action[] defaultActions = {
 146             new NewAction(),
 147             new OpenAction(),
 148             new SaveAction(),
 149             new StyledEditorKit.FontFamilyAction("font-family-SansSerif",
 150             "SansSerif"), };
 151         Action[] a = TextAction.augmentList(super.getActions(), defaultActions);
 152         return a;
 153     }
 154 
 155     /**
 156      * Try and resolve the resource name in the local
 157      * resource file, and if not found fall back to
 158      * the superclass resource file.
 159      */
 160     @Override
 161     protected String getResourceString(String nm) {
 162         String str;
 163         try {
 164             str = Stylepad.resources.getString(nm);
 165         } catch (MissingResourceException mre) {
 166             str = super.getResourceString(nm);
 167         }
 168         return str;
 169     }
 170 
 171     /**
 172      * Create an editor to represent the given document.
 173      */
 174     @Override
 175     protected JTextComponent createEditor() {
 176         StyleContext sc = new StyleContext();
 177         DefaultStyledDocument doc = new DefaultStyledDocument(sc);
 178         initDocument(doc, sc);
 179         JTextPane p = new JTextPane(doc);
 180         p.setDragEnabled(true);
 181 
 182         //p.getCaret().setBlinkRate(0);
 183 
 184         return p;
 185     }
 186 
 187     /**
 188      * Create a menu for the app.  This is redefined to trap
 189      * a couple of special entries for now.
 190      */
 191     @Override
 192     protected JMenu createMenu(String key) {
 193         if (key.equals("color")) {
 194             return createColorMenu();
 195         }
 196         return super.createMenu(key);
 197     }
 198 
 199     @Override
 200     protected String[] getItemKeys(String key) {
 201         switch (key) {
 202             case "font":
 203                 return FONT_KEYS;
 204             default:
 205                 return super.getItemKeys(key);
 206         }
 207     }
 208 
 209     @Override
 210     protected String[] getMenuBarKeys() {
 211         return MENUBAR_KEYS;
 212     }
 213 
 214     @Override
 215     protected String[] getToolBarKeys() {
 216         return TOOLBAR_KEYS;
 217     }
 218 
 219     // this will soon be replaced
 220     JMenu createColorMenu() {
 221         ActionListener a;
 222         JMenuItem mi;
 223         JMenu menu = new JMenu(getResourceString("color" + labelSuffix));
 224         mi = new JMenuItem(resources.getString("Red"));
 225         mi.setHorizontalTextPosition(JButton.RIGHT);
 226         mi.setIcon(new ColoredSquare(Color.red));
 227         a =
 228                 new StyledEditorKit.ForegroundAction("set-foreground-red",
 229                 Color.red);
 230         //a = new ColorAction(se, Color.red);
 231         mi.addActionListener(a);
 232         menu.add(mi);
 233         mi = new JMenuItem(resources.getString("Green"));
 234         mi.setHorizontalTextPosition(JButton.RIGHT);
 235         mi.setIcon(new ColoredSquare(Color.green));
 236         a = new StyledEditorKit.ForegroundAction("set-foreground-green",
 237                 Color.green);
 238         //a = new ColorAction(se, Color.green);
 239         mi.addActionListener(a);
 240         menu.add(mi);
 241         mi = new JMenuItem(resources.getString("Blue"));
 242         mi.setHorizontalTextPosition(JButton.RIGHT);
 243         mi.setIcon(new ColoredSquare(Color.blue));
 244         a = new StyledEditorKit.ForegroundAction("set-foreground-blue",
 245                 Color.blue);
 246         //a = new ColorAction(se, Color.blue);
 247         mi.addActionListener(a);
 248         menu.add(mi);
 249 
 250         return menu;
 251     }
 252 
 253     void initDocument(DefaultStyledDocument doc, StyleContext sc) {
 254         Wonderland w = new Wonderland(doc, sc);
 255         w.loadDocument();
 256     }
 257 
 258     JComboBox createFamilyChoices() {
 259         JComboBox b = new JComboBox();
 260         String[] fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment().
 261                 getAvailableFontFamilyNames();
 262         for (String fontName : fontNames) {
 263             b.addItem(fontName);
 264         }
 265         return b;
 266     }
 267 
 268 
 269     /**
 270      * Trys to read a file which is assumed to be a
 271      * serialization of a document.
 272      */
 273     class OpenAction extends AbstractAction {
 274 
 275         OpenAction() {
 276             super(openAction);
 277         }
 278 
 279         @Override
 280         public void actionPerformed(ActionEvent e) {
 281             Frame frame = getFrame();
 282             if (fileDialog == null) {
 283                 fileDialog = new FileDialog(frame);
 284             }
 285             fileDialog.setMode(FileDialog.LOAD);
 286             fileDialog.setVisible(true);
 287 
 288             String file = fileDialog.getFile();
 289             if (file == null) {
 290                 return;
 291             }
 292             String directory = fileDialog.getDirectory();
 293             File f = new File(directory, file);
 294             if (f.exists()) {
 295                 try {
 296                     FileInputStream fin = new FileInputStream(f);
 297                     ObjectInputStream istrm = new ObjectInputStream(fin);
 298                     Document doc = (Document) istrm.readObject();
 299                     if (getEditor().getDocument() != null) {
 300                         getEditor().getDocument().removeUndoableEditListener(
 301                                 undoHandler);
 302                     }
 303                     getEditor().setDocument(doc);
 304                     doc.addUndoableEditListener(undoHandler);
 305                     resetUndoManager();
 306                     frame.setTitle(file);
 307                     validate();
 308                 } catch (IOException io) {
 309                     // should put in status panel
 310                     System.err.println("IOException: " + io.getMessage());
 311                 } catch (ClassNotFoundException cnf) {
 312                     // should put in status panel
 313                     System.err.println("Class not found: " + cnf.getMessage());
 314                 }
 315             } else {
 316                 // should put in status panel
 317                 System.err.println("No such file: " + f);
 318             }
 319         }
 320     }
 321 
 322 
 323     /**
 324      * Trys to write the document as a serialization.
 325      */
 326     class SaveAction extends AbstractAction {
 327 
 328         SaveAction() {
 329             super(saveAction);
 330         }
 331 
 332         @Override
 333         public void actionPerformed(ActionEvent e) {
 334             Frame frame = getFrame();
 335             if (fileDialog == null) {
 336                 fileDialog = new FileDialog(frame);
 337             }
 338             fileDialog.setMode(FileDialog.SAVE);
 339             fileDialog.setVisible(true);
 340             String file = fileDialog.getFile();
 341             if (file == null) {
 342                 return;
 343             }
 344             String directory = fileDialog.getDirectory();
 345             File f = new File(directory, file);
 346             try {
 347                 FileOutputStream fstrm = new FileOutputStream(f);
 348                 ObjectOutput ostrm = new ObjectOutputStream(fstrm);
 349                 ostrm.writeObject(getEditor().getDocument());
 350                 ostrm.flush();
 351                 frame.setTitle(f.getName());
 352             } catch (IOException io) {
 353                 // should put in status panel
 354                 System.err.println("IOException: " + io.getMessage());
 355             }
 356         }
 357     }
 358 
 359 
 360     /**
 361      * Creates an empty document.
 362      */
 363     class NewAction extends AbstractAction {
 364 
 365         NewAction() {
 366             super(newAction);
 367         }
 368 
 369         @Override
 370         public void actionPerformed(ActionEvent e) {
 371             if (getEditor().getDocument() != null) {
 372                 getEditor().getDocument().removeUndoableEditListener(undoHandler);
 373             }
 374             getEditor().setDocument(new DefaultStyledDocument());
 375             getEditor().getDocument().addUndoableEditListener(undoHandler);
 376             resetUndoManager();
 377             getFrame().setTitle(resources.getString("Title"));
 378             validate();
 379         }
 380     }
 381 
 382 
 383     class ColoredSquare implements Icon {
 384 
 385         Color color;
 386 
 387         public ColoredSquare(Color c) {
 388             this.color = c;
 389         }
 390 
 391         @Override
 392         public void paintIcon(Component c, Graphics g, int x, int y) {
 393             Color oldColor = g.getColor();
 394             g.setColor(color);
 395             g.fill3DRect(x, y, getIconWidth(), getIconHeight(), true);
 396             g.setColor(oldColor);
 397         }
 398 
 399         @Override
 400         public int getIconWidth() {
 401             return 12;
 402         }
 403 
 404         @Override
 405         public int getIconHeight() {
 406             return 12;
 407         }
 408     }
 409 }