simplyhtml-0.17.3/0040755 0000000 0000000 00000000000 13147565156 012553 5ustar000000000 0000000 simplyhtml-0.17.3/dist.gradle0100644 0000000 0000000 00000001600 12677550144 014666 0ustar000000000 0000000 task binDistrib(type: Zip) { archiveName = 'simplyhtml_bin-' + project.version + '.zip' destinationDir = rootDir from(configurations.compile) from(mainJar.outputs.files) from(helpJar.outputs.files) from(rootDir) { include('*.txt') } into('simplyhtml-' + project.version) dependsOn mainJar dependsOn helpJar } task sourceDistrib(type: Tar) { archiveName = 'simplyhtml_src-' + project.version + '.tar.gz' destinationDir = rootDir compression = 'GZIP' includeEmptyDirs = false from(rootDir) { include('src/**') include('*.txt') include('*.gradle') } into('simplyhtml-' + project.version) } task dist { dependsOn binDistrib dependsOn sourceDistrib } task cleanDist(type: Delete) { delete binDistrib.outputs.files delete sourceDistrib.outputs.files } clean.dependsOn cleanDist simplyhtml-0.17.3/readme.txt0100644 0000000 0000000 00000006301 12114157751 014535 0ustar000000000 0000000 SimplyHTML readme file Stage 13, May 24th, 2009 Copyright (c) 2002, 2003 Ulrich Hilger, 2008 Dimitri Polivaev http://simplyhtml.sf.net/ (see 'License' below) This file contains ------------------ About SimplyHTML Downloads Requirements Installation Usage Compilation License About SimplyHTML ---------------- SimplyHTML is an application for text processing. It stores documents as HTML files in combination with Cascading Style Sheets (CSS). SimplyHTML is not intended to be used as an editor for web pages. The application combines text processing features as known from popular word processors with a simple and generic way of storing textual information and styles. Downloads --------- The SimplyHTML offers 3 packages you can download: - SimplyHTML_bin_.zip - this is the one you need! - SimplyHTML_manual_.zip - an HTML version of the help (only for your convenience, the same help is available within the application) - SimplyHTML_src_.tar.gz - the source code, if you don't know what it is, you don't need it! Requirements ------------ To use SimplyHTML, you will need to have a Java JRE (Java Runtime Environment) installed, with a version equal or higher to 1.4.2. To compile and run the sources, a Java 2 Standard Edition 1.4 (J2SE) or higher Development Kit (JDK) is required. J2SE and/or JRE can be obtained at http://java.sun.com/javase/downloads/ Installation ------------ Once you've downloaded the 'bin' and possibly the 'manual' zip files, extract them (keeping the directory structure) into a directory of your choice, e.g. "C:\Program Files\" or "/opt", a directory "SimplyHTML" will be created, with everything you need within it. In the below lines, we'll call this directory . Note: Contents of the downloaded zip file can be restored by using one of the many applications capable to extract ZIP files. If you do not have such an application, you can use application Extractor available free at http://www.calcom.de/eng/product/xtract.htm Usage ----- Starting SimplyHTML can be as easy as double-clicking on the file "/lib/SimplyHTML.jar". If it doesn't work, try to call the following from the command line: Under Windows: javaw -jar "\lib\SimplyHTML.jar" Under Linux and other UN*X-like OSs: java -jar "/lib/SimplyHTML.jar" If you've downloaded the manual, you can see it in your browser by just pointing it to "/manual/index.htm". Compilation ----------- If you'd like to compile SimplyHTML yourself, we would assume that you know what you're doing, hence only the highlights: - make sure that you have jhall.jar (from JavaHelp2) and gnu-regexp.jar somewhere on your system and adapt the properties 'jhall.jar' and 'gnu-regexp.jar' within src/build.xml. - the call of 'ant' within the 'src' sub-directory should then create everything you need one level above. License ------- This distribution of SimplyHTML is published under the terms and conditions of the GNU General Public License. To read the license, please open file 'gpl.txt' which is part of this package too. simplyhtml-0.17.3/gpl.txt0100644 0000000 0000000 00000043133 12114157751 014066 0ustar000000000 0000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. simplyhtml-0.17.3/src/0040755 0000000 0000000 00000000000 12677551350 013340 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/0040755 0000000 0000000 00000000000 12114157751 014107 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/0040755 0000000 0000000 00000000000 12114157751 015715 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/0040755 0000000 0000000 00000000000 12114157751 016475 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/0040755 0000000 0000000 00000000000 13147564753 017463 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/HTMLTextSelection.java0100644 0000000 0000000 00000010354 12114157751 023572 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; /** * A transferable for HTML text. * *

It can be used in drag and drop operations or in copy and paste * operations. Additional to HTMLText it supports the * String data flavor.

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * * * @see java.awt.datatransfer.DataFlavor.stringFlavor * @see java.awt.datatransfer.DataFlavor.plainTextFlavor * @see com.lightdev.app.shtm.HTMLText */ class HTMLTextSelection implements Transferable { /** index of HTML text data flavor */ private static final int HTML_TEXT = 0; /** the data to transfer */ private final HTMLText data; /** the data flavor of this transferable */ private static final DataFlavor[] flavors = { new DataFlavor(com.lightdev.app.shtm.HTMLText.class, "HTMLText") }; /** * construct a HTMLTextSelection with a chunk * of styled text. * * @param data - a HTMLText object * * @see com.lightdev.app.shtm.HTMLText */ public HTMLTextSelection(final HTMLText data) { this.data = data; } /* ---- start of Transferable implementation ----------------------------*/ /** * Returns an array of DataFlavor objects indicating the flavors the data * can be provided in. The array should be ordered according to preference * for providing the data (from most richly descriptive to least descriptive). * @return an array of data flavors in which this data can be transferred */ public DataFlavor[] getTransferDataFlavors() { return (DataFlavor[]) flavors.clone(); } /** * Returns whether or not the specified data flavor is supported for * this object. * @param flavor the requested flavor for the data * @return boolean indicating wjether or not the data flavor is supported */ public boolean isDataFlavorSupported(final DataFlavor flavor) { for (int i = 0; i < flavors.length; i++) { if (flavors[i].equals(flavor)) { return true; } } return false; } /** * Returns an object which represents the data to be transferred. The class * of the object returned is defined by the representation class of the flavor. * * @param flavor the requested flavor for the data * @see DataFlavor#getRepresentationClass * @exception IOException if the data is no longer available * in the requested flavor. * @exception UnsupportedFlavorException if the requested data flavor is * not supported. */ public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (flavor.equals(flavors[HTML_TEXT])) { return data; } else { throw new UnsupportedFlavorException(flavor); } } /* ----------- end of Transferable implementation ------------------- */ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/ImageDialog.java0100644 0000000 0000000 00000064476 12114157751 022473 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.io.StringWriter; import java.util.Vector; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLDocument; import com.sun.demo.ExampleFileFilter; /** * A dialog providing an image repository and a way to edit display options * for images from the repository. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class ImageDialog extends DialogShell implements ActionListener, ListSelectionListener, ChangeListener { /** directory this ImageDialog maintains */ private File imgDir; /** KeyListener for watching changes in the scale text field */ private final KeyHandler keyHandler = new KeyHandler(); /** FocusListener for watching changes in the scale text field */ private final FocusHandler focusHandler = new FocusHandler(); private final SimpleAttributeSet originalAttributes = new SimpleAttributeSet(); /** * indicates whether or not changes in a SizeSelectorPanel are * to be processed. Usually, changes caused by a method of this * class are to be ignored */ private boolean ignoreChangeEvents = false; /** list with images in this image repository */ private JList imgFileList; /** button to add an image file to the repository */ private JButton addImgBtn; /** button to delete an image file from the repository */ private JButton delImgBtn; /** text field for manipulating the scale of an image */ private JTextField scale; /** component to manipulate the image width */ private SizeSelectorPanel imgWidth; /** component to manipulate the image height */ private SizeSelectorPanel imgHeight; /** component to display the original width of an image */ private JLabel oWidth; /** component to display the original height of an image */ private JLabel oHeight; /** component to preview an image */ private ImagePreview preview; /** component to scroll an image inside the preview */ private JScrollPane scPrev; /** * contains all components having attributes for the image represented * in this ImageDialog */ private final Vector attributeComponents = new Vector(); /** the help id for this dialog */ private static final String helpTopicId = "item166"; /** the document the image came from, if any */ private SHTMLDocument doc; /** * construct a new ImageDialog * * @param parent the parent frame of this ImageDialog * @param title the title of this ImageDialog * @param imgDir the directory of the image repository */ public ImageDialog(final Dialog parent, final String title, final File imgDir) { super(parent, title, helpTopicId); initDialog(title, imgDir); } /** * construct a new ImageDialog * * @param parent the parent frame of this ImageDialog * @param title the title of this ImageDialog * @param imgDir the directory of the image repository */ public ImageDialog(final Frame parent, final String title, final File imgDir) { super(parent, title, helpTopicId); initDialog(title, imgDir); } public ImageDialog(final Frame parent, final String title, final File imgDir, final SHTMLDocument sourceDoc) { super(parent, title, helpTopicId); doc = sourceDoc; initDialog(title, imgDir); } /** * build the dialog contents after construction * * @param title the title of this ImageDialog * @param imgDir the directory of the image repository */ private void initDialog(final String title, final File imgDir) { //System.out.println("ImageDialog.initDialog imgDir=" + imgDir.getAbsolutePath()); this.imgDir = imgDir; Dimension dim; // create an image directory panel final JPanel dirPanel = new JPanel(new BorderLayout()); dirPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("imgDirPanelTitle"))); // create a list to disply image files in imgFileList = new JList(); dim = new Dimension(100, 100); imgFileList.setMinimumSize(dim); imgFileList.setPreferredSize(dim); imgFileList.addListSelectionListener(this); updateFileList(); // create a panel with action buttons for image files final JPanel dirBtnPanel = new JPanel(); // create image directory action buttons addImgBtn = new JButton(Util.getResourceString("addImgBtnTitle")); addImgBtn.addActionListener(this); delImgBtn = new JButton(Util.getResourceString("delImgBtnTitle")); delImgBtn.addActionListener(this); // add action buttons to button panel dirBtnPanel.add(addImgBtn); dirBtnPanel.add(delImgBtn); // add components to image directory panel dirPanel.add(imgFileList, BorderLayout.CENTER); dirPanel.add(dirBtnPanel, BorderLayout.SOUTH); // create an image preview panel final JPanel previewPanel = new JPanel(new BorderLayout()); previewPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("imgPreviewPanelTitle"))); // add a new ImagePreview object to the preview panel preview = new ImagePreview(); dim = new Dimension(250, 250); preview.setMinimumSize(dim); preview.setPreferredSize(dim); scPrev = new JScrollPane(preview); previewPanel.add(scPrev, BorderLayout.CENTER); // layout and constraints to use later on final GridBagLayout g = new GridBagLayout(); final GridBagConstraints c = new GridBagConstraints(); // create an image properties panel final JPanel eastPanel = new JPanel(new BorderLayout()); final JPanel propertiesPanel = new JPanel(g); eastPanel.add(propertiesPanel, BorderLayout.NORTH); eastPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("imgPropertiesPanelTitle"))); // add scale component Util.addGridBagComponent(propertiesPanel, new JLabel(Util.getResourceString("imgScaleLabel")), g, c, 0, 0, GridBagConstraints.EAST); scale = new JTextField(); scale.addKeyListener(keyHandler); scale.addFocusListener(focusHandler); dim = new Dimension(50, 20); scale.setMinimumSize(dim); scale.setPreferredSize(dim); final JPanel helperPanel = new JPanel(); helperPanel.add(scale); helperPanel.add(new JLabel(SizeSelectorPanel.UNIT_PERCENT, SwingConstants.LEFT)); Util.addGridBagComponent(propertiesPanel, helperPanel, g, c, 1, 0, GridBagConstraints.WEST); // add width component Util.addGridBagComponent(propertiesPanel, new JLabel(Util.getResourceString("imgWidthLabel")), g, c, 0, 1, GridBagConstraints.EAST); imgWidth = new SizeSelectorPanel(HTML.Attribute.WIDTH, null, false, SizeSelectorPanel.TYPE_LABEL); attributeComponents.addElement(imgWidth); imgWidth.getValueSelector().addChangeListener(this); Util.addGridBagComponent(propertiesPanel, imgWidth, g, c, 1, 1, GridBagConstraints.WEST); // add height component Util.addGridBagComponent(propertiesPanel, new JLabel(Util.getResourceString("imgHeightLabel")), g, c, 0, 2, GridBagConstraints.EAST); imgHeight = new SizeSelectorPanel(HTML.Attribute.HEIGHT, null, false, SizeSelectorPanel.TYPE_LABEL); attributeComponents.addElement(imgHeight); imgHeight.getValueSelector().addChangeListener(this); Util.addGridBagComponent(propertiesPanel, imgHeight, g, c, 1, 2, GridBagConstraints.WEST); // add hspace component Util.addGridBagComponent(propertiesPanel, new JLabel(Util.getResourceString("imgHSpaceLabel")), g, c, 0, 3, GridBagConstraints.EAST); final SizeSelectorPanel hSpace = new SizeSelectorPanel(HTML.Attribute.HSPACE, null, false, SizeSelectorPanel.TYPE_LABEL); attributeComponents.addElement(hSpace); Util.addGridBagComponent(propertiesPanel, hSpace, g, c, 1, 3, GridBagConstraints.WEST); // add vspace component Util.addGridBagComponent(propertiesPanel, new JLabel(Util.getResourceString("imgVSpaceLabel")), g, c, 0, 4, GridBagConstraints.EAST); final SizeSelectorPanel vSpace = new SizeSelectorPanel(HTML.Attribute.VSPACE, null, false, SizeSelectorPanel.TYPE_LABEL); attributeComponents.addElement(vSpace); Util.addGridBagComponent(propertiesPanel, vSpace, g, c, 1, 4, GridBagConstraints.WEST); // add alignment component Util.addGridBagComponent(propertiesPanel, new JLabel(Util.getResourceString("imgAlignLabel")), g, c, 0, 5, GridBagConstraints.EAST); final String[] items = new String[] { Util.getResourceString("imgAlignTop"), Util.getResourceString("imgAlignMiddle"), Util.getResourceString("imgAlignBottom"), Util.getResourceString("imgAlignLeft"), Util.getResourceString("imgAlignCenter"), Util.getResourceString("imgAlignRight") }; final String[] names = new String[] { "top", "middle", "bottom", "left", "center", "right" }; final AttributeComboBox imgAlign = new AttributeComboBox(items, names, null, HTML.Attribute.ALIGN); attributeComponents.addElement(imgAlign); Util.addGridBagComponent(propertiesPanel, imgAlign, g, c, 1, 5, GridBagConstraints.WEST); // add original width component Util.addGridBagComponent(propertiesPanel, new JLabel(Util.getResourceString("oWidthLabel")), g, c, 0, 6, GridBagConstraints.EAST); oWidth = new JLabel(""); Util.addGridBagComponent(propertiesPanel, oWidth, g, c, 1, 6, GridBagConstraints.WEST); // add original height component Util.addGridBagComponent(propertiesPanel, new JLabel(Util.getResourceString("oHeightLabel")), g, c, 0, 7, GridBagConstraints.EAST); oHeight = new JLabel(""); Util.addGridBagComponent(propertiesPanel, oHeight, g, c, 1, 7, GridBagConstraints.WEST); // add border component Util.addGridBagComponent(propertiesPanel, new JLabel(Util.getResourceString("imgBorderLabel")), g, c, 0, 8, GridBagConstraints.EAST); final SizeSelectorPanel imgBorder = new SizeSelectorPanel(HTML.Attribute.BORDER, null, false, SizeSelectorPanel.TYPE_LABEL); attributeComponents.addElement(imgBorder); Util.addGridBagComponent(propertiesPanel, imgBorder, g, c, 1, 8, GridBagConstraints.WEST); // add to content pane of DialogShell final Container contentPane = super.getContentPane(); contentPane.add(dirPanel, BorderLayout.WEST); contentPane.add(previewPanel, BorderLayout.CENTER); contentPane.add(eastPanel, BorderLayout.EAST); // cause optimal placement of all elements pack(); scPrev.addComponentListener(new ResizeListener()); } public Integer getImgWidth() { return imgWidth.getIntValue(); } public Integer getImgHeight() { return imgHeight.getIntValue(); } /** * set dialog content from a given set of image attributes * * @param a the set of attributes to set dialog contents from */ public void setImageAttributes(final AttributeSet a) { //System.out.println("ImageDialog.setImageAttributes"); ignoreChangeEvents = true; originalAttributes.addAttributes(a); if (a.isDefined(HTML.Attribute.SRC)) { File imgFile = null; if (doc != null) { imgFile = new File(Util.resolveRelativePath(a.getAttribute(HTML.Attribute.SRC).toString(), doc .getBase().getFile())); } else { imgFile = new File(a.getAttribute(HTML.Attribute.SRC).toString()); } //System.out.println("ImageDialog.setImageAttribute imgFile=" + imgFile.getAbsolutePath()); imgFileList.setSelectedValue(imgFile.getName().toLowerCase(), true); } for (int i = 0; i < attributeComponents.size(); i++) { ((AttributeComponent) attributeComponents.get(i)).setValue(a); } if (a.isDefined(HTML.Attribute.WIDTH)) { preview.setPreviewWidth(Integer.parseInt(a.getAttribute(HTML.Attribute.WIDTH).toString())); } if (a.isDefined(HTML.Attribute.HEIGHT)) { preview.setPreviewHeight(Integer.parseInt(a.getAttribute(HTML.Attribute.HEIGHT).toString())); } final int scalePct = preview.getScale(); scale.setText(Integer.toString(scalePct)); ignoreChangeEvents = false; } public void setImage(final String fName, final String w, final String h) { //System.out.println("ImageDialog.setImage fName=" + fName); imgFileList.setSelectedValue(new File(fName).getName(), true); preview.setImage(new ImageIcon(fName)); try { if (w != null && w.length() > 0) { preview.setPreviewWidth(Integer.parseInt(w)); } if (h != null && h.length() > 0) { preview.setPreviewHeight(Integer.parseInt(h)); } } catch (final Exception e) { Util.errMsg(this, null, e); } } /** * get the HTML representing the image selected in this * ImageDialog */ public String getImageHTML() { final SimpleAttributeSet set = new SimpleAttributeSet(originalAttributes); final StringWriter sw = new StringWriter(); final SHTMLWriter w = new SHTMLWriter(sw, doc == null ? new HTMLDocument() : doc); for (int i = 0; i < attributeComponents.size(); i++) { set.addAttributes(((AttributeComponent) attributeComponents.get(i)).getValue()); } set.addAttribute(HTML.Attribute.SRC, getImageSrc()); try { w.writeStartTag(HTML.Tag.IMG.toString(), set); } catch (final Exception e) { Util.errMsg(this, e.getMessage(), e); } return sw.getBuffer().toString(); } /** * get the value for the SRC attribute of an image tag * * @return the value of the SRC attribute of an image tag */ public String getImageSrc() { final StringBuffer buf = new StringBuffer(); final Object value = imgFileList.getSelectedValue(); if (value != null) { buf.append(SHTMLPanelImpl.IMAGE_DIR); buf.append(Util.URL_SEPARATOR); buf.append(value.toString()); } return buf.toString(); } /** * handle the event when the user pressed the 'Add...' button * to add a new image to the repository */ private void handleAddImage() { try { final JFileChooser chooser = new JFileChooser(); chooser.setMultiSelectionEnabled(true); final ExampleFileFilter filter = new ExampleFileFilter(); filter.addExtension("gif"); filter.addExtension("jpg"); filter.addExtension("jpeg"); filter.setDescription(Util.getResourceString("imageFileDesc")); chooser.setFileFilter(filter); if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { final File[] sFiles = chooser.getSelectedFiles(); if (!imgDir.exists()) { imgDir.mkdirs(); } final String imgDirName = imgDir.getAbsolutePath(); for (int i = 0; i < sFiles.length; i++) { //System.out.println("file selected: " + sFiles[i] + " new name= " + imgDirName + File.separator + sFiles[i].getName()); Util.copyFile(sFiles[i], new File(imgDirName + File.separator + sFiles[i].getName())); updateFileList(); } } } catch (final Exception e) { Util.errMsg(this, e.getMessage(), e); } } /** * handle the event occurring when the user pressed the 'Delete' button * to remove an image from the repository */ private void handleDeleteImage() { final String fName = imgFileList.getSelectedValue().toString(); if (Util.msg(JOptionPane.YES_NO_OPTION, "confirmDelete", "deleteFileQuery", fName, "\r\n")) { final File delFile = new File(imgDir.getAbsolutePath() + File.separator + fName); delFile.delete(); updateFileList(); } } /** * display all files found in the image directory */ private void updateFileList() { if (imgDir != null && imgFileList != null) { final String[] files = imgDir.list(); if (files != null && files.length > 0) { for (int i = 0; i < files.length; i++) { files[i] = files[i].toLowerCase(); } imgFileList.setListData(files); } } } /** * update all image property displays to the current setting */ private void updateControls() { ignoreChangeEvents = true; final int scalePct = preview.getScale(); final SimpleAttributeSet set = new SimpleAttributeSet(); oWidth.setText(Integer.toString(preview.getOriginalWidth())); oHeight.setText(Integer.toString(preview.getOriginalHeight())); //System.out.println("updateControls origW=" + preview.getOriginalWidth()); //System.out.println("updateControls add WIDTH attr as " + Integer.toString( // preview.getOriginalWidth() * scalePct / 100) + SizeSelectorPanel.UNIT_PT); set.addAttribute(HTML.Attribute.WIDTH, Integer.toString(preview.getOriginalWidth() * scalePct / 100) + SizeSelectorPanel.UNIT_PT); set.addAttribute(HTML.Attribute.HEIGHT, Integer.toString(preview.getOriginalHeight() * scalePct / 100) + SizeSelectorPanel.UNIT_PT); imgWidth.setValue(set); imgHeight.setValue(set); scale.setText(Integer.toString(scalePct)); ignoreChangeEvents = false; } /** * apply a scale set by the user through respective text field and * update all related image property displays */ private void applyPreviewScale() { //System.out.println("applyPreviewScale scale=" + scale.getText()); ignoreChangeEvents = true; try { preview.setScale(Integer.parseInt(scale.getText())); updateControls(); } catch (final Exception e) { } ignoreChangeEvents = false; } /** * apply a new width set by the user and update * all related image property displays */ private void applyPreviewWidth() { //System.out.println("applyPreviewWidth width=" + imgWidth.getIntValue().intValue()); ignoreChangeEvents = true; preview.setPreviewWidth(imgWidth.getIntValue().intValue()); final int scalePct = preview.getScale(); //System.out.println("applyPreviewWidth scale now " + scalePct); final SimpleAttributeSet set = new SimpleAttributeSet(); scale.setText(Integer.toString(scalePct)); set.addAttribute(HTML.Attribute.HEIGHT, Integer.toString(preview.getOriginalHeight() * scalePct / 100) + SizeSelectorPanel.UNIT_PT); //System.out.println("applyPreviewWidth, changing height to " + Integer.toString( // preview.getOriginalHeight() * scalePct / 100) + SizeSelectorPanel.UNIT_PT); imgHeight.setValue(set); ignoreChangeEvents = false; } /** * apply a new height set by the user and update * all related image property displays */ private void applyPreviewHeight() { //System.out.println("applyPreviewHeight height=" + imgHeight.getIntValue().intValue()); ignoreChangeEvents = true; preview.setPreviewHeight(imgHeight.getIntValue().intValue()); final int scalePct = preview.getScale(); //System.out.println("applyPreviewHeight scale now " + scalePct); final SimpleAttributeSet set = new SimpleAttributeSet(); scale.setText(Integer.toString(scalePct)); set.addAttribute(HTML.Attribute.WIDTH, Integer.toString(preview.getOriginalWidth() * scalePct / 100) + SizeSelectorPanel.UNIT_PT); //System.out.println("applyPreviewHeight, changing width to " + Integer.toString( // preview.getOriginalWidth() * scalePct / 100) + SizeSelectorPanel.UNIT_PT); imgWidth.setValue(set); ignoreChangeEvents = false; } /* ---------------- event handling start ------------------------- */ /** * implements the ActionListener interface to be notified of * clicks onto the file repository buttons. */ public void actionPerformed(final ActionEvent e) { final Object src = e.getSource(); if (src == addImgBtn) { handleAddImage(); } else if (src == delImgBtn) { handleDeleteImage(); } else { super.actionPerformed(e); } } /** * Listener for changes in the image list. * *

updates the image preview and property displays according * to the current selection (if any)

*/ public void valueChanged(final ListSelectionEvent e) { if (!imgFileList.isSelectionEmpty()) { /*System.out.println("ImageDialog.valueChanged setting preview image to " + imgDir.getAbsolutePath() + File.separator + imgFileList.getSelectedValue().toString());*/ preview.setImage(new ImageIcon(imgDir.getAbsolutePath() + File.separator + imgFileList.getSelectedValue().toString())); updateControls(); } else { preview.setImage(null); final int vWidth = scPrev.getWidth() - 5; final int vHeight = scPrev.getHeight() - 5; preview.setPreferredSize(new Dimension(vWidth, vHeight)); preview.revalidate(); } } /** * Listener for resize events. * *

used on the JScrollPane holding the image preview * to adjust the preview to size changes and to synchronize * property displays accordingly.

*/ private class ResizeListener extends ComponentAdapter { public void componentResized(final ComponentEvent e) { final int vWidth = scPrev.getWidth() - 5; final int vHeight = scPrev.getHeight() - 5; preview.setPreferredSize(new Dimension(vWidth, vHeight)); preview.revalidate(); updateControls(); } } /** * Listener for key events * *

Used to adjust preview properties according to * user settings in the scale text field

*/ private class KeyHandler extends KeyAdapter { public void keyReleased(final KeyEvent e) { final Object source = e.getSource(); final int keyCode = e.getKeyCode(); if (source.equals(scale)) { if (keyCode == KeyEvent.VK_ENTER) { applyPreviewScale(); } } } } /** * Listener for focus events * *

Used to adjust preview properties according to * user settings in the scale text field

*/ private class FocusHandler extends FocusAdapter { public void focusLost(final FocusEvent e) { final Object source = e.getSource(); if (source.equals(scale)) { applyPreviewScale(); } } } /** * Listener for change events * *

Used to adjust preview properties according to * user settings in SizeSelectorPanels

*/ public void stateChanged(final ChangeEvent e) { if (!ignoreChangeEvents) { final Object source = e.getSource(); if (source.equals(imgWidth.getValueSelector())) { applyPreviewWidth(); } else if (source.equals(imgHeight.getValueSelector())) { applyPreviewHeight(); } } } /* ---------------- event handling end ------------------------- */ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/BorderPanel.java0100644 0000000 0000000 00000021652 12114157751 022513 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.Enumeration; import java.util.Vector; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.CSS; /** * Panel to show and manipulate border settings for a rectangular * object such as a table cell * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class BorderPanel extends JPanel implements AttributeComponent { private final Vector components = new Vector(); /** the attributes for border width */ private CombinedAttribute bWidth; /** the attributes for border color */ private CombinedAttribute bColor; /** the color value to compare to for determining changes */ private String oColor; /** the width value to compare to for determining changes */ private String oWidth; /** indicates if a call to setValue is for initial setting or for changes */ private int setValueCalls = 0; public BorderPanel() { super(); // set layout final GridBagLayout g = new GridBagLayout(); setLayout(g); // constraints to use on our GridBagLayout final GridBagConstraints c = new GridBagConstraints(); addSettings(g, c, Util.getResourceString("topLabel"), CombinedAttribute.ATTR_TOP, 0, 0); addSettings(g, c, Util.getResourceString("rightLabel"), CombinedAttribute.ATTR_RIGHT, 1, 1); addSettings(g, c, Util.getResourceString("bottomLabel"), CombinedAttribute.ATTR_BOTTOM, 1, 0); addSettings(g, c, Util.getResourceString("leftLabel"), CombinedAttribute.ATTR_LEFT, 0, 1); } private void addSettings(final GridBagLayout g, final GridBagConstraints c, final String title, final int side, final int x, final int y) { final BorderSettings bs = new BorderSettings(title, side); Util.addGridBagComponent(this, bs, g, c, x, y, GridBagConstraints.WEST); components.addElement(bs); } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { final boolean success = true; final Enumeration e = components.elements(); bWidth = new CombinedAttribute(CSS.Attribute.BORDER_WIDTH, a, true); bColor = new CombinedAttribute(CSS.Attribute.BORDER_COLOR, a, true); if (++setValueCalls < 2) { oColor = bColor.getAttribute(); oWidth = bWidth.getAttribute(); } while (e.hasMoreElements()) { ((BorderSettings) e.nextElement()).setValue(bWidth, bColor); } return success; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet set = new SimpleAttributeSet(); BorderSettings bs; for (int i = 0; i < components.size(); i++) { bs = (BorderSettings) components.elementAt(i); bColor.setAttribute(i, bs.getBorderColor()); bWidth.setAttribute(i, bs.getBorderWidth()); } String newValue = bColor.getAttribute(); newValue = bWidth.getAttribute(CombinedAttribute.ATTR_TOP); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_TOP_WIDTH, newValue); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_RIGHT_WIDTH, newValue); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_BOTTOM_WIDTH, newValue); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_LEFT_WIDTH, newValue); return set; } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { final SimpleAttributeSet set = new SimpleAttributeSet(); BorderSettings bs; for (int i = 0; i < components.size(); i++) { bs = (BorderSettings) components.elementAt(i); bColor.setAttribute(i, bs.getBorderColor()); bWidth.setAttribute(i, bs.getBorderWidth()); } String newValue = bColor.getAttribute(); newValue = bWidth.getAttribute(CombinedAttribute.ATTR_TOP); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_TOP_WIDTH, newValue); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_RIGHT_WIDTH, newValue); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_BOTTOM_WIDTH, newValue); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_LEFT_WIDTH, newValue); return set; } else { return getValue(); } } /** * Panel to show and manipulate border settings */ private class BorderSettings extends JPanel { /** the border side */ private final int side; /** selector for border width */ private final SizeSelectorPanel bWidth; /** selector for border color */ private final ColorPanel bColor; /** * construct a BorderSettings panel * * @param title the title of this object * @param borderKey the attribute key for the border width this * object represents * @param colorKey the attribute key for the border color this * object represents */ public BorderSettings(final String title, final int side) { super(); this.side = side; // set layout final GridBagLayout g = new GridBagLayout(); setLayout(g); // constraints to use on our GridBagLayout final GridBagConstraints c = new GridBagConstraints(); // set border and title setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), title)); // add the width control and label Util.addGridBagComponent(this, new JLabel(Util.getResourceString("borderWidthLabel")), g, c, 0, 0, GridBagConstraints.EAST); bWidth = new SizeSelectorPanel(CSS.Attribute.BORDER_WIDTH, null, false, SizeSelectorPanel.TYPE_LABEL); Util.addGridBagComponent(this, bWidth, g, c, 1, 0, GridBagConstraints.WEST); // add the color control and label Util.addGridBagComponent(this, new JLabel(Util.getResourceString("borderColorLabel")), g, c, 0, 1, GridBagConstraints.EAST); bColor = new ColorPanel(null, Color.black, CSS.Attribute.BORDER_COLOR); Util.addGridBagComponent(this, bColor, g, c, 1, 1, GridBagConstraints.WEST); } public String getBorderColor() { return bColor.getAttr(); } public String getBorderWidth() { return bWidth.getAttr(); } /** * set the value of this AttributeComponent * * @param color the CombinedAttribute to take the color from * */ public void setValue(final CombinedAttribute borderWidths, final CombinedAttribute borderColors) { String attr = borderColors.getAttribute(side); //System.out.println("BorderSettings setValue attr='" + attr + "'"); if (attr != null) { bColor.setValue(attr); } attr = borderWidths.getAttribute(side); if (attr != null) { bWidth.setValue(attr); } } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/Remover.java0100644 0000000 0000000 00000005171 12114157751 021733 0ustar000000000 0000000 package com.lightdev.app.shtm; import java.util.Locale; class Remover { final private String searchedText; private String searchedSubstring; private int begin; private int end; private int removedCharacterNumber; private String processedText; public String getProcessedText() { return processedText; } public Remover(String text) { super(); this.processedText = text; this.searchedText = text.toLowerCase(Locale.ENGLISH); begin = end = removedCharacterNumber = 0; } public int getBegin() { return begin; } public int getEnd() { return end; } private String createSearchedSubstring(final String substring) { return "<" + substring.toLowerCase(Locale.ENGLISH); } public boolean findFirst(){ begin = removedCharacterNumber; return findNext(); } public boolean findNext(){ begin = searchedText.indexOf(searchedSubstring, begin); return findEndOfElement(); } public boolean findLast(){ begin = searchedText.lastIndexOf(searchedSubstring); return findEndOfElement(); } private boolean findEndOfElement() { if(begin == -1) { end = -1; return false; } end = searchedText.indexOf('>', begin + searchedSubstring.length()); if(end == -1){ begin = -1; return false; } end++; return true; } public int getWhiteSpaceBefore(){ if(begin <= 0) return begin; for(int i = begin - 1; i > 0; i--){ if(! Character.isWhitespace(searchedText.charAt(i))) return i + 1; } return 0; } public int getWhiteSpaceAfter(){ if(end == -1) return -1; for(int i = end + 1; i < searchedText.length(); i++){ if(! Character.isWhitespace(searchedText.charAt(i))) return i; } return searchedText.length(); } public Remover removeFirstAndBefore(String element){ searchedSubstring = createSearchedSubstring(element); if(findFirst()){ final int lastIndex = getWhiteSpaceAfter() - removedCharacterNumber; processedText = processedText.substring(lastIndex); removedCharacterNumber += lastIndex; } return this; } public Remover removeLastAndAfter(String element){ searchedSubstring = createSearchedSubstring(element); if(findLast()){ final int firstIndex = getWhiteSpaceBefore() - removedCharacterNumber; processedText = processedText.substring(0, firstIndex); } return this; } static public void main(String[] argv){ String text = "\n\t 1 "; final Remover removeFirstAndBefore = new Remover(text).removeFirstAndBefore("body"); String removeLastAndAfter = removeFirstAndBefore.removeLastAndAfter("/body").getProcessedText(); System.out.println ('"' + removeLastAndAfter + '"'); // assert removeLastAndAfter.equals("1"); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/DocumentPane.java0100644 0000000 0000000 00000105263 12534067656 022713 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.EventObject; import java.util.Vector; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.Document; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.StyleSheet; import com.sun.demo.ExampleFileFilter; /** * GUI representation of a document. * *

Swing already uses three types of classes to implement a model, view, * controller (MVC) approach for a document:

* *

JTextComponent - the view implementation
* Document - the model implementation
* EditorKit - the controller implementation

* *

For a GUI representation of a document, additional parts are needed, such * as a JScrollPane as well as listeners and fields to track the state of * the document while it is represented on a GUI.

* *

DocumentPane wraps all those elements to implement a single * document centric external view to all elements.

* *

If for instance an application wants to create a new document, it simply * creates an instance of this class instead of having to implement own * methods for instatiating each element (editor pane, scroll pane, etc.) * separately.

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class DocumentPane extends JPanel implements DocumentListener, ChangeListener { /** the editor displaying the document in layout view */ private final SHTMLEditorPane editorPane; /** the editor displaying the document in HTML code view */ private final SyntaxPane sourceEditorPane; /** temporary storage location for this document */ private File docTempDir = null; /** the save thread, if a save operation is in progress */ public Thread saveThread = null; /** indicator if a save operation was succesful */ public boolean saveSuccessful = false; /** indicates if the document text has changed */ private boolean documentChanged = false; /** * @param documentChanged The documentChanged to set. */ private void setDocumentChanged(final boolean documentChanged) { this.documentChanged = documentChanged; } /** * @return Returns the documentChanged. */ private boolean isDocumentChanged() { return documentChanged; } /** indicates if the document text has changed */ private boolean htmlChanged = true; /** * @param htmlChanged The htmlChanged to set. */ private void setHtmlChanged(final boolean htmlChanged) { this.htmlChanged = htmlChanged; } /** * @return Returns the htmlChanged. */ private boolean isHtmlChanged() { return htmlChanged; } /** the name of the document */ private String docName; /** the file the current style sheet was loaded from, if any */ private final File loadedStyleSheet = null; /** the URL the document was loaded from (if applicable)*/ private URL sourceUrl = null; /** JTabbedPane for our views */ private JComponent paneHoldingScrollPanes; private final JScrollPane richViewScrollPane; private final JScrollPane sourceViewScrollPane; public static final int VIEW_TAB_LAYOUT = 0; public static final int VIEW_TAB_HTML = 1; /** * a save place for sourceUrl, when a document is to be saved * under a new name and this fails */ private URL savedUrl = null; /** indicates if this document was loaded froma file */ private boolean loadedFromFile = false; /** default document name */ private String DEFAULT_DOC_NAME = "Untitled"; /** default name for style sheet, when saved */ public static String DEFAULT_STYLE_SHEET_NAME = "style.css"; /** number for title of a new document */ private int newDocNo; private int activeView; //private int renderMode; /** * construct a new DocumentPane. * *

A document still has to be either created or loaded after using * this constructor, so it is better to use the constructor doing this * right away instead.

*/ public DocumentPane(/*int renderMode*/) { super(); // EditorPane and ScrollPane for layout view editorPane = new SHTMLEditorPane(); final SHTMLEditorKit kit = new SHTMLEditorKit(/*renderMode*/); //kit.resetStyleSheet(); editorPane.setEditorKit(kit); richViewScrollPane = new JScrollPane(); // create a new JScrollPane, richViewScrollPane.getViewport().setView(editorPane); // ..add the editor pane to it // EditorPane and ScrollPane for html view sourceEditorPane = new SyntaxPane(); sourceEditorPane.setFont(new Font("Monospaced", Font.PLAIN, 12)); //sourceEditorPane.addKeyListener(rkw); sourceViewScrollPane = new JScrollPane(); sourceViewScrollPane.getViewport().setView(sourceEditorPane); // Tabbed pane for HTML and layout views if (Util.showViewsInTabs()) { paneHoldingScrollPanes = new JTabbedPane(); paneHoldingScrollPanes.add(richViewScrollPane, VIEW_TAB_LAYOUT); paneHoldingScrollPanes.add(sourceViewScrollPane, VIEW_TAB_HTML); final JTabbedPane tabbedPane = (JTabbedPane) paneHoldingScrollPanes; tabbedPane.setTabPlacement(JTabbedPane.BOTTOM); tabbedPane.setTitleAt(VIEW_TAB_LAYOUT, Util.getResourceString("layoutTabTitle")); tabbedPane.setTitleAt(VIEW_TAB_HTML, Util.getResourceString("htmlTabTitle")); tabbedPane.addChangeListener(this); setLayout(new BorderLayout()); add(paneHoldingScrollPanes, BorderLayout.CENTER); } else { paneHoldingScrollPanes = new JPanel(new BorderLayout()); paneHoldingScrollPanes.add(richViewScrollPane, BorderLayout.CENTER); activeView = VIEW_TAB_LAYOUT; //BorderLayout DOES NOT allow two parts with ..CENTER. //paneHoldingScrollPanes.add(sourceViewScrollPane, BorderLayout.CENTER); //sourceViewScrollPane.setVisible(false); //paneHoldingScrollPanes.addChangeListener(this); setLayout(new BorderLayout()); add(paneHoldingScrollPanes, BorderLayout.CENTER); } setDocumentChanged(false); // no changes so far setPreferredSize(new Dimension(550, 550)); } /** * construct a new DocumentPane with either a new Document or an exisiting * Document that is to be loaded into the DocumentPane upon construction. * * @param docToLoad the document to be loaded. If this is null, a new * Document is created upon construction of the DocumentPane * @param newDocNo the number a new document shall have in the * title as long as it is not saved (such as in 'Untitled1'). If an * existing document shall be loaded, this number is ignored */ public DocumentPane(final URL docToLoad, final int newDocNo/*, int renderMode*/) { this(/*renderMode*/); DEFAULT_DOC_NAME = Util.getResourceString("defaultDocName"); if (docToLoad != null) { loadDocument(docToLoad); } else { this.newDocNo = newDocNo; createNewDocument(); } } /** * get the JEditorPane of this DocumentPane * * @return the JEditorPane of this DocumentPane */ public SHTMLEditorPane getEditor() { return editorPane; } /** * get the SyntaxPane of this DocumentPane * * @return the SyntaxPane of this DocumentPane */ public SyntaxPane getHtmlEditor() { return sourceEditorPane; } /** * @return the selected tab index */ public int getSelectedTab() { if (paneHoldingScrollPanes instanceof JTabbedPane) { return ((JTabbedPane) paneHoldingScrollPanes).getSelectedIndex(); } return activeView; } /** * create a new HTMLDocument and attach it to the editor */ public void createNewDocument() { try { final SHTMLEditorKit kit = (SHTMLEditorKit) editorPane.getEditorKit(); final SHTMLDocument doc = (SHTMLDocument) kit.createDefaultDocument(); //insertStyleRef(doc); // create style sheet reference in HTML header tag //styles = kit.getStyleSheet(); doc.addDocumentListener(this); // listen to changes doc.setBase(createTempDir()); editorPane.setDocument(doc); // let the document be edited in our editor //doc.putProperty(Document.TitleProperty, getDocumentName()); final boolean useStyle = Util.useSteStyleSheet(); if (useStyle) { doc.insertStyleRef(); } } catch (final Exception e) { Util.errMsg(this, e.getMessage(), e); } } public void setDocument(final Document docToSet) { try { editorPane.getEditorKit(); final HTMLDocument doc = (HTMLDocument) getDocument(); if (doc != null) { doc.removeDocumentListener(this); } docToSet.addDocumentListener(this); // listen to changes editorPane.setDocument(docToSet); // let the document be edited in our editor } catch (final Exception e) { Util.errMsg(this, e.getMessage(), e); } } /** * create temporary directory for a newly created document * so that images can be stored and referenced until the document * is saved. * * @return URL of created temporary document directoy */ private URL createTempDir() throws MalformedURLException { docTempDir = new File(SHTMLPanelImpl.getAppTempDir().getAbsolutePath() + File.separator + getDocumentName() + File.separator); return docTempDir.toURI().toURL(); } /** * remove the temporary storage created for this DocumentPane */ public void deleteTempDir() { if (docTempDir != null) { Util.deleteDir(docTempDir); docTempDir = null; } } /** * load a document found at a certain URL. * * @param url the URL to look for the document */ public void loadDocument(final URL url) { try { final SHTMLEditorKit kit = (SHTMLEditorKit) editorPane.getEditorKit(); final SHTMLDocument doc = (SHTMLDocument) kit.createEmptyDocument(); doc.putProperty("IgnoreCharsetDirective", new Boolean(true)); doc.setBase(new File(url.getPath()).getParentFile().toURI().toURL()); // set the doc base final InputStream in = url.openStream(); // get an input stream kit.read(in, doc, 0); // ..and read the document contents from it in.close(); // .. then properly close the stream doc.addDocumentListener(this); // listen to changes editorPane.setDocument(doc); // let the document be edited in our editor setSource(url); // remember where the document came from loadedFromFile = true; } catch (final Exception ex) { Util.errMsg(this, "An exception occurred while loading the file", ex); ex.printStackTrace(); } } /** * load the rules from a given style sheet file into a new StyleSheet object. * * @param cssFile the file object referring to the style sheet to load from * * @return the style sheet with rules loaded */ private StyleSheet loadStyleSheet(final File cssFile) throws MalformedURLException, IOException { final StyleSheet s = new StyleSheet(); s.importStyleSheet(cssFile.toURI().toURL()); return s; } /** * saves the document to the file specified in the source of the * DocumentPane and creates the associated style sheet. * * The actual save process only is done, when there is a name to save * to. The class(es) calling this method have to make sure that a * name for new documents is requested from the user, for instance. * * The desired name and location for the save need then to be set using method * setSource prior to a call to this method * * @throws DocNameMissingException to ensure the caller gets notified * that a save did not take place because of a missing name * and location */ public void saveDocument() throws DocNameMissingException { if (!saveInProgress()) { saveThread = Thread.currentThread(); // store thread for saveInProgress saveSuccessful = false; // if something goes wrong, this remains false File file = null; try { if (sourceUrl != null) { /* write the HTML document */ if (getSelectedTab() == VIEW_TAB_HTML) { editorPane.setText(sourceEditorPane.getText()); } final SHTMLDocument doc = (SHTMLDocument) getDocument(); final OutputStream os = new FileOutputStream(sourceUrl.getPath()); final OutputStreamWriter osw = new OutputStreamWriter(os); final SHTMLWriter hw = new SHTMLWriter(osw, doc); hw.write(); osw.flush(); osw.close(); os.flush(); os.close(); /* write the style sheet */ if (doc.hasStyleRef()) { saveStyleSheet(); } /* copy image directory, if new document or saved from different location */ saveImages(); /* clean up */ //System.out.println("DocumentPane textChanged = false"); setDocumentChanged(false); // indicate no changes pending anymore after the save file = new File(sourceUrl.getPath()).getParentFile(); ((HTMLDocument) getDocument()).setBase(file.toURI().toURL()); // set the doc base deleteTempDir(); //System.out.println("DocumentPane saveSuccessful = true"); saveSuccessful = true; // signal that saving was successful } else { saveThread = null; throw new DocNameMissingException(); } } catch (final MalformedURLException mue) { if (file != null) { Util.errMsg(this, "Can not create a valid URL for\n" + file.getAbsolutePath(), mue); } else { Util.errMsg(this, mue.getMessage(), mue); } } catch (final Exception e) { if (savedUrl != null) { sourceUrl = savedUrl; } Util.errMsg(this, "An exception occurred while saving the file", e); } saveThread = null; savedUrl = sourceUrl; } } /** * determine the directory this DocumentPane references image * files from * * @return the directory image files referenced by this * DocumentPane are found */ public File getImageDir() { File srcDir = null; if (savedUrl == null && newDocNo > 0) { // new Document: use temp dir as source srcDir = new File(docTempDir + File.separator + SHTMLPanelImpl.IMAGE_DIR + File.separator); } else { if (savedUrl == null) { // document has been saved before: source is 'sourceUrl' srcDir = new File(new File(sourceUrl.getPath()).getParent() + File.separator + SHTMLPanelImpl.IMAGE_DIR + File.separator); } else { /* document has been saved before but now is to be saved under new name: source is 'old' url */ srcDir = new File(new File(savedUrl.getPath()).getParent() + File.separator + SHTMLPanelImpl.IMAGE_DIR + File.separator); } } return srcDir; } /** * save image files */ private void saveImages() { final File srcDir = getImageDir(); final File destDir = new File(new File(sourceUrl.getPath()).getParent() + File.separator + SHTMLPanelImpl.IMAGE_DIR + File.separator); try { if (srcDir.exists()) { final ExampleFileFilter filter = new ExampleFileFilter(); filter.addExtension("gif"); filter.addExtension("jpg"); filter.addExtension("jpeg"); final File[] imgFiles = srcDir.listFiles(); for (int i = 0; i < imgFiles.length; i++) { Util.copyFile(imgFiles[i], new File(destDir.getAbsolutePath() + File.separator + imgFiles[i].getName())); } } } catch (final Exception e) { Util.errMsg(this, e.getMessage(), e); } } /** * indicates whether or not a save process is in progress * * @return true, if a save process is going on, else false */ public boolean saveInProgress() { //System.out.println("DocumentPane.saveInProgress=" + (saveThread != null) + " for document " + getDocumentName()); return saveThread != null; } /** * Saves the style sheet of this document to a CSS file. * *

With stage 8, saves a style sheet by merging it with the existing * one with the same name/location. Styles in this style sheet overwrite * styles in the already existing style sheet.

*/ public void saveStyleSheet() throws IOException { final SHTMLDocument doc = (SHTMLDocument) getDocument(); final StyleSheet styles = doc.getStyleSheet(); final String styleSheetName = getStyleSheetName(); if (styleSheetName != null) { final File styleSheetFile = new File(new URL(styleSheetName).getFile()); if (!styleSheetFile.exists()) { // no styles present at save location, create new style sheet styleSheetFile.createNewFile(); } else { if (loadedFromFile) { if ((savedUrl == null) || (!savedUrl.getPath().equals(sourceUrl.getPath()))) { /* this style sheet was loaded from somewhere else and now is being saved at a new location where a style sheet exists havig the same name --> merge */ mergeStyleSheets(loadStyleSheet(styleSheetFile), styles); } else { /* same location where styles originally came from, overwrite existing styles with new version */ styleSheetFile.delete(); styleSheetFile.createNewFile(); } } else { /* this style sheet was newly created and now is being saved at a location where a style sheet exists havig the same name --> merge */ mergeStyleSheets(loadStyleSheet(styleSheetFile), styles); } } final OutputStream os = new FileOutputStream(styleSheetFile); final OutputStreamWriter osw = new OutputStreamWriter(os); CSSWriter cssWriter; cssWriter = new CSSWriter(osw, styles); cssWriter.write(); osw.close(); os.close(); } } /** * Merges two style sheets by adding all the rules found * in the source style sheet that are not contained * in the destination style sheet. Assumes the rules * of src and dest are already loaded. * * @param sourceStyleSheet the source StyleSheet * @param destinationStyleSheet the destination StyleSheet */ private void mergeStyleSheets(final StyleSheet sourceStyleSheet, final StyleSheet destinationStyleSheet) throws IOException { String name; Object elem; final Vector srcNames = Util.getStyleNames(sourceStyleSheet); final Vector destNames = Util.getStyleNames(destinationStyleSheet); final StringWriter sw = new StringWriter(); final StringBuffer buf = sw.getBuffer(); final CSSWriter cssWriter = new CSSWriter(sw, null); for (int i = 0; i < srcNames.size(); i++) { elem = srcNames.get(i); name = elem.toString(); if (destNames.indexOf(elem) < 0) { buf.delete(0, buf.length()); cssWriter.writeRule(name, sourceStyleSheet.getStyle(name)); destinationStyleSheet.removeStyle(name); destinationStyleSheet.addRule(buf.toString()); } } } /** * get the URL of the style sheet of this document * *

The name is built by

    *
  1. get the style sheet reference, if none, use default style * sheet name
  2. *
  3. get the document base
  4. *
  5. if the style sheet reference is a relative path, resolve base * and relative path
  6. *
  7. else simply concatenate doc base and style sheet reference
  8. *

* * @return the URL of the style sheet */ private String getStyleSheetName() throws MalformedURLException { String name = DEFAULT_STYLE_SHEET_NAME; //SHTMLEditorKit.DEFAULT_CSS; final SHTMLDocument doc = (SHTMLDocument) getDocument(); final String styleRef = doc.getStyleRef(); final File file = new File(sourceUrl.getPath()).getParentFile(); String newDocBase = null; try { newDocBase = file.toURI().toURL().toString(); } catch (final Exception e) { if (file != null) { Util.errMsg(this, "Can not create a valid URL for\n" + file.getAbsolutePath(), e); } else { Util.errMsg(this, e.getMessage(), e); } } if (styleRef != null) { name = Util.resolveRelativePath(styleRef, newDocBase); } else { name = null; // Util.resolveRelativePath(name, newDocBase); } //System.out.println("DocumentPane.getStyleSheetName=" + name); return name; } /** * get the name of the document of this pane. * * @return the name of the document */ public String getDocumentName() { String theName; if (docName == null || docName.length() < 1) { theName = DEFAULT_DOC_NAME + " " + Integer.toString(newDocNo); } else { theName = docName; } return theName; } /** * indicates whether or not the document needs to be saved. * * @return true, if changes need to be saved */ public boolean needsSaving() { //System.out.println("DocumentPane.needsSaving=" + textChanged + " for document " + getDocumentName()); return isDocumentChanged(); } /** * set the source this document is to be loaded from * *

This is only to be used when it is made sure, * that the document is saved at the location specified * by 'source'.

* * @param the URL of the source this document is to be loaded from */ public void setSource(final URL source) { savedUrl = sourceUrl; sourceUrl = source; final String fName = source.getFile(); docName = fName.substring(fName.lastIndexOf("/") + 1); fireNameChanged(); } /** * get the source, this document was having before its current sourceUrl * was set. * * @return the source URL before a name change */ public URL getOldSource() { if (savedUrl == null) { return sourceUrl; } else { return savedUrl; } } /** * get the source this document can be loaded from * * @return the URL this document can be loaded from */ public URL getSource() { return sourceUrl; } /** * indicates whether or not this document was newly created and not saved so * far. * * @return true, if this is a new document that has not been saved so far */ public boolean isNewDoc() { return sourceUrl == null; } /** * get the document of this DocumentPane * * @return the Document of this DocumentPane */ public Document getDocument() { return editorPane.getDocument(); } HTMLDocument getHTMLDocument() { return (HTMLDocument) sourceEditorPane.getDocument(); } /** * Switches between the rich text view and the source view, given * tabbed panes are not used. Has no corresponding action; calling * this method is up to the caller application of SimplyHTML; the * application should call the method of the same name available at * SHTMLPanel. */ public void switchViews() { if (paneHoldingScrollPanes instanceof JTabbedPane) { return; } // [ Tabbed pane not used ] if (activeView == VIEW_TAB_LAYOUT) { setHTMLView(); paneHoldingScrollPanes.remove(richViewScrollPane); paneHoldingScrollPanes.add(sourceViewScrollPane); activeView = VIEW_TAB_HTML; } else { setLayoutView(); paneHoldingScrollPanes.remove(sourceViewScrollPane); paneHoldingScrollPanes.add(richViewScrollPane); activeView = VIEW_TAB_LAYOUT; } } /** * Switches the DocumentPane to HTML view. */ private void setHTMLView() { try { editorPane.getDocument().removeDocumentListener(this); final StringWriter stringWriter = new StringWriter(); if (isHtmlChanged()) { editorPane.getEditorKit().write(stringWriter, editorPane.getDocument(), 0, editorPane.getDocument().getLength()); stringWriter.close(); String newText = stringWriter.toString(); if (!Util.preferenceIsTrue("writeHead", "true")) { newText = newText.replaceAll("(?ims).*?(http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class ColorPanel extends JPanel implements ActionListener, AttributeComponent { /** the component showing the chosen color */ JTextField colorDisplay = new JTextField(); /** default color */ private final Color defaultColor; /** the attribute key, this component returns values for */ private final Object attributeKey; /** value to compare for determining changes */ private Color originalColor; /** indicates if setValue is called initially */ private int setValCount = 0; private final JColorChooser colorChooserPane = new JColorChooser(); /** * construct a color panel * * @param title the title of the color panel * @param col the color to be displayed first * @param titleFont font for title * @param key the attribute key this component shall return color values for */ public ColorPanel(final String title, final Color col, final Object key) { super(new BorderLayout(5, 5)); defaultColor = col; attributeKey = key; /** adjust the color display */ colorDisplay.setBackground(col); Dimension dim = new Dimension(20, 15); colorDisplay.setMinimumSize(dim); colorDisplay.setMaximumSize(dim); colorDisplay.setPreferredSize(dim); colorDisplay.setEditable(false); /** a button to open a color chooser window */ final JButton browseButton = new JButton(); browseButton.setText("..."); dim = new Dimension(20, 15); browseButton.setMinimumSize(dim); browseButton.setMaximumSize(dim); browseButton.setPreferredSize(dim); browseButton.addActionListener(this); /** a helper panel for proper component placement */ final JPanel eastPanel = new JPanel(new FlowLayout()); eastPanel.add(colorDisplay); eastPanel.add(browseButton); /** set the title */ if ((title != null) && (title.length() > 0)) { final JLabel titleLabel = new JLabel(title); titleLabel.setFont(UIManager.getFont("TextField.font")); add(titleLabel, BorderLayout.WEST); add(eastPanel, BorderLayout.EAST); } else { add(eastPanel, BorderLayout.WEST); } } /** * get the selected color * * @return the selected color */ public Color getColor() { return colorDisplay.getBackground(); } /** * set the selected color * * @param color the selected color */ private void setColor(final Color color) { //System.out.println("ColorPanel setColor attributeKey=" + attributeKey + ", color=" + color); colorDisplay.setBackground(color); if (++setValCount < 2) { originalColor = color; } fireColorChanged(); } /** * open a color chooser when a 'Browse' button * is clicked and change the associated color * display accordingly, when another color * is selected from the color chooser */ public void actionPerformed(final ActionEvent e) { final Color color = showColorChooserDialog(); if (color != null) { setValCount++; setColor(color); } } private Color showColorChooserDialog() { // the listener for OK button of the Color Choose Dialog: class ColorTracker implements ActionListener { JColorChooser chooser; Color color; public ColorTracker(final JColorChooser c) { chooser = c; } public void actionPerformed(final ActionEvent e) { color = chooser.getColor(); } } colorChooserPane.setColor(colorDisplay.getBackground()); // setting up the current text color final ColorTracker ok = new ColorTracker(colorChooserPane); final JDialog dialog = JColorChooser.createDialog(this, "Select Color", true, colorChooserPane, ok, null); dialog.setVisible(true); // blocks until user brings dialog down... dialog.dispose(); return ok.color; } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { Color newSelection = null; if (getAttributeKey() == CSS.Attribute.COLOR) { newSelection = Util.styleSheet().getForeground(a); } if (getAttributeKey() == CSS.Attribute.BACKGROUND_COLOR) { newSelection = Util.styleSheet().getBackground(a); if (newSelection == null) { newSelection = SystemColor.text; } } if (newSelection != null) { setColor(newSelection); return true; } return false; } public void setValue(final String value) { //System.out.println("ColorPanel setValue value=" + value); try { setColor(new Color(Integer.parseInt(value.toString().substring(1).toUpperCase(), 16))); } catch (final Exception e) { try { //setColor(Util.styleSheet().getForeground(a)); //System.out.println("ColorPanel setValue value=" + value + "=" + Color.getColor(value)); setColor(Color.getColor(value)); } catch (final Exception e2) { Util.errMsg(null, null, e2); } } } public String getAttr() { final String color = "#" + Integer.toHexString(getColor().getRGB()).substring(2); return color; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet set = new SimpleAttributeSet(); final Color value = getColor(); if (value != originalColor) { final String color = "#" + Integer.toHexString(value.getRGB()).substring(2); Util.styleSheet().addCSSAttribute(set, (CSS.Attribute) getAttributeKey(), color); if (getAttributeKey() == CSS.Attribute.COLOR) { set.addAttribute(HTML.Attribute.COLOR, color); } else if (getAttributeKey() == CSS.Attribute.BACKGROUND_COLOR) { set.addAttribute(HTML.Attribute.BGCOLOR, color); } } return set; } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { final SimpleAttributeSet set = new SimpleAttributeSet(); final Color value = getColor(); final String color = "#" + Integer.toHexString(value.getRGB()).substring(2); try { Util.styleSheet().addCSSAttribute(set, (CSS.Attribute) getAttributeKey(), color); } catch (final Exception e) { set.addAttribute(getAttributeKey(), color); } //System.out.println("ColorPanel getValue color=" + color); return set; } else { return getValue(); } } /** * get the attribute key this object was created for * * @return the attribute key this ColorPanel returns values for */ public Object getAttributeKey() { return attributeKey; } /* -------------- event listener implementation start ----------- */ /** the listeners for ColorPanelEvents */ private final Vector listeners = new Vector(0); /** * add an event listener. * * @param listener the event listener to add */ public void addColorPanelListener(final ColorPanelListener listener) { listeners.addElement(listener); } /** * remove an event listener. * * @param listener the event listener to remove */ public void removeColorPanelListener(final ColorPanelListener listener) { listeners.removeElement(listener); } /** fire a color changed event to all registered listeners */ void fireColorChanged() { final Enumeration listenerList = listeners.elements(); while (listenerList.hasMoreElements()) { ((ColorPanelListener) listenerList.nextElement()).colorChanged(new ColorPanelEvent(this)); } } /** the event object definition for ColorPanels */ class ColorPanelEvent extends EventObject { public ColorPanelEvent(final Object source) { super(source); } } /** the event listener definition for ColorPanels */ interface ColorPanelListener extends EventListener { public void colorChanged(ColorPanelEvent e); } /* -------------- event listener implementation end ----------- */ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/AttributeMapper.java0100644 0000000 0000000 00000012676 12114157751 023434 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.CSS; import javax.swing.text.html.HTML; /** *

Maps HTML and CSS attributes to their equivalents to * compensate discrepancies in HTML and CSS rendering of * various different view environments.

* *

Introduced in stage 5 this class only contains hard wired * fixes to certain discrepancies. Should there come up an increased * number of necessary fixes in future stages, a more generic way * of mapping (such as through a Hashtable of from/to values), etc. * will be done.

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class AttributeMapper extends SimpleAttributeSet { public static final int toCSS = 0; public static final int toHTML = 1; public static final int toJava = 2; public AttributeMapper() { super(); } public AttributeMapper(final AttributeSet a) { super(a); } public AttributeSet getMappedAttributes(final int direction) { switch (direction) { case toCSS: mapToCSSAttributes(); break; case toHTML: mapToHTMLAttributes(); break; case toJava: mapToJavaAttributes(); break; } //System.out.println("AttributeMapper transformed attributes="); //de.calcom.cclib.html.HTMLDiag hd = new de.calcom.cclib.html.HTMLDiag(); //hd.listAttributes(this, 2); return this; } private void mapToCSSAttributes() { final Object cssFontSize = getAttribute(CSS.Attribute.FONT_SIZE); if (cssFontSize != null) { final int fontNumber = Integer.parseInt(cssFontSize.toString()); addAttribute(CSS.Attribute.FONT_SIZE, SHTMLPanelImpl.FONT_SIZES[fontNumber - 1] + "pt"); } mapToHTMLAttributes(); } private void mapToHTMLAttributes() { final Object cssFontFamily = getAttribute(CSS.Attribute.FONT_FAMILY); if (cssFontFamily != null) { if (cssFontFamily.toString().equalsIgnoreCase("SansSerif")) { addAttribute(CSS.Attribute.FONT_FAMILY, "SansSerif, Sans-Serif"); //System.out.println("mapToHTMLAttributes SansSerif, Sans-Serif"); } else if (cssFontFamily.toString().indexOf("Monospaced") > -1) { addAttribute(CSS.Attribute.FONT_FAMILY, "Monospace, Monospaced"); } } } private void mapToJavaAttributes() { final Object htmlFontFace = getAttribute(HTML.Attribute.FACE); final Object cssFontFamily = getAttribute(CSS.Attribute.FONT_FAMILY); if (htmlFontFace != null) { if (cssFontFamily != null) { removeAttribute(HTML.Attribute.FACE); if (cssFontFamily.toString().indexOf("Sans-Serif") > -1) { Util.styleSheet().addCSSAttribute(this, CSS.Attribute.FONT_FAMILY, "SansSerif"); } else if (cssFontFamily.toString().indexOf("Monospace") > -1) { Util.styleSheet().addCSSAttribute(this, CSS.Attribute.FONT_FAMILY, "Monospaced"); } } else { removeAttribute(HTML.Attribute.FACE); if (htmlFontFace.toString().indexOf("Sans-Serif") > -1) { Util.styleSheet().addCSSAttribute(this, CSS.Attribute.FONT_FAMILY, "SansSerif"); } else if (htmlFontFace.toString().indexOf("Monospace") > -1) { Util.styleSheet().addCSSAttribute(this, CSS.Attribute.FONT_FAMILY, "Monospaced"); } else { Util.styleSheet().addCSSAttribute(this, CSS.Attribute.FONT_FAMILY, htmlFontFace.toString()); } } } else { if (cssFontFamily != null) { if (cssFontFamily.toString().indexOf("Sans-Serif") > -1) { Util.styleSheet().addCSSAttribute(this, CSS.Attribute.FONT_FAMILY, "SansSerif"); } else if (cssFontFamily.toString().indexOf("Monospace") > -1) { Util.styleSheet().addCSSAttribute(this, CSS.Attribute.FONT_FAMILY, "Monospaced"); } } } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/LinkDialog.java0100644 0000000 0000000 00000076763 12114157751 022350 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Hashtable; import java.util.Vector; import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.HTML; /** * Dialog to create and edit links. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class LinkDialog extends DialogShell implements ActionListener { /** table for link types: name -> type */ private Hashtable linkTypes; /** table for link types: type -> name */ private Hashtable linkTypeNames; /** cache for link address */ private String addressCache = null; /** the document this dialog was constructed with */ private final Document doc; private final SHTMLEditorPane editorPane; /** dialog components */ private final JComboBox linkStyle; private final JComboBox linkType; private final JTextField linkAddress; private final JButton browseAddress; private final JTextField linkAnchor; private final JButton browseAnchor; private final JTextField linkText; private final JRadioButton showAsText; private final JRadioButton showAsImage; private String linkImageFileName; private final ImagePreview linkImage; private final JButton setImage; private final JTextField linkImgWidth; private final JTextField linkImgHeight; private final JPanel linkTextPanel; private final JPanel linkImagePanel; /** some constants */ private final String LINK_TYPE_KEY = "linkType"; private final String LINK_TYPE_NAME_KEY = "linkTypeName"; private final String LINK_TYPE_RELATIVE_KEY = Util.getResourceString("linkType1"); private final String LINK_TYPE_NEWS_KEY = Util.getResourceString("linkType7"); private final String LINK_TYPE_MAILTO_KEY = Util.getResourceString("linkType8"); private final String LINK_TYPE_RELATIVE = Util.getResourceString("linkTypeName1"); private final String LINK_TYPE_LOCAL = Util.getResourceString("linkTypeName2"); private final String LINK_TYPE_NEWS = Util.getResourceString("linkTypeName7"); private final String LINK_TYPE_MAILTO = Util.getResourceString("linkTypeName8"); /** indicates, whether or not action handlers should react on events */ private boolean ignoreActions = false; /** the image directory for the document links are edited from in this dialog */ private final File imgDir; /** the currently selected image file for this link */ private String imgFile = null; /** the help id for this dialog */ private static final String helpTopicId = "item164"; private static boolean simpleLinkDialog = Util.preferenceIsTrue("simpleLinkDialog"); //private int renderMode; /** * construct a new LinkDialog * * If the selection (selectionStart and selectionEnd) has an existing link, * edit this link * Create a link for the selected text otherwise. * * @param parent the parent frame for the dialog * @param title the dialog title * @param doc the document to edit link settings for */ public LinkDialog(final Frame parent, final String title, final SHTMLEditorPane editorPane, final File imgDir/*, int renderMode*/) { // initialize DialogShell super(parent, title, helpTopicId); // save document for later use this.editorPane = editorPane; doc = editorPane.getSHTMLDocument(); this.imgDir = imgDir; //this.renderMode = renderMode; // layout and constraints to use later on final GridBagLayout g = new GridBagLayout(); final GridBagConstraints c = new GridBagConstraints(); // create link style selector final JPanel p = new JPanel(g); JLabel lb = new JLabel(Util.getResourceString("linkStyleLabel")); if (!simpleLinkDialog) { Util.addGridBagComponent(p, lb, g, c, 0, 0, GridBagConstraints.EAST); } final Vector styleNames = Util .getStyleNamesForTag(((SHTMLDocument) doc).getStyleSheet(), HTML.Tag.A.toString()); final String standardStyleName = Util.getResourceString("standardStyleName"); styleNames.insertElementAt(standardStyleName, 0); linkStyle = new JComboBox(styleNames); if (!simpleLinkDialog) { Util.addGridBagComponent(p, linkStyle, g, c, 1, 0, GridBagConstraints.WEST); } // create link type selector lb = new JLabel(Util.getResourceString("linkTypeLabel")); if (!simpleLinkDialog) { Util.addGridBagComponent(p, lb, g, c, 0, 1, GridBagConstraints.EAST); } buildLinkTypes(); linkType = new JComboBox(linkTypeNames.values().toArray()); linkType.addActionListener(this); if (!simpleLinkDialog) { Util.addGridBagComponent(p, linkType, g, c, 1, 1, GridBagConstraints.WEST); } // create link address field lb = new JLabel(Util.getResourceString("linkAddressLabel")); Util.addGridBagComponent(p, lb, g, c, 0, 2, GridBagConstraints.EAST); linkAddress = new JTextField(); linkAddress.setPreferredSize(new Dimension(300, 20)); linkAddress.setMaximumSize(new Dimension(500, 20)); linkAddress.addActionListener(this); Util.addGridBagComponent(p, linkAddress, g, c, 1, 2, GridBagConstraints.WEST, 2, 1, GridBagConstraints.HORIZONTAL, 1, 0); browseAddress = new JButton(Util.getResourceString("linkBrowseLabel")); browseAddress.addActionListener(this); Util.addGridBagComponent(p, browseAddress, g, c, 3, 2, GridBagConstraints.WEST); // create link anchor field lb = new JLabel(Util.getResourceString("linkAnchorLabel")); if (!simpleLinkDialog) { Util.addGridBagComponent(p, lb, g, c, 0, 3, GridBagConstraints.EAST); } linkAnchor = new JTextField(); linkAnchor.setPreferredSize(new Dimension(150, 20)); linkAnchor.setMaximumSize(new Dimension(500, 20)); if (!simpleLinkDialog) { Util.addGridBagComponent(p, linkAnchor, g, c, 1, 3, GridBagConstraints.WEST, 1, 1, GridBagConstraints.HORIZONTAL, 1, 0); } browseAnchor = new JButton(Util.getResourceString("linkBrowseLabel")); browseAnchor.addActionListener(this); if (!simpleLinkDialog) { Util.addGridBagComponent(p, browseAnchor, g, c, 2, 3, GridBagConstraints.WEST); } // create link display selector lb = new JLabel(Util.getResourceString("linkDisplayLabel")); if (!simpleLinkDialog) { Util.addGridBagComponent(p, lb, g, c, 0, 4, GridBagConstraints.EAST); } showAsText = new JRadioButton(Util.getResourceString("showAsTextLabel")); showAsText.addActionListener(this); showAsImage = new JRadioButton(Util.getResourceString("showAsImageLabel")); showAsImage.addActionListener(this); JPanel helpPanel = new JPanel(); helpPanel.add(showAsText); helpPanel.add(showAsImage); if (!simpleLinkDialog) { Util.addGridBagComponent(p, helpPanel, g, c, 1, 4, GridBagConstraints.WEST); } final ButtonGroup bg = new ButtonGroup(); bg.add(showAsText); bg.add(showAsImage); // create link text panel linkTextPanel = new JPanel(new BorderLayout()); linkTextPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("linkTextLabel"))); linkText = new JTextField(); linkText.setPreferredSize(new Dimension(400, 20)); linkText.setMaximumSize(new Dimension(500, 20)); if (!simpleLinkDialog) { linkTextPanel.add(linkText, BorderLayout.CENTER); Util.addGridBagComponent(p, linkTextPanel, g, c, 1, 5, GridBagConstraints.WEST, 2, 1, GridBagConstraints.HORIZONTAL, 1, 0); } else { lb = new JLabel(Util.getResourceString("linkTextLabel")); Util.addGridBagComponent(p, lb, g, c, 0, 5, GridBagConstraints.EAST); Util.addGridBagComponent(p, linkText, g, c, 1, 5, GridBagConstraints.WEST, 2, 1, GridBagConstraints.HORIZONTAL, 1, 0); } //linkTextPanel.setVisible(false); // create link image panel linkImagePanel = new JPanel(new BorderLayout(5, 5)); linkImagePanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("linkImageLabel"))); linkImage = new ImagePreview(); linkImage.setPreferredSize(new Dimension(70, 70)); linkImagePanel.add(new JScrollPane(linkImage), BorderLayout.CENTER); helpPanel = new JPanel(g); lb = new JLabel(Util.getResourceString("imgWidthLabel")); Util.addGridBagComponent(helpPanel, lb, g, c, 0, 0, GridBagConstraints.EAST); linkImgWidth = new JTextField(); linkImgWidth.setPreferredSize(new Dimension(50, 20)); linkImgWidth.setMinimumSize(new Dimension(50, 20)); linkImgWidth.setEditable(false); Util.addGridBagComponent(helpPanel, linkImgWidth, g, c, 1, 0, GridBagConstraints.WEST); lb = new JLabel(Util.getResourceString("imgHeightLabel")); Util.addGridBagComponent(helpPanel, lb, g, c, 0, 1, GridBagConstraints.EAST); linkImgHeight = new JTextField(); linkImgHeight.setPreferredSize(new Dimension(50, 20)); linkImgHeight.setMinimumSize(new Dimension(50, 20)); linkImgHeight.setEditable(false); Util.addGridBagComponent(helpPanel, linkImgHeight, g, c, 1, 1, GridBagConstraints.WEST); setImage = new JButton(Util.getResourceString("setImageLabel")); setImage.addActionListener(this); Util.addGridBagComponent(helpPanel, setImage, g, c, 1, 2, GridBagConstraints.WEST); final JPanel helpPanel2 = new JPanel(new BorderLayout()); helpPanel2.add(helpPanel, BorderLayout.NORTH); linkImagePanel.add(helpPanel2, BorderLayout.EAST); Util.addGridBagComponent(p, linkImagePanel, g, c, 1, 5, GridBagConstraints.WEST, 2, 1, GridBagConstraints.BOTH, 1, 1); // get content pane of DialogShell to add components to final Container contentPane = super.getContentPane(); // add panels to content pane of DialogShell contentPane.add(p, BorderLayout.CENTER); // add key listeners // The following could perhaps be done by adding the listener only // to one place. But to which one? linkAddress.addKeyListener(getCompletionKeyListener()); linkText.addKeyListener(getCompletionKeyListener()); linkAnchor.addKeyListener(getCompletionKeyListener()); // cause optimal placement of all elements pack(); // init dialog with existing link (if any) if (!setExistingLink(editorPane.getSelectionStart(), editorPane.getSelectionEnd())) { setLinkText(editorPane.getSelectionStart(), editorPane.getSelectionEnd()); } } /** * set the link text component of this dialog from the document * this dialog is associated to * * @param start the start position of the link text in the document * @param end the end position of the link text in the document */ private void setLinkText(final int start, final int end) { try { linkText.setText(doc.getText(start, end - start)); //System.out.println("showAsText = true"); showAsText.setSelected(true); linkTextPanel.setVisible(true); linkImagePanel.setVisible(false); } catch (final BadLocationException ble) { Util.errMsg(this, ble.getLocalizedMessage(), ble); } } /** * set components of this dialog from an exisiting link in * the associated document (if any). * * @param selectionStart the start position of the text currently selected in the document * @param selectionEnd the end position of the text currently selected in the document * * @return ture, if a link was found, false if not */ private boolean setExistingLink(final int selectionStart, final int selectionEnd) { setIgnoreActions(true); final Element linkElement = editorPane.getCurrentLinkElement(); final boolean foundLink = (linkElement != null); if (foundLink) { final AttributeSet elemAttrs = linkElement.getAttributes(); final Object linkAttr = elemAttrs.getAttribute(HTML.Tag.A); final Object href = ((AttributeSet) linkAttr).getAttribute(HTML.Attribute.HREF); if (href != null) { try { setDialogFromUrl(new URL(href.toString())); } catch (final Exception ex) { setDialogFromRelative(href.toString()); } final Object img = elemAttrs.getAttribute(HTML.Attribute.SRC); if (img != null) { setLinkImage(img, elemAttrs); } else { setLinkText(linkElement.getStartOffset(), linkElement.getEndOffset()); } } } else { linkType.setSelectedItem(LINK_TYPE_LOCAL); setLinkText(selectionStart, selectionEnd); } setIgnoreActions(false); return foundLink; } /** * set the link image to be shown in this dialog from a given * image file name and AttributeSet * * @param imgAttr the file name of the image to be shown * @param attrSet the set of attributes having width and height of the image (if any) */ public void setLinkImage(final Object imgAttr, final AttributeSet attrSet) { String wStr = null; String hStr = null; if (imgAttr != null) { imgFile = Util.resolveRelativePath(imgAttr.toString(), ((SHTMLDocument) doc).getBase().getPath()).replace( Util.URL_SEPARATOR_CHAR, File.separatorChar); while (imgFile.startsWith(File.separator)) { imgFile = imgFile.substring(1); } } final Object width = attrSet.getAttribute(HTML.Attribute.WIDTH); if (width != null) { wStr = width.toString(); } final Object height = attrSet.getAttribute(HTML.Attribute.HEIGHT); if (height != null) { hStr = height.toString(); } setImageSpecs(wStr, hStr); showAsImage.setSelected(true); linkTextPanel.setVisible(false); linkImagePanel.setVisible(true); } /** * get the text to be displayed for the link * * @return the link text */ public String getLinkText() { return linkText.getText(); } /** * get the style name (attribute 'class') to be used for * a link * * @return the style name */ public String getStyleName() { return linkStyle.getSelectedItem().toString(); } /** * set this dialog to ignore actions * * @param ignore indicator whether or not to ignore actions */ public void setIgnoreActions(final boolean ignore) { ignoreActions = ignore; } /** * set the components of this dialog from a given URL * * @param url the url to set link components from */ private void setDialogFromUrl(final URL url) { if (url == null) { return; } if (simpleLinkDialog) { setLinkAddress(url.toString()); return; } String protName; final String protocol = url.getProtocol(); if (protocol != null) { protName = (String) linkTypeNames.get(protocol); } else { protName = (String) linkTypeNames.get(LINK_TYPE_RELATIVE_KEY); } if (protName != null) { linkType.setSelectedItem(protName); } setLinkAddress(getPathFromUrl(url, protocol)); linkAnchor.setText(url.getRef()); } /** * extract the path from a URL * * @param url the url to get the path from * @param protocol the protocol of the url * * @return the path of the URL */ private String getPathFromUrl(final URL url, final String protocol) { String path = ""; final String urlStr = url.toString(); int pos = urlStr.indexOf(protocol); if (pos > -1) { path = urlStr.substring(protocol.length()); while (path.startsWith(Util.URL_SEPARATOR) || path.startsWith(Util.PROTOCOL_SEPARATOR)) { path = path.substring(1); } } pos = path.indexOf(Util.ANCHOR_SEPARATOR); if (pos > -1) { path = path.substring(0, pos); } return path; } /** * set components of this dialog from a relative link path * * @param hrefStr the relative link to show in the dialog */ private void setDialogFromRelative(String hrefStr) { linkType.setSelectedItem(LINK_TYPE_RELATIVE); final int pos = hrefStr.indexOf(Util.ANCHOR_SEPARATOR); if (pos > -1) { linkAnchor.setText(hrefStr.substring(pos + 1)); hrefStr = hrefStr.substring(0, pos); } setLinkAddress(hrefStr); } /** * build link type tables to match * type names by types and vice versa */ private void buildLinkTypes() { String name; String type; linkTypes = new Hashtable(); // key = type name -> value = type linkTypeNames = new Hashtable(); // key = type -> value = type name for (int i = 1; i < 9; i++) { type = Util.getResourceString(LINK_TYPE_KEY + Integer.toString(i)); name = Util.getResourceString(LINK_TYPE_NAME_KEY + Integer.toString(i)); linkTypes.put(name, type); linkTypeNames.put(type, name); } } private boolean linkAddressHasProtocol() { return linkAddress.getText().indexOf(':') >= 0; } /** * get the chosen protocol */ private String getProtocol() { String prot = null; try { final String protName = linkType.getSelectedItem().toString(); if (!protName.equalsIgnoreCase(LINK_TYPE_RELATIVE)) { prot = transformProtocol(linkTypes.get(protName).toString()); } } catch (final Exception e) { } return prot; } /** * transform a given protocol to be shown in the correct notation */ private String transformProtocol(final String protName) { final StringBuffer prot = new StringBuffer(protName); if (protName.equalsIgnoreCase(LINK_TYPE_MAILTO_KEY) || protName.equalsIgnoreCase(LINK_TYPE_NEWS_KEY)) { prot.append(Util.PROTOCOL_SEPARATOR); } else { if (!protName.equalsIgnoreCase(LINK_TYPE_RELATIVE_KEY)) { prot.append(Util.PROTOCOL_SEPARATOR + Util.URL_SEPARATOR); } } return prot.toString(); } /** * Gets a file from a file chooser. * * @return the chosen file, or null, if none has been chosen or cancel has benn pressed */ private File chooseFile() { File file = null; final JFileChooser chooser = new JFileChooser(); chooser.setMultiSelectionEnabled(false); chooser.setSelectedFile(new File(((SHTMLDocument) doc).getBase().getFile())); if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { file = chooser.getSelectedFile(); } return file; } /** * set the address field of this LinkDialog * * @param address the address to be set */ private void setLinkAddress(final String address) { addressCache = getLinkAddress(); linkAddress.setText(address); } /** * get a set of attributes to represent the link * defined in this dialog * * @return the set of attributes defining this link */ public AttributeSet getLinkAttribute() { final SimpleAttributeSet aSet = new SimpleAttributeSet(); aSet.addAttribute(HTML.Attribute.HREF, getHref()); final SimpleAttributeSet set = new SimpleAttributeSet(); if (showAsImage.isSelected()) { final SimpleAttributeSet imgSet = new SimpleAttributeSet(); imgSet.addAttribute(HTML.Attribute.SRC, imgFile); set.addAttribute(HTML.Tag.IMG, imgSet); } set.addAttribute(HTML.Tag.A, aSet); return set; } /** * get the file name of the image to be taken * for the link defined in this dialog * * @return the image file name */ public String getLinkImage() { if (showAsImage.isSelected()) { return imgFile; } else { return null; } } /** * get the size of the image to be taken * for the link defined in this dialog * * @return the image size */ public Dimension getLinkImageSize() { if (showAsImage.isSelected()) { try { return new Dimension(Integer.parseInt(linkImgWidth.getText()), Integer.parseInt(linkImgHeight.getText())); } catch (final Exception e) { return null; } } else { return null; } } /** * get the currently selected link address * * @return the link address */ public String getLinkAddress() { String link = linkAddress.getText(); final String prot = getProtocol(); if (prot != null && !linkAddressHasProtocol() && !prot.equalsIgnoreCase(transformProtocol(LINK_TYPE_RELATIVE_KEY)) && !prot.equalsIgnoreCase(transformProtocol(LINK_TYPE_MAILTO_KEY)) && !link.startsWith(Util.URL_SEPARATOR)) { link = Util.URL_SEPARATOR + link; } return link; } /** * Gets the URL string of the hyperlink's reference. * * @return the object this link refers to */ public String getHref() { final StringBuffer href = new StringBuffer(); final String protocol = getProtocol(); final String linkAddressText = linkAddress.getText(); final String linkAnchorText = linkAnchor.getText(); if ((linkAddressText == null || linkAddressText.length() < 1) && (linkAnchorText != null && linkAnchorText.length() > 0)) { // link to an anchor inside this document href.append(Util.ANCHOR_SEPARATOR); href.append(linkAnchorText); } else { if (protocol != null && !linkAddressHasProtocol()) { href.append(protocol); } href.append(getLinkAddress()); final String anchor = linkAnchor.getText(); if (anchor.length() > 0) { href.append(Util.ANCHOR_SEPARATOR); href.append(anchor); } } return href.toString(); } /** * get the file this link refers to (if any) * * @return the file this link refers to, or null if no file is referenced */ private File getLinkedFile() { File file = null; try { final String prot = linkType.getSelectedItem().toString(); if (prot.equalsIgnoreCase(LINK_TYPE_LOCAL)) { file = new File(getLinkAddress().replace(Util.URL_SEPARATOR_CHAR, File.separatorChar)); } else if (prot.equalsIgnoreCase(LINK_TYPE_RELATIVE)) { new File(((SHTMLDocument) doc).getBase().getPath()); final String toStr = getLinkAddress(); new File(toStr); file = new File(Util.resolveRelativePath(getLinkAddress(), ((SHTMLDocument) doc).getBase().getPath())); } } catch (final Exception e) { } return file; } /** -------- ActionListener implementation start (including additional handling methods) ---------- */ /** * actionListener implementation to control dialog components */ public void actionPerformed(final ActionEvent e) { if (!ignoreActions) { final Object source = e.getSource(); if (source.equals(showAsText)) { linkTextPanel.setVisible(true); linkImagePanel.setVisible(false); } else if (source.equals(showAsImage)) { linkTextPanel.setVisible(false); linkImagePanel.setVisible(true); } else if (source.equals(browseAddress)) { final File file = chooseFile(); if (file != null) { if (simpleLinkDialog) { try { setLinkAddress(file.toURI().toURL().toString()); } catch (final Exception ex) { } } else { setLinkAddress(file.getPath().replace(File.separatorChar, Util.URL_SEPARATOR_CHAR)); } } } else if (source.equals(linkType)) { handleLinkTypeAction(); } else if (source.equals(browseAnchor)) { handleBrowseAnchorAction(); } else if (source.equals(setImage)) { handleLinkImageAction(); } else { super.actionPerformed(e); } } } /** * handle an action performed by the component that allows * selection of a link image */ private void handleLinkImageAction() { final ImageDialog dlg = new ImageDialog(this, Util.getResourceString("imageDialogTitle"), imgDir); if (imgFile != null) { dlg.setImage(imgFile, linkImgWidth.getText(), linkImgHeight.getText()); } Util.center(this, dlg); dlg.setModal(true); dlg.setVisible(true); /** if the user made a selection, apply it to the document */ if (dlg.getResult() == DialogShell.RESULT_OK) { imgFile = Util.resolveRelativePath(dlg.getImageSrc(), ((SHTMLDocument) doc).getBase().getPath()).replace( Util.URL_SEPARATOR_CHAR, File.separatorChar); while (imgFile.startsWith(File.separator)) { imgFile = imgFile.substring(1); } setImageSpecs(dlg.getImgWidth().toString(), dlg.getImgHeight().toString()); } } /** * set the properties of the image to be shown for * the link defined ni this dialog * * @param width image width * @param height image height */ private void setImageSpecs(final String width, final String height) { final ImageIcon icon = new ImageIcon(imgFile); linkImage.setImage(icon); linkImage.setScale(100); if (width != null) { linkImgWidth.setText(width); linkImage.setPreviewWidth(Integer.parseInt(width)); } if (height != null) { linkImgHeight.setText(height); linkImage.setPreviewHeight(Integer.parseInt(height)); } } /** * handle an action performed by the component that allows * selection of a link protocol ('link type' on the GUI) */ private void handleLinkTypeAction() { final String type = linkType.getSelectedItem().toString(); browseAddress.setEnabled(type.equalsIgnoreCase(LINK_TYPE_LOCAL)); browseAnchor.setEnabled(type.equalsIgnoreCase(LINK_TYPE_LOCAL) || type.equalsIgnoreCase(LINK_TYPE_RELATIVE)); if (type.equalsIgnoreCase(LINK_TYPE_RELATIVE)) { try { final File from = new File(((SHTMLDocument) doc).getBase().getPath()); final String toStr = getLinkAddress(); final File to = new File(toStr); setLinkAddress(Util.getRelativePath(from, to)); } catch (final Exception ex) { Util.errMsg(this, null, ex); } } else if (type.equalsIgnoreCase(LINK_TYPE_LOCAL)) { try { final String absPath = ((SHTMLDocument) doc).getBase().getFile().substring(1); final String relPath = getLinkAddress(); setLinkAddress(Util.URL_SEPARATOR + Util.resolveRelativePath(relPath, absPath)); } catch (final Exception ex) { Util.errMsg(this, ex.getMessage(), ex); } } } /** * Handles an action performed by the button used * to browse anchors of a given file. */ private void handleBrowseAnchorAction() { //System.out.println("LinkDialog actionPerformed browseAnchor file=" + getLinkedFile().getAbsolutePath()); try { AnchorDialog anchorDialog; final File file = getLinkedFile(); final String linkAddrText = linkAddress.getText(); if (linkAddrText == null || linkAddrText.length() < 1) { anchorDialog = new AnchorDialog(this, Util.getResourceString("anchorDialogTitle"), doc); } else { anchorDialog = new AnchorDialog(this, Util.getResourceString("anchorDialogTitle"), file.toURI().toURL()); } Util.center(this, anchorDialog); anchorDialog.setModal(true); anchorDialog.setVisible(true); if (anchorDialog.getResult() == DialogShell.RESULT_OK) { linkAnchor.setText(anchorDialog.getAnchor()); } } catch (final MalformedURLException ex) { Util.errMsg(this, ex.getMessage(), ex); } } /** -------- ActionListener implementation end (including additional handling methods) ---------- */ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/PluginManager.java0100644 0000000 0000000 00000024022 12114157751 023041 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * Finds and loads plug-ins of application SimplyHTML. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class PluginManager { /** name of sub-package where plug-ins are stored */ private final String PLUGIN_PACKAGE = "installed"; /** the class loader pointing to all plug-in locations (JARs) */ private URLClassLoader loader; /** the class names of all loaded plug-ins */ private final Vector pluginClassNames = new Vector(); /** the plug-in objects loaded by this PluginManager */ private final Hashtable loadedPlugins = new Hashtable(); private final Hashtable nameMap = new Hashtable(); /** the URLs pointing to the classes in pluginClassNames */ private final Vector urls = new Vector(); private final SHTMLPanelImpl owner; /** * construct a new PluginManager and load * all available plug-ins. */ public PluginManager(final SHTMLPanelImpl owner) { this.owner = owner; } /** * get all plug-ins loaded by this PluginManager */ public Enumeration plugins() { return loadedPlugins.elements(); } public ClassLoader getPluginLoader() { return loader; } /** * get a loaded plug-in by its GUI name. * * @param guiName the GUI name of this plaug-in * * @return the plug-in having the given GUI name, or null of no plug-in * with that name is present */ public SHTMLPlugin pluginForName(final String guiName) { final String intName = (String) nameMap.get(guiName); return (SHTMLPlugin) loadedPlugins.get(intName); } public Object[] getPluginNames() { return nameMap.keySet().toArray(); } /** * load all plug-ins found in the plug-in path */ public void loadPlugins() { loadedPlugins.clear(); final String pluginPrefix = this.getClass().getPackage().getName() + Util.CLASS_SEPARATOR + PLUGIN_PACKAGE + Util.CLASS_SEPARATOR; findPlugins(pluginPrefix.replace(Util.CLASS_SEPARATOR_CHAR, Util.URL_SEPARATOR_CHAR)); final Enumeration cNames = pluginClassNames.elements(); Class cl; Object o; SHTMLPlugin p; String intName; while (cNames.hasMoreElements()) { try { final String nextClass = (String) cNames.nextElement(); //System.out.println("PluginManager loadPlugins loading " + pluginPrefix + nextClass /* pluginPrefix + (String) cNames.nextElement()*/); cl = loader.loadClass(/*pluginPrefix +*/ /*(String) cNames.nextElement()*/pluginPrefix + nextClass); //System.out.println("PluginManager loadPlugins calling newInstance "); o = cl.newInstance(); if (o instanceof SHTMLPlugin) { p = (SHTMLPlugin) o; p.initPlugin(owner, null, null, null); //p.setOwner(owner); //p.initPluginActions(); intName = p.getInternalName(); loadedPlugins.put(intName, o); nameMap.put(p.getGUIName(), intName); } } catch (final Exception e) { Util.errMsg(null, this.getClass().getName() + ".loadPlugins: " + e.getMessage(), e); } } } /** * get a class loader for a given set of URLs * specifying one or more class paths * * @param urls set of URLs specifying the class path(s) * * @return the class loader */ private URLClassLoader createLoader(final Vector urls) { final URL[] urlArray = new URL[urls.size()]; for (int i = 0; i < urls.size(); i++) { urlArray[i] = (URL) urls.elementAt(i); //System.out.println("urlArray[" + i + "]=" + urlArray[i]); } return new URLClassLoader(urlArray, this.getClass().getClassLoader()); } /** * find plug-ins by looking for JARs inside a * given path and create a class loader for them. * *

JARs are searched in sub-package .plugin.installed * of the package this class resides in.

* *

On return of this method fields loader and * pluginClassNames are initialized and * filled accordingly.

* * @param pluginPath the path to look for plug-in JAR files, e.g. * com/lightdev/app/shtm/plugin/installed/ */ private void findPlugins(final String pluginPath) { final String appPath = Util.getClassFilePath(this.getClass()); String filePath; if (appPath.indexOf(":") < 0) { filePath = "/" + appPath; } else { filePath = appPath; } //System.out.println("PluginManager.findPlugins appPath=" + appPath + ", filePath=" + filePath); pluginClassNames.clear(); urls.clear(); String fName; try { final File plugindir = new File(filePath);//new URI(Util.FILE_PREFIX + Util.URL_SEPARATOR + appPath)); if (plugindir != null) { final File[] content = plugindir.listFiles(); if (content != null) { for (int i = 0; i < content.length; i++) { if (content[i].isFile()) { fName = content[i].getName(); //System.out.println("PluginManager.findPlugins fName=" + fName); if (fName.toLowerCase().endsWith("jhall.jar")) { /*System.out.println("PluginManager.findPlugins adding URL " + Util.FILE_PREFIX + Util.URL_SEPARATOR + appPath + fName);*/ urls.addElement(new URL(Util.FILE_PREFIX + Util.URL_SEPARATOR + appPath + fName)); } if (fName.toLowerCase().endsWith("simplyhtml.jar")) { /*System.out.println("PluginManager.findPlugins adding URL " + Util.FILE_PREFIX + Util.URL_SEPARATOR + appPath + fName);*/ urls.addElement(new URL(Util.FILE_PREFIX + Util.URL_SEPARATOR + appPath + fName)); } else if (fName.endsWith(Util.JAR_EXTENSION)) { readJar(appPath, pluginPath, content[i], fName); } } } } } loader = createLoader(urls); } catch (final Exception e) { Util.errMsg(null, this.getClass().getName() + ".findPlugins: " + e.getMessage(), e); } } /** * read a Java archive (JAR) file and look for classes within * a given package path inside the JAR file. Store class names and * their URLS for later use. * *

Some of the parameters required below could be extracted from * others passed to this method but as previous methods already determined * respective paramaters, it is faster to pass the existing values as * parameters than to re-build the values locally.

* * @param filePath the absolute path pointing to the JAR file, e.g. * file:/C:/Programs/SimplyHTML/ * @param pluginPath the path inside filePath pointing to potential plug-ins * @param jarFile the file object referring to the JAR file to read * @param fileName the name of the JAR file */ private void readJar(final String filePath, final String pluginPath, final File jarFile, final String fileName) { try { final Enumeration jarEntries = new JarFile(jarFile).entries(); JarEntry je; String jeName; while (jarEntries.hasMoreElements()) { je = (JarEntry) jarEntries.nextElement(); jeName = je.getName(); if (jeName.startsWith(pluginPath) && !je.isDirectory() && jeName.endsWith(Util.CLASS_EXT)) { /*System.out.println("PluginManager.readJar adding URL " + Util.FILE_PREFIX + Util.URL_SEPARATOR + filePath + fileName);*/ urls.addElement(new URL(Util.FILE_PREFIX + Util.URL_SEPARATOR + filePath + fileName)); pluginClassNames.addElement(jeName.substring(pluginPath.length(), jeName.indexOf(Util.CLASS_SEPARATOR))); } } } catch (final Exception e) { /*Util.errMsg(null, this.getClass().getName() + ".readJar: " + e.getMessage(), e);*/ } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLDocument.java0100644 0000000 0000000 00000070256 12744670610 022713 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Color; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URL; import java.util.HashSet; import java.util.Set; import javax.swing.event.DocumentEvent; import javax.swing.event.UndoableEditEvent; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.GapContent; import javax.swing.text.MutableAttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.html.HTML; import javax.swing.text.html.HTML.Tag; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import javax.swing.undo.CompoundEdit; import javax.swing.undo.UndoableEdit; /** * Extends HTMLDocument by a custom reader which supports * the SPAN tag. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ public class SHTMLDocument extends HTMLDocument { public static final String SUFFIX = " "; private static Set paragraphElements; private CompoundEdit compoundEdit; private int compoundEditDepth; private boolean inSetParagraphAttributes = false; private boolean baseDirChecked = false; private final boolean keepSpanTag = Util.preferenceIsTrue("keepSpanTag"); /** * Constructs an SHTMLDocument. */ public SHTMLDocument() { this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet()); } /** * Constructs an SHTMLDocument with the default content * storage implementation and the given style/attribute * storage mechanism. * * @param styles the styles */ public SHTMLDocument(final StyleSheet styles) { this(new GapContent(BUFFER_SIZE_DEFAULT), styles); } /** * Constructs an SHTMLDocument with the given content * storage implementation and the given style/attribute * storage mechanism. * * @param c the container for the content * @param styles the styles */ public SHTMLDocument(final Content c, final StyleSheet styles) { super(c, styles); compoundEdit = null; } /** * apply a set of attributes to a given document element * * @param e the element to apply attributes to * @param a the set of attributes to apply */ public void addAttributes(final Element e, final AttributeSet a) { if ((e != null) && (a != null)) { try { writeLock(); //System.out.println("SHTMLDocument addAttributes e=" + e); //System.out.println("SHTMLDocument addAttributes a=" + a); final int start = e.getStartOffset(); final DefaultDocumentEvent changes = new DefaultDocumentEvent(start, e.getEndOffset() - start, DocumentEvent.EventType.CHANGE); final AttributeSet sCopy = a.copyAttributes(); final MutableAttributeSet attr = (MutableAttributeSet) e.getAttributes(); changes.addEdit(new AttributeUndoableEdit(e, sCopy, false)); attr.addAttributes(a); changes.end(); fireChangedUpdate(changes); fireUndoableEditUpdate(new UndoableEditEvent(this, changes)); } finally { writeUnlock(); } } } /** * Removes a consecutive group of child elements. * * @param element the parent element to remove child elements from * @param index the index of the first child element to remove * @param count the number of child elements to remove */ public void removeElements(final Element element, final int index, final int count) throws BadLocationException { writeLock(); final int start = element.getElement(index).getStartOffset(); final int end = element.getElement(index + count - 1).getEndOffset(); try { final Element[] removed = new Element[count]; final Element[] added = new Element[0]; for (int counter = 0; counter < count; counter++) { removed[counter] = element.getElement(counter + index); } final DefaultDocumentEvent defaultDocumentEvent = new DefaultDocumentEvent(start, end - start, DocumentEvent.EventType.REMOVE); ((AbstractDocument.BranchElement) element).replace(index, removed.length, added); defaultDocumentEvent.addEdit(new ElementEdit(element, index, removed, added)); final UndoableEdit undoableEdit = getContent().remove(start, end - start); if (undoableEdit != null) { defaultDocumentEvent.addEdit(undoableEdit); } postRemoveUpdate(defaultDocumentEvent); defaultDocumentEvent.end(); fireRemoveUpdate(defaultDocumentEvent); if (undoableEdit != null) { fireUndoableEditUpdate(new UndoableEditEvent(this, defaultDocumentEvent)); } } finally { writeUnlock(); } } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument#setOuterHTML(javax.swing.text.Element, java.lang.String) */ public void setOuterHTML(final Element paragraphElement, final String htmlText) throws BadLocationException, IOException { try { startCompoundEdit(); if (paragraphElement.getName().equalsIgnoreCase("p-implied")) { //What has to be replaced is the HTML of the parent of this implied element. final Element parentElement = paragraphElement.getParentElement(); final SHTMLWriter writer = new SHTMLWriter(this); final int indexOfElement = parentElement.getElementIndex(paragraphElement.getStartOffset()); writer.writeStartTag(parentElement); for (int i = 0; i < indexOfElement; i++) { writer.write(parentElement.getElement(i)); } writer.write(htmlText); for (int i = indexOfElement + 1; i < parentElement.getElementCount(); i++) { writer.write(parentElement.getElement(i)); } writer.writeEndTag(parentElement); super.setOuterHTML(parentElement, writer.toString()); } else { super.setOuterHTML(paragraphElement, htmlText); } } finally { endCompoundEdit(); } } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument#insertAfterEnd(javax.swing.text.Element, java.lang.String) */ public void insertAfterEnd(final Element elem, final String htmlText) throws BadLocationException, IOException { try { startCompoundEdit(); super.insertAfterEnd(elem, htmlText); } finally { endCompoundEdit(); } } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument#insertAfterStart(javax.swing.text.Element, java.lang.String) */ public void insertAfterStart(final Element elem, final String htmlText) throws BadLocationException, IOException { try { startCompoundEdit(); super.insertAfterStart(elem, htmlText); } finally { endCompoundEdit(); } } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument#insertBeforeEnd(javax.swing.text.Element, java.lang.String) */ public void insertBeforeEnd(final Element elem, final String htmlText) throws BadLocationException, IOException { try { startCompoundEdit(); super.insertBeforeEnd(elem, htmlText); } finally { endCompoundEdit(); } } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument#insertBeforeStart(javax.swing.text.Element, java.lang.String) */ public void insertBeforeStart(final Element elem, final String htmlText) throws BadLocationException, IOException { try { startCompoundEdit(); super.insertBeforeStart(elem, htmlText); } finally { endCompoundEdit(); } } /** */ public void replaceHTML(final Element firstElement, final int number, final String htmlText) throws BadLocationException, IOException { if (number > 1) { if (firstElement != null && firstElement.getParentElement() != null && htmlText != null) { final int start = firstElement.getStartOffset(); final Element parent = firstElement.getParentElement(); final int removeIndex = parent.getElementIndex(start); try { startCompoundEdit(); removeElements(parent, removeIndex, number - 1); setOuterHTML(parent.getElement(removeIndex), htmlText); } finally { endCompoundEdit(); } } } else if (number == 1) { setOuterHTML(firstElement, htmlText); } } public void startCompoundEdit() { compoundEditDepth++; } public void endCompoundEdit() { if (compoundEditDepth != 0) { compoundEditDepth--; if (compoundEditDepth == 0 && compoundEdit != null) { compoundEdit.end(); super.fireUndoableEditUpdate(new UndoableEditEvent(this, compoundEdit)); compoundEdit = null; } } } protected void fireUndoableEditUpdate(final UndoableEditEvent e) { if (compoundEditDepth == 0) { super.fireUndoableEditUpdate(e); } else { if (compoundEdit == null) { compoundEdit = new CompoundEdit();; } compoundEdit.addEdit(e.getEdit()); } } /* ------------------ custom document title handling start -------------------- */ /** * set the title of this SHTMLDocument * * @param title the title this document shall have */ public void setDocumentTitle(final String title) { try { final String titleHTML = ""; final Element defaultRoot = getDefaultRootElement(); final Element head = Util.findElementDown(HTML.Tag.HEAD.toString(), defaultRoot); if (head != null) { final Element pImpl = Util.findElementDown(HTML.Tag.IMPLIED.toString(), head); if (pImpl != null) { final Element tElem = Util.findElementDown(HTML.Tag.TITLE.toString(), pImpl); if (tElem == null) { insertBeforeEnd(pImpl, titleHTML); } } } else { final Element body = Util.findElementDown(HTML.Tag.BODY.toString(), defaultRoot); insertBeforeStart(body, "" + titleHTML + ""); } putProperty(Document.TitleProperty, title); } catch (final Exception e) { Util.errMsg(null, "An exception occurred while trying to insert the title", e); } } /** * get the title of this SHTMLDocument * * @return the title of this document or null if none was set so far */ public String getDocumentTitle() { final Object title = getProperty(Document.TitleProperty); if (title != null) { return title.toString(); } else { return null; } } /* ------------------ custom document title handling end -------------------- */ /* ------------------ custom style sheet reference handling start -------------------- */ /** * insert a style sheet reference into the head of this SHTMLDocument */ public void insertStyleRef() { try { final String styleRef = " "; final Element defaultRoot = getDefaultRootElement(); final Element head = Util.findElementDown(HTML.Tag.HEAD.toString(), defaultRoot); if (head != null) { final Element pImpl = Util.findElementDown(HTML.Tag.IMPLIED.toString(), head); if (pImpl != null) { final Element link = Util.findElementDown(HTML.Tag.LINK.toString(), pImpl); if (link != null) { setOuterHTML(link, styleRef); } else { insertBeforeEnd(pImpl, styleRef); } } } else { final Element body = Util.findElementDown(HTML.Tag.BODY.toString(), defaultRoot); insertBeforeStart(body, "" + styleRef + ""); } } catch (final Exception e) { Util.errMsg(null, "An exception occurred while trying to insert the style sheet reference link", e); } } /** * check whether or not this SHTMLDocument has an explicit style sheet reference * * @return true, if a style sheet reference was found, false if not */ public boolean hasStyleRef() { return (getStyleRef() != null); } /** * get the style sheet reference of the document in this * DocumentPane. * * @return the reference to this document's style sheet or * null if none is found */ public String getStyleRef() { String linkName = null; final Element link = Util.findElementDown(HTML.Tag.LINK.toString(), getDefaultRootElement()); if (link != null) { final Object href = link.getAttributes().getAttribute(HTML.Attribute.HREF); if (href != null) { linkName = href.toString(); } } return linkName; } /* ------------------ custom style sheet reference handling end -------------------- */ /* -------- custom reader implementation start ------ */ /** * Fetches the reader for the parser to use to load the document * with HTML. This is implemented to return an instance of * SHTMLDocument.SHTMLReader. */ public HTMLEditorKit.ParserCallback getReader(final int pos) { final Object desc = getProperty(Document.StreamDescriptionProperty); if (desc instanceof URL) { setBase((URL) desc); } final SHTMLReader reader = new SHTMLReader(pos, getLength() == 0); return reader; } /** * This reader extends HTMLDocument.HTMLReader by the capability * to handle SPAN tags */ public class SHTMLReader extends HTMLDocument.HTMLReader { /** action needed to handle SPAN tags */ SHTMLCharacterAction characterAction = new SHTMLCharacterAction(); /** the attributes found in a STYLE attribute */ AttributeSet styleAttributes; /** indicates whether we're inside a SPAN tag */ boolean inSpan = false; boolean emptyDocument; private boolean paragraphInserted; private boolean inBody; private int inPreLevel = 0; private boolean paragraphCreated; private boolean isParagraphTag; /** * Constructor * */ public SHTMLReader(final int offset, final boolean emptyDocument) { super(offset, 0, 0, null); this.emptyDocument = emptyDocument; inBody = false; paragraphInserted = false; paragraphCreated = false; } /** * Handles the start tag received by the parser. * * If it is a SPAN tag, converts the contents of the STYLE * attribute to an AttributeSet, and adds it to the contents * of this tag. * * Otherwise lets HTMLDocument.HTMLReader do the work. */ public void handleStartTag(final HTML.Tag tag, final MutableAttributeSet attributeSet, final int pos) { if (tag == HTML.Tag.BODY) { inBody = true; } else if (inBody) { isParagraphTag = isParagraphTag(tag); if (isParagraphTag) { if(HTML.Tag.PRE.equals(tag)) { inPreLevel++; if(inPreLevel > 1) return; } else if(inPreLevel >= 1) return; if (paragraphCreated && !paragraphInserted) { insertParagraphEndTag(pos); } paragraphInserted = true; } else if (!paragraphCreated && !paragraphInserted) { insertParagraphStartTag(pos); } } if (tag == HTML.Tag.SPAN && !keepSpanTag) { handleStartSpan(attributeSet); } else { super.handleStartTag(tag, attributeSet, pos); if (tag == HTML.Tag.FONT) { charAttr.removeAttribute(tag); } } } private void insertParagraphStartTag(final int pos) { super.handleStartTag(HTML.Tag.P, new SimpleAttributeSet(), pos); paragraphCreated = true; paragraphInserted = true; } private void insertParagraphEndTag(final int pos) { super.handleEndTag(HTML.Tag.P, pos); paragraphCreated = false; } private boolean isParagraphTag(final Tag t) { if (paragraphElements == null) { paragraphElements = new HashSet(); final Object[] elementList = new Object[] { HTML.Tag.BLOCKQUOTE, HTML.Tag.DIR, HTML.Tag.DIV, HTML.Tag.DL, HTML.Tag.DT, HTML.Tag.FRAMESET, HTML.Tag.H1, HTML.Tag.H2, HTML.Tag.H3, HTML.Tag.H4, HTML.Tag.H5, HTML.Tag.H6, HTML.Tag.HR, HTML.Tag.LI, HTML.Tag.MENU, HTML.Tag.OL, HTML.Tag.P, HTML.Tag.PRE, HTML.Tag.TABLE, HTML.Tag.TD, HTML.Tag.TH, HTML.Tag.TR, HTML.Tag.UL }; for (int i = 0; i < elementList.length; i++) { paragraphElements.add(elementList[i]); } } return paragraphElements.contains(t); } private void handleStartSpan(final MutableAttributeSet attributeSet) { if (attributeSet.isDefined(HTML.Attribute.STYLE)) { final String styleAttributeValue = (String) attributeSet.getAttribute(HTML.Attribute.STYLE); attributeSet.removeAttribute(HTML.Attribute.STYLE); styleAttributes = getStyleSheet().getDeclaration(styleAttributeValue); attributeSet.addAttributes(styleAttributes); } else { styleAttributes = null; } final TagAction action = characterAction; if (action != null) { /** Remembers which part we're in for handleSimpleTag. */ inSpan = true; action.start(HTML.Tag.SPAN, attributeSet); } } /** * SPAN tags are directed to handleSimpleTag by the parser. * If a SPAN tag is detected in this method, it gets redirected * to handleStartTag and handleEndTag respectively. */ public void handleSimpleTag(final HTML.Tag t, final MutableAttributeSet a, final int pos) { if (inBody && !paragraphCreated && !paragraphInserted) { insertParagraphStartTag(pos); } if (t == HTML.Tag.SPAN && !keepSpanTag) { if (inSpan) { handleEndTag(t, pos); } else { handleStartTag(t, a, pos); } } else { super.handleSimpleTag(t, a, pos); } } /** * Handles end tag. If a SPAN tag is directed to this method, end its action, * otherwise, let HTMLDocument.HTMLReader do the work */ public void handleEndTag(final HTML.Tag tag, final int pos) { if (tag == HTML.Tag.BODY) { if (paragraphCreated) { insertParagraphEndTag(pos); } inBody = false; if (emptyDocument) { if (!paragraphInserted) { super.handleStartTag(HTML.Tag.P, getEndingAttributeSet(), pos); super.handleText("\n".toCharArray(), pos); super.handleEndTag(HTML.Tag.P, pos); } super.handleStartTag(HTML.Tag.P, getEndingAttributeSet(), pos); super.handleText(" ".toCharArray(), pos); super.handleEndTag(HTML.Tag.P, pos); } super.handleEndTag(tag, pos); } else if (tag == HTML.Tag.SPAN && !keepSpanTag) { handleEndSpan(); } else { if(HTML.Tag.PRE.equals(tag) && inPreLevel > 0) inPreLevel--; if(inPreLevel == 0 || ! isParagraphTag(tag)) super.handleEndTag(tag, pos); } } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument.HTMLReader#handleComment(char[], int) */ public void handleComment(final char[] data, final int pos) { if (emptyDocument) { super.handleComment(data, pos); } } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument.HTMLReader#handleText(char[], int) */ private void handleEndSpan() { final TagAction action = characterAction; if (action != null) { /** * remember which part we're in for handleSimpleTag */ inSpan = false; action.end(HTML.Tag.SPAN); } } /** * Is used to read the style attribute from * a SPAN tag and to map from HTML to Java attributes. */ class SHTMLCharacterAction extends HTMLDocument.HTMLReader.CharacterAction { public void start(final HTML.Tag tag, final MutableAttributeSet attr) { pushCharacterStyle(); if (attr.isDefined(IMPLIED)) { attr.removeAttribute(IMPLIED); } charAttr.addAttribute(tag, attr.copyAttributes()); if (styleAttributes != null) { charAttr.addAttributes(styleAttributes); } if (charAttr.isDefined(HTML.Tag.SPAN)) { charAttr.removeAttribute(HTML.Tag.SPAN); } //System.out.println("mapping attributes"); charAttr = (MutableAttributeSet) new AttributeMapper(charAttr) .getMappedAttributes(AttributeMapper.toJava); } public void end(final HTML.Tag t) { popCharacterStyle(); } } } /* -------- custom reader implementation end -------- */ public Element getParagraphElement(final int pos) { return getParagraphElement(pos, inSetParagraphAttributes); } /** Gets the current paragraph element, retracing out of p-implied if the parameter * noImplied is true. * @see javax.swing.text.DefaultStyledDocument#getParagraphElement(int) */ public Element getParagraphElement(final int pos, final boolean noPImplied) { Element element = super.getParagraphElement(pos); if (noPImplied) { while (element != null && element.getName().equalsIgnoreCase("p-implied")) { element = element.getParentElement(); } } return element; } public int getLastDocumentPosition() { final int length = getLength(); final int suffixLength = 1; return length > suffixLength ? length - suffixLength : length; } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument#setParagraphAttributes(int, int, javax.swing.text.AttributeSet, boolean) */ public void setParagraphAttributes(final int offset, final int length, final AttributeSet s, final boolean replace) { startCompoundEdit(); super.setParagraphAttributes(offset, length, s, replace); inSetParagraphAttributes = true; super.setParagraphAttributes(offset, length, s, replace); inSetParagraphAttributes = false; endCompoundEdit(); } public void removeParagraphAttributes(final int offset, final int length) { startCompoundEdit(); // clear all paragraph attributes in selection for (int i = offset; i < offset + length;) { final Element paragraphElement = super.getParagraphElement(i); removeParagraphAtributes(paragraphElement); final int endOffset = paragraphElement.getEndOffset(); i = endOffset; } endCompoundEdit(); } private void removeParagraphAtributes(final Element paragraphElement) { if (paragraphElement != null && paragraphElement.getName().equalsIgnoreCase("p-implied")) { removeParagraphAtributes(paragraphElement.getParentElement()); return; } final StringWriter writer = new StringWriter(); final SHTMLWriter htmlStartWriter = new SHTMLWriter(writer, this); try { htmlStartWriter.writeStartTag(paragraphElement.getName(), null); htmlStartWriter.writeChildElements(paragraphElement); htmlStartWriter.writeEndTag(paragraphElement.getName()); setOuterHTML(paragraphElement, writer.toString()); } catch (final IOException e) { e.printStackTrace(); } catch (final BadLocationException e) { e.printStackTrace(); } } private SimpleAttributeSet getEndingAttributeSet() { final SimpleAttributeSet set = new SimpleAttributeSet(); if (Util.preferenceIsTrue("gray_row_below_end")) { StyleConstants.setBackground(set, Color.GRAY); } return set; } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument#getBase() */ public URL getBase() { URL url = super.getBase(); if (false == baseDirChecked) { baseDirChecked = true; final File docDir = new File(url.getFile()); if (!docDir.exists()) { docDir.mkdirs(); } try { url = docDir.toURI().toURL(); super.setBase(url); return url; } catch (final MalformedURLException e) { } } return url; } /* (non-Javadoc) * @see javax.swing.text.html.HTMLDocument#setBase(java.net.URL) */ public void setBase(final URL u) { baseDirChecked = false; super.setBase(u); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/AboutBox.java0100644 0000000 0000000 00000014122 12114157751 022033 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Frame; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.EtchedBorder; /** * A dialog to display information about application SimplyHTML. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class AboutBox extends JDialog implements ActionListener { /** button to close the dialog */ JButton closeButton = new JButton("Close"); /** name of the license file */ private final String LICENSE = "resources/gpl.txt"; /** * construct an AboutBox. * * @param parent the parent frame of the about box */ public AboutBox(final Frame parent) { super(parent); enableEvents(AWTEvent.WINDOW_EVENT_MASK); closeButton.addActionListener(this); closeButton.setText(Util.getResourceString("closeBtnName")); constructFrame(); setTitle(Util.getResourceString("aboutFrameTitle")); pack(); } /** * construct the dialog contents */ private void constructFrame() { /** initialize dialog components */ final Container contentPane = getContentPane(); final JPanel infoPane = new JPanel(); final JPanel imagePane = new JPanel(); final JPanel textPane = new JPanel(); final JPanel buttonPane = new JPanel(); final JPanel northPane = new JPanel(); final JPanel emptyPane = new JPanel(); final LicensePane licPane = new LicensePane(new Dimension(650, 200), LICENSE); final JLabel imageLabel = new JLabel(new ImageIcon(this.getClass().getResource( Util.getResourceString("splashImage")))); final JLabel emptyLabel = new JLabel(""); final JLabel appTitleLabel = new JLabel(FrmMain.APP_NAME); final JLabel appStageLabel = new JLabel(FrmMain.VERSION); final JLabel appCopyrightLabel = new JLabel("Copyright (c) 2002-2008 Ulrich Hilger, Dimitry Polivaev"); final JLabel appHomepageLabel = new JLabel("http://simplyhtml.sf.net/"); /* set the dialog title */ setTitle("About this application"); /* highlight the application name with an appropriate font */ appTitleLabel.setFont(new Font("SansSerif", Font.BOLD, 12)); /* load the application image into a panel */ imagePane.setLayout(new FlowLayout()); imagePane.add(imageLabel); imagePane.setBorder(new EtchedBorder(EtchedBorder.LOWERED)); /** * textPane is the panel where all the application infos are shown. * Infos are shown in a one columns grid of labels, each on one row. */ textPane.setLayout(new GridLayout(6, 1, 5, 5)); textPane.add(emptyLabel); textPane.add(appTitleLabel); textPane.add(appStageLabel); textPane.add(appCopyrightLabel); textPane.add(appHomepageLabel); /** * infoPane shows the application image and the application info text * in a one row, two column grid. */ infoPane.setLayout(new GridLayout(1, 2, 5, 5)); infoPane.add(imagePane); infoPane.add(textPane); /** * northPane is a helper pane to show application image and application * info text left aligned in the upper left corner of the dialog. */ northPane.setLayout(new BorderLayout()); northPane.add(infoPane, BorderLayout.WEST); northPane.add(emptyPane, BorderLayout.CENTER); /* panel for showing the close button at the dialog bottom */ buttonPane.setLayout(new FlowLayout()); buttonPane.add(closeButton); /** * now put together all parts of above application info and combine them * with license information */ contentPane.setLayout(new BorderLayout()); contentPane.add(northPane, BorderLayout.NORTH); contentPane.add(licPane, BorderLayout.CENTER); contentPane.add(buttonPane, BorderLayout.SOUTH); } /** * dispose the dialog properly in case of window close events */ protected void processWindowEvent(final WindowEvent e) { if (e.getID() == WindowEvent.WINDOW_CLOSING) { cancel(); } super.processWindowEvent(e); } /** * dispose the dialog */ private void cancel() { dispose(); } /** * implements the ActionListener interface to be notified of * clicks onto the ok button. Closes and disposes the dialog in this case. */ public void actionPerformed(final ActionEvent e) { if (e.getSource() == closeButton) { cancel(); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/CSSWriter.java0100644 0000000 0000000 00000013665 12114157751 022150 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.io.IOException; import java.io.Writer; import java.util.Enumeration; import javax.swing.text.AttributeSet; import javax.swing.text.Style; import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; import javax.swing.text.html.StyleSheet; /** * A writer for writing a StyleSheet into a CSS file. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * */ class CSSWriter { /** spaces for indent */ private char[] indentChars; /** new line character sequence */ private final String newLine = System.getProperty("line.separator"); /** the writer to write to */ private final Writer writer; /** the style sheet to write */ private final StyleSheet styleSheet; /** indent length */ private int indentLen; /** * construct a new CSSWriter * * @param writer the writer to write to * @param styleSheet the StyleSheet to write */ public CSSWriter(final Writer writer, final StyleSheet styleSheet) { this.writer = writer; this.styleSheet = styleSheet; } /** write the style sheet to the given writer */ public void write() throws IOException { final Enumeration rules = styleSheet.getStyleNames(); while (rules.hasMoreElements()) { writeRule((String) rules.nextElement()); try { Thread.currentThread(); Thread.sleep(0, 1); } catch (final Exception e) { } } } /** * write out a rule with a given name * *

Takes the style with the given name from the style sheet passed in the * constructor and writes it to the writer passed in the constructor.

. * * @param ruleName the name of the rule to write out * * @exception IOException if i/o fails */ private void writeRule(final String ruleName) throws IOException { writeRule(ruleName, styleSheet.getStyle(ruleName)); } /** * write out a rule with a given name and style * *

Takes the style passed in paramter 'rule' and writes it under * the given name to the writer passed in the constructor.

. * @param ruleName the name of the rule to write out * @apram rule the style to write out * * @exception IOException if i/o fails */ public void writeRule(final String ruleName, final AttributeSet rule) throws IOException { //System.out.println("CSSWriter writeRule ruleName=" + ruleName); indentLen = ruleName.length() + 3; if (!ruleName.equalsIgnoreCase(StyleContext.DEFAULT_STYLE)) { writer.write(ruleName); writer.write(" { "); writeStyle(rule); writer.write(newLine); } } /** * write a given style * *

A style is an AttributeSet which can have other * AttributeSets in the value field of one of its Attributes. * Therefore this is recursively called whenever an Attribute * contains another AttributeSet.

* * @param style the Style to write * * @return true, if the style was closed in this run of recursion, * false if not */ private boolean writeStyle(final AttributeSet style) throws IOException { boolean closed = false; final Enumeration names = style.getAttributeNames(); Object value; Object key; int count = 0; while (names.hasMoreElements()) { key = names.nextElement(); value = style.getAttribute(key); if ((!key.equals(StyleConstants.NameAttribute)) && (!key.equals(StyleConstants.ResolveAttribute))) { if (count > 0) { writer.write(newLine); indent(indentLen); } else { count++; } writer.write(key.toString()); writer.write(":"); writer.write(value.toString()); writer.write(";"); } else { if (key.equals(StyleConstants.ResolveAttribute)) { closed = writeStyle((Style) value); } } } if (!closed) { writer.write(" }"); writer.write(newLine); closed = true; } return closed; } /** * indent by a given number of characters * * @param len the number of characters to indent */ private void indent(final int len) throws IOException { if (indentChars == null || len > indentChars.length) { indentChars = new char[len]; for (int i = 0; i < len; i++) { indentChars[i] = ' '; } } writer.write(indentChars, 0, len); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/AnchorDialog.java0100644 0000000 0000000 00000031655 12114157751 022654 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.net.URL; import java.util.Hashtable; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultHighlighter; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.ElementIterator; import javax.swing.text.Highlighter; import javax.swing.text.html.HTML; /** * Dialog to create and edit link anchors. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class AnchorDialog extends DialogShell implements ActionListener, CaretListener, ListSelectionListener, DocumentListener { /** dialog components */ private JList anchorList; private JButton addAnchor; private JButton delAnchor; private SHTMLEditorPane editor; private DocumentPane dp; /** the document this dialog was constructed with */ private Document doc = null; /** the URL this document was loaded from (if loaded from this dialog) */ private URL url = null; /** table for document anchors */ private final Hashtable anchorTable = new Hashtable(); /** indicates whether or not changes to the document need to be saved */ private boolean needsSaving = true; /** the help id for this dialog */ private static final String helpTopicId = "item165"; //private int renderMode; /** * create an AnchorDialog * * @param parent the parent dialog of this dialog * @param title the dialog title * @param doc the document to edit anchors of */ public AnchorDialog(final Dialog parent, final String title, final Document doc) { super(parent, title, helpTopicId); initDialog(doc, null/*, renderMode*/); } /** * create an AnchorDialog * * @param parent the parent frame of this dialog * @param title the dialog title * @param doc the document to edit anchors of */ public AnchorDialog(final Frame parent, final String title, final Document doc) { super(parent, title, helpTopicId); initDialog(doc, null/*, renderMode*/); } /** * create an AnchorDialog * * @param parent the parent frame of this dialog * @param title the dialog title * @param url the document url */ public AnchorDialog(final Dialog parent, final String title, final URL url) { super(parent, title, helpTopicId); initDialog(null, url/*, renderMode*/); } /** * create an AnchorDialog * * @param parent the parent frame of this dialog * @param title the dialog title * @param url the document url */ public AnchorDialog(final Frame parent, final String title, final URL url) { super(parent, title, helpTopicId); initDialog(null, url/*, renderMode*/); } /** * initialize this AnchorDialog * *

If a document is passed, anchors of this document * are edited. If doc is null and a url is passed, this * document is loaded (and saved).

* * @param doc the document to edit anchors of, or null * @param url the url to load a document from, or null */ private void initDialog(final Document doc, final URL url) { this.url = url; //this.renderMode = renderMode; // create anchor panel final JPanel anchorPanel = new JPanel(new BorderLayout()); anchorPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("anchorPanelLabel"))); //getAnchors(doc); anchorList = new JList(/*anchorTable.keySet().toArray()*/); anchorPanel.add(new JScrollPane(anchorList), BorderLayout.CENTER); anchorList.addListSelectionListener(this); addAnchor = new JButton(Util.getResourceString("addImgBtnTitle")); addAnchor.addActionListener(this); delAnchor = new JButton(Util.getResourceString("delImgBtnTitle")); delAnchor.addActionListener(this); // use a help panel to add add/del buttons JPanel helpPanel = new JPanel(); helpPanel.add(addAnchor); helpPanel.add(delAnchor); anchorPanel.add(helpPanel, BorderLayout.SOUTH); // init DocumentPane if (doc != null) { needsSaving = false; dp = new DocumentPane(/*renderMode*/); doc.addDocumentListener(this); dp.setDocument(doc); this.doc = doc; } else { needsSaving = true; dp = new DocumentPane(url, 1/*, renderMode*/); this.doc = dp.getDocument(); } // init editor to our needs editor = dp.getEditor(); editor.setEditable(false); editor.addCaretListener(this); // create document panel final JPanel docPanel = new JPanel(new BorderLayout()); docPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("docPanelLabel"))); docPanel.add(editor, BorderLayout.CENTER); // use a help panel to properly align anchorPanel and docPanel helpPanel = new JPanel(new BorderLayout()); helpPanel.add(anchorPanel, BorderLayout.WEST); helpPanel.add(docPanel, BorderLayout.CENTER); // get content pane of DialogShell to add components to final Container contentPane = super.getContentPane(); ((JComponent) contentPane).setPreferredSize(new Dimension(600, 500)); contentPane.add(helpPanel, BorderLayout.CENTER); // add help button // cause optimal placement of all elements pack(); updateAnchorList(); addAnchor.setEnabled(false); delAnchor.setEnabled(false); } /** * overridden to addd some custom cleanup upon closing of dialog */ public void dispose() { editor.removeCaretListener(this); doc.removeDocumentListener(this); super.dispose(); } /** * re-display the list of anchors of the document */ private void updateAnchorList() { getAnchors(doc); anchorList.setListData(anchorTable.keySet().toArray()); } /** * get the anchors of a given document * * @param doc the document to get anchors from */ private void getAnchors(final Document doc) { HTML.Tag.A.toString(); Object nameAttr; Object link; anchorTable.clear(); final ElementIterator eli = new ElementIterator(doc); Element elem = eli.first(); while (elem != null) { link = elem.getAttributes().getAttribute(HTML.Tag.A); if (link != null) { nameAttr = ((AttributeSet) link).getAttribute(HTML.Attribute.NAME); if (nameAttr != null) { //model.addElement(nameAttr); anchorTable.put(nameAttr, elem); } } elem = eli.next(); } } /** * get an anchor name and add it at the current editor location */ private void doAddAnchor() { final String anchorName = Util.nameInput(null, "", ".*", "addAnchorTitle", "addAnchorText"); if (anchorName != null) { editor.insertAnchor(anchorName); saveChanges(); updateAnchorList(); } } /** * save changes to the document */ private void saveChanges() { if (needsSaving) { if (url != null) { try { dp.saveDocument(/*renderMode*/); } catch (final Exception e) { Util.errMsg(this, e.getMessage(), e); } } } } /** * Gets the anchor currently selected in the list of anchors. * * @return the anchor name, or null if none is selected */ public String getAnchor() { String anchorName = null; if (anchorList.getSelectedIndex() > -1) { anchorName = anchorList.getSelectedValue().toString(); } return anchorName; } /** * remove an anchor from the document */ private void doDelAnchor() { final String anchorName = anchorList.getSelectedValue().toString(); dp.getEditor().removeAnchor(anchorName); saveChanges(); updateAnchorList(); } /** * ActionListener implementatin for proper handling of * buttons */ public void actionPerformed(final ActionEvent e) { final Object src = e.getSource(); if (src.equals(addAnchor)) { doAddAnchor(); } else if (src.equals(delAnchor)) { doDelAnchor(); } else { super.actionPerformed(e); } } /** * ListSelectionListener implementation to properly react to * changes in the list of anchors */ public void valueChanged(final ListSelectionEvent e) { final Object src = e.getSource(); if (src.equals(anchorList)) { //if(!ignoreAnchorListChanges) { final Highlighter h = editor.getHighlighter(); final Highlighter.HighlightPainter p = new DefaultHighlighter.DefaultHighlightPainter(Color.yellow); final Object o = anchorList.getSelectedValue(); if (o != null) { final Element elem = (Element) anchorTable.get(anchorList.getSelectedValue()); final int start = elem.getStartOffset(); int end = elem.getEndOffset(); try { if (end == start) { end = start + 3; } editor.select(start, end); h.removeAllHighlights(); h.addHighlight(start, end, p); } catch (final BadLocationException ble) { ble.printStackTrace(); } } //} } } /** * CaretListener implementation to adjust 'add anchor' button * according to whether or not a selection is present in the document * to possibly add an anchor to */ public void caretUpdate(final CaretEvent e) { final Object src = e.getSource(); if (src.equals(editor)) { addAnchor.setEnabled(editor.getSelectionStart() != editor.getSelectionEnd()); delAnchor.setEnabled(anchorList.getSelectedIndex() > -1); } } /* -------- DocumentListener implementation start ------------*/ /** * listens to inserts into the document to track whether or not the document * needs to be saved. */ public void insertUpdate(final DocumentEvent e) { updateAnchorList(); } /** * listens to removes into the document to track whether or not the document * needs to be saved. */ public void removeUpdate(final DocumentEvent e) { updateAnchorList(); } /** * listens to changes on the document to track whether or not the document * needs to be saved. */ public void changedUpdate(final DocumentEvent e) { updateAnchorList(); } /* -------- DocumentListener implementation end ------------*/ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/BoundariesPanel.java0100644 0000000 0000000 00000013576 12114157751 023377 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.Vector; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.CSS; /** * Panel to show and manipulate boundaries of a rectangular object * such as a table cell. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class BoundariesPanel extends JPanel implements AttributeComponent { /** the components used for single attributes */ private final Vector components = new Vector(); /** the attributes represented by this compoent */ private CombinedAttribute ca; /** the value to compare to determine changes */ private String originalValue; /** indicates if a call to setValue is for initial setting or for changes */ private int setValueCalls = 0; /** the attribute key this component represents */ private final Object key; /** * construct a BoundariesPanel. */ public BoundariesPanel(final Object attrKey) { super(); key = attrKey; // set layout final GridBagLayout g = new GridBagLayout(); setLayout(g); // constraints to use on our GridBagLayout final GridBagConstraints c = new GridBagConstraints(); Util.addGridBagComponent(this, new JLabel(Util.getResourceString("topLabel")), g, c, 0, 0, GridBagConstraints.EAST); Util.addGridBagComponent(this, new JLabel(Util.getResourceString("rightLabel")), g, c, 2, 0, GridBagConstraints.EAST); Util.addGridBagComponent(this, new JLabel(Util.getResourceString("bottomLabel")), g, c, 0, 1, GridBagConstraints.EAST); Util.addGridBagComponent(this, new JLabel(Util.getResourceString("leftLabel")), g, c, 2, 1, GridBagConstraints.EAST); addSizeSelector(g, c, attrKey, 1, 0); // top addSizeSelector(g, c, attrKey, 3, 0); // right addSizeSelector(g, c, attrKey, 1, 1); // bottom addSizeSelector(g, c, attrKey, 3, 1); // left } private void addSizeSelector(final GridBagLayout g, final GridBagConstraints c, final Object attr, final int x, final int y) { final SizeSelectorPanel ssp = new SizeSelectorPanel(attr, null, false, SizeSelectorPanel.TYPE_LABEL); Util.addGridBagComponent(this, ssp, g, c, x, y, GridBagConstraints.WEST); components.addElement(ssp); } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { final boolean success = true; ca = new CombinedAttribute(key, a, true); if (++setValueCalls < 2) { originalValue = ca.getAttribute(); } SizeSelectorPanel ssp; for (int i = 0; i < components.size(); i++) { ssp = (SizeSelectorPanel) components.elementAt(i); ssp.setValue(ca.getAttribute(i)); } return success; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet set = new SimpleAttributeSet(); SizeSelectorPanel ssp; for (int i = 0; i < components.size(); i++) { ssp = (SizeSelectorPanel) components.elementAt(i); if (ssp.valueChanged()) { ca.setAttribute(i, ssp.getAttr()); } } final String newValue = ca.getAttribute(); if (!originalValue.equalsIgnoreCase(newValue)) { set.addAttribute(key, newValue); Util.styleSheet().addCSSAttribute(set, (CSS.Attribute) key, newValue); } return set; } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { final SimpleAttributeSet set = new SimpleAttributeSet(); SizeSelectorPanel ssp; for (int i = 0; i < components.size(); i++) { ssp = (SizeSelectorPanel) components.elementAt(i); ca.setAttribute(i, ssp.getAttr()); } final String newValue = ca.getAttribute(); set.addAttribute(key, newValue); Util.styleSheet().addCSSAttribute(set, (CSS.Attribute) key, newValue); return set; } else { return getValue(); } } public void reset() { setValueCalls = 0; originalValue = null; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/CombinedAttribute.java0100644 0000000 0000000 00000030353 12114157751 023720 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.util.Enumeration; import java.util.Vector; import javax.swing.text.AttributeSet; import javax.swing.text.html.CSS; /** * A class to represent an attribute combining several other attributes. * *

The CSS 1.0 specification * defines 'shorthand properties' which can hold more than * one value separated by blanks. Depending on the number of values inside * the property the values are applied following a fixed ratio.

* *

Following is an excerpt of the spec for CSS property * border-width

*
 * There can be from one to four values, with the following interpretation:
 *     one value: all four border widths are set to that value
 *     two values: top and bottom border widths are set to the
 *                    first value, right and left are set to the second
 *     three values: top is set to the first, right and left are set to
 *                    the second, bottom is set to the third
 *     four values: top, right, bottom and left, respectively
 * 
* *

In SimplyHTML this spec is used on properties margin, * padding, border-width and border-color

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class CombinedAttribute { /** index of top value */ public static final int ATTR_TOP = 0; /** index of right value */ public static final int ATTR_RIGHT = 1; /** index of bottom value */ public static final int ATTR_BOTTOM = 2; /** index of left value */ public static final int ATTR_LEFT = 3; /** the values of this CombinedAttribute */ private final String[] values = new String[4]; /** * the attribute key the values of this * CombinedAttribute belong to */ private final Object attributeKey; /** indicates which sides were present in the attribute set */ private final boolean[] present = new boolean[4]; /** table with attribute names from the source attribute set */ private final Vector aNames = new Vector(); /** indicates if attributes of parent elements shall be used */ private final boolean includeParents; /** * construct a CombinedAttribute for a certain * attribute out of a given set of attributes * * @param key the attribute key to get single attribute values from * @param a the set of attributes to get the attribute of type 'key' */ public CombinedAttribute(final Object key, final AttributeSet a, final boolean includeParents) { attributeKey = key; this.includeParents = includeParents; // get names in this attribute set to filter out parent attributes later final Enumeration names = a.getAttributeNames(); while (names.hasMoreElements()) { aNames.addElement(names.nextElement()); } // now load attributes into this object final Object attr = a.getAttribute(key); if (attr != null) { //System.out.println(" construct CombinedAttribute attr=" + attr); copyValues(Util.tokenize(attr.toString(), " ")); } else { copyValues(key, a); } } /** * copy the values for individual border settings from a given * set of attributes into the structure top, right, bottom, left of * this CombinedAttribute * *

Used in cases where attributes are not found for a 'shorthand * property' such as PADDING or MARGIN.

* * @param key the 'shorthand property' to copy individual attributes for * @param a the set of attributes to copy from */ private void copyValues(final Object key, final AttributeSet a) { if (key.equals(CSS.Attribute.BORDER_WIDTH)) { setValue(ATTR_TOP, CSS.Attribute.BORDER_TOP_WIDTH, a); setValue(ATTR_RIGHT, CSS.Attribute.BORDER_RIGHT_WIDTH, a); setValue(ATTR_BOTTOM, CSS.Attribute.BORDER_BOTTOM_WIDTH, a); setValue(ATTR_LEFT, CSS.Attribute.BORDER_LEFT_WIDTH, a); } else if (key.equals(CSS.Attribute.PADDING)) { setValue(ATTR_TOP, CSS.Attribute.PADDING_TOP, a); setValue(ATTR_RIGHT, CSS.Attribute.PADDING_RIGHT, a); setValue(ATTR_BOTTOM, CSS.Attribute.PADDING_BOTTOM, a); setValue(ATTR_LEFT, CSS.Attribute.PADDING_LEFT, a); } else if (key.equals(CSS.Attribute.MARGIN)) { setValue(ATTR_TOP, CSS.Attribute.MARGIN_TOP, a); setValue(ATTR_RIGHT, CSS.Attribute.MARGIN_RIGHT, a); setValue(ATTR_BOTTOM, CSS.Attribute.MARGIN_BOTTOM, a); setValue(ATTR_LEFT, CSS.Attribute.MARGIN_LEFT, a); } } /** * set the value of a certain side from a given attribute key and * set of attributes. * * @param side the side to set the value for, one of ATTR_TOP, * ATTR_RIGHT, ATTR_BOTTOM and ATTR_LEFT * @param key the attribute key to get the value from * @param a the set of attributes to get the value from */ private void setValue(final int side, final Object key, final AttributeSet a) { if ((includeParents) || ((!includeParents) && (aNames.contains(key)))) { // filter out parent attributes final Object attr = a.getAttribute(key); if (attr != null) { values[side] = attr.toString(); present[side] = true; } else { values[side] = defaultValue(attributeKey); present[side] = true; } } else { // key not present, set default value values[side] = defaultValue(attributeKey); present[side] = false; } } /** * determine whether or not the set of attributes this * CombinedAttribute was created from contained any * of the attributes in this CombinedAttribute * *

Can be used for instance to determine whether or not this * CombinedAttribute should be written

* * @return true, if this CombinedAttribute contains * default values only, false if not */ public boolean isEmpty() { boolean notEmpty = false; int i = 0; while (!notEmpty && i < present.length) { notEmpty = present[i]; i++; } return !notEmpty; } /** * get the default value for a given key * * @param key the attribute key to get the default value for * * @return the default value for the given key */ private String defaultValue(final Object key) { String value = "0"; if (key.equals(CSS.Attribute.BORDER_COLOR)) { value = "#000000"; } return value; } /** * get the side opposite of a given side * * @param side the side to get the opposite of * * @return the opposite side of the given side */ public int getOppositeSide(final int side) { int oppositeSide = -1; switch (side) { case ATTR_TOP: oppositeSide = ATTR_BOTTOM; break; case ATTR_RIGHT: oppositeSide = ATTR_LEFT; break; case ATTR_BOTTOM: oppositeSide = ATTR_TOP; break; case ATTR_LEFT: oppositeSide = ATTR_RIGHT; break; } return oppositeSide; } /** * copy the atribute value(s) found in a 'shorthand property' such * as PADDING or MARGIN into the structure top, right, bottom, left of * this CombinedAttribute * * @param v the array of Strings holding the found values */ private void copyValues(final String[] v) { switch (v.length) { case 1: for (int i = 0; i < values.length; i++) { values[i] = v[0]; } break; case 2: values[ATTR_TOP] = v[ATTR_TOP]; values[ATTR_RIGHT] = v[ATTR_RIGHT]; values[ATTR_BOTTOM] = v[ATTR_TOP]; values[ATTR_LEFT] = v[ATTR_RIGHT]; break; case 3: values[ATTR_TOP] = v[ATTR_TOP]; values[ATTR_RIGHT] = v[ATTR_RIGHT]; values[ATTR_BOTTOM] = v[ATTR_BOTTOM]; values[ATTR_LEFT] = v[ATTR_RIGHT]; break; case 4: for (int i = 0; i < values.length; i++) { values[i] = v[i]; } break; } } /** * set one attribute of this CombinedAttribute * * @param side the side to set the attribute for, one of ATTR_TOP, * ATTR_RIGHT, ATTR_BOTTOM, ATTR_LEFT * @param value the attribute value to set */ public void setAttribute(final int side, final String value) { values[side] = value; } /** * get one attribute of this CombinedAttribute * * @param side the side to get the attribute for, one of ATTR_TOP, * ATTR_RIGHT, ATTR_BOTTOM, ATTR_LEFT * * @return the attribute value for the specified side or null, if the * attribute key provided in the constructor was not found */ public String getAttribute(final int side) { return values[side]; } /** * get the attribute key this CombinedAttribute represents * * @return the attribute key */ public Object getAttributeKey() { return attributeKey; } /** * get all values of this CombinedAttribute * as one attribute. * * @return a String having all values delimited by blanks * in the order top right, bottom, left or null if no * attributes were found */ public String getAttribute() { String result = null; final StringBuffer buf = new StringBuffer(); if (values[0] != null) { buf.append(values[0]); int additionalValueCount = 3; if (values[ATTR_RIGHT].equalsIgnoreCase(values[ATTR_LEFT])) { --additionalValueCount; // total 3 if (values[ATTR_TOP].equalsIgnoreCase(values[ATTR_BOTTOM])) { --additionalValueCount; // total 2 if (values[ATTR_TOP].equalsIgnoreCase(values[ATTR_RIGHT])) { --additionalValueCount; // total 1 } } } appendValues(buf, additionalValueCount); result = buf.toString(); } return result; } /** * append a given number of values to a given output buffer * starting with ATTR_RIGHT and necessarily continuing * with ATTR_BOTTOM and ATTR_LEFT ( helper method to getAttribute() ) * * @param buf the output buffer to append to * @param count the number of values to append */ private void appendValues(final StringBuffer buf, final int count) { for (int i = 1; i < count + 1; i++) { buf.append(' '); buf.append(values[i]); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SplashScreen.java0100644 0000000 0000000 00000006020 12114157751 022700 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * Copyright (C) 2006 Karsten Pawlik * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Toolkit; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JWindow; /** * Class that displays a splash screen * Is run in a separate thread so that the applet continues to load in the background * @author Karsten Pawlik * */ public class SplashScreen extends JWindow { private static SplashScreen instance = null; private static int counter; private SplashScreen() { try { final JPanel panel = new JPanel(new BorderLayout()); final ImageIcon icon = new ImageIcon(SplashScreen.class.getResource(Util.getResourceString("splashImage"))); panel.add(new JLabel(icon), BorderLayout.CENTER); panel.setBorder(BorderFactory.createLineBorder(Color.BLACK)); getContentPane().add(panel); getRootPane().setOpaque(true); pack(); final Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((int) (d.getWidth() - getWidth()) / 2, (int) (d.getHeight() - getHeight()) / 2); } catch (final Exception e) { } } /** * Hides the splash screen. */ synchronized public static void hideInstance() { if (!Util.getPreference("show_splash_screen", "true").equalsIgnoreCase("true")) { return; } if (counter > 0) { counter--; } if (counter == 0) { instance.setVisible(false); } } /** * Shows the splash screen. */ synchronized public static void showInstance() { if (!Util.getPreference("show_splash_screen", "true").equalsIgnoreCase("true")) { return; } if (instance == null) { instance = new SplashScreen(); counter = 0; } if (counter == 0) { instance.setVisible(true); instance.getRootPane().paintImmediately(0, 0, instance.getWidth(), instance.getHeight()); } counter++; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/AttributeComponent.java0100644 0000000 0000000 00000003676 12114157751 024152 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import javax.swing.text.AttributeSet; /** * Defines a set of methods common to components bound to AttributeSets. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ interface AttributeComponent { /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(AttributeSet a); /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue(); public AttributeSet getValue(boolean includeUnchanged); } simplyhtml-0.17.3/src/com/lightdev/app/shtm/PluginManagerDialog.java0100644 0000000 0000000 00000021123 12114157751 024160 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; /** * User interface for changing plug-in settings. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class PluginManagerDialog extends DialogShell implements ListSelectionListener, ActionListener { /** combo box for selecting the dock location */ private final JComboBox dockLocation; /** indicates if we can ignore changes (when happenig programmatically */ private boolean ignoreChanges = false; /** the list with available plug-ins */ private final JList pluginNames; /** constant for activation button label */ private final String activateName = Util.getResourceString("activatePlugin"); /** constant for deactivation button label */ private final String deactivateName = Util.getResourceString("deactivatePlugin"); /** button to toggle plug-in activation state */ private final JButton toggleActivationButton; /** checkbox to toggle plug-in activation state */ private final JCheckBox toggleActivationCheckbox; /** * construct a new PluginManagerDialog * * @param parent the parent frame * @param title the title of the dialog */ public PluginManagerDialog(final Frame parent, final String title) { super(parent, title); final Container contentPane = super.getContentPane(); okButton.setText(Util.getResourceString("close")); cancelButton.setVisible(false); GridBagLayout g; final GridBagConstraints c = new GridBagConstraints(); /** create panel to show and select plug-ins */ final JPanel pluginPanel = new JPanel(new BorderLayout()); pluginPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("pluginPanelTitle"))); //pluginPanel.setMinimumSize(new Dimension(400, 400)); SHTMLPanelImpl.pluginManager.plugins(); pluginNames = new JList(SHTMLPanelImpl.pluginManager.getPluginNames()); pluginNames.addListSelectionListener(this); pluginNames.setMinimumSize(new Dimension(250, 400)); pluginNames.setPreferredSize(new Dimension(250, 400)); pluginPanel.add(new JScrollPane(pluginNames), BorderLayout.CENTER); /** create panel for actins on loaded plug-ins */ final JPanel actionPanel = new JPanel(); toggleActivationButton = new JButton(activateName); toggleActivationButton.setEnabled(false); toggleActivationButton.addActionListener(this); actionPanel.add(toggleActivationButton); pluginPanel.add(actionPanel, BorderLayout.SOUTH); /** create panel to edit settings for a plug-in */ g = new GridBagLayout(); final JPanel pluginSettingsPanel = new JPanel(g); toggleActivationCheckbox = new JCheckBox("togglePluginActivationCheckbox"); toggleActivationCheckbox.setEnabled(false); toggleActivationCheckbox.addActionListener(this); Util.addGridBagComponent(pluginSettingsPanel, toggleActivationCheckbox, g, c, 0, 0, GridBagConstraints.WEST); Util.addGridBagComponent(pluginSettingsPanel, new JLabel(Util.getResourceString("dockLocationLabel")), g, c, 0, 1, GridBagConstraints.EAST); final String[] locations = { Util.getResourceString("pluginDockLocationNone"), Util.getResourceString("pluginDockLocationTop"), Util.getResourceString("pluginDockLocationRight"), Util.getResourceString("pluginDockLocationBottom"), Util.getResourceString("pluginDockLocationLeft"), }; dockLocation = new JComboBox(locations); dockLocation.setEnabled(false); dockLocation.addActionListener(this); Util.addGridBagComponent(pluginSettingsPanel, dockLocation, g, c, 1, 1, GridBagConstraints.WEST); /** add components to dialog */ contentPane.add(pluginPanel, BorderLayout.WEST); final JPanel centerPanel = new JPanel(new BorderLayout()); final JPanel centerWestPanel = new JPanel(new BorderLayout()); centerWestPanel.add(pluginSettingsPanel, BorderLayout.NORTH); centerPanel.add(centerWestPanel, BorderLayout.WEST); centerPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("pluginSettingsPanelTitle"))); //centerPanel.setPreferredSize(new Dimension(200,400)); contentPane.add(centerPanel, BorderLayout.CENTER); pack(); } /** * ListSelectionListener implementation */ public void valueChanged(final ListSelectionEvent e) { ignoreChanges = true; if (pluginNames.getSelectedIndex() > -1) { final SHTMLPlugin p = getSelectedPlugin(); final boolean active = p.isActive(); updateActivationButtonText(active); toggleActivationButton.setEnabled(true); toggleActivationCheckbox.setEnabled(true); toggleActivationCheckbox.setSelected(active); dockLocation.setSelectedIndex(p.getDockLocation()); dockLocation.setEnabled(true); } else { toggleActivationButton.setEnabled(false); toggleActivationCheckbox.setEnabled(false); dockLocation.setEnabled(false); } ignoreChanges = false; } /** * helper method for getting the currently selected * line in the list of plug-ins */ private SHTMLPlugin getSelectedPlugin() { final String name = (String) pluginNames.getSelectedValue(); return SHTMLPanelImpl.pluginManager.pluginForName(name); } /** * helper method to toggle the button text between * activate and deactivate */ private void updateActivationButtonText(final boolean active) { if (active) { toggleActivationButton.setText(deactivateName); } else { toggleActivationButton.setText(activateName); } } /** * ActionListener implementation */ public void actionPerformed(final ActionEvent e) { final Object source = e.getSource(); if ((pluginNames.getSelectedIndex() > -1) && (!ignoreChanges)) { ignoreChanges = true; final SHTMLPlugin p = getSelectedPlugin(); if (source.equals(toggleActivationButton)) { p.setStatus(!p.isActive()); } else if (source.equals(toggleActivationCheckbox)) { p.setStatus(!p.isActive()); } else if (source.equals(dockLocation)) { p.setDockLocation(dockLocation.getSelectedIndex()); } else { super.actionPerformed(e); } final boolean active = p.isActive(); toggleActivationCheckbox.setSelected(active); updateActivationButtonText(active); ignoreChanges = false; } else { super.actionPerformed(e); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/plugin/0040755 0000000 0000000 00000000000 12114157751 020746 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/plugin/package.html0100644 0000000 0000000 00000001002 12114157751 023215 0ustar000000000 0000000 Components building a plug-in architecture for application SimplyHTML, a word processor based on Java, HTML and CSS @author Ulrich Hilger, Dimitry Polivaev @author http://simplyhtml.sourceforge.net/ @author published under the terms and conditions of the GNU General Public License, for details see file gpl.txt in the distribution package of this software @version 0.12.2, 2007 simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLPanelImpl.java0100644 0000000 0000000 00000161344 13147564753 023025 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2006 Ulrich Hilger, Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.*; import java.awt.event.*; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.net.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.text.*; import javax.swing.text.html.*; import javax.swing.undo.*; import java.util.*; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import com.lightdev.app.shtm.SHTMLEditorKitActions.ChangeFontSizeAction.Change; import com.lightdev.app.shtm.SHTMLEditorKitActions.SetStyleAction; import com.lightdev.app.shtm.SHTMLEditorKitActions.SetTagAction; import java.util.prefs.*; /** * Main component of application SimplyHTML. * *

This class constructs the main panel and all of its GUI elements * such as menus, etc.

* *

It defines a set of inner classes creating actions which can be * connected to menus, buttons or instantiated individually.

* * @author Ulrich Hilger * @author Dimitri Polivaev * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ public class SHTMLPanelImpl extends SHTMLPanel implements CaretListener { //private int renderMode = SHTMLEditorKit.RENDER_MODE_JAVA; /* some public constants */ public static final String APP_TEMP_DIR = "temp"; public static final String IMAGE_DIR = "images"; public static final String ACTION_SELECTED_KEY = "selected"; public static final String ACTION_SELECTED = "true"; public static final String ACTION_UNSELECTED = "false"; public static final String FILE_LAST_OPEN = "lastOpenFileName"; public static final String FILE_LAST_SAVE = "lastSaveFileName"; /** single instance of a dynamic resource for use by all */ public DynamicResource dynRes = new DynamicResource(); /** SimplyHTML's main resource bundle (plug-ins use their own) */ private static UIResources uiResources = null; public static UIResources getUiResources() { if (uiResources == null) { setInternalUiResources(); } return uiResources; } /** the plug-in manager of SimplyHTML */ public static PluginManager pluginManager; // = new PluginManager(mainFrame); protected ActionListener openHyperlinkHandler = null; public static void setUiResources(final UIResources uiResources) { SHTMLPanelImpl.uiResources = uiResources; } public static void setInternalUiResources() { SHTMLPanelImpl.uiResources = SHTMLPanelImpl.readDefaultResources(); } private static UIResources readDefaultResources() { try { final String propsLoc = "/com/lightdev/app/shtm/resources/SimplyHTML_common.properties"; final URL defaultPropsURL = SHTMLPanelImpl.class.getResource(propsLoc); final Properties props = new Properties(); InputStream in = null; in = defaultPropsURL.openStream(); props.load(in); in.close(); final ResourceBundle resourceBundle = ResourceBundle.getBundle( "com.lightdev.app.shtm.resources.SimplyHTML", Locale.getDefault()); return new InternalUiResources(resourceBundle, props); } catch (final Exception ex) { Util.errMsg(null, "resources not found", ex); return null; } } private final SHTMLMenuBar menuBar; /** currently active DocumentPane */ private DocumentPane documentPane; /** currently active SHTMLEditorPane */ private SHTMLEditorPane editorPane; /** currently active SHTMLDocument */ protected SHTMLDocument doc; /** tool bar for formatting commands */ private JToolBar formatToolBar; /** tool bar for formatting commands */ private JToolBar paraToolBar; /** plugin menu ID */ public final String pluginMenuId = "plugin"; /** help menu ID */ public final String helpMenuId = "help"; /** id in TextResources for a relative path to an empty menu icon */ private final String emptyIcon = "emptyIcon"; /** watch for repeated key events */ private final RepeatKeyWatcher rkw = new RepeatKeyWatcher(40); /** counter for newly created documents */ int newDocCounter = 0; /** reference to applicatin temp directory */ private static File appTempDir; private static ActionBuilder actionBuilder; /** tool bar selector for certain tags */ private TagSelector tagSelector; /** panel for plug-in display */ SplitPanel splitPanel; /** indicates, whether document activation shall be handled */ boolean ignoreActivateDoc = false; private final JPopupMenu editorPopup; /** * action names * * these have to correspond with the keys in the * resource bundle to allow for dynamic * menu creation and control */ public static final String exitAction = "exit"; public static final String undoAction = "undo"; public static final String redoAction = "redo"; public static final String cutAction = "cut"; public static final String copyAction = "copy"; public static final String pasteAction = "paste"; public static final String pasteOtherAction ="pasteOther"; public static final String selectAllAction = "selectAll"; public static final String clearFormatAction = "clearFormat"; public static final String fontAction = "font"; public static final String fontFamilyAction = "fontFamily"; public static final String fontSizeAction = "fontSize"; public static final String increaseFontSizeAction = "increaseFontSize"; public static final String decreaseFontSizeAction = "decreaseFontSize"; public static final String fontBoldAction = "fontBold"; public static final String fontStrikethroughAction = "fontStrikethrough"; public static final String fontItalicAction = "fontItalic"; public static final String fontUnderlineAction = "fontUnderline"; public static final String fontColorAction = "fontColor"; public static final String removeFontColorAction = "removeFontColor"; public static final String selectedFontColorAction = "selectedFontColor"; public static final String redFontColorAction = "redFontColor"; public static final String blueFontColorAction = "blueFontColor"; public static final String blackFontColorAction = "blackFontColor"; public static final String greenFontColorAction = "greenFontColor"; public static final String helpTopicsAction = "helpTopics"; public static final String aboutAction = "about"; public static final String gcAction = "gc"; public static final String elemTreeAction = "elemTree"; public static final String testAction = "test"; public static final String insertTableAction = "insertTable"; public static final String formatTableAction = "formatTable"; public static final String toggleTableHeaderCellAction = "toggleTableHeaderCell"; public static final String insertTableColAction = "insertTableCol"; public static final String insertTableRowAction = "insertTableRow"; public static final String insertTableRowHeaderAction = "insertTableRowHeader"; public static final String appendTableRowAction = "appendTableRow"; public static final String appendTableColAction = "appendTableCol"; public static final String deleteTableRowAction = "deleteTableRow"; public static final String deleteTableColAction = "deleteTableCol"; public static final String nextTableCellAction = "nextTableCell"; public static final String prevTableCellAction = "prevTableCell"; public static final String moveTableRowUpAction = "moveTableRowUp"; public static final String moveTableColumnLeftAction = "moveTableColumnLeft"; public static final String moveTableColumnRightAction = "moveTableColumnRight"; public static final String moveTableRowDownAction = "moveTableRowDown"; //public static final String nextCellAction = "nextCell"; //public static final String prevCellAction = "prevCell"; public static final String toggleBulletsAction = "toggleBullets"; public static final String toggleNumbersAction = "toggleNumbers"; public static final String formatListAction = "formatList"; public static final String editPrefsAction = "editPrefs"; public static final String insertImageAction = "insertImage"; public static final String formatImageAction = "formatImage"; public static final String formatParaAction = "formatPara"; public static final String editNamedStyleAction = "editNamedStyle"; public static final String paraAlignLeftAction = "paraAlignLeft"; public static final String paraAlignCenterAction = "paraAlignCenter"; public static final String paraAlignRightAction = "paraAlignRight"; public static final String insertLinkAction = "insertLink"; public static final String editLinkAction = "editLink"; public static final String openLinkAction = "openLink"; public static final String setTagAction = "setTag"; public static final String editAnchorsAction = "editAnchors"; public static final String saveAllAction = "saveAll"; public static final String documentTitleAction = "documentTitle"; public static final String setDefaultStyleRefAction = "setDefaultStyleRef"; public static final String findReplaceAction = "findReplace"; public static final String setStyleAction = "setStyle"; public static final String formatAsCodeAction = "formatAsCode"; public static final String printAction = "print"; public static SHTMLPanelImpl getOwnerSHTMLPanel(Component c) { for (;;) { if (c == null) { return null; } if (c instanceof SHTMLPanelImpl) { return (SHTMLPanelImpl) c; } c = c.getParent(); } } /** construct a new main application frame */ SHTMLPanelImpl() { super(new BorderLayout()); SplashScreen.showInstance(); enableEvents(AWTEvent.WINDOW_EVENT_MASK); initActions(); if(actionBuilder != null) actionBuilder.initActions(this); menuBar = dynRes.createMenubar(uiResources, "menubar"); editorPopup = dynRes.createPopupMenu(uiResources, "popup"); setJMenuBar(menuBar); customizeFrame(); initAppTempDir(); initPlugins(); initDocumentPane(); updateActions(); initJavaHelp(); SplashScreen.hideInstance(); } private void setJMenuBar(final JMenuBar bar) { add(bar, BorderLayout.NORTH); } /* (non-Javadoc) * @see javax.swing.JComponent#processKeyBinding(javax.swing.KeyStroke, java.awt.event.KeyEvent, int, boolean) */ protected boolean processKeyBinding(final KeyStroke ks, final KeyEvent e, final int condition, final boolean pressed) { if (super.processKeyBinding(ks, e, condition, pressed)) { return true; } return menuBar.handleKeyBinding(ks, e, condition, pressed); } public JMenuItem createActionMenuItem(final String actionName) { return dynRes.createMenuItem(SHTMLPanelImpl.getUiResources(), actionName); } public Action getAction(final String actionName) { return dynRes.getAction(actionName); } /** * get the DynamicResource used in this instance of FrmMain * * @return the DynamicResource */ DynamicResource getDynRes() { return dynRes; } /** * get the temporary directory of SimplyHTML * * @return the temp dir */ static File getAppTempDir() { return appTempDir; } /** * get the file object for the document shown in the currently open DocumentPane * * @return the document file */ File getCurrentFile() { File file = null; final URL url = getDocumentPane().getSource(); if (url != null) { file = new File(url.getFile()); } return file; } /** * get the name of the file for the document shown in the currently open DocumentPane * * @return the document name */ String getCurrentDocName() { return getDocumentPane().getDocumentName(); } /** * Convenience method for obtaining the document text * @return returns the document text as string. */ public String getDocumentText() { return getDocumentPane().getDocumentText(); } Document getCurrentDocument() { return getDocumentPane().getDocument(); } /** * indicates whether or not the document needs to be saved. * * @return true, if changes need to be saved */ public boolean needsSaving() { return getDocumentPane().needsSaving(); } /** * Convenience method for clearing out the UndoManager */ void purgeUndos() { if (undo != null) { undo.discardAllEdits(); dynRes.getAction(undoAction).putValue("enabled", Boolean.FALSE); dynRes.getAction(redoAction).putValue("enabled", Boolean.FALSE); updateFormatControls(); } } /** * Convenience method for setting the document text */ public void setCurrentDocumentContent(final String sText) { getDocumentPane().setDocumentText(sText); purgeUndos(); } public void setContentPanePreferredSize(final Dimension prefSize) { getDocumentPane().setContentPanePreferredSize(prefSize); } /** * @return returns the currently used ExtendedHTMLDocument Object */ public HTMLDocument getDocument() { return doc; } /** * get the DocumentPane object that is currently active * * @return the active DocumentPane */ DocumentPane getCurrentDocumentPane() { return getDocumentPane(); } /** * add a DocumentPaneListener from the currently active DocumentPane (if any) */ void addDocumentPaneListener(final DocumentPane.DocumentPaneListener listener) { if (getDocumentPane() != null) { //System.out.println("FrmMain.addDocumentPaneListener documentPane.source=" + documentPane.getSource()); getDocumentPane().addDocumentPaneListener(listener); } else { //System.out.println("FrmMain.addDocumentPaneListener documentPane is null, did not add"); } } /** * remove a DocumentPaneListener from the currently active DocumentPane (if any) */ void removeDocumentPaneListener(final DocumentPane.DocumentPaneListener listener) { if (getDocumentPane() != null) { getDocumentPane().removeDocumentPaneListener(listener); } } /** * initialize SimplyHTML's temporary directory */ private void initAppTempDir() { appTempDir = new File(System.getProperty("user.home") + File.separator + "." + FrmMain.APP_NAME.toLowerCase() + File.separator + APP_TEMP_DIR + File.separator); } /** * find plug-ins and load them accordingly, * i.e. display / dock components and add * plug-in menus. */ void initPlugins() { pluginManager = new PluginManager(this); final JMenu pMenu = dynRes.getMenu(pluginMenuId); final JMenu hMenu; if (pMenu != null) { final Container contentPane = SHTMLPanelImpl.this; pluginManager.loadPlugins(); final Enumeration plugins = pluginManager.plugins(); SHTMLPlugin pi; final JComponent pc; final JMenuItem pluginMenu; final JMenuItem helpMenu; while (plugins.hasMoreElements()) { pi = (SHTMLPlugin) plugins.nextElement(); if (pi.isActive()) { refreshPluginDisplay(pi); } } } adjustDividers(); } /** * adjust the divider sizes of SimplyHTML's SplitPanel * according to visibility */ public void adjustDividers() { splitPanel.adjustDividerSizes(); } /** * watch for key events that are automatically repeated * due to the user holding down a key. * *

When a key is held down by the user, every keyPressed * event is followed by a keyTyped event and a keyReleased * event although the key is actually still down. I.e. it * can not be determined by a keyReleased event if a key * actually is released, which is why this implementation * is necessary.

*/ class RepeatKeyWatcher implements KeyListener { /** timer for handling keyReleased events */ private final java.util.Timer releaseTimer = new java.util.Timer(); /** the next scheduled task for a keyReleased event */ private ReleaseTask nextTask; /** time of the last keyPressed event */ private long lastWhen = 0; /** time of the current KeyEvent */ private long when; /** delay to distinguish between single and repeated events */ private final long delay; /** indicates whether or not a KeyEvent currently occurs repeatedly */ private boolean repeating = false; /** * construct a RepeatKeyWatcher * * @param delay the delay in milliseconds until a * keyReleased event should be handled */ RepeatKeyWatcher(final long delay) { super(); this.delay = delay; } /** * handle a keyPressed event by cancelling the previous * release task (if any) and indicating repeated key press * as applicable. */ public void keyPressed(final KeyEvent e) { if (nextTask != null) { nextTask.cancel(); } when = e.getWhen(); if ((when - lastWhen) <= delay) { repeating = true; } else { repeating = false; } lastWhen = when; } /** * handle a keyReleased event by scheduling a * ReleaseTask. */ public void keyReleased(final KeyEvent e) { nextTask = new ReleaseTask(); releaseTimer.schedule(nextTask, delay); } public void keyTyped(final KeyEvent e) { } /** * indicate whether or not a key is being held down * * @return true if a key is being held down, false if not */ boolean isRepeating() { return repeating; } /** * Task to be executed when a key is released */ private class ReleaseTask extends TimerTask implements Runnable { public void run() { if (EventQueue.isDispatchThread()) { repeating = false; updateFormatControls(); } else { try { EventQueue.invokeAndWait(this); } catch (final InterruptedException e) { } catch (final InvocationTargetException e) { } } } } } public void clearDockPanels() { splitPanel.removeAllOuterPanels(); } /** * refresh the display for a given plug-in * * @param pi the plug-in to refresh */ public void refreshPluginDisplay(final SHTMLPlugin pi) { final JMenu pMenu = dynRes.getMenu(pluginMenuId); final JMenu hMenu = dynRes.getMenu(helpMenuId); final JMenuItem pluginMenu = pi.getPluginMenu(); final JMenuItem helpMenu = pi.getHelpMenu(); JTabbedPane p = null; final Preferences prefs; if (pi.isActive()) { final JComponent pc = pi.getComponent(); if (pc != null) { int panelNo = SplitPanel.WEST; double loc = 0.3; switch (pi.getDockLocation()) { case SHTMLPlugin.DOCK_LOCATION_LEFT: break; case SHTMLPlugin.DOCK_LOCATION_RIGHT: panelNo = SplitPanel.EAST; loc = 0.7; break; case SHTMLPlugin.DOCK_LOCATION_BOTTOM: panelNo = SplitPanel.SOUTH; loc = 0.7; break; case SHTMLPlugin.DOCK_LOCATION_TOP: panelNo = SplitPanel.NORTH; break; } p = (JTabbedPane) splitPanel.getPanel(panelNo); p.setVisible(true); p.add(pi.getGUIName(), pc); if (((panelNo == SplitPanel.WEST) && splitPanel.getDivLoc(panelNo) < this.getWidth() / 10) || ((panelNo == SplitPanel.NORTH) && splitPanel.getDivLoc(panelNo) < this.getHeight() / 10) || ((panelNo == SplitPanel.EAST) && splitPanel.getDivLoc(panelNo) > this.getWidth() - (this.getWidth() / 10)) || ((panelNo == SplitPanel.SOUTH) && splitPanel.getDivLoc(panelNo) > this.getHeight() - (this.getHeight() / 10))) { splitPanel.setDivLoc(panelNo, loc); } } if (pluginMenu != null) { Icon menuIcon = pluginMenu.getIcon(); if (menuIcon == null) { final URL url = DynamicResource.getResource(uiResources, emptyIcon); if (url != null) { menuIcon = new ImageIcon(url); pluginMenu.setIcon(new ImageIcon(url)); } } pMenu.add(pluginMenu); } if (helpMenu != null) { //System.out.println("FrmMain.refreshPluginDisplay insert helpMenu"); if (helpMenu.getSubElements().length > 0) { Icon menuIcon = helpMenu.getIcon(); if (menuIcon == null) { final URL url = DynamicResource.getResource(uiResources, emptyIcon); if (url != null) { menuIcon = new ImageIcon(url); helpMenu.setIcon(new ImageIcon(url)); } } } hMenu.insert(helpMenu, hMenu.getItemCount() - 2); } SwingUtilities.invokeLater(new PluginInfo(pi)); } else { if (pluginMenu != null) { pMenu.remove(pluginMenu); } if (helpMenu != null) { hMenu.remove(helpMenu); } } } class PluginInfo implements Runnable { SHTMLPlugin pi; PluginInfo(final SHTMLPlugin pi) { this.pi = pi; } public void run() { pi.showInitialInfo(); } } /** * get a HelpBroker for our application, * store it for later use and connect it to the help menu. */ private void initJavaHelp() { try { final JMenuItem mi = dynRes.getMenuItem(helpTopicsAction); if (mi == null) { return; } SHTMLHelpBroker.initJavaHelpItem(mi, "item15"); } catch (final Throwable e) { System.err.println("Simply HTML : Warning : loading help failed."); // --Dan //Util.errMsg(this, // Util.getResourceString("helpNotFoundError"), // e); } } protected void initDocumentPane() { //TODO } /** * instantiate Actions and put them into the commands * Hashtable for later use along with their action commands. * * This is hard coded as Actions need to be instantiated * hard coded anyway, so we do the storage in commands * right away. */ protected void initActions() { addAction(setDefaultStyleRefAction, new SHTMLEditorKitActions.SetDefaultStyleRefAction(this)); addAction(documentTitleAction, new SHTMLEditorKitActions.DocumentTitleAction(this)); addAction(editAnchorsAction, new SHTMLEditorKitActions.EditAnchorsAction(this)); addAction(setTagAction, new SHTMLEditorKitActions.SetTagAction(this)); addAction(formatAsCodeAction, new SHTMLEditorKitActions.SetTagAction(this, "code")); addAction(editLinkAction, new SHTMLEditorKitActions.EditLinkAction(this)); addAction(openLinkAction, new SHTMLEditorKitActions.OpenLinkAction(this)); addAction(prevTableCellAction, new SHTMLEditorKitActions.PrevTableCellAction(this)); addAction(nextTableCellAction, new SHTMLEditorKitActions.NextTableCellAction(this)); addAction(editNamedStyleAction, new SHTMLEditorKitActions.EditNamedStyleAction(this)); addAction(clearFormatAction, new SHTMLEditorKitActions.ClearFormatAction(this)); addAction(formatParaAction, new SHTMLEditorKitActions.FormatParaAction(this)); addAction(formatImageAction, new SHTMLEditorKitActions.FormatImageAction(this)); addAction(insertImageAction, new SHTMLEditorKitActions.InsertImageAction(this)); addAction(editPrefsAction, new SHTMLEditorKitActions.SHTMLEditPrefsAction(this)); addAction(toggleBulletsAction, new SHTMLEditorKitActions.ToggleListAction(this, toggleBulletsAction, HTML.Tag.UL)); addAction(toggleNumbersAction, new SHTMLEditorKitActions.ToggleListAction(this, toggleNumbersAction, HTML.Tag.OL)); addAction(formatListAction, new SHTMLEditorKitActions.FormatListAction(this)); addAction(ManagePluginsAction.managePluginsAction, new ManagePluginsAction()); addAction(elemTreeAction, new SHTMLEditorKitActions.ShowElementTreeAction(this)); addAction(gcAction, new SHTMLEditorKitActions.GarbageCollectionAction(this)); addAction(undoAction, new SHTMLEditorKitActions.UndoAction(this)); addAction(redoAction, new SHTMLEditorKitActions.RedoAction(this)); addAction(cutAction, new SHTMLEditorKitActions.SHTMLEditCutAction(this)); addAction(copyAction, new SHTMLEditorKitActions.SHTMLEditCopyAction(this)); addAction(pasteAction, new SHTMLEditorKitActions.SHTMLEditPasteAction(this)); addAction(pasteOtherAction, new SHTMLEditorKitActions.SHTMLEditPasteOtherAction(this)); addAction(selectAllAction, new SHTMLEditorKitActions.SHTMLEditSelectAllAction(this)); addAction(aboutAction, new SHTMLEditorKitActions.SHTMLHelpAppInfoAction(this)); addAction(fontAction, new SHTMLEditorKitActions.FontAction(this)); addAction(fontFamilyAction, new SHTMLEditorKitActions.FontFamilyAction(this)); addAction(fontSizeAction, new SHTMLEditorKitActions.FontSizeAction(this)); addAction(increaseFontSizeAction, new SHTMLEditorKitActions.ChangeFontSizeAction(this, increaseFontSizeAction, Change.INCREASE)); addAction(decreaseFontSizeAction, new SHTMLEditorKitActions.ChangeFontSizeAction(this, decreaseFontSizeAction, Change.DECREASE)); addAction(insertTableAction, new SHTMLEditorKitActions.InsertTableAction(this)); addAction(insertTableRowAction, new SHTMLEditorKitActions.InsertTableRowAction(this, null, insertTableRowAction)); addAction(insertTableRowHeaderAction, new SHTMLEditorKitActions.InsertTableRowAction(this, "th", insertTableRowHeaderAction)); addAction(insertTableColAction, new SHTMLEditorKitActions.InsertTableColAction(this)); addAction(appendTableColAction, new SHTMLEditorKitActions.AppendTableColAction(this)); addAction(appendTableRowAction, new SHTMLEditorKitActions.AppendTableRowAction(this)); addAction(deleteTableRowAction, new SHTMLEditorKitActions.DeleteTableRowAction(this)); addAction(deleteTableColAction, new SHTMLEditorKitActions.DeleteTableColAction(this)); addAction(moveTableRowUpAction, new SHTMLEditorKitActions.MoveTableRowUpAction(this)); addAction(moveTableRowDownAction, new SHTMLEditorKitActions.MoveTableRowDownAction(this)); addAction(moveTableColumnLeftAction, new SHTMLEditorKitActions.MoveTableColumnLeftAction(this)); addAction(moveTableColumnRightAction, new SHTMLEditorKitActions.MoveTableColumnRightAction(this)); addAction(formatTableAction, new SHTMLEditorKitActions.FormatTableAction(this)); addAction(toggleTableHeaderCellAction, new SHTMLEditorKitActions.ToggleTableHeaderCellAction(this)); addAction(fontBoldAction, new SHTMLEditorKitActions.BoldAction(this)); addAction(fontItalicAction, new SHTMLEditorKitActions.ItalicAction(this)); addAction(fontUnderlineAction, new SHTMLEditorKitActions.UnderlineAction(this)); addAction(fontColorAction, new SHTMLEditorKitActions.FontColorByDialogAction(this)); addAction(selectedFontColorAction, new SHTMLEditorKitActions.SelectedFontColorAction(this)); addAction(redFontColorAction, new SHTMLEditorKitActions.FixedFontColorAction(this, redFontColorAction, Color.RED)); addAction(greenFontColorAction, new SHTMLEditorKitActions.FixedFontColorAction(this, greenFontColorAction, new Color(0, 0x80, 0))); addAction(blueFontColorAction, new SHTMLEditorKitActions.FixedFontColorAction(this, blueFontColorAction, new Color(0, 0, 0xc0))); addAction(blackFontColorAction, new SHTMLEditorKitActions.FixedFontColorAction(this, blackFontColorAction, new Color(0, 0, 0))); addAction(removeFontColorAction, new SHTMLEditorKitActions.RemoveStyleAttributeAction(this, removeFontColorAction, HTML.Attribute.COLOR, CSS.Attribute.COLOR)); addAction(fontStrikethroughAction, new SHTMLEditorKitActions.ApplyCSSAttributeAction(this, fontStrikethroughAction, CSS.Attribute.TEXT_DECORATION, "line-through", false)); addAction(paraAlignLeftAction, new SHTMLEditorKitActions.ApplyCSSAttributeAction(this, paraAlignLeftAction, CSS.Attribute.TEXT_ALIGN, Util.CSS_ATTRIBUTE_ALIGN_LEFT, true)); addAction(paraAlignCenterAction, new SHTMLEditorKitActions.ApplyCSSAttributeAction(this, paraAlignCenterAction, CSS.Attribute.TEXT_ALIGN, Util.CSS_ATTRIBUTE_ALIGN_CENTER, true)); addAction(paraAlignRightAction, new SHTMLEditorKitActions.ApplyCSSAttributeAction(this, paraAlignRightAction, CSS.Attribute.TEXT_ALIGN, Util.CSS_ATTRIBUTE_ALIGN_RIGHT, true)); addAction(testAction, new SHTMLEditorKitActions.SHTMLTestAction(this)); addAction(printAction, new SHTMLEditorKitActions.PrintAction(this)); } public static void setActionBuilder(final ActionBuilder ab){ SHTMLPanelImpl.actionBuilder = ab; } public void addAction(String text, Action action) { dynRes.addAction(text, action); } /** * update all actions */ public void updateActions() { Action action; final Enumeration actions = dynRes.getActions(); while (actions.hasMoreElements()) { action = (Action) actions.nextElement(); if (action instanceof SHTMLAction) { ((SHTMLAction) action).update(); } } } /** customize the frame to our needs */ protected void customizeFrame() { splitPanel = new SplitPanel(); for (int i = 0; i < 4; i++) { final JTabbedPane p = new JTabbedPane(); p.setVisible(false); splitPanel.addComponent(p, i); } final JPanel toolBarPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)) { /** * */ private static final long serialVersionUID = 1L; public Dimension getPreferredSize() { final int maxWidth = splitPanel.getWidth(); int height = 0; int rowHeight = 0; int width = 0; for (int i = 0; i < getComponentCount(); i++) { final Component component = getComponent(i); final Dimension compPreferredSize = component.getPreferredSize(); if (maxWidth < compPreferredSize.width) { height += rowHeight + compPreferredSize.height; rowHeight = 0; width = 0; } else if (maxWidth < width + compPreferredSize.width) { height += rowHeight; rowHeight = compPreferredSize.height; width = compPreferredSize.width; } else { rowHeight = Math.max(rowHeight, compPreferredSize.height); width += compPreferredSize.width; } } height += rowHeight; return new Dimension(maxWidth, height); } }; final Container contentPane = new JPanel() { /** * */ private static final long serialVersionUID = 1L; public Dimension getPreferredSize() { final Dimension splitPreferredSize = splitPanel.getPreferredSize(); final Dimension toolbaPreferredSize = toolBarPanel.getPreferredSize(); return new Dimension(splitPreferredSize.width, splitPreferredSize.height + toolbaPreferredSize.height); } }; contentPane.setLayout(new BorderLayout()); toolBarPanel.add(createToolBar("toolBar")); formatToolBar = createToolBar("formatToolBar"); paraToolBar = createToolBar("paraToolBar"); toolBarPanel.add(formatToolBar); toolBarPanel.add(paraToolBar); if (Util.getPreference("show_toolbars", "true").equalsIgnoreCase("true")) { contentPane.add(toolBarPanel, BorderLayout.NORTH); } //contentPane.add(workPanel, BorderLayout.CENTER); contentPane.add(splitPanel, BorderLayout.CENTER); //contentPane.add(workPanel); add(contentPane, BorderLayout.CENTER); splitPanel.addComponentListener(new ComponentListener() { public void componentHidden(final ComponentEvent e) { } public void componentMoved(final ComponentEvent e) { } public void componentResized(final ComponentEvent e) { resizeToolbarPane(toolBarPanel); } public void componentShown(final ComponentEvent e) { } }); toolBarPanel.addContainerListener(new ContainerListener() { public void componentAdded(final ContainerEvent e) { resizeToolbarPane(toolBarPanel); } public void componentRemoved(final ContainerEvent e) { resizeToolbarPane(toolBarPanel); } }); } private void resizeToolbarPane(final JComponent toolBarPanel) { if (toolBarPanel.getPreferredSize().height != toolBarPanel.getHeight()) { toolBarPanel.revalidate(); } } /** * Create a tool bar. This reads the definition of a tool bar * from the associated resource file. * * @param nm the name of the tool bar definition in the resource file * * @return the created tool bar */ JToolBar createToolBar(final String nm) { final String[] itemKeys = Util.tokenize(Util.getResourceString(uiResources, nm), " "); final JToolBar toolBar = new JToolBar(); for (int i = 0; i < itemKeys.length; i++) { /** special handling for separators */ final String itemKey = itemKeys[i]; createToolbarItem(toolBar, itemKey); } return toolBar; } protected void createToolbarItem(final JToolBar toolBar, final String itemKey) { JSeparator separator; if (itemKey.equals(DynamicResource.menuSeparatorKey)) { separator = new JSeparator(JSeparator.VERTICAL); toolBar.add(separator); } /** * special handling for list elements in the * tool bar */ else if (itemKey.equalsIgnoreCase(fontFamilyAction)) { final FontFamilyPicker fontFamily = new FontFamilyPicker(); fontFamily.setAction(dynRes.getAction(fontFamilyAction)); toolBar.add(fontFamily); } else if (itemKey.equalsIgnoreCase(fontSizeAction)) { final FontSizePicker fontSize = new FontSizePicker(); fontSize.setPrototypeDisplayValue("88888"); fontSize.setAction(dynRes.getAction(fontSizeAction)); toolBar.add(fontSize); } else if (itemKey.equalsIgnoreCase(setTagAction)) { tagSelector = new TagSelector(); tagSelector.setAction(dynRes.getAction(setTagAction)); /* styleSelector = new StyleSelector(HTML.Attribute.CLASS); styleSelector.setPreferredSize(new Dimension(110, 23)); styleSelector.setAction(dynRes.getAction(setStyleAction)); styleSelector.setMaximumSize(comboBoxSize); jtpDocs.addChangeListener(styleSelector); */ toolBar.add(tagSelector); } else { AbstractButton newButton; try { if (itemKey.equalsIgnoreCase(helpTopicsAction)) { newButton = SHTMLHelpBroker.createHelpButton("item15"); final Icon icon = DynamicResource .getIconForCommand(SHTMLPanelImpl.getUiResources(), helpTopicsAction); newButton.setIcon(icon); newButton.setToolTipText(Util.getResourceString(helpTopicsAction + DynamicResource.toolTipSuffix)); toolBar.add(newButton); } else { /** * special handling for JToggleButtons in the tool bar */ final Action action = dynRes.getAction(itemKey); if (action instanceof AttributeComponent) { newButton = new JToggleButton(action); newButton.setText(""); newButton.setHideActionText(true); action.addPropertyChangeListener(new ToggleActionChangedListener((JToggleButton) newButton)); newButton.setVerticalAlignment(SwingConstants.CENTER); toolBar.add(newButton); } /** * this is the usual way to add tool bar buttons finally */ else { newButton = toolBar.add(action); } } if (System.getProperty("os.name").equals("Mac OS X")) { newButton.putClientProperty("JButton.buttonType", "segmented"); newButton.putClientProperty("JButton.segmentPosition", "middle"); } } catch (final Exception ex) { } catch (final java.lang.NoClassDefFoundError e) { } //When one of the help components is not there } } /** * register FrmMain as an object which has interest * in events from a given document pane */ protected void registerDocument() { doc.addUndoableEditListener(undoHandler); getSHTMLEditorPane().addCaretListener(this); getSHTMLEditorPane().addKeyListener(rkw); } /** * remove FrmMain as a registered object from a given * document pane and its components * * remove all plug-ins owned by this FrmMain from * SimplyHTML objects too */ protected void unregisterDocument() { getSHTMLEditorPane().removeCaretListener(this); getSHTMLEditorPane().removeKeyListener(rkw); if (doc != null) { doc.removeUndoableEditListener(undoHandler); } getDocumentPane().removeAllListeners(); // for plug-in removal from any documentPane that is about to close //System.out.println("FrmMain unregister document documentPane.name=" + documentPane.getDocumentName()); } /** * save a document and catch possible errors * * this is shared by save and saveAs so we put it here to avoid redundancy * * @param documentPane the document pane containing the document to save */ void doSave(final DocumentPane documentPane) { try { documentPane.saveDocument(); } /** * this exception should never happen as the menu allows to save a * document only if a name has been set. For new documents, whose * name is not set, only save as is enabled anyway. * * Just in case this is changed without remembering why it was designed * that way, we catch the exception here. */ catch(DocNameMissingException e) { Util.errMsg(this, Util.getResourceString(uiResources, "docNameMissingError"), e); } } public boolean isWYSIWYGEditorActive() { return getDocumentPane() != null && editorPane != null && getDocumentPane().getSelectedTab() == DocumentPane.VIEW_TAB_LAYOUT; } public boolean isHtmlEditorActive() { return getDocumentPane() != null && getDocumentPane().getSelectedTab() == DocumentPane.VIEW_TAB_HTML; } /** * get action properties from the associated resource bundle * * @param action the action to apply properties to * @param cmd the name of the action to get properties for */ public static void configureActionProperties(final Action action, final String cmd) { final String name = Util.getResourceString(uiResources, cmd + DynamicResource.labelSuffix); if (name != null) { action.putValue(Action.NAME, name); } final Icon icon = DynamicResource.getIconForCommand(uiResources, cmd); if (icon != null) { action.putValue(Action.SMALL_ICON, icon); } final String toolTip = Util.getResourceString(uiResources, cmd + DynamicResource.toolTipSuffix); if (toolTip != null) { action.putValue(Action.SHORT_DESCRIPTION, toolTip); } final String accelerator = Util.getResourceString(uiResources, cmd + DynamicResource.acceleratorSuffix); if (accelerator != null) { action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(accelerator)); } } /* ---------- undo/redo implementation ----------------------- */ /** Listener for edits on a document. */ private final UndoableEditListener undoHandler = new UndoHandler(); /** UndoManager that we add edits to. */ private UndoManager undo = new UndoManager(); /** inner class for handling undoable edit events */ class UndoHandler implements UndoableEditListener { /** * Messaged when the Document has created an edit, the edit is * added to undo, an instance of UndoManager. */ public void undoableEditHappened(final UndoableEditEvent e) { // ignore all events happened when the html source code pane is open if (getCurrentDocumentPane().getSelectedTab() != DocumentPane.VIEW_TAB_LAYOUT) { return; } getUndo().addEdit(e.getEdit()); } } /** * caret listener implementation to track format changes */ public void caretUpdate(final CaretEvent e) { if (!rkw.isRepeating()) { EventQueue.invokeLater(new Runnable() { public void run() { updateFormatControls(); } }); } } /** * update any controls that relate to formats at the * current caret position */ void updateFormatControls() { updateAToolBar(formatToolBar); updateAToolBar(paraToolBar); if (tagSelector != null) { final SetTagAction sta = (SetTagAction) tagSelector.getAction(); sta.setIgnoreActions(true); final Element e = doc.getParagraphElement(getSHTMLEditorPane().getCaretPosition()); tagSelector.setSelectedTag(e.getName()); sta.setIgnoreActions(false); } } private void updateAToolBar(final JToolBar bar) { Component c; Action action; final int count = bar.getComponentCount(); final AttributeSet a = getMaxAttributes(getSHTMLEditorPane(), null); for (int i = 0; i < count; i++) { c = bar.getComponentAtIndex(i); if (c instanceof AttributeComponent) { if (c instanceof StyleSelector) { final SetStyleAction ssa = (SetStyleAction) ((StyleSelector) c).getAction(); final AttributeSet oldAttibuteSet = ((AttributeComponent) c).getValue(); if (!a.isEqual(oldAttibuteSet)) { ssa.setIgnoreActions(true); ((AttributeComponent) c).setValue(a); ssa.setIgnoreActions(false); } } else { ((AttributeComponent) c).setValue(a); } } else if (c instanceof AbstractButton) { action = ((AbstractButton) c).getAction(); if ((action != null) && (action instanceof AttributeComponent)) { ((AttributeComponent) action).setValue(a); } } } } /** * a JComboBox for selecting a font family names * from those available in the system. */ class FontFamilyPicker extends JComboBox implements AttributeComponent { /** switch for the action listener */ private boolean ignoreActions = false; FontFamilyPicker() { /** * add the font family names available in the system * to the combo box */ super(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()); setSelectedItem("SansSerif"); } boolean ignore() { return ignoreActions; } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { ignoreActions = true; final String newSelection = Util.styleSheet().getFont(a).getFamily(); setSelectedItem(newSelection); ignoreActions = false; return true; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet set = new SimpleAttributeSet(); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_FAMILY, (String) getSelectedItem()); set.addAttribute(HTML.Attribute.FACE, (String) getSelectedItem()); return set; } public AttributeSet getValue(final boolean includeUnchanged) { return getValue(); } } /** * a JComboBox for selecting a font size */ static final String[] FONT_SIZES = new String[] { "8", "10", "12", "14", "18", "24" }; class FontSizePicker extends JComboBox implements AttributeComponent { private boolean ignoreActions = false; final private Object key; FontSizePicker() { /** * add font sizes to the combo box */ super(FONT_SIZES); key = CSS.Attribute.FONT_SIZE; } boolean ignore() { return ignoreActions; } /** * set the value of this combo box * * @param a the set of attributes possibly having a * font size attribute this pick list could display * * @return true, if the set of attributes had a font size attribute, * false if not */ public boolean setValue(final AttributeSet a) { ignoreActions = true; final int size = Util.styleSheet().getFont(a).getSize(); final String newSelection = Integer.toString(size); setEditable(true); setSelectedItem(newSelection); setEditable(false); ignoreActions = false; return true; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet set = new SimpleAttributeSet(); final String relativeSize = Integer.toString(getSelectedIndex() + 1); set.addAttribute(HTML.Attribute.SIZE, relativeSize); Util.styleSheet().addCSSAttributeFromHTML(set, CSS.Attribute.FONT_SIZE, relativeSize /*+ "pt"*/); return set; } public AttributeSet getValue(final boolean includeUnchanged) { return getValue(); } } /** * a listener for property change events on ToggleFontActions */ private class ToggleActionChangedListener implements PropertyChangeListener { JToggleButton button; ToggleActionChangedListener(final JToggleButton button) { super(); this.button = button; } public void propertyChange(final PropertyChangeEvent e) { final String propertyName = e.getPropertyName(); if (e.getPropertyName().equals(SHTMLPanelImpl.ACTION_SELECTED_KEY)) { //System.out.println("propertyName=" + propertyName + " newValue=" + e.getNewValue()); if (e.getNewValue().toString().equals(SHTMLPanelImpl.ACTION_SELECTED)) { button.setSelected(true); } else { button.setSelected(false); } } } } public AttributeSet getMaxAttributes(final int caretPosition) { final Element paragraphElement = getSHTMLDocument().getParagraphElement(caretPosition); final StyleSheet styleSheet = getSHTMLDocument().getStyleSheet(); return SHTMLPanelImpl.getMaxAttributes(paragraphElement, styleSheet); } /** * Gets all the attributes that can be found in the element tree * starting at the highest parent down to the character element * at the current position in the document. Combine element * attributes with attributes from the style sheet. * * @param editorPane the editor pane to combine attributes from * * @return the resulting set of combined attributes */ AttributeSet getMaxAttributes(final SHTMLEditorPane editorPane, final String elemName) { Element element = doc.getCharacterElement(editorPane.getSelectionStart()); final StyleSheet styleSheet = doc.getStyleSheet(); if (elemName != null && elemName.length() > 0) { element = Util.findElementUp(elemName, element); return SHTMLPanelImpl.getMaxAttributes(element, styleSheet); } final MutableAttributeSet maxAttributes = (MutableAttributeSet) SHTMLPanelImpl.getMaxAttributes(element, styleSheet); final StyledEditorKit editorKit = (StyledEditorKit) editorPane.getEditorKit(); final MutableAttributeSet inputAttributes = editorKit.getInputAttributes(); maxAttributes.addAttributes(inputAttributes); return maxAttributes; } Frame getMainFrame() { return JOptionPane.getFrameForComponent(SHTMLPanelImpl.this); } static AttributeSet getMaxAttributes(Element e, final StyleSheet s) { final SimpleAttributeSet a = new SimpleAttributeSet(); final Element cElem = e; AttributeSet attrs; final Vector elements = new Vector(); Object classAttr; String styleName; String elemName; while (e != null) { elements.insertElementAt(e, 0); e = e.getParentElement(); } for (int i = 0; i < elements.size(); i++) { e = (Element) elements.elementAt(i); classAttr = e.getAttributes().getAttribute(HTML.Attribute.CLASS); elemName = e.getName(); styleName = elemName; if (classAttr != null) { styleName = elemName + "." + classAttr.toString(); a.addAttribute(HTML.Attribute.CLASS, classAttr); } //System.out.println("getMaxAttributes name=" + styleName); attrs = s.getStyle(styleName); if (attrs != null) { a.addAttributes(Util.resolveAttributes(attrs)); } else { attrs = s.getStyle(elemName); if (attrs != null) { a.addAttributes(Util.resolveAttributes(attrs)); } } a.addAttributes(Util.resolveAttributes(e.getAttributes())); } if (cElem != null) { //System.out.println("getMaxAttributes cElem.name=" + cElem.getName()); a.addAttributes(cElem.getAttributes()); } //System.out.println(" "); //de.calcom.cclib.html.HTMLDiag hd = new de.calcom.cclib.html.HTMLDiag(); //hd.listAttributes(a, 4); return new AttributeMapper(a).getMappedAttributes(AttributeMapper.toJava); } /** * @param documentPane The documentPane to set. */ void setDocumentPane(final DocumentPane documentPane) { this.documentPane = documentPane; } /** * @return Returns the documentPane. */ public DocumentPane getDocumentPane() { return documentPane; } protected void setEditorPane(final SHTMLEditorPane editorPane) { if (editorPane != null) { editorPane.setPopup(editorPopup); } this.editorPane = editorPane; } /** * @return Returns the editorPane. */ public SHTMLEditorPane getSHTMLEditorPane() { return (SHTMLEditorPane) getEditorPane(); } public JEditorPane getEditorPane() { return editorPane; } public JEditorPane getSourceEditorPane() { return (JEditorPane) getDocumentPane().getHtmlEditor(); } /** * @return Returns the doc. */ SHTMLDocument getSHTMLDocument() { return doc; } /** * @param undo The undo to set. */ void setUndo(final UndoManager undo) { this.undo = undo; } /** * @return Returns the undo. */ UndoManager getUndo() { return undo; } /** * @param tagSelector The tagSelector to set. */ void setTagSelector(final TagSelector tagSelector) { this.tagSelector = tagSelector; } /** * @return Returns the tagSelector. */ TagSelector getTagSelector() { return tagSelector; } void savePrefs() { splitPanel.savePrefs(); } boolean close() { return true; } /* (non-Javadoc) * @see javax.swing.JComponent#requestFocus() */ public JEditorPane getMostRecentFocusOwner() { if (getDocumentPane() != null) { return getDocumentPane().getMostRecentFocusOwner(); } return null; } /* ---------- font manipulation code end ------------------ */ public int getCaretPosition() { return getSHTMLEditorPane().getCaretPosition(); } public JMenuBar getMenuBar() { return menuBar; } public void switchViews() { getDocumentPane().switchViews(); } public void setOpenHyperlinkHandler(final ActionListener openHyperlinkHandler) { this.openHyperlinkHandler = openHyperlinkHandler; } public void openHyperlink(final String linkURL) { if (openHyperlinkHandler != null) { openHyperlinkHandler.actionPerformed(new ActionEvent(this, 0, linkURL)); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/AttributeComboBox.java0100644 0000000 0000000 00000016767 12114157751 023725 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import javax.swing.JComboBox; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.html.CSS; import javax.swing.text.html.HTML; /** * ComboBox to show and manipulate an attribute out * of a given set of attribute values. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class AttributeComboBox extends JComboBox implements AttributeComponent { /** CSS attribute key associated with this component */ private final Object attributeKey; /** HTML attribute key associated with this component */ private final Object htmlAttributeKey; /** attribute names associated with the items of this component */ private final String[] names; /** indicates wether or not a call to setValue is the initial one */ private int setValCount = 0; /** stores the initial value for tracking changes */ private int originalIndex = -2; /** * construct an AttributeComboBox * * @param items the items to appear in the list of this component * @param names the attributes to associate with items * (in the same order) * @param key the CSS attribute key this component represents * @param htmlKey the HTL attribute key this component represents * * @see getValue */ public AttributeComboBox(final String[] items, final String[] names, final Object key, final Object htmlKey) { super(items); this.names = names; attributeKey = key; htmlAttributeKey = htmlKey; } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { //System.out.println("AttributeComboBox setValue"); //de.calcom.cclib.html.HTMLDiag hd = new de.calcom.cclib.html.HTMLDiag(); //hd.listAttributes(a, 2); boolean success = false; Object valObj; if (attributeKey != null) { valObj = a.getAttribute(attributeKey); if (valObj == null && htmlAttributeKey != null) { valObj = a.getAttribute(htmlAttributeKey); if (valObj != null) { success = setValue(valObj); } } /* correction start: missing list-style-type attribute from style sheet */ else if (valObj == null && attributeKey.equals(CSS.Attribute.LIST_STYLE_TYPE)) { final Object name = a.getAttribute(StyleConstants.NameAttribute); if (name != null && name.toString().equalsIgnoreCase(HTML.Tag.UL.toString())) { success = setValue("disc"); } else if (name != null && name.toString().equalsIgnoreCase(HTML.Tag.OL.toString())) { success = setValue("decimal"); } } if (valObj == null && htmlAttributeKey != null) { if (htmlAttributeKey.equals(HTML.Attribute.ALIGN) || htmlAttributeKey.equals(HTML.Attribute.VALIGN)) { success = setValue(names[0]); } } /* correction end: missing list-style-type attribute from style sheet */ else { //System.out.println("AttributeComboBox setValue value=" + valObj); success = setValue(valObj); } } else { if (htmlAttributeKey != null) { valObj = a.getAttribute(htmlAttributeKey); if (valObj != null) { success = setValue(valObj); } } } return success; } public void reset() { setValCount = 0; originalIndex = -2; } private boolean setValue(final Object valObj) { if (valObj != null) { final String valStr = valObj.toString(); int i = 0; while (!valStr.equalsIgnoreCase(names[i])) { i++; if(i >= names.length) return false; } setSelectedIndex(i); if (++setValCount < 2) { originalIndex = i; } return true; } return false; } /** * get the value of this AttributeComponent * *

If one an attribute key is not set, the value will not * be returned for that attribute key. If both attribute keys are * set, two attributes with identical value are returned. Up * to J2SE 1.4, the Java language does not render * CSS.Attribute.VERTICAL_ALIGN but it does render HTML.Attribute.VALIGN. * Some browsers handle it vice versa, so it is useful to * store both attributes.

* * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet a = new SimpleAttributeSet(); final int value = getSelectedIndex(); //System.out.println("AttributeComboBox getValue originalIndex=" + originalIndex + " value=" + value); if (originalIndex != value) { //System.out.println("changed " + attributeKey + " originalIndex=" + originalIndex + " value=" + value); if (attributeKey != null) { Util.styleSheet().addCSSAttribute(a, (CSS.Attribute) attributeKey, names[value]); //a.addAttribute(attributeKey, names[value]); } if (htmlAttributeKey != null) { a.addAttribute(htmlAttributeKey, names[value]); } } return a; } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { final SimpleAttributeSet a = new SimpleAttributeSet(); final int value = getSelectedIndex(); if (attributeKey != null) { Util.styleSheet().addCSSAttribute(a, (CSS.Attribute) attributeKey, names[value]); } if (htmlAttributeKey != null) { a.addAttribute(htmlAttributeKey, names[value]); } return a; } else { return getValue(); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/ParaStyleDialog.java0100644 0000000 0000000 00000052630 12114157751 023342 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Frame; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.StringWriter; import java.util.Enumeration; import java.util.Vector; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.MutableAttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleContext; import javax.swing.text.html.HTML; import javax.swing.text.html.StyleSheet; /** * Dialog to set paragraph attributes and to manipulate styles in a * given style sheet. * *

In stage 9 this has an additional combo box to select different * element types in MODE_NAMED_STYLES.

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class ParaStyleDialog extends DialogShell implements AttributeComponent, ActionListener, ListSelectionListener, ChangeListener { private final String standardStyleName = Util.getResourceString("standardStyleName"); /** mode to edit named styles with this dialog */ private static int MODE_NAMED_STYLES = 1; /** mode to set a paragraph style with this dialog */ private static int MODE_PARAGRAPH_STYLE = 2; /** button to save a named style */ private JButton saveStyleBtn; /** button to save a named style under a different name */ private JButton saveStyleAsBtn; /** button to delete a named style */ private JButton deleteStyleBtn; /** the mode this dialog was created in */ private final int mode; /** the AttributeComponents in this dialog */ private final Vector components = new Vector(); /** the FontPanel for the paragraph font settings */ private final FontPanel fp; /** list of styles available in style sheet */ private JList styleList; /** style sheet to use in MODE_NAMED_STYLES */ private StyleSheet styles; /** the document this dialog is operating on when in MODE_NAMED_STYLES */ private final Document doc; /** set of attributes for mapping discrepancies between HTML and Java */ private AttributeSet mapSet; /** * panel for setting paragraph styles (needed in the change listener * of the list of named styles) */ private final StylePanel sp; /** * panel for setting margins (needed in the change listener * of the list of named styles) */ private final MarginPanel mp; /** table to map between HTML tags and 'content types' */ static private NamedObject[] cTypes = null; /** selector for content type */ private JComboBox cType; /** * create a ParaStyleDialog to manipulate * the format of a paragraph * * @param parent the parent frame of this dialog * @param title the text to be shown as title for this dialog */ public ParaStyleDialog(final Frame parent, final String title) { this(parent, title, null, MODE_PARAGRAPH_STYLE); } /** * create a ParaStyleDialog to edit named * styles of a given document * * @param parent the parent frame of this dialog * @param title the text to be shown as title for this dialog * @param doc the document having the style sheet to edit named styles from */ public ParaStyleDialog(final Frame parent, final String title, final Document doc) { this(parent, title, doc, MODE_NAMED_STYLES); } /** * construct a ParaStyleDialog * * @param parent the parent frame for this dialog * @param title the text to be shown as title for this dialog * @param mode the mode this dialog is to be created, one of MODE_NAMED_STYLES or MODE_PARAGRAPH_STYLE */ private ParaStyleDialog(final Frame parent, final String title, final Document doc, final int mode) { super(parent, title); JPanel hPanel = null; this.mode = mode; this.doc = doc; // get content pane of DialogShell to add components to final Container contentPane = super.getContentPane(); // construct tabbed pane for the various groups of settings final JTabbedPane tp = new JTabbedPane(); tp.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); // create style panel sp = new StylePanel(StylePanel.TYPE_PARAGRAPH); sp.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util.getResourceString("cellGenTabLabel"))); components.add(sp); // create margin panel mp = new MarginPanel(); components.add(mp); mp.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("cellMarginTabLabel"))); if (mode == MODE_NAMED_STYLES) { styles = ((SHTMLDocument) doc).getStyleSheet(); // create a combo box for content type initContentTypes(); cType = new JComboBox(cTypes); cType.addActionListener(this); // create a list of styles //Vector styleNames = Util.getStyleNamesForTag(styles, getContentType()); //styleNames.insertElementAt(standardStyleName, 0); styleList = new JList(/*new DefaultComboBoxModel(styleNames)*/); updateStyleList(); styles.addChangeListener(this); styleList.addListSelectionListener(this); // create a panel to control the styles final JPanel btnPanel = new JPanel(new GridLayout(3, 1, 5, 5)); saveStyleBtn = new JButton(Util.getResourceString("saveStyleButtonLabel")); saveStyleBtn.addActionListener(this); saveStyleBtn.setEnabled(false); saveStyleAsBtn = new JButton(Util.getResourceString("saveStyleAsButtonLabel")); saveStyleAsBtn.addActionListener(this); deleteStyleBtn = new JButton(Util.getResourceString("deleteStyleButtonLabel")); deleteStyleBtn.addActionListener(this); deleteStyleBtn.setEnabled(false); btnPanel.add(saveStyleBtn); btnPanel.add(saveStyleAsBtn); btnPanel.add(deleteStyleBtn); // use a helper panel for placement of buttons hPanel = new JPanel(new BorderLayout()); hPanel.add(btnPanel, BorderLayout.NORTH); // create named styles panel final JPanel nsPanel = new JPanel(new BorderLayout(5, 5)); nsPanel.add(cType, BorderLayout.NORTH); nsPanel.add(new JScrollPane(styleList), BorderLayout.CENTER); nsPanel.add(hPanel, BorderLayout.EAST); nsPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("stylePanelLabel"))); nsPanel.setVisible(mode == MODE_NAMED_STYLES); // use a helper panel for placement of style and named styles panels hPanel = new JPanel(new BorderLayout()); hPanel.add(sp, BorderLayout.NORTH); hPanel.add(nsPanel, BorderLayout.CENTER); okButton.setText(Util.getResourceString("closeLabel")); } else { hPanel = new JPanel(new BorderLayout()); hPanel.add(sp, BorderLayout.NORTH); } // create paragraph panel final JPanel paraPanel = new JPanel(new BorderLayout()); paraPanel.add(hPanel, BorderLayout.CENTER); paraPanel.add(mp, BorderLayout.EAST); // add paragraph panel to tabbed pane tp.add(Util.getResourceString("paraTabLabel"), paraPanel); // create font panel and add to tabbed pane fp = new FontPanel(true); // add tabbed pane to content pane of dialog contentPane.add(tp, BorderLayout.CENTER); cancelButton.setVisible(mode != MODE_NAMED_STYLES); tp.add(Util.getResourceString("fontTabLabel"), fp); // cause optimal placement of all elements pack(); } /** * update the list of available styles for the currently * selected tag */ private void updateStyleList() { final Vector styleNames = Util.getStyleNamesForTag(styles, getContentType()); styleNames.insertElementAt(standardStyleName, 0); styleList.setModel(new DefaultComboBoxModel(styleNames)); } /** * initialize content types hashtable */ private void initContentTypes() { cTypes = new NamedObject[10]; int i = 0; cTypes[i++] = new NamedObject(HTML.Tag.P.toString(), Util.getResourceString("cTagNamePara")); cTypes[i++] = new NamedObject(HTML.Tag.H1.toString(), Util.getResourceString("cTagNameHead1")); cTypes[i++] = new NamedObject(HTML.Tag.H2.toString(), Util.getResourceString("cTagNameHead2")); cTypes[i++] = new NamedObject(HTML.Tag.H3.toString(), Util.getResourceString("cTagNameHead3")); cTypes[i++] = new NamedObject(HTML.Tag.H4.toString(), Util.getResourceString("cTagNameHead4")); cTypes[i++] = new NamedObject(HTML.Tag.H5.toString(), Util.getResourceString("cTagNameHead5")); cTypes[i++] = new NamedObject(HTML.Tag.H6.toString(), Util.getResourceString("cTagNameHead6")); cTypes[i++] = new NamedObject(HTML.Tag.A.toString(), Util.getResourceString("cTagNameLink")); cTypes[i++] = new NamedObject(HTML.Tag.UL.toString(), Util.getResourceString("cTagNameUL")); cTypes[i++] = new NamedObject(HTML.Tag.OL.toString(), Util.getResourceString("cTagNameOL")); } /** * get the currently selected tag * * @return the tag name currently selected */ private String getContentType() { return ((NamedObject) cType.getSelectedItem()).getObject().toString(); } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet attributes = new SimpleAttributeSet(); final Enumeration elements = components.elements(); AttributeComponent ac; while (elements.hasMoreElements()) { ac = (AttributeComponent) elements.nextElement(); attributes.addAttributes(ac.getValue()); } attributes.addAttributes(fp.getAttributes()); return attributes; } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { final SimpleAttributeSet attributes = new SimpleAttributeSet(); final Enumeration elements = components.elements(); AttributeComponent ac; while (elements.hasMoreElements()) { ac = (AttributeComponent) elements.nextElement(); attributes.addAttributes(ac.getValue(includeUnchanged)); } attributes.addAttributes(fp.getAttributes(includeUnchanged)); return attributes; } else { return getValue(); } } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { boolean result = true; /* System.out.println("\r\n"); de.calcom.cclib.html.HTMLDiag hd = new de.calcom.cclib.html.HTMLDiag(); hd.listAttributes(a, 4); */ final AttributeSet set = Util.resolveAttributes(a); final Enumeration elements = components.elements(); AttributeComponent ac; while (elements.hasMoreElements()) { ac = (AttributeComponent) elements.nextElement(); if (!ac.setValue(set)) { result = false; } } fp.setAttributes(set); return result; } /** * listen to changes of style list, * switch state of save and delete buttons accordingly and * set dialog to the selected style, if any */ public void valueChanged(final ListSelectionEvent e) { if (e.getSource().equals(styleList)) { final int selectedStyleNo = styleList.getSelectedIndex(); final boolean styleSelected = selectedStyleNo > -1; saveStyleBtn.setEnabled(styleSelected); deleteStyleBtn.setEnabled(styleSelected); if (styleSelected) { // set dialog contents to selected style sp.reset(); fp.reset(); mp.reset(); String styleName; final String className = styleList.getSelectedValue().toString(); if (className.equalsIgnoreCase(standardStyleName)) { styleName = getContentType(); } else { styleName = getContentType() + Util.CLASS_SEPARATOR + className; } //Style style = styles.getStyle(styleName); AttributeSet style = styles.getStyle(styleName); if (style == null) { style = new SimpleAttributeSet(); } final MutableAttributeSet allStyles = (MutableAttributeSet) SHTMLPanelImpl.getMaxAttributes( ((SHTMLDocument) doc).getCharacterElement(doc.getEndPosition().getOffset()), ((SHTMLDocument) doc).getStyleSheet()); allStyles.addAttributes(style); //mapSet = new AttributeMapper(Util.resolveAttributes(style)).getMappedAttributes(AttributeMapper.toJava); //setValue(style); setValue(allStyles); } } } /** * get the style name currently selected in the list of style names * * @return the name of the style currently selected in the * list of style names or null if none is currently selected */ private String getSelectedStyleName() { String styleName = null; if (styleList.getSelectedIndex() > -1) { styleName = styleList.getSelectedValue().toString(); } return styleName; } /** * save the current settings on this ParaStyleDialog * to its associated style sheet under the name currently * selected in the list of named styles. * *

This will overwrite the existing style with the current * settings on this dialog.

*/ private void doSaveStyle() { final String styleName = getSelectedStyleName(); if (styleName != null) { saveStyleAs(styleName); } } /** * save the current settings on this ParaStyleDialog * to its associated style sheet under a name defined by the user. * *

This will ask for a name a style shall be saved under. If the name * exists, the user is prompted whether or not it shall be overwritten. * The sytle is saved according to the user's choices.

*/ private void doSaveStyleAs() { String initialName = getSelectedStyleName(); if (initialName == null) { initialName = Util.getResourceString("newStyleDefaultName"); } final String newStyleName = Util.nameInput(null, initialName, "\\w[\\w ]*", "styleNameInputTitle", "styleNameInputText").trim(); if (newStyleName != null) { if (styleNameExists(newStyleName) || newStyleName.equalsIgnoreCase(standardStyleName)) { if (Util.msg(JOptionPane.YES_NO_OPTION, "confirmSaveAs", "fileExistsQuery", newStyleName, " ")) { saveStyleAs(newStyleName); } } else { saveStyleAs(newStyleName); } } } /** * delete the currently selected style name for the * currently selected tag */ private void doDeleteStyle() { final String styleName = getSelectedStyleName(); if (styleName != null) { if (Util.msg(JOptionPane.YES_NO_OPTION, "confirmDelete", "deleteStyleQuery", styleName, "\r\n\r\n")) { styles.removeStyle(getContentType() + Util.CLASS_SEPARATOR + styleName); } } } /** * save a style under a given name * * @param newStyleName the name the style has to be saved under */ private void saveStyleAs(final String newStyleName) { try { String className = getContentType(); if (!newStyleName.equalsIgnoreCase(standardStyleName)) { className = className + Util.CLASS_SEPARATOR + newStyleName; } final StringWriter sw = new StringWriter(); final CSSWriter cw = new CSSWriter(sw, null); final SimpleAttributeSet a = new SimpleAttributeSet(); if (mapSet != null) { a.addAttributes(mapSet); } /* AttributeSet test = getValue(true); de.calcom.cclib.html.HTMLDiag hd = new de.calcom.cclib.html.HTMLDiag(); hd.listAttributes(test, 4); System.out.println(" \r\n"); */ a.addAttributes(new AttributeMapper(getValue(true)).getMappedAttributes(AttributeMapper.toCSS)); // hd.listAttributes(a, 4); cw.writeRule(className, a); final String ruleStr = sw.getBuffer().toString(); styles.removeStyle(className); styles.addRule(ruleStr); if (doc != null) { final SHTMLDocument sd = (SHTMLDocument) doc; if (!sd.hasStyleRef()) { sd.insertStyleRef(); } } } catch (final Exception ex) { Util.errMsg(this, ex.getMessage(), ex); } } /** * get the style sheet this dialog uses * * @return the used style sheet */ public StyleSheet getStyleSheet() { return styles; } /** * check whether or not a named style already exists in the style sheet * associated to this dialog * * @param styleName the name of the style to be looked for * * @return true, if the given style name alread is used in the style sheet, * false if not */ private boolean styleNameExists(final String styleName) { final Vector styleNames = Util.getStyleNamesForTag(styles, getContentType() /*HTML.Tag.P.toString()*/); return (styleNames.indexOf(styleName) > -1); } /** * overridden to addd some custom cleanup upon closing of dialog */ public void dispose() { if (mode == MODE_NAMED_STYLES) { styles.removeChangeListener(this); } super.dispose(); } /** * ChangeListener implementation to be used on a style sheet. * *

This is used to update the list of named styles whenever * a change was saved to the style sheet.

*/ public void stateChanged(final ChangeEvent e) { final Object src = e.getSource(); if (src instanceof StyleContext.NamedStyle) { final Vector styleNames = Util .getStyleNamesForTag((AttributeSet) src, getContentType() /*HTML.Tag.P.toString()*/); styleNames.insertElementAt(standardStyleName, 0); styleList.setModel(new DefaultComboBoxModel(styleNames)); } } /** * listen to actions and route them accordingly, i.e. react to * buttons save, save as and delete style */ public void actionPerformed(final ActionEvent e) { final Object src = e.getSource(); if (src.equals(saveStyleBtn)) { doSaveStyle(); } else if (src.equals(saveStyleAsBtn)) { doSaveStyleAs(); } else if (src.equals(deleteStyleBtn)) { doDeleteStyle(); } else if (src.equals(cType)) { // update list of named styles updateStyleList(); } else { super.actionPerformed(e); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/PrefsDialog.java0100644 0000000 0000000 00000021002 12114157751 022502 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.LinkedList; import java.util.List; import java.util.prefs.Preferences; import javax.swing.DefaultComboBoxModel; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; import javax.swing.UIManager; /** * Dialog to set user preferences for application SimplyHTML. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class PrefsDialog extends DialogShell implements ActionListener { /** the look and feels avaliable in the system */ private UIManager.LookAndFeelInfo[] lfinfo; /** reference for user preferences for this class */ protected Preferences prefs = Preferences.userNodeForPackage(getClass()); /** constant for dock location setting in preferences file */ public static final String PREFSID_LOOK_AND_FEEL = "Laf"; public static final String PREFS_USE_STD_STYLE_SHEET = "use_std_styles"; public static final String PREFS_DEFAULT_PASTE_MODE ="default_paste_mode"; private final String lafName = UIManager.getLookAndFeel().getName(); private final JComboBox lafCombo; private final JCheckBox useStdStyleSheet; private final JComboBox pasteModeCombo; /** the help id for this dialog */ private static final String helpTopicId = "item167"; private final List prefChangeListeners = new LinkedList(); public PrefsDialog(final Frame parent, final String title) { super(parent, title, helpTopicId); // have a grid bag layout ready to use final GridBagLayout g = new GridBagLayout(); final GridBagConstraints c = new GridBagConstraints(); final JPanel layoutPanel = new JPanel(g); // build a panel for preferences related to the application final JPanel appPrefsPanel = new JPanel(g); Util.addGridBagComponent(appPrefsPanel, new JLabel(Util.getResourceString("prfLafLabel")), g, c, 0, 0, GridBagConstraints.EAST); lafCombo = new JComboBox(); initLfComboBox(); Util.addGridBagComponent(appPrefsPanel, lafCombo, g, c, 1, 0, GridBagConstraints.EAST); pasteModeCombo = new JComboBox(); initPasteModeComboBox(); Util.addGridBagComponent(appPrefsPanel, new JLabel(Util.getResourceString("prefsPasteModeLabel")), g, c, 0, 1, GridBagConstraints.EAST); Util.addGridBagComponent(appPrefsPanel, pasteModeCombo, g, c, 1, 1, GridBagConstraints.EAST); // build a panel for preferences related to documents /* JPanel docPrefsPanel = new JPanel(g); Util.addGridBagComponent(docPrefsPanel, new JCheckBox( FrmMain.dynRes.getResourceString( FrmMain.resources, "prfShareDocResourcesLabel")), g, c, 0, 1, GridBagConstraints.EAST); */ Util.addGridBagComponent(layoutPanel, appPrefsPanel, g, c, 0, 0, GridBagConstraints.WEST); // add option for standard stlye sheet useStdStyleSheet = new JCheckBox(Util.getResourceString("linkDefaultStyleSheetLabel")); final boolean useStyle = prefs.getBoolean(PrefsDialog.PREFS_USE_STD_STYLE_SHEET, false); useStdStyleSheet.setSelected(useStyle); Util.addGridBagComponent(layoutPanel, useStdStyleSheet, g, c, 0, 2, GridBagConstraints.WEST); // add to content pane of DialogShell final Container contentPane = super.getContentPane(); contentPane.add(layoutPanel, BorderLayout.CENTER); //contentPane.add(appPrefsPanel, BorderLayout.NORTH); //contentPane.add(docPrefsPanel, BorderLayout.CENTER); // cause optimal placement of all elements pack(); } public void addPrefChangeListener(final SHTMLPrefsChangeListener listener) { prefChangeListeners.add(listener); } public void removePrefChangeListener(final SHTMLPrefsChangeListener listener) { prefChangeListeners.remove(listener); } private void initLfComboBox() { lfinfo = UIManager.getInstalledLookAndFeels(); final int count = lfinfo.length; final String[] lfNames = new String[count]; for (int i = 0; i < count; i++) { lfNames[i] = lfinfo[i].getName(); } lafCombo.setModel(new DefaultComboBoxModel(lfNames)); lafCombo.setSelectedItem(lafName); } private void initPasteModeComboBox() { pasteModeCombo.setModel(new DefaultComboBoxModel(SHTMLEditorPane.PasteMode.values())); pasteModeCombo.setSelectedItem( SHTMLEditorPane.PasteMode.valueOf(SHTMLEditorPane.PasteMode.class, prefs.get(PREFS_DEFAULT_PASTE_MODE, SHTMLEditorPane.PasteMode.PASTE_HTML.name()))); pasteModeCombo.setRenderer(new ListCellRenderer() { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { switch ((SHTMLEditorPane.PasteMode)value) { case PASTE_HTML: return new JLabel(Util.getResourceString("pasteModeHTML")); case PASTE_PLAIN_TEXT: return new JLabel(Util.getResourceString("pasteModePlainText")); default: throw new AssertionError(); } } }); } /** * implements the ActionListener interface to be notified of * clicks onto the ok and cancel button. */ public void actionPerformed(final ActionEvent e) { final Component src = (Component) e.getSource(); if (src == okButton) { savePrefs(src); } super.actionPerformed(e); } private void savePrefs(final Component src) { try { final String newLaf = lfinfo[lafCombo.getSelectedIndex()].getClassName(); if (!lafName.equalsIgnoreCase(newLaf)) { prefs.put(PREFSID_LOOK_AND_FEEL, newLaf); UIManager.setLookAndFeel(newLaf); SwingUtilities.updateComponentTreeUI(JOptionPane.getFrameForComponent(src)); } boolean oldStyleSheetPref = prefs.getBoolean(PREFS_USE_STD_STYLE_SHEET, false); prefs.putBoolean(PREFS_USE_STD_STYLE_SHEET, useStdStyleSheet.isSelected()); String oldDefaultPasteMode = prefs.get(PREFS_DEFAULT_PASTE_MODE, SHTMLEditorPane.PasteMode.PASTE_HTML.name()); prefs.put(PREFS_DEFAULT_PASTE_MODE, ((SHTMLEditorPane.PasteMode)pasteModeCombo.getSelectedItem()).name()); for (SHTMLPrefsChangeListener listener: prefChangeListeners) { listener.shtmlPrefChanged(PREFS_USE_STD_STYLE_SHEET, new Boolean(useStdStyleSheet.isSelected()).toString(), new Boolean(oldStyleSheetPref).toString()); listener.shtmlPrefChanged(PREFS_DEFAULT_PASTE_MODE, prefs.get(PREFS_DEFAULT_PASTE_MODE, null), oldDefaultPasteMode); } } catch (final Exception ex) { Util.errMsg(this, ex.getMessage(), ex); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/TitledPickList.java0100644 0000000 0000000 00000021020 12114157751 023173 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.Enumeration; import java.util.EventListener; import java.util.EventObject; import java.util.Vector; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; /** * A pick list typically being used in font dialogs, consisting * of a list title, a text field for the currently selected * value and the actual pick list containing all possible values. * * As three different lists are needed in our font panel for * family, style and size, its quite handy to have the code * making up such a component in a separate class only once. * * As well in a separate class it is easier to implement the * special 'behaviour', i.e. when list is clicked, the value * of the text field is set accordingly and when a value is * typed in the text field, the value in the list changes * accordingly. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class TitledPickList extends JPanel implements ListSelectionListener, CaretListener, FocusListener, KeyListener { /** the chosen list entry */ private final JTextField choice; boolean ignoreTextChanges = false; /** the list having all possible entries */ private final JList optionsList; /** * constructor * * @param options the options to be selectable in this list * @param titleText the title for the pick list */ public TitledPickList(final String[] options, final String titleText) { super(new BorderLayout()); choice = new JTextField(); choice.addCaretListener(this); choice.addFocusListener(this); choice.addKeyListener(this); optionsList = new JList(options); optionsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); optionsList.addListSelectionListener(this); final JScrollPane scrollableList = new JScrollPane(optionsList); final JPanel pickListPanel = new JPanel(new BorderLayout()); pickListPanel.add(choice, BorderLayout.NORTH); pickListPanel.add(scrollableList, BorderLayout.CENTER); final JLabel title = new JLabel(); title.setText(titleText); add(title, BorderLayout.NORTH); add(pickListPanel, BorderLayout.CENTER); } /** * if the caret was updated, i.e. the user typed into * the text field, try to find a font family name starting * with what was typed so far, set the list to that family */ public void caretUpdate(final CaretEvent ce) { if (!ignoreTextChanges) { if (choice.hasFocus()) { final ListModel model = optionsList.getModel(); final String key = choice.getText().toLowerCase(); if (key != null) { int i = 0; final int modelSize = model.getSize(); String listEntry = (String) model.getElementAt(i); while (++i < modelSize && !listEntry.toLowerCase().startsWith(key)) { listEntry = (String) model.getElementAt(i); } if (i < modelSize) { optionsList.setSelectedValue(listEntry, true); } } } } } /** for FocusListener implementation, but unused here */ public void focusGained(final FocusEvent e) { } /** * put the currently selected value in the list * into the text field when user leaves field */ public void focusLost(final FocusEvent e) { updateTextFromList(); } /** * put the currently selected value in the list * into the text field when user pressed enter in field */ public void keyPressed(final KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { updateTextFromList(); } } /** for KeyListener implementation, but unused here */ public void keyReleased(final KeyEvent e) { } /** for KeyListener implementation, but unused here */ public void keyTyped(final KeyEvent e) { } /** put selected value into text field */ private void updateTextFromList() { final Object value = optionsList.getSelectedValue(); if (value != null) { choice.setText(value.toString()); } } /** * get the value selected from the pick list * * @return the selected value or null if nothing is selected */ public Object getSelection() { return optionsList.getSelectedValue(); } /** * set the value selected in the pick list * * @param value the value to be selected in the list */ public void setSelection(final Object value) { optionsList.setSelectedValue(value.toString(), true); updateTextFromList(); } /** * set the selected index in the pick list * * @param index the index of the value to be selected in the list */ public void setSelection(final int index) { optionsList.setSelectedIndex(index); updateTextFromList(); } /** * get the index of the value selected in the pick list * * @return the index of the selected value or -1 if none is selected */ public int getIndex() { return optionsList.getSelectedIndex(); } /** * if another value was picked from the list, set the * textfield according to the choice and update the * sample */ public void valueChanged(final ListSelectionEvent e) { if (optionsList.hasFocus()) { updateTextFromList(); } fireValueChanged(); } /* ------------- event handling start ------------ */ /** the listeners for TitledPickkListEvents */ private final Vector listeners = new Vector(0); /** * add an event listener. * * @param listener the event listener to add */ public void addTitledPickListListener(final TitledPickListListener listener) { listeners.addElement(listener); } /** * remove an event listener. * * @param listener the event listener to remove */ public void removeTitledPickListListener(final TitledPickListListener listener) { listeners.removeElement(listener); } /** fire a value changed event to all registered listeners */ void fireValueChanged() { final Enumeration listenerList = listeners.elements(); while (listenerList.hasMoreElements()) { ((TitledPickListListener) listenerList.nextElement()).valueChanged(new TitledPickListEvent(this)); } } /** the event object definition for ColorPanels */ class TitledPickListEvent extends EventObject { public TitledPickListEvent(final Object source) { super(source); } } /** the event listener definition for ColorPanels */ interface TitledPickListListener extends EventListener { public void valueChanged(TitledPickListEvent e); } /* ------------- event handling end ------------ */ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/ListDialog.java0100644 0000000 0000000 00000005217 12114157751 022350 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Frame; import javax.swing.text.AttributeSet; /** * A dialog for showing and manipulating list format. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class ListDialog extends DialogShell { private final ListPanel listPanel; public ListDialog(final Frame parent, final String title) { super(parent, title); // create a ListPanel and keep a reference for later use listPanel = new ListPanel(); // add to content pane of DialogShell final Container contentPane = super.getContentPane(); contentPane.add(listPanel, BorderLayout.CENTER); // cause optimal placement of all elements pack(); } /** * set the attributes this ListDialog shall represent * * @param a the set of attributes to display list attributes from */ public void setListAttributes(final AttributeSet a) { listPanel.setValue(a); } /** * get the list attributes the ListDialog currently is set to. * * @return the set of list attributes this ListDialog currently represents */ public AttributeSet getListAttributes() { return listPanel.getValue(); } /** * get the list tag currently selected in this ListDialog * * @return the list tag currently selected or null, if no list is selected */ public String getListTag() { return listPanel.getListTag(); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLEditorKitActions.java0100644 0000000 0000000 00000320166 13147564753 024362 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.util.TimerTask; import java.util.prefs.Preferences; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JEditorPane; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JToggleButton; import javax.swing.text.AttributeSet; import javax.swing.text.DefaultEditorKit; import javax.swing.text.Element; import javax.swing.text.JTextComponent; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; import javax.swing.text.StyledEditorKit; import javax.swing.text.html.CSS; import javax.swing.text.html.HTML; import javax.swing.undo.CannotRedoException; import com.lightdev.app.shtm.SHTMLEditorPane.PasteMode; import com.lightdev.app.shtm.SHTMLPanelImpl.FontFamilyPicker; import com.lightdev.app.shtm.SHTMLPanelImpl.FontSizePicker; import com.sun.demo.ElementTreePanel; import com.sun.demo.ExampleFileFilter; import de.calcom.cclib.text.FindReplaceDialog; import de.calcom.cclib.text.FindReplaceEvent; import de.calcom.cclib.text.FindReplaceListener; /** A class groupping actions. Most actions forward the operation to editor pane. */ class SHTMLEditorKitActions { /** * action to set the style */ static class SetStyleAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; private boolean ignoreActions = false; public SetStyleAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.setStyleAction); } public void actionPerformed(final ActionEvent ae) { if (!ignoreActions) { final StyleSelector styleSelector = (StyleSelector) ae.getSource(); final AttributeSet attributeSet = styleSelector.getValue(); if (attributeSet != null) { //de.calcom.cclib.html.HTMLDiag hd = new de.calcom.cclib.html.HTMLDiag(); //hd.listAttributes(a, 2); panel.getSHTMLEditorPane().applyAttributes(attributeSet, true); } panel.updateActions(); } } public void setIgnoreActions(final boolean ignore) { ignoreActions = ignore; } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } /** * append a new table col */ static class AppendTableColAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public AppendTableColAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.appendTableColAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().appendTableColumn(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * Applies a tag to the paragraph element surrounding the selection, * based on the paragraph tag previously stored in the tag selector; tag selector * is a combo box. If constructed when the tag name passed, it applies that tag. */ static class SetTagAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; private boolean ignoreActions = false; private String tag = null; public SetTagAction(final SHTMLPanelImpl panel) { this(panel, null); } public SetTagAction(final SHTMLPanelImpl panel, final String tag) { super(); this.panel = panel; this.tag = tag; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.setTagAction); } public void actionPerformed(final ActionEvent ae) { if (!ignoreActions) { if (tag != null) { panel.getSHTMLEditorPane().applyParagraphTag(tag, null); panel.updateActions(); } else { final String tagFromSelector = panel.getTagSelector().getSelectedTag(); panel.getSHTMLEditorPane().applyParagraphTag(tagFromSelector, panel.getTagSelector().getTags()); panel.updateActions(); } } } public void setIgnoreActions(final boolean ignore) { ignoreActions = ignore; } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } /** * append a new table row */ static class AppendTableRowAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public AppendTableRowAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.appendTableRowAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().appendTableRow(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /* * Created on 20.08.2006 * Copyright (C) 2006 Dimitri Polivaev */ static class BoldAction extends StyledEditorKit.BoldAction implements SHTMLAction, AttributeComponent { /** * */ private final SHTMLPanelImpl panel; public BoldAction(final SHTMLPanelImpl panel) { //Action act = new StyledEditorKit.BoldAction(); super(); this.panel = panel; putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.fontBoldAction); } /** * do the format change for the associated attribute * *

This reverses the current setting for the associated attribute

* * @param e the ActionEvent describing the cause for this action */ public void actionPerformed(final ActionEvent e) { //System.out.println("ToggleAction getValue=" + getValue() + "selectedValue=" + selectedValue); //editor.applyAttributes(getValue(), (unselectedValue == null)); super.actionPerformed(e); //if(unselectedValue != null) { if (panel.getSHTMLEditorPane() != null) { final SHTMLDocument doc = (SHTMLDocument) panel.getSHTMLEditorPane().getDocument(); if (doc != null) { final AttributeSet a = doc.getCharacterElement(panel.getSHTMLEditorPane().getSelectionStart()) .getAttributes(); final boolean isBold = StyleConstants.isBold(a); //if(a.isDefined(attributeKey)) { //Object value = a.getAttribute(attributeKey); putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, isBold ? SHTMLPanelImpl.ACTION_SELECTED : SHTMLPanelImpl.ACTION_UNSELECTED); } } /*} else { putValue(FrmMain.ACTION_SELECTED_KEY, FrmMain.ACTION_SELECTED); }*/ panel.updateActions(); } public void update() { this.setEnabled(panel.isWYSIWYGEditorActive()); } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { boolean success = false; boolean isBold = StyleConstants.isBold(a); if (a.isDefined(CSS.Attribute.FONT_WEIGHT)) { final Object value = a.getAttribute(CSS.Attribute.FONT_WEIGHT); if (value.toString().equalsIgnoreCase(StyleConstants.Bold.toString())) { isBold = true; } } if (isBold) { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_SELECTED); } else { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); } success = true; return success; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { //System.out.println("ToggleAction getValue getValue(FrmMain.ACTION_SELECTED_KEY)=" + getValue(FrmMain.ACTION_SELECTED_KEY)); final SimpleAttributeSet set = new SimpleAttributeSet(); //if(unselectedValue != null) { if (getValue(SHTMLPanelImpl.ACTION_SELECTED_KEY).toString().equals(SHTMLPanelImpl.ACTION_SELECTED)) { Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, StyleConstants.Bold.toString()); } else { Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, Util.CSS_ATTRIBUTE_NORMAL.toString()); } /*} else { Util.styleSheet().addCSSAttribute(set, (CSS.Attribute) getAttributeKey(), selectedValue.toString()); }*/ return set; } public AttributeSet getValue(final boolean includeUnchanged) { return getValue(); } } /** * Applies a text attribute. (Used to be ToggleAction.) */ static class ApplyCSSAttributeAction extends AbstractAction implements SHTMLAction, AttributeComponent { /** * */ private final SHTMLPanelImpl panel; /** the attribute this action represents values for */ Object attributeName; /** the value for the attribute being selected */ final private Object attributeValue; private final boolean applyToParagraph; /** * Constructs a ToggleAttributeAction. * @param panel TODO * @param actionName the name and command for this action * @param attributeName the name of the attribute to be modified * @param attributeValue the value the attribute should be set to * @param applyToParagraph TODO * @param uVal the value for the attribute not being selected */ public ApplyCSSAttributeAction(final SHTMLPanelImpl panel, final String actionName, final Object attributeName, final Object attributeValue, final boolean applyToParagraph) { super(); this.panel = panel; putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); this.attributeName = attributeName; this.attributeValue = attributeValue; this.applyToParagraph = applyToParagraph; SHTMLPanelImpl.configureActionProperties(this, actionName); } /** * do the format change for the associated attribute * *

This reverses the current setting for the associated attribute

* * @param ev the ActionEvent describing the cause for this action */ public void actionPerformed(final ActionEvent ev) { boolean performTheAction = false; if (ev.getSource() instanceof JToggleButton) { final JToggleButton button = (JToggleButton) ev.getSource(); performTheAction = button.isSelected(); } else { performTheAction = true; } if (performTheAction) { panel.getSHTMLEditorPane().applyAttributes(getValue(), applyToParagraph); putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_SELECTED); panel.updateActions(); } } /** * get the attribute this action represents values for * * @return the attribute this action represents values for */ public Object getAttributeName() { return attributeName; } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { boolean success = false; if (a.isDefined(attributeName)) { final Object value = a.getAttribute(attributeName); if (value.toString().equalsIgnoreCase(attributeValue.toString())) { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_SELECTED); } else { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); } success = true; } else { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); } return success; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { //System.out.println("ToggleAction getValue getValue(FrmMain.ACTION_SELECTED_KEY)=" + getValue(FrmMain.ACTION_SELECTED_KEY)); final SimpleAttributeSet attributeSet = new SimpleAttributeSet(); Util.styleSheet().addCSSAttribute(attributeSet, (CSS.Attribute) getAttributeName(), attributeValue.toString()); return attributeSet; } public AttributeSet getValue(final boolean includeUnchanged) { return getValue(); } /** update the action's state */ public void update() { this.setEnabled(panel.isWYSIWYGEditorActive()); } } /** * delete a table col */ static class DeleteTableColAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public DeleteTableColAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.deleteTableColAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().deleteTableCol(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * Action that brings up a JFrame with a JTree showing the structure * of the document in the currently active DocumentPane. * * will be hidden from menu if not in development mode (DEV_MODE = false) */ static class ShowElementTreeAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; /** a frame for showing an element tree panel */ private JFrame elementTreeFrame = null; public ShowElementTreeAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.elemTreeAction); } public void actionPerformed(final ActionEvent e) { if (elementTreeFrame == null) { final String title = Util.getResourceString("elementTreeTitle"); elementTreeFrame = new JFrame(title); elementTreeFrame.addWindowListener(new WindowAdapter() { public void windowClosing(final WindowEvent we) { elementTreeFrame.dispose(); elementTreeFrame = null; } }); final Container fContentPane = elementTreeFrame.getContentPane(); fContentPane.setLayout(new BorderLayout()); final ElementTreePanel elementTreePanel = new ElementTreePanel(panel.getSHTMLEditorPane()); fContentPane.add(elementTreePanel); elementTreeFrame.pack(); } elementTreeFrame.setVisible(true); panel.updateActions(); } public void update() { } } /** * delete a table row */ static class DeleteTableRowAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public DeleteTableRowAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.deleteTableRowAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().deleteTableRow(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * toggle list formatting for a given type of list on/off */ static class ToggleListAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; private final HTML.Tag listTag; public ToggleListAction(final SHTMLPanelImpl panel, final String name, final HTML.Tag listTag) { super(); this.panel = panel; this.listTag = listTag; SHTMLPanelImpl.configureActionProperties(this, name); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().toggleList(listTag.toString(), null, // What are the attributes good for? They break the appearance of nested numbered lists. --Dan //panel.getMaxAttributes(panel.getSHTMLEditorPane(), listTag.toString()), false); panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } /** * set the title of the currently active document */ static class DocumentTitleAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public DocumentTitleAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.documentTitleAction); } public void actionPerformed(final ActionEvent ae) { String newTitle; final String currentTitle = panel.getSHTMLDocument().getDocumentTitle(); if (currentTitle != null) { newTitle = currentTitle; } else { newTitle = ""; } newTitle = Util.nameInput(JOptionPane.getFrameForComponent(panel), newTitle, ".*", "docTitleTitle", "docTitleQuery"); if (newTitle != null && newTitle.length() > 0) { panel.getSHTMLDocument().setDocumentTitle(newTitle); } } public void update() { this.setEnabled(panel.isWYSIWYGEditorActive()); } } /* * Created on 20.08.2006 * Copyright (C) 2006 Dimitri Polivaev */ static class UnderlineAction extends StyledEditorKit.UnderlineAction implements SHTMLAction, AttributeComponent { /** * */ private final SHTMLPanelImpl panel; public UnderlineAction(final SHTMLPanelImpl panel) { //Action act = new StyledEditorKit.BoldAction(); super(); this.panel = panel; putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.fontUnderlineAction); } /** * do the format change for the associated attribute * *

This reverses the current setting for the associated attribute

* * @param e the ActionEvent describing the cause for this action */ public void actionPerformed(final ActionEvent e) { //System.out.println("ToggleAction getValue=" + getValue() + "selectedValue=" + selectedValue); //editor.applyAttributes(getValue(), (unselectedValue == null)); super.actionPerformed(e); //if(unselectedValue != null) { if (panel.getSHTMLEditorPane() != null) { final SHTMLDocument doc = (SHTMLDocument) panel.getSHTMLEditorPane().getDocument(); if (doc != null) { final AttributeSet a = doc.getCharacterElement(panel.getSHTMLEditorPane().getSelectionStart()) .getAttributes(); final boolean isUnderlined = StyleConstants.isUnderline(a); if (isUnderlined) { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_SELECTED); } else { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); } } } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { boolean success = false; boolean isUnderlined = StyleConstants.isUnderline(a); if (a.isDefined(CSS.Attribute.TEXT_DECORATION)) { final Object value = a.getAttribute(CSS.Attribute.TEXT_DECORATION); if (value.toString() .equalsIgnoreCase(Util.CSS_ATTRIBUTE_UNDERLINE /*StyleConstants.Underline.toString()*/)) { isUnderlined = true; } } if (isUnderlined) { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_SELECTED); } else { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); } success = true; return success; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet set = new SimpleAttributeSet(); if (getValue(SHTMLPanelImpl.ACTION_SELECTED_KEY).toString().equals(SHTMLPanelImpl.ACTION_SELECTED)) { Util.styleSheet().addCSSAttribute(set, CSS.Attribute.TEXT_DECORATION, Util.CSS_ATTRIBUTE_UNDERLINE); } else { Util.styleSheet().addCSSAttribute(set, CSS.Attribute.TEXT_DECORATION, Util.CSS_ATTRIBUTE_NONE); } return set; } public AttributeSet getValue(final boolean includeUnchanged) { return getValue(); } } abstract static class FontColorAction extends AbstractAction implements SHTMLAction { protected static ColorPanel hiddenColorPanel = new ColorPanel("Select Color", Color.BLACK, CSS.Attribute.COLOR); protected final SHTMLPanelImpl panel; public FontColorAction(SHTMLPanelImpl panel) { super(); this.panel = panel; } public void actionPerformed(final ActionEvent e) { final SHTMLEditorPane editorPane = panel.getSHTMLEditorPane(); if (panel.isWYSIWYGEditorActive()) { final AttributeSet color = getColor(); editorPane.applyAttributes(color, false); // apply the color setting to the editor panel.updateActions(); } } abstract protected AttributeSet getColor(); } static class FontColorByDialogAction extends FontColorAction implements SHTMLAction { public FontColorByDialogAction(final SHTMLPanelImpl panel) { super(panel); SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.fontColorAction); } protected AttributeSet getColor() { final SHTMLEditorPane editorPane = panel.getSHTMLEditorPane(); hiddenColorPanel.setValue(panel.getMaxAttributes(editorPane, null)); hiddenColorPanel.actionPerformed(null); // show the color chooser final AttributeSet color = hiddenColorPanel.getValue(); return color; } public void update() { } } static class SelectedFontColorAction extends FontColorAction implements SHTMLAction { public SelectedFontColorAction(final SHTMLPanelImpl panel) { super(panel); SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.selectedFontColorAction); } protected AttributeSet getColor() { final Color color = hiddenColorPanel.getColor(); final SimpleAttributeSet set = new SimpleAttributeSet(); final String colorRGB = "#" + Integer.toHexString(color.getRGB()).substring(2); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.COLOR, colorRGB); set.addAttribute(HTML.Attribute.COLOR, colorRGB); return set; } public void update() { } } static class FixedFontColorAction extends FontColorAction implements SHTMLAction { private Color color; public FixedFontColorAction(final SHTMLPanelImpl panel, String name, Color color) { super(panel); SHTMLPanelImpl.configureActionProperties(this, name); this.color = color; } protected AttributeSet getColor() { final SimpleAttributeSet set = new SimpleAttributeSet(); final String colorRGB = "#" + Integer.toHexString(color.getRGB()).substring(2); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.COLOR, colorRGB); set.addAttribute(HTML.Attribute.COLOR, colorRGB); return set; } public void update() { } } /** * action to edit anchors inside a document */ static class EditAnchorsAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public EditAnchorsAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.editAnchorsAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); final AnchorDialog dlg = new AnchorDialog(parent, Util.getResourceString("anchorDialogTitle"), panel.getSHTMLDocument()); Util.center(parent, dlg); dlg.setModal(true); dlg.setVisible(true); panel.updateActions(); } public void update() { this.setEnabled(panel.isWYSIWYGEditorActive()); } } /** * action to edit a link */ static class EditLinkAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public EditLinkAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.editLinkAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); final LinkDialog dialog = new LinkDialog(parent, Util.getResourceString("linkDialogTitle"), panel.getSHTMLEditorPane(), panel.getDocumentPane().getImageDir()/*, renderMode*/); if (parent != null) { Util.center(parent, dialog); } dialog.setModal(true); dialog.setVisible(true); if (dialog.getResult() == DialogShell.RESULT_OK) { // apply link here panel.getSHTMLEditorPane().setLink(dialog.getLinkText(), dialog.getHref(), dialog.getStyleName(), dialog.getLinkImage(), dialog.getLinkImageSize()); } panel.updateActions(); } public void update() { if (panel.isHtmlEditorActive()) { this.setEnabled(false); return; } if (panel.getSHTMLEditorPane() != null) { if ((panel.getSHTMLEditorPane().getSelectionEnd() > panel.getSHTMLEditorPane().getSelectionStart()) || (panel.getSHTMLEditorPane().getCurrentLinkElement() != null)) { this.setEnabled(true); } else { this.setEnabled(false); } } else { this.setEnabled(false); } } } /** * Is an action to open a hyperlink. */ static class OpenLinkAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public OpenLinkAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.openLinkAction); } public void actionPerformed(final ActionEvent ae) { final String linkURL = panel.getSHTMLEditorPane().getURLOfExistingLink(); if (linkURL != null) { panel.openHyperlink(linkURL); } panel.updateActions(); } public void update() { if (panel.isHtmlEditorActive()) { setEnabled(false); return; } if (panel.getSHTMLEditorPane() != null) { if ((panel.getSHTMLEditorPane().getSelectionEnd() > panel.getSHTMLEditorPane().getSelectionStart()) || (panel.getSHTMLEditorPane().getCurrentLinkElement() != null)) { setEnabled(true); } else { setEnabled(false); } } else { this.setEnabled(false); } } } /** * UndoAction for the edit menu */ static class UndoAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public UndoAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; setEnabled(false); SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.undoAction); } public void actionPerformed(final ActionEvent e) { if (panel.getCurrentDocumentPane().getSelectedTab() != DocumentPane.VIEW_TAB_LAYOUT) { return; } try { panel.getUndo().undo(); panel.getSHTMLEditorPane(); } catch (final Exception ex) { Util.errMsg((Component) e.getSource(), Util.getResourceString("unableToUndoError") + ex, ex); } panel.updateActions(); } public void update() { this.setEnabled(panel.isWYSIWYGEditorActive()); setEnabled(panel.isWYSIWYGEditorActive() && panel.getUndo().canUndo()); } } /** * action to change the paragraph style */ static class EditNamedStyleAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public EditNamedStyleAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.editNamedStyleAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); final ParaStyleDialog dlg = new ParaStyleDialog(parent, Util.getResourceString("namedStyleDialogTitle"), panel.getSHTMLDocument()); Util.center(parent, dlg); dlg.setModal(true); dlg.setValue(panel.getMaxAttributes(panel.getSHTMLEditorPane(), null)); dlg.setVisible(true); panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } static public class RemoveStyleAttributeAction extends AbstractAction implements SHTMLAction { final private Object[] attributes; private final SHTMLPanelImpl panel; public RemoveStyleAttributeAction(final SHTMLPanelImpl panel, String name, Object... attributes) { super(name); this.panel = panel; this.attributes = attributes; SHTMLPanelImpl.configureActionProperties(this, name); } public void actionPerformed(ActionEvent e) { if(!panel.isWYSIWYGEditorActive()){ return; } final JEditorPane editor = panel.getSHTMLEditorPane(); final int selectionStart = editor.getSelectionStart(); final int selectionEnd = editor.getSelectionEnd(); if(selectionStart == selectionEnd){ return; } for(Object attribute : attributes) SHTMLEditorKit.removeCharacterAttributes((StyledDocument) editor.getDocument(), attribute, selectionStart, selectionEnd - selectionStart); } public void update() { this.setEnabled(panel.isWYSIWYGEditorActive()); } } static class ClearFormatAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public ClearFormatAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.clearFormatAction); } /** * do the format change for the associated attribute * *

This reverses the current setting for the associated attribute

* * @param e the ActionEvent describing the cause for this action */ public void actionPerformed(final ActionEvent e) { final SHTMLEditorPane editor = panel.getSHTMLEditorPane(); if (editor != null) { if (editor.getSelectionStart() != editor.getSelectionEnd()) { editor.removeCharacterAttributes(); } else { editor.removeParagraphAttributes(); } } panel.updateActions(); } public void update() { this.setEnabled(panel.isWYSIWYGEditorActive()); } } /** * action to find and replace a given text */ static class MultipleDocFindReplaceAction extends AbstractAction implements SHTMLAction, FindReplaceListener { /** * */ private final SHTMLPanelMultipleDocImpl panel; public MultipleDocFindReplaceAction(final SHTMLPanelMultipleDocImpl panel) { super(); this.panel = panel; SHTMLPanelMultipleDocImpl.configureActionProperties(this, SHTMLPanelMultipleDocImpl.findReplaceAction); } public void actionPerformed(final ActionEvent ae) { currentTab = panel.getTabbedPaneForDocuments().getSelectedIndex(); caretPos = panel.getDocumentPane().getEditor().getCaretPosition(); if (panel.getTabbedPaneForDocuments().getTabCount() > 1) { //System.out.println("FindReplaceAction.actionPerformed with Listener"); new FindReplaceDialog(panel.getMainFrame(), panel.getSHTMLEditorPane(), this); } else { //System.out.println("FindReplaceAction.actionPerformed NO Listener"); new FindReplaceDialog(panel.getMainFrame(), panel.getSHTMLEditorPane()); } } public void update() { if (panel.isHtmlEditorActive()) { this.setEnabled(false); return; } if (panel.getTabbedPaneForDocuments().getTabCount() > 0) { this.setEnabled(true); } else { this.setEnabled(false); } } public void getNextDocument(final FindReplaceEvent e) { final FindReplaceDialog frd = (FindReplaceDialog) e.getSource(); final int tabCount = panel.getTabbedPaneForDocuments().getTabCount(); int curTab = panel.getTabbedPaneForDocuments().getSelectedIndex(); System.out.println("FindReplaceAction.getNextDocument curTab=" + curTab + ", tabCount=" + tabCount); if (++curTab < tabCount) { System.out.println("FindReplaceAction.getNextDocument next tab no=" + curTab); resumeWithNewEditor(frd, curTab); } else { frd.terminateOperation(); } } public void getFirstDocument(final FindReplaceEvent e) { final FindReplaceDialog frd = (FindReplaceDialog) e.getSource(); resumeWithNewEditor(frd, 0); } public void findReplaceTerminated(final FindReplaceEvent e) { panel.getTabbedPaneForDocuments().setSelectedIndex(currentTab); final DocumentPane docPane = (DocumentPane) panel.getTabbedPaneForDocuments().getSelectedComponent(); final JEditorPane editor = docPane.getEditor(); editor.setCaretPosition(caretPos); editor.requestFocus(); } private void resumeWithNewEditor(final FindReplaceDialog frd, final int tabNo) { panel.getTabbedPaneForDocuments().setSelectedIndex(tabNo); final DocumentPane docPane = (DocumentPane) panel.getTabbedPaneForDocuments().getComponentAt(tabNo); final JEditorPane editor = docPane.getEditor(); editor.requestFocus(); frd.setEditor(editor); frd.resumeOperation(); } private int caretPos; private int currentTab; } /** * action to find and replace a given text */ static class SingleDocFindReplaceAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SingleDocFindReplaceAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.findReplaceAction); } public void actionPerformed(final ActionEvent ae) { currentDocumentPane = panel.getDocumentPane(); if (currentDocumentPane != null) { caretPos = currentDocumentPane.getEditor().getCaretPosition(); new FindReplaceDialog(panel.getMainFrame(), panel.getSHTMLEditorPane()); } } public void update() { if (panel.isHtmlEditorActive()) { this.setEnabled(false); return; } if (panel.getDocumentPane() != null) { this.setEnabled(true); } else { this.setEnabled(false); } } public void findReplaceTerminated(final FindReplaceEvent e) { if (currentDocumentPane.isVisible()) { final JEditorPane editor = currentDocumentPane.getEditor(); editor.setCaretPosition(caretPos); editor.requestFocus(); } } private int caretPos; private DocumentPane currentDocumentPane; } /** * Show a dialog to format fonts */ static class FontAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public FontAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.fontAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); panel.getSHTMLEditorPane().requestFocus(); /** create a modal FontDialog, center and show it */ final FontDialog fd = new FontDialog(parent, Util.getResourceString("fontDialogTitle"), panel.getMaxAttributes(panel.getSHTMLEditorPane(), null)); Util.center(parent, fd); fd.setModal(true); fd.setVisible(true); /** if the user made a selection, apply it to the document */ if (fd.getResult() == FontDialog.RESULT_OK) { panel.getSHTMLEditorPane().applyAttributes(fd.getAttributes(), false); panel.updateFormatControls(); } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } /** * change a font family setting */ static class FontFamilyAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public FontFamilyAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.fontFamilyAction); } public void actionPerformed(final ActionEvent ae) { final FontFamilyPicker ffp = ((FontFamilyPicker) ae.getSource()); if (!ffp.ignore()) { panel.getSHTMLEditorPane().applyAttributes(ffp.getValue(), false); } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } /** * change a font size setting */ static class FontSizeAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public FontSizeAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.fontSizeAction); } public void actionPerformed(final ActionEvent ae) { final FontSizePicker fsp = ((FontSizePicker) ae.getSource()); if (!fsp.ignore()) { panel.getSHTMLEditorPane().applyAttributes(fsp.getValue(), false); } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } /** * change a font size setting */ static class ChangeFontSizeAction extends AbstractAction implements SHTMLAction { enum Change{INCREASE(1), DECREASE(-1); final int changeAmount; private Change(int changeAmount) { this.changeAmount = changeAmount; } }; /** * */ private final SHTMLPanelImpl panel; private Change change; ChangeFontSizeAction(final SHTMLPanelImpl panel, String name, Change change ) { super(name); this.panel = panel; this.change = change; SHTMLPanelImpl.configureActionProperties(this, name); } public void actionPerformed(final ActionEvent ae) { final SHTMLEditorPane editorPane = panel.getSHTMLEditorPane(); final AttributeSet a = panel.getMaxAttributes(editorPane, null); final int size = Util.styleSheet().getFont(a).getSize(); int index = 0; for (String availableSizeAsString : SHTMLPanelImpl.FONT_SIZES){ final Integer availableSizeAsNumber = Integer.valueOf(availableSizeAsString); if(size < availableSizeAsNumber) { setSize(change == Change.INCREASE ? index + 1 : index); return; } else if(size == availableSizeAsNumber) { setSize(index + change.changeAmount); return; } else { index++; if(index == SHTMLPanelImpl.FONT_SIZES.length && change == Change.DECREASE) { setSize(index - 1); return; } } } } private void setSize(int index) { if(index >= 0 && index < SHTMLPanelImpl.FONT_SIZES.length) { final SimpleAttributeSet set = new SimpleAttributeSet(); final String relativeSize = Integer.toString(index + 1); set.addAttribute(HTML.Attribute.SIZE, relativeSize); Util.styleSheet().addCSSAttributeFromHTML(set, CSS.Attribute.FONT_SIZE, relativeSize /*+ "pt"*/); panel.getSHTMLEditorPane().applyAttributes(set, false); panel.updateActions(); } } public void update() { this.setEnabled(panel.isWYSIWYGEditorActive()); } } static class FormatImageAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public FormatImageAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.formatImageAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); final ImageDialog dlg = new ImageDialog(parent, Util.getResourceString("imageDialogTitle"), panel .getDocumentPane().getImageDir(), (SHTMLDocument) panel.getDocumentPane().getDocument()); final Element img = panel.getSHTMLDocument().getCharacterElement( panel.getSHTMLEditorPane().getCaretPosition()); if (img.getName().equalsIgnoreCase(HTML.Tag.IMG.toString())) { Util.center(parent, dlg); dlg.setImageAttributes(img.getAttributes()); dlg.setModal(true); dlg.setVisible(true); /** if the user made a selection, apply it to the document */ if (dlg.getResult() == DialogShell.RESULT_OK) { try { panel.getSHTMLDocument().setOuterHTML(img, dlg.getImageHTML()); } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } } panel.updateActions(); } } public void update() { if (panel.isHtmlEditorActive()) { this.setEnabled(false); return; } if (panel.getSHTMLEditorPane() != null) { final Element img = panel.getSHTMLDocument().getCharacterElement( panel.getSHTMLEditorPane().getCaretPosition()); if (img.getName().equalsIgnoreCase(HTML.Tag.IMG.toString())) { this.setEnabled(true); } else { this.setEnabled(false); } } else { this.setEnabled(false); } } } /** * Change list formatting */ static class FormatListAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public FormatListAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.formatListAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); panel.getSHTMLEditorPane().requestFocus(); panel.getSHTMLEditorPane().getSelectionStart(); final ListDialog dlg = new ListDialog(parent, Util.getResourceString("listDialogTitle")); final SimpleAttributeSet set = new SimpleAttributeSet(panel.getMaxAttributes(panel.getSHTMLEditorPane(), HTML.Tag.UL.toString())); set.addAttributes(panel.getMaxAttributes(panel.getSHTMLEditorPane(), HTML.Tag.OL.toString())); dlg.setListAttributes(set); final String currentTag = dlg.getListTag(); Util.center(parent, dlg); dlg.setModal(true); dlg.setVisible(true); /** if the user made a selection, apply it to the document */ if (dlg.getResult() == DialogShell.RESULT_OK) { final AttributeSet a = dlg.getListAttributes(); final String newTag = dlg.getListTag(); if (newTag == null) { panel.getSHTMLEditorPane().toggleList(newTag, a, true); } else if (newTag.equalsIgnoreCase(currentTag)) { if (a.getAttributeCount() > 0) { panel.getSHTMLEditorPane().applyListAttributes(a); } } else { panel.getSHTMLEditorPane().toggleList(newTag, a, false); } } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } /** * action to change the paragraph style */ static class FormatParaAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public FormatParaAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.formatParaAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); final ParaStyleDialog dlg = new ParaStyleDialog(parent, Util.getResourceString("paraStyleDialogTitle")); Util.center(parent, dlg); dlg.setModal(true); //SHTMLDocument doc = (SHTMLDocument) dp.getDocument(); final int caretPosition = panel.getSHTMLEditorPane().getCaretPosition(); dlg.setValue(panel.getMaxAttributes(caretPosition)); dlg.setVisible(true); /** if the user made a selection, apply it to the document */ if (dlg.getResult() == DialogShell.RESULT_OK) { panel.getSHTMLEditorPane().applyAttributes(dlg.getValue(), true); } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } /** * format table attributes */ static class FormatTableAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public FormatTableAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.formatTableAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); final SHTMLEditorPane editor = panel.getSHTMLEditorPane(); editor.requestFocus(); editor.getSelectionStart(); final TableDialog td = new TableDialog(parent, Util.getResourceString("tableDialogTitle")); td.setTableAttributes(panel.getMaxAttributes(editor, HTML.Tag.TABLE.toString())); td.setCellAttributes(panel.getMaxAttributes(editor, HTML.Tag.TD.toString())); Util.center(parent, td); td.setModal(true); td.setVisible(true); /** if the user made a selection, apply it to the document */ if (td.getResult() == DialogShell.RESULT_OK) { final SHTMLDocument doc = (SHTMLDocument) editor.getDocument(); doc.startCompoundEdit(); AttributeSet a = td.getTableAttributes(); if (a.getAttributeCount() > 0) { editor.applyTableAttributes(a); } a = td.getCellAttributes(); if (a.getAttributeCount() > 0) { editor.applyCellAttributes(a, td.getCellRange()); } doc.endCompoundEdit(); } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * force a garbage collection. This can be helpful to find out * whether or not objects are properly disposed. * * Without forcing a garbage collection, this would happen * at random intervals so although an object might be properly * disposed, it might still be around until the next GC. * * will be hidden from menu if not in development mode (DEV_MODE = false) */ static class GarbageCollectionAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public GarbageCollectionAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.gcAction); } public void actionPerformed(final ActionEvent e) { System.gc(); panel.updateActions(); } public void update() { } } static class InsertImageAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public InsertImageAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.insertImageAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); final ImageDialog dlg = new ImageDialog(parent, Util.getResourceString("imageDialogTitle"), panel .getDocumentPane().getImageDir()); Util.center(parent, dlg); dlg.setModal(true); dlg.setVisible(true); /** if the user made a selection, apply it to the document */ if (dlg.getResult() == DialogShell.RESULT_OK) { try { panel.getSHTMLDocument().insertBeforeStart( panel.getSHTMLDocument().getCharacterElement(panel.getSHTMLEditorPane().getSelectionEnd()), dlg.getImageHTML()); } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } } panel.updateActions(); } public void update() { if (panel.isHtmlEditorActive()) { this.setEnabled(false); return; } if (panel.getSHTMLEditorPane() != null) { this.setEnabled(true); } else { this.setEnabled(false); } } } /** * insert a new table */ static class InsertTableAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public InsertTableAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.insertTableAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); Object input = null; final boolean showPopup = Util.preferenceIsTrue("table.popupBeforeInserting", "true"); if (showPopup) { input = Util.nameInput(parent, "3", "\\d+", "insertTableTitle", "insertTableMsg"); } if (input != null || !showPopup) { final int choice = input != null ? Integer.parseInt(input.toString()) : 3; if (choice > 0) { panel.getSHTMLEditorPane().insertNewTable(choice); } } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } } /** * insert a new table column */ static class InsertTableColAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public InsertTableColAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.insertTableColAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().insertTableColumn(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * insert a new table row */ static class InsertTableRowAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; private final String forcedCellName; public InsertTableRowAction(final SHTMLPanelImpl panel, final String forcedCellName, final String titleID) { super(); this.panel = panel; this.forcedCellName = forcedCellName; SHTMLPanelImpl.configureActionProperties(this, titleID); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().insertTableRow(forcedCellName); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * Move theinsert a new table row */ static class MoveTableRowUpAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public MoveTableRowUpAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.moveTableRowUpAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().moveTableRowUp(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * Moves the the table row up. */ static class MoveTableRowDownAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public MoveTableRowDownAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.moveTableRowDownAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().moveTableRowDown(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * Moves the the table column left. */ static class MoveTableColumnLeftAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public MoveTableColumnLeftAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.moveTableColumnLeftAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().moveTableColumnLeft(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * Moves the the table column right. */ static class MoveTableColumnRightAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public MoveTableColumnRightAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.moveTableColumnRightAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().moveTableColumnRight(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * Turns a table data cell into a table header cell or vice versa. */ static class ToggleTableHeaderCellAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public ToggleTableHeaderCellAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, null); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().toggleTableHeaderCell(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } static class ItalicAction extends StyledEditorKit.ItalicAction implements SHTMLAction, AttributeComponent { /** * */ private final SHTMLPanelImpl panel; public ItalicAction(final SHTMLPanelImpl panel) { //Action act = new StyledEditorKit.BoldAction(); super(); this.panel = panel; putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.fontItalicAction); } /** * do the format change for the associated attribute * *

This reverses the current setting for the associated attribute

* * @param e the ActionEvent describing the cause for this action */ public void actionPerformed(final ActionEvent e) { super.actionPerformed(e); if (panel.getSHTMLEditorPane() != null) { final SHTMLDocument doc = (SHTMLDocument) panel.getSHTMLEditorPane().getDocument(); if (doc != null) { final AttributeSet a = doc.getCharacterElement(panel.getSHTMLEditorPane().getSelectionStart()) .getAttributes(); final boolean isItalic = StyleConstants.isItalic(a); if (isItalic) { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_SELECTED); } else { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); } } } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive()); } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { boolean success = false; boolean isItalic = StyleConstants.isItalic(a); if (a.isDefined(CSS.Attribute.FONT_STYLE)) { final Object value = a.getAttribute(CSS.Attribute.FONT_STYLE); if (value.toString().equalsIgnoreCase(StyleConstants.Italic.toString())) { isItalic = true; } } if (isItalic) { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_SELECTED); } else { putValue(SHTMLPanelImpl.ACTION_SELECTED_KEY, SHTMLPanelImpl.ACTION_UNSELECTED); } success = true; return success; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet set = new SimpleAttributeSet(); if (getValue(SHTMLPanelImpl.ACTION_SELECTED_KEY).toString().equals(SHTMLPanelImpl.ACTION_SELECTED)) { Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_STYLE, Util.CSS_ATTRIBUTE_NORMAL.toString()); } else { Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_STYLE, StyleConstants.Italic.toString()); } return set; } public AttributeSet getValue(final boolean includeUnchanged) { return getValue(); } } /** * action to move to the next cell in a table */ static class NextTableCellAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public NextTableCellAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.nextTableCellAction); } public void actionPerformed(final ActionEvent ae) { final Element cell = panel.getSHTMLEditorPane().getCurrentTableCell(); if (cell != null) { panel.getSHTMLEditorPane().goNextCell(cell); panel.updateActions(); } } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * action to move to the previous cell in a table */ static class PrevTableCellAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public PrevTableCellAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.prevTableCellAction); } public void actionPerformed(final ActionEvent ae) { final Element cell = panel.getSHTMLEditorPane().getCurrentTableCell(); if (cell != null) { panel.getSHTMLEditorPane().goPrevCell(cell); panel.updateActions(); } } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getSHTMLEditorPane().getCurrentTableCell() != null); } } /** * action to move to the previous cell in a table */ static class PrintAction extends AbstractAction implements SHTMLAction { static private Method printMethod; static { Method printMethod = null; try { printMethod = JTextComponent.class.getMethod("print", new Class[] {}); } catch (final Exception e) { } PrintAction.printMethod = printMethod; } static boolean canPrint() { return printMethod != null; } private final SHTMLPanelImpl panel; public PrintAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.printAction); } public void actionPerformed(final ActionEvent e) { if (PrintAction.canPrint()) { try { printMethod.invoke(panel.getEditorPane(), new Object[] {}); } catch (final Exception ex) { ex.printStackTrace(); } } else { JOptionPane.showMessageDialog(panel, Util.getResourceString("printing_not_supported")); setEnabled(false); } } public void update() { } } /** * RedoAction for the edit menu */ static class RedoAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public RedoAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; setEnabled(false); SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.redoAction); } public void actionPerformed(final ActionEvent e) { if (panel.getCurrentDocumentPane().getSelectedTab() != DocumentPane.VIEW_TAB_LAYOUT) { return; } try { panel.getUndo().redo(); panel.getSHTMLEditorPane(); } catch (final CannotRedoException ex) { Util.errMsg((Component) e.getSource(), Util.getResourceString("unableToRedoError") + ex, ex); } panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && panel.getUndo().canRedo()); } } /** just adds a normal name to the superclasse's action */ static class SHTMLEditCopyAction extends DefaultEditorKit.CopyAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SHTMLEditCopyAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.copyAction); } public void actionPerformed(final ActionEvent e) { super.actionPerformed(e); panel.updateActions(); } public void update() { if (panel.getSHTMLEditorPane() != null) { setEnabled(true); } else { setEnabled(false); } } } /** just adds a normal name to the superclasse's action */ static class SHTMLEditCutAction extends DefaultEditorKit.CutAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SHTMLEditCutAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.cutAction); } public void actionPerformed(final ActionEvent e) { super.actionPerformed(e); panel.updateActions(); } public void update() { if (panel.getSHTMLEditorPane() != null) { setEnabled(true); } else { setEnabled(false); } } } /** just adds a normal name to the superclasse's action */ static class SHTMLEditPasteAction extends DefaultEditorKit.PasteAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SHTMLEditPasteAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.pasteAction); } public void actionPerformed(final ActionEvent e) { super.actionPerformed(e); panel.updateActions(); } public void update() { if (panel.getSHTMLEditorPane() != null) { setEnabled(true); } else { setEnabled(false); } } } /** * This action does either "Paste as HTML" or "Paste as Text", depending on default_paste_mode! * @author Felix Natter * */ static class SHTMLEditPasteOtherAction extends DefaultEditorKit.PasteAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SHTMLEditPasteOtherAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, "pasteOther"); updateActionName(PasteMode.getValueFromPrefs().invert()); } public void updateActionName(final PasteMode pm) { if (pm == PasteMode.PASTE_HTML) { putValue(Action.NAME, Util.getResourceString("pasteHTMLLabel")); } else if (pm == PasteMode.PASTE_PLAIN_TEXT) { putValue(Action.NAME, Util.getResourceString("pastePlainTextLabel")); } else { throw new RuntimeException("Unknown SHTMLEditorPane.PasteMode: " + pm.toString()); } panel.updateActions(); } public void actionPerformed(final ActionEvent e) { PasteMode pm = panel.getSHTMLEditorPane().getPasteMode().invert(); panel.getSHTMLEditorPane().setPasteMode(pm); super.actionPerformed(e); panel.updateActions(); panel.getSHTMLEditorPane().setPasteModeFromPrefs(); } public void update() { if (panel.getSHTMLEditorPane() != null) { setEnabled(true); } else { setEnabled(false); } } } static class SHTMLEditPrefsAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SHTMLEditPrefsAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.editPrefsAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); final PrefsDialog dlg = new PrefsDialog(parent, Util.getResourceString("prefsDialogTitle")); dlg.addPrefChangeListener(panel); Util.center(parent, dlg); dlg.setModal(true); dlg.setVisible(true); /** if the user made a selection, apply it to the document */ if (dlg.getResult() == DialogShell.RESULT_OK) { } panel.updateActions(); dlg.removePrefChangeListener(panel); } public void update() { } } static class SHTMLEditSelectAllAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SHTMLEditSelectAllAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.selectAllAction); } public void actionPerformed(final ActionEvent ae) { if (panel.isHtmlEditorActive()) { panel.getDocumentPane().getHtmlEditor().selectAll(); } else { panel.getSHTMLEditorPane().selectAll(); panel.updateActions(); } } public void update() { if (panel.getSHTMLEditorPane() != null) { this.setEnabled(true); } else { this.setEnabled(false); } } } /** * close a document. * *

the action takes into account whether or not a document needs to be * saved.

* *

By having the actual closing task in a separate public method of this * action, the close functionality can be shared with action 'close all' or * others that might need it.

*/ static class SHTMLFileCloseAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelMultipleDocImpl panel; private boolean exitApp = false; /** constructor * @param panel TODO*/ public SHTMLFileCloseAction(final SHTMLPanelMultipleDocImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelMultipleDocImpl.closeAction); } /** close the currently active document, if there is one */ public void actionPerformed(final ActionEvent ae) { if (panel.getSHTMLEditorPane() != null) { // if documents are open closeDocument(panel.getActiveTabNo(), ae, false); // close the active one } panel.updateActions(); } /** * close a document by its tab index. * *

The method takes care of saving the document if necessary prior * to closing.

* * @param the tab index number of the document in the tabbed pane. * @return true, if the document was closed successfully. */ public void closeDocument(final int index, final ActionEvent ae, final boolean ignoreChanges) { exitApp = ae.getActionCommand().indexOf(SHTMLPanelImpl.exitAction) > -1; final DocumentPane dp = (DocumentPane) panel.getTabbedPaneForDocuments().getComponentAt(index); if (!dp.saveInProgress()) { // if no save is going on and.. if (ignoreChanges) { closeDoc(dp); } else { if (dp.needsSaving()) { // ..the document needs to be saved panel.selectTabbedPane(index); final String docName = dp.getDocumentName(); final int choice = Util.msgChoice(JOptionPane.YES_NO_CANCEL_OPTION, "confirmClosing", "saveChangesQuery", docName, "\r\n\r\n"); switch (choice) { case JOptionPane.YES_OPTION: // if the user wanted to save if (dp.isNewDoc()) { //if the document is new panel.dynRes.getAction(SHTMLPanelMultipleDocImpl.saveAsAction).actionPerformed(ae); // 'save as' } else { // else panel.dynRes.getAction(SHTMLPanelMultipleDocImpl.saveAction).actionPerformed(ae); // 'save' } scheduleClose(dp); //..and wait until it is finshed, then close break; case JOptionPane.NO_OPTION: // if the user don't like to save closeDoc(dp); // close the document without saving break; case JOptionPane.CANCEL_OPTION: // if the user cancelled break; // do nothing } } else { // if the document does not need to be saved closeDoc(dp); // close the document } } } else { // save was going on upon close request, so scheduleClose(dp); // wait for completion, then close } } /** * schedule closing of a document. * *

This creates a Timer thread for which a * TimerTask is scheduled to peridically check * whether or not the save process for respective document commenced * successfully.

* *

If yes, Timer and TimerTask are disposed and the document * is closed. If not, the document remains open.

* * @param dp the document to close * @param index the number of the tab for that document */ private void scheduleClose(final DocumentPane dp) { final java.util.Timer timer = new java.util.Timer(); final TimerTask task = new TimerTask() { public void run() { if (!dp.saveInProgress()) { // if done with saving if (dp.saveSuccessful) { // and all went fine closeDoc(dp); // close the document this.cancel(); // dispose the task timer.cancel(); // dispose the timer } } } }; timer.schedule(task, 0, 400); // try to close every 400 milliseconds } /** * convenience method for closing a document */ private void closeDoc(final DocumentPane dp) { try { dp.deleteTempDir(); panel.unregisterDocument(); panel.getTabbedPaneForDocuments().remove(dp); } catch (final IndexOutOfBoundsException e) { // if the tabs have changed meanwhile catchCloseErr(dp); } if (exitApp) { // if the doc close was caused by a request to exit the app if (panel.getTabbedPaneForDocuments().getTabCount() == 0) { // ..and if there are no open docs System.exit(0); // exit the application } } } private void catchCloseErr(DocumentPane dp) { try { int i = panel.getTabbedPaneForDocuments().indexOfComponent(dp); // get the current tab index if (i < 0 && panel.getSHTMLEditorPane() != null) { panel.setActiveTabNo(panel.getTabbedPaneForDocuments().getSelectedIndex()); dp = (DocumentPane) panel.getTabbedPaneForDocuments().getComponentAt(panel.getActiveTabNo()); i = panel.getTabbedPaneForDocuments().indexOfComponent(dp); // get the current tab index again panel.unregisterDocument(); panel.getTabbedPaneForDocuments().remove(i); //now remove it } else { while (i > 0 && i > panel.getTabbedPaneForDocuments().getTabCount()) { // while its still wrong i = panel.getTabbedPaneForDocuments().indexOfComponent(dp); // get the current tab index again } panel.unregisterDocument(); panel.getTabbedPaneForDocuments().remove(i); //now remove it } } catch (final IndexOutOfBoundsException e) { catchCloseErr(dp); } } /** update the state of this action */ public void update() { if (panel.getSHTMLEditorPane() != null) { this.setEnabled(true); } else { this.setEnabled(false); } } } /** * close all documents currently shown. * *

This action simply loops through all open documents and uses an instance * of SHTMLFileCloseAction to perform the actual closing on each of them.

*/ static class SHTMLFileCloseAllAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelMultipleDocImpl panel; /** constructor * @param panel TODO*/ public SHTMLFileCloseAllAction(final SHTMLPanelMultipleDocImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelMultipleDocImpl.closeAllAction); } /** close all open documents */ public void actionPerformed(final ActionEvent ae) { final SHTMLFileCloseAction a = (SHTMLFileCloseAction) panel.dynRes .getAction(SHTMLPanelMultipleDocImpl.closeAction); for (int i = panel.getTabbedPaneForDocuments().getTabCount(); i > 0; i--) { //System.out.println("CloseAll, close tab no " + i); a.closeDocument(i - 1, ae, false); } panel.updateActions(); } public void update() { if (panel.getSHTMLEditorPane() != null) { this.setEnabled(true); } else { this.setEnabled(false); } } } /** * exit the application. * *

This will only exit the application, if

    *
  • no documents are open or
  • *
  • documents are open that do not need to be saved or
  • *
  • documents are open and are saved successfully prior to close or
  • *
  • documents are open for which the user explicitly opted not * to save them
  • *

*/ static class SHTMLFileExitAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelMultipleDocImpl panel; public SHTMLFileExitAction(final SHTMLPanelMultipleDocImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.exitAction); } public void actionPerformed(final ActionEvent ae) { saveRelevantPrefs(); new SHTMLFileCloseAllAction(panel).actionPerformed(ae); if (panel.getTabbedPaneForDocuments().getTabCount() == 0) { System.exit(0); } panel.updateActions(); } public void saveRelevantPrefs() { /* ---- save splitpane sizes start -------------- */ panel.savePrefs(); /* ---- save splitpane sizes end -------------- */ } public void update() { } } /** create a new empty document and show it */ static class SHTMLFileNewAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelMultipleDocImpl panel; public SHTMLFileNewAction(final SHTMLPanelMultipleDocImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelMultipleDocImpl.newAction); } /** create a new empty document and show it */ public void actionPerformed(final ActionEvent ae) { panel.createNewDocumentPane(); // create a new empty document panel.getTabbedPaneForDocuments().setSelectedComponent( // add the document to the panel.getTabbedPaneForDocuments().add(panel.getDocumentPane().getDocumentName(), panel.getDocumentPane())); // tabbed pane for display panel.registerDocument(); panel.updateActions(); } public void update() { } } /** open an existing document from file and show it */ static class SHTMLFileOpenAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelMultipleDocImpl panel; public SHTMLFileOpenAction(final SHTMLPanelMultipleDocImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelMultipleDocImpl.openAction); } public void actionPerformed(final ActionEvent ae) { final Preferences prefs = Preferences.userNodeForPackage(panel.getClass()); final JFileChooser chooser = new JFileChooser(); // create a file chooser final ExampleFileFilter filter = new ExampleFileFilter(); // create a filter filter.addExtension("htm"); filter.addExtension("html"); filter.setDescription(Util.getResourceString("htmlFileDesc")); chooser.setFileFilter(filter); // apply the file filter final String lastFileName = prefs.get(SHTMLPanelImpl.FILE_LAST_OPEN, ""); if (lastFileName.length() > 0) { chooser.setCurrentDirectory(new File(lastFileName).getParentFile()); } final int returnVal = // ..and show the file chooser chooser.showOpenDialog((Component) ae.getSource()); if (returnVal == JFileChooser.APPROVE_OPTION) { // if a file was selected final File file = chooser.getSelectedFile(); prefs.put(SHTMLPanelImpl.FILE_LAST_OPEN, file.getAbsolutePath()); openDocument(file); } panel.updateActions(); } public void openDocument(final File file) { openDocument(file, null); } public void openDocument(final File file, final DocumentPane.DocumentPaneListener listener) { int openDocNo = -1; try { openDocNo = getOpenDocument(file.toURI().toURL().toString()); } catch (final MalformedURLException mue) { } if (openDocNo > -1) { panel.getTabbedPaneForDocuments().setSelectedIndex(openDocNo); } else { final FileLoader loader = new FileLoader(file, null, listener); loader.start(); } } public int getOpenDocument(final String url) { int tabNo = -1; final int openDocCount = panel.getTabbedPaneForDocuments().getTabCount(); int i = 0; while (i < openDocCount && tabNo < 0) { final URL source = ((DocumentPane) panel.getTabbedPaneForDocuments().getComponentAt(i)).getSource(); if (source != null) { if (source.toString().equalsIgnoreCase(url)) { tabNo = i; } } i++; } return tabNo; } /** * get a FileLoader object for the document currently active * * @param url the url of the file to open */ public FileLoader createFileLoader(final URL url) { return new FileLoader(new File(url.getFile()), null); } /** * Helper class for being able to load a document in a separate thread. * Using a separate thread will not cause the application to block during * a lengthy load operation */ class FileLoader extends Thread { File file; Component owner; DocumentPane.DocumentPaneListener l; public FileLoader(final File file, final Component owner) { this.file = file; this.owner = owner; } public FileLoader(final File file, final Component owner, final DocumentPane.DocumentPaneListener listener) { this(file, owner); l = listener; } public void run() { try { JOptionPane.getFrameForComponent(panel); panel.setDocumentPane(new DocumentPane(file.toURI().toURL(), 0/*, renderMode*/)); if (l != null) { panel.getDocumentPane().addDocumentPaneListener(l); } panel.getTabbedPaneForDocuments().setSelectedComponent( panel.getTabbedPaneForDocuments().add(panel.getDocumentPane().getDocumentName(), panel.getDocumentPane())); panel.registerDocument(); } catch (final Exception e) { Util.errMsg(owner, Util.getResourceString("unableToOpenFileError"), e); } } } public void update() { } } /** save a document */ static class SHTMLFileSaveAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SHTMLFileSaveAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelMultipleDocImpl.saveAction); } public void actionPerformed(final ActionEvent ae) { if (!panel.getDocumentPane().isNewDoc()) { final FileSaver saver = new FileSaver(panel.getDocumentPane()); saver.setName("FileSaver"); saver.start(); } else { panel.dynRes.getAction(SHTMLPanelMultipleDocImpl.saveAsAction).actionPerformed(ae); } panel.updateActions(); } /** * Helper class for being able to save a document in a separate thread. * Using a separate thread will not cause the application to block during * a lengthy save operation */ class FileSaver extends Thread { DocumentPane dp; Component owner; FileSaver(final DocumentPane dp) { setPriority(Thread.MIN_PRIORITY); this.dp = dp; } public void run() { panel.doSave(dp); } } public void update() { final boolean isEnabled = panel.getSHTMLEditorPane() != null; boolean saveInProgress = false; boolean needsSaving = false; if (isEnabled) { saveInProgress = panel.getDocumentPane().saveInProgress(); needsSaving = panel.getDocumentPane().needsSaving(); } this.setEnabled(isEnabled && needsSaving && !saveInProgress); } } static class SHTMLFileSaveAllAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelMultipleDocImpl panel; public SHTMLFileSaveAllAction(final SHTMLPanelMultipleDocImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelMultipleDocImpl.saveAllAction); } public void actionPerformed(final ActionEvent ae) { final int count = panel.getTabbedPaneForDocuments().getTabCount(); for (int i = 0; i < count; i++) { panel.getTabbedPaneForDocuments().setSelectedIndex(i); panel.setDocumentPane((DocumentPane) panel.getTabbedPaneForDocuments().getSelectedComponent()); if (panel.getDocumentPane().needsSaving()) { panel.dynRes.getAction(SHTMLPanelMultipleDocImpl.saveAction).actionPerformed(ae); } } panel.updateActions(); } public void update() { if (panel.getSHTMLEditorPane() != null) { this.setEnabled(true); } else { this.setEnabled(false); } } } /** * save a document under a different name and/or location * *

If a file already exists at the chosen location / name, the method * will ask the user if the existing file shall be overwritten. */ static class SHTMLFileSaveAsAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelMultipleDocImpl panel; public SHTMLFileSaveAsAction(final SHTMLPanelMultipleDocImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelMultipleDocImpl.saveAsAction); } public void actionPerformed(final ActionEvent ae) { boolean canSave = true; final Preferences prefs = Preferences.userNodeForPackage(panel.getClass()); final JFileChooser chooser = new JFileChooser(); final ExampleFileFilter filter = new ExampleFileFilter(); filter.addExtension("htm"); filter.addExtension("html"); filter.setDescription(Util.getResourceString("htmlFileDesc")); chooser.setFileFilter(filter); final String lastSaveFileName = prefs.get(SHTMLPanelImpl.FILE_LAST_SAVE, ""); if (lastSaveFileName.length() > 0) { chooser.setCurrentDirectory(new File(lastSaveFileName).getParentFile()); } final URL sourceUrl = panel.getDocumentPane().getSource(); String fName; if (sourceUrl != null) { fName = sourceUrl.getFile(); } else { fName = panel.getDocumentPane().getDocumentName(); fName = Util.removeChar(fName, ' '); } if (fName.indexOf(Util.CLASS_SEPARATOR) < 0) { chooser.setSelectedFile(new File(fName + ".htm")); } else { chooser.setSelectedFile(new File(fName)); } final int result = chooser.showSaveDialog((Component) ae.getSource()); if (result == JFileChooser.APPROVE_OPTION) { final File selection = chooser.getSelectedFile(); prefs.put(SHTMLPanelImpl.FILE_LAST_SAVE, selection.getAbsolutePath()); if (selection.exists()) { final String newName = selection.getName(); canSave = Util.msg(JOptionPane.YES_NO_OPTION, "confirmSaveAs", "fileExistsQuery", newName, " "); } if (canSave) { try { final NewFileSaver saver = new NewFileSaver(panel.getDocumentPane(), selection.toURI().toURL(), panel.getActiveTabNo()); saver.setName("NewFileSaver"); saver.start(); } catch (final Exception ex) { Util.errMsg((Component) ae.getSource(), Util.getResourceString("cantCreateURLError") + selection.getAbsolutePath(), ex); } } } panel.updateActions(); } /** * Helper class for being able to save a document in a separate thread. * Using a separate thread will not cause the application to block during * a lengthy save operation */ class NewFileSaver extends Thread { DocumentPane dp; URL url; int activeTabNo; DocumentPane.DocumentPaneListener l; NewFileSaver(final DocumentPane dp, final URL url, final int activeTabNo) { this.dp = dp; this.url = url; this.activeTabNo = activeTabNo; } NewFileSaver(final DocumentPane dp, final URL url, final int activeTabNo, final DocumentPane.DocumentPaneListener listener) { this(dp, url, activeTabNo); l = listener; } public void run() { dp.setSource(url); panel.doSave(dp); if (dp.saveSuccessful) { panel.getTabbedPaneForDocuments().setTitleAt( panel.getTabbedPaneForDocuments().indexOfComponent(dp), dp.getDocumentName()); if (l != null) { dp.addDocumentPaneListener(l); } } } } /** * get a FileSaver object for the document currently active * * @param url the url of the file to save */ public NewFileSaver createNewFileSaver(final URL url) { return new NewFileSaver(panel.getDocumentPane(), url, panel.getActiveTabNo()); } /** * get a FileSaver object for the document currently active * * @param url the url of the file to save */ public NewFileSaver createNewFileSaver(final URL url, final DocumentPane.DocumentPaneListener listener) { return new NewFileSaver(panel.getDocumentPane(), url, panel.getActiveTabNo(), listener); } public void update() { final boolean isEnabled = panel.getSHTMLEditorPane() != null; boolean saveInProgress = false; if (isEnabled) { saveInProgress = panel.getDocumentPane().saveInProgress(); } this.setEnabled(isEnabled && !saveInProgress); } } /** * a slot for testing certain things conveniently during development */ static class SHTMLTestAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SHTMLTestAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.testAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLEditorPane().insertBreak(); } public void update() { } } /** show information about SimplyHTML in a dialog */ static class SHTMLHelpAppInfoAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SHTMLHelpAppInfoAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.aboutAction); } public void actionPerformed(final ActionEvent ae) { final Frame parent = JOptionPane.getFrameForComponent(panel); final AboutBox dlg = new AboutBox(parent); Util.center(parent, dlg); dlg.setModal(true); dlg.setVisible(true); panel.repaint(); panel.updateActions(); } public void update() { } } /** * action to set a reference to the default style sheet * (for being able to use an already existing style sheet * without having to define named styles) */ static class SetDefaultStyleRefAction extends AbstractAction implements SHTMLAction { /** * */ private final SHTMLPanelImpl panel; public SetDefaultStyleRefAction(final SHTMLPanelImpl panel) { super(); this.panel = panel; SHTMLPanelImpl.configureActionProperties(this, SHTMLPanelImpl.setDefaultStyleRefAction); } public void actionPerformed(final ActionEvent ae) { panel.getSHTMLDocument().insertStyleRef(); panel.updateActions(); } public void update() { setEnabled(panel.isWYSIWYGEditorActive() && !panel.getSHTMLDocument().hasStyleRef()); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLPlugin.java0100644 0000000 0000000 00000013536 12114157751 022366 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import javax.swing.Action; import javax.swing.JComponent; import javax.swing.JMenuItem; /** * Defines an interface all plug-ins for application SimplyHTML * have to implement. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ public interface SHTMLPlugin { /** indicates docking is not requested */ public static final int DOCK_LOCATION_NONE = 0; /** indicates docking requested on top of a given container */ public static final int DOCK_LOCATION_TOP = 1; /** indicates docking requested on the right of a given container */ public static final int DOCK_LOCATION_RIGHT = 2; /** indicates docking requested on bottom of a given container */ public static final int DOCK_LOCATION_BOTTOM = 3; /** indicates docking requested on the left of a given container */ public static final int DOCK_LOCATION_LEFT = 4; /** * get the name of the plug-in as it shall appear * on a GUI. * * @return the name of the plug-in */ public String getGUIName(); /** * get the name used internally for this plug-in * * @return the internal name of this plug-in */ public String getInternalName(); /** * get a menu of actions this plug-in provides. * *

JMenu is a decendant of JMenuItem * so this method may return a single menu item up to a whole * structure of submenus in its return value.

* * @return the plug-in menu */ public JMenuItem getPluginMenu(); /** * get a menu item providing documentation about this * plug-in. * *

JMenu is a decendant of JMenuItem * so this method may return a single menu item up to a whole * structure of submenus in its return value.

* * @return a menu item with help for this plug-in */ public JMenuItem getHelpMenu(); /** * get the location the component returned by getDockComponent() * shall be docked at. * * @return the dock location, one of DOCK_LOCATION_TOP, DOCK_LOCATION_BOTTOM, * DOCK_LOCATION.LEFT, DOCK_LOCATION_RIGHT or DOCK_LOCATION_NONE, if the * component shall not dock. */ public int getDockLocation(); /** * set the location the component returned by getDockComponent() * shall be docked at. * * @param location the dock location, one of DOCK_LOCATION_TOP, DOCK_LOCATION_BOTTOM, * DOCK_LOCATION.LEFT, DOCK_LOCATION_RIGHT or DOCK_LOCATION_NONE, if the * component shall not dock. */ public void setDockLocation(int location); /** * get the component that this plug-in produces, if any * * @return the component produced by this plug-in, or null if none * is produced */ public JComponent getComponent(); /** * get the status of the plug-in * * @return true, if activated, false if not */ public boolean isActive(); /** * set status of plug-in * * @param isActive indicates whether or not the plug-in shall be activated */ public void setStatus(boolean isActive); /** * set the owner of this plug-in * * @param owner the main frame of the instance of SimplyHTML creating the plug-in */ public void setOwner(SHTMLPanelImpl owner); /** * get the owner of this plug-in * * @return the main frame of the instance of SimplyHTML that created the plug-in */ public SHTMLPanelImpl getOwner(); /** * get a string from the resource bundle of the owner of this plug-in * * @param nm the name of the string resource to get * * @return the string with the given name or null, if none is found */ public String getOwnerResString(String nm); /** * get an action from the resource bundle of the owner of this plug-in * * @param cmd the name of the action to get * * @return the action with the given name or null, if none is found */ public Action getOwnerAction(String cmd); /** * init the plug-in * * this is called by the PluginManager directly after instantiating the plug-in * @param owner the owner of this plug-in * @param internalName the internal name this plug-in shall have * @param pluginMenuId the id of the plug-in menu in the TextResources, * or null if no plugin-in menu is to be created * @param helpMenuId the id of the help menu for this plug-in in the * TextResources, or null if no help menu is to be created */ public void initPlugin(SHTMLPanelImpl owner, String internalName, String pluginMenuId, String helpMenuId); public void initHelpMenu(); public void showInitialInfo(); } simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/0040755 0000000 0000000 00000000000 12114157751 020400 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/style.css0100644 0000000 0000000 00000015144 12114157751 022254 0ustar000000000 0000000 p.heading3 { font-size:12pt; color:#000000; text-decoration:none; font-weight:bold; font-family:Arial,Sans-Serif; text-align:left; font-style:normal; margin-top:12pt; margin-bottom:3pt; background-color:#ffffff; } p.heading2 { font-size:14pt; color:#000000; text-decoration:none; font-weight:bold; font-family:Arial,Sans-Serif; text-align:left; font-style:normal; margin-top:15pt; margin-bottom:3pt; background-color:#ffffff; } nobr { white-space:nowrap; } p.heading1 { font-size:18pt; color:#000000; text-decoration:none; font-weight:bold; font-family:Arial,Sans-Serif; text-align:left; font-style:normal; margin-top:18pt; margin-bottom:6pt; background-color:#ffffff; } ol { font-size:12pt; list-style-type:decimal; font-family:Arial,Sans-Serif; margin-top:6pt; list-style-position:outside; margin-bottom:10; margin-left:30pt; } u { text-decoration:underline; } p.table { font-size:12pt; font-family:Arial,Sans-Serif; margin-top:0pt; margin-bottom:0pt; } s { text-decoration:line-through; } p { font-size:12pt; color:#000000; font-family:Arial,Sans-Serif; margin-top:6pt; } dd p { margin-top:0; margin-left:0; margin-bottom:0; } p.code { font-size:12pt; color:#000000; text-decoration:none; font-family:Monospaced; font-weight:normal; text-align:left; font-style:normal; margin-top:6pt; background-color:#ffffff; } ol li p { margin-top:0; margin-bottom:0; } address { color:blue; font-style:italic; } i { font-style:italic; } p.standard_bold { font-size:12pt; color:#000000; text-decoration:none; font-family:Arial,Sans-Serif; font-weight:bold; text-align:left; font-style:normal; background-color:#ffffff; } h6 { font-size:xx-small; font-weight:bold; margin-top:10; margin-bottom:10; } h5 { font-size:x-small; font-weight:bold; margin-top:10; margin-bottom:10; } h4 { font-size:small; font-weight:bold; margin-top:10; margin-bottom:10; } h3 { font-size:medium; font-weight:bold; margin-top:10; margin-bottom:10; } dir li p { margin-top:0; margin-bottom:0; } h2 { font-size:large; font-weight:bold; margin-top:10; margin-bottom:10; } b { font-weight:bold; } h1 { font-size:x-large; font-weight:bold; margin-top:10; margin-bottom:10; } caption { caption-side:top; text-align:center; } a { color:blue; text-decoration:underline; } ul li ul li ul li { margin-right:0; margin-top:0; margin-left:0; margin-bottom:0; } p.standard { font-family:Arial,Sans-Serif; font-size:12pt; background-color:#ffffff; margin-top:6pt; color:#000000; } menu { margin-top:10; margin-left:40; margin-bottom:10; } menu li p { margin-top:0; margin-bottom:0; } sup { vertical-align:sup; } body { font-size:14pt; color:black; font-family:Serif; font-weight:normal; margin-right:0; margin-left:0; } ul li ul li ul { list-style-type:square; margin-left:25; } blockquote { margin-right:35; margin-top:5; margin-bottom:5; margin-left:35; } samp { font-size:small; font-family:Monospaced; } cite { font-style:italic; } sub { vertical-align:sub; } em { font-style:italic; } ul li p { margin-top:0; margin-bottom:0; } ul li ul li { margin-right:0; margin-top:0; margin-left:0; margin-bottom:0; } var { font-weight:bold; font-style:italic; } table { width:80%; border-color:Gray; border-style:outset; } dfn { font-style:italic; } menu li { margin-right:0; margin-top:0; margin-left:0; margin-bottom:0; } strong { font-weight:bold; } ul { font-size:12pt; list-style-type:disc; font-family:Arial,Sans-Serif; margin-top:6pt; list-style-position:outside; margin-bottom:10; margin-left:30pt; } center { text-align:center; } ul li ul { list-style-type:circle; margin-left:25; } kbd { font-size:small; font-family:Monospaced; } dir li { margin-right:0; margin-top:0; margin-left:0; margin-bottom:0; } ul li menu { list-style-type:circle; margin-left:25; } dt { margin-top:0; margin-bottom:0; } ol li { margin-right:0; margin-top:0; margin-left:0; margin-bottom:0; } li p { margin-top:0; margin-bottom:0; } strike { text-decoration:line-through; } dl { margin-top:10; margin-left:0; margin-bottom:10; } tt { font-family:Monospaced; } ul li { margin-right:0; margin-top:0; margin-left:0; margin-bottom:0; } dir { margin-top:10; margin-left:40; margin-bottom:10; } tr { text-align:left; } pre p { margin-top:0; } dd { margin-top:0; margin-left:40; margin-bottom:0; } li { margin-top:0; } p.image_subtitle { font-size:12pt; color:#000000; text-decoration:none; font-family:Arial,Sans-Serif; font-weight:normal; text-align:center; font-style:italic; margin-top:6pt; background-color:#ffffff; } th { padding-bottom:3; border-color:Gray; padding-right:3; font-weight:bold; border-style:inset; padding-left:3; text-align:center; padding-top:3; } pre { font-family:Monospaced; margin-top:5; margin-bottom:5; } td { margin-top:1pt; padding-left:4pt; text-align:left; padding-top:0pt; margin-right:1pt; border-right-width:0pt; margin-left:1pt; border-top-width:0pt; padding-right:4pt; border-color:Gray; border-bottom-width:0pt; border-left-width:0pt; background-color:#dcdcdb; border-style:solid; margin-bottom:1pt; vertical-align:top; padding-bottom:2pt; } code { font-size:small; font-family:Monospaced; } small { font-size:x-small; } big { font-size:x-large; } simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/help.jhm0100644 0000000 0000000 00000023543 12114157751 022034 0ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic73.htm0100644 0000000 0000000 00000012532 12114157751 022402 0ustar000000000 0000000

Tutorial

Stage 11

April 27, 2003

Ulrich Hilger

info@lightdev.com

http://www.lightdev.com

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/images/0040755 0000000 0000000 00000000000 12114157751 021645 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/images/optDlg.jpg0100644 0000000 0000000 00000021323 12114157751 023576 0ustar000000000 0000000 ÿØÿàJFIF``ÿÛC   '0"$'92<;8276?GZL?CUD67NkOU]`efe=KownbvZceaÿÛC..aA7AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaÿÀÝû"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?××õÓ¢›p-–a*žàc}ŽzÖGü'-ÿ@äÿ¾ÇÿVüQ<Ú®•-ÔK$puaUÆqœã©Úîku¦Gö­ÑÞT14Q®e#Œqƒ»=0¿ˆÚ*«™ù›Ð¥ÿ Ãã?ÙË]ãÿ‰¥7cÿ0ôÿ¾Çÿ\ÕëÝw–Í«P$vÞß{ ù@ãÐóŽ+¯±w]kA²V"Òm9ZHýÛ’ŽI+ÐäÕòB×hw²eqãV?óOûìñ4£Æÿ> ÿ}þ&ªiºݶ–§߉¾pãle3—?˜«’XÀɇ̶uÑ|÷0°A)ôaŽsÎ}xôå¸SÚÄÞB1ÿ—ÿ¾ÇÿJ<`Çþ\SþúüM1¼;h.§¶Y'ßk-ºHå†$>\gÔÒK¢X™¯<¤½6’ ¢Gl.K¯ûR奨/"aâæ?òäŸ÷ÐÿâiG‹ÿË’ßCÿ‰¬mVÑ,¯q 6VÈ`Ùî¬8e=ŽÓŠª*Õ(5{ žGJn1@ÒõÏIÖy¿ëÇßþ÷^¾ýk°¢o.È=š8ù4}Zy “DòHÝYåROãšA¡ê?óïÿ¯ø×cE?o öh䉨Ͽþ>¿ãN6¡ÿ>ÿøúÿtZòéÖ†áк‚â±ÿá0µÿŸy?:j­G²„Vì€h÷ÿóÃÿ_ñ§ "ûþxãëþ5/ü&¿óï'çGü&¿óï'çOž¯ò‹–=Æ *÷þxÿãëþ4ñ¥ÞÏüyÆøL-çÞOÎøL-çÞOÎŽz½ƒ–=Ç 2óþxÿãÃüiÃN»ÿž_øðÿþ _ù÷“ó§Eâ¸&™"ŽÚFw`ª7’xsÔì±îJ4û¯ùåÿñ§ Ÿùçÿñ«þv£ÿ@‰¿ïâÿv£ÿ@‰¿ïâÿG¶ýš) ùçÿ p³¸ÿž¨«ös½ÊLd„ÂÐÈ#e,çíøTô{i³F`´ŸûŸ¨§ Y¿¹úŠÑ¢—¶{4PòÿwõñŸÝýE\¢m öhª!“û¿­8FþŸ­X¢m öh¯"ÜyL°H±³pY—wzdzTF¥ÿ?Ñÿßþ5~Š—;êÑJ6Ù” ³¸[Áqqp²…íà}}«“Öä-sþù®î¸MgþB×?ïšW¸íc¾—ï÷Wù ©ov²ÛI<˜‰#y‹7#É?ðÕ¹~øÿu¬¡¦Îa¸µ’æ3i9—r¬$H–<6â8ÝýÚ‘“N×amÒ[BâCœãFãÐô¡¦Ç©Á%Ɉ”ª<å™™ÆÝ¸È#aϧ9Æ DÚlï0º{˜ÍÚ•ØË €ŽWvOúÆþ!ÛÐå Ñü‹µºYÿ|1“³åœuû¤ÉÀÏTœâ€-Ü_[Û8I\ƒÇÌz± 8<œ¡¦.©dÎè'aef`B©\î'Î9éÍWÕ´q©î "(hÌè<õU];Y–æâO²¤AˆŠàÊê[¢• 9=1×< Õ«KI¢ºšæâàJò¢&=Š»K9'ø»“Î~‚6Óôh´÷pâ4w2d1L•î ^Fzw  §k°6é $-as Æ3”pê:ŽãÔRÚÞ¥ÔÓ" ª6׿W³žƒï®3T[AŒ¤gm‘t,vµ ò~m¹Â~AÉ'©öÅÛ+#dÒ4nAÛ°0ˆƒ¦ðƒ¿lrGÅò÷…r¿Øîd½U™JÛŠÿ­YÃbˆH<õ^Ç#ªñ_ü_ýá\¬š»“§É/™hVC¸p]B¨ïÓliø–ö®ª³±•MÈΕ)ŽgšCìÀZMÅsÝBŸO˜ƒ‚9‰ôÛ´–8Ú,3ç0‘Ë9Â9 àס¯oà“d¾R][Hœ ÞTJÊ3ÏÞ ¯¶sÒ«Yßż:¿ÊÓ‡ HÕ29䌎;sé½äg Ù´‹ˆžùwË ™·2ª ÞÊâqƒ€AÎáŽÙˆé·bX£ò¾i¥ò£ù†¾SÁÎ1‡RBZ¸×öEÙ~а}—ìí!U,œdÜ sÀÆxÏSŽOít_µ*#mxR8ËIXÌY<ñ”g8ÁÛÉ伇¡J ‹ˆÖHÑ|¶,7³ª¨Û·9$€>òõî@©l!’ rÒ9Wk ãïFAÁràŠµ.®—‡Ÿ!xÐÄTí\nf26QŽw³`06ž£˜m¤ŠoÚ¼ ± ññ€2r2p898 àt¡·gpÐîu}N=:bf†fFc‡@ÎzuëK§ß&¡ –8¥Hó€dnú`Õ«Èc¸ó¢™ÆÄ‚Ö‘UQB¢…U +Î: º_ÜÔ?ëïÿenªisPÿ¯¿ý”Uº(¢Š(¢Š(¢Š(¢Š+„Öä-sþù®î¸MgþB×?ïšhßK÷Çû«ü…2Ÿ/ßî¯òÊ@QEQEQEQECuio{•tŒñg%CmÏãT¿áÑ?çÅÿïûVÔšÙƒI™ŸðèŸóâÿ÷ý¨ÿ„oDÿŸÿ¿íZtSö’î.UØÌÿ„oDÿŸÿ¿íGü#z'üø¿ýÿjÓ¢i.áÊ»ŸðèŸóâÿ÷ý©Éáí7WK9”‚² hÑG<»‡*ìTþÉÓ¿ç„ßøÔdéßóÂoüj·EMÆ2Þ {HZ+hÙ›yÜå²qŽôú( Š( Š( Š( Š( ¸MgþB×?ïšîë„Öä-sþù¦ô¿|º¿ÈUwI¤µ;.rÉU„YØHàòyÆ}*Ä¿|º¿ÈTiþ¦/úäŸú¤ ¿Ô&?*…Á0F2ÊÜ}öŸ;9àùcÏ55y.fÒeyn2{˜Ô×åTI¸É“·Œqï’zz( í×GSHã»…¢ ¢³Ó¡U%„%º·Ì(ÛÈÀ9´ëtÚÝÇÙ¦†?ôhwy‘ÏÍ&1†­:(ŸÓ,í^]V¶…¤6Ë”–_'iϨì{UO´;N¶g&¶ŸvîX;Ç´UÉ”Ûbþ=u™ ^ZÍ¥ÙAÌ2L–ɺ5pYp ŽÜÖN¬Rf”ɺY#ÕNÕŽÀŒŽXþ¾­ž¦ŠÂŠúõµ •§…B´ ÁÌŽŠ»¶·–«»œ)ÉlÜrÃôk—¸ICÝ „ ÊÊëŸgP öà€AÏPEiQ@Ü¥ãI*5Êð4›˜gÉ`Ê6ä 7 —ÛÁ<›_J™îl÷5Ï™†#ÌÚ£. çèFA«´Pl?óØ߯þʇþ{ûõÿÙRÑ@ °ÿÏaÿ~¿û*6ùì?ï×ÿeKE&Ãÿ=‡ýúÿì¨Øç°ÿ¿_ý•-›üö÷ëÿ²¡yI9'wltb?¥-"©O«ÿèm@ EPEPEPEP\&³ÿ!kŸ÷ÍwuÂk?ò¹ÿ|Ó@Îú_¾?Ý_ä*4ÿSýrOýT’ýñþêÿ!P—Ú£œáaRqþè¤謕•[4ºÔ'ŠêñD¬(<´ ÷ÉÉãÓ©×1´Jªí½‚€ÍŒdŽ ÇÔ,QYPk]Ef-ž ..G0‰Æc>[78õéßð¦fX¢Š[«THä¹k|Ç)}»w†c•Q³?LžØ  z+5uu}TÙ$$€êžnx$¬Œqëƒß®}9¿3:DLQùÑWv>ç°õëô=(ôUK+Óujó,aŠ’áÒN3ò1À=qÛGlÒÚÜË$òÁqG4j¯û·.¥X°9ÊžÞ”jФšœF™ÕÂMóBGtÀù¶¨$QœŠž[¸"&/¹<°\¾F~P¹'Žxì3@QQ[\ÃwšÞA$dz?j+›Ô¶¹Ž)Fãy7u9 €;’_Œ};Ъ*¼ÖóºÇììdea·È##ï/^Ädz¤¦0’–*ÛhÜP[äpHÜC0 MÍ"6p—=pOO§ôâ‡bíšmJ?Ê·Ó¢ósö,s·ïâ6O^>ö{ÓJI`He}ñ‰å•†1¸H$zñþ³¯µhQ@F–9bš¤&Öfx‹oÞîlÉ”œvÅhùw×kN‚~¾bG…κIã±ç×všŠ©¬ð¤¬'ŒÏ3—‘¼£·;Œ.캽Îyéž >Ö{Tu¸ž9™ŽâëFfî[,sÛ¦ŒnŠÇþÇ2ÚÚÅ*@^Í gˆK‹…ù‚ä~QߘsÁ©¯-¦Š o²©‘­ŠùJ±§k),7(# Æ¨â´¨  Z]¼öö¨’¶Iiƒ[,å†Hã8'8ÏN1M¥4Ï!’ä°ÃÃ.pò¿?2å@Ç.G=kJŠÈA‰m%1$þn0Š ¨GPèWpõ³É©.ôqu|·&DdŽPZ Î¥H;CŠqÐw$çŠÓ¢€3dÒ]6ÒÊIŽÈc2ŒSGǧÞÏ~•RëBca8„[,Å\ª[Ûˆ•Ï–èSÎ_©8ã äÖí™q§Îð]#KçKu¶2m o˜Œòß1é€N8Q“ZtQ@"©O«ÿèmKHŸêSêÿúPÑEQEQEQEW ¬ÿÈZçýó]ÝpšÏü…®ß4Ð3¾—ï÷Wù ?ÔÅÿ\“ÿA$¿|º¿ÈT-¿ì‰åýÿ%vývŒR5 H¦Ép‹!ãü*À!€ ‚ Š›D†Üh¨‰BÈ™˜2ýæ#æÎzÿ,cb›*¢XÔ*ª€vý1C@2ŠÀ´šú{MnX¦4‰pÅäÌ.yùF=zœ>µ›‹k8fµ–ååûlÀ£Jò?;'¨P?#ÉÒQ\ü—ë¨Ægò%‘|¸Áùvm¸û†Ø>ã®l_Ï%µ”²Ã’@0€!nIÀ$HÉÇ8€,QYúÆ{-$Ò2Ï*ï™J±FÇP;c ã§ÅhPEPEPEPEPEPHŸêSêÿúRÒ'ú”ú¿þ†Ô´QEQEQEQEÂk?ò¹ÿ|×w\&³ÿ!kŸ÷Í4 ï¥ûãýÕþB£Oõ1×$ÿÐEI/ßî¯òîÆ Sd"ƒû¦ê”€”Hàc=:2GãL$““É4™oùå7ýúoð£-ÿ<¦ÿ¿MþÑ J±ªÆbÿVŒ'ãÓ‚E†ÜFƒk_”pÇ9#ÜäóîiÙoùå7ýúoð£-ÿ<¦ÿ¿Mþ–r I- tP +Fg§lœ}MK 1[Ä"‚4Š5舡@ü;-ÿ<¦ÿ¿Mþe¿ç”ß÷é¿Â€cE 'c’rOæsN¤ËÏ)¿ïÓ…oùå7ýúo𠢓-ÿ<¦ÿ¿Mþe¿ç”ß÷é¿Â€ŠL·üò›þý7øQ–ÿžSߦÿ Z)2ßóÊoûôßáF[þyMÿ~›ü(h¤ËÏ)¿ïÓ…oùå7ýúo𠢓-ÿ<¦ÿ¿Mþe¿ç”ß÷é¿Â€‘?Ô§Õÿô6£-ÿ<¦ÿ¿Mþª¬± ee?1à ¼ÔQEQEQEQEW ¬ÿÈZçýó]ÝpšÏü…®ß4Ð3¾—ï÷Wù eG¨Ü Kwœ®àˆ¤€qžGOþ2ßøÿÆ(¦³ \‡ YÃ0Âc9ÉéÚ„ue0ŒƒžAÁ˜Å:Šj:È2ŒFAÏ àÌb@Q@×uŒeØ($ “ŽIÀ™Å:€ (¢€ (¦»¬c.ÁA dœrNüÎ(ÔQESh£eY$Dfû¡˜yÄøŠTue0ŒƒžAÁ˜Å:ŠkºÆ2ìIÇ$àÌâ@Ôu‘ã`ÈÀe9zŠÖ1—` 2N9'~gê)¨ë"+ÆÁ‘€*ÊrõPá ä<1“ú΀E5T¨f¹Â‚~ñÁ8€'ð§PE5Ýcv $ã’pæqN Š)¨ë"+ÆÁ‘€*Êrõêá5Ÿù \ÿ¾k»®YÿµÏûæšv:ê³é³ª)fhÔI8¿æÇÿ=þú!+ *H;W‘ôžlŸóÑÿï£EÀ­âit«ÄK³ÛÊ(ÉbwcŠc¿eÔ%/x(ä{t€gÍ·âãg# ñ] 31ËO¹¤¤V’“Çs:β*™¢à…Á•‰Èõû¤Ô My#ÔR-»ÜµÌ“Ü"«œª®%)Áà Á0O¨Ç»EaZ­ÜPK:Ms2ÂË ¢•7à0uF.Çi.à¿íU‹u¹H®æ,±+:“‡fP»sŽFï1±Û1㎭‡¨$ò^Vé˜]@c É „“Ûï?Þéü5}·ÉM¿lßå¯ÛwoûÛÓvÌÿ³æÿ«öÇð×AEdE#Á<2Ä—ÒZí‘$ X±)·†ù€áù|cž@"ªXC¨H–þ|׫½ãŽ@IO³‚ÝFF\`ž çsÑQ@í¯ö”·6Ò\Ë:HD'`ðWjïÉ #ïûÃpíü5Ö·?`°óô»£Š[šBÁ–H»/ …/Àäã'$fºz(*(Ö_&é-Á‚¨íÔ” ñ¸aœå°_C¨5ËÅi1U”UØœ#*•Ûœp7ymŽø“N¹Ë®ã¼Iš-ÊYÇ#m³JÇ䌎Qƒ‘“'$‘ÛÓÏù:„—ˆ·!– ,N7&ÕÞK+ˆÔç\°ìOËZ:yz%š‘YbPË `Á±Èù¹ëŸltã~Š+„Öä-sþù®î¸MgþB×?ïšhßK÷Çû«ü…2Ÿ/ßî¯òÊ@QESÀAf I$pqéíïFcþãÿßCü(”Só÷þúáFcþãÿßCü(”Só÷þúáFcþãÿßCü(”T qæ_Ü@jD¨A'$“œÿ!VØF¤¬NûÞßJŽŠ~cþãÿßCü(ÌÜûè…2Š~cþãÿßCü(ÌÜûè…2Š~cþãÿßCü(ÌÜûè…2Š~cþãÿßCü*ª\y—÷Ú‘*IÉ$ç?ÈPôTŒ#RV'ýïo¥&cþãÿßCü(”Só÷þúáFcþãÿßCü(”Só÷þúáFcþãÿßCü(”T:…ÊÚØM:FK¢’¡›ŒûñSPEP\&³ÿ!kŸ÷ÍwuÂk?ò¹ÿ|Ó@Îú_¾?Ý_ä)”ù~øÿu¦R¢Š(çýJÿ¼¦SÏú•ÿxÿ!L  ÷7 ¤0F$ž@YU›jíÉ'Ô9#¶HªÚÄ0¹[  òÑÚb[! ìàqódHïÐc< W6í#¤ÐH#ž0UY—rí8È##Ð9¶A®ºLMt·&}² 7F0Ìá vSn=$òHØYÞ ÒÇå±çfì;gßqùž´úŠÚ&‚Ý"yL¥ÝíÕ€éŸSާ¹çŽ•-Q¶ÿ½÷û‘&­~øÿu¬ëoù ß¹òjÑ—ï÷Wù eT’îc4‰kn&%&M‡8jŒrpAäÈç®-ÕI-&Hö·1PcÞs€7)ÏA:ä“S´ˆÈR¢0Å›cm;A,cŒ'ƒèh¡Bë)lTbÏœà¨, d¡ª~SI'žŠÍæaü\‡F\3g$ ÃŒŽx"ÝΞÒÞý®)‚J¡6MÊ ‰ÈÈÎD‡¸ä¥HÚ¢…&S†$#ƒ$eøù9|Øè} Kiqö˜ZM»q$‘ã9û®W?Ž3T.toµJ²ÌðK!@’4¶Êø“òdá~ñ뻢ç89¿ioöhZ=Û³$’gûÎ[†q@Uoù ß¹òj½Tm¿ä/}þä_ɨF_¾?Ý_ä)”ù~øÿu¦PeÖ¹kh/–Y!Y­s¶&”+Kòäã¿J’ëV··Šá—.ð£°YUÊ‚J‡Æ àð2F¡§M§ù¶úŒ^n>ÛžvýÌÆ©ëÏÝÏj©.›¦󄸓ìàÊ †Î_<¸ð<ž¹½£k<æåÜá™Ê@ܤåsŒnàœuÇ=9«U™e§Î²+Ï.;™¦HöŒ‚Ìà|ÀýÝ­œc9={VQÖÿäsþçõ«ÕG[ÿEÏûŸÖ¯PEP\&³ÿ!kŸ÷ÍwuÂk?ò¹ÿ|Ó@Îú_¾?Ý_ä)•Ì?‹'fȶŒ€n=)¿ð•Oÿ>Ñþf•€êh®[þ©ÿçÚ?ÌÑÿ Tÿóíæh°Ê]I´Ar É Ÿ“õϵAömGþ‚ÿà8ÿÄÿ„ªùöó4ÂU?üûGùšvoìÚý#ÿÀqþ4}›Qÿ „ø?Ʊ?á*Ÿþ}£üÍð•Oÿ>Ñþf€6þͨÿÐB?üãGÙµúGÿ€ãükþ©ÿçÚ?ÌÑÿ TÿóíæhzÎÒX'ši§¼¡FBmÆ3î}hš+ù%fKØÑzò3õÝX?ð•Oÿ>ÑþføJ§ÿŸhÿ3E€Ûû6£ÿAÿðfÔè!þñ¬OøJ§ÿŸhÿ3Gü%SÿÏ´™  ¿³j?ôÿÇøÑömGþ‚ÿà8ÿÄÿ„ªùöó4ÂU?üûGùšÛû6£ÿAÿðfÔè!þñ¬OøJ§ÿŸhÿ3Gü%SÿÏ´™  ¿³j?ôÿÇøÓìí%‚y¦šq+Êd&Üc>çÖ°á*Ÿþ}£üÍð•Oÿ>Ñþf‹½4WòJÌ—±¢ô äg뺙ömGþ‚ÿà8ÿÄÿ„ªùöó4ÂU?üûGùš,ßÙµúGÿ€ãühû6£ÿAÿðbÂU?üûGùš?á*Ÿþ}£üÍmý›Qÿ „ø?Ƴj?ôÿÇøÖ'ü%SÿÏ´™£þ©ÿçÚ?ÌнŅíÌ ·èQÆcÿf­*å¿á*Ÿþ}£üÍð•Oÿ>Ñþf‹ÔÑ\·ü%SÿÏ´™£þ©ÿçÚ?ÌÒ°MpšÏü…®ß5¥ÿ TÿóíækîàÝ]I9P¥ÎH© ?ÿÙsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/images/anchrDlg.jpg0100644 0000000 0000000 00000062063 12114157751 024075 0ustar000000000 0000000 ÿØÿàJFIF``ÿÛC 2!=,.$2I@LKG@FEPZsbPUmVEFdˆemw{‚N`—Œ}–s~|ÿÛC;!!;|SFS||||||||||||||||||||||||||||||||||||||||||||||||||ÿÀL™"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?»­kI0Ê$_aŒì}k/þÿŸ$ÿ¾‡ÿVüA40jzd—Q¬Œ‡ 20UFqƒœg?…I¬é6×6 ö(áGiÆÑƹ01Ž0séÇã]1TÔS’ÜZÜ£ÿ sã?a\tÎáÿÄÒÂ`ßóäŸ÷ÐÿâkúKåÅsnк Qæ)ÎÞØÏAÇóúV¶¹rÑYXD——1–°‹÷)þ­ÁÈ9;½=JÕS†—Žþ`ü™'ü& ÿ>Iÿ}þ&øLþ|“þúüM:ÂÊ .ù4úCÎÎX¿„.3éÎ 5û(-tÛ­Þdó-ê¢Ï+&<¥8-Œ‘׎;zRå¥{XZÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰¬ýKŠñ·d¤/:ÀŒ$ÚKžÀmlñޏþš1øvËu­´’Ü›‰'Œ:í¾Yaœc=‡õæ›…ìÐj'ü& ÿ>Iÿ}þ&øLþ|“þúüMQ¸Ó-­í¢]·RÏ%˜ºßW'¡\d?‹?…lkZU¶¥ªÞ´rKñIn’ÁS¿ ÀëÀÁëê=èp¤žÁ©WþÿŸ$ÿ¾‡ÿGü& ÿ>Iÿ}þ&–ßÃÖW7*¨÷ ÞIjັm¨X0;F:tÁªi–Ói]¢Ý öUÀ (# Þ:†`F>\QÉG°j^ÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰®fŠÓØSì+³¦ÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰®fŠ=…>Ávtßð˜7üù'ýô?øš?á0oùòOûèñ5ÌÑG°§Ø.ΛþÿŸ$ÿ¾‡ÿGü& ÿ>Iÿ}þ&¹š(öûÙÓÂ`ßóäŸ÷Ðÿâhÿ„Á¿çÉ?ï¡ÿÄ×3EŸ`»:oøLþ|“þúüMð˜7üù'ýô?øšæh£ØSìgMÿ ƒÏ’ßCÿ‰£þÿŸ$ÿ¾‡ÿ\Í{ }‚ìé¿á0oùòOûèñ4Â`ßóäŸ÷Ðÿâk™¢aO°]7ü& ÿ>Iÿ}þ&øLþ|“þúüMs4Qì)ö ³¦ÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰®fŠ=…>Ávtßð˜7üù'ýô?øš?á0oùòOûèñ5ÌÑG°§Ø.ΛþÿŸ$ÿ¾‡ÿGü& ÿ>Iÿ}þ&¹š(öûÙÓÂ`ßóäŸ÷Ðÿâhÿ„Á¿çÉ?ï¡ÿÄ×3EŸ`»:oøLþ|“þúüMð˜7üù'ýô?øšæh£ØSìgMÿ ƒÏ’ßCÿ‰£þÿŸ$ÿ¾‡ÿ\Í{ }‚ìé¿á0oùòOûèñ4Â`ßóäŸ÷Ðÿâk™¢aO°]7ü& ÿ>Iÿ}þ&øLþ|“þúüMs4Qì)ö ³¦ÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰®fŠ=…>Ávv–zÍýü&km27Œ1\™£^pp=EOöÝ[þQàLUGÃ{°¥ójyϹ·mÀÚœç·Ö™gq3Ü[­ÌÓ =ÇìÒ0Úg=ƒœút Ý}«Ž|±“I~æR-ìMqusg5ªA$Q%][;ïë\%uv¿ò1j_õÉ¿öZå*'m,3µñE…ÍóÛ}š=ûîù€ÆBúŸjϳ·ñ Šl¶Ü©ÙK£ôÉãð®ªrbÚ¼Ÿ ¨$¹Š5Ü_<à‰>Àr{þUq¬Ôyl¬+•Æ‘«ÜÌóO yå˜È¼þµ/Øuÿ'ÉßqåmÙ³ínÜcÝÒºµ(0å° 7* ¡ˆ Hã9š~ºygXpG–'pzŒg×Ó5¹<ß0Jþn<ÍÓƒ¿3Ï8®ŸíÖåK'[=ÉGJeÆ¥ï(mÄ!`¸#¦x')$Ï~:Ñõ‰vAc›·Ó5»]ßfû¼¹ÂçëƒNþÏ×w£æ}èX«yã*[ïÏïë]L×Á·~â[¢¢'ßqQ­ý³I嬡› d@Èdôç#½¨úÄ» ±Ìfkfû6%ò?ç—ž6õÏLã¯4>™­Éæù‚WóqænœøéžyÅt’êP$eÃd^XKX0@Îr8éëOûtCeÉ$‚6.1ê¸Èê;w¢¬K² ÛXëï"HïpÒGŒn+ž¸;¸¨×IÖVRA ´ba´ŸR3ŽÃò® _Ûáe °ÛA=qŽs‘^Ý*Hî"‘7«`‚ úy}b]Xã¿°u/ùöÿÈ‹þ4`ê_óíÿ‘ük³ócÿž‰ÿ} <Øÿç¢ßBŸÖgÙŽ3ûRÿŸoüˆ¿ãGö¥ÿ>ßùÆ»?6?ùèŸ÷УÍþz'ýô(úÌû ±Æ`ê_óíÿ‘ühþÁÔ¿çÛÿ"/ø×gæÇÿ=þúy±ÿÏDÿ¾…YŸd8ÏìKþ}¿ò"ÿØ:—üûäEÿìüØÿç¢ßBœ¬® R¸9ÅYŸd8¯ìKþ}¿ò"ÿØ:—üûäEÿìüÄÎ7®zc5RÞýš(e¹!ŽeÜŒ$ÜË» 1À'Óƒí“ë3ì‚Ç/ýƒ©Ï·þD_ñ£ûRÿŸoüˆ¿ã]oÛ Ú[. l1°sŸEÆOCÛ±ô4¢ò’?™òÆžcð~QÏëòž:ñGÖgÙŽGûRÿŸoüˆ¿ãGö¥ÿ>ßùƺÏí@XÔb§> àþ]O äñÍ+ß[¡}Î@@Im‡o0HÁàsÁô£ë3ì‚Ç%ýƒ©Ï·þD_ñ£ûRÿŸoüˆ¿ã]SjPù°Ççó_nB7†éÈ8àôê{²eŒP~´}f}Xã?°u/ùöÿÈ‹þ4`ê_óíÿ‘ük³ócÿž‰ÿ} <Øÿç¢ßB¬Ï² gö¥ÿ>ßùÆìKþ}¿ò"ÿv~lóÑ?ï¡G›üôOûèQõ™öAcŒþÁÔ¿çÛÿ"/øÑýƒ©Ï·þD_ñ®ÏÍþz'ýô(ócÿž‰ÿ} >³>È,qŸØ:—üûäEÿ?°u/ùöÿÈ‹þ5Ùù±ÿÏDÿ¾…lóÑ?ï¡GÖgÙŽ3ûRÿŸoüˆ¿ãGö¥ÿ>ßùÆ»?6?ùèŸ÷УÍþz'ýô(úÌû ±Æ`ê_óíÿ‘ühþÁÔ¿çÛÿ"/ø×gæÇÿ=þúy±ÿÏDÿ¾…YŸd8ÏìKþ}¿ò"ÿØ:—üûäEÿìüØÿç¢ßB6?ùèŸ÷Уë3ì‚Çýƒ©Ï·þD_ñ£ûRÿŸoüˆ¿ã]Ÿ›üôOûèQæÇÿ=þú}f}Xã?°u/ùöÿÈ‹þ4`ê_óíÿ‘ük³ócÿž‰ÿ} <Øÿç¢ßB¬Ï² gö¥ÿ>ßùÆìKþ}¿ò"ÿv~lóÑ?ï¡G›üôOûèQõ™öAcœ²‡\±¶ò ¶fòùfRr@Þö©üïϼ?øïÿ[žlóÑ?ï¡G›üôOûèVN§3»Kñÿ0±…§YߥýÕÝì!<ÈXc¼wÏ5kþ&?ô ›þÿGÿÅQÿúMÿ£ÿ⨗–bïfâ¸\‚®Ç=À±Gù×Þ‚Ì»çKÿ=þú4yÒÿÏGÿ¾Róo¿è7ýýÿŠ¥_±ÀÒæÏýuÿЦ«ÒnÊKï 2ç/üôûèÑçKÿ=þú5Wþ&?ô ›þÿGÿÅQÿúMÿ£ÿâ«Q¼éç£ÿßF:_ùèÿ÷Ѫ¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@¼éç£ÿßF:_ùèÿ÷Ѫ¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@¼éç£ÿßF:_ùèÿ÷Ѫ¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@¼éç£ÿßF:_ùèÿ÷Ѫ¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@¼éç£ÿßF:_ùèÿ÷Ѫ¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@¼éç£ÿßF:_ùèÿ÷Ѫ¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@¼éç£ÿßF:_ùèÿ÷Ѫ¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@¼éç£ÿßF:_ùèÿ÷Ѫ¿ñ1ÿ Tß÷ú?þ*‘ŸPLgJ›“‰c?û5[ó¥ÿžÿ}<éç£ÿßF©y·ÿô ¸ÿ¾Óüi`¸™îÅ´ö’[±¤ÙN@úq@LI´$œ’¯ÿ¡5y­zT¿ñéÿý «ÍhÔ$ÿ_ûÑÿJ«cr-µKß‘äyDLeŽ$=È<žÕjOõñ½ôªV© êW­,ÿgdXš9T&q»#îîê:döÍ^‡ZVäxdavªíò£êƒz!àx§ç0xã‡f… ]Û.w\D1œåÇÎôÿ¾O¥ wl¹ÝqÆs—c9ÿÐ[þù>”Y5 ]Û.w\D1œåÇÎôÿ¾O¥ wl¹ÝqÆs—c9ÿÐ[þù>”Y5 ]Û.w\D1œåÇÎôÿ¾O¥ wl¹ÝqÆs—c9ÿÐ[þù>”Y5 ]Û.w\D1œåÇÎôÿ¾O¥ wl¹ÝqÆs—c9ÿÐ[þù>”Y5 ]Û.w\D1œåÇÎôÿ¾O¥ wl¹ÝqÆs—c9ÿÐ[þù>”Y5 ]Û.w\D1œåÇÎôÿ¾O¥ wl¹ÝqÆs—c9ÿÐ[þù>”Y5 ]Û.w\D1œåÇÎôÿ¾O¥ wl¹ÝqÆs—c9ÿÐ[þù>”Y€ù¿ÔÉþéþU™'úë÷—ÿf«ÒÜ@Èè³F\‚6†ÏÍÇþ:ß÷Éôª2®¸ÿyöj@R¼ÿWcÿ_QÿèêÙ¸uŒ»»U,NëXןêìëê?ýMñçٴlj[pÁI÷`ýk‡MÔŒ`ºµú•aš?ˆ¿´îÄZýŸteÑšLïÁÆ=ÿ#Võ½Sû"Ñ'ò|íÒÛ»obsÐúW-yu¦Ú 6m6àË5¡ êÓÌ\’y=2Kqþ×µjøÂTŸC¶–#¹$™YN1Uˆ¯>Xx{XZ6‹èî]ô:Z+™Õ…Þ„_&¡spZP³G!_<£/CôÏçfîæãP׎— ò[Á[æhÎ×cÁO>«éÞ¹~®ÚROMuô*æí…isq§ëÃKšy. –-ð´‡s©äÇFõíG‡î&›TÖY¤‘cŸ¬Ä…ŸéÐR•“•ôI?¿@¹»Qi·Ð_4lÛÒ9 e‡B@QÍ`é^uýæ·m-ÝʪÎ2HAA¹¸_N€RxÜIqæËþ±“ËÝòtSœzöÍuQèÎíêœ[Ðì(¢Š÷Ì‚Š( Š( Š( Š( Š( Š( Š( ¡ºÿV?ýÔÕSR’X­ÃCnó¶ìmRPyäŠçf×láÔÑŸÙäþoCý}?ŸËÖ€'—þ=?à/ÿ¡5y­zT¿ñéÿý «ÍhÔ$ÿ_ûÑÿJ浫&º{ùƒ"‹hâs¸€NK sø÷ìF:Y?×ÅþôÒ¹­bM“ß§òù°Æ™N à³ç;O'##\diJüêÂf0Ò¯Õîsò#+2 ÁäÁçƒéUç‚Kw (Œ‚¬Xz‚8=dž¶¢»užw’ÆTó¼Ù'Þ«?—(À£%óÉ•wB!¢¦kK•ÎëyF3œ¡ãÏþ‚ß÷Éô¡­.W;­åÎr‡Œg?ú ß'Ò‹ !¢¦kK•ÎëyF3œ¡ãÏþ‚ß÷Éô¡­.W;­åÎr‡Œg?ú ß'Ò‹ !¢¦kK•ÎëyF3œ¡ãÏþ‚ß÷Éô¡­.W;­åÎr‡Œg?ú ß'Ò‹ !¢¦kK•ÎëyF3œ¡ãÏþ‚ß÷Éô¡­.W;­åÎr‡Œg?ú ß'Ò‹ !¢¦kK•ÎëyF3œ¡ãÏþ‚ß÷Éô¡­.W;­åÎr‡Œg?ú ß'Ò‹ !¢¦kK•ÎëyF3œ¡ãÏþ‚ß÷Éô¡­.W;­åÎr‡Œg?ú ß'Ò‹ ,ècv¯l1Ÿ˜ž™ì}ùî:ŽîOõ×ï/þÍ\FkXã¹TØÉ ;$ícŸçÐzV÷”žŸ­Rz~´¾©‰ÓUÿ¿@æF Ž—0Ô[QÔ%ŽK–MŠ‘ƒ²1þÎyþ]O­WV£c©]\é“[4w's¥Æxl“ƹüý³]7”žŸ­Rz~´}Swªíåù29íJ¹Ó®o¥ºš9Æ ¼Ëd‘Ž:ö§xoL¼Òd’ d‚KVfueݼžÏ`0+ÊOOÖ”Fªr5¤0Ø…>i5«Wù|…̬:Š(¯P€¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¨n¾ààyþST7_êÇãÿ šÏó[Ñ?ïþP9mr,ã‹IxÖ¬UTÿäõé/óZž_øôÿ€¿þ„ÕæµéRÿǧüÿô&¯5 P“ý|_ïGý*=þBÚ‡ýs‡ÿg©$ÿ_ûÑÿJGÿ¶¡ÿ\áÿÙèjŠ( Š( Š( Š( Š( Š( Š( Š( Mþ¦O÷Oò¬É?×\¼¿û5iÍþ¦O÷Oò¬É?×\¼¿û5R¼ÿWcÿ_Qÿèê‹Ç3Ãkk<‘ÛÌì&’0sŒcoPAn\T·Ÿêìëê?ýOñ&©.›5¸¸´†çL›å”2ÁÏ—Ѐzàôë@özeŸÚ!ŸÃZ¸ûB¾+‡+æ†ÆŽ9Áõâ¶îþÃÿ u—™ö¶ù'ËÛ·ËÛ‡ëß=Jç5Oì©î톑Úü͸Ô…ú7Ó€3žÕµ}ÿ#ö›ÿ^ÇùI@ωí~Õqm­äÓÀå"‹q8$=2_QM“Åšz[$ñ¥ÄèT4žTyòrpäàsù}3[Âßò×ÿëçÿfz¥á¿ù5_ûmÿ¢ÅtÚõ•– ù/5¼Ì @È'¹†¢‡Ä–r]E Åuœâ e„„›ÓלƒÈ®^ûþD7þ¾Oó’´¼[}kªiövÖÏ<Ó©DFç¡ÿw–qú±}ÿ#ö›ÿ^ÇùIZÚv±m¨C<ª³±YV|+&R3Àë×ÐÖM÷üÚoý{å%dkò¾©êÂÛcÔa Ü–Ãg=?å§OïÀ׊5(µo CsSGÚÂ5@-…nF ã·àkoVÕtù4›ÔŽþÕ *¬ÊI;OšÅñEÓü'aj9T6ÒH-µ‹Ÿrjþ§á"ßL»š+M²G ²Ÿ1ÎRGz‹GÒ Ö<#corò*+³ƒçsŽàúÖM·†ìæñ=Þ˜ÒN † êÁ†âp½xÇñÕÒxCþE»Oøþ†ÕNÇþGíKþ½‡òŽ€*ø®Æ-7Ö3´qÜŒ žCžßZ…nZÇÂZ­—ÚÉorÖè•,3Ç¿Î?JÐñïüaÿ¯•ÿÐZ³õ+f>-ûµCÕÌÈÃ*«oÈ÷Ëô૲^ëû·ù2,{±Œà¸Î*Èoúùoýhð·ü†µÿúùÿÙž³ü#­éún™,7—T1`61ãjŽÃØÐmEU°Ô-u(Zk9|ØÕ¶“´Žpqî*ÕQEQEQEÙ,“€3ÿ šž«_[ÃsIáIT0!]C þ#üûô ~t_óÑ?ï¡PDÊÚäeX0û,½ºÕÏì­?þ|-ïÊÿ‡Óÿ¯üNŠÊÖÙ·ÛÛCa¹)#o·áþy ËÿŸðÿК¼Ö½*_øôÿ€¿þ„Õæ´ê¯‹ýèÿ¥G£ÿÈ[Pÿ®pÿìõ$Ÿëâÿz?éQèÿòÔ?ëœ?û=mQEQEQEQEQEQEQEQEÉ¿ÔÉþéþU™'úë÷—ÿf­9¿ÔÉþéþU™'úë÷—ÿf  WŸêìëê?ý] ¢ÈŒ’(d`C+ ‚= sןêìëê?ý]W·°³µröÖ°Bä`´q…$zqOkhánÌè0²€ô¯sùÔ´PQ[AÈðÃo)˲ Ï©õêi±ÙÛC ü1ÄùÜŠ€+d`äwâ§¢€9ÿi2ÝèðÚé–ÉòLËM¨ÃgÐu5³´3<Ñ[ïΨ6NNO~jz(&¶®ᡌ΃ !A¸@z÷?a]Xßêšý¤—6¢ÞÊÉÙÑÄ¡šC‘ƒŽÙÂñŽ™ç¥tTPWÐ] K˜c™ÈY0ך{¢ÈŒ’(d`C+ ‚= :Šd0ÅoŠÒ(×¢¢€à)«mÜ5ÂÃÆ@ƒq„õì?*–ŠŠâÚ ¤ s s 9 "úóC[@× pÐÆgA… Ü ={ŸÎ¥¢€"ŠÚG†ãyN]‘.}O¯SUÿ±ôÏúZß…ÿ »EEomª¶†8Pœ•‚}x©h¢€ (¢€ (¢€ (¢€ Šãî¯ùÿ?Ï¡–¢¸ûƒëþÏóè@+õÿ?ç×õ§àßú ÿ?ç%zÿŸóëú÷ÏÌÓðoýÿŸó’F_øôÿ€¿þ„ÕæµéRÿǧüÿô&¯5 P“ý|_ïGý*=þBÚ‡ýs‡ÿg©$ÿ_ûÑÿJGÿ¶¡ÿ\áÿÙèjŠ( Š( Š( Š( Š( Š( Š( Š( Mþ¦O÷Oò¬É?×\¼¿û5iÍþ¦O÷Oò¬É?×\¼¿û5R¼ÿWcÿ_QÿèêܸºŠÛo˜\³g lìqÔáA8äsî=kóý]ý}Gÿ£«ZxCÞæ —‚äÆ26‚²('r';H?7'‘@#¸ŽTã%•É x#9û¸Áãž:Ôµ“ÊnmÕŠûkG$‘)U›±Î2z©å?¬R-%ÍÝÑ’ì*ÌÆbw/”Í€:º@ ï’MoS$•#xÕÛ +mAާÿ kF+:Z›©£…oÄ ù§qSí¥',}sÈÆ00I’ám<é‚%ëF¼—@m‰áŽNrÄ‚hjiRÞ&•¶Ç–cŒà“D²¤(FÚ¥•AÆybýH¬+ë©o´ë”c±­í%iÂd|0]¾ÉËrrOÖº j8q‘ž¤rèqÞXª&¸…xòíšã0¬í° °9;GIæ#´¬]^ÕJ4ŒeýáË)‚¤÷ÁÏ9ÇSÖ€,QXPLë}`ñ;˜®Ù°Òܳ<«±›w–FÕ xÆ2H§iâHíôyÍÄòIre2HX013tè9QÈã’rr·EPÔGšñ@žcJÁ™cYš c,̼ñ03’Ý8Èγ˜Þ=„s]:¤‹rŠs‰6È¡@n ` ç‚psÔä ¨¤¸Ž'äï(ÒU,J®3Œ}Gy¬x˜Ü\Ú[Íu2¡ûRYJ™6JŒõ$×9àäòs ÄÒ‹yHûâ´¾T}Çp "…ç®@ž´ÑÑY›{‹é'¸š!o Te˜¢¢ùhÙ#;O,OÌ¿WY™ãŽòæÝßu²òïrѤm´P aÏ üÝw“ÐmPnÃn*ÁX(ÜTœc têÓž”úÂh„3_Í“+›øþõˆÃsÆqܧ8§åü¶ù³yßmò¿Ö¶Í¾—™Û÷xéïךڢ±H¶ÿkûDí/ÛŒ`ІãfݽõŽÇº*+¸>¿çüÿ>†ZŠãî¯ùÿ?Ï¡¯×üÿŸ_×¾~dnŸƒè'üÿœ•ëþϯëß?27OÁ¿ôþÎHãÓþÿúWš×¥KÿŸðÿК¼Ö€=BOõñ½ô¨ôù jõÎýž¤“ý|_ïGý*=þBÚ‡ýs‡ÿg  ª(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€7ú™?Ý?ʳ$ÿ]qþòÿìÕ§7ú™?Ý?ʳ$ÿ]qþòÿìÔJóý]ý}Gÿ£«zâÚ ¤ s s 9 "úóX7Ÿêìëê?ýVnÞX5KË·òeŽÒÑeDhÎáÄ™ÚÙùIÛÉÇ#¶h\C¬j± X¿Õ€£ Æ8ôà‘@†%XÕc@±«FŒqéÁ"³n¯/,Ra3A3‹YgB‘”¦8#qÎwNžü^Œ\ù/æ¼"S’»P•N8Ÿ›¿ôË›®&eM«!’E(“1²`þ~X©c¶‚$"†4HÉ(ª€'9#Ó©üÍc[_ÝçéÉ$¡äž7ÌÒJ@8*§$ÙÝ8éÍ\Žþu¶Žâx–ÄÇ€…vâªv±à7¦Fr2@‹{M‘Î.&k†Ý/É„?(\$ñ…Iïô«Šã¡€ àŒòAüÅgZ›ã¬L³Í–!ŒJŒ@É“€Ku㓎p8$³^I¨ImlÐF‰H^D.rÅÆ0þï\ñïž.”RáÊà€q‘úÊÃݘÐî`íòŽXcûŒÈV}¥õÍôÑyK Q5´7 ¸oœ¶Ttì:öô9ൽ¹qc4¦î6¢¡ P¸ËdîáqÐuϵ\šÎÚáJÏo ªÍ¼‡@Alc<÷Ç[ÙÛZnû5¼0îÆï-ç3Нa5åÌP\»@`ùadd|Ù!½:_c[OÕ..ä¶b…£¸*-dO(, ü­ÐÎsí@2ÙÛM•5¼2G¸¾Ö@F㜜zò:•cEHÔ*(UF‚³ì®î&¸ 4+à—¶(É$cÔNðÔ1E•ÝÄ×&’|öÅ$Œz‚IÞã zƒÆ(ÚYÛG+K¼)#6òÊ€ÜóŸ^OæjA J±ªÆbÿVŒ'ãÓ‚EeXÝ^]$Û XìpÌIŒ¥·|¡A|¾¼c¾x– ë›ß(Û,1æÚ;†YmÛ÷aAÛ½pzôã ×Ð] K˜c™ÈY0ך†m: §G’8Ú%I¢dX³+dþ+Ÿ©§éóµÖŸmq 剂ô€x¬ÍM§[éÍ´¢'Ř$©<œc‚=yõó@éÐM,Eã Ž&‹É( JÇN6t©´cØÆ`ÀSŒ¯Óǵ2æIá³,Š$˜’ªqÛ,98äí'ÍfÜ\ÝËb»vas —Ž6R2ê 2îÈ##9<‚FZÓ’ÎÚi’imá’TÆ×d—#·4Kgm4¾lÖðÉ&Ò›™;Nr3éÉüêŒ÷—‘Çy0h6YÞ)ŒæR9ÁÝòçv:c¿Jt·×1¶¡.ØM½–NÜòb5|g¢òzàçÐc$á³¶34ÆÞ+csìŽ#'Øù >ÇmöŸ´ýž´Ï]ƒLuëÓŠ£ovÏ"yFàˆ™×ýKq¸c —È9ÏኛO¹–wpóÁ.Ñó*ÄÑ¿çüÿ>†ZŠãî¯ùÿ?Ï¡¯×üÿŸ_×¾~dnŸƒè'üÿœ•ëþϯëß?27OÁ¿ôþÎHãÓþÿúWš×¥KÿŸðÿК¼Ö€=BOõñ½ô¨ôù jõÎýž¤“ý|_ïGý*=þBÚ‡ýs‡ÿg  ª(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€7ú™?Ý?ʳ$ÿ]qþòÿìÕ§7ú™?Ý?ʳ$ÿ]qþòÿìÔJóý]ý}Gÿ£«nK(f{†Ä!uÎQ»ñþ#X—Ÿêìëê?ý[3Þˆfò–¦ePòy`ŠINOC‚xéÓ 6˜²¤«ss<æHšϰVÆìmP9À랟Z¿Y–z”eiû‰®®Ú9eòö n¸É ÓÓ·1Ô‘ö}– ®·Æ²þïh¶v“¸Ž¸?—8 E¥a†8ï. ]‘ÉònTÀù~î1òŽqž:ÔÂÆ#·Îg¸ ¥q) 2s¸ãÔƒ@8rø®’Yš%cI>eÇ X ü§‚*²jÉ7–-ín&g…' ¡F³Œ’@Ï3ôÏ8–+&èN·3ŸFÊÅHeŠä‘ž7s“œÔRYK.§,ë4Öêaã*s†rA î¼ãèzÓàÔ¢¸xvE0Šõ32€²€3¸p Éqî2A©$ïî&H§ÿS+mÛ'†$Œ€O tõâ€%·²†Õ÷B B9Sv=ÿˆÔPi©Ãûùž(?ÔÄÛvÇÁQ‚'‘É=}y¦A«%ÄpÈ–·'\ÂX(óÒÛ@ÎAÀ<œ:ò2ËMVI¬­d{9šâxÈÓgÌ0¹a–ÆÜ°àœóÒ€,[X}˜ [™ÚÆ#„• ƒ€ Àã’}zóEµ‡Ùй¡Œb8IP¨1€8œ9'ׯ5b<؃ìt'ª¸Á¸ÿëŽlНþé£I-§…e$FòÎ ÆÜ8òO^(ŽÃlѼ—3̱cI ‡g n<9'¯¯4Ga¶hÞK™æX‰1¤…HC‚37 “×ך-¯þÒP­´ë ƒ1ÌB•qŒƒÁ$dsÈx¨îÔ-Òââ8`’) ó(, c“ìZ€%´±ŠÏo–ÎvÃ#qu3Çæ5J[&µòRÚ;·aHY ’0ΫœÝŒu<©“Ó¤Žäj7·Z_â(ãƒ[”l1/‘’`¿•WßvšUåá¿ÞpY#ÛòU' žÀõëùP…³ÛYZBïóC ÆÁ~é ž™ì:K>;–‘Ì’Fî#“.Æ,¤dç½P¿Õ."ÐÌÑl[þ<ã(#ÛälgÛ=ëBÒW’âõ]²±L:-ó&€%xKÛùFY7`~ð#¿†1íŽ*«i‹$r .gy\¡óŽÀÃcnP]¼öïô§j·2ÚÙ†ÝäHò›r»˜ Ççr3Æj»“Ï·ˆ<ÙûHŠQ(Lÿ¨/—ޏ?\öÅI&˜² ®gÙ0t17Ê“òäd>R=±S›(Yn‘Ádº9‘Iÿd.=…E¤“¼?¸™"ŸýL­·lœ’2<Ó׊uµÿÚJ¶af9ˆR®1x$ŒŽyÓ¯±Ù²ïó®î'Ü¥Fæ ´¸ØŸ~£¶9¢ 3Þt·\Hª™ N6ׯ§Ö’òö;G-!„‚YЍ!vç¯9çŽÜœö¥‚ôM7”ÐÍ 2—ÌoP@$r:ŽÏN¸µEPQ\}Áõÿ?çùô2ÔWp}Ïùþ}~¿çüúþ½óó#tüÿA?çüä¯_óþ}^ùù‘º~ ÿ Ÿóþr@(ËÿŸðÿК¼Ö½*_øôÿ€¿þ„Õæ´ê¯‹ýèÿ¥G£ÿÈ[Pÿ®pÿìõ$Ÿëâÿz?éQèÿòÔ?ëœ?û=mQEQEQEQEQEQEQEQEÉ¿ÔÉþéþU™'úë÷—ÿf­9¿ÔÉþéþU™'úë÷—ÿf  WŸêìëê?ýZÓAr—¯=°…¼ØÖ6ó›K€Í÷ºdtëÏ7Ÿêìëê?ý[—–Ö›~Óq ;³·Ìp¹Ç\f€(ÚÙÞX¤&‚g±@áä(LòÓœî>=øtW6WÙ„7¶ŽóÇ÷7`Œë»§lw«—–Ö›~Óq ;³·Ìp¹Ç\f‰ï-­¿ãââ±õŽ®q××ò4]à¼K£4&ybHÝŸ*©c¸/9ûÇŒŽyá4ËlöyŒ‡m¤0¤ýäÝŸÃæmn`k†·Y£3 ËFnÔŽ½ÇçPØê0^ÅY#¼K+B@@<޽Ç42Çm¥ÄÌ›­6ù˜'2qø‘PiºWØžûŠy+·í3$˜ÏÝIêy=Ç9Í]7öÑÂ’\\ÛŹU³æ¼ƒŒŒƒƒƒßû‹ËkM¿i¸†ÙÛæ8\ã®3@ ±–;m.&dÝi·ÌÁ88‰“ÄŠ‚;+±¦ZÛMoi+@¡2°è VÛ•n½~£Ú—S¶Žæk_:¸Š?3l’‚qž£dñÀ ÔÒ^[C2C-Ä1ÊøÚŒà3dà`wæ€ 8åŠÙgÞã<äœ ð2y8<œdõ¬èt¹Åí¬òÇk¾&IÆL³erNÞHùrG`FѼ¶ŽUŠKˆRFmYÀ%¸ã¼ÌT¬ê¥C0ÎÔã8€?•gÛZ\Gx%1Áä™ .ؘœòPŒ)'9'ŒdŠ´ð3jÜ6$R!òÅÿÐMJf‰VFi,_ë a„ã<úpAª¿ÚvÉu´³B²Ë¿h8m vù‰íêv  R]Bk‚FÇŠ4¾T¹?úªíc+i–“Ì›ÏÚrp7³Ÿûèf¥±Ô`½Š"²F'x–V„8,€€y{ŽjK›ƒ #ŒË,„…P@Æ,OedóÔpsM&Ýnô¦šK÷À7:&ãÑ™BœñÓ¦?à^Õ(ŽúÞêéà†ÞXæH ÎÈGÈ«Œ?Ýõ«0K39KˆDmŒ‚Œ]Húà`ûcéžp°][Üîû<ñM·ïyn\Sqhî –æÖ4!A$R0ÜH]X€qÏCŽ?*‡ì2ý·ÎÜ›~×çc';|/óÏéV º·¹Ýöyâ›oÞòÜ6>¸¢ «{ßgž)¶ýï-ÃcëŠZèv›¥}‰à_±Ø§’»~Ѓ2IŒýÑ´ž§“ÜsœÔ¶Ö—Þ Lp@¹&C ¶&'<”# IÁÎIã"®Auos»ìóÅ6ß½å¸l}qPJ|[É «åHæA(Ú¥vðHÎ>÷^Ô(I»X.3S±–ó–È7ZMÜOÞ}¸ü>SVmB€FÄŠD#¾X¡ú §=Õ¼r,ocùš»v+¨.–6•Q^7TêªØ;±ß@ÀçŸlK}o …›+÷˜FÅSýæ Ç<žœÔwÑØV¼¶ŽiÂþãÍl` Ç?N}ê šjèCšàÜÃ7•jÒ ¾YTÇæ7÷páîOŽ¼â‚£ÜÝ“(º¹‰­¥F”¹%2«Èõ?¬‹M$Ç‚Î×d´=0xàçŽ{ñÖ¦þÊÓÿçÂ×þü¯øVªP†×û¿àJA5ݵÔ(²O›y$–‚GÝÉû£9ÎjÛOÊJc´’fX˜,F0Ùþ °qî8çµT—û|¹4õ X¨ÅƒÄdðBsÀ'Jš ]&áŠÇc`¡Š½®Â$ÔÒsƒïý|ÿÈ5)ÍÅÛÌÜ;ÎdH|¥Û~U?ýN ?PÿKyä‚Únlg{BT±;p¼ŒúöîqÞ´?²´ÿùðµÿ¿+þeiÿóákÿ~Wü*Õh&¼…b­Å»mh¬$É(!H^\yJ¼çñÍC©‘¦­°Hþp2&#%p#Q’Ý “ÉÏv·öVŸÿ>¿÷å¡“AÒä™ek(ƒ.0m^=@àþ4¡V «ßîôóò 3FŠdQGb8cXÐtTøSë” ¨®>àúÿŸóüúj+¸>¿çüÿ>„¿_óþ}^ùù‘º~ ÿ ŸóþrW¯ùÿ>¿¯|üÈÝ?ÿÐOùÿ9 eÿOø ÿèM^k^•/üzÀ_ÿBjóZõ ?×ÅþôÒ£Ñÿä-¨×8öz’Oõñ½ô¨ôù jõÎýž€6¨¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šdßêdÿtÿ*Ì“ýuÇûËÿ³Vœßêdÿtÿ*Ì“ýuÇûËÿ³P+Ïõv?õõþŽ­‰¢w˜n ƒÈÚ º‚qœŽ@$çiæäò+óý]ý}Gÿ£«ZóN¤‰n$1äŽ6PqŒÊÉ«…¯«°™JT7Âo¼·l$ щ?tÙ<£zŸºG¨©¡–F’+#o†C¾Lœ²&Ö÷'zg×çö§É¤‰aXeº•âLmFŠ«ŽÊ"Ónie“Rð ¬qd€~S’2yã­t9E­öõÿ!mâxmbw¤vP6>ÒðàÀýÐs÷G^˜÷5ª€K¥ªÝ0œ<•£É•äŒuϵV—FI‘[‰$XÆZHQè>N: ±ö9ÿè#uÿ|ÅÿÄTÔ”e³üÁ¹–%…¾Ð·É"mYK6HåJà`›îž‡ŸJÑ!ŠÜܬ²ïûk ÎЦr¤mèx'’ çØbÌzHŠfš+©RWÎçX¡ Ùääì§ÿg>Ý¿nŸníØòâÆsœýιçëOš+Dÿ¯¸ q ¹$7•¹dóéÀÚ$+·ËÆÜ0=ùëDBîI Æå@.Y<ƺp6‰ íòñ·$ ~zÕŸì‘ö´}ª_?þzyPî鎻3ÓŠ?²GÚ>Ñö©|ÿùéåC»¦:ìÏN)óúü~ïOÃÈ,V¶¶+dV X3ÜÊ =²JžäŽNÞzšÐ°e1:/œ >ÖI›s!À8ÎNx õ=XèÈD ÜHD§2&œç9?'<Ô±iÏb8o§EHâ~*g(Êþ÷çý_ EfçJ¸‘¹iv˜žñ¬Ølú‹Œz VŒ’ÀÑÆX«¤Œ¾^íǨ#צsÛíUbÓ¦‰ßf£rÎí¡#ở»ßŽƒ®O9¤ILÓEu*JùÜë!›<œ”¥Èúþä1BÄÒZ^‘Œ‰2ˆs¹fœòqƒÇ¤‰¾KÂÍepÖèL;]£¢GE$ƒ·žqÅi"Y–in¥yS]¢„²ã‘ƒ²–},Ü K‹É¥Prã…†}yJwó_v¿0"¸vm'R.#ó"ûØ×hvU0äà‚1Ôò¿€Õ¬é4¹8âó¬HÊv„Œ}ÞWQŒ?,UÈa ³$# #€$€pLšŠ[F¯}ÿzoý|Ÿý%BöËs­\’eU¶‹åŽB™;¤ä•Áõã8çéEb¥”‡*HèqŒÀŸÎ€Š¸Q¼€ c’p?SùÖ#0a{ÍCìùýïú2·úSÁó>ìŸs£¯LqÔÖÍ‘v²€Ë*K!KH‡*ç‘ìi%°³#I­`‘"ExÁ=§AV(¢Š(¢Š(¨®>àúÿŸóüúj+¸>¿çüÿ>„¿_óþ}^ùù‘º~ ÿ ŸóþrW¯ùÿ>¿¯|üÈÝ?ÿÐOùÿ9 eÿOø ÿèM^k^•/üzÀ_ÿBjóZõ ?×ÅþôÒ£Ñÿä-¨×8öz’Oõñ½ô¨ôù jõÎýž€6¨¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šdßêdÿtÿ*Ì“ýuÇûËÿ³Vœßêdÿtÿ*Ì“ýuÇûËÿ³P+Ïõv?õõþŽ­MBæhrÇ#ä<îÞ¿*@sÏ^Üg.óý]ý}Gÿ£«f{!4ÞjÍ4,ÊO,½A$HÈêyR=z`¥½õåó ¶ò FµŠrdRäÝòàž}ºð[ß^_: o"kX§&E.A}ß.é×Û¡ÏZ[Åuˆgž㵊‘Im¥ò ŽëÎ>é˦²^±‚Y­¢Kh¢£`xRù0=ŠòF}úÐU¼ó¥ûW”Ÿ¾†É¶?Ìt­úŒð}@«Öó^\1™•ãòŠØV*Nüã¶q·ÛÞý—n ½VU€"bË×ÜóNK ’îK™Ö"æO$ ¸ÇœnêsŒã·N(­Æ¡5½ØVxÝ ©!vÛ¹€“w¸5Œ·#ìßixgß{4j|²1æä‚XÿwÐqÍ[}.7oõ󈄢a`>íùé““ž #ž1‡E¦¤R«ùó:¤Í2#m³oÏAœ|ç©ì([Û—3Ja1^ãj*Ñå Œ¶Nî\ûSl¯¯$OšçÈÙx œ¡(_;‰çîôÇêq͈4ÔáýüÏêbm»cà¨Á€Häž¾¼Ó㱊8lâV}¶˜òòFN§?4—SOö¨m­š4yä/")QŒ:îëžÞõ“^y°[+@“º;´… ®¨árÎàzœcõ©îm|÷Ii •Pñí'iÆFØvÏǰÜ"+s:Ma祈b ¸Éð8ÆU/¯'’aòÜNÝIÆá2=òxÏêqȗמRÜIäyBqnȪw9ó<½Àçåç¸<¼ñn  ’'VrѬ‹’GÌ]ƒ1>äŒñÉâ°Åö'síó¼ìägw™æ~Yý(>Ò{›x\Ãö¶Éͤ»nœ®wgº`ôëÏŠîæIc‚Ùmâóä³$.ÉBƒ€FIÏ<ŽN}„ÒãFÿ_9ˆÊf1Kîßž™8à8ç99–¡™eVrËæã$c÷Ž¿QÅQ“U›l0’9˜3­»Ì?váNFsž¼tç9§µýѵ†B¢Ü1pòÉ»N)T0Ëdœ(=sVš€)†y¡‘ZF.Òpí¹‡ Œg³Ç^¹{Y±‰.î#tÎd lòr#¯·øZYm„ÈË•‘2È}èx8϶OZȶ¿»‡OÓ’ICÉ<o˜-¤”€pUNI;³» qÓšÙ¶m¡¡'’Å›«1$’~¤“ÇR-(C 1Çyp­ìŽO“r¦Ë÷q”sŒñÖ€üëmÄð-‰ >íÅTícÀn0LŒäd„µ7ÇX™gš,C•“'–ëÇ'àp*ȱˆíó™î©\JC œî8õ ãМ¤VMÐng? •ŠÊ É#¼xÈÆ­^¹Ô è¨[Æ[÷r+cû¹ç·~~”‘^I,&Þîâp»œ1ˆ:‚HÁ ÎONiÚË7†c´Ç—+Ù°ùX¦9ôæ‹í;ν7c´»ÝǶàãf ƒµºîöéß°ƒ¹"{kiîRDŒ*§§Þ#òŽøÈª§YÄÓ¸·‘í#µ[*íåHcœÏ;@g9Ï4ë>Y|¤1ZOÆG"‘L:²¦î1€HÀæ5zUÊÛ›f0ùrØ%¬’9FPã!qÈù‡q@wÑYîóÎØd˜íî¦3øü˜uò´¸iqHðªÌƒnX`|ÃÏJ«we}}æ™E¼[­&U]›æ}¸$àqÇLqïž'Ô¬>Õ4}žÞçÊW_*ã…ùŠÙÚyqÓ¿_Pÿi#$~TK+ïýÒí »NÖÉ$ ƒôÈæ­C'›}Ž„õW ÷ýqÁí‘T$²“ìp––l–ò1¬dò 0‚2yNxéVìã–+dIŸ{Œó’p3ÀÉäà`dòq“Ö€#ŽÿtѤ–Ó²’#y€çãîy§¯[_ý¤¡[iÖc˜…*ã‚HÈç=:ñT¡Ò綳ˮø\™'2Í”eÉ8y#åÉFf¶´¸ŽðJc‚É2]±19ä¡RNrOÉ<÷By@`–GÝ€Ò˜$öÀ&£:žeò£³º’M‚M¡U~RXwÝèyç§»°[»Ø%—˜£ŽE 1S¸” ñé´Ÿc‚9µÄw5Ë£³CE—«/–Æ8È`qÛ‘î@©#ìû,]oeýÞÑ…lí'qp.qBê–ï Ê›ÙFÍ£2oaö$ã'ƒéUílï,R Á3‹X pò¦yiÎwNžü: 'É–Ó2oŽR77´yØqéó1ëÔ.:P“jÖÑG€I*H‹ 1¡8VeUÈëÎî2v·¥Omuç»Æðɨ“i;Npr¤ŽÇ¾xªCJdóB8Á–'…$ߎœ`—tÀQëWR]Bk‚FÇŠ4¾T¹?ú  ÇÜ_óþŸC-Eq÷×üÿŸçЀWëþϯëß?27OÁ¿ôþÎJõÿ?ç×õ§àßú ÿ?ç$Œ¿ñéÿý «ÍkÒ¥ÿOø ÿèM^k@¡'úø¿ÞúStäh/n§}»&XÕ@aŸ—vs“ïN“ý|_ïGý)¨»ÝW8ÉÅi}¥=?ñåÿ>ÒžŸøòÿgâ/ï¿ýò?ÆŒEý÷ÿ¾GøÐ‡ÚSÓÿ_ñ£í)éÿ/øÖ~"þûÿß#ühÄ_ßûäh}¥=?ñåÿ>ÒžŸøòÿgâ/ï¿ýò?ÆŒEý÷ÿ¾GøÐ‡ÚSÓÿ_ñ£í)éÿ/øÖ~"þûÿß#üj ‰Ò­ã]Ìfr¼Œc NJ×ûJzãËþ4}¥=?ñåÿÏ »3’G>žþôb/ï¿ýò?Æ€4>ÒžŸøòÿiOOüyƳñ÷ßþùãF"þûÿß#ühCí)éÿ/øÑö”ôÿÇ—ük?}ÿï‘þ4b/ï¿ýò?Æ€4>ÒžŸøòÿiOOüyƳñ÷ßþùãF"þûÿß#ühô“«FÊ$÷—üj‹e¸ ä_ýš«‰ÕîåýÚ+œÌUk€¶—¶²ˆœ[Ç ‘b(‹m$¡QµA8žØãéUì­›í¶²É ÀR+2}Ýò©_¡*OzûЈ¼¶ic‰n!2J»ãPã.¾ w’Y¢…wM"F¼òÌà@OáYKl˧0XHwÔ<Æ9#íÜà sè*m\ªË§3ÄeUºÝµWqâ79¾:ñÏs@þÙmöo´ý¢³ÿÏ]ãg\ué׊å±…f˜›;_xÚp 8>ÀÈÖfÏûo•7“öß7ýSoÛäyyÙß{Žžý9¨æ#í\=¼š€‘Æw-ÈÝ´óÁRzgŽâ€6c¹‚Tâš7I Êà†#9סü2[µŽx"\9’Sa¾áØ_ŸÀ=ë7çý·Ê›Éûo›þ©·íò<¼ìÆï½ÇO~œÑ Ë-ð›È™¯÷èAÛöm¡½†}~‡ŠÓŽòÚiž®!’TÎäW—#·4èn`œ°†hä*!2£¥cÃçÉy§å.bï„B?vàp zd¿L¨­ ³èöQü¦XSríÁ Žr=sœÐ¡4L±²È…eÿVC ?ã×€MG5嵺–žâ•[a.àØÎ9ïŽk-téåóTá ©amœ…$È$QÓ0ãÐÒÀÞk{Ùâ›l‹;6Ø™Ütd '!Wƒn3Ò€4ÞòÚ8–Y.!HÙw†gãœúr?1QÜê0@ÞZÉ“‡Z ãr†e\‘×øªVVÍöÛYd„à ©™>îùT¯Ð•'޽}ê)ù jÖ³<É~&Ï”J¢™÷n ÓîžÄ‘“ž‡RÍ+ºi5ç–`“ú Œ^[Vaq ‰³µ÷§“ƒìüTÕʬºssÁ=ºó@Vòt‹gdUv±VÚqëÍ6=BÖH Þ|kr›™À×=ñŸ¥S}.Fi Úâ]ÊÃÍt »t_½Ãô8éךsXO•l+ypw*í»$¨ÎF1Žó@”TpD!‚8†0Šc8à{“üêJ¥ü…¯?ëœ_û=]^’ÿÀ?öj¥ü…¯?ëœ_û=]^’ÿÀ?öj«yþ®Çþ¾£ÿÑÕÑ×9yþ®Çþ¾£ÿÑÕzåçÒZFÒbàÆêÙ?*ò$öPtä4ƒ‘‘€ 7pƒ'=@àÔãµ0ÜB cÌÌs㜰‘øm?•c‹©y²H¯nmâ¯ÊF˜Ïà ŽŸ,ŸíTpY KEY']ú„êOœÄàyÝ2N2:‘ƒß9æ€:*d‘$:å¢mÈsÐàäMUÓ²¯y÷d†`©½‹ hÝO'–=k>êgKˆî wÚ×k™%Ë ÇÌÚʱciddàðO=HíE*FÒ@dRY˜È†ÚFN=‰ëÇ>¸¨u’H£H¤ ÌÿpÊcóx?(aÈþ÷ÝÇBjœRâK8ãk„ÅÙI#™÷2þå›i9;‡Fêzû`kÑX6±Hl´—7wFK°«3‰Ü¾S6è>è7¾I4²1YÒÔÝM+~!Í;Š˜7m,y9cëžF1€ ©%HÞ5vÃJÛPc©Á?È}b®Vúw»$7åS{ bÝO'–=išP¼qau+¢ Ô4…®Ý̹Bp##jœàñлEPÔGšñ@žcJÁ™cYš c,̼ñ03’Ý8È¡bò_µ’Iq'–Rç>LÇUUùÆ Àþ. ïÔäz™©2·(fRqŽTPk"ܽԶöÒË7”>Ó÷eec²P‹–€OSÏS“V´2NÊËæ©šb$È;ÿzÜñÇ>ÔvH’GrÑ6ä9èpGò&ŸXV¬Gö4Æêo6ïç–6”‘'´ô‘ÀÀäqÓ”/X]Jè‚u !k·s.PœÈÚ§8ŽÄõá3ØÔ|±Ê`ži¢ŽÑf•å\“·/®w ¬zƒ–ŒõÁÈÕ‚«t/M¬,`w¦D{·•·+¦ÍùÎÐy) ŒóÀ5zÂëûFssu#UT<Ϊä‘Ó€P«{P…Eq÷×üÿŸçÐËPÝ}ÁÀ8$ò3ü&€ ëþϯëß?27OÁ¿ôþÎNDšíœWÝä`Ûv}œ“Ÿûçš9mr,ã‹IxÖ€%—þ=?à/ÿ¡5y­zT¿ñéÿý «ÍhÔ$ÿ_ûÑÿJ`fC½3/ €O×µ>Lù¨A.Æç§4Í­ýè¿ï¦ÿâh–íOþxÚßÖÿâhÝ©ÿÏOûúßüM]ÚßÞ‹þúoþ&­ýè¿ï¦ÿâh–íOþxÚßÖÿâhÝ©ÿÏOûúßüM]ÚßÞ‹þúoþ&­ýè¿ï¦ÿâh–íOþxÚßÖÿâhÝ©ÿÏOûúßüM]ÚßÞ‹þúoþ&­ýè¿ï¦ÿâh–íOþxÚßÖÿâižMì×vòN–ê±c±Ø“•#¸µ¡µ¿½ýôßüM[ûÑßMÿÄÐ7]áRÚ8FI2HAÉöúTµ?ùãiÿ[ÿ‰«»[ûÑßMÿÄѵ¿½ýôßüMRÝ©ÿÏOûúßüMµ?ùãiÿ[ÿ‰«»[ûÑßMÿÄѵ¿½ýôßüMRÝ©ÿÏOûúßüMµ?ùãiÿ[ÿ‰«»[ûÑßMÿÄѵ¿½ýôßüMRÝ©ÿÏOûúßüMµ?ùãiÿ[ÿ‰«»[ûÑßMÿÄѵ¿½ýôßüMS´†án§žäD¦EE6-Ów¨µqzKÿÿÙ¨ÚßÞ‹þúoþ&”.Õ|²Åp'¦}@õ  —Ÿêìëê?ý] E.¨Þ±È ü«%C(\~\ãvORO÷}éÛäõ·üÿ@fŽìƇso”rÃ'Ü`~B£K;håic·…$fÞYP[žsëÉüÍPß'­¿äøš7Éëoùþ&€5KP œ±©Æ2?*ˆÙÛšco •±¹ö Ç“ì@ü…Pß'­¿äøš7Éëoùþ&€4æ†+ˆŒSÆ’ÆÝUÔMŽÚ’4ŠÑ#$¢ªœäN§ó5¾O[Èÿñ4o“Ößò?üMiˆbUV4 ú°a8Çœ* ›®&eM«!’E(“1²`þ~Xª{äõ·üÿFù=mÿ#ÿÄÐŒvÐD‘¤PƉ%P¤ç$zu?™¦Çgm Ï4VðÇ+çsªÍ“““ßš¡¾O[Èÿñ4o“Ößò?üMhÜ[At.aŽd!d@À^iË HÁ–4V°B€Fã–üÏ&³7Éëoùþ&òzÛþGÿ‰  òÙÛM•5¼2G¸¾Ö@F㜜zò:•Pa($œŽIÉ?™¬½òzÛþGÿ‰£|ž¶ÿ‘ÿâhH4³ÒÌïÚæLÇGvÃ.]ÃpÍÐOn•n;;hfy¢·†9_;PlœœžüÕ òzÛþGÿ‰£|ž¶ÿ‘ÿâhLC¬j± X¿Õ€£ Æ8ôà‘QÇgm Ï4VðÇ+çsªÍ“““ßš¡¾O[Èÿñ4o“Ößò?üMiˆbUV4 ú°a8Çœ*8ìí¡™æŠÞå|îu@²rr{óT7Éëoùþ&òzÛþGÿ‰  FEb¥”‡*HèqŒÀŸÎ€Š¸Q¼€ c’p?SùÖ^ù=mÿ#ÿÄѾO[Èÿñ4z;8-Ћ8 ðv•ˆ ÇaAŸ\ [K³@#-½‹3±ÆfbǰÉ8þµC|ž¶ÿ‘ÿâhß'­¿äøšÖªz•Ì–âK‰V$$¨,qÉSÅUß'­¿äøš7Éëoùþ&€2šûIk„¸iíÌÈ¥UóÈüþ§ÖiuÖ¶†ÞT-¬ í9Ç+Z{äõ·üÿFùyù àÿ²Ð2ÿǧüÿô&¯5¯Jœmµ+HFÎ3ŽI=þµæ´êýñþêÿ!L§Ë÷Çû«ü…2€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ *YVFQà:Ÿñ¦ï_ù䟙ÿeýëÿ<“ó?ãFõÿžIùŸñ QOÞ¿óÉ?3þ4Ù% 0‰2=ÿÆ€Š‹Ls=¼’üìЇnÙ;sÚ¬o_ù䟙ÿeýëÿ<“ó?ãFõÿžIùŸñ QOÞ¿óÉ?3þ4o_ù䟙ÿeýëÿ<“ó?ãFõÿžIùŸñ QK$¡cf&@'¿øÕkky$9w‰Y© PŠ(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€#›ýLŸîŸå^m^“7ú™?Ý?ʼڀ=B_¾?Ý_ä)”ù~øÿu¦PEPEPEPEPEPEPEPEPæÿ]'ûÇùÓ)ó®“ýãüé”Eî¥Ër»>Ï `ÈAÞÁI AÎCŽqÔg„‚üÏ06ù¡äÜppª¯Œ¼A{çКÍÎâ&9xF61ïž3Ïp~§*-U-”•¥SžA$“ø|Ä~>¼ÐŠŽoõ2º•IQÍþ¦O÷Oò hÿòµÿ¯aÿ Tõÿ ë_úöúO@Ï*Á“8%cRĸ5 –{tyn|¶Œ.q ©ôÉ8#ÜíÆ3ô°Ê®¥]C+ FA\ÙR¯<Î8Ù¹‡É‚#ŽH rÙéîrz¬3mÇ$®Û¾TÚpFÜó½¾Ýx©c¾I˜y1É$gndQÀ$2:ô#·®,R ÌÂIÎì—9É!sÿ ×Û ‚A±b–UE ”ÈÕN3Є:u ± Ü[X²c$©uRpGÞïƒíÖ´ª¢Ø"¢¡–SØ„Œ R9è:äûòjÝG7ú™?Ý?Ê¡Ó?äiÿ\SÿA4ßêdÿtÿ*‡Lÿe§ýqOýPª(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€#›ýLŸîŸå^m^“7ú™?Ý?ʼڀ=B_¾?Ý_ä)•Æ·Šu9>OýñÿצÿÂQ¨Óûãÿ¯@¥ÅÿÂQ¨Óûãÿ¯Gü%‡ý1ÿ¾?úôÚQ\_ü%‡ý1ÿ¾?úôÂQ¨Óûãÿ¯@¥ÅÿÂQ¨Óûãÿ¯Gü%‡ý1ÿ¾?úôÚQ\_ü%‡ý1ÿ¾?úôÂQ¨Óûãÿ¯@¥ÅÿÂQ¨Óûãÿ¯Gü%‡ý1ÿ¾?úôÚQ\_ü%‡ý1ÿ¾?úôÂQ¨Óûãÿ¯@¥ÅÿÂQ¨Óûãÿ¯Gü%‡ý1ÿ¾?úôÚQ\_ü%‡ý1ÿ¾?úôÂQ¨Óûãÿ¯@CYJìYµ ¼““Êý–“ìÐBïóOþ&¹øJ5úcÿ|õèÿ„£Pÿ¦?÷Çÿ^€:°IÿA ¿Í?øš>Á'ý.ÿ4ÿâk˜ÿ„£Pÿ¦?÷Çÿ^øJ5úcÿ|õè§ûŸô»üÓÿ‰¤:{‘ƒvA÷Oþ&¹ŸøJ5úcÿ|õèÿ„£Pÿ¦?÷Çÿ^€:äƒÊ´[x¥x @ã°;Œtö¨>Á'ý.ÿ4ÿâk˜ÿ„£Pÿ¦?÷Çÿ^øJ5úcÿ|õè§ûŸô»üÓÿ‰£ìÐBïóOþ&¹øJ5úcÿ|õèÿ„£Pÿ¦?÷Çÿ^€:°IÿA ¿Í?øš>Á'ý.ÿ4ÿâk˜ÿ„£Pÿ¦?÷Çÿ^øJ5úcÿ|õè§ûŸô»üÓÿ‰£ìÐBïóOþ&¹øJ5úcÿ|õèÿ„£Pÿ¦?÷Çÿ^€:c§¹7÷dtÿâjÔ,G V5 ë€1\ü%‡ý1ÿ¾?úôÂQ¨Óûãÿ¯@¥ÅÿÂQ¨Óûãÿ¯Gü%‡ý1ÿ¾?úôÚQ\_ü%‡ý1ÿ¾?úôÂQ¨Óûãÿ¯@¥ÅÿÂQ¨Óûãÿ¯Gü%‡ý1ÿ¾?úôÚQ\_ü%‡ý1ÿ¾?úôÂQ¨Óûãÿ¯@¥ÅÿÂQ¨Óûãÿ¯Gü%‡ý1ÿ¾?úôÚQ\_ü%‡ý1ÿ¾?úôÂQ¨Óûãÿ¯@¥ÅÿÂQ¨Óûãÿ¯Gü%‡ý1ÿ¾?úôÚQ\_ü%‡ý1ÿ¾?úôÂQ¨Óûãÿ¯@„ßêdÿtÿ*ójÙo_²D8#äÿëÖ5ÿÙsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/images/fr.jpg0100644 0000000 0000000 00000035462 12114157751 022765 0ustar000000000 0000000 ÿØÿàJFIF``ÿÛC   '0"$'92<;8276?GZL?CUD67NkOU]`efe=KownbvZceaÿÛC..aA7AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaÿÀëª"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ÛÖ5i´ë‹K{{1s%À¨À9}­J—›‚E¥Ÿ“ûìàŒÿ«õÏåYúô‹½¢³8A¹b¥‡*£ úÕë–ž;‰â‰ $—ÏsÛã¯J¶­Ò.šŒ›æf=ÇŒfµ¸’ ´ÔY#b¬<Å8#þLÿ„á¿èŸ÷Øÿâj‡‰lbËj!·g”Ç$.z¶2Y}½GcV5û·‡OÓ¢K븋éÐþá?Õ¸9'w¦{‚º=œ4ÐÁ¹&õ'ÿ„Ý¿èŸ÷Øÿâiá6oú§ýö?øš–ûA°[¹îâþyö+Dc*’w"±+Û'òÞ¡é0izΞÌòM<²ÜƬUQW•Á'<ž£õ<´í{äCÿ «Ï‚ßcÿ‰¥ÿ„Ñ¿çÁ?ï±ÿÄÕ/8‹X{‡ùP[¼’3‚J¨Ç+ŽÿÓ5CZ³’ÇSšŒa;Ù`"g£ v"«ÙÚÖ4­swþ6ÿŸÿ¾ÇÿKÿ “Ï‚ßcÿ‰«Ú¤¾n§­ÁçÍ6Û2ßgb$¡ܧ'‘Ô|£žýëMÊ-=§”Ý=˜ºU˜sÎ …àcźö©Q¦ú ¹w-ÂbßóâŸ÷Ðÿâiá0oùñOûèñ5$z%¥¶­ ·iÃØÜÛdÈá„Øvc÷¦ÿbE{ªOö—1=ÕÍÀƒç;IÉÛ´ô>¬?Äå§Ø/!?á/oùñOûèñ4¿ð—7üù'ýô?øš¡q¦Û[ÛD»n¤žK1u¾0 ®OB¸È ŸUÑì졹X§•î-¼½ß)*wsò€r>céÖŸ%>Âæ‘gþÖÿŸ$ÿ¾‡ÿKÿ cÏ’ßCÿ‰®hRНcÂç‘ÒÂVßóæŸ÷Ðÿâiá*oùóOûèñ5Î QG±‡`ç‘ÑÿÂRßóæŸ÷Ðÿâiá(oùóOÌñ5Ίp£ÙC°sÈèá'oùôOÌñ4¿ð“7üú'æ?øšç©ÂeÂç‘¿ÿ +Ï¢~cÿ‰¥ÿ„‘¿çÕ?1ÿÄÖ¥½”;<ïøHÛþ}SóüM/ü$Mÿ>©ùþ&°…8Qì¡Ø9änÂBßó쟘ÿ ?á oùöOÓü+S¨öPìò6¿·Ûþ}“ôÿ _íæÿŸtý?±…(£ÙC°sÈÙþÝoù÷OÓü)·þ}Óôÿ ÇáG²‡`瑯ý¶ßóøQý´ßóÁ?Oð¬‘N½”;<_í–ÿž ú…/öÃÏý?²Å(£ÙC°sÈÔþ×oùâŸÿ _ífÿž)ùð¬ÁJ(öQìò4ÿµ[þx§ä?ÂíFÿž)ùð¬áN{(öyÚmÿ<“òáKý¤ßóÉ?!þž)—²`ç‘ûE¿ç’~Cü)´þy§ä?¨ŠuÎ=ƒžEß··üóOûä…noùæŸ÷Èÿ ¦)ÂgÁÏ"ßÛ[û‰ÿ|ð¥ûcq?ï‘þTRŠ=œ{<‹_koî'ýò?—íMýÄÿ¾GøUaJ(öqìò-¤’H T®BŠveþì_šTpÿ¨÷—ú×+ö¹|ýŸl¹þÃóñö¼Û³÷<ÌçËÝÆü{gÖ²v±¬nÕîu_iýûCû²ê0NÎ?‘¯<¹%®¥$’K’Iú×ioÿ!›Ï÷#þM\UÇü|Kþùþt˜ÑØkÐßii×vë;[e™Y‚ŽBú‘ïP´šœÈÚ9”î2NŸ07pOOÊ·eûãýÕþB™MUiZ÷S˜×mµ-ZTi ‘“ûÃ,yÇ÷F1Ç^¹þu•&ƒ­Í³Î…䨡tÊv¨è<jï(«Uä¶D¸&q/¥øŠB ™Ø‡d܃ó€ß{¨ ÓâÓ¼Iÿ)îcÞÅÛmȘõ'æäû×gEÞ]{4pÐèzÕ¼¢Xax¤^Ž“*‘øƒOŸG×.œ=ÌrLà`4“+=95ÛQGÖ%Ù³Gt­y¥’V%]ŽÆq—_BsÈàRÿek¿fû6%ò?ç—ž6uÏLã¯5ÙQG·—dͽü!Ô.|éQÓ Q&€À*7pÆ¢ŠÇÄnòžá7±vÛpæ=Iùº×[H̨¥‚¨îN/lö²DrCLÖþÍölKäÏ/Óü÷þû}¦ùïýö)ûyvfŽKûRÿŸoü}Æ”hzüûÿãëþ5Ö}¦ùïýö(ûLóÞ?ûìQíä/fŽThšüûÿãëþ4¿Øº‡üûÿãëþ5Ôý¦ùïýö(ûLóÞ?ûìQíäÍ¿ö.¡ÿ>ÿøúÿ(ѯÿç‡þ>¿ã]?Ú`ÿžñÿßb´Áÿ=ãÿ¾ÅÞAìÑÍcßÿÏü}Æ”ißóÃÿ_ñ®“í0Ïxÿï±GÚ`ÿžñÿßbo öhç?²/¿ç‡þ>¿ãKý“}ÿÓü÷þû{i³F_ØnçŸþ<)EÇüóÿÇ…iý¦ùïýö(ûLóÞ?ûìQí¤Í¿b¸ÿžøð§}Žãþyþ¢´>Óü÷þû(ä:}EÚAìÑ—öIÿ¹úŠQi?÷?QZtá°Ê£êÚAìÑ–-fþçê)ßf›ûŸ¨­?*Oùæÿ÷ɣʓþy¿ýòhöÒfŒÑm/÷?QKöy»úŠÑò¤ÿžoÿ|š<©?ç›ÿß&m öhÏIýßÔS¼™?»úÕï*Oùæÿ÷ɣʓþy¿ýòi{i³E/&Oîþ´¾Sÿwõ«žTŸóÍÿï“G•'üóûäÑí¤Í<·ôýiDméV¼©?ç›ÿß&*Oùæÿ÷É£ÛH=š)Ì—F0–ò¤Y9bɸŸÔzÔ>F¥ÿ?Ñÿßþ5¥åIÿ<ßþù4×=»Á]Ç »Œœgð¥Êîí£m(ÙÚM ̳O0•ä ·Ϲõ®"ãþ>%ÿ|ÿ:ôJó»øø—ýóüé^ãµG—ï÷Wù e>_¾?Ý_ä)•# ¨ú®²Ij®¤†V™AÐóVꔿòµÿ¯i¿ô(¨í5T¨f¹Â‚~ñÁ8€'𬧑õ âá#I„ÃíND7m&Óþü¯øTz'üxÉÿ_w?ú=ëB¨E?ì7þöŸ÷åÂì7þöŸ÷å®Q@ÿ²tßúÚß•ÿ ?²tßúÚß•ÿ ¹ESþÉÓèiÿ~Wü(þÉÓèiÿ~Wü*åOû'Mÿ }§ýù_ð£û'Mÿ }§ýù_ð«”P?ì7þöŸ÷åÂì7þöŸ÷å®Q@ÿ²tßúÚß•ÿ ?²tßúÚß•ÿ ¹ESþÉÓèiÿ~Wü(þÉÓèiÿ~Wü*åOû'Mÿ }§ýù_ð£û'Mÿ }§ýù_ð«”P?ì7þöŸ÷åÂì7þöŸ÷å®Q@ÿ²tßúÚß•ÿ ?²tßúÚß•ÿ ¹ESþÉÓèiÿ~Wü(þÉÓèiÿ~Wü*åOû'Mÿ }§ýù_ðªÒýñþêÿ!Zµ•/ßî¯ò˜ÐÊ{ýÈÿÝþ¦™O¹û¿ÔÒ”QEW7en-ü--ȱ³†O°ed@¤2w‚£Ðsš»-ÍÒ]\È'ýÔ7p±l!Ä`äõþ2F1Ï\޽‡çI6¡`ò\ºòuXHYW+ßÓ9Ï$c º•ÍÔSÞ¼SìŽÖÑg °í™8$ÿ Ú2=0G9×¢±æ¼¼þÒ”F$ò¡¸Ž±ˆˆ`„îÉß»ç8ÇwƒÎjÜMq{ ÆnÊ‹k¨¢Ê*†ùàn;—¶#r¿PtTQEFóþBZûïÿ ½To?ä%§ÿ¾ÿú  Õçwññ/ûçù×¢WÜÇÄ¿ïŸçM=_¾?Ý_ä)”ù~øÿu¦R¢Š(¢Š(¢Š(¢Š(ªÚ…»ÝØËoÝò ÇsVi•³°UÉÀ  ¶¾M¬M{ˆiO™×«¹cßÕMö”ôÿÇ—ük#í0Ïxÿï±GÚ`ÿžñÿßbÀ×ûJzãËþ4}¥=?ñåÿÈûLóÞ?ûìQö˜?ç¼÷Ø¢àký¥=?ñåÿ>ÒžŸøòÿd}¦ùïýö(ûLóÞ?ûìQp5þÒžŸøòÿiOOüyƲ>Óü÷þû}¦ùïýö(¸ÿiOOüyÆ´§§þ<¿ãYiƒþ{Çÿ}Š>Óü÷þû\ ´§§þ<¿ãGÚSÓÿ_ñ¬´Áÿ=ãÿ¾Åiƒþ{Çÿ}Š.¿ÚSÓÿ_ñ£í)éÿ/øÖGÚ`ÿžñÿßb´Áÿ=ãÿ¾Å_í)éÿ/øÑö”ôÿÇ—ük#í0Ïxÿï±GÚ`ÿžñÿßb‹¯ö”ôÿÇ—ühûJzãËþ5‘ö˜?ç¼÷Ø£í0Ïxÿï±EÀ×ûJzãËþ4}¥=?ñåÿÈûLóÞ?ûìQö˜?ç¼÷Ø¢àký¥=?ñåÿ>ÒžŸøòÿd}¦ùïýö(ûLóÞ?ûìQp5þÒžŸøòÿiOOüyƲ>Óü÷þû}¦ùïýö(¸ÿiOOüyƨK÷úƒ€ØU´Áÿ=ãÿ¾ÅJ9Ž„dQHžÿr?÷©¦TépÝ8–`Ä‘™Ùxöâ€'¢ªaÚpÿàCñTaÚpÿàCñTcÉ‹Èò<´òvìòö»qŒcÓ¨0Äwf4;˜;|£–Á>ãŸaUÿ°í?¸ð!¿øª?°í?¸ð!¿øª˜[@&3#’ }ƒq ~„¡§<1>ýñ£y‹±ò î^x>£“ǹªÿØvŸÜ?øßüUØvŸÜ?øßüULÖÐ5ÂÜ4™Ðad(7(ç€z÷?#ÙÚÉÅ%´/®ÅF@@^8Óǰ¨¿°í?¸ð!¿øª?°í?¸ð!¿øª˜Û@AÈ!!ŽX~'“ëRÕOì;Oîüoþ*ì;Oîüoþ*€-ÕÏù iÿï¿þ€iÿØvŸÜ?øßüU Óì¬î!p1+1þù›§%ÿ|ÿ:hèòýñþêÿ!Uî.`µ@÷3Ç €Ò8PO§5b_¾?Ý_ä+3U´ÚpÑ$ûIÃ:÷Rv:@[·¹‚é ÛOÈ Fá€>œTµ…;]G ô²HŸkVŠ'Ù˜“ÉßÙÉ mwËvÚq÷rbóØ[[™.Ê[5á]ðNò”Ä0€HÜ -ü>£ou°A!sÉ?¨üéÕ…ϾKLûm®ü¶ŒovA"l+Ÿ¼J‚sž9¨ »Çx‘ˈìäº\¼À+HDŒ$`6ŽHápNAÍt”VüáÌ%Æë6Y™5Ó@ vþð ž cûÀg'«sIáÝÆxçæävŠˆ\À@"xÈ!H!Ç!Žþ'ëRÐTo?ä%§ÿ¾ÿú«ÕFóþBZûïÿ ½^wqÿÿ¾z%yÝÇü|KþùþtÐ3ÑåûãýÕþB™O—ï÷Wù e d¤ºå¢mèsÐàäM>Š(¢Š(¢Š(¢Š*¶¡n÷v2ÛÅ·|ƒqÀÕš(Kí)éÿ/øÑö”ôÿÇ—ük6Šw _iOOüyÆ´§§þ<¿ãY´Qp±¥ö”ôÿÇ—ühûJzãËþ5›E _iOOüyÆ´§§þ<¿ãY´Qp±¥ö”ôÿÇ—ühûJzãËþ5›E _iOOüyÆ´§§þ<¿ãY´Qp±¥ö”ôÿÇ—ühûJzãËþ5›E _iOOüyÆ´§§þ<¿ãY´Qp±¥ö”ôÿÇ—ühûJzãËþ5›E _iOOüyÆ´§§þ<¿ãY´Qp±¥ö”ôÿÇ—ühûJzãËþ5›E _iOOüyÆ´§§þ<¿ãY´Qp±¥ö”ôÿÇ—üj„¿¨8p}…2Š@M4QˆÃÈŠvç Àw4TYÚÊåå¶…ÜõfŒhßiƒþ{Çÿ}Š>Óü÷þûöu—üùÛÿߥÿ ?³¬¿çÎßþý/øPŸiƒþ{Çÿ}Š>Óü÷þûöu—üùÛÿߥÿ ?³¬¿çÎßþý/øPÇ%¤o+¤±†•·¹ßÔàä?í0Ïxÿï±QÿgYÏ¿ýú_ð£û:Ëþ|íÿïÒÿ…Iö˜?ç¼÷Ø£í0Ïxÿï±QÿgYÏ¿ýú_ð£û:Ëþ|íÿïÒÿ…Iö˜?ç¼÷Ø£í0Ïxÿï±QÿgYÏ¿ýú_ð£û:Ëþ|íÿïÒÿ…Iö˜?ç¼÷ت—2Ç&¥§ùn¯‡|í9ÇÈjìë/ùó·ÿ¿Kþèìíbpñ[BŽ:2Æ=yÝÇü|Kþùþuè•çwññ/ûçùÓ@ÏG—ï÷Wù e>_¾?Ý_ä*% ÛϘÚÍÝõ´€u˜a·¸º¾kõCä01´‡(ö)ܧø~mÿ0çåëòŒRkëåšKŸ.h4èîšC;á÷g= LÜ ¢°â¯&e.PÆTíÿFÏdvAîrÃvšp¸ûAg¸Ó亪â'P¤Àé󟽞ƒß ôVT“]Â÷P} Hê‘8rH.ì¥W<Ê<žI«:t’K ‰$Òy‘¾ÖócRëÀ8;HSÁ#±®hå›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙQ±¿ç°ÿ¿?ý•-›þ{ûóÿÙP™*ÙmÄ9\ãé“ë@ R*‰€¼Œœ¨=ÍGO¹û¿ÔÐæ·¢ßü(ó[Ñ?ïþÊ(þkz'ýð?Â5½þøáL¨€ÌFRå_-‚IÚ 1:œç×µXó[Ñ?ïþy­èŸ÷Àÿ œù»TäoTÀBz2[¢õéÏëIHÅ ó¨9ÂîäŸøOÿUXó[Ñ?ïþy­èŸ÷Àÿ ­½Ä !Û—TN7{wÆG×Òœ¡Ëż0Ø ÈSvœàûÿ*ŸÍoDÿ¾øQæ·¢ßü)”PüÖôOûà…R¼žO·XÆw}Á@Âf­UÏù iÿï¿þ€hõyÝÇü|Kþùþuè•çwññ/ûçùÓ@ÏG—ï÷Wù Ž?¸ÿõÔÿè+RK÷Çû«ü…GÜúêô¤rÛA;ÆóAÊ3 %¨ÏNƒò¦=”]‰Hø@€B”,A‡æ<Ô<(Å^TV‹iRh¤m©*3Á4Ñm˜Ì ŒJH%ö Ä€@9ú>†‘líSÎÛm ùÿëp€y~÷¯S×Ö¦¢€ðÄû÷Ææ.Çʃ¹yàúŽOæˆaŠÞ!¤Q¯DE à)ôPEPEPEPEPEPEPE5¥\#:†=<šuQEQEQEQEQEQEQE‘ýÇÿ®§ÿAZZHþãÿ×Sÿ ­-=þäîÿSLª×1ÞË 0ÝÇ€¾NãøœÐš*ÙµúGÿ€ãühû6£ÿAÿð^¦ˆÓ~ò€·^IÆ}qÐþ>•OìÚý#ÿÀqþ4}›Qÿ „ø?Æ€.ìC ‘—s '‚;ã=xëJ˜QònÇÔç?úª?fÔè!þñ£ìÚý#ÿÀqþ4pF6…ÇNrsÇLØíŠUTR¥P ¤ž¤œ‘Œ’NO©}›Qÿ „ø?Ƴj?ôÿÇøÐê*ÙµúGÿ€ãühû6£ÿAÿð^ª7ŸòÓÿßýÑömGþ‚ÿà8ÿÊäÝC5ÅÚÊ"$…mêëŸz½^wqÿÿ¾z%yÝÇü|KþùþtÐ3ÑåûãýÕþB£î?ýu?ú Ô’ýñþêÿ!QÇ÷þºŸýiimu«êi·Šm©ß1c†éšÒkKožÞÖ\‚ GSŒ8ªÖâ;ye•ù’à9Ü9§÷53\–!\dLzS¸zÌÖ·.±HCÜÛì‡=#8PØ÷2®}“½R½¹ ¹µóäWµŠé÷«œª¦1ž§ (ÎyÜ çŒù!I'uËDÛÐç¡ÁÈšd¶Mçy‰»ÏŒE'$n^xÿÇçH R_Ý@fŠU„Ì<’¥AÚ¾k”ÿ{iÏôßµN×vðÊã|W†'hÁUy ㌟QÆO#?KòÚA3HÒ&ZEUc’’¸ô ’AþTØìmâíBLnd ÎÌŶ•É$äðqÏlz ¥¤êsß\±y ù‚>˜]ÍÃçwQ»ÓžÕš+¥·‘ñpRB£„ \s×÷±çèþÙ»ocolåâB6Œ»0Aè œ(àp08‚-¤yÞbnóãIÉ—ž?ñãùÐ_ö­ÓÚÉr‚H­…ÙVRLˆÅÊ®sò¶Ô?0ÉéÇ3Ët²ÝíXDqO¼d‚Igòù>Ãyã¿N1“nâÆÞåÃÊ„œm8vPãÑ€8aÉàär}M9­ u™Y8™ƒ¿'%€{´`Ž˜ÏZÍK›è–÷vÖè!eF‘c_)B˜äÿ<''›fôǤKzÁ$h£w!2¡ŠçŒTñÈ9ÁÈçåÓ-&"*üÂÊì~Ý»9ÉÏ99êjd¶…-þÎ#"*ß6ìõÎzç'9ëšÏžúòÛΉ¼‰§CV cR$¦ËŒ}úqË&º¼i#…?´EtÑn¬r³³ŒŒ“Œ‘ÆOLý/&ŸlˆWk¾YX´’3±*w/ÌI8œtëêjO²Açy»>3ÍÎOÞÙ³?÷Ïý d–5ÛXöîmÌ¡Î{c ŸŽ}ªíCgoökqmìY›™‹Ã$ãúÔÔSefXe‚’©§Q@ Òt» *e…'–dß$ó0cÔê0r8éZ‘âHBGÝU 9ÏúJ’”Œ"ª…ä8¦’Xäõ¡°12ëqæÈ×!¾Ó´\$Û¡Á“6nëŒ!;x9=³RÛjsÏåˆÉˆË$DlÛ¸n2}Ó’½>÷^9·ýŸmçù»_;·ìófìç;3·9ç8ëÏZrXۥǞ¨wä°ت“Ô…Î9<žO©  ¶ÖgD¹?»­¬·•…ÕÌpñ ;‡Ì¸éî1u'¼kÏ ˆIG<‡fSþÈ^¤|ßìÓ†“b7þãïÆÐŸ¸FÆTsÂñÀqŒšž[H%&‘2éŒr@89#9ÁäPuÕÒÉ”£Ã5Üð¯$¸ÃHÀ“é…Û·‡=ª{Ûùmÿ´6*³Z Ó òÇÌàû|ƒõ«KiùxLyr4«Éá›vOþ<ßGw§ZÞçí1o »n 0í8É#=#€+It—S°ýž˜àÆöÞœô/žùéÇS§T“L€^Mu ÞòH$@Uœ6 äg¡%ÿ|ÿ:hèòýñþêÿ!QÇ÷þºŸýjI~øÿu¨ãûÿ]Oþ‚´€Z*žŸm«_ ¤g¬[@‘— ž„z Ñ]&ÊɼÛx™_ 2Ò3q´ú“NÀEEfê³Zêv(¤y®99hÑHú3Ã=ñUìµiä²¹º—ÉÙç/ÌD‘ÆÈŒ»›ž~nqŸ˜úrTV/öË´‘\*¡³ÓÉ&ÇÜI€%xäzr2'«7w±ý}œ,²\Ú5qå¹q gŒðpÒ¢ª-ú6[ɨ„Iæ*œò„†ÇLò§? lדÛÙ™g‚[i/8X€õÞFqÛîç=±Í]¢²›Z_²A:”¸ß,û"ʤÁÎO#Ž@'Œb‹ýn;Û"Æ…b:M0GÁÏC7ÊxÈ9ç€ Z(¢€ (¢€ (¢€ (¦Êþ\NøÎÕ'´ê+!V6Ô7wéæ „r¢òÁÛÔç§_jÙh¼ˆ› ÍÔ㌟®3E€m‘ö‹…»ýåÄÑIçíà d0R8*IÎ8éž”§EP—Pd¸uƒR¤>ü0wÛŒ.0GμäwãŽc]nåùãÊýÛ4Ü–ò™wexÿ«—‘ýÏq@tVdz¤åØMeå,rG§Í†p˜Î àô㑞ƒN€ (¢€ (¢€ Hþãÿ×Sÿ ­-$qÿë©ÿÐV€žÿr?÷©¦Uk‹GžMßl¹Œ€±²€?J³EQþÎoú^ßkÿÄÑýœßô¼ÿ¾×ÿ‰  ÔÉ¡Iâ1Ê»”ûàƒÔ{ytªŸÙÍÿA Ïûíøš?³›þ‚Ÿ÷Úÿñ42ØÛ¬>VÂWzÈK;33%‰ÉÆSÐÒ„±·K=PïÉ`7±U'© œry<ŸSPÿg7ý/?ïµÿâhþÎoú^ßkÿÄБiÖ±o U”¦×bêõU«ÓÀô%µ¤VÛŒaË62ÒHÎă,IÇ'sëUÿ³›þ‚Ÿ÷Úÿñ4g7ý/?ïµÿâhõGû9¿è!yÿ}¯ÿGösÐBóþû_þ&€/UÏù iÿï¿þ€hþÎoú^ßkÿÄÒǧ¸Žgº¸•£$¨‘‚==èíyÝÇü|Kþùþuè•çwññ/ûçùÓ@ÏG—ï÷Wù Ž?¸ÿõÔÿè+RK÷Çû«ü…GÜúêô¤­-®'œ;œ( ®@ œw÷5fKë‚O|aqØ_z«E­Ý„WmºFuaF¥Hw;‡£€ƒÚ˜tÈ@o%žó¨cÛû²'ÊÆ6Œ`ƒ×é‹´PxÑíÂ${æhÕfFV|ïYNXyëŽsŸsS-’y¥x¤2‡r2NÒ½ €_\Ú¢€+­”"Ò[Vâ”È\×y%‡ïcXn‰U®g2£ù‹1+¹[xÛ÷IÆ9Ï^jÝI4á ºEousÖvÜ1;˜±p òz‘Ÿ~NC¦D»Ín«ÂV6:.v©$1“È óצ.Ñ@Q@Q@Q@Q@[ȶðbMˆ½ž)ŒÅŽM%Kû5<Ïõóyg›ä|»7nÝœãwÞç¯éÅ>! åÒy¼½Ì☒OLžI8$Žzp1jŠÍþŃc!šr†Ýí•K GcÇlu9>¤ñ‹RY«Ý $ˆØPá@¤•¿ž„g89UŠ(ªXDžVÿu;μŽY÷äoœþ”Ëí2+ß3|³F%ÊFÀo^qž;=1œàäqWh  ’iñÉpe2Hex†6»®0ÇŒñµz8é×-“Jµ“ÍÜŸëd꣺rÙ÷¿©«´PW°‰üܳþöt¹2lÀß ýjÕPEPEPIÜúêô¥¤î?ýu?ú ÐÑEQEQEQEQEQEQEWÜÇÄ¿ïŸç^‰^wqÿÿ¾4 ôy~øÿu¨ãûÿ]Oþ‚µ$¿|º¿ÈTqýÇÿ®§ÿAZ@PTKVx®/§¶PbH„lf`ǃýÚ—M[yn®ZÞæîæ(>A$® nÄ1;pqÏ¿ÐÓΛoqöÁvQÒà Ñ•æyÿ9Å_ìðZ¥¼ ‘Å!Usè}½úÓ'R¼kg‰Ð[#†&{”c Õy9'¯ðž=¿q !¡3Ï(fU€®ãx%€ÁÊœdŸ›¾ ©îaÝ$¶œDê è]uŽF89î}j”Ú*Ë90I$fB|ø<Ä&FÜÄ.F9éÏ#ž´€±.§@— ÒÛ˜ÄÍ*¨ ˆFwO8ŸnFGÔâIn#ò¦&Tba™¶íPsÉ;Àöï‚jê^À°ù¨Aä‚ð«”À8dèœóÈp Z“OÞ·X— 4é:¼+(LÏ#(3Ó®=è’ê.—6Ñ­¬ÏçG#ÂáÃ+(ÆI -Îpp0NFV÷P1é±ÝZFgóŒb20>ù1QLJ$J–®.`žY¼ÇŽ9¾\Y”ñèÜÏ×Õ‹§íÓ-¬üßõOÏ·ïyl§¦xÎßÖ€¨ _êf’A!€m@¢Yví¹n˜Ry8í’r)N©0¬pO,’‡Äh£ £`I8'®pqÁäd:{,1ˆ¦X®$“+—/FFxr:Ž@>Ôëm?ÈšL»™Pß.4Ž®Hçp9ë׎@i¨Ãv걬dO2'uÚ%QŒ:Œn@ÎxÈ¥7£íM Á3ª0G‘@*Œ@ Ý ò<ži§ý›ì_½Ýö[co÷q»;9ëÇÜéïMŸN3_¥ÉxÆÒ¤7”<ÕÇ;UóÂžàƒœ°Ï<2×Rškc#XÎÒy² DÙ÷UˆÉ%±è:äœàcšeî´±ÚÌöÉ;­¨¹V òm!°NHþïN¼ð8$ÑŒ±ùrM –W ð†;nèIÆN¦ àæœš>Ûm¼üù–Ii»gM¡†ìgý®žÝhE²+2$U±•ö8âM@ÁHC>⣟a“Î@Q@Q@Q@Q@Q@Q@Q@Q@Q@$qÿë©ÿÐV–’?¸ÿõÔÿè+@ EPEPEPEPEPEPEP^wqÿÿ¾z%yÝÇü|KþùþtÐ3ÑåûãýÕþB£î?ýu?ú Ô’ýñþêÿ!QÇ÷þºŸýi´UH¢¼½Ô.¡†í`HU Aó»>ãҭǧÝ[¾û‹Õ0~E„'8<ç&‹QUn®eŽx ·‰$šEgýã”PªT@<å‡oZOíŽkˆäŽF,¾RÆÒ7ÊpHd¯Léš·EU—Qµ‹air¬¡÷"—P§£1…^¼œ¡¡µT–hŒ¿<8ó)%IÆN§pÀïÈÁ  TU 5kxåyd™”ª±l©PW`ÏÌr:§=ñkí0â$Nq^C|¥¸#Ø@ÑT®õ8-¬d¹~ß1Uy™¹Çq¹ö§¦£jñI"Ë•Lgå9 ð¥F>`OŒç¶hÕSûB7kq-çJb!FB›•#?Â88àƒO·¾·¹r‘9'†Q”8õRFr9Q@(ª§QµMleýê°FNÉÆr1ž§Í'ö¡”¿–æ6ŒÇx$ ’0Nnzs@誢þÈDˆ#hÞBÌJ• @9qŒó’ôë5 fŠI7:ˆñ¹^6Fç… “ÀÀäð9  TU%Ôâ’úÞÚ5vFòØØR¤ 8<œç Év€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ Hþãÿ×Sÿ ­-$qÿë©ÿÐV€Š( Š( Š( Š( Š( Š( Š( ¼îãþ>%ÿ|ÿ:ôJó»øø—ýóüé g£Ë÷Çû«ü…GÜúêô©%ûãýÕþB ÆÕäEo0œýÕ¤šlmo}ypûvL# †ùAÏz½4ªéŒõaè}ë7íÿÏhÿï¡GÚ!ÿžÑÿßB€+j6¯såâ k˜×;¡¸áIã ­ÈäcÅíU$Ò§òí™pÏ‘|µ¸xB‡`ÀQœ.€çŒbµ>Ñüöþú}¢ùíýô('PÒ.g±[KyPF-¼•d‘ªÎùàa‰Æ3ódƒj[ ]nö²n–æ;ˆÁ'`ƒé’‡žqœóÒ®}¢ùíýô(ûD?óÚ?ûèP íyos1)ˆÁ3ÁfBÏ\#èàgëÇ:?h‡þ{Gÿ} >Ñüöþú™ö[©¯/¶íw»˜ß"ÆxìÀ•Çl`õè%û ñÃËiaº–uVbªÁÌœƒŽ=#õ{íÿÏhÿï¡GÚ!ÿžÑÿßB€2[M–RmØíó-®DŽ*3†g±†ü†q‘OM2o&f*‰34L ÜI6|·Þfè ãÇ'žƒOíÿÏhÿï¡GÚ!ÿžÑÿßB€+.šúÖâAÉ© V#nâ¤cŽq·ã×Ú®Ôh‡þ{Gÿ} >Ñüöþú%Ú!ÿžÑÿßB´Cÿ=£ÿ¾…IEGöˆç´÷УíÿÏhÿï¡@QQý¢ùíýô(ûD?óÚ?ûèP”Th‡þ{Gÿ} >Ñüöþú%Ú!ÿžÑÿßB´Cÿ=£ÿ¾…IEGöˆç´÷УíÿÏhÿï¡@QQý¢ùíýô(ûD?óÚ?ûèP”Th‡þ{Gÿ} >Ñüöþú%$qÿë©ÿÐV™öˆç´÷ЧDÊÑ»)O ÿ²´ê(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ó»øø—ýóüëÑ+Îî?ãâ_÷Ïó¦ž/ßî¯ò‚GQ…vÐàÛYÔY‰7rdûÒkêó÷'çE€ï¼Ù?ç£ÿßF6Oùèÿ÷Ñ®û_PÿŸ¹?:?µõùû“ó¢ÀwÞlŸóÑÿï£G›'üôûè×ý¯¨ÏÜŸÚú‡üýÉùÑ`;ï6Oùèÿ÷ѣ͓þz?ýôkþ×Ô?çîOÎí}Cþ~äüè°÷›'üôûèÑæÉÿ=þú5Àÿkêó÷'çGö¾¡ÿ?r~tXûÍ“þz?ýôhódÿžÿ}àµõùû“ó£û_PÿŸ¹?:,}æÉÿ=þú4y²ÏGÿ¾p?Úú‡üýÉùÑý¯¨ÏÜŸ¾ódÿžÿ}<Ù?ç£ÿßF¸í}Cþ~äüèþ×Ô?çîO΋ßy²ÏGÿ¾lŸóÑÿï£\ö¾¡ÿ?r~tkêó÷'çE€ï¼Ù?ç£ÿßF6Oùèÿ÷Ñ®û_PÿŸ¹?:?µõùû“ó¢ÀwÞlŸóÑÿï£G›'üôûè×ý¯¨ÏÜŸÚú‡üýÉùÑ`;ï6Oùèÿ÷ѣ͓þz?ýôkþ×Ô?çîOÎí}Cþ~äüè°÷›'üôûèÑæÉÿ=þú5Àÿkêó÷'çGö¾¡ÿ?r~tXûÍ“þz?ýôhódÿžÿ}àµõùû“ó£û_PÿŸ¹?:,}æÉÿ=þú4y²ÏGÿ¾p?Úú‡üýÉùÑý¯¨ÏÜŸ¾ódÿžÿ}<Ù?ç£ÿßF¸í}Cþ~äüèþ×Ô?çîO΋ßy²ÏGÿ¾lŸóÑÿï£\ö¾¡ÿ?r~tkêó÷'çE€ï¼Ù?ç£ÿßF6Oùèÿ÷Ñ®û_PÿŸ¹?:?µõùû“ó¢ÀwÞlŸóÑÿï£MgfûÌN=MpÚú‡üýÉùÑý¯¨ÏÜŸ»¢¸Oí}Cþ~äüèþ×Ô?çîO΋ÎîŠá?µõùû“ó£û_PÿŸ¹?:,;º+„þ×Ô?çîOÎí}Cþ~äüè°\îè®û_PÿŸ¹?:?µõùû“ó¢Ás»¢¸Oí}Cþ~äüèþ×Ô?çîO΋ÎîŠá?µõùû“ó£û_PÿŸ¹?:,;º+„þ×Ô?çîOÎí}Cþ~äüè°\îëÎî?ãâ_÷Ïó«?Úú‡üýÉùÕ6bÌYŽI94!ÿÙsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/images/imgDlg.jpg0100644 0000000 0000000 00000056764 12114157751 023571 0ustar000000000 0000000 ÿØÿàJFIF``ÿÛC 2!=,.$2I@LKG@FEPZsbPUmVEFdˆemw{‚N`—Œ}–s~|ÿÛC;!!;|SFS||||||||||||||||||||||||||||||||||||||||||||||||||ÿÀ’ç"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?»­kI0Ê$_aŒì}k/þÿŸ$ÿ¾‡ÿVüA40jzd—Q¬Œ‡ 20UFqƒœg?…I¬é6×6 ö(áGiÆÑƹ01Ž0séÇã]1TÔS’ÜZÜ£ÿ sã?a\tÎáÿÄÒÂ`ßóäŸ÷ÐÿâkúKåÅsnк Qæ)ÎÞØÏAÇóúV¶¹rÑYXD——1–°‹÷)þ­ÁÈ9;½=JÕS†—Žþ`ü™'ü& ÿ>Iÿ}þ&øLþ|“þúüM:ÂÊ .ù4úCÎÎX¿„.3éÎ v¹§Ç…–æçíÉJä4§1)Á8ÉxúRå¥{XZ‘ÿÂ`ßóäŸ÷Ðÿâhÿ„Á¿çÉ?ï¡ÿÄÕˆÝ`ZdlYi“ps™‚À@xèZ¤ú%‘µQÜ Ÿìÿ¶–b¥;|¸Æyç¿u¡B—T’ÿÂ`ßóäŸ÷Ðÿâhÿ„Á¿çÉ?ï¡ÿÄÓSÃÖÒÜØÄt‹3m‘Á ¸©Q¸<ŒA4È´}:Ti£’áâ[Yf!rd#£² ƒŸN§ËG°jKÿ ƒÏ’ßCÿ‰£þÿŸ$ÿ¾‡ÿYúôPDš`Š!½”nåpžq޽r{ñéZWVP^=±¸ó6A£,ÀFÁI+Û$Z9)Ù;ØßøLþ|“þúüMð˜7üù'ýô?øš[ß Â³¤vFWat e‘ÇÌ aÉ`g±þ•ЬžG·6ÓLU[$˜È Tc9W±ëš9hö I?á0oùòOûèñ4Â`ßóäŸ÷Ðÿâi­¡X$ tòÊ XfÚïÈi¼ð1ýÞý«S¶ŽÏPšZFHÚdB‚3È ÿZq§JNÉÙ¹ÿ ƒÏ’ßCÿ‰£þÿŸ$ÿ¾‡ÿ\Í~Ÿa]7ü& ÿ>Iÿ}þ&øLþ|“þúüMs4Qì)ö ³¦ÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰®fŠ=…>Ávtßð˜7üù'ýô?øš?á0oùòOûèñ5ÌÑG°§Ø.ΛþÿŸ$ÿ¾‡ÿGü& ÿ>Iÿ}þ&¹š(öûÙÓÂ`ßóäŸ÷Ðÿâhÿ„Á¿çÉ?ï¡ÿÄ×3EŸ`»:oøLþ|“þúüMð˜7üù'ýô?øšæh£ØSìgMÿ ƒÏ’ßCÿ‰£þÿŸ$ÿ¾‡ÿ\Í{ }‚ìé¿á0oùòOûèñ4Â`ßóäŸ÷Ðÿâk™¢aO°]7ü& ÿ>Iÿ}þ&øLþ|“þúüMs4Qì)ö ³¦ÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰®fŠ=…>Ávtßð˜7üù'ýô?øš?á0oùòOûèñ5ÌÑG°§Ø.ΛþÿŸ$ÿ¾‡ÿGü& ÿ>Iÿ}þ&¹š(öûÙÓÂ`ßóäŸ÷Ðÿâhÿ„Á¿çÉ?ï¡ÿÄ×3EŸ`»:oøLþ|“þúüMð˜7üù'ýô?øšæh£ØSìgMÿ ƒÏ’ßCÿ‰£þÿŸ$ÿ¾‡ÿ\Í{ }‚ìé¿á0oùòOûèñ4Â`ßóäŸ÷Ðÿâk™¢aO°]7ü& ÿ>Iÿ}þ&øLþ|“þúüMs4Qì)ö ³¦ÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰®fŠ=…>Ávtßð˜7üù'ýô?øš?á0oùòOûèñ5ÌÑG°§Ø.ΛþÿŸ$ÿ¾‡ÿGü& ÿ>Iÿ}þ&¹š(öûÙÜŨj’Ä’&•GPÊMÄc ŒŽ§ÝT *ý<ÅPÜ:¦‰jÍ4¸†/,ÇËØ0þ,úúê+)f•®~ÜÌ—‚?õÂ*z¨ÉÝ“ÔöéÇ~$­ùÿ™EMOTmSÓË倕Wô>ƒÖŠÏ‹þE;úøÉh©šJZ ÚñE…ÍóÛ}š=ûîù€ÆBúŸjϳ·ñ Šl¶Ü©ÙK£ôÉãð®ªrbÚ¼Ÿ ª—±[„f ¡bÎЛ?øíiÍG–ʱË\i½ÌÏ4ð—‘ÎYŒ‹ÏëRý‡_ò|÷VÝ›>Ð6íÆ1Ý+¤[ûV8§BNN–Hõ#Š>ÝÒÙpAaƒœú.2zÝ¡ªúÄ» ±Í&Ÿ®¤BÖå‰À\£ÅÙúî÷|Ͻʖo±.È,rí¦kmånŸ'ýVg»é÷yã ééN}?]‘ÙÜÎÎÉå³Á%ºyéí]Y–0p]AúÑæÇÿ=þú}b]XãÛFÕÞ4âvŽ<ìS*¹ëž)ÿٚߤ¿êü¯õãîw¯On•Öù±ÿÏDÿ¾…lóÑ?ï¡Oë2ì‚Ç(ú~ºç.gc¼I“8?0 ×®;ÔÑ[k«:M4r\:͹?&zC‚?:é|Øÿç¢ßB6?ùèŸ÷Ð¥õ‰vAc›º‡_¹º11L ;gvO<òMR}T‘ÙÞÎÇ,ÆE$ŸSÍv>lóÑ?ï¡G›üôOûèP±[$8ÏìKþ}¿ò"ÿØ:—üûäEÿìüØÿç¢ßB6?ùèŸ÷Чõ™öAcŒþÁÔ¿çÛÿ"/øÑýƒ©Ï·þD_ñ®ÏÍþz'ýô)ÊÊà•!€ëƒœQõ™öAcŠþÁÔ¿çÛÿ"/øÑýƒ©Ï·þD_ñ®Ô£,@¦«Ü\”1, ’¼°û@ùIêþíYŸd9/ìKþ}¿ò"ÿØ:—üûäEÿêâÔ –FÈ¡‹)è çÓ*yã·LŠ|wÈÊ¡˜36ЮŒ§8'¡0>Ƭϲ ö¥ÿ>ßùÆìKþ}¿ò"ÿuŸo‡{Ù …V,NX9û§¦zÔáyVa' ¡ˆÁÏ$€1ëF:çŠ>³>È,r?Ø:—üûäEÿ?°u/ùöÿÈ‹þ5Ø­ÄLHYáUòNpsøw›üôOûèQõ™öAcŒþÁÔ¿çÛÿ"/øÑýƒ©Ï·þD_ñ®ÏÍþz'ýô(ócÿž‰ÿ} >³>È,qŸØ:—üûäEÿ?°u/ùöÿÈ‹þ5Ùù±ÿÏDÿ¾…lóÑ?ï¡GÖgÙŽ3ûRÿŸoüˆ¿ãGö¥ÿ>ßùÆ»?6?ùèŸ÷УÍþz'ýô(úÌû ±Æ`ê_óíÿ‘ühþÁÔ¿çÛÿ"/ø×gæÇÿ=þúy±ÿÏDÿ¾…YŸd8ÏìKþ}¿ò"ÿØ:—üûäEÿìüØÿç¢ßB6?ùèŸ÷Уë3ì‚Çýƒ©Ï·þD_ñ£ûRÿŸoüˆ¿ã]Ÿ›üôOûèSý=ÆhúÌû ±Äÿ`ê_óíÿ‘ühþÁÔ¿çÛÿ"/ø×hΊpÌ ûš¨o\M ò”Â’¤EÃüİ\`cù‡z>³>È,rߨ:—üûäEÿ?°u/ùöÿÈ‹þ5Ö.¡lñ,‘»:¿ÝÙ1=2pp2=iVþÙ¤òÖPÍ2 d2zs‘^Ô}f}Xä¿°u/ùöÿÈ‹þ4`ê_óíÿ‘ük¬:…¨RÍ.Õù™HA#‘’9r*8õ(žGÕ$ØZEdãfìò=^Ü÷>³>È,rÿØ:—üûäEÿ?°u/ùöÿÈ‹þ5Öýº ¥²à‚Ã9ô\dô=»CR¤ñ:+‡[¦xúŽ{ûQõ™öAcþÁÔ¿çÛÿ"/øÑýƒ©Ï·þD_ñ®ÏÍþz'ýô(ócÿž‰ÿ} >³>È,qŸØ:—üûäEÿ?°u/ùöÿÈ‹þ5Ùù±ÿÏDÿ¾…lóÑ?ï¡GÖgÙŽ3ûRÿŸoüˆ¿ãGö¥ÿ>ßùÆ»?6?ùèŸ÷УÍþz'ýô(úÌû ±Æ`ê_óíÿ‘ühþÁÔ¿çÛÿ"/ø×gæÇÿ=þúy±ÿÏDÿ¾…YŸd8ÏìKþ}¿ò"ÿØ:—üûäEÿìüØÿç¢ßB6?ùèŸ÷Уë3ì‚Æ?ˆ#Ž8ÒÚ±¢ åzþ/jO²máÁ<¯ÿ[¾lóÑ?ï¡G›üôOûèV<éô_ùËÍg=—†'Šå69˜0Ž=>”V¯Š< ŒËüè©“»¸Í™?×ÅþôÒªj6Í ÆÒ•ÆîHÝÕölþnOõñ½ô©¡´óc¿íŠ@eKaæ;·œÉºS&W‚3ί|ÕS§=«yð*´»”„†UR à°ÈÃzç>ÜWEöúiÿŽÿõèûý4ÿÇúôÎǤï@óyeÛvï6%v»0Ç`ß7<œ~:2ÃæÉîÇ”åñŽ¿)_ëZ?`ÿ¦Ÿøïÿ^°ÓOüwÿ¯@òèʨ€´R²õÆÄSÆzüƒœ÷5dX”ž'I,j>X€>îFßllq°ÓOüwÿ¯GØ?é§þ;ÿ×  ¢YÀ‘€ô¾t¿óÑÿï£V~ÁÿM?ñßþ½`ÿ¦Ÿøïÿ^€+yÒÿÏGÿ¾t¿óÑÿï£V~ÁÿM?ñßþ½`ÿ¦Ÿøïÿ^€+yÒÿÏGÿ¾t¿óÑÿï£V~ÁÿM?ñßþ½`ÿ¦Ÿøïÿ^€+yÒÿÏGÿ¾t¿óÑÿï£V~ÁÿM?ñßþ½`ÿ¦Ÿøïÿ^€+yÒÿÏGÿ¾t¿óÑÿï£V~ÁÿM?ñßþ½`ÿ¦Ÿøïÿ^€+yÒÿÏGÿ¾437šY‰?'Sþõ[ûý4ÿÇúõP®Çsœú ÿYýtOýT7 Óœ‰7ÞUÆàß)þ¹ü*aþ²/úèŸúª7‚wŒC¸ \»Jˆ>là|ÄsòŸÊ€${$=s¶9aXv¨ÆÐ7tÿ¾¿JŠ=8$ òcwÁS !•9SŒœ}N8úæ?íoúeþCÿÅÑý­ÿL¢ÿÀÈøº|¯°›JŽB»vmEED‘7¨Û¸r3ÏùR =6Ûƒ´y,I ›AÝ€A¸)ü;äÔÚßôÊ/ü ‡ÿ‹£û[þ™Eÿÿñtr°.Y[‹(ÊÆÇïg#Œ `Á@ø«^t¿óÑÿï£Y?ÚßôÊ/ü ‡ÿ‹£û[þ™Eÿÿñtr¾ÀkyÒÿÏGÿ¾t¿óÑÿï£Y?ÚßôÊ/ü ‡ÿ‹£û[þ™Eÿÿñtr¾ÀkyÒÿÏGÿ¾t¿óÑÿï£Y?ÚßôÊ/ü ‡ÿ‹£û[þ™Eÿÿñtr¾ÀkyÒÿÏGÿ¾t¿óÑÿï£Y?ÚßôÊ/ü ‡ÿ‹£û[þ™Eÿÿñtr¾ÀkyÒÿÏGÿ¾t¿óÑÿï£Y?ÚßôÊ/ü ‡ÿ‹£û[þ™Eÿÿñtr¾ÀkyÒÿÏGÿ¾t¿óÑÿï£Y?ÚÙé gþÞáÿâéÑêRJq§š{ùw6?&¢Í©çKÿ=þú4Ïàþ¹§þ‚*Y&œHe¶hFæS»œv&¦þÿëšè" 2ù¥Xƒòt?ïUd³‰nå¹eGšGܨܿ(\gð?X^’ÿÀ?öj(šÙ¼Q[eQ$y[2qž29ùGZ†ßNxÙãó·FUH°EL{r¼ävãkJŠÍ‡HHUULJ¦Ò€Ä++|Ç<Ÿ—Û©ã¦>ç¼»åÄr9}¡yŒÆyÏãÒ¯Ñ@ë¦í†DfRäeVØØ õ\äõõì=ózØI*žk3Ëgÿ>ù>ç­:Š/üôûèÑçKÿ=þú4Ê(þt¿óÑÿï£G/üôûèÓ( ùÒÿÏGÿ¾t¿óÑÿï£L¢€çKÿ=þú4yÒÿÏGÿ¾2Š/üôûèÑçKÿ=þú4Ê(þt¿óÑÿï£QÏ,† ‘ˆ*{ûRÓ&ÿRÿîšÎñoüƒn?ë ÿШ£Å¿ò ¸ÿ®ƒÿB¢€5äÿ_ûÑÿJŠmBîÜùv° „²Ú›‚K.ߺyÃuéÇ2Éþ¾/÷£þ•VëXÒ¬Þ(ïá/4#|dûÉùI÷P2;‘èpÔ\´@ ©ÞÇç$yË K$Œåq·Í¹v€®éÇ2É«ÞF²¹Šˆ.rAÛàþ-=±žs´U“Äš 2³C½¢ft>@Èl’HÏBHþ=ðçñ6‡† 8Ç“÷ƒXÀˆõÜ3ßì§ØW,ÜæÒïQ¸æf‚ÐNˆó9MÇÌÏËœcåvǧ]êðJcH#sBiHÆÜØ]ÌË·~ö®CZOè­æn ûÔ£~ëï¨Ý€sÔ}¾=ðÙüM¡Êèò¡•â$ÆÍJžyé£óø=”ûËÖÚ…Ü×ÛZ¶­,‘+£•Ü2ì¶vž6Œg¯±¯®?µ¤³… {¾]Í÷R#ÂçŸ¼Ü zö9©ÿ 6†—pˆ|ö3ž3×øW¯ªúUþØðò»1Y%\81´ £Œ ¸Ç$ê3ÎH=”ûÍ$Ögš(®`Ž7–¼·'q2bAï€øÆ;žÕ·\¤úî-ïÚdžáÊã!PÒX ØÝʧêÃÁszŠÁoéC8yN3Ò>½ÀßCßx¿JÃÊqž‘õëþþúø=”ûÍê+¼_¥ áå8ÏHúõÿÿ}| âý(g)ÆzGׯøûè{àöSì7«&Oõ×ï/þÍUÛÅúPÎSŒô¯_ð÷Ð÷Ä僼î2# ƒü]AäTÊ2ŽècGúÈ¿ë¢èB².­fº»¹KFÐ9.TŒJ0F­kõ‘×DÿÐ…U¶ÿûÿ÷aÿÚ”“³¸ÙWÛòì6 #;Ùxs×3Óscž‹y¥^]¦ß³ipïBŒ¤ËØ{þg;´·9ãµ§µ¬aiú~«§ÎeŠK6É'cnØÿ8ã±"«7‡ïZi$Í¢ï,v®v®sÀxëÇ¥u9˜e!f¡Gó4yWÇÙߟö—ühö² ‰ðÅïüõƒþú?áGü#¿óÖßþúoð®žâcl²<Ñ:¬`³õ©j¾±0²9?øF/ç­¿ýôßáGü#¿óÖßþúo𮲊>±>ádrðŒ^ÿÏ[ûé¿ÂøF/ç­¿ýôßá]e}b}ÂÈäÿá½ÿž¶ÿ÷Ó…ðŒ^ÿÏ[ûé¿ÂºÊ(úÄû…‘ÉÿÂ1{ÿ=mÿï¦ÿ ?á½ÿž¶ÿ÷Ó…u”Qõ‰÷ #œÓ´Kí>õ.U­œ a´»ªG÷}ëNΘï Ü”9‹bírljä?½úsZ¯O÷[ùŠÎU%=ÇbxþäŸîÿQLþÿëšè"ŸÜ“ýßê)ŸÁýsOýT/Iàû5/Iàû5QEQEP¹y£™æ’†62¢´}?Üäð1ŽA  ôUAväI'’<„.7y€6W ä29ôéÎ*6¬À©’6ËbÒ(Só&Ç•'ØõùçcÿŸÿ‰¦ù éŸõÑÿô[UØÒXu rL Âíºw2 ÞÜrÜä``pWìz¿üó±ÿ¿ÏÿÄÑö=_þyØÿßçÿâkCI.lÏ›+ÊË4˽Ï'0 «´…ö=_þyØÿßçÿâhû¯ÿ<ìïóÿñ5»EbÇoysx _—+å9lò:äZZ¿{÷?à'ù­`ê|n³ÆùUÚ7óg¦:Ù ÏÝë@é“©÷MWÓçYa ¹Ù‡.ãï†Îrp½X›ýKÿºh;Å¿ò ¸ÿ®ƒÿB¢ÿÈ6ãþºý Š×“ý|_ïGý+VÓ#¼¸ŒàG$‡i©p:gœëù‰3ÿ²a–{dó¼qå$!a¸Ç1<ñ÷øëÏ æ«iö yŤH¶0ùÙIÀÙ#víòzéèz+M#XµÌbY£ ÆÂe 6Œ¹ä"ƒ‚2ßÚhzÅ”E"†"Ãv% ~䊤q׿''ÕG8^ÓG¨ìa6Â. É[w ±Ú?›$¸€'1· “Óƒœ ·}–ò{}Ûü©7cÁÆk¨ƒDÕ£yxÄœa2¤j¸Ý‚ ¡÷`þíŠw^Õî.gãƒ|ŽÎB¿’ÇŒýýô=ñJ¢¾¬V9Ú+y¼!ªŒá"8ÏI:õÿÿ}| á Tg ÆzIׯøûè{âý¬;…Œ+y¼!ªŒá"8ÏI:õÿÿ}| á Tg ÆzIׯøûè{àö°î0h­æð†ª3„ˆã=$ë×üýô=ð7„5Qœ$Gé'^¿à?ï¡ïƒÚøXÁ¢·›Â¨Î#Œô“¯_ð÷Ð÷ÀÞÕFp‘g¤zÿ€ÿ¾‡¾kácŠÞoj£8HŽ3ÒN½ÀßCßxCUÂDqž’uëþþúø=¬;…ŒôKq¶¸Û…ŒcÇ Ûú æ[¨Î#Œô“¯_ð÷Ð÷ÇQf$xÈ E cº`ú ®\D£$¬Æ…ë"ÿ®‰ÿ¡ «mÿ÷ÿîÃÿµ*ÐÿYýtOýU[oøÿ¿ÿvý©\…ªkÈ`X«‚ùî{pr8•51¡ÜW{Iò6åˆÇÓt9  ièŽöÁö©·`Žs‘èG8=FN1šVÓ¢kƒ;Ú»J]_-;6lŒp1“ÓÔŽ„ÔR„!Ü81î H`Ü€0z¿¡¨ìéèQþT.\‰†îNsÎÜòN~£Üä/ôÖ–)vÅ*Ñy@„¢ãôö©v»»µ5ÅÑž‰íeÚÜcbüª´­½·‘ ´¨ƒTFÜ}½èÔR|ÿóÆoûôßáGÏÿÒžŸøòÿfQ@.eYã ¼qè}ª‰Ub¥”§ ‘Ðôþ¦E1#H÷yh©¹‹6ÑŒ“ÜûÑ7ú—ÿtÓé“©÷Mgx·þA·õÐèTQâßùÜ×Aÿ¡Q@ò¯‹ýèÿ¥Fگ؟dƒ÷Ií\±âf#¨Çòr$“ý|_ïGý)N•ô[åyt(v‘Ó/§¤ù tÚÂG,*Èñ'ÌP£•*9%0ç¶2E4ëK óÇs ‹°’6®v*¤lŰHãyé铌غÒíîç2ͽ²¡Jƒ€F×_¯I¿¥1ôˆd3™%™šxÞ7bW'r¢“ÀëˆÇæhF« ;R)Ú@NèÂ|Ê ),Aöu8o›È SƒZk‹ŸÝIÆx‚[ýó+ªß{¶âOóÞ¬^iò™ÌÖƒ2HÄÈLæ3‚¨¥F¸;<Ž XÓlO¶+—$&æ#*ŠœÜ(  ¿ÛK,É 2'œc1ù«êΊÄ`çã¯\‚2*kIm¯<†I$fcL’Íæ¹ÿcÐÔœgöT]¢o“¨¨œŽ@daž=c­Cq¥Í=úÜÊ…u «a€ &ÀÇY1ƒ‘…ç9"€.µp¼¾Lî"Bò„PLX,0yçæVdq’@æ´«óG–Dh-ŽØæŒ¤Òˆg$±ÉP¼à³ äƒÅmPEPEPEPEPY2®¸ÿyöjÖ¬™?×\¼¿û50¬‹þº'þ„*­·üßÿ»þÔ«Cýd_õÑ?ô!Um¿ãþÿýØö¥X ûÂ䓘ÏóÏ?AC†g]•»Áî?ÃBhmĸrNzú cü?‚1v²>ãy* $íÚ:uÏ>Ý(N „ŽB‘ížõ^xRYä•//á2 Æãh€A¦r9äóSG$ácXšÜ&>o1Žï¾3Óý߉°5Ú2y·V΄³8ÁÈTóŽyÇ8u4 ¤2Ë)»½–I.]— ÇP mÏáýiä-UnnAXÕ¤ñÕŽx$ã¸úc&¥±–èöém~êå1?7;²OcÁR9êmùÑÏDÿ¾…CöØÿºÿ•mû¯ùTÞt_óÑ?ï¡GüôOûèP?lû¯ùSþÑÿL¥ÿ¾j+×Fˆmu8npEg±pèTð Ü ÆF±ïôþ”«öúe/ýóGÚ?锿÷Íb;^Ï—ä+ÜX…ãqמüuöæHžVRgTFôG,?P(òKæ;:¡ecAQãþ™È?à^ßZfž¬š}ªº•e‰`ƒV(ˆÿ–n}ÉãI’fL©_•ºãÚ¤¨ÛýzºßÌPñýÉ?Ýþ¢™üÿ×4ÿÐE>?¹'û¿ÔS?‚?úæŸú zKÿÿÙ¨¡zKÿÿÙ¨ Š( Š( Š( Š( ³ªýæ>¦“Íþz'ýô+;]b–Û—ª«‘ÆyÛZáËXÑžKÛ…E³7–§ä ócÿž‰ÿ} <Øÿç¢ßB£¸Ð¬-P=Σ,(NHÑ('Ó•¢] Â&Ôeå8Evˆ>ƒå稠 <Øÿç¢ßB6?ùèŸ÷ЩM¶µ‰Æ³$î•FOÝë€=jº›F¸x~€ªƒ¸ÄÁO°= ÿ>´ ‘á]IôŸUìÕ4@±C´(Æ2[?žä*ÅɿԿû¦•Y\eX0É<Ž $ßê_ýÓ@Þ-ÿmÇýtúx·þA·õÐèTP¼Ÿëâÿz?éV#½·¶†4•È8,p…‚ žX…NÐÕy?×ÅþôÒžlžå-äIR2™Á1ntç’‘‚zä1œ€>}bÖ8n7ó•Á²”ÝŒnàñ×ã7ö…¨•‘¥Û·9vRŽ 1I9ñƒèk?û"{›i¡ž}ˆÒ\2.ÀJ—. äWkçrzö©N›õº…uwpâ%óåa‚ç9Æá´c x Yu‹xÌ_|“Ë`èÈË•bR2rW“ӑбj³wÚ…P£`’ÌÅþM¸Èaåž:çŒf›m¥´- 2Æ)|ÅŽ¶ ùx8ÎìžqÇL’JC¤yW«qççl†M»=LÇÏý6ÿÇ}ø°5;6Bé8òÁrÙÏÝ%º™û­èpÙuk(bó$›jm,NÆùÈù¸ùNA8$‚:ñU±?s~r?— Qm’-Èû™sÈ;óŒð@<ÕI´‹‹{ilì¼ÆYâhÞM‘…³•xmÆóœ)Èè  ¤½·{!\ïÉPv¤Ž 60HÁàð} E&§oï ¯‰TEgf©<×ç ñÏ®"·Òc·¾7 ûï o y¤¶r úrzz õÍt±¾þÒ7*é7™–( Ÿ–%åsœŒFdõ}µ;5tS8;ʪ²‚T–ÆÐq“q×ôæ­ÖölðÊ––«0µY!rî ³`ë»8Â6ýîø­Ú(¢Š(¢Š(¢Š+&Oõ×ï/þÍZÕ“'úë÷—ÿf õ‘×DÿÐ…T·$_ßaKqãíóÍ[ë"ÿ®‰ÿ¡ «mÿ÷ÿîÃÿµ(ñ܇”#A:{”ãéŸÀûqî2±Ü #” ~d+³Žwg§<{öãššŠn[xr¸99Æø~½;dˆ–໡‘“…Î' }ˆó+È‘¡v8ààgðúôãÞ‘$Y7mÜ6§r•çñëõPѨerÇG9ýj’^32+ZÎ¥‘¼ Ä gÔg'صEQEQEQHH$ñ@ EPQ·úôÿu¿˜©*6ÿ^Ÿî·ó<rO÷¨¦õÍ?ôOîIþïõÏàþ¹§þ‚(^’ÿÀ?öj(^’ÿÀ?öj(¢Š(¬Ûˆç»¸šÔ¹TùŠÃ°)…ãýíÍìP{V•†n&7Fä¨JÁ1ª´a¹ÿd´œôã' «+nšõÍÉpÐ΀¢LG·•CìúˆbÀi»ˆŸ=²@éü>çó éÑy7Z’yÒ͈áù¤Æz¿ ª—Í)¼™ci÷Ä#'h—ÆqôïòñÏjÑ´¶ž¼šåí÷L±ª¬2û¥½@õ§mPÅ‚ÄN9 tþgó  ºd~T&$M'úÍÇøŽÏQŒsýsVfÿRÿîš}2oõ/þé  ïÿÈ6ãþºý Š<[ÿ Ûúè?ô*(^Oõñ½ô­ ?øöOÇùÖ|Ÿëâÿz?éZñìŸó  ¨¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+&Oõ×ï/þÍZÕ“'úë÷—ÿf õ‘×DÿÐ…U¶ÿûÿ÷aÿÚ•h¬‹þº'þ„*­·üßÿ»þÔ  TQE5vâF‘PH‹òc«¨õ55ŒPß[¬±Í8R€Jd¡‡qÃÿV ‘3äeÜrpõÿ>ãþ~þù_ð  ?ÙËÿ=æÿÇÂìåÿžóã¿áU¼ûùøûåÂ>ãþ~þù_ð  ?ÙËÿ=æÿÇÂìåÿžóã¿áU¼ûùøûåÂ>ãþ~þù_ð  ?ÙËÿ=æÿÇÂìåÿžóã¿áU¼ûùøûåÂ>ãþ~þù_ð  ?ÙËÿ=æÿÇ£žÅc·•üÙj1ÁÆ:{ ‹Ï¸ÿŸ‡ÿ¾Wü)YäFGаÁ_ð ÑETmþ½?Ýoæ*J¿×§û­üÅOÜ“ýßê)ŸÁýsOýSãû’»ýE3ø#ÿ®iÿ Š¤¿ðýšŠ¤¿ðýšŠ(¢Š(¢Š(¢Š(¢Š(¦³ªýæ>¦“Íþz'ýô(ôS<Øÿç¢ßB6?ùèŸ÷РÑLócÿž‰ÿ} <Øÿç¢ßB€E0HŒp®¤úO ™7ú—ÿtÓé“©÷Mgx·þA·õÐèTQâßùÜ×Aÿ¡Q@ò¯‹ýèÿ¥hYÿDz~?γäÿ_ûÑÿJгÿdüMEPEPEPEPEPEPEPEPY2®¸ÿyöjÖ¬™?×\¼¿û50¬‹þº'þ„*­·üßÿ»þÔ«Cýd_õÑ?ô!Um¿ãþÿýØö¥Z¢Š(¢Š(£Q.^0¨ÛX)±œš(9ÈÍGLŒû¤‰€È‡½,JV$V!G½>‘ÙPeØ(È'ž”ÁB™À|°#;†Ç·P ­<=¬iþ±Ä‘qŒáÔ“ù@¨¢Š(¢Š*6ÿ^Ÿî·ó%FßëÓýÖþb€'îIþïõÏàþ¹§þ‚)ñýÉ?Ýþ¢™üÿ×4ÿÐE Ò_øþÍE Ò_øþÍEQEU]OþA—õÅÿôT¯^î „s$NþK€U ã’Kt9Çï@ôV »Ÿ(ÆXßåqæ¦ º [vÐ8º p3œX$šëPÚ·Dª eXÀ%qã˜÷5š ½ÔvFˆ(v…ÆKgóÀü…Xªºt^MÖ¤žt³b8~i1ž¯è*¥óJo&XÚ}Â1ÉÚ$%ñœ};ü¼sÚ€4Õ•ÆUƒ ‘sÈàÒMþ¥ÿÝ5[LÊ‚DÄ€‰¤ÿY¸ÿÁê1Ž®jÌßê_ýÓ@Þ-ÿmÇýtúx·þA·õÐèTP¼Ÿëâÿz?éZñìŸó¬ù?×ÅþôÒ´,ÿãÙ?ç@QEQEQEQEQEQEQEQEVLŸë®?Þ_ýšµ«&Oõ×ï/þÍ@ ë"ÿ®‰ÿ¡ ¡+j×›”ÝÅÔ½WÇúÈ¿ë¢èB©Aÿ!kÏúçþÏ@¼¨ÿçšß"*?ùæŸ÷ȧÑ@ ò£ÿžiÿ|Š<¨ÿçšß"ŸE3Êþy§ýò(ò£ÿžiÿ|Š}Ï*?ùæŸ÷È£Êþy§ýò)ôP<¨ÿçšß"*?ùæŸ÷ȧÑ@ ò£ÿžiÿ|Š<¨ÿçšß"ŸE3Êþy§ýò(ò£ÿžiÿ|Š}Ï*?ùæŸ÷È¡cE9TPzd }øþäŸîÿQLþÿëšè"ŸÜ“ýßê)ŸÁýsOýP½%ÿ€ìÔP½%ÿ€ìÔPEPEP$ŒJ…°û¬TþcšHaH¬`òrK1bO¹<š’Š(¢Š­ud/Þ8¶FÁƒ¿ c½ny¿ôÞ/Ëÿ¯Y”PŸ›ÿMâü¿úôy¿ôÞ/Ëÿ¯Y”P÷ßhee{W,Nó,…qÓÀ>•CìúˆbÀi»ˆŸ=²@éü>çó«Pv–ÓÂד\½¾é–5U†Bßt·¨´íª°Q¸€ Ç$ŸÌþtê(¦Mþ¥ÿÝ4údßê_ýÓ@Þ-ÿmÇýtúx·þA·õÐèTP¼Ÿëâÿz?éZñìŸó¬ù?×ÅþôÒ´,ÿãÙ?ç@QEQEQEQEQEQEQEQEVLŸë®?Þ_ýšµ«&Oõ×ï/þÍ@ ë"ÿ®‰ÿ¡ ¥ü…¯?ëœ_û=]ë"ÿ®‰ÿ¡ ¥ü…¯?ëœ_û=]¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ñýÉ?Ýþ¢™üÿ×4ÿÐE>?¹'û¿ÔS?‚?úæŸú zKÿÿÙ¨¡zKÿÿÙ¨ Š( Š( Š( Š( ³ªýæ>¦“Íþz'ýô+;]b–Û—ª«‘ÆyÛZ_ðŒÛÿÏÝ×åÿ@ æÇÿ=þúy±ÿÏDÿ¾…/ü#6ÿó÷uùGÿÄTRèVaœ)É89㨭J(;OžI‰.£‘C ¥>フÈP:p9õàÖPV¿ÿgýÇÿÐMuÕÎÝY ÷Žm‘°`ïÇï[žoý7‹òÿëÐÕ‰«º£êHìî,V8Tœ[÷Ÿ*úŸ™x£Öµ|ßúoåÿ×£Íÿ¦ñ~_ýz¥¨ÛµÁ8ºšÝT’|­¿7 ×"²Å¬-3Bº½Ù•FJà>˜÷«}ö†QöWµrÄï2ÈW1ŒéT>Ϩ†,›¸€ óÛ$ŸÃî:n“u©',ØŽšLg«ú ©|қɖ6ŸpLB2v‰ |gNÿ/ö­KiákÉ®^ßt˪Ã!oº[ÔZvÕ X(Ü@ã’Oæ:«¦GåA"b@DҬ܈àŒõÇ?×5foõ/þé§Ó&ÿRÿîšÎñoüƒn?ë ÿШ£Å¿ò ¸ÿ®ƒÿB¢€5äÿ_ûÑÿJгÿdügÉþ¾/÷£þ•¡gÿÉøÿ:šŠ( Š( Š( Š( Š( Š( Š( Š( ²dÿ]qþòÿìÕ­Y2®¸ÿyöj`ÿYýtOýU(?ä-yÿ\âÿÙêèÿYýtOýU(?ä-yÿ\âÿÙèíQ@Q@Q@Q@Q@Q@Q@Q@îIþïõÏàþ¹§þ‚)ñýÉ?Ýþ¢™üÿ×4ÿÐE Ò_øþÍE Ò_øþÍEQEQEQEQESYÕ~óŸSIæÇÿ=þúú)žlóÑ?ï¡G›üôOûèPè¦y±ÿÏDÿ¾…lóÑ?ï¡@¢˜$F8WR}§ÐL›ýKÿºiôɿԿû¦€3¼[ÿ Ûúè?ô*(ñoüƒn?ë ÿШ  y?×ÅþôÒ´,ÿãÙ?çYò¯‹ýèÿ¥hYÿDz~?΀&¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬™?×\¼¿û5kVLŸë®?Þ_ýš€?ÖEÿ]ÿBJù ^×8¿özº?ÖEÿ]ÿBJù ^×8¿öz»EPEPEPEPEPEPEPEPãû’»ýE3ø#ÿ®iÿ Š|rO÷¨¦õÍ?ô@ô—þÿ³QBô—þÿ³TW>oÆd\0|Ø9ÛÏLôÏlдVEÅÝÁXäY’f,êîáQ´(ÜT˜e°F*Žy'šÞI..|¥ŽhA @Ï–I;†p ÈÎ=óÐmÑQ¼ƒË£ Èp `ã='·’/e û`*fÚò(Yœ0vGNF(jŠÃ¹Ô.c‡ÌYGȬ˜8lòÜN6àç‹îQç²Oºá|ÌZì§æœ“ƒž:ŠÔ¢³´ù䑈’ê90ÚQÃî89\… Ÿ^ hÐ^ºÅ-·/UW#Œó¶´¿á·ÿŸ»¯Ê?þ"³5ÿøó?î?þ‚k®  OøFmÿçîëòÿˆ¨¥Ð¬ xÒmFXÞS„Whsè>^zŠè+WuGÔ‘Ø+ÜX¬p©82·ï>Uõ?2ð=G­=tÛkX™ k1BNéTdýÞ¸Ö«©´k‡‡ì( ¨;ŒLûПóëWuv¸'S[ª’O•·æázäVXµ…¦hWW»2¨É@cÜÓâ€&³TºŽÈÑÅУÉlþx«WN‹ÉºÔ“ΖlGÍ&3ÕýT¾iMäËO¸@¦!;D„¾3§—Ž{Pš²¸Ê°a’2yI¿Ô¿û¦«i‘ùPH˜4Ÿë7â8#=F1ÏõÍY›ýKÿºh;Å¿ò ¸ÿ®ƒÿB¢ÿÈ6ãþºý Š×“ý|_ïGý+BÏþ=“ñþuŸ'úø¿ÞúV…Ÿü{'ãüèj(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ É“ýuÇûËÿ³VµdÉþºãýåÿÙ¨ƒýd_õÑ?ô!T ÿµçýs‹ÿg«£ýd_õÑ?ô!T ÿµçýs‹ÿg  ´QEQEQEQEQEQEQEQE>?¹'û¿ÔS?‚?úæŸú§Ç÷$ÿwúŠgðGÿ\ÓÿA/Iàû5/Iàû5QES5vFa“n_c‚?‘4ú(¢Š(¢Š(µÕ¿xàvÙüp1Žõ¹æÿÓx¿/þ½fQ@~oý7‹òÿëÑæÿÓx¿/þ½fQ@ß}¡”}•í\±;̲ÇLcúU³ê!‹¦î |öɧðûŸÎ¬Q@Ú[O ^Mröû¦XÕV }ÒÞ zÓ¶¨bÁFâ':3ùÓ¨ ™7ú—ÿtÓé“©÷Mgx·þA·õÐèTQâßùÜ×Aÿ¡Q@ò¯‹ýèÿ¥hYÿDz~?γäÿ_ûÑÿJгÿdüMEPEPEPEPEPEPEPEPY2®¸ÿyöjÖ¬™?×\¼¿û50¬‹þº'þ„*”ò¼ÿ®qìõt¬‹þº'þ„*”ò¼ÿ®qìôvŠ( Š( Š( Š( Š( Š( Š( Š( Ç÷$ÿwúŠgðGÿ\ÓÿAøþäŸîÿQLþÿëšè"€é/üÿf¢…é/üÿf¢€ )’ȰÄòHpˆ¥˜úY–ú„’Û6e¥YaÏ–C—###®ñë€>¤ZŠÍƒRw‚)%И‘ظÈg sŒ`Œ0äwì4 Qü˜¤kR<åVC‚H,ªsØ}ñŽyïŠÒ¢³›UØ„¼ *¸9ÜÁœã,0q“Ï2±êJÉZ#?;Q·o<¨8ù½:NhB«j3=½„óGè…†j>îk³,…E½ve¹ P7Luäwî}]¬È&ëþ¹š¹ý“¨ÐJüÿìèþÉÔ?è%þÿöu*_Ý-ÍÔMnÆèCó0?Õ;ŽÞ8÷äã Í™7ú—ÿtÐw‹äqÿ]þ…E-ÿmÇýtú¯'úø¿ÞúV…Ÿü{'ãüë>Oõñ½ô­ ?øöOÇùÐÔQEQEQEQEQEQEQEQE“'úë÷—ÿf­jÉ“ýuÇûËÿ³PúÈ¿ë¢èB©Aÿ!kÏúçþÏWGúÈ¿ë¢èB©Aÿ!kÏúçþÏ@h¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š|rO÷¨¦õÍ?ôOîIþïõÏàþ¹§þ‚(^’ÿÀ?öj(^’ÿÀ?öj(³Þ"LSc•VÒ mV8Àëžã Ç?Z…u4uŠ@’• "²`±ÊŽxå±Ï×8äÌöhóÞáYƒ´cY†0zg°èqÇÖ¢–ÁE¼IYàŒGçÛÝH$àò6ÓÔbÞån<Ì#¡¶0q‚þµ×ðÀ\K¸lŽ£níÃÛ¿ï’M%•´«´²òI½€mÙùBã$Lð:t,–±K0’E B• €A×õð#ë@ɨ"LS @Üï°(0}_\ò1U$Õ§ܪ۱G#ã*¨pyõb8둎æ®>"‘.çÌjT6FIʶãÇ'*皌éq1™¦&EurH;·*ƒÛºËŠž+¡+”H¬0H`Ês†ëÐàŒuõ¤žU‚‘!GAÕ`=ÏAIä´ƒ°fUR8Áv?ô/ÐS¤&B’¢ºªÃ þMnn^ÓÍýÜl®êøF—r 0O×ôô|rËq 4J‘É “!Ãdü½ã7^ÿÍc°Žö[;ÛüÌÙŒ/99ÆÇ dR¥´q´%Q f4\ñ´ãÿ‰5Q@Q@Q@2oõ/þé§Ó&ÿRÿîšÎñoüƒn?ë ÿШ£Å¿ò ¸ÿ®ƒÿB¢€5äÿ_ûÑÿJгÿdügÉþ¾/÷£þ•¡gÿÉøÿ:šŠ( Š( Š( Š( Š( Š( Š( Š( ²dÿ]qþòÿìÕ­Y2®¸ÿyöj`ÿYýtOýU(?ä-yÿ\âÿÙêèÿYýtOýU(?ä-yÿ\âÿÙèíQ@Q@Q@Q@Q@Q@Q@Q@îIþïõÏàþ¹§þ‚)ñýÉ?Ýþ¢™üÿ×4ÿÐE Ò_øþÍE Ò_øþÍEV(½¸1ÇåÜ‘ÂyªT—A·}æ'Ʀ ™7ú—ÿtÓé“©÷Mgx·þA·õÐèTQâßùÜ×Aÿ¡Q@ò¯‹ýèÿ¥$@4¨BÀY?×ÅþôÒˆ×Gþðþto_ù䟙ÿ7¯üòOÌÿ2Š~õÿžIùŸñ£zÿÏ$üÏøÕîÙïZ" E–TVP ýHÇm‡ð‹ûML±â7¼eÔ•åòÊiÎ9ÝßqœPžõÿžIùŸñ£zÿÏ$üÏøÕ;K–¸i÷!O.M€0Á*“ŸÄžœUš~õÿžIùŸñª—w ·6q¢ª,’0lgŽ9÷ÅXªWŸñý§ÿ×GÿЀ4FÕˆ‰b9ϵ&õÿžIùŸñ ÿ©_÷òÊ~õÿžIùŸñ£zÿÏ$üÏøÕ+‰'ûTPÀѦäw%зB£±Þ¨X·ˆ(›änwËòናɆTôûs@›×þy'æÆëÿ<“ó?ãYGX†. ó‚;(8''îöÉöé@Õ–8CÜÆcv’EU,‹­Œä¶=^¹Ç«½ç’~gühÞ¿óÉ?3þ5 R,Ñ$‘œ£¨e> ÓèºNÒj7`*Ge@õ;²AùU…é/üÿfªPÈZóþ¹Åÿ³ÕÕé/üÿf ¬‹þº'þ„*”ò¼ÿ®qìõt¬‹þº'þ„*²³j÷T“åEн@(§ù2ÿÏ7ÿ¾MF§tï‚eE ˃qü-ÿ&_ùæÿ÷ɣɗþy¿ýòh”Sü™ç›ÿß&&_ùæÿ÷É QOòeÿžoÿ|š<™ç›ÿß&€E?É—þy¿ýòhòeÿžoÿ|šeÿ&_ùæÿ÷ɣɗþy¿ýòh”Sü™ç›ÿß&£Sºg„¬Œ`xÀ9Çò4´S¼§Î6’}4¾L¿óÍÿï“@rO÷¨¦õÍ?ôJ޾dðç÷ˆŠÌ¸è ãùOàþ¹§þ‚(^’ÿÀ?öj(^’ÿÀ?öj(¢Š(_yg€‘³œ g >¢Ÿ‹*C•?‡áì}ªô,÷PH!šTTu>L›$®?ˆqÁªÍe1ƒ{: eI‹1 I=pW$d’§¯ئG"Ê¥ä+øƒƒúŠÎûX²Ðy¹¸w’<ƒæ!/´rq˜6>§­Vþ͸!D‰&~mË%ØîÜÀ‘Á^Wž9€7hªv–ÂYáÌ4» 혃׃Ðxúš¹@ZÿüyŸ÷ÿA5ÐÿcéŸô´ÿ¿ þÏkÿñæÜý×]@¿±ôÏúZß…ÿ ©uo¤Ú»+iP8<ÉY '?1Î û­Ó'Ž3±YZ•µÄx±Bdv¢Á€‘¿–É8é“Áã¦@$Cg$!-ÐW Ý<~µIogûC†š?#hÚD§v{ätÇãÛß‹—öÖ÷ ÆæÞ)¶TH¹ÆvÖbÛXµÃÅý‘  ù†!´ç°ç9ü?¥Kfþeî¦Û·f89Î{½=çŠ6 ò¢± XIÎ?<Ê¢ÓãŠ+½M`†8S˃åp:½V»µ–{Ë€Še¶X‹°9—Î|qÇ~9rÖê+¸Ì0e TAävüþ„SæÿRÿÒ'†7I6ó+²•9Èf-ÏjY¿Ô¿û¦€3¼[ÿ Ûúè?ô*(ñoüƒn?ë ÿШ  y?×ÅþôÒˆ×GþðþtIþ¾/÷£þ•Ý•Š7fÈ>¼ÐÑT¾Á'ý.ÿ4ÿâhûŸô»üÓÿ‰ &l  ©ÎyŒG\¶9Ï\š`Ó"y)Ø»#†¤`c±Q×9ïš_°IÿA ¿Í?øš>Á'ý.ÿ4ÿâhX-DŠË#}îXƒ¸íÓ§ñü¸©Q õ‘ŸåœuøMUûŸô»üÓÿ‰£ìÐBïóOþ&€.Õ+ÏøþÓÿë£ÿè GØ$ÿ …ßæŸüM*iûgŠY.®%1’T9\d‚;ë@Ïú•ÿxÿ!L¨. yÊâêxU†=¸Ï¯ Ô?`“þ‚šñ4,öÞt©"Í$NŠÊ mäg¨>‚šlPb’H¾]¯°ýñ’y''9'Aç­3ìÐBïóOþ&°IÿA ¿Í?øšp°Ebc–X÷¿i0,[ÆG,z`óÖb˜ç•s°a´ä¯#Ï>¾ôß°IÿA ¿Í?øš>Á'ý.ÿ4ÿâhÚ¨E 3€02I?™§U/°IÿA ¿Í?øš>Á'ý.ÿ4ÿâhƒþBןõÎ/ýž®¯Iàû5Vµ³òI!šY^@ ™1Ðgõ5ezKÿÿÙ¨ë"ÿ®‰ÿ¡ «iÿ!=Cþ¸Åÿ¡5Zë"ÿ®‰ÿ¡ ¦îë©a±šá&2`’zŸq@Ü_NšÕ­šÄR ÆCüxSÀúqïøu±iÿ!=Cþ¸Åÿ¡5!{Ö*[Hœ•9²pzzûšdú;«©›K¹ýôhª'N~oz½EUóoÿèqÿ}§øÑæßÿÐ&ãþûOñ  TU_6ÿþ7÷Úmÿýn?ï´ÿµEUóoÿèqÿ}§øÑæßÿÐ&ãþûOñ  TU_6ÿþ7÷Úmÿýn?ï´ÿµEUóoÿèqÿ}§øÑæßÿÐ&ãþûOñ  UŸÿ!kïúçþÏþÎC<ÛÿúÜßiþ5&›Ƚºž{I`$j¡°IÆìôúóÈ¿/Üo÷¨ÿ?çÞúwÖ®¬Ú"ð VàÊŽןæ­ä—¨YÍq¸vávò=Hôÿ=*ˆ{Õ,WHœ9$2rzzû [Où êõÆ/ý ª×ðGÿ\ÓÿAZÆ+Ÿ´ÞÜ\ZÉn¯j¡È9!Žzê*ÏðGÿ\ÓÿA/Iàû5/Iàû5QEQEQEQE[Å<Ñ›…ßçrc;²1ŠÐûL>’ÿßGüjsí0úKÿ}ñ£í0úKÿ}ñªtPîÖ+¥P³Ý@$ùE~nwéU~Áý5Î?þ&§¢€oo¨œ¤×I0PZm¼'õ4ê( ™7ú—ÿtÓé“©÷Mgx·þA·õÐèTQâßùÜ×Aÿ¡Q@òãÌRr0ŒuÈÓ6§÷¥ÿ¾—ÿ‰§Ë÷Çû«ü…2€ ©ýéï¥ÿâhÚŸÞ—þú_þ&Š(ÚŸÞ—þú_þ&©ýéï¥ÿâh¢€ ©ýéï¥ÿâhÚŸÞ—þú_þ&Š(ÚŸÞ—þú_þ&©ýéï¥ÿâh¢€ ©ýéï¥ÿâhÚŸÞ—þú_þ&Š(ÚŸÞ—þú_þ&©ýéï¥ÿâh¢€ ©ýéï¥ÿâhÚŸÞ—þú_þ&Š(ÚŸÞ—þú_þ&©ýéï¥ÿâh¢€ ©ýéï¥ÿâiFÕV ¼–#%ˆ=3è­%qNF#r9£jz_ûéøš( jz_ûéøš6§÷¥ÿ¾—ÿ‰¢Š6§÷¥ÿ¾—ÿ‰£jz_ûéøš( jz_ûéøš6§÷¥ÿ¾—ÿ‰¢Š6§÷¥ÿ¾—ÿ‰£jz_ûéøš( jz_ûéøš6§÷¥ÿ¾—ÿ‰¢Š6§÷¥ÿ¾—ÿ‰£jz_ûéøš( jz_ûéøš6§÷¥ÿ¾—ÿ‰¢Š6§÷¥ÿ¾—ÿ‰£jz_ûéøš( jz_ûéøš8 BŒœô¢Š^r\gÚGlúƒêhÚŸÞ—þú_þ&Š(ÚŸÞ—þú_þ&©ýéï¥ÿâh¢€ ©ýéï¥ÿâhÚŸÞ—þú_þ&Š(ÚŸÞ—þú_þ&©ýéï¥ÿâh¢€ ©ýéï¥ÿâhÚŸÞ—þú_þ&Š(ÚŸÞ—þú_þ&©ýéï¥ÿâh¢€ ©ýéï¥ÿâhÚŸÞ—þú_þ&Š(ÚŸÞ—þú_þ&©ýéï¥ÿâh¢€ ©ýéï¥ÿâhÚŸÞ—þú_þ&Š(ÚŸÞ—þú_þ&¤l&\Ì¿üM-•â¦ß¥LØÆ\ÖŠo‰ÿä 'ûËüè  ©~øÿu¦SåûãýÕþB™@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@‡ýtïçG/üôûèÑúèÿÞΙ@ó¥ÿžÿ}<éç£ÿßF™Y±»ýžÞëÌsq+"º%y8eÛÐméÈÛÏ|€jùÒÿÏGÿ¾t¿óÑÿï£YdÓL-EÆågWæÉã [óíg¯MJ«¬Í/öMß˜úUè‰U© íê>¢³µùÝ×3ZýÉ?Ýþ¢€:_ùèÿ÷ѣΗþz?ýôi•^öFŠÙ™Ó¹Aoî©` ~'ž8  ~t¿óÑÿï£G/üôûèÖlÄÙ.ÕÚId`r9Ž ÈÉBž¬sÁ¯©q,2Ì‹Èaó¹l3‚vuÉǽmyÒÿÏGÿ¾t¿óÑÿï£Y{,pËrÞQ‰ €Fr”Ýß¾vž1À>ÜÆ—7MsoÂùgÍí…Ü ?C7^¹çð  :_ùèÿ÷ѪZÌÒÿdÝþñù‰ù¥ZªZÇü‚n¿ë™  ´QEQEQEQEQEQEQEQEQEQEQEdxŸþ@Ò¼¿ÎŠVÿ÷éÂìÛùò·ÿ¿Kþ 6à  #û€cåãzqÅ;΋þz'ýô*ìÛùò·ÿ¿KþfØÿÏ•¿ýú_ð uy´« ®¤ùg€kIHXå,@zŸ¨ªÙ¶?óåoÿ~—ü*i Šu ¹»¤Y|¬„'$“o=Hû¹õÈ$ã9×¢²µoµ-oæîX÷FYƒ79è@ãïdè2Åæ E²g “œ† £·û$tÆÜ0ã9ù¨B¡šY±Áo›<¹ÀÎüMè:úêjH#Ù¨Çr ±´lpHêÑ@Q@Q@Q@Q@ ›ýKÿºi··i ãEåÍ#¶æ eÎÆxúŠtßê_ýÓU58Z}VDEÛÉfÛ.vœMÁü©­À“í¿ôç}ÿ€¯þ}´ÿÏ÷þ¿øVGö‚ábƒÉ,§Ê2“òç%s·<àsÔ ŽsCèyK{umÊqç9\ÁÆy<õÿÓ’=Äký´ÿÏ÷þ¿øQöÓÿ>wßø ÿáYCC”na±Â#¤%U±´¾Ý½Hç¯'žÓͤ#Á2%…ºK.yö†;I90éÇj9#Ü ßm?óç}ÿ€¯þ}´ÿÏ÷þ¿øW;ÿÅïüõ·ÿ¾›ü(ÿ„b÷þzÛÿßMþ^ÎÌgEöÓÿ>wßø ÿáGÛOüùßà+ÿ…s¿ðŒ^ÿÏ[ûé¿ÂøF/ç­¿ýôßáG³‡óÙÑ}´ÿÏ÷þ¿øQöÓÿ>wßø ÿá\ïü#¿óÖßþúoð£þ‹ßùëoÿ}7øQìáüÁvt_m?óç}ÿ€¯þ}´ÿÏ÷þ¿øW;ÿÅïüõ·ÿ¾›ü(ÿ„b÷þzÛÿßMþ{80]ÛOüùßà+ÿ…mÿ§;ïüð®wþ‹ßùëoÿ}7øU»}xmž7ŠÚIí²ϼ>ö3®;Sê8æŠEŽDe"P6Õ²3ß¾(uTmÃí†]»—+»î‚;ÛÖ˜o.<«Ëi ÎÐ’['…Iy’ßͺ¶³š(P°3åF2{JxóœOm Z+€w¬rÈ[œãב@£‘&@ñ8t=NA§TFÞæÍÂÍp&2p¡Ø^˜QØÊ¥úÐEPEPEPEPp¨ýÑüªJŽõÿº?•I@“îGþïõ5JúeHÚÛ">AÀ8È+ž}7gßïWdû‘ÿ»ýM2€2o4ÖòÚ;UÄ^S¹þ1@2OpGÓg½C}§M:º‹`ïºV2>UöqßQ“Ó·5¹Ee‹"IR-üèŒc#(€ `0p8 :ã¿£$ žX 0œ|¾ÜqO¢€2?6ÓV7¿˜û9ò€ãßœ~5vþ=ñ!TÜâXð@ÉÌR3øTööðÂea#–•÷·Ê0ãŸj›}ÿï‘þ4‘|’‹©dÎЪÃ僃 -ØèG^9ç8ãRŸˆ¿¾ÿ÷Èÿ1÷ßþùã@ ¢Ÿˆ¿¾ÿ÷Èÿ1÷ßþùã@ÿRßïäi”òPFUKH<ŒzûûÓ(_õmÿ]þ‚´P¿êÛþºýh Š( Š( Š( Š( Mþ¥ÿÝ4Éä<ÿõí'þŒJ|ßê_ýÓL—þCÏÿ^ÒèÄ  ˆ»÷83dýÐsÉöàþU&Öd_ Ã"F~_/)ÇlwÁ©]So˜áq[s'òÍDÐJªEÀc+ÇÓ¿ñïz±2ÝÜ/’öšlѦVi s‚FÃŽýi±5ô ÷ e¦Ûù«æHÆFB{å¾N¼ž¾ôë™<¨ãK©¬‘;ò‘Œdz~4¦V¶ )–Ê s¹üÀ¹Ã`äíìÍùŸz‰®¯f‚+–‚ÄÇ.ÍŒfgqxÙܰüé ë¤Y"ŽÈ‡E´6v·C™ÁüªÔ’”ÛÃd#!J˜ñ‚ÜÂúœ®jT‚ò?õkn˜P¿)Ç û½MUû.§ÿË©ÿÏ?üoþ"®íÔ=aÿ¾¿ûFûz‚ÌbÀäá¿û§ö]OþxÙÿàCñ}—Sÿž6øßüEJ÷ïÌ“*VÀíŸä  ê¸Ý(]Çp&€"Ú‚œÉ ¨AˉǰÙJôÉÈ{œZ•®¥e ¶Aàð*½X”•×éŠq  œçïÛüGçIMÚÙÏšÿ§øR ýöü‡øPAþ¢?÷Gò©*8ÔGþèþU%>O¹û¿ÔÓ)ò}ÈÿÝþ¦±/s®c’5ÊÑ’Oî—¡ÈÇå@VÍÝÉûXi"¬ ÂHbÚvíã8’Aϸ¤Ô.å0̆~[ÎG‡`ùT#íç¶@ž½¸ÍnÑY´«©ZÅ5ÉÂÈ­œ*‚YdéÓåÀïóOÔˆƒ0~>o~8  øb–ê{²o.#ÌQU à  ÷Ö¦ûŸô»üÓÿ‰ª7Ép Sí§tDÞ,qƒÁÁÁçÓÚŸÆÚpûD 3¬’¤p“•-½€Q‘ŽÆqÀÏAš±ö ?è!wù§ÿGØ$ÿ …ßæŸüMY‚6Šãw22(Rç«:Ô”KìÐBïóOþ&°IÿA ¿Í?øš»E2Ú »†šI˜¸ù¤##ƒÇSéãýK¼?‘¦P¿êÛþºýh¡Õ·ýt?ú Ñ@Q@Q@Q@Q@ ›ýKÿºj9ä:ÿõí'þŒJ’oõ/þé¦Hq¯?ý{Iÿ£€&£¸¦EpGåÄŠˆ8ÂŒ i·CŒ´œné#¿Cùzv© ‚ÙçÓ¯—õë@²F$XžHdÜ€²zžŸwô¥p²Æ±O¹û¿ÔÓ(¨VÖ%›ÍùÙòHÝ#0úp? šŠ(¢Š¯"èN°¼s˼+0 UH-ÝPam!G·¥M!†,oÈÈ^K€2qÇOSQ «6Æ$8Æ%çíþÒþcÖ€$X6(Tª¨À”)|¦õOûìD.¬Ûã”sœc·ûKùZÕ›cœcŽsŒvÿi1ë@ùMêŸ÷Øÿ<¦õOûìD.¬Ûã”sœc·ûKùZÕ›cœcŽsŒvÿi1ë@Û W%‡FÖ£¦‹«6Æ$8Æ%çíþÒþcÖ¢žé¶‹ÊÎÇæÝ‘¤ñ   ×ý[×Cÿ ­/ú¶ÿ®‡ÿAZ(¢Š(¢Š(¢Š(¢Š(“©÷M2_ù?ý{Iÿ£Ÿ7ú—ÿtÓ%ÿóÿ×´Ÿú1(j(¢€ °Iu:¦@Ÿ)p¬A Žã/žæ­¥¬ÊРŀ1ÊðªÉ!Ýã]ð€ óîjAu0@úð  þÍ7¬_÷Èÿ >Í7¬_÷Èÿ ƒíS|þCü(ûTßß?ÿ ŸìÓzÅÿ|ð£ìÓzÅÿ|ð¨>Õ7÷Ïä?µMýóùð  þÍ7¬_÷Èÿ >Í7¬_÷Èÿ ƒíS|þCü(ûTßß?ÿ .ã–(b˜.£åQž¿J†4²L]²È©´QEê#ÿt*’£ƒýDîåRPäû‘ÿ»ýM2Ÿ'Üýßêi”QEQEÛÓ€§8æ1××ܞǡu6ôà)Î9ŒuÇu÷ç±è]@å¹Ù/—2LáC0M£h9ÇR=åN7vêT<Ȍ˼+­·®pyì*Šò %e+ R`pZFÐ÷ÃO?‡Nùâ§Ëöò0—s#³™~e >ààò¹ÎxÏCŽ@.Û\%Õ¼sÄr’.G··Ö¥ª¶‘Ïoe ,±—Ž-§pHÝéïVì¶àÏ9Èÿ9 VkÒéG9É'9Îv{äÿ3õ5¥Y®wK¥ç$œç9ýÙï“üÏÔÐ’ÿ«oúèô¢…ÿVßõÐÿè+EQEQEQEQE2oõ/þé¦Kÿ!çÿ¯i?ôbSæÿRÿwT×\»ͼ€dãþZ%X¢™çGÿ=þúyÑÿÏDÿ¾…>ŠgüôOûèQçGÿ=þúú*)'EBUƒ8Ua“ùš¥%üñÊ#º]ŒÁ,[O¨#w9þœc¸—R@êH¢ªG{´LÆÞL•UU/O$çïb¡7R#¹ŒÜ@x[wNXàpzc­hÑT£½líx& ·Þg‹€O³v«^tóÑ?ï¡@¢™çGÿ=þúyÑÿÏDÿ¾…>ŠgüôOûèQçGÿ=þú¨ýÑüªJŽ`ýÑüªJ|Ÿr?÷©¦Säû‘ÿ»ýM2€ (¢€ (¢€*ÜÝNÒK ÙW0‘8Çû]8ÿ®$¸ÆŸuÆ:܃éþ×°ý}Nt¨  Ñ%ÀÆ4û®1ÖäOö½‡ëêr .1§ÝqŽ· úµì?_S*(4Ip1>ëŒu¹Óý¯aúúœ‚KŒi÷\c­È>Ÿí{×ÔçJŠÍ\ cOºãnAôÿkØ~¾§ ]Yî´–(ábK<ŠÜl#Ô“ÔV•/ú¶ÿ®‡ÿAZ(_õmÿ]þ‚´PEPEPEPEP&ÿRÿîš‚çþFÿ\dÿÐÖ§›ýKÿºj Ÿùýq“ÿCZµEPEPEPEPEPEPEPEPäû‘ÿ»ýM2Ÿ'Üýßêi”QEQEQEQEQEQE/ú¶ÿ®‡ÿAZ(_õmÿ]þ‚´PEPEPEPEP&ÿRÿîšYmCjmreUÚ­Ò9`súS¿}ˆÍ/üò‹þý¯øPö/üõOÈÿ…þz§ä™òÿÏ(¿ïÚÿ…/üò‹þý¯øPö/üõOÈÿ…þz§ä™òÿÏ(¿ïÚÿ…/üò‹þý¯øPö/üõOÈÿ…þz§ä™òÿÏ(¿ïÚÿ…/üò‹þý¯øPö/üõOÈÿ…þz§ä™òÿÏ(¿ïÚÿ…/üò‹þý¯øPö/üõOÈÿ…þz§ä™òÿÏ(¿ïÚÿ…/üò‹þý¯øPö/üõOÈÿ…þz§ä™òÿÏ(¿ïÚÿ…/üò‹þý¯øPö/üõOÈÿ…þz§ä™òÿÏ(¿ïÚÿ…/üò‹þý¯øPö/üõOÈÿ…þz§ä™òÿÏ(¿ïÚÿ…/üò‹þý¯øPä#Âà‘õ4Ê>_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ >_ùåýû_ð Š>_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ >_ùåýû_ð Š>_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ >_ùåýû_ð Š>_ùåýû_ð£åÿžQßµÿ ý[×Cÿ ­gå ¨8UùQ@ÿÙsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/images/elemTree.jpg0100644 0000000 0000000 00000125661 12114157751 024121 0ustar000000000 0000000 ÿØÿàJFIFÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀ¤˜"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?¥§išbh:S#Lw’ÆÝ‹=ŒLÌÆ%$’W$“““SÙÂ{i(Hlžþ2áFˆªŒù2>ðÎqѺm8žÂ:„àptûe?Scù~µGDð|¥Õ´PËpŠge·n˜ŽÑ˜€ wãn>nß’!.k‰2ކ•q°Ñ´±• †ÓbSƒÓ‚™CȪ7RiPkK‹Ã6w7&ßí‹Oµ&í½[çùÖΕfñCnŒ›|»uR»và8ÆÕÆ1Ójõ•‘ªè·rxÅu°]ÜYý€A›[‘ <ÂÜüêqéXNºŒÜnK•†Ãq =ÍôèÚ]£XˆŒíqclª¦A2íõéš´WÃBÙn ®€ `Ydû%¾Ò Nvã‚@ú‘XWÞÕ®5K»È-¥Ž?µØ\D ÈÒ•Ž2®bAp[øŽ'½­;ÃðkÚ]ñµ•#w—iãwb*©!@’¹Âç¾’ñµù¿« ™°Cá뙼˜,´eØ$Ø–vìÛH6ôÁûŠÂÒA,釚±æ:Û[•Lœ ¼dÖ ÖFÐlÞÝ#’ {ø§-"‘•X&pNAÈéœTW>Õî´9“ìW&ñtè-ϸ‡lªÅP û£i;™³Î0höñ¿Åý\9—swT¹ðÖ•¦Ï}&™£Ë2ù,Ù[³y™åqŽ£©€Ñosá«BæÐiš:4&=•¸IL«¹|ÙªjžÔ.“ÅPÚ[GW‰h,ÆåUaPÝ霪êžÖõ;¯?öTQOì© ­:‰T©mÞØ_˜ ò7D¬D_Úü}?à‡2ît>G‡~Ýö/±h?kÿžd·ßÓ?wnzsô¦ìðÉŠ)~Ëáÿ.UvþËo‡ ÷ˆ;y>••¨hÜÚ‰‘m3 ¾¬—ÑÇoä¤rÆ1Ï8s/$’Ä)Áç¥gë˦é–&ű·Ô!iëó´¨ÁýÓ•<õÈR¯|_ˆsy#Ÿ Åæùxu<§òäÝmn67<—ƒòž=^þÅÒ¿è ¤ÿà¾þ&¹ïøF5_ùõò,}ƒýbÿÇÇ÷:þ½=ë¬Ñ,e³ÐtëiÓlÐÚÅ‹pÁ@##Þ²ž)E]H—?2¯ö.•ÿ@]'ÿðÿñ4¿ØºWýtŸüÃÿÄÖÇ“íKäÖ?]}Åí oìM+þ€ºOþ áÿâhþÄÒ¿è ¤ÿà¾þ&¶¼Ÿj<žzRúóî/hcbi?ôÒð_ÿGö&“ÿ@]'ÿðÿñ5µäûRù>Õ?^}ÃÚ¿ØzOýtŸüÃÿÄÑý‡¤ÿÐIÿÁ|?üMmy4¾Oµ/¯K¸½£1°ôŸúi?ø/‡ÿ‰¥þÂÒ?è ¤ÿà¾þ&¶¼ŸjQ O×¥Ü=£1?°´úi?ø/‡ÿ‰£û Hÿ &“ÿ‚øøšÛò}©|Ÿj__—q{Fb`éôÒðþ&ì#þ€šOþCÿÄÖç“G“íSõù0{Fb`éôÒðþ&—ûGÿ &“ÿ€ÿñ5·äûRˆ}©}~Ì/hÌ?ì þ€šOþCÿÄÒÿ`hÿôÒðþ&¶üŸj_'Ú§ûBÌÕ÷0ÿ°4úi?øÿKÿþÿ@M'ÿ!ÿâkoÉö¥ò}©hOùŸÞÕ÷0ÿáÑÿè¤ÿà?üMðèßôÒðþ&·|Ÿj<šŸí ÿ3ûÅí_sþýþ€zOþCÿÄÒÿÂ=£ÐIÿÀøšÜò}©|Ÿj_Ú3þg÷‡µ}Ì/øG´oúé?øÿGü#Ú7ýôŸü‡ÿ‰­áµOµOöOæx½«îaÂ=¢ÿÐIÿÀøš_øGt_úé?øÿ[¾Oµ/“KûF§ó?¼=«î`ÿÂ;¢ÿÐIÿÀ¿øš?áÑè¤ÿà_üMoy>ÔŒ‹ÜÞ£ ’{;Ÿj#˜T””yž¾`ª¶írˆðĆMz ÄN ‰e··v1œdsŒÌSdðׄomž÷E³Ð¯mQö3Ek…8d¼Ž=ÅtÝž³±å£p uZÂýŠmÖÞêX,µi¼NÊo:«mPXŒ²Œã­6÷PTÖ´mi©ªÛÞ¼ÓIq§O "}šuÉg@>ó(ëÞŠt¹aÊÛ~wVV0‡÷5¬šf©&õ–XHÆÏ ;üÄîý~•'…¿äEðçýƒmÿôRÖiq‹m-ÇdŒ5ЧQyã9ÿÇéߨ—óûýø?üUmQY¼5'«ˆ¹#ØÅþĸÿŸØ¿ïÁÿâ¨þŹÿŸÈ¿ïÁÿâ«j±fñV›©w¦¨¼žêÓgž–örËåï]Ë’ªG"—Õhÿ(rG°bÜÿÏä_÷àÿñT¿Ø·?óûýø?üU[µÕì¯5Ký6 K]XyhM¤lÞ»—ž‡ v«Ô}Rò‡${ߨ×?óùþŸþ*ìkŸùü‹ÿÏÿ[4QõJʃ’=ŒoìkŸùü‹ÿÏÿKýsÿ?‘à9ÿâëb ½½µÓ­îöâ+{xñ¾YX*®NI÷ Rú¥åBäc;ûçþ"ÿÀsÿÅÑý‘uÿ?‘à9ÿâëbŠ>§CùPrG±ý‘uÿ?ÿà9ÿâé².¿çòü?ü]Y}gNKëÑwXy‚å£;ü¢ƒ.¤àvëSÙ]èXÛÞÛ>ø."Ycl•apzphúåAìãØÏþɺÿŸÈðÿñtdÝÏä?øøº×¨,¯muD»²¸ŠâÞLì–& ­ƒƒ‚=Á¾¥‡þDÎ=Œÿ웯ùü‡ÿÏÿKý•uÿ?ÿà9ÿâëZŠ>¥‡þDÎ=ŒŸì«¿ùü‡ÿÏÿGöUßüþCÿ€çÿ‹­j£¦êöZ·ÚþÅ)“ì—/i6T®ÙS‡=zŽG}Küˆ=œ{ÿ²îÿçòü?ü]/ö]ßüþCÿ€çÿ‹­Z)}Güˆ=œ{_Ù—óùþŸþ.ìË¿ùü‡ÿÏÿZ´Qõ7ò öqìef]ÿÏä?øøº_ìÛ¿ùüƒÿÏÿWoomtëG»½¸ŠÞÞóþ ÿÀsÿÅÖgᑲ‡c3û>óþ ÿÀsÿÅÒý‚óþ~àÿÀsÿÅÖ•G<ñ[[É<ò,pÄ¥ÝÜà*’Iì£û? üˆ^ÊŠ?`½ÿŸ¸?ðÿñt}‚÷þ ÿÀsÿÅÕè'ŠæÞ9à‘d†Uއ!”Œ‚pE2öî>ÆâöåöAoK#`œ*Œ“×Kû; üˆ=”;>Ã{ÿ?pà9ÿâèû ïüýÁÿ€çÿ‹«vWpê6÷¶Ï¾ ˆ–XÛeXdœžìì/ò öPìg}Š÷þ~àÿÀsÿÅÑö+ßùûƒÿÏÿZ4Qý›„ÿŸh=”;ßb½ÿŸ»ü?ü]Y³¶0Kç\2Í*ð„.ÕORO>ùééÎlQWO‡¥.xA&5N)Ý"qrèÅ£-¥O_ÀŒQö–/½÷;ÿyùð¨*¶Úý¿ìh‹ížWänü¼ãv:ã(ø“-‚ÉöÁilm¶&âdÍ·¹Î8¬]ãP>Ô®ãÖâlømËÚG}$Ó‹…LùÇpÊ7ÌA p+Ó¿¶tßùþƒþûlé¿óýýö(Ϭá½Þˆê:‘µ„¿Ú × LaG Ï Ï”ÆyÉÍR¸Ô5ˆü9y w7?f¶ñcZ;É3®Ë0ÀíiF\&âlçÕéßÛ:oüÿAÿ}Š?¶tßùþƒþûæ÷©Ç¡i1E¯ÖF‰-…ËJaÁ>QvlŸ˜Ó>•SÅV—si¾>Òa7³ÚØ5„ö±4²HSrƒ!É$°À$‚HίSþÙÓçúûìQý³¦ÿÏô÷Ø +Æ:—.ƒâ h`‰¦t†æö@·œ48rUMØçž‚º¯ Ý­ÿ…´ûµ´¸´G¿É¸‘¤uÉ?ÄÄ’POb:t«Û:oüÿAÿ}Š?¶tßùþƒþûæÚ]•µŽ…ñ ÑŒÉ~$Ô pI3–h jUö“Îr>|dç­[ÑëLÔ¾$7f=CN”^$’F nŒƒoÝ]§ã’rsßlé¿óýýö(þÙÓçúûìP˜éÚì—^0Ñ"in--ïZòÖö ¯¤i‰ÆÔóS……ËFÌuÀÀ®‡á<Zø>+bκŒLéynò±0‘,›ABpœg ï]oöΛÿ?Ðßbí7þ ÿ¾År÷Ðͨ|V[ n¯OþÄóZghÑßÏ#’¤zr<œdgJ¼ÕG„ü¬I%ýί¤Åä8ò7H… t<(;˜ÉÎzcÓ¿¶tßùþƒþûlé¿óýýö(Í5¿Mkâ›wŽY¬Ú z;{ƒqxûä·#æc<±ÖÀn¼Ô“Rh—Ž—>¿~ñÉ.‹ˆî¤¼ŽDÉ‚¥dãïÆ (Áã<Œ­z?öΛÿ?Ðßbí7þ ÿ¾Åy¦¿{©^ø§ZXu¸4øü»9t»›‹ÇŠ0˜Ü̈ ¬ ÀîÏo¥z7ˆÖåü/«-”Ý)„>VwïØvíÇ9Î1Š—ûgMÿŸè?ï±GöΛÿ?Ðßb€83[–ûZøy·±L/ˆv*Ò uáÏF`A$FAïXžÔîçÐ|!4ž¡q¬Ëªª\$“;nµÝ.ó´ðÉËH Œ YþÙÓçúûìVn‚º‡4[}*Êý ¼¶e¾f,rF;“@¿줾øw©¬QK,±yrªÆO@ë¸:€»£‘Ðää1ÌÙ]~ͬÁy©M¬<.žcLìÉÚ0ØÊî9a“‚zpÖ4Òp/ 'ýñOþÔ±ÿŸ¨¿ïªòíZãP¶ðOŠî,µ¸¥Œµ£[Cc}$ïjwª¿ïÃc8ϯ㡯Y]]Ðíæ¿¹·¹ðÌ—[Jîd¹¤zЪáNqŒq^ƒý©cÿ?QßTjXÿÏÔ_÷Õbxìð…é‚Îc!ñ Á™¤Ù/–»—’vã”`JäüQq#ê¾5ƒP»¼Œ¦Ò¡Ø ÂþfÔ8ÝăžF2=ûRÇþ~¢ÿ¾¨þÔ±ÿŸ¨¿ïªó‹ …ŽûÃVúÍÜöº7ü#0´é5ÆP0ܤÁqÁÏ n-žàåß²)Ï}ñL³†Ýí!ýÔLÁwÊ Îæ—M»K¯ˆzvË…›n•{œ>ìf[Zã­¤»³»Õ/á»uEÕì`û8QµÄ±ZFåSò¿ÆÏ9ÅmOÜ”— ™Ûýšùáýð(û4óÂ?ûàWbuì4ë8õk¡%ν{Ò˵ÜD¢ä…qÄjÃМãU­2ûR[­2YïÞd¸Ôî´ã(ˈ\mc޲Éà`‘Žõ²˜Ž§ÉµòüÏ.˜Ý»hÆ=sHc´ ŒR®@S†ÏLz×'ÿ4GþåÏý¶«·°Ø vx5DŠ=6 :&´Wù4‚R˜èÁD<ŽW#ɧÌEöh?ç„÷À¦I¤+ºT8ËVÐ$ºé¿Ú¾Þlák•q†²;æ°5øå›Ä’‹—±0ؤЭü{ãw'˜éœyYnJŒ`Ƈ-.LÆÁ6ï6˸nÚ2=iË£0UH Ü$zý+žÔí¬µ+=&Ùt›xoocETšÙí PN;vƒ°uÝ{Tëckaã X$‹H¹09’[\õ dãêhæ`n-”e¢ˆ ÊŠ_³Aÿ<#ÿ¾rÞ*½k»øtx–ô,q›©¦´¶iŒr ÙÁ“ëƒÃVÅýæ±§ø–ý.õ I΂öEm¾Dêg*‚3Ñž¤>{®‚Ç¢ýšùáýð)« «ýØánp ðzÃ…nî¶»„m åÖ}ÛAï‘ã¬\Üßi±ø¢h.®§”ê–ÑGºþî9Ü1L€ýc€Oh'¡É΂Çcöh?ç„÷À£ìÐÏÿï\šO­6…)iä@·›T›˜MÃÃ僀ÿêÃy„õþ޼×G¥ÉŽÌ7SͶÑ2‰°i‡#†lóôè9ªR¸ù6¾g—åÿ¶íÇ®)ßfƒþxGÿ| ǶŽ3ãÍNRŠd]2ÑUñÈ[œ€}ä+3ÄWÚ•½ö»5µûÙ¤G}* ‡—7æ'øHŒ;ñ‚1Êæ²¸_Ù ÿžÿß³Aÿ<#ÿ¾r:Þµue®ï‚âs7–¶Ò)xÒ4a²§çs¶Lî^  ¦Cq«Ï%£ÿkJ¢ûW¼°Ú#\E mpFßöÿrcÀ¡ÆJçÖÀv?fƒþxGÿ| >ÍüðþøÄO¬j’ǧڬ·,¦Kô’X%Š)ȸ&Yþ^T’@'‘€ kÞkÝŽ‘¥H,#¹¼š×B4‘Õdڹǖ­ÆIëéÆió 7ÌÀ€bˆp2£š_³Aÿ<#ÿ¾rÚ„—º¬>yKX]K©Hj6P {€p QÁ#‚sƒÞ-&k˯irÏrR8µ+r›€IL7+åŠäžœ¯ç®û4óÂ?ûàRyÛŠùQn$mÿ´ï³Aÿ<#ÿ¾cÙÇxçXdEV“O²g `±ßr2}N€­ÚiÜÒ’âÙ–$R."ä(ƵØ×#ÿ-­ÿëâ/ýµ×W&+t8…Q\¥Q@Q@Q@Q@Q@Q@Q@œ·…|f0°\Çý9Æ­ž4ÿ‘[Æ¿õÆãÿH⢀+ø*ÂÏQÒ<) õ¤Q 0IãÛn3‚:ò:ìáðîà¿Øz^HÈdü=ë–ø}ÿ ß ÿØ+z³©|B´´×ÚÚÛNº½‚Ýš î +„pFðœ±\sŽüvªŒ[v@u6z6—§Lf±Ólíe+´¼*:ã tà~UÌA§º[">›.㱟÷˨cêFÕÁö•ÔØêº~§rØÞÛÜ$‘‰TÅ l©Æ=9Šþ+¹’i¾Ã£KuoÒB&71ǹ£vþV9áÕ‡¾3WJ£…ì®&Š¿bo3ÌþÍ—~ÿ3w‘Îí»wg×oôâdË·l£c™}Ö9Ës¹²}Ï­ej¾/ñRß2Ùè—Dª¹œ×;Ôàþ•ÖèÚÜWžÒ5‹·HEõ½³Ü ó ¿‹8ë[:òKd+š@½Ò.4·±¹KYàkvXâÛµvàqÇŠ“û9ž xî,n.LYhw6õ?O½î1Ö»:*~²ûÇšðß\Þ‹K¶žà"34g…LíQì 1ú±öÄ“Y5Ç—çé²Ë帑7Á»kŒ3ÐZì*§ö¥öGö·Ú£þÏò>ÓöŒüžVÝÛóéŽhúÌ»ŽpÛJeYM„æEUü“2ô8ª­¤ÈúÌz£[ߢ·{tMŸ Vef8ÇRQ{ö®¦}oM¶·´¸žö(â»* f8߸dcÛ‘Í_£ë2ì9¶•Ù,'Vs¹È„‚Çdúœ?M[&YÌë¦Ê%9Ì‚˜äyú*ÿß#Ò·ïuý+M½ŠÎòö(n%¢>FA$žA«Q´ Ó Â}´Ân9ùŒa‚–údøÑõ™v ÄVM<6Xð‹Ù0««Ça“Û& —E·ž%ŠmI…UGµ¹Ú# Üqé“ë]ÍÔq,·¬hÒ$A˜àv£êY€üií4I¿|ˆ»{å€Ú¼ò}ò4}eö „úrÝ2µÆ”ó2†U2[î 0ÑЎ­9ìš_3ÌÓe5<¹7AëÏÊ}G'së]…V¶Ôm/e½ÂHþLwAçË“vÆú¡£ë2ì8ÿì?³}›û>Ï¿Ìò¾Æ6îÆ7cÎ8Í[Ò‰ZQa8‘€V$äœ}Oæk®¦«« À•8`CŒàþ~t}eö Jé2&³&¨¶÷ÂimÒÝÓgÈUY™N1ÔnýêÃÙ4¾g™¦Êþjyrnƒ;ן”úŽOçÖ» (úÌ»Ž"ëE·¾”Kw¢ ‰Ú[PäL‘Ó“S‹&]¸Óeȸƒî±ÎX{Í“î}k§¡Õ˜.í¢paÏÌ#,T7Ó Â¬Ñõ—Ø,qSiQ\ÁäO£™bdòä¶ »‰$¶êI<ûšž;ia‰"ŠÂtUE„€ tv×QGÖeØ,r-m+²3ØṄr %NÈô8$~&š,™vãM”ls"âºÇ9aîw6O¹õ®ÂŠ>³.ÁcK&‹ËòôÙSÊO.=°cbñòAÀãØzQ“A'M–<"Æ6AŒ*çjñØdàvÉ®ªk¨-å·ŠYUâC*O.ÁYð=ö«ÀÔÔ}f]‚Ç“$­Þ¢-ïšk¨ãƒ'Ê‹í¡F8åØž½jç•qÿ>w?÷è×YPÝ]Acg=ÝÔ«¼´²ÈÇFI>À >³.ÁcšH.^âÜ}’q‰ãbLd‚Oä+«ªÚ†£i¤ØK}p–ö°ŒÉ+Ÿ•Fq“ù՚ʥG7v X(¢ŠÌaEPEPEPEPEPEPEPŸøÓþEoÿ×ý#ŠŠËN¶¿ñ“Ȳ\¾m¥XKf‚W–ó.0ÝrX(^½9â¹]iþn™/‰^õ" i`»4Š0óó½—ø'žíšõª(Ë5-CV°ð¶¤uC|5+Ï [Gª±v»T¸3£)t,xã•wQ°ŸM‡ÆaÇqsªYÜ9g}¯jË•‰!x¹¯ Ž‚½ŠóHÊÿÂ#3K¬Yý“ûO1B²OäíòGîYñæ}ýÒäwÀé]¾ŒÖÍonÉm-­É²€Ém3ð¦j¶Ix'©Ç=«RŠçìÿä¡ë?ö °ÿÑ·uâëk™5^Å-à’ÃÃñ\Y,nÁÈ7d8ï0Ú¼tçx®Ôi¶ƒUmPBë@-Ú\œ˜Ã GN 'ñ5j€<Óź£éÞ)[ßg’Û5ß,’—h Åæ˜ÑFÏ/kHîr$>•%¶•4ÒéòKq¨o¾ñ£mr|æè¡®ÙcÝBcž¼à¯G¢€<³P’ùlôÈƩ }²I‘Žè%¸ÝÌHˆ8à€IÉÅtw'Å áí û*ä^ÏöEû]ÈDÄͱ>|9\dî?vP©Xê·„­õ9n©!¹1§oÙ®° B@aNñv5…¨I|¶zdwLƒLŠãT…¾Ù$È€ÇtÜnæ$D ð@$äâ½NŠò^^ÙøzÖCt'Ô`ÑR[{ç’UYî6ZAËî Ä?2 u¥ñ„ÖwÚW‹^âKÉ/¦µs¤,ó%³Z!ùTpT¹›9ÆïA^­Esþ;ÿ’yâ_û]覮‚ªêZm¦¯§ÍažÖuÛ$dg88íV¨¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Ïüiÿ"·ëÇþ‘ÅE4ÿ‘[Æ¿õÆãÿH⢀‡ßò ð¯ý€ò·ªÚ—Ä[±âo³i¶v²ÁÓÚ…žR’Îð‘çyc¦þ½*ÏÃïùøWþÀù[ÕxzkOˆº¦–ó[éúãÞI~cˆ7”Â0ÌUÈù<Ì}òETOP=MÔ Õ´«=JÔ±·»'ˆ°Á(êdvà×7ÿ µxóËbšlv«q<.<ÂäÅ+ÄÄíã–ˆö"º[=>ÓO¶‚ÞÒŠG/DP0ú àíf¿ÓÒæÖM U‘–þö@ñ@Ydº–E îèUÔÕSQr´ž‚d­Ïîo™íä…" ýšåcCï†Ï×ÒºÍ^Sà} WÔ·Þ[Ù d™'òÐãÎ+ûJóþ…ígÿ‡ÿOŸE¿„Z’t÷žò´µšÐ¾Â|¹a2)=°²{b®·*IE‚;8¯ìç¶{ˆnà’ó¾T\ œ‘Àãš>ßgö?¶}®²ÿÏo0lë½Ó¯Ä\i—7ßÚ—v:Lö–Mý›þ†ñÚ_"奟èÙWý¬cµÙ—8þÐþÉŸû7ûkíÙÞPßä}‹ÈÇ•œ®ùñéóu¬v6Z½–£su´Êïm °G͘ã“+ê6ÊœZv¡©Úéšeþ¡q&`±‰æŸgÌÊwž=vóq\%µ”º*jÞ$]5ôáoªý¦;vÆém$··ITÜ”Üjµ£­ióØü*ñ<—¡´o4뻫§ JзÊpŠ0º‚€7/¼I›s]Xê n`Ë?‘ò[§$ù‡?.Ð2}+j¹?ßKyqœtÛë½{q$òÙF$!‰Vr0¸oPÀõÑE¨A6¥sac5´qÉ!ÇÊ7îÀÏ÷¾\‘Ø=ÅWÔµ¨4Ù"æêáÆá¬~c„鸎Ã<“Uï¼F–Ç$š^¦èð Ù£·ÈŒä9Ïc‘Ú²ìíÇ®pqëƒ^tžÖ¯´ÿYê–PÍ©Ýè+¦­éQ²âhŒØ±änÀßï#àÓXé\xÊ}V]3Ë€iVQÚù±å2ÉpÌ¡zPÑý3ÅhÃâM2I6Ip¶çìÐ]fv6M¿`É8ÏîŸjÒ{ˆcó7Ëùi½÷0žO àóìkÍ´=*}&M2mWÃw©†ôë#²5”Å2yÅÓa>ë“ÛÞ›¢ë¶¾°þÏšþö÷¶úfbpÀON$ÜÇ×ÍþñÈâ€=[O¹{Ô†ökÙtÜ¡°ÞœçèjO·ÙýíŸkƒì¿óÛÌ:ãïtëÅqš¯‡ï~Ï┳µòå¼ÔìïÒH£Vó"A˜¸þ&ýÌ¿!àïÞ5^=$¯‡fw‡VyÛRûBGýØ|úŒì1íþsÔPÄ3cÊ–92Šãk•làýµU]V×eÒ%7[%Ó¶Ï‘QÙ•FïRQ¸ö4Ý,Ê-àK›í®Å¤>p…G”­ƒ˜Ñ»… þ =k6ÒDõx‹¨‘´‹UÏ$ ®òqè2?1@’ÝÛC"Ç-ÄQ»UVp - |zàÐ×vÉt–­qÜ8ܱØsÈOCùWâŸϨ^xªò+5Ãøz;{ _<¢Bz0-=²0jŸŠ­uQ¯›‹m:åÕoìçSij²bGˆÈZRC#a]vûÆ€;ÓfÔÝÁ¹ǘ2ª¤†'Ð'±Kg²\MwpI’¼€+dd`ž×ká\¶˜ÓiŸ3øŠþêð‘ËBÆìÆ[Õ 0½2FESºÑ5X£Óå0N-íî5T0Çj—$,—aà"68 å©ÁìèÒêö§k¦Éh—Ryj•¡ÝcyNOa¶6æ¹»W›ÃÚ¦•©OoöKEŠF¸•ã’L"_n~n}ÍA«èú‚é~ŽöÕõ‡±ÔšâíQË“pù¹lAƒÔàw ¿íöcûgÚàû/üöóθûÝ:ñD·öp[%Ä×pG˜Ù+ȶFF àñÍqÙ—8þÐþÉŸû7ûkíÙÞPßä}‹ÈÇ•œ®ùñéóu¢ßL¹±þË»¾Òg»²_í/ô4ˆHÑy÷+,Bp6Ƭ¿ìçè³Óõ;]N9ÚLùrË ©áŽG‰¸ôÝ`÷ÅGªë6ºF‹w«M¾[[Dg—È]ìœ1Çû89ôÁ®N³Ô¼+£ZjºzA}5Íå“XI&C‰.f{o˜rÁY‡=’Yô­ŸØÃ¡ü%×lÌû¶iWAç€f•Ñ‹9ÿi݉úµvQEQEQEQEQEQEQEQEyÿ?äVñ¯ýq¸ÿÒ8¨£ÆŸò+x×þ¸ÜéTPðûþA¾ÿ°þVõèçÿ¿äá_ûåo^@W5ã]~çBÓ-ÉP]êke ²r°³#¶ò;à!ãÔŽÔÒ»²¢‰üØ’@0CcëO¯>ðе+Û‹{ UÚT»´’îÖi­þÏ"¤r,L¬žåÐQ]‰5«eÓ-ìg½ýÛ@g Æ fÈ‚O•·žÌiÊ-;½Epwðë×v2Û§ˆä&EÛ‰-QTŽù)†éèj„,5-#ÇÛ]ßÅqÆ›s&ØÃŒ2Iî'ûç¥[¤ãÈW=!Ñ$]®ªÀØažAÈ?Ðè’ÆÑȪèÀ«+ ‚PEdAâ&áõuºdÌ ºi#eŠ‚ñóg8äžFaºñ†•mo§Ü‰·Û]Ý5©‚¦&É/*FìŸ/q“¹qœŒä3u"cU@UU€ ™UAs¹ˆxà ŸÀøV@ñ-ŒÏ¥ý™Òþí­`©‰–%ù”ŒŽ#èq÷éV4ýwLÕ.%‚Êée’1’6gRFtår9¢€4h¬—Ö':õî•ocæ½µ„w~a”(f‘¤Tã>Se»qÅV±=íõÕ…íØïmãŽfe©ŽBáNà9øÇaë@^D^Ÿå'·g™´nÛœã=qžÕ%CywŸcq{u(ŠÚÞ6–Y¢"Œ’~€Ƴñ’Å}{{i ž›g-ÁºY=œ•tQò0\’2qŒPý…§kz†£ö·J‰b‰\Gþš¬æAŒFêîÛžy8÷«ú&¦ºÖƒ§j©‰/mb¹XØä¨u ‚}³@¨®r_évöºíÔìëŽI”¢—2 Ê–PüôIcúÆONjeñ~Šo/­Äˆl¬SPÞUXX»$v$uüAÀíÎ_øËOµ±¼»¶"ê;[+«²•. X.F0|Àã‘×<\Å,‘^J·éåÙÄÓLåXòΤ™Gªär=EkÔ~D^Ÿå'·g™´nÛœã=qžÕ•¬ø§IÐr/§pêð+$q³²ùÎÈ„€:ÿ|žøĺšl—J·L@ ´àÐÆÐOääzŒ€hÑY£_Ó ÕݺÜ’Ò6’m‘; ÷°À`‘œ`s‘Ž I­êk¢è:Žªñ™RÊÖ[–N RØßzŠ«-òYé/¨íµH`3\e·€\· r8íU¢ñ“5­ÕÔwÑ´6Ƕè@ÆXÀ#!4§Ed7Š4T±ŠõïÑ –S V d ϳiݵIÈ7¿öÊÞ½¼ÿá÷üƒ|+ÿ`ü­ëÐ(ªz¦—i¬ØIe{™àðpÊÃÊ{0<ƒW( áv‡yaàëK­vN¹pí/rûÜ(‘¶(<ávààw&µüS§ê_Ùé–ÑO%•ãLñI/” µ¼ÑppyÌ ôìkn×þ<àÿ®küªj¦Ú`pþGŠèiÿƒÿÄTú.—­ŸÛêZ…½­¼7ãeÏšY¤’mD1]qr'HñÃ+Èñ§Õʬä¬Åc˜ºÑu%"û#ÆPº‚î&ݵ¾XâãÎ>V"±pqòóÏYxkÄp\‹“e4ZÚêh·wÆvtû@T¾Ñón ƒÛpþï>‘U翳µp—pBäd,’$zóY æ-´JMZÛT¸Ž]õ£¨MË¿ÊO°5¨°7Á[ á½¹ƒÁž»Ð®,Òö¤6ʧÔUeÌyÛ o-N2q€+´GY]20YNA¢@´²]Ãã]wìP‡¼“D¶kA2°ŠIK¯”¸ºduÃf>]GEѵ »í ½îæ;‘4—³ð ';T ƒØ êh  ZΛ³¡êT®ÑÇ{m%»ºuPêT‘ïÍs‘øV{ûKûKÈ ÓmçÓdÓ;‘0a8ç¡;AÉ›$äc°¢€9'NŸK¸“Q»‚ÞÂÞÖÐÀÐÙå–p¤7šF3À*òFæÉ9o‡¡Ô¢ø]¢ÛÙ(ƒRM´Hg\y/å*–e=vòv÷Æ8Íu4Pß|=ÕHŽÊË]yÐiÓiE. E SibÊ2̬ùë†n&µu½RÕ-õ÷Ž8R]_@>Y—ˆfQ68ù”™ÏÖY,ަ)¯ »­ÍÄr¡mဠçiþ÷‰Eprø3P¼°’ÚIa„ÝiÚÄ2°;¼™/gIT´nñœZ×ð¾“scw}w{iÿo…ì?•½zyÿÃïùøWþÀù[× PEP6¿ñçýs_åST6¿ñçýs_åSS–ìÔ5{QñEíÃ_ÞXÇeuu 1Ü,PÚÅÍ:7úÏ0ü8¯QÐu9µ=M»»­ï.mb–h|¶_-Ùeç¦ #šç|Gàñ¬|Dð¶²¶ö­ob. ï™÷¤ù‡Œ|Û\“ÏJíj¤ÓK@8]T\^\ßêK+^ÝŶ Ù"EX®e‰U –5Ï©ÉïY×¾†úé§}[P°0þ\Ä`yÔ±üMn'…üCjÓÇi©ébÝî®'ŒKi#8Ìòà‘ B:”ïøGüSÿAMÿ¥ÿãµ¼'I%}Éw¥ø‚Má…õ²¹¿y­tøa+»t¢4Üw݇Ô8äØÒuI‘[Û(n;U3ۚͰð®áíoQÖ´ë×̱Ž·¶ŒÊ4ŽØ$–܃psèhÚN Ÿ ô­Ìö˦[ÚË"òð«í ýñó`òÁäq@æñÒ[éºõù²’h¬a{»D n­Ó(Î 8 :;ddymrX ·/Œ¢¶¼Õ¢»Òï­áÓ4´Ôæ•«% ÷‡–ÃЕo@N}ïÃ]>K­loõuŽÖK²]I* iËx± pF È WÃ:¥¾¬ò$ŸUÑ¿³nb;UÀ“k Î@ÌÒdz/# õ=¦yuog,m—{}]÷Ί· §ÌýÜsrOØÁoy=Í¥õ´vörß'Sq@tÏ“†Ú~qÇ\%ðËëþ~Û¥‡ÌÒotîSv Ç•‡ëü>WNùíUî|+y¬}©5E%GÓ®tØžÞ-ü¹öowÉ*_÷i¡FKqÓ5ÿáË$ aw{qÙ‡Ž £ q3D‡$âFãÔ¯©"Ålj¬íõF²hnY#ž;i®•• ÒmÙîÉó#èùÇ=q—'…5+ËBòÿSîîRÈÄ"·+Ok;Ìœn$©f\‚s÷°FF Ÿ#ø®Mf8´É„÷0ÜÈníZI£dTAå°`ˆÔŒƒ†$ûP½¾¾—s^-­…ìÐÛ @6K$mµ£L°;ƒ^@SÎ0Lž#ԤѼ1«j¢<¶VS\">v³"ã·FÛC¾·ÖõU'²ŠYât‰!•d$?Íó²…·†Q‹.³¸Ô|®ØÚFe¹¹Ó®!Š0@Üí9ã’EZ½Ô \jZ‰ ,íZ{“ðv.æÚ ö8þ5NÚO Û-­è–ÝcqC‰%I XÝW<*ÀnÚFࣚ›X±øNÿOùíQ±’Þ¨- ‘ üÀ3È·Z§ªønMBóT¸K±Ûm-mÂÈý̲ÈCóó#‰v²ñòîçžOØÃoIi|·^}‡ì‚ Ò¤þQ™P€Hå çpÉâKØj[Ç Ô~tn²KÔ¼Ï.¡’2¿)‰â±ô/ÝiwÑÍ%ÆžGªh¬vÍ‚mÙ”eÏ*ß]Þ£–Þx?³3t­ö-ZïQ?'ßý§ ׌} sßo¿4ŸYëÅ 71 à76’L€-Ô ¨2& 8ùãûÀœq×-|C¢_\¥µ¦±§ÜNùÛ7(ìØ8äð ¬? ø1 ÒGœ ¯$‘yr¤àõ  v¾8·—K‚òãKÔ-ÞãQŸN‚ßb»É$~g¡ÀϔÞêp7Uÿj×zoƒ5 fÒK›[&»Ý.qµw”`§®­A†^ ìJÝ)†ÏV¹ÔPù˜L“îBsÙë“RøÒÞæóÁ啬—7W63[ÅeAft(>ñ9<ô€7h¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠóÿÈ­ã_úãqÿ¤qQG?äVñ¯ýq¸ÿÒ8¨ á÷üƒ|+ÿ`ü­ëÐ+ÏþÈ7¿öÊÞ½€ (¢€!µÿ8?ëšÿ*š¡µÿ8?ëšÿ*šœ·`Bßñùýsæµ5Bßñùýsæµ5dQÛ¹’Ú'c–dþUÉüHkÁáÛu„ʶ x‹ª4|jU·dõ »fìvÎ~]ÕÌü0Û^*Ø5¬Ú{ÚH÷릻=´w^bˆ‚9Ýå†-ösÆÊ¥ÆàzµCww¬—WR¬PF»Û Îx½Vèè6“ÄÖêþDž +"­¥Ã¨aЀèUµbÉá}HÊ"Ò奴~Lƒèé†äqUN“ž¨Mv™âM#X¹k{ ÅžUBåB0Âä ò=Åj×x{G²Ò~!Y 5DºUæÿ6æIzKkŒocާ¥m'Šoá}³O€%†¥…˜ŠrLÍ2óy+òÓ!$g‘ƒ·-5#Ë+ ]Îhº¶«yâRÃQµ[Amek*Ä’ ´p•ð "@ÁSŽIfúŽ©¨øžÜj³Û-½ìVöÆ(¢&ðÊÄnS’ÆFvp1ŒTÑÑXž»»»³ºiîZòÙ. ÙÞ²*µÔ[ÞdP@ª©qלLmåíNPùm"–PØà$g¶GÖ€$¢¸ØõN^ßIxf¼¶½¹·3˜ÓÌtŽíâù­)EÂ.0Ï´Íg˯jc¾-žÓW»wÓ,¾ÑmqyfÜoTg?º1¨1ª¡Šä°”gå€= Š‚úößM°¹¾»EmmM4„µÇž5ÆIâ­~ÇH‘&Ó ma/m7ÛÜO´-½Ü¡SæE ”bð繈¹¤º¢¹ñn£gý¿%Ö•±éú¥µ…´vÓæI„ÞN Ü ûõ=qÎÜŒn5õjö?jGµ1^Aý–ímo*L¬'½x™Q™S,È˜É F1‚HmEs â«›FžßQÓc†ö)l”ÇÏš†;©ü„måä2¹+Ž€sÏÞx¦ý|Gk¦Ùiðj¯oãYµ%ÒÓIÓòkø®Ü0» yR';Â’ÈK¬'åù@bT®¢¹BÖRºÓ®-ì%ÒåÕ­Xº´²ABûp„ùˆTnlƒÎÒ1Vt[êÿÚ(ÿdi,"Iå:u×Ûc(ûðU¿îÛ*8+Œç¥¢±í|Em©ùñiÐÝ=ÒDÒ"]YÏlŒG@]ãd‘Ó'©ÁÅdé~+Õ5 Ãò&—k&­ªØóÚš8V5òòCìc“çG…ÇvùŽ9먮sBÖuï ]êmg$÷ÑÜ_,VŒR7o.yR8‰PpйÉç'­U—âšßiû ý¯‘Mg‚SíRÉåíˆe~Sþ‘kó?~?ºØëh®RßÄדˆmll…Ýô׆չ¸ ŠÞäÂÇr¡ç-Þ™Ë2Õ<;âkQÖ4û;‹(…µ×öœ’<²š"ïÊDÚ ©Â²óäœü¿0mEr> ñEö­§h‘ê¶f+‹ý-o"›z“6Á•™@Âe¥R yÚF+ZÇÄÚn¡y­ºß‰_;|Ý:â%àË:;𨢹Æ}FÓÅV¶ñj³ßGpî÷6’E[H ¹W ª>b¢ÄîÎÒVžq¬ÂC,ZÕþ¡nZêäZÛI ¸·¹ˆ;ùaA}Â=†!Ž Á Øëè®Ë^×’þ]Tº‚Êîo'7%¼†E+H¬c" Ù–Bã…WèüSwwc§YÜYÜ´,5+8¤„‘ÉqL§ ã‡'# s@tQEQEyÿ?äVñ¯ýq¸ÿÒ8¨£ÆŸò+x×þ¸ÜéTPðûþA¾ÿ°þVõèçÿ¿äá_ûåo^@Q@ÚÿÇœõÍ•MPÚÿÇœõÍ•MN[°!oøüþ¹¿óZš¡oøüþ¹¿óZš‡²FAê sžðßü"^ Ó´Sr.LØÌ#Ù»{³ôÉé»{WIPÚÿÇœõÍ•Ï×t1­ÇfõÍ”¶“™âšÜ!l˜Þ2ua‚²7oJÉÿ„2ïþ†½[þüÚÿñšë+2ï\Òìu‹]>ëT³·º?wo-£ÈY‚®œœ@ÇSšq”–Ì O…?³u¤Õ'Ö/¯æŽÞKtYÖUWdf?")'1¯_z³uá»K¯íÞÍÛ¦†å¶û»ˆ¶l•r#Ê‹ƒ•ýØã–ÎÍdÞø«ÃºmÛÚ_kÚ]­Ìxß ÷‘£®FFTœŽ?&Ûw`7IðøÓ5+ÍFMBòúòî¡–K–^‘™ íUP«þ°ŒÆI'>O Ý_7‰­.gû5ž©w Ä3ÛHB« (èèÈWiòpAÜ\‚tºø¢C([c>¡\‰ìUeˆûÎ Ùôëšèl/aÔ´ë[ûs˜.aI£'«GN:n-+°3×OÕ­l¡¶‡V’æCuËssA’AdUDU;‚•ço-“´©ðûMöQ铦?{e!m®²&á‚Ù#ÆGUÇ*ÌSEc\ønÒæ[·2Ì‹wwk{")ó hÈa‘Ÿ˜E‘Ó Æ $Ç«xVÏXžîY續Ý%¢?“&¢Úf™ °äÎA çÆ5»Esßð‰Å,7f÷Q¼»¼¸ò?Ó$«Çä9’ªŠå‘™¹SœàäqE§„m­Ìoo&º:Šê_h™Ã7›äˆ[Œck a·]çnÜ.Þ†ŠÅ²ð÷öv¦×6º•ÊZ4ÒÎl|¸Œ{ä,Îwó9vfûÝñÓŠ]?A{n._T»¸½¸1,—2$JÆ8ذj \òq»ç<ð¸Ù¢€1üO§ÞjZ(†Áa{˜îínQ&¢7•q¤ˆÈB3ƒÖ®jzlZ­¢[Ì\„gtR¤ª9ìJ}³W(  +¿ [ÜÉuðö›yÝŽ‰§Û\Ç’ÃnªËAÁБøÖҧxzm;U¸¼MnúH®.$ž[i#„«Î/y 6…ù¸ £ ÅX‡EÆ©öûËû›ÖÙíb™P%©lƒ°*Œ§hfÜ@$ó6u( jïÁ:}ì ’Ü\™dŽîã+æ]¤¡DªäŒ Á»J… Ž*ï‰4ûÍNÆÒÚÍa8Ô-'•¥®Øâ%b¸s˜޹ÏìQ@Q@Q@ãOù¼kÿ\n?ôŽ*(ñ§üŠÞ5ÿ®7úG|>ÿo…ì?•½zyÿÃïùøWþÀù[× PEP6¿ñçýs_åST6¿ñçýs_åSS–ì[þ?#ÿ®oüÖ¦¨[þ?#ÿ®oüÖ¦¡ì€*_øóƒþ¹¯ò©ª_øóƒþ¹¯ò£ WŒjZü>(¿R²½¼ûMÝÔÁ`ÓÒ^Ç/èg#ˆøRŽäÚÇÙè§8» 8Ïh>3Ñä‹þ?G«[­¨‹ìâÙT¤Ÿ/Íæãsãe¹9Éæ¡Ñ%‘4é»(:–£ÐÿÓìõÜ×9'´9&–\jQ™ey™aÕ®£MîÅØ…YŒ³1ÀdšªsQ•Ú1®ô=þåîotm2êáñ¾k‹8ävÀÀË2’x~&•©Þiü3ua \µ¦—n‘ÎÅQ¼Ö†#–‘ÞpqסÓÿ„Cþþ¯ÿƒ«Ïþ;Vu/ ØßxbÛÃéG§Û½¨XdO5LPIùd1änNzäæYÆV²°$=þ¤u›í#T’Öyí­à¹Yí`hT¬­*…(Îç ÂNws¸ dеHn¯ò[¨|-ãXCëcÚi¿j¶¶º¿yn#!$a?œ²9ž2¾Xr„’?xEuö~Ñìl.¬bŽí­î_ÌtšúypûËïBîJ>ò_ràîÃg ¯¨øB ¼5«éZuÄO©Û›i//$–ñÂW’MÄÏ´nÀ,NH šæ³gáí÷W¿}–¶‘4¯‚ltUɱ8g’@¯<ƒÆí§K¦iþ$Ñ®ut½±™§þÙŠæáX×ç +8øb1 †mÃÒîìmï¼s˜°Ê³"–;w¯Ý$tl˜g8`¬9PE}OCÓµ(ß[y|·WduÎ:2G*¬9á‘a‘Hæ&×¼Acÿ $žeá¶Ö¬ìl xšT›ìÙ à±%«jïf÷·6l‹§\(·™Ík“û§ ѰA ’§7ü?ªjº¬w"îkK{ÈÞ"ö-e"Ij¥ŽõfgÄÀ€Ê² J1€*4!ðþŸmªBµÅ)vs^L°lî>Ho/$’Äíå‰n¼Ñeáý?OIµ—‘ãgš[É¥•¶6åS#±}€çäÎß™¸ù› üfÒ¢G<ðùÚ…Œ.ðJÑ>Ǻ‰RʱõcÄÚæ‘£ «`šå®ímÒ9ت7›ÿo…ì?•½zQECkÿp×5þU5Ckÿp×5þU59nÀ…¿ãò?úæÿÍjj…¿ãò?úæÿÍjjÈ š–¥g£éÓ_ßΰÛB2îr}€rI$$&³ü5â]+Ä6Gû6å¤{p«,rDñ:äpv¸8>†ªxßAº×4Û,‚=Îz·©à ö£¡L“€HrFxÈG)ð¾ÉµkkZ=ÜvQÙÏg ê2‰î.‹Lär¤`)Œ"ޤÐZ’\»èº–©i¤Û¬×M&ü¸ãŠ&–IáQf8 Hà)=5ƒ¨øûKÓôù®šÛSAðn´éí£É8¤‘¨ÉO°ÉÀ.ñY){á“#.´¥ÉÆüy]T+p¨Á’P¬:Ø5¥*\úÜM؃Â-|S­¾™6é"Û=Æa»Y¸VE9ÀãÚ×c;OñMÝ)“UïVÎ?{kP5Þ¥§¿ŠÝu[¹Kë¶VpùÛZÇ2Ú«ÆÜÍŒäe Å¢¬ye`GyßdFyÇxq^/ø‚ÞMTêMŸ`†WT&^é¶É°-ódaWåeàýãgD³¶›Zñ¡xÂ<ú„pË,lc ²·ÀÞ¸aÌF’1YŒéòÎF=h0È ý+ÎâŠ~_YÚ¤q¯öÅ͵¼(„3jN‘¤Š?å‰%UÔuŒ°«á a§kšÍ›ØØiÏå[L¶ºcfÕcc"†û©™K#î;GÊ"àÐ`H$àP# äU=VÎý.âÚ{;[ÔdÈ·»Äì9PÙ 9ÁÇ\W/¤½7ÃZ¼z.ŸêPÞìm:«³0ˆAeR‚6Yq¹w³6|¶bªÙ’$‘ÇZZò‡…¦ð7ÄÛkí>ñvy“¨É®Òý†ÉØÌUqŒ*îP¸Û…ô?ë+áýïRòæH”mã´ò± cœ³•\àã9  N´W‘¶¡uoeýƒ©¬ÚÈ÷¶7‘ÞµƒY´ÆK¨£»P%ó$óoßíÆÅ î\]k'Å/k¬Ü4çÄÖßjD’;t—삨 v‘3.3Ðg!‹1ô Z+ÌüRóéÍ©Ûͪ+}™4I~ß{yRuNé ʨQÆ@„’tîõ=OH»¼ÓÓUžñ"—I‘nn/3©ò"®Ý‰Ç˜ç¦;š+Ï¥Ôï5OÛ-¾µ:ÙÛx•¬ü»fc¢éÞkFçiÎ%1ž7áJØÑ5oê>$ó|›³aý¡wm?˜ÖâÙ!‰¥DhÀ>vòÑÆì¯Ìä6๠IÀ®[Ó_ÜIyiªjW«ªD`šhñ…T³sU'Êrޏ™¨ÎÒA2øæn¼9½ÄI,2êzrIŠ]Mä ‚Ú€:J+Å·wÖZɦÜ%½ÓÞÙÀ’¼~b¨’æ(Û+‘‘µˆê<y¬)µ]F ký,êWÓ\Aª­•¼ÐÅÚ®µK‚78XTÏóÆÄÛÄ5w4WŸiZ®¹¯\hÖGSŸOE©‹¦X¡yÛì×QCÎàüħ-€2¥`‹Å:í¯‡ ÖfºŠæ{ÿ Üë"ÜÀ%‰!dDîÚ|ã»s1$6Ž(Ò(®KD¼×­íõy/m牢 u–×ûA i¤—oOôPß&<|¥²Í÷¸t?jÚõÅ͕֛&˜¦ÝŠ\ÇÈel€1çÛ"g’zžŸtŒà¬£­yös©Þh>Ó_\½…¯4V¾¸½SNξ@K£.?|ÝFã±rß{:> ûF¡à+Ÿ²jQ ›‹½KÊ¿‚ È®¦Äª„FH` >™=h°£­yºøÇ^Ô³äÙMeý¥vV¿(²Ý~çÍa•ùÙ<ù·!Æ>Á&q–ÛoN¼¾¿¸µÒãÔ$Ó¢šãX™ç´Š%ra¾¢üÈË‚$bNÝĨ9ë÷­FEyŸƒ$º¸Ö´i—Y–kY—[•ã¶Ú¶× /ÀÜyó 1ÀQƒ‚Û®x}BÓOðŒ÷ÆæÞûÃâäFbUX<¡n¨ò³Å‹d€FÑòРRRp'Ó5Èhž+Õõ=^ K­ìÐÉ»tÞ]àÛ…$s%²/P,:ñ“€_o¦é—~,ŽûF²´´û%Ä­}{Jy!WF‡ à3orNÆ£– °¬Ü¤ãpϦhÈÁ9u®'KЬü=âÄ/i¥ÞÜjww]Cd©wk¹žF2>Idùü½ß. pwü¼ôkg¡j¶—3O}¢Œ—&Þ «t°&’b[÷¬»ÓÎeËLÅSn"‘XÖ ©ÅÍøÚeÑì^H‘ÞcNxÙ”ö¸—#Ðá˜dv$w®’€ (¢€<ÿÆŸò+x×þ¸ÜéTQãOù¼kÿ\n?ôŽ*(ø}ÿ ß ÿØ+zô óÿ‡ßò ð¯ý€ò·¯@ Š( mãÎúæ¿Ê¦¨mãÎúæ¿Ê¦§-Ø·ü~Gÿ\ßù­MP·ü~Gÿ\ßù­MCÙU EÓü=¤Á¥éVâÞÊ Þ\A™¶îbÇ–$õ$õ«ôR¦¡¥éú´ ¥cmy ¶õŽæ‘C`ŒáÁ<ûÖoü!žÜþ­$d°EÿÄÖíqz—Ä -u­¡°¾½†Ø´77V¨­O‘¹qÌWíž:‚)Æ-½è´ÿhºLí>›£éös2ìi-­’6+q•ã qíVå²´ŸÏó­a“ífß>dcv³Õ~fàñóSIi}i}sÚ\ÅÞ¥Ùq6È‹*àŒ0 0OÔÔði)k ¥­”6v‘Ý%ÛAiÄ’H„2– ;2£q‚J(9Z«E¨ÙOwukÔ/qi·í«‚Ñn—pí‘Ï=ª@u핾£hö·qù°Iñ’@p88ê§ ðFA(³²´Ó­RÖÆÖó²#‹““€8’†cM¸ÒŽ« õ¼šxVcr² Hc»§~”iº¾¬@óé·°]ıžqœ}EC¬hvºÆ¨é¬ÒZǨ¡K™m‚¬Ž …9%H$  ’3Ž˜ÀÅù ŠW‰ä‰âmñ³(%r=†} ê;ëëM6ÎK»ëˆ­í£Æùe`ª¹ “î@¤±Ô,õ;D»±¹ŠæÝòXœ2œG½:êÊÒú1å¬7ØY£9R§ƒê¬Ê}˜ŽôIei4$¶°¼åîfŒv1dÉÿe‰aèNEAs¬é–v·w77öÑAfâ;™@!H {:õþð«Ô–V“HÒKk ÈÞ^æhÁ'cLŸöX–„äTÚ.•ea5…®™eœû¼Ûx UŽMÃrƒ9íVaº‚â#,3G$bFˆ²° :±F_¨`A£ ’޵U‹KÓàƒÈŠÆÚ8w#ùi …ÜBcP‰ƒÛjã ¦bé_Ú¿Ú¿Ù–_Ú?ó÷ä/÷výügîñ×§,Ú…¼×1Fë催àæ>Èÿï¦G©â¬Ð]J±µ6¶ze•½±•f0ây€‚cp*¤¿(ô¤Öt˜õ­5¬¥ž{qæÅ2ËPèñȲ)yPjñ  ž½-G4\ I¢I2¸WP@e!”óÜ=ˆ ¸ÒôûÈ. ¹±¶ž–ÏÇÛwt®ÄÏ®Ñè*x¬­ ò<›Xcûµj"·BÄ‘¡fr¨ ÌK1ã¹$’{’MIE@¶V‰åíµ„yr´ÉˆÇË#nÜãÑŽ÷ÉêwSP]hºUõ¨µ¼Ó,®-„­0†hÓÌ$’ø#‰f$õù­^ªÓjv×–¶s\ÅÍÖáo8 .Ñ–Ú;àrhËehž^ÛXG—+L˜Œ|²6íÎ=ï|ž§qõ4Eei‘äÚÃÙâ0ò0<¸ÎÜ¢ã¢ü«Àãå‚§¢€ ͇ÃÚ-¾¤u(t}>;òÌæé-JY³¸ï9999ç&´¨  –š^Ÿa=Ìöv6Öó]6û‰!…Q¥l“– eŽIäúšKÓä‚Ö±¶hmÚ3 •…aJ aHéVè  ¶“±´3OÈ7¿öÊÞ½¼ÿá÷üƒ|+ÿ`ü­ëÐ(¢Š(_øóƒþ¹¯ò©ª_øóƒþ¹¯ò©©Ëv-ÿ‘ÿ×7þkST-ÿ‘ÿ×7þkSPö@QE 󛿇—‘ø†KÍ8éòE$÷ÍxÒy¶qÄÛF×êHÜGaÆ7F¢š“[Êx[ádž¼t.ô{7ŠèÁä<Ï)f‘x'=²JƒÀÍØø‡G°ŠêÖïR¶‚â=GPߎ.o'a‘î?z}P¨âî+sÿ g‡ÿè1gÿE9¦šÓே9®í¤iÏj ™A–`£'Œ`ç8ÁÎ+Ñ* »;{èV˜Ä‘¬±Ì’0èáÐñèʧðª©UÔµÁ+×…&µ}gSMîK­míÌr‡EÁi¼ÐÉ9Ú!Êç ã“™t³j·¾57ÞWÙ  þp<¿°ÛnÝž1Œæºz©m¦ZZ_^ÞÁIïYᷱʡAÁ8h€3ž•Î×R²Ô~j±Ù\Ç,±jóÊÒFÛ¢¾¢ïÉ L3Á Ï­o j1>¯«ß\ê–º‚J¶‘.§ yQ,è¶Ê70ʳêI7tÅv•ͽá‡í‰2‰pgއÈÏBê Ô'ÚuÌÆæ+P‘3yòŒ¤|}â22¦ErºI¿¿ðÖ­&‹qjW¤E˜ˆn¸Œ# ¸ŒÇ £Áß‚[°–§@’Æ’(epA”‚§êzO -—v ø™mqgkmÇ1U·™äË>-ij(-Ÿ”–<–-ž™=׉õ;½/CšM6ßí:¤ß¹±ƒÞLßw9#å³s«ÕwSÓ-56}:þ#-¥Âì–0ì›—Ó*Aǯ¨â­ÐêVWvk¤_éz¥¦”÷VpÍqx¬ÂH¦Œ\–xŸŒÄ¦n‰f~ ¯{`öRx©-g¼´º¼×ôÇG3Èöª]Cd[Î^˜ùJžèôPšx¢ØØ>§co. - Mãï<ˆÇQ”»&ýÅŸzôQŒ*Fº#ÔŸA¾¸ŸE‚].c:ÜÉ2£ ²n¿xÄœV2Ëœçœú=æ›â׉ms=´²j1ùÖçŠ×q+Á êõW93iú~±`ò\}‚ÛXH"iïdŠ8£6‘J|ÙÆd æ3`ƒì‹¹ètP˜xngñ=Æ‹ms¨Ü=´QjÉ:Z]¾×ÝB!S ð²Ù F $3ZÜk‘xzÆòÒîúãVÔü+u}!œ½Ú¥¹‹jcjepžûˆÍz}ÂxNâåíõÆÓ¯ìnUmÐÛ[ZßÉ{å͉2ÌÒí?7îÀ]À|‡‘’kO×*—Q‘uË.×Ê%O‘|ùågôÏoDZê( /ðUìSÚø>KMJúëVž:²\Nì²¹ ÑŸ•WÌáÀ^NâOaâùxOþ²éÕlÙYÛéÖö6‘ˆ­­¢XbŒB¢€çÐQÜé–—wÖW³Ä^{&w·mì3)Rp ÚHädã­[¢Š(¢Š(¢Š(¢Š(¢Š(Ïüiÿ"·ëÇþ‘ÅE4ÿ‘[Æ¿õÆãÿH⢀‡ßò ð¯ý€ò·¯@¯?ø}ÿ ß ÿØ+zô (¢Š†×þ<àÿ®küªj†×þ<àÿ®küªjrÝç:—Äiãñ/Ù´Ý> ‹xešÔù·\³Žæ éeÞÛX,'íƒQ}Ì÷9t=6ãvâ>C•Ú€: :þßUÓ-5F-mu O‚Q”08=8"«sMTÔ¤’ê8£Ó_eÔ’«ȯœžØaϨ#¨5‰á›››?…öv¦êét{Q€ò('²äŒžÃ'µrº—„|Mƒ=£Ùé·ŸmÓ'°»h™ç÷IÃP'w'wÎfè£˜š•Œ—2Û%Ü 4Q,Ò q•³µ±Úyö¨5 nÇM·¹¸¸ù6Öó\L襂,@ðÃŽ§šãõÍ Í‡Å:~ŽPê^[kQ¸WÞ«01‘Œ«bH@S×f?‡‡x³Ãl–÷Phúiòßú¬…2^i¸@Ç«;l<œ“¶€;¡sy›fŒù_ë0Ãäúút5Þ¡g`îî¡K¢fG ó;mQøžp7Ú$º¥¾¥k£h³iåô+ë)ÒdòÄ×2ÄYsþ·eùò~ösórjöøPÕo¤Ð® ¢¦“$KuoóJ°]Í$ÛPó»c0ÛÔƒèà †g‰fXL¨%a…†â>Ÿ¤70  чEÜêXeG©ôÀ_è÷¯ã›‰® ÂE.£k=´ÐéÆfX‘a<ðt¥Ò@ËŽŒÇø«GNµKm{Zc¥ÉsdÑÜ4ó\Xâf%òbV<ΗÀ(D^r(ªÔoíô­2ïQ»b¶Ö°¼ò°!K×€jH®[D¹!¢FA!®ÆPF~`zÜ•‰ã¿ù'ž%ÿ°U×þŠj“ÅðK¨ø^·³C<·eÂB‘òdf‰‚ë’El â*ì%B±’†R:çÒ\Àb‰£òÎpû†3žùW¢Ëcs®C§i›¬^ÆÇl{K#¸šo9Êç2¸MŒTœ¾I9¬]G¹Ô.íìïtÛ–±‹ÄjìZÖ/ éîœGœóAzÜž€=9g‰Ø*ʌ̻À +ëô÷¢9â•c•á°%O¿¥púN5ÎsŒ‘L5Ûÿ=ðr¶˜»/´_ê6¨ùs´ã5_À=îŸ}ký nòßN0\§RYsçi²DÌ 7pÌ{šôJ«o©XÝÀg·»‚XUÞ2êà€ÈHaŸQ´çé\Ή¢ø¦ÓW‚}KUóíw™Ú÷ç*@ãÉ\óŽâ³­ü,«“§I¤…·ƒÄ—·7‘íŒÄñÝ4lqÃ.$…oá=1@ž««Úhú5Æ­rdkKxüÙ2Û¨“ì «ÕÍøá!µøkâ(‘c†Òn#EPTyL·`t”QEQEQEQEQEQEQEçþ4ÿ‘[Æ¿õÆãÿHâ¢È­ã_úãqÿ¤qQ@ÃïùøWþÀù[× WŸü>ÿo…ì?•½zQECkÿp×5þU5Ckÿp×5þU59nÀ(¢Š@ØÜIH(8ú×=ã=~ãAÓ-œhך…ÒÙ@ò}ÈÝ•›{àVÞ¿ÅžÕ~‘rú²%âì©|«²À€´Ç®8î1JqåvRH£™B˺†  Œ‚?P@#ÜSë#Kñ†³©\ÚXÈ&[{xg2©àù*…ÇPG”O>¢šºåÄ÷Ìzd“ɦÍy¨¾s´i!ÆzmY¯^Õ lÓ%Š9âx¥dÔ«£Œ†‚î+?JÖWQµº’h ¤¶’´7 #†ÁC}ñÁXdއ#¨4Ý_¶×¾ÞֱʱÚ\‹}Ò.ß31G p?ºDƒ½hJ(£‚$Š(Ö8ÑB¢ ÀP8ŸYž ÖWAÒý 3âXaXÄËI*Æ>cÀpI=ª8µÆ‹F}GR³k5«k*ÌeÜT&Ò¼ÌÁ@îqë@ôW9yâ³c¡ê··lÉy¥Ûý¦æÇÌ]ÞW$:·Ý „ÅXuÑÐEsKãhŸÚÓÜyV¦ñmÆì—aå·$ì¸5rh³K©F/‘?³nRÖå¤ÊªHûBŒŸRÀ}hfŠç§ñ†› q¬ÖêÖƒÌW‰î y2““×*õ¯ˆ´‹ËV¹·¿‰áYRÙ#ì«0ÜЄ±G™©­õ­2ê{ˆ`¾‚G·Pòíq…\g9é~ÔjæÖÞòÝ­î Šx_£•)ÁÈÈ.ÐåÐ쵟·*Y^¨hԂ߆3ÇzšÇ_²¼Ñ.5vq ¼—+$®À¨XdtgÈþÝ–úP­RãT±´û_Ú.£ìvâæãqÿW݇>ß»ûäÕ7ñ.™md.¯.¢·ŒÜMn¹mÙhÝô÷SŸN‡š×¢¹í7ÆzN¥¨Ûéé)[»–ºòcÆC¬4LÛ‡%IÕcÃÞ&Ó|Iaoqg2ù²ÛEpðËFC{3ƒŽ†€6h¢±¿·.!Ö`±½Ó$¶Šêg‚ÖãÍGº£¸ùG+”Ï>˜ï@4W=£ø–}gQž4¶[X.g·’àÜÆJ˜ÝÓ%Üd8úæ«ØøÞÓP‰Ä6“—T{[pAiÕ÷l?ì‘‹ÁA‚zŠêh¬ígUþÇ·¶œÛ<ñÍy«•`<¿6EŒ7=@f^<ÖQEyÿ?äVñ¯ýq¸ÿÒ8¨£ÆŸò+x×þ¸ÜéTPðûþA¾ÿ°þVõèçÿ¿äá_ûåo^@Q@ÚÿÇœõÍ•MPÚÿÇœõÍ•MN[° (¢um"Ï[ÓÞÊö=Ñ’XpѰèÊ{ë\ŸÂýòßÂwúüS¾»p®'{¿™Ñ±G÷Wn\ý1ÝT6¿ñçýs_åM^Àaø¦ÆþìyôÛ5¹{+×™áó<«[ÍA7ù>F轉û7§ï¡®²Š™IÉÝŒæô;Kþ[X¾°†É/--bHÒ@îZ6Ÿqr8'¸>˜ª­œª·Þ5·6³\È÷ñ³Ç•2,––è0Ü`eXÏIí]D·vÐ0Y®"ˆÎÀ8üjUeu ¬HÈ äR1aáÙo<3u¤êI%¬·à‚)A’Ã+íg — {åX“š‡IÓî<+y«Í4··çTÕ [e’@ìWɉÏvÈOû1Ç®¢€)ê¨_M™Fžš;Ñ\¨Èþ÷uçÒ°Ó÷sxv{fxí¥{دíí³º;c‘ȱd hòqлmà ê( Äö×CÂ^/Öoáû5ÅöŽl£´Fó …Y‚d¯VgœŒ1·¾k¤ñ=þ©¡Í¦ióýšKÏÜIr:Áû죻mÈ…è+bŠào¼#®Ãuç[ÞÁ{ hÒÛ¬ nÚ̳FW!Z3õ²ÕýK×w_Û0y1Mö¯a~7‘ƒmn²!¸[r}ð=k¯¢€8ßxsQÕ/µ9­a¶•n#Ò–5¾F6÷’K a鵇בM»Ñµ}ZmCS–Émgìï*Õ¦W/ö[–œüû~ÑèFOÚQ@]¦‡¬>¨u[»xc“ûuuKŸÜ›mŒôܤ’}vœu“á›Û/Æ÷Q^Mm¡w} ¢í<„3ˆÄXÝ&e<õ$ô®âŠæ|7gªi–óDÖo Š˜c´²’ádh 8òÂÚ½~Fõ'aðìs>|¸µ \€NÕ[¸YŽ`?…tTP?‰ô¹5,ãŽ9Ólå‘$ÆÖŽ;˜ä|ç¯Ê­ÇzÇ¿Ñuu—XžÑããVK¤H©+D-"ˆìv#oSœõPÃø«°¢€8_ xwT´Ô4ÛJÌ'ØÆ©‡–e•óqs¨ÙöùŠO¨=@ž ¿ éZ@X-Ú/ ÝiS°?*O*ÛŒàuÉ?ã^ƒEr:.…tɬ5ìWÐ\ÞÛ%·Ÿwv—jù˜ÆÌcF>ùö©¼9áÐ5.›Pû@xŒ{<¶\dƒž\úzWQEqv6¯¢YønX¬–òm+L—K’™S~L8”ƃ8ëóCZ¾ÑæÓ|4tíFÞÙYînÝá„f-’O#€þ®8ü+~Šó»?êý‡í’Kþ »Þû·ÛEäùdÿx¿Ù*z}ª_ÇB×FÕô‹»]F%»’)uD6ë2¡Ùsv&GÜxáP:å½v”PáÏj:UΕqq ²§±[¶5¸¹#/û FFñèiþðåÞ’¾Y!Š5Ót9lîB3cÆ:äÅ!'ük¯¢€8ýÀï£êð_OÎnýß”Ã9R:—>¾•jÆ=^_Ëq©é9‰$’;Yņ.v°N»Ü“ÔgÏMEsi·ø‘oM‹LŠÞI ’ÆÊZø0  Ðg–ç Õ«"÷ÃÚ÷Ûµ´ñ ¥íÐ`ðÂUVÎcg8ýàÉs ¹Ø wÆn£H±‹’òêúz¢€I$]Dç§¢«º*( Š( ?ñ§üŠÞ5ÿ®7úGxÓþEoÿ×ý#ŠŠ>È7¿öÊÞ½¼ÿá÷üƒ|+ÿ`ü­ëÐ(¢Š(_øóƒþ¹¯ò©ª_øóƒþ¹¯ò©©ËvER¨mãÎúæ¿Ê¦¨mãÎúæ¿ÊŸ@&¯¿×|A©x¢îhïn좳ººƒ2¬0G*Ò†å¼ÌøŽØ¯a¬›ï hš–¢š…æ›×I·0çåTÊù£Ï$d”÷*qE;ÃúÔúÌšªÜY5¡³¼$nrå 1J zÞôíÒ¢°ðÚ6s§ë Ä77y¶ˆ 8*B¦yrïöf8ãWNðÿü"ׄšE¢HuMFÛ–Û "8ÕË{•‡©e£âVãEÐe¾µ‰$•e†ã¤»#÷Åh^xjYåÔ|»„ÞjVZ̧*д;—èVÝ1îǶ(¶£ã5±†yÚÚHV°´‘\ÆË"$÷Mbz,ô÷­üUbÑÈg†îÖXå·‰ ¸‹lƒÏEc?t¾FÙ>•S]ð½Î«{}so|–ïpšpŒ{ŒfÚåç'C€¨æ£›Ã:Ž¡öëËû›UÔ&û” Vò‡ÙfiãÝžyf ã¶1Í[¼ñm­®µk¦Giw<³jÙòDã­?ÆÜM ©¶¶–æH¯¬§1D2ì±ÝDíÜ…R ÐÕõ{MNkûçd·Y#™T±ÝQxõaT×ÄÖ`÷ mzÓ$ÿg{AgY6‡ÚW<|„?^„}*ƹ¦>­a²H±”¼µ¹Ë äEÛÂÏbdÜT—'¦óŒzšµ¢øRÃB¼{«YnÞ3È`}¢¹ï x{ZFð¤Z´°,zM¼R¤a˜IövˆÆüí‰drvzÙ×!¸Ÿ_ðÀ†ÚY#‚úYæ•GÉ‹Yn>¥¤P?Þ¢Š(¢Š(¢Š(¢Š(¢Š(Ïüiÿ"·ëÇþ‘ÅE4ÿ‘[Æ¿õÆãÿH⢀‡ßò ð¯ý€ò·¯@¯?ø}ÿ ß ÿØ+zô (¢Š†×þ<àÿ®küªj†×þ<àÿ®küªjrÝ€QE€*_øóƒþ¹¯ò®Wâ<·±x~ÙmÚT²–ñ#Ôd‚–Å[qÏa»`'Мñšç>½ÝµòCnÑÍc%œ’Þ¥œÆha¸óDÛ¹Œ1 z ö«Q¼n#Ôê+›˜lí¤¸¸•b†5ÜîÇ ç<]‹“¡ZJ¬m®¯Ý'…¸*ÚÜHc¶ôFú¨¬I¼3¢ÍF,ÿÈãèAâª'=³±ÓüA¥j× ìsÊ«¼ªç!rz{ŠÒ¯9ðæi£üA³rDºUÙq4í'Im±Œôêku£¡Á¯é—i«^è·ÓýªkX!¹[­ ¤­*€Ê8È17#±ó ª+'ĺÏö…. 3 !…wB´’¬aˆ ‚@äXÄW:G‡ï¯îï®.nc–Þ³ÞZ¬oleF¬Ë,§xn3¤s€֊᮵]R×Á^"¿MbäêV6p±]ÚD’@QYÆUx!ÀÆyïŽAÇeywo§ØÜ^ÝJ"¶·¥–CÑQFIü4=Ã?‹µ«=üí-V[ÛEki¥ ¶ ©BÇÈeK4YîÑ3tàÜÞZoIw¤–‹NÔíì [yC<Þw“ƒƒ€ï”ã=ñÛ$­¢¸­[ň¹hÐÝAý˜Ím,ÙYï& p2̉߀HÇsZÅRÛ Ô4Óoy¶jbIÄ‹²æ%v!ƒ’1Ðyॢ¹kÏ]§ˆmtËM9%ŒêßÙ×¼ÛJ² Ê1èOýóþÐÁgãkKϦ–©Yn¦³‰…Ê´¦X·ïÝU_ÝI†Ï?/÷¸êh¬-_¸×,仂Â5ƒ|b0nA}¤Þbã÷nªA(rsÇiþ*¼»²ÐÖKü‹‰o--Ä»ìÜG Úæ€6¨¬Ý{X”ošÚ[ŸßÁŠ,oc,©Æ{åóŽø¬æñD©¦ÝË&ž‘ÞYÝ [ˆ¤ºUŠ6(²e#Jºs¼À{ÐGErøÑ¯†›™¦5ä÷ÑÝ0 :ˆÑ­åH¤Ëàåw1ÃÎð[øîÖK¨ÜY\[ØI¦Ëª[HØ/,.Jºx˜9·Jêè®wEñm¾©ý “,ËcO/ÙnÊym¿r½û·ùqžžµj×Ä–:ŸŸ˜d¸ºŽ&‘b’! Žƒs($@W%¦x³PÔ4=dÒc“TÕlM÷ÙÅÆÈÖ5òòwyýìx\w<ñͽ[¾¾ðÞªÖrÍw÷Â;Lvò§•?@ØE\úÐEEr²øóLÿJû'úVËH§µØØûT’lÛäp}mÉã÷ëèp°xžæeŠÞÎɯo¦¸¿ ’¬@Eorac¸ÝŒrNG MÅx{Åz®¥«ØYÍ`¢ ¯í)Y$‘ {¯)P ÈÈV@yäœö9±àïÝë:vŒš•›Ãs}¦-är’?}´F%m£î Ò©^¹çV°´Õmì®5 hn&C² &Uw%€\)999ñªŒ¤¶`eé>—O×T¹Ö./eŽÚKxÒH£@¡Ú6cò“û¥ýjÍ߆­î´±<±ýºx.Î0|»ˆ¶lg¯ú¨¾SÇÉÓ“[uuâ Æåí®õ}>ÞtÆè¥¹DeÈÈÈ'#‚ &Ûw`WÒ4Óu;ÍJãQ¸½»»†dyB€m!]ª ÿXr¦z“Uít­N;ÿlœY¥õÜW6·Qí‘€CŒ09ˆúðݱQ^xïDµ˜G’Þ)\ù¶Š$O¦àpOÒ·ìo!Ô4ûkÛsºˆ–XϪ°~†›‹Jì ôðõŸö\öwIÞæA=ÅÃI$ÃnÙ2>ë)DÛ»±@è*Ò/¬‚ËowöBæxEÝìê ùŶÁecBÝsÚ*@­¨X[êvOir¤ÆÅX8ee!•”ö`À{+6? [½­ÊjË{uuåù·O„ݶè¶íÀ]Œw tbORknŠåu¯ ÝËá-zÚÞ_·ë›ZµÅÃò ²¨ùFT;É'»[:Öm¯X ܵ›H¯<=¦U9ßìî N:ã‚kFŠån~蓪]hÿ <3wgsÜ5¦™E+VóZ#§yÁú•¡ÿ&—ÿ?šÇþ 'ÿâªÎ£ák;Ï Zøz%T±·{@±È ƒÆûO9Xöäúçšug[•Xý#QÔW¾Òu3k%Å´\‰­‘‘ JÒ¨]¬Ìr $ç<îqÎÕ¾­ªŧÞ\}¦=VÒ(ݼJ¤VÒȈÃ; ”;…tÚ^‹i¤´Ï \K4ÁCÍs;Í!UÎÕÜÄ ³:ÇÔÕ8´ >ׯ¯Zêw1\ ¶g†X™bŽ6b·Cå)ãH9Íd3)uÓ|1p–’Ý¥úê6Ö7îá®^ÚI¤†2U›;€IU×±ÈÈ"´ôg–Ï_Ôt3q5ŵµ­½ÔR\Hdy­2²–<°ŒóóÐ4aÒ,aÒÛMû:Éjᄉ/ÏæîûÅÉå‰$’O$š¨4¶µŠÞÂêh3u÷3¼$Ó í.Ç';UNsòeF8 ¾,ÔîtIufŽÓ´ööêC8ófH‰PÜÉã gŠç%Ô®4 QH×PÂËg–òÞ5áTžqI I€IËà>dÁOssm å´–×1$°J¥7 PER¶Ð´ûk ìŒM ñ\_iÔí5‹M9®Š¶¥$æ<,Ç!Ç£02P‚Á=¦³«Zh:-æ«|û-m"id9 ƒ$dž€w$ ÌÔü.³øKUÑtéü©µ^.®‹Nçzì,Ķæ!x<`v­{Ý>ÛQHRî?68¥Y„dü¬ËÊîÜÔ ó¸¼]©6M>ÓYÒn5t½²•¤ŽO´ÆaºQÐaù$gAé—üM‘±/ˆuÛøHäagwö]fÒÆÊ¦,$ßgÈfÉí?\uò0C¨øoJÔÌMqjªñgd‘.H=W†TaèÈ­ÕA§Ï¡i÷2Ï$‘6n&‚âP®@iaed|ùD÷ ðrzæ­­ÙËyld‰ïáþÈpmÝâŽC-üˆS¶ÐUB“É99È üGªé“ÜXß%œ÷p˧6dCÕÉ€¥‰Ü»ç8äqÁÎÆ¥á½+W{–¾¶ó…Ê[¤ÊÌpë,`@ÌÄú犎/ i‰is‹™ÍÆÍó\\<’‡txv%†Ö%—†$Žhó_Ö$ñM­…£ÚEjšçØ&f–?° ž¹àä°ü¶AM3Æ7Z‡‰E’Ûî¶{Û›2‰k0h<“ ócû¶V1chÁEàçfÓºMŠ‚V7‹|d2±s8@…÷œ²®ûÛ›9Üs-¿‡ìíu3}o%ÜD»È`K§|–>Vvä’Xñ÷‰=h‡õmOWŠá§{H.QãÝf`‘d¶üèä¶à )e$d Tž1ySÃè°ÜMMcI…$—Q#Ñ•b8õ«V^³°YLR]¼²´eçšéä•‚6åMìIØ o—8ù›ÔÑâ-.}_Hû-¬ñÁ:\ÛÜFò¡uÝÉ( f:Ž´ßê—Z>Œ.ìàŽ{†º¶"•Š«y³ÇätáÏ8?CÒ²%ñ.¡o§ßÇ;ØÇ}e~¶o7•#Æù…&!R]ŽÙí³6p1]-õ…¾£n°\¡xÖh§>xäYñèʦ©\øsNºûK2J’Ü\‹¶–)™$IDKä`r§ËP§È'ÔÐ?eâ½_Ym& 6 4’ò+ã,× T6ÓÇáCa‹”W#$í ²ÞG¤.¯{a¶ºÑ'Öm­â—D‰c%ϰ•H ¸#æë[úO…´Íhæµ-$~~Öžáå#Îty~ñ?y£VúçÔÓãðΓ­¯ÙÁi`útQÈÅ—ìî20?{"$ëéîh/DñMÅÌ:«ßGæÇaÜ}¢+I-QÁ”b>eòòNqóŽ˜&¬h4ÓüG~öv‘H’$FRZh`:G#ã¶=ëKMÑ ÓeK‹Û…˜ÂîêIñŒôÞN:ó޼UØíà…·G hØÆU@ GAñ.µyká»ýJ ·×Q8mÃ͸±8 ˆÏË€FáÉÁ'O^ybñ…Ú+‰£Yoå‚XÖBD6³¾z4jFzcÞ¡ðß‚ìµM'-ßøûF±±šä‹Ñ±r<û Bzd‘¯>§é“U|-ñ ËÅËé°E J¶íq˜î–^‘NvŽ>øëSn>¦«X±?ôÜŸù…^ÿèÛZÚty Þâ¹ÚÑ\©­jñÉã;È5¥|=ûèlRÉu‘ˉI¶3onÓ÷þc·oÇÅsáÈíæd†]KOŽDaêo!ÜH®aç:Ò›{^»Ötý?P°Ò¥„Ckt…æ’Š3ˆü¡Œ†@ÊÀG•Ø5¥7:ö½w¬éú~¡a¥K†Öé Í$/g)ùC €1•€+°èÔQ^s­)¹×µë½gOÓõ *XD6·H^i!x£8OÊÈdጬy]€€F¢¸ÏøsE×uø4çҬ人S>¡taS*[¨Ú|e]Ûj©þêJT†PkCO†(¾"ëÍj.›a$…F7·™t¹>§j¨ú@çž-ñ~XìÒúIt[”ŽÞÂi–æé†L;ÕCä\œô¥lf:©%ö¡¨éúÍöŸâmCìÓkúbÚH‹ÙÂÐíPS!HŸ¡ë´g;Ÿp§Q\½{«K§xîäê*,ô¨¦Ž O³FË&lcù…Ü¡Ÿ w° //oìnü}©Yjâ#¤Ì/>±£y¤XÂÀJXÛ06íIL͸G˜ÀìÉÎÚè¼Gâ›ÝQŽÚÛIûZ4BC&.x$‘Ý[Ƚ½Aç§B@:Š+¿Õ5ÝoöjK¤®§§Ü\H¢5i ‰ä±ùªàó¹>èl¨ ØðÍõÆ¡£.ŸÌš««S&Ð ‚äˆ1Œ€œ2NP½Îx­¼9%¼1¬pÅ©jÆŠ0EäÀ;ÑÐEPEPEPEPEPEPEPEPŸøÓþEoÿ×ý#ŠŠø!¤ØuÃøWÆZ®±â/ì½KNkPÖ’\#5”ðgcƤ~ô ÿ¬÷Çí#žžTŠTw…öJªÀ”m¡°Þ‡k)ÁìAïCVvJ§©iv³n¶ú…µì(þbÇqu ‚2ï‚GâjÄsÃ3Ê‘JŽð¾ÉUX´6Ðíe8=ˆ=ê?·ZyþGÚ ó|ß#Ëóï3g™³ÞÙóc®ÞzRþO г¤àáVôß hZ=Ë\éš=…œì†3%½º£$2Lǰ­Z(x[H}NãP¹²†êæ[¡vq¹…ÄqGòdq ¯áVu½%5½0Ù5ÌÖÇΆdš»Ñâ‘dR7>ò Ö…G<ðÚÛËqq*CH^I$`ªŠI$ðè)´û;‹Ë{É­a’æÛw‘3 /á†ÚzŒŽ¸¢m>ÎâòÞòkXd¹¶ÝäLè Ÿa¶ž£#®)ªéÑÛÚ\>¡j°^:%¬2…œeá‹€uíW(†ƒÞH$’Þ\‰ÈÉmª 0ˆ£ýÜœ’I|Ú}Åå½äÖ°Ésm»È™Ð‹pÃm=FG\S5WNÑíÖãSÔ-l g²\̱©l€XœDZª—^+ðí“ö½J·óâYáóo#O26û®¹<©ÁÁÓ[xVåîV$È‹ÈneRÅA=À,ØíZ§m¤¥¾»«™¤–òaòßnÈ’-äÀ“#“’zö$®uq½¾¡k4òÛ‹¨ãŽefxIÀrPŸâéV&žt<©T ìf £žåˆw$ ·†Ù ‰"Vv„Pf%˜ýI$“êj°ÑôÁÌcOµ t¥'_)q*–f!¸äîyîÇÖ®Ñ@¤ÓìåŠî)-ahï3ö”dM•wâùT/=€^ã@ÑîïòãK³–å%ó–gK‰0£p$g8DÿdzU›këKÍ¿eº‚}Ñ$ãÊ6c|ì~?…¶¶CƒŽ•b€)O£é—7¢ö}>Ö[ ª¢g‰K€®Fqž„EΑ¦ÞÛÜÛÝX[M ˉ'ŽHƒ,¬€ÌS…^O÷G¥]¢€2dž´1”cG± bÅíWì눶âS”’ã½hG0¼¯H3ï‘”`»m “êpª>€T•_íÖŸÚÙÿjƒí¾WŸöo0yž^vïÛ×nxÏLÐ{%,5=Rõ.fq¨Ì“Ûåyÿfó™åçný½vçŒôÍX  ©¼3¡\j‹©Í£ØI~®² —·S eÆÖÝŒä``ûUÈ4û;o³y°ÅöhLl@<¨ÎÜ¢ú/Èœ>QéVh  «¿ èWöö¶÷z=„ðÚ'—o¶êËà ªà` ­Z(  ­KÃÚv±}Ρ\¤VòÛù¢¼n²aÝßn¢ÃÔPÊ÷ß„¯,íïo¯ïî¼5s.±mܲ¼:€XŠFP1ò±œyJ6J¸¿‹ »|,× ¤þ}±Ñ. 3yÆ_1<†ÚÛÉ%²0ws×&º}ÃÔU{û;]ON¹°¼A%­ÔM ɸÈÀ†Œ‚zP9ªÍccâ›ÉõЇNºÓbµ´Gæ,®^S4(¸9wSÈdÚ0aƧ†MÄ>Òì5)÷jöú}¹½G˜I r˜,Äœ²?ÍÜ©äÖœGmoÎR4¦I ±c–bKrI=ê8lí`¼¹»Ú.vù²,HQ…QžŠ2HQ–cŒ±$ñ \YxɯçÕÿ³-NŸVNÖÂ:àI!xTŸœsm’]«±‡–Õ©¨Þ^ßÛèúQ‰ôýCRA5Üq˹­`@¦pq“¹’ÊC7zýÃ]áê(Ü=Es’[Ãkã­ÞÞ(á‚-ú8ãBª(–Ìà;V_‹&ºÖuuÑ­tÝVîÊÖ&–îm2h#xîq î–E›ʰ·qïÕ6͵”Õ™\Þ%»[#›jÆÌ€Lí•\œdí8spõä7ÜxŠÏÅÒÞZß[ë·H%°ŽæPMÔF1#\‚¹009IÔž&;ºû+xõ:êßK¦ZiV±Ã|æ9Y¤¸ÄŒÀî“*‹œ±W, G_¸zŠ7Q@K£^é“\i³ø“^»²I¼/¥HÓ5óÚ¬Ò“pró©R_–!KüÙcµ¶ålj^#Ô´ï jSë·vº•ׄ­¤·B9ð%Á˜ÆŠ¤Æ\¨ÒØ#Ô·QFáê(ÎoÒãG‹ÆÒØË:\ͪÙÊâkÉT W[u’MÇqŠ<}¡Lª>QÁýÐ wì|#4ÒøŸG[!©m‰WÄR24~H>C_m«ù›¥à´û§JÜ=E‡¨  Í[I­íÞµÅ;Y@Íiy3´ðÆCló˜ùÞ X©¼S³ÿ’‡¬ÿØ*ÃÿFÝÖþáê*˜Ól×Y}YUÅãÛ­³°™¶´jÅ”ÎÒAfÁÆFâ3ƒ@—ŸòPôoû_ÿèÛJè*‘ÓlÛYMY•Íâ[µ²1™¶¬lÁ˜ÎÐIUÉÆNÐ3W7Q@ E&áê(Ü=E-›‡¨£põ´Rn¢ÃÔPÑI¸zŠ7Q@ E&áê(Ü=E-›‡¨£põ´Rn¢ÃÔPÑI¸zŠ7Q@ E&áê(Ü=E-›‡¨£põ´Rn¢ÃÔPÑI¸zŠ7Q@?äVñ¯ýq¸ÿÒ8¨£Æœø[Æ¿õÆãÿH⢀<Çã½c¥YéÃLÑg‚ÒÝ-£ûE«HJ(P3—Æ~U=:Š“þ6±ÿBÿ†?ð]ÿÙQEð¹µúü1ÿ‚ïþÊø\ÚÇý þÿÁwÿeEÂæÖ?è_ðÇþ ¿û*?áskô/øcÿßý•Pÿ ›Xÿ¡Ãø.ÿì¨ÿ…ͬпáüöTQ@ü.mcþ…ÿ à»ÿ²£þ6±ÿBÿ†?ð]ÿÙQEð¹µúü1ÿ‚ïþÊø\ÚÇý þÿÁwÿeEÂæÖ?è_ðÇþ ¿û*?áskô/øcÿßý•Pÿ ›Xÿ¡Ãø.ÿì¨ÿ…ͬпáüöTQ@ü.mcþ…ÿ à»ÿ²£þ6±ÿBÿ†?ð]ÿÙQEð¹µúü1ÿ‚ïþÊø\ÚÇý þÿÁwÿeEÂæÖ?è_ðÇþ ¿û*?áskô/øcÿßý•Pÿ ›Xÿ¡Ãø.ÿì¨ÿ…ͬпáüöTQ@ü.mcþ…ÿ à»ÿ²£þ6±ÿBÿ†?ð]ÿÙQEð¹µúü1ÿ‚ïþÊø\ÚÇý þÿÁwÿeEÂæÖ?è_ðÇþ ¿û*?áskô/øcÿßý•Pÿ ›Xÿ¡Ãø.ÿì¨ÿ…ͬпáüöTQ@ü.mcþ…ÿ à»ÿ²£þ6±ÿBÿ†?ð]ÿÙQEð¹µúü1ÿ‚ïþÊø\ÚÇý þÿÁwÿeEÂæÖ?è_ðÇþ ¿û*?áskô/øcÿßý•Pÿ ›Xÿ¡Ãø.ÿì¨ÿ…ͬпáüöTQ@ü.mcþ…ÿ à»ÿ²£þ6±ÿBÿ†?ð]ÿÙQEð¹µúü1ÿ‚ïþÊø\ÚÇý þÿÁwÿeEÂæÖ?è_ðÇþ ¿û*?áskô/øcÿßý•Pÿ ›Xÿ¡Ãø.ÿì¨ÿ…ͬпáüöTQ@ü.mcþ…ÿ à»ÿ²£þ6±ÿBÿ†?ð]ÿÙQE2ûã.¿}¢Þi?Ùº-½­ÜRE ¶µhÈ»XŒ>3Žäv¢Š(ÿÙsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/images/linkDlg.jpg0100644 0000000 0000000 00000034641 12114157751 023740 0ustar000000000 0000000 ÿØÿàJFIF``ÿÛC 2!=,.$2I@LKG@FEPZsbPUmVEFdˆemw{‚N`—Œ}–s~|ÿÛC;!!;|SFS||||||||||||||||||||||||||||||||||||||||||||||||||ÿÀK¯"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?»­kI0Ê$_aŒì}k/þÿŸ$ÿ¾‡ÿVüA40jzd—Q¬Œ‡ 20UFqƒœg?…I¬é6×6 ö(áGiÆÑƹ01Ž0séÇã]1TÔS’ÜZÜ£ÿ sã?a\tÎáÿÄÒÂ`ßóäŸ÷ÐÿâkúKåÅsnк Qæ)ÎÞØÏAÇóúV¶¡=í¹Òíôã&$°B`EÜ®HmħCžsÅj©Ã¬~DŸð˜7üù'ýô?øš?á0oùòOûèñ5z›Ii%UþÏûd¤°ÁöSß8?J‰´{U–I•ä6IHÎìÑí,p>Y,QúrrRì-K_ð˜7üù'ýô?øš?á0oùòOûèñ5%Þ‹mohÖC™RHRr£r«"œP2xãž}«7XÓlí!w´–Vhî —ýíªã§=zÑR}R÷ü& ÿ>Iÿ}þ&øLþ|“þúüMQ°ÿ‰~wzÿë.ÔÚ§ºŸ¾ØôG~µ6…'“¢êïçË<ŸÞD2Ãæ=9Λ§M]Ø.Ëð˜7üù'ýô?øš?á0oùòOûèñ5ooäýÜ× m° °W“sm“»§?ZꥲŽ}pÉ3<‘µç–`r Gý;¶žþõ2(ô LoøLþ|“þúüMð˜7üù'ýô?øš¥áb§Ä¶ÅTËí䱺ž3PhÖÿhû屢˳’Oݶ7c)õÒ­Ò¦›Ð.ÍOøLþ|“þúüMð˜7üù'ýô?øštK+mQ¾Ê÷eűc!R{Æ8çëÇzËñ=šZê,ñ‰Îws)#cÇ*¸þïC“œöæ0¥'dƒRïü& ÿ>Iÿ}þ&øLþ|“þúüMs4VžÂŸa]7ü& ÿ>Iÿ}þ&øLþ|“þúüMs4Qì)ö ³¦ÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰®fŠ=…>Ávtßð˜7üù'ýô?øš?á0oùòOûèñ5ÌÑG°§Ø.ΛþÿŸ$ÿ¾‡ÿGü& ÿ>Iÿ}þ&¹š(öûÙÓÂ`ßóäŸ÷Ðÿâhÿ„Á¿çÉ?ï¡ÿÄ×3EŸ`»:oøLþ|“þúüMð˜7üù'ýô?øšæh£ØSìgMÿ ƒÏ’ßCÿ‰£þÿŸ$ÿ¾‡ÿ\Í{ }‚ìé¿á0oùòOûèñ4Â`ßóäŸ÷Ðÿâk™¢aO°]7ü& ÿ>Iÿ}þ&øLþ|“þúüMs4Qì)ö ³¦ÿ„Á¿çÉ?ï¡ÿÄÑÿ ƒÏ’ßCÿ‰®fŠ=…>Ávtßð˜7üù'ýô?øš?á0oùòOûèñ5ÌÑG°§Ø.ΛþÿŸ$ÿ¾‡ÿGü& ÿ>Iÿ}þ&¹š(öûÙÓÂ`ßóäŸ÷Ðÿâhÿ„Á¿çÉ?ï¡ÿÄ×3EŸ`»:oøLþ|“þúüMð˜7üù'ýô?øšæh£ØSìgMÿ ƒÏ’ßCÿ‰£þÿŸ$ÿ¾‡ÿ\Í{ }‚ìé¿á0oùòOûèñ4Â`ßóäŸ÷Ðÿâk™¢aO°]7ü& ÿ>Iÿ}þ&´âÔ5IbIJˆ£¨e&â1FGW ]­Ãªh–¬ÓI ˆbòÌ|±}ƒâϧÿ®±­SI¥ùjLouP2t¨@ôódêz£jžž_(Dª¸ç¡ôµnÊY¥kŸ·3%àýFpŠžª2wdõ=ºqß/ùî?ëà%®}^ƒ6¼Qas|ößf~Å;¾`1¾§Ú³ìíüCb›-·*vRèÀ}2xü+«—ï÷Wù eTk5[+Ž6ãHÕîfy§„¼ŽrÌd^Z‘4ýu òΰà,NàõÎ+®¢«ëì‚Ç4ÍmdŽ@%F»„ã(¾€çɧ.Ÿ®¬í:™ÄÌ0Ò Æâ= Î{ʺê(úÄ» ±Ç¾™­Éæù‚WóqænœøéžyÅf·u·í"Y¶ço™8l}2k°¢¬K² chڻƑ¼NÑÇŠeR=p3Å>ßLÖíw}˜Kìnòç Ÿ® vSúÌ» ±È®Ÿ®¬í:™ÄÌ0Ò Æâ= Î{ʇÓõ×9s;âL™Áù€Àn½qÞºê)}b]Xã“IÖR=E›$ù‚`'©ÎsRIc¯ËŸ1î*Pî¸å=GÞèp?*ëh£ëì‚Ç#ýŸ®ïwÌûÜ©fóÆX¯Ý'žqÛÒ˜úN³"2:HÈÏæ2™¿¼yëï]}b]Xâ°u/ùöÿÈ‹þ4`ê_óíÿ‘ük¶¢ŸÖgÙŽ'ûRÿŸoüˆ¿ãGö¥ÿ>ßùÆ»j(úÌû ±Äÿ`ê_óíÿ‘ühþÁÔ¿çÛÿ"/ø×mEYŸd8ŸìKþ}¿ò"ÿØ:—üûäEÿí¨£ë3ì‚Çýƒ©Ï·þD_ñ£ûRÿŸoüˆ¿ã]µ}f}Xâ°u/ùöÿÈ‹þ4`ê_óíÿ‘ük¶¢¬Ï² Oö¥ÿ>ßùÆìKþ}¿ò"ÿvÔQõ™öAc‰þÁÔ¿çÛÿ"/øÑýƒ©Ï·þD_ñ®ÚŠ>³>È,q?Ø:—üûäEÿ?°u/ùöÿÈ‹þ5ÛQGÖgÙŽ'ûRÿŸoüˆ¿ãGö¥ÿ>ßùÆ»j(úÌû ±Äÿ`ê_óíÿ‘ühþÁÔ¿çÛÿ"/ø×mEYŸd8ŸìKþ}¿ò"ÿØ:—üûäEÿí¨£ë3ì‚Çýƒ©Ï·þD_ñ£ûRÿŸoüˆ¿ã]µ}f}Xâ°u/ùöÿÈ‹þ4`ê_óíÿ‘ük¶¢¬Ï² Oö¥ÿ>ßùÆìKþ}¿ò"ÿvÔQõ™öAc‰þÁÔ¿çÛÿ"/øÑýƒ©Ï·þD_ñ®ÚŠ>³>È,q?Ø:—üûäEÿ?°u/ùöÿÈ‹þ5ÛQGÖgÙŽ'ûRÿŸoüˆ¿ãZÈþ Ž8ãKhBÆŠƒ•èø½« ¢¢Uœþ$‚Ç>Òx…”ƒoÁåøª©5œö^ž+”Øæ`Àd8ôúWWY'ÿ4Ÿï/ó¨rÒÉ Ú—ï÷Wù e>_¾?Ý_ä)•QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE‘âùIþòÿ:׬ÿÈO÷—ùÐÔ¿|º¿ÈS)òýñþêÿ!L Š( Š+*Kû˜àšCå#œÛ”*é×$üÀ208ô  Z*·Û¬Ž²nUØq¸îÇ#ž@ç'ý–ôªÖúÄ/öt”,Š›°@˜ ÜzŽ€ŽzðhJªÁý¡w%ÇÙc¶Ù ¾^d‘”“€z>µaIe©RFJœd{qMÒnE²^ü#ÉzU1–>Zžä€žOj>Ç«ÿÏ;ûüÿüMcÕÿçýþþ&®¶¢âö¾Í2ÄðÉ#’¿2•*1€rzž€ç+ŒŒÒ¶©QÈÓÁ_¾?Ý_ä)”QEU94õ™ K<Ò&Ö ¬GÉW ã$à‘É5rЬ-‰»ŽV!¼¸öî nfõ<``gþú=;¶ƒbÅ,ªŠ)‘‡*œg tëVè ¨* ,@ÁcŒŸ~)šm±º¶Ô">o +,bDo‘8+Æ}x#>†Z"¸Š Ë–…›-‡ “êyëÅN4¶[’"–&(¸ 9ä9ùv09ÀãÞ¢ƒD™XI + 4[îàß§$þX~Þ¿ßOûøÆ·¯÷Óþþñ  äÓØË$ÑLS8 &åSåˆÈ##Þ¿ßOûøÆ·¯÷Óþþñ  !Ò-^ÞIK#Ù¥«8PÃpë×wéOKi –k«›•‘¤XÔá6*bxäñów'¿ÐAöõþúßÃþ4É®`¸ˆÅ8†XÛª»àMDÒBã ñ°È8$G"—΋þz'ýô*³ióåcÿ| >Í¥ÿÏ•ýð(Ò¿äýv›ÿC5j£Y-ã‰b„CjI ‡š<è¿ç¢ßB€$¢@ ä„w¥ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ²_¾?Ý_ä)”QET0ÝÛNÅ`¸ŠV$#‚qøTÕDE+}»Ë%Ì­9‰Œûd`þ4zŠÇ24’ÛË#Üy7 $¥"'…ù6ð9éŒã¹=³DK|nb7H²~ïåXØ‚6Ù „îê3éÚ€6*-ÂÎé¯ÞæÖ œ]HÃ6'Ӣǔ›wmÚ1»9ǾyÏÖ«Yy%–HÖMI•Ìm´•ò3Œþ_LØþÇÓ?èiÿ~ü)©¥iR"¼vlŒVXP‚=GFÎißVÚÒ q4¢DóÙœÆ7mÌXÚƒîÜgŽ~niOvöÚ-›FβÇ`fdÛÕUAóÀ$7c &€6¿³tû>Çc»vÌyIØÝŽqÏÒŸý¦Ð:Óþü/øVaE:ÌÜMM{¶M“ÁM± Œž9Î?݃ –æòKe¹„JÒ¬2¯žbB£n;†Jþ÷+žûTt9 ¿Øúgý­?ïÂÿ…Øúgý­?ïÂÿ…;KiÂ6•Ã’[ ¬XmÜvሸÇÍ߯9Í[  _Øúgý­?ïÂÿ…Eq¤é¡}¯SÒµiT7_êÇãÿ šÅûŸÿ@ëOû÷LšÆÀBåtû@Bœ(qYÚ—‡a½¼Iã$3~ø÷½Ç¡ÿõýu<˜íìÌP HÑ (°ÿNŸÿ^ÉSÔò ÓÿëÙ*z(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+#Äÿò“ýåþu¯Y'ÿ4Ÿï/ó  ©~øÿu¦SåûãýÕþB™@Q@5•]Jº†V Œ‚)ÔPv©`ÅFà€zÿ!ùS¨¢€ “JSh·&M§ÎœÈ¸qÀÚ£ž}ª:ª—3I2ÛXI2Äû„ˆpr;@¿iOOüyÆ´§§þ<¿ãX¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@_iOOüyÆ´§§þ<¿ãX¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@_iOOüyÆ´§§þ<¿ãX¿ñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@_iOOüyƪßÏ3À¢DÒgþZÊcvÍgÿÄÇþSßèÿøª?âcÿ@©¿ïôüU3f©ÿ<¬ð ÿ…#Gª2•1ØàŒÇÁÿ “þ&?ô ›þÿGÿÅQÿúMÿ£ÿâ¨H!k{+H©x¡Tb§#"ŸPÄÇþSßèÿøª?âcÿ@©¿ïôüUOEAÿúMÿ£ÿâ¨ÿ‰ý¦ÿ¿ÑÿñT=üLè7ýþÿŠ£þ&?ô ›þÿGÿÅPôTñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@ÑPÄÇþSßèÿøª?âcÿ@©¿ïôüUOEAÿúMÿ£ÿâ¨ÿ‰ý¦ÿ¿ÑÿñT=üLè7ýþÿŠ£þ&?ô ›þÿGÿÅPôTñ1ÿ Tß÷ú?þ*ø˜ÿÐ*oûýÿ@ÑPÄÇþSßèÿøª?âcÿ@©¿ïôüUOEAÿúMÿ£ÿâ¨ÿ‰ý¦ÿ¿ÑÿñT=üLè7ýþÿЧÄ/^TYl$… »H„/à 4%ô ±™8 pqëþf/î?ýô?€E?1qÿï¡þf/î?ýô?€E?1qÿï¡þf/î?ýô?€E?1qÿï¡þf/î?ýô?€E?1qÿï¡þf/î?ýô?€Y'ÿ4Ÿï/ó­‡x‘¶9À'ïð¬nVŸÃI3€Eˆ2ph —ï÷Wù e>_¾?Ý_ä)”Ux/!ž™[½Kq€Aú`ƒøÓîQä’&ØÍ€[8 É×Ǿ*™Ó¤ŒŠ]Êv6ù‘”¨@'ð¾”e/ rß¼P„V,0ä¨a^ ÞA,+(‘TÞC0FçÓÌU4°e–]¶äÊϹ0v€Á2z|Ç)ÓŒçÆÂí `ñÁäç8Á<‘BjpÉ­œìxäޤªIÆ%°3ÔŽ=*(ío"–Ú\Ç+G¨Áä#Š•Á-¡I<ž½x§Ic+ý§ Ÿ½»ŠeäýÕòóŸþ”Ø5«i# âHÝžEHü§,û=AÆ8çÐâÕ½õµË”‚Q'gkåOFÇCŒàõª¶v3Ãv²IåìCq¬I"IÇn:ÁÖV2Ûÿgïd?f´0¾ åŽÎžß!ý(BŠ( Š( Š( Š( Š( Š( Š( Š( Š( ™/Üï/óúd¿p¼¿ÌP`ÿRßïäi”ñþ¥¿ÞÈÓ(®ÛJò£'s€8'úP£ î8R¹!ºtüéÃ!Õ•¶•'œg±£ÖœRͽ™ØXŒg;}ýõ ÃƒŽ¿1Âð~nqÇ­Ôœ“0=O¡FÂîÊy`cwÏ=¥9p¥NyUôÆ[ž}¾ó~b€½qž@= Ÿ§¯áKž¼GPFü)`)Ëdœ|»<úþ”þ…`N# :z æ€Š1êsÀí­ßêdÿtÿ*ÃÕ?äSƒþ¹Åü…nMþ¦O÷Oò¬=SþE8?ëœ_ÈPG/ßî¯òÊ|¿|º¿ÈS(¢Š(¢Š(¢Š(§Û2Z‰AÁ‘÷¿Ì9léè2ªÁý¡w%ÇÙc¶Ù ¾^d‘”“€z>´§öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…Rû¯ÿ<ìïóÿñ4}Wÿžv?÷ùÿøš»öÆÿoóáGÛý¿Ì…UŠÚù7kl±àÿªvcœg¸(Ú±P1,G9ö  ~Øßíþcü(ûc·ùð¨w¯üòOÌÿ×þy'æÆ€&ûc·ùð£íþßæ?¡޿óÉ?3þ4o_ù䟙ÿ›íþßæ?¶7û˜ÿ ‡zÿÏ$üÏøÑ½ç’~güho¶7û˜ÿ >Øßíþcü*ëÿ<“ó?ãFõÿžIùŸñ  ¾Øßíþcü(7dã!ˆd…C½ç’~güj®¥r`Ó®%‰]c%O'×­>yä†-±[<嘕”cõ#Ö«}®ëþ³ßÈÿøª»ERû]×ýfÿ¿‘ÿñT}®ëþ³ßÈÿøª»ERû]×ýfÿ¿‘ÿñT}®ëþ³ßÈÿøª»ERû]×ýfÿ¿‘ÿñT}®ëþ³ßÈÿøª»ERû]×ýfÿ¿‘ÿñT}®ëþ³ßÈÿøª»EP{›§F_ìé†Aë#ÿ⪆±CáˆãaÑ#V„b·«#Äÿò“ýåþtµ/ßî¯òÊ|¿|º¿ÈS(¨®&ò"ß·q,ª£8É$ødÔµ^âÔ\¼eÙ‚&HUb§w@r=7Æ€öø<¨‹:oDÚKÇažyäS¾ÙuA&íØÃ(%yè7täqžãÔS ²Nd%8T9$n*O$äò¤óýïje­Œ–ª‰ãËÂïù>f*¡x9À(ì{óèë}J íÒRÛI@ÅpO\p>b Žüu§‹ërÈ›Èw$*!²1ž1‘Ô§=*§~êÙ<Þmâ§oR zôÊt÷ëRAfÑÜý¡ä #ݵp ;œpƒ×­YV¡†pFFAò5™u%½ÃÇ&EŸPdvùG”r=3߀{â§]ÁFò c’?Jf•nӼΒ0jNW;‡”Ž£7Z¿«!‘ÒH¢òŒèîF1–{Žži—Z”ÐÛ[ÖO64(û~ë06=G\ƒŒŒsIa£¥ŽáB˜ËGŠY1Ç,Ç;›Ø ç ñ†¦Ém"G,;s$ÐÄa3«H˜Ü€ Ä“ÆGן­Zª÷P<ÆŠEG‰÷‚ɸ”Ž™´ŒRlW*Ø„ØK«ã*qÈçÓКzß[·G<(@lSŽFHäqȦÁfÑÜý¡ä #ݵp ;œpƒ×­2-<ÌâH’(R§aÆwò~QÈÇÀÇÚ ù¿x>VqêNëÇÔCLÖä1pA(FáÓåãæê:g¨õÈtõŠX¤-¹ÑNãŒosŸ›¯yøÿkØTgM.’¤’!W àDf9qœÇ8ÆyöÀÈfIÔ´dðpC)R¸<Š’«ZÛhö¯”¹mÌ#ˆ"ôÇ;rsßÛ q÷ÙOÊ:.9îzôöþtú¥¬È&ëþ¹š»TµùÝ×3@h¢Š(¢Š(¢Š(¢Š(¢Š+#Äÿò“ýåþu¯Y'ÿ4Ÿï/ó  ©~øÿu¦SåûãýÕþB™@Q@Q@Q@:'†Æ«¸îl7SÍ6Š›í/éÿ7øÑö—ôÿÇ›üj(o´¿§þ<ßãGÚ_Óÿoñ¨h  ¾ÒþŸøóiOüy¿Æ¡¢€&ûKúãÍþ4}¥ý?ñæÿ†Š›í/éÿ7øÑö—ôÿÇ›üj(o´¿§þ<ßãGÚ_Óÿoñ¨h  ¾ÒþŸøóiOüy¿Æ¡¢€&ûKúãÍþ4}¥ý?ñæÿ†Š›í/éÿ7øÑö—ôÿÇ›üj(o´¿§þ<ßãGÚ_Óÿoñ¨h  MÂCÔ“ýj¥Ä—xT¶ŽQ’L’r}€>•5Kv§ÿŸþ³úš©äWÉ™¹~öè™qÓŽ@ç‘ÅX¬ùí¦’+ÕPG›2°Áe ™ñÎÁã׊Š+)V‘Fñ¶âÊ]ÕJ.Wä±Û8ûÝ@¤’+³ªœ˜Ûk{üˆ§Õ>ÜA-É[QÈÊË€££ŽcŸÎ¯P3K –8-âóg—8ÀP?‰½Oÿ]H‘^£•º‚06îDû”ûrÏSÓ¥‚=šŒw!€ÛFÀ÷ƒÇ¡ÈúâíÍÀeÀÇN™Ï·olÐ"‰V2~vRÀ{ gùŠ}gê6²\Í E%ÃÛHýägëÑOOJ€ØÎªB!ÚÂ@ÃUóÅ8û›°:sƒÔнƒå}“ì ˆ³ùQÉ*&Ü„Ãq€0Cd¨È'Œç4ë; ƒ[I"K¸,E[äUÊ’FñÈn=²hrŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ²_¾?Ý_ä)”QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVG‰ÿä 'ûËüë^²_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ >_ùåýû_ð Š>_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ >_ùåýû_ð Š>_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ >_ùåýû_ð Š>_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ >_ùåýû_ð Š>_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ >_ùåýû_ð Š>_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ >_ùåýû_ð Š>_ùåýû_ð£åÿžQßµÿ (£åÿžQßµÿ Û ‹û³÷Tâ_J(¢Š(¢Š(¢Š(¢Š(¢Š+#Äÿò“ýåþu¯Y'ÿ4Ÿï/ó  ©~øÿu¦SåûãýÕþB™@Q@Q@Q@&à©3€)ÿЩi’¨¹ÿu“PdÔDWŽÖñ‘€*Ëläê8§}´ÿÏ÷þ¿øUÍP¶öÍ*,YE,™`©_¯l~£Ö´>Ùmöo´ý¢³ÿÏ]ãg\ué׊ÃûiÿŸ;ïüð£í§þ|ï¿ðÿ¶´-ÜË*4“Æe e÷ï’GÐúUªç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü(ûiÿŸ;ïü𮎊ç>ÚçÎûÿ_ü*Ø9ÏÅŸÌ©­ŠÉõÇþ¸ý’€EPEPEPEPEPY'ÿ4Ÿï/ó­zÈñ?ü¤ÿymK÷Çû«ü…2Ÿ/ßî¯òÊ(¢Š(¢Š(¢Š)’¨¹ÿu“SéŒSl¨îpQÏü € ¶´¸›O€Äެ­dR¹ÌlXŸâÁã#¦­­¤âÙ¥1Ý4æ4ñy«òlÈÙÓŒÇ9Ï%µÜ¶°ÛÇ0)*)`2@çš“ûJ?ùêŸ÷Èÿ⨖Ð\%Õœ’CÀŽer»FÒ̬ üD)ÎÜÄö­:ÏþÒþz§ýò?øª?´£ÿž©ÿ|þ*€4(¬ÿí(ÿçªß#ÿŠ£ûJ?ùêŸ÷Èÿâ¨BŠÏþÒþz§ýò?øª?´£ÿž©ÿ|þ*€4(¬ÿí(ÿçªß#ÿŠ£ûJ?ùêŸ÷Èÿâ¨BŠÏþÒþz§ýò?øª?´£ÿž©ÿ|þ*€4(¬ÿí(ÿçªß#ÿŠ£ûJ?ùêŸ÷Èÿâ¨BŠÏþÒþz§ýò?øª?´£ÿž©ÿ|þ*€4(¬ÿí(ÿçªß#ÿŠ£ûJ?ùêŸ÷Èÿâ¨BŠÏþÒþz§ýò?øª?´£ÿž©ÿ|þ*€4(¬ÿí(ÿçªß#ÿŠ£ûJ?ùêŸ÷Èÿâ¨BŠÏþÒþz§ýò?øª?´£ÿž©ÿ|þ*€4(¬ÿí(ÿçªß#ÿŠ£ûJ?ùêŸ÷Èÿâ¨BŠÏþÒþz§ýò?øª?´£ÿž©ÿ|þ*€4(¬ÿí(ÿçªß#ÿŠ£ûJ?ùêŸ÷Èÿâ¨B²_ýqÿ®#ÿd©¿´£ÿž©ÿ|þ*«ù‰$Ìc`ÀEÕh(¢Š(¢Š(¢Š(¢Š(¢Š+#Äÿò“ýåþu¯Y'ÿ4Ÿï/ó  ©~øÿu¦SåûãýÕþB™@Q@Q@Q@9duW`=6Š/üôûèÑçKÿ=þú4Ê(þt¿óÑÿï£G/üôûèÓ( ùÒÿÏGÿ¾t¿óÑÿï£L¢€çKÿ=þú4yÒÿÏGÿ¾2Š/üôûèÑçKÿ=þú4Ê(þt¿óÑÿï£G/üôûèÓ( ùÒÿÏGÿ¾t¿óÑÿï£L¢€çKÿ=þú4yÒÿÏGÿ¾2Š/üôûèÑçKÿ=þú4Ê(þt¿óÑÿï£G/üôûèÓ( ùÒÿÏGÿ¾t¿óÑÿï£L¢€çKÿ=þú4yÒÿÏGÿ¾2Š/üôûèÑçKÿ=þú4Ê(þt¿óÑÿï£G/üôûèÓ( ùÒÿÏGÿ¾t¿óÑÿï£L¢€çKÿ=þú4†Ga†v#КmQEQEQEQEQEVG‰ÿä 'ûËüë^²¦€ÎF–g9"Yð@ýX¦GÄ¥P`-ø““úš}RƒþBןõÎ/ýž®Õ(?ä-yÿ\âÿÙêíQEQEQEQEQEQEQEQE‘âùIþòÿ:׬ÿÈO÷—ùÐÔ¿|º¿ÈS+ƒ:î¦Ç&íóôáIý¹©ÏÛþCü(½¢¸/íÍKþ~ßòáGöæ¥ÿ?oùð öŠà¿·5/ùûÈ…Ûš—üý¿ä?€;Ú+‚þÜÔ¿çíÿ!þnj_óöÿÿ ïh® ûsRÿŸ·ü‡øQý¹©ÏÛþCü(½¢¸/íÍKþ~ßòáGöæ¥ÿ?oùð öŠà¿·5/ùûÈ…Ûš—üý¿ä?€;Ú+‚þÜÔ¿çíÿ!þnj_óöÿÿ ïh® ûsRÿŸ·ü‡øQý¹©ÏÛþCü(½ª÷q\°2ù‡hÀ +(ü®+ûsRÿŸ·ü‡øQý¹©ÏÛþCü(°þʵô›þÿÉþ4eZúMÿäÿãÿ·5/ùûÈ…Ûš—üý¿ä?€;ì«_I¿ïüŸãGöU¯¤ß÷þOñ®?ûsRÿŸ·ü‡øQý¹©ÏÛþCü(°þʵô›þÿÉþ5-µœ6¬í °.bÎÍœgIõ5Ånj_óöÿÿ ?·5/ùûÈ…vRéÖòÈÒIçc“û÷Ö›ý•ké7ýÿ“ükþÜÔ¿çíÿ!þnj_óöÿÿ ì?²­}&ÿ¿òÙV¾“ßù?ƸÿíÍKþ~ßòáGöæ¥ÿ?oùð Ãû*×Òoûÿ'øÑý•ké7ýÿ“ükþÜÔ¿çíÿ!þnj_óöÿÿ ì?²­}&ÿ¿òÙV¾“ßù?ƸÿíÍKþ~ßòáGöæ¥ÿ?oùð ÖÚÎVv…X1gfÎ3ޤúš±\öæ¥ÿ?oùð£ûsRÿŸ·ü‡øP{Ep_Ûš—üý¿ä?ÂíÍKþ~ßòá@íÁnj_óöÿÿ ?·5/ùûÈ…w´Wý¹©ÏÛþCü(þÜÔ¿çíÿ!þÞÑ\öæ¥ÿ?oùð£ûsRÿŸ·ü‡øP{Ep_Ûš—üý¿ä?ÂíÍKþ~ßòá@íÁnj_óöÿÿ ?·5/ùûÈ…w´Wý¹©ÏÛþCü(þÜÔ¿çíÿ!þÞÑ\öæ¥ÿ?oùð£ûsRÿŸ·ü‡øP{Y'ÿ4Ÿï/ó®gûsRÿŸ·ü‡øTW¥íÌF)çgCÔ(ÿÙsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/images/splashImage.jpg0100644 0000000 0000000 00000010666 12114157751 024612 0ustar000000000 0000000 ÿØÿàJFIFHHÿÛC   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcÿÛC//cB8BccccccccccccccccccccccccccccccccccccccccccccccccccÿÀ"ÿÄÿÄJ  !1AQ‘"Uaq¡±2rs’ÁÑ#356Rb‚“áBCT¢²Â%&DS”ÒðÿÄÿÄ&1A!"Qq3a#BÿÚ ?°E„@ePXDQaDDD@aDD”XDQDX@eD@DDaeaó$ŒŠ7I#ƒXÀ\âzUÅÛ\\*'smîÐæž^áëÏ7b”kšÿÓòFÓ‡Ô¸D=œçÜ1ÚªÕíÓbR[¤FÉßTׇ9ªf ÙÅŒrÙåµ§UgòÇàµìZ¶K-¼RGGƒˆ¸¼¸‚sÖºCÂ$ùÞÝ>´þ IFwâ*ˆzX*5#ïÔÐÜTØw)ÃNÙÇ^[YÜ+èéiâ¶ ¹iK8°Ð9¹ºI÷.•‚óòßãQÆè‹^XöœØôó…¿j«œWª¸¨«È#“­àiÆ6<ã¯+§“'ðŽéª¥•‘¶JÀ^CFbÀß±Yа²1Ï/sZqç'­B4eÚñv»8UU¹ôð°¹ã važ³Ø½5¶ ­·\a¦ ©1˜h9$íÎ=^õrAÎj $ ª*²—Têꢎ§M#œ“oêØ-êúíeANêš¹@€]Ã>ÅËÓI:m ,TU?•×ßóîþ[?Ö¥¸ë•RÒ:1‘ʆFÒýý}\Û#ÓJ<´,°‘Uu:›QRÔ> ëÉc8sLlØ÷.ÞŽÔ·*û·‰ÖÊ'cØHqh¤{–žQŽádåK\ß*ín¤††~JG‡9øí°ãÛÜ¢ôú²þúˆÚÊ·Jâà|›|ýù¶RyJ;²ÖE^ÖUëHb’ª`ø¡hâw c!£Ù¹\«¯£ÿ=ßËgຎšRá¡e° ÃQWÒ__MAVø™Ð8¹úGQ I§ïO®ÓÆáZÃwš0oJ«jêWW5LŸ>W—Ÿi9]iñ{Þîƒ$Úfñzº_)éß]+¢ŽAóFý];Õc¨WƒŠ zª÷ ÞîI‡Ô7>üw.5ÓV݅Ω´•¥´í•Â0Óæƒ¶øIãõ25g"†h‹¥Úí[;ë*Ý%<,oF\NÜè½®.õªqG9†ydç´ ùýd,^&§³±d•PÝY{Ã[\òç#fç¹ZTŒ–:XY<œ¤¡€=øÇ±¹L˜ž>EžÈˆ²(DDD@V À$ô !^hkê䤒žžI ®“iqk‰àz€P¯“«¿ÉT)ß‚™Çá žp–ÞNÎdœý„l¾ÿ´:GËüÁø/~7–Û´çÁššz|rðÉ7KsÞ¼—oSê~¨…Í„Ã-!­.É$ã'܆´Mx¹Åc&¸:gô5½;õ•éSj;¥à«XwDøÃÀrfbÙ{¾hÿhU“‰s‹œI$ä“Ò§ÞkÄt”ÖøÎ Žåì€ïø(%4.©©Šþ|¯ o´œ,tëÚæû ±ô ‰ØÝU õ.ãÏSFÃï=ª{®ùJïSW“Ã#Ïdl=À+RÔÇeÒ¯†ÂLbž!œÆ>*«SN·Iäa’ÿvþZå5sÇ›NÞäyßÓ=ë£á¿‚’š‡y]Ê?~°÷Ÿrëhº!E§`8sægo7» ªî娕ŽâŠ3ÉF}Cúäö®#ü™›ø/G*ŸQl¢!Ï?¹‡f@ìZêAEvDBµeÊ‚¦FœÇä™ìnßžÕ¿ (,µ¶ê:þ§Žn›Æ3…«äå›Ñ´ÿatÖTRk†&Atâ06&·€4lêZNY½OöQašáƒRŽÙC@ç:Ž–8\ᇠe+-T5òÕÒÇ3š0 ÆpÚÔ®ºQ[‹eC!ãÏNNMøäþNY½OöoÂ-pŽ [lDïÊ<ÌÍûû”‹Êk/¤aïGÛ,÷¾çÁHxÃdÉÜÿ¤$ã%)Ù žŽÕuÓ3gJö°¬œ+qÖ Tœ¥,``.`ÎÀ\³”¶W ø¼0¸Ëi]êK;ÜÛ„%ÄàNëLÙ%’œSÏ“–oFÓý…¿ÓD"‚&EækjÖ^­´3ò5U‘E&3Â㾇”Ö_HÃÞ°©¿’ŸoÓö‡½Ï}¾9Ç$–ä’½©-ú¹ZZH¡“âcpqÔµ¼¦²úFôòšËé{Õ©þÁ­Y©¬%óÑ×JË—‚Aõ7ÇôGìSÿñŸÿÕ}Wéý6+êêdc*ÆÙ ›8öÙi|•£= ›ý±P¯CKRÜl·ø½–yY8äl%¥­ã$gs…§‚J™ã‚—É#ƒZÑÒJžPéÝ+_?%ITù¤ˆ±²ôwzÔ†Û`¶ZŸÊRRµ²cn%Îì'›±j³Çi]þÅÊ6Ûí´ôÆ"`i#¤ôžÒ¶×)úŽÏÜÇÜ!i Œó)¬¾‘‡½ydüÑN²/YN(ülÈ<_‡£‡­hyMeôŒ=ë•ø@ë"Ò¡»P\æQÔ²g4eÁ½n£Mr"(" ˆ€"ùÂ[α©-ùÎoj 6ÑjН¢¾…OîŽô¢Y°‹â7ñ·8ÂûBŸ/sXÇ=î kFI'»ëWÇ+Yk®ˆäxÄ­$8þër3Ö3Ï—öðo3:žÓãv6ÿäþèè<ýJ?SÇ_U +yG½Ü-h<îïØ ó‘Ì”z#‡Ù¾E‡§.r]($–^:9L|ma`xàósãÚYFôý¶V2††¡óSÅñ¼œ°M‘Ž£ó²:‘D©>L(‡„†’©]ÄøøJ—¨„Ñ¿_ÿ¶Áù]+’Àlà‹FÒ6¥4zæºŽŽ fRÓ¹°ÆØÁzò¢ZŸO¶Ã%;[Pf僎íáÆ1ëõ«Y@¼%þ~ƒè¿â“O’Nj7଄©ïöw6¹?ùCñP%y­µ9% ÚȈv¨¡uˆ†–W‰]ÚÐð1Î=ʾÁÆq·Z³µ÷êÛþµŸ m-£kGOXæ¸þëšÏ¿êiçP·Û ñÓ•ÿ'_)j Ãø_ôNÇãžÅi^ë…¶ÏUWœ:6¤vò4¥zŠýãÚj×N™8¦ß|·ÍöœžÅsbÝ8°™9;œïÒ°»JÓö‰0ú“,§=^`àjã¯Dd¤­¹çc~E’>Áâäc£*˜WDÿ¢$úƒþÕK¯.“û2yàÑ£’¸;ñ0gí)„x4üÅÒgÀ©ºój?#""Ä¡D@chÉKˆsA!o¯ †|vªˆÍ^J?ذh+áÑãy%v=¸_-àÏä£âõžeÑ úcÎÞÕÄÕWwSD-ôŽÅTíóÞ?˜Ÿiæ§¡oÏTÚY*¦ 6»A@êëÝtŒ¦a–²¥Üs8g†1Ð ºß™rk‰&î\#VœÖË$vÊ(LÒµŽnÙhé9èÇO½Hô……Õg2)2̰¸=à Ý­ÈèÜõŽe!ÓÚ~žÇNCq%LŸ›„ ú€è °¡g‘Ïèò§§†–ÃOb‰ƒ k^¨ˆfaD|$~ˆ¥úÿø•.QmwGYp¤¥§£¥’b$/qhæÛâµÂë" ­U½c£¦uŽÞçSÄ\i£$– üЫ&o^Ž›¸+:ÄÙYd¢Žx‘ÄØÜÇs‚ѹzuRM*dE{®£dZ‰ícy6ìÑ€¹úsõ†ßõíø®î®´].7ù¦¦¡•ñµ­x;~V¦Åw£»RTÉnŸ‚)ZçmÐëXÍzU}²ÒP/ _Ÿ ú/ø…=P­wn¯¹VR¶ŽŽY[^ѶIæ÷/¥‘6V@Uæª/&o^Ž›¸+jºH#{˜Xç4×s´ã™k«’uAí}ú¶ÿ­gÅs´ekô…}#¹¥™í¨ð7½uuµ5Ue™´ÔtïžGÊ Àg‚ðД•”úšjÊi!<¯(Òáó²ÿ½faÿGejö:7¹®i ƒÐW½º‘õ÷ zFg2¼7nÒ{êGªtÕ{ï³ÍCHùa›eƒ`O?¿'µnh?YIu}]u3áGˆøº\vøg½{hú{¬”cÂDmŠ;Tlc#@p(B°uå¾¾å=(é$™±5ÅÎhÛ.#oôûÔSÉ›×£¦î œŠÆ“a–”ÿ¢$úƒþÕK«’gÌë Ü .êrXß‹‡›½V>LÞ½7pY餕Û+$þ ?1_ô™ð*lªº;V¦¡UÀ»¸3ï[ޱë¸}¿ê¹É‰NnJHbÊàhúZê{t¯¹ò¦¦YI<«²îzï/,•:)”D\€ˆˆÁ+(€Óž.nGA_<ËwxÉNׂÆU²Q¿SWÜ! ¥pü»ø€q±é;g›¤ö]J*:{|\•-…™É é=dô­¡NèÆ݇RdzŽÜ„>„ïGÚ½£/pË€|ÇnîÜü²…A¡aeDDVYDeaE•„VE”P…”@aQDDD@DDD@DDD@DDD@DDD@ÿÙsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15.htm0100644 0000000 0000000 00000003210 12114157751 022367 0ustar000000000 0000000

Using SimplyHTML

Once application SimplyHTML is set up as described in chapter ' Getting started', it is ready to be used. The chapters in this section describe usage of application SimplyHTML. For information about how the functionality mentioned in this section is achieved, please consult chapter 'Inside SimplyHTML'. This section is divided into the following chapters

Users might want to start with 'What is SimplyHTML' to see whether or not the application fits their needs. Besides general information about document usage in SimplyHTML the part about editing documents mentioned above takes the most room as it is the main functionality of SimplyHTML.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/index.htm0100644 0000000 0000000 00000000262 12114157751 022216 0ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic1/0040755 0000000 0000000 00000000000 12114157751 021577 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic1/topic24.htm0100644 0000000 0000000 00000005727 12114157751 023605 0ustar000000000 0000000

Parts of the distribution package

The distribution package comes as a single compressed zip file. It contains

  • SimplyHTML.jar - the executable file for the latest stage of SimplyHTML
  • Help.pdf - this tutorial as PDF file
  • readme.txt - essential information about SimplyHTML
  • gpl.txt - the file containing the license agreement valid for all parts of the distribution package
  • jhall.jar - JavaHelp runtime extension
  • source - source code directory
  • doc - directory with API documentation files

Please refer to chapter 'License' for terms and conditions of using above parts.

Restoring contents of the downloaded ZIP file

Above contents can be restored from the compressed distribution file by using one of the many applications capable to extract ZIP files (WinZIP or Ark for instance). If you do not have such an application, you could use application Extractor available free at

http://www.calcom.de/eng/product/xtract.htm

Getting compiled classes

Besides compiling the sources, file SimplyHTML.jar has a complete set of classes for the latest stage of SimplyHTML. Java Archive (JAR) files are structured like ZIP files. Their contents can be restored with any application able to extract ZIP archives (see above).

Getting this tutorial in JavaHelp format

Additional to the version in PDF format SimplyHTML.jar has a complete set of this tutorial in JavaHelp format in package com.lightdev.app.shtm.help too. Opening file index.htm in this package lets you open the tutorial with any Java enabled browser as well.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic1/topic7.htm0100644 0000000 0000000 00000001466 12114157751 023522 0ustar000000000 0000000

Author

SimplyHTML, this documentation and all contents of the distribution package of SimplyHTML are created and maintained by

Ulrich Hilger
Gartenstrasse 15
65830 Kriftel
Germany

e-mail info@lightdev.com
Internet http://www.lightdev.com
Fax +49 721 151 41 09 67

I would like to hear your comments and suggestions so please don't hesitate to write.

All rights reserved. Please see chapter ' License' for details.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic1/topic5.htm0100644 0000000 0000000 00000002137 12114157751 023514 0ustar000000000 0000000

License

Except for parts shown separately below, application SimplyHTML and all of its components such as source codes, documentation and accompanying documents are distributed unter the terms and conditions of the GNU General Public License. To read the full license text, please see file 'gpl.txt' in the distribution package of this software or refer to its text here.

Parts falling under different license terms

Classes ExampleFileFilter , ElementTreePanel and their source code is Copyright 2002 Sun Microsystems, Inc. All rights reserved.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic1/topic5/0040755 0000000 0000000 00000000000 12114157751 023002 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic1/topic5/topic56.htm0100644 0000000 0000000 00000053444 12114157751 025014 0ustar000000000 0000000

GNU GENERAL PUBLIC LICENSE

Version 2, June 1991

Copyright (C) 1989, 1991 Free Software Foundation, Inc.

59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

Preamble

The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.

To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

We protect your rights with two steps:

(1) copyright the software, and

(2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.

Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.

Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.

The precise terms and conditions for copying, distribution and modification follow.

GNU GENERAL PUBLIC LICENSE

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.

1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.

You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.

b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.

c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.

In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:

a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.

4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.

6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.

7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or amongcountries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.

10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

NO WARRANTY

11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Programs

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

<one line to give the program's name and a brief idea of what it does.>

Copyright (C) <year> <name of author>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this when it starts in an interactive mode:

Gnomovision version 69, Copyright (C) year name of author

Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.

This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:

Yoyodyne, Inc., hereby disclaims all copyright interest in the program

`Gnomovision' (which makes passes at compilers) written by James Hacker.

<signature of Ty Coon>, 1 April 1989

Ty Coon, President of Vice

This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic1/topic21.htm0100644 0000000 0000000 00000012063 12114157751 023571 0ustar000000000 0000000

History of SimplyHTML

April 27, 2003

Stage 11 published with find and replace support.

February 15, 2003

Stage 10 published adding a HTML editor with syntax highlighting and some enhancements in the plug-in architecture.

December 29, 2002

Release 2 of stage 9 published. This release is a major update with enhancements to the way SimplyHTML handles HTML content. Release 2 focuses on a clearer distinction between HTML 3.2 and HTML 4. It lets the user choose the HTML version SimplyHTML should use through the options dialog. Changes were made to the style sheet handling and the plug-in architecture too.

December 13, 2002

Stage 9 published featuring creation and manipulation of links and link anchors. Refines working with paragraph styles and named styles by supporting tag types and multiple styles for given tags in certain controls. Refines cut and paste for nested paragraph tags. Compensates different display of font sizes between Java and Web Browsers. Table rendering was enhanced. Selected dialogs have a button for context sensitive help on the respective dialog. A new section user interface has been added to the tutorial for this purpose. The application 'remembers' the directories of the file that was last opened and saved.

November 8, 2002

Stage 8 published implementing paragraph and named style manipulation. SimplyHTML is enabled for Java Web Start as well and can be started with a single mouse click now from SimplyHTML's homepage at http://www.lightdev.com.

October 20, 2002

Stage 7 published implementing image insertion and manipulation. Fixed a serious bug in class AbstractPlugin.

October 4, 2002

Stage 6 published implementing list formatting.

September 19, 2002

Stage 5 published implementing a plug-in architecture and enhanced handling of resource bundles for dynamic menu creation. Along with the plug-in facility, user settings are implemented to be persistently stored. An AttributeMapper compensates some discrepancies between Java and HTML.

August 15, 2002

Stage 4 published featuring table manipulation

July 11, 2002

Stage 3 published featuring font manipulation and dynamic tool bar creation.

June 29, 2002

Stage 2 published. Features resource bundles, multiple language support, cut and paste, drag and drop, both including HTML styles and dynamic menu creation.

June 21, 2002

First full release of stage 1 published. Inlcudes initial build of the tutorial, API docs, source codes, executable JAR file and a PDF version of the tutorial.

June 16, 2002

published pre-release 1 of stage 1 (source codes and javadoc compilation). Wow, eight weeks went by and still no first release finished. Let's at least share what we have so far. Because the tutorial takes more time to write than the application itself and although the tutorial is almoset done, this pre-release comes without the tutorial.

April 2002

start of the SimplyHTML project.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic22/0040755 0000000 0000000 00000000000 12114157751 021662 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic22/topic6.htm0100644 0000000 0000000 00000003643 12114157751 023603 0ustar000000000 0000000

Requirements

Java

To run SimplyHTML, a Java 2 Standard Editition (J2SE) Runtime Environment (JRE) of version 1.4 or higher is necessary. To work with the source code, a Software Development Kit (SDK) for J2SE version 1.4 or higher is required.

J2SE JRE and SDK are available at no charge at

http://java.sun.com/j2se/1.4

JavaHelp

For using this tutorial as online help from out of the application, the JavaHelp runtime extension is needed. The JavaHelp runtime extension is distributed with the SimplyHTML package (file ' jhall.jar'). To learn more about JavaHelp and for access to sources and API documentation of JavaHelp, please visit

http://java.sun.com/products/javahelp

Java Web Start

To install and run SimplyHTML directly from its home page the Java Web Start extension is needed. Java Web Start is installed together with the JRE (see above) automatically. To learn more about Java Web Start technlogy and for access to sources and API documentation, please visit

http://java.sun.com/products/javawebstart

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic22/topic52.htm0100644 0000000 0000000 00000006251 12114157751 023662 0ustar000000000 0000000

Starting SimplyHTML

Java Web Start

The simplest way to run SimplyHTML is to press button 'Web Start Me Now' at http://www.lightdev.com/dev/sh.htm . Alternately, direct your browser to http://www.lightdev.com/shtm.jnlp to achieve the same.

No file copying, no link or path settings, just one click.

Traditional start

To run the executable JAR file without Java Web Start, use the following command on the command line prompt of your system

(replace \ by / and omit .exe on Unix or Linux systems)

[JRE]\bin\java.exe -jar [AppDir]\SimplyHTML.jar

[AppDir] in above command means the directory, you have installed SimplyHTML on your computer. [JRE] means the directory, the Java 2 Sandard Edition (J2SE) Runtime Environment (JRE) is stored on your computer.

Note: All paths should not contain blanks. A path such as C:\Program Files\SimplyHTML as the <AppDir> will only work if it is put in quotes, such as in "C:\Program Files\SimplyHTML\SimplyHTML.jar"

Example 1 (Windows)

If your JRE is in directory

c:\programs\java\j2re1.4.0_01

and SimplyHTML is in directory

c:\programs\SimplyHTML\

the command to start SimplyHTML would be

c:\programs\java\j2re1.4.0_01\bin\java.exe -jar c:\programs\SimplyHTML\SimplyHTML.jar

Example 2 (Linux)

On Linux your JRE might be on

/usr/lib/java2/j2re1.4.0_01

and SimplyHTML might be in

/opt/simplyhtml

then the command to run SimplyHTML would be

/usr/lib/java2/j2re1.4.0_01/bin/java -jar /opt/simplyhtml/SimplyHTML.jar

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic22/topic23.htm0100644 0000000 0000000 00000001615 12114157751 023657 0ustar000000000 0000000

Installation

Java Web Start

No installation is necessary with Java Web Start, just go on to ' Starting SimplyHTML' when using Java Web Start.

Traditional installation

Once downloaded the SimplyHTML distribution package zip file

  1. create an arbitrary folder such as C:\Programs\SimplyHTML
  2. extract all contents of the downloaded zip file into that folder
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16.htm0100644 0000000 0000000 00000006500 12114157751 022375 0ustar000000000 0000000

Inside SimplyHTML

This section explains the way application SimplyHTML is built, its structure, design and internal functionality. Refer to section ' Using SimplyHTML' to read about its usage.

Stages sections

SimplyHTML is built in stages. By dividing development into pieces, it is easier to concentrate on a certain detail of the application, paying more attention to the particular design of this part. The application will be more modular making maintenance easier to achieve.

Each stage results in a complete application. The resulting application gets more complex with every stage added while retaining its modular design. This section has chapters directly corresponding to each stage of SimplyHTML.

Source codes

A complete set of source codes is distributed together with this tutorial for each stage.

For stage 2 for instance there is a complete set of sources chapter 'Stage 2' refers to. This set of sources contains all sources of stage 1 and 2 making up one executable which - when compiled - represents stage 2 of the application.

In essence, sources of stage 2 do not contain changes versus stage 1 only, they represent a complete application stage inlcuding previous stages.

Spotlights section

The spotlights section discusses certain topics from a process oriented point of view. Where the stages sections explain the parts of SimplyHTML in the way they are structured (by classes and methods), spotlight topics wrap several parts of the application together to explain one process.

How to use this part of the documentation

This part of the tutorial should be used together with the source codes which have plenty of documentation in addition. Source codes are commented and most of the comments went into the API documentation accompanying the package. Additional (non Javadoc) comments make clear certain parts of code on top of that.

This tutorial does not repeat code. It is structured to lead the way into the very details of the application's source code by adressing certain topics in one block. Chapters usually refer to the source codes by naming certain fields or methods. It is recommended to open the source codes in parallel to reading this tutorial.

Target audience

Basics of Java and programming in general are not covered here, so interested readers should have a basic knowledge about these already. This section concentrates on discussing best practices and how to's in conjunction with a particular part of the application, trying to make a top down approach in covering the key targets of SimplyHTML.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/0040755 0000000 0000000 00000000000 12114157751 021665 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/0040755 0000000 0000000 00000000000 12114157751 023156 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/topic80.htm0100644 0000000 0000000 00000006554 12114157751 025165 0ustar000000000 0000000

Using the new font formatting GUI

Previous chapter described FontPanel as SimplyHTML's GUI to set most relevant font and font style settings at once. To actually use class FontPanel two additional classes are required. First of all, a dialog is needed to present a FontPanel to the user. As well an action to invoke respective dialog must be created.

Class FontDialog

Class FontDialog simply wraps class FontPanel into a JDialog and creates all methods necessary to control the dialog such as closing the dialog with 'OK', cancelling the dialog etc.

In its constructor FontDialog expects an AttributeSet which is routed on to FontPanel for display and manipulation. Once the dialog is closed by pressing the 'OK' button, method getAttributes returns the AttributeSet from method getAttributes of class FontPanel. Method getResult returns the information whether the dialog was closed with the 'OK' or the 'Cancel' button.

Action FontAction

With class FontAction all prevously descibed font functionality is 'plugged' into SimplyHTML's mechanism to make functions avaliable on the GUI. Its method actionPerformed creates an instance of class FontDialog and applies font changes from FontDialog to a document. FontAction is added to the commands Hashtable of FrmMain through method initActions and its name is reflected in the action name constants of FrmMain ensuring proper usage during dynamic menu creation.

To always reflect proper state to components bound to FontAction, it implements interface SHTMLAction with method update.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/topic81.htm0100644 0000000 0000000 00000016607 12114157751 025166 0ustar000000000 0000000

Actions and components to switch single font attributes

On top of being able to change most relevant font settings at once using class FontPanel, a couple of actions and components are needed to allow users to toggle or switch single font attributes quickly. In stage 3 of SimplyHTML this is done by adding some inner classes to FrmMain implementing respective parts:

  • FontFamilyPicker - a JComboBox dedicated to font family changes
  • FontSizePicker - a JComboBox dedicated to font size changes
  • FontFamilyAction - an action to change the font family
  • FontSizeAction - an action to change the font size
  • ToggleFontAction - an action to toggle a single font setting on or off

FontFamilyPicker and FontSizePicker

The easiest way to act on a certain font setting probably would be an action bound to a JButton . For font properties family and size however, a possible setting is not just 'on' or 'off', for both attributes there a is a certain list of possible selections instead. For this type of setting a JComboBox is the GUI component of choice.

Extending JComboBox

To make such JComboBoxes easier to handle, two inner classes FontFamilyPicker and FontSizePicker extend class JComboBox with functions special to the purpose of changing respective font settings.

Customized content and common interface

FontFamilyPicker adds all font family names found on the particular system to its combo box using method getAvailableFontFamilyNames of class GraphicsEnvironment in its constructor. FontSizePicker adds a fixed list of point sizes instead. Both classes implement interface FontComponent for standardized access to their selected value.

FontFamilyAction and FontSizeAction

Both actions implement interface SHTMLAction so that common handling of setting action properties from our resource bundle and common updating can be used. In their actionPerformed method they apply the attribute represented by their associated picker component (family or size) to the editor.

ToggleFontAction

ToggleFontAction allows to switch a single font setting on or off in a generic way. It extends AbstractAction by defining some private fields reflecting the font attribute this instance of ToggleFontAction represents as well as the value for 'on' and 'off' for that particular font attribute. In the constructor, those fields are initialized from respective arguments passed to the constructor.

Shifting state

Method actionPerformed applies the font attribute resulting from the current state (on or off) and then toggles the action's state using method putValue. By passing either value FrmMain.ACTION_SELECTED or FrmMain.ACTION_UNSELECTED with key FrmMain.ACTION_SELECTED_KEY to method putValue, respective value is stored in the action's poperties table causing a PropertyChangeEvent being fired. Any listener to such events can then update its state accordingly.

Interfaces

ToggleFontAction implements interface FontComponent so that its value can be changed in a standard way from other objects through methods getValue and setValue . To always reflect proper state to components bound to FontAction, it implements interface SHTMLAction with method update.

Integration to FrmMain

Method initActions of class FrmMain initializes three instances of ToggleFontAction to the central commands Hashtable , one for CSS.Attribute.FONT_WEIGHT, one for CSS.Attribute.FONT_STYLE and one for switching CSS.Attribute.TEXT_DECORATION between normal and underline . For each of the three instances a separate action command is created in the constants list of class FrmMain for proper handling in dynamic menu and tool bar creation.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/topic75.htm0100644 0000000 0000000 00000025024 12114157751 025162 0ustar000000000 0000000

Creating a GUI for font manipulation

To use the functionality described in the previous chapters, application SimplyHTML needs additional GUI components the first of which is class FontPanel. Class FontPanel allows to display and change most relevant font and font style attributes at once. By wrapping all related components into a panel, it is easier to use it in different places such as dialogs later.

Setting and getting attributes

FontPanel uses methods getAttributes and setAttributes to exchange font settings with other objects through an AttributeSet.

In the constructor of FontPanel all components implementing the FontComponent interface (see below) are added to Vector fontComponents . This makes it easy for methods getAttributes and setAttributes to distribute or collect the attributes to and from the various font manipulation components through an AttributeSet.

Methods getAttributes and setAttributes simply go through all objects in Vector fontComponents and call methods getValue and setValue respectively passing the AttributeSet containing the actual font settings.

Components of class FontPanel

FontPanel uses GUI components defined in different classes to set the various font attributes:

  • font family - FamilyPickList
  • font size - SizePickList
  • font style - StylePickList
  • line effects - EffectPanel
  • colors - ColorPanel

FamilyPickList, SizePickList and StylePickList are inner classes of class FontPanel and variations of a separate class TitledPickList which defines the general behaviour of a pick list typical for font dialogs having a list, a text field and a title label. All mentioned classes are described below shortly. Please consult the sources and API documents for further details.

TitledPickList

Class TitledPickList defines a pick list typically being used in font dialogs, consisting of a list title, a text field for the currently selected value and the actual pick list containing all possible values. It implements listeners for the various events produced by user settings inside its controls to synchronize selections in the text field and the pick list at all times. Then it has some getter/setter methods to programmatically get and set a selection.

It also defines a an EventListener and Event so that external components can be notified of changes in the TitledPickList . This mainly is meant to allow FontPanel to update the sample text display whenever a selection changes.

FamilyPickList, SizePickList and StylePickList

Classes FamilyPickList , SizePickList and StylePickList all are subclasses of TitledPickList. They extend TitledPickList by implementing interface FontComponent.

Interface FontComponent

Interface FontComponent is used to standardize the way attributes are set and retrieved. It defines two generic methods getValue and setValue. setValue is meant for setting a component from an AttributeSet, getValue should return the setting of a font component in the form of an AttributeSet.

Implementing the FontComponent Interface

Each component implementing the FontComponent interface can do the implementation special to the attribute or set of attributes it is meant to manipulate. FamilyPickList for instance simply reads CSS.Attribute.FONT_FAMILY, StylePickList acts on a combination of CSS.Attribute.FONT_WEIGHT and CSS.Attribute.FONT_STYLE and SizePickList uses CSS.Attribute.FONT_SIZE and adds certain handling for the 'pt' identifier.

EffectPanel

Class EffectPanel is a JPanel with a ButtonGroup of JRadioButtons allowing to select, whether or not a text portion should be underlined or striked out. With CSS.Attribute.TEXT_DECORATION, attributes underline and line-through can not be combined which is why JRadioButtons are used allowing only one of the possible selections at a time.

EffectPanel implements interface FontComponent to set and return the component's value in form of an AttributeSet.

ColorPanel

Class ColorPanel adds a JLabel, a JTextField and a JButton to a JPanel and shows a JColorChooser dialog when the JButton is pressed. Colors selected from the JColorChooser are set as the background color of the JTextField. The JTextField is not editable, it is only used to show the currently selected color as its background color.

ColorPanel implements interface FontComponent to set and return the component's value in form of an AttributeSet. In addition it defines a an EventListener and Event so that external components can be notified of changes in the ColorPanel. This mainly is meant to allow FontPanel to update the sample text display when a color is changed.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/topic77.htm0100644 0000000 0000000 00000017631 12114157751 025171 0ustar000000000 0000000

Extending classes for tag SPAN

SimplyHTML extends classes HTMLDocument, HTMLDocument.HTMLReader, HTMLEditorKit and HTMLWriter to support the SPAN tag of HTML. In this chapter the overall approach of how this is done is described. Please consult the source code and API documents of the mentioned classes for additional details.

Why extending the mentioned classes?

Class HTMLDocument has an inner class HTMLReader which is used by HTMLEditorKit to read HTML files. HTMLReader does not support SPAN tags so it is extended by SHTMLReader accordingly. To use SHTMLReader in favor of HTMLReader, class HTMLDocument has to be extended too.

When a HTML document is edited in SimplyHTML, all attributes are stored as CSS attributes which is why it is also necessary to extend class HTMLWriter to write out those CSS styles inside SPAN tags. To use both our own reader and our own writer, HTMLEditorKit needs to be extended as well.

SHTMLWriter

Usually a class is extended simply by overriding one or more of its public methods. Unfortunately class HTMLWriter only has one public method write which calls other private methods making up the actual write process.

If not a completely new writer is to be created, the only way to extend HTMLWriter is to copy its source code completely into a new subclass SHTMLWriter and change some of the code to our needs (please do let me know if you can provide a more elegant way to do this...).

To allow to write SPAN tags for style attributes, new class SHTMLWriter changes method convertToHTML32. Whenever a CONTENT tag is encountered during write, any CSS attributes are converted to a syntax used in STYLE attributes. The resulting style string then is added to a new SPAN tag and the SPAN tag including the found styles is added to the CONTENT tag.

SHTMLDocument

Whenever data is read into an instance of SHTMLDocument, method getReader returns an instance of the reader special to this type of document, so this method is overridden to provide an instance of SHTMLReader (see below).

SHTMLDocument.SHTMLReader

Class HTMLDocument contains an inner class HTMLReader to read HTML data into an instance of HTMLDocument. To support SPAN tags SHTMLDocument creates a new inner class SHTMLReader.

SHTMLReader overrides methods handleStartTag, handleSimpleTag and handleEndTag which are called by the parser for every tag found. SPAN tags are delivered to the reader through method handleSimpleTag. SHTMLReader deviates this tag to be handled by handleStartTag instead.

In method handleStartTag, for any SPAN tag found an instance of SHTMLCharacterAction is invoked. SHTMLCharacterAction is an inner class of SHTMLReader and extends class CharacterAction of class HTMLReader. It does the actual handling of the SPAN tag by removing the SPAN tag and by adding its style attributes to the CONTENT tag the SPAN tag belongs to.

Method handleEndTag properly terminates SHTMLCharacterActions for any SPAN tag in process.

SHTMLEditorKit

Class HTMLEditorKit provides methods to read and write data stored inside an instance of HTMLDocument. It uses method getReader of HTMLDocument in its read an write methods. To support our own set of classes as described above, class SHTMLEditorKit overrides methods read and write accordingly.

SHTMLEditorKit also ensures that a SHTMLDocument is created instead of a HTMLDocument by overriding method createDefaultDocument.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/topic78.htm0100644 0000000 0000000 00000010767 12114157751 025175 0ustar000000000 0000000

Manipulating fonts and font styles

Documents are modeled by Elements which are hierarchically linked according to the content structure in the document. HTML documents for instance define - with certain exceptions - an Element object for every HTML tag. As with HTML tags, each Element can have one or more attributes (bold, italic, etc.) which are assigned to an Element through class AttributeSet .

Applicable ranges for AttributeSets

How AttributeSets influence the rendering of a document depends on the context they are found in. Attributes for a paragraph for instance can be valid for a whole range of subseuqent Elements. In addition, a HTML document has an associated style sheet which defines AttributeSets too. If a paragraph element or an element for a range of characters has no attributes in the document itself, attributes from the style sheet might still be relevant for rendering respective content.

Limitation to attributes on character level

In stage 3 of application SimplyHTML only manipulation of fonts and font styles on character level is implemented. There are two methods in SimplyHTML for dealing with attribute changes, one for reading attributes for a given position in a document and one for applying attributes to a given part of a document.

Method getMaxAttributes

All methods in stage 3 of SimplyHTML dealing with fonts and font styles use static method getMaxAttributes of class FrmMain to determine which attributes are assigned to a certain point inisde a document. Method getMaxAttributes combines attributes from the style sheet associated to the document with attributes assigend directly to to a character element inside the document (in later stages this has to be refined to deal with style sheet styles other than <p>, paragraph styles, etc.).

The attribute sets from the style sheet and from the character element are added to a new attribute set which is returned to the calling method.

Method applyAttributes

As described in the follwing chapters all components manipulating fonts and font styles do their changes entirely on the basis of AttributeSets. An AttributeSet is applied to a document by method applyAttributes in class FrmMain.

Method applyAttributes determines whether or not a range of characters is selected in the given editor pane. If a selection is present, the given AttributeSet is applied to that range of text. If no selection is present, the given AttributeSet is applied as attributes for subsequent inputs.

To define attributes for subsequent inputs, class EditorKit defines a method getInputAttributes. When attributes are stored in the AttributeSet returned by this method, these attributes are applied for inputs thereafter.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/topic82.htm0100644 0000000 0000000 00000004060 12114157751 025155 0ustar000000000 0000000

Synchronizing tool bar and document

Font formatting controls in the tool bar not only allow to act on certain font settings in a document, they should also be used to reflect the settings at the current caret position. Class FrmMain implements interface CaretListener for doing this.

Method caretUpdate

Method caretUpdate in class FrmMain calls method updateFormatControls (see below) whenever the caret changes in the curently active document. updateFormatControls is called by FontAction too because this action also changes font attributes but the caret position does not change in this case.

caretUpdate is registered with every newly opened or created document through method registerDocument. Method unregisterDocument takes care of removing any listener when a document is closed.

Method updateFormatControls

updateFormatControls gets the attributes for the current caret position and calls method setValue of any FontComponents found in the format tool bar.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/topic79.htm0100644 0000000 0000000 00000010263 12114157751 025165 0ustar000000000 0000000

Creating a font formatting tool bar

As we have created functionality to manipulate font settings as described in the previous chapters, now it would be handy to have certain font formatting functions availabe in a tool bar as it is done in other text processors too.

Creating a font formatting tool bar for that purpose is easily done though a mechanism we already know from SimplyHTML's dynamic menu creation function. Method createToolBar uses the same technique by reading a tool bar definition string and turning it into a tool bar.

Method createToolBar

To create a tool bar a tool bar definition string from the resource file is read having the key for each element in the tool bar delimited by blanks (e.g. fontFamily fontBold fontItalic). The keys are in the order as elements shall appear in the tool bar.

Standard tool bar buttons

The typical case is to add a button on the tool bar for an action defined in the commands Hashtable of class FrmMain. Class JToolBar has a constructor returning a newly created button by passing an action to the constructor. The constructor will do all the connections between the tool bar button and the action automatically.

Combo box elements

Some of the elements in the tool bar however require special handling. FontFamilyPicker and FontSizePicker for instance are subclasses of JComboBox. In their case, createToolbar creates an instance of the component and uses method add of JToolBar.

Toggle buttons

FontComponents other than FontFamilyPicker and FontSizePicker are instances of ToggleFontAction. For ToggleFontActions we need a JToggleButton instead of a JButton in the tool bar and we have to make sure, the JToggleButton is property connected to its ToggleFontAction.

For each JToggleButton in the tool bar a ToggleActionChangedListener associated with the corresponding ToggleFontAction is created. ToggleActionChangedListener implements interface PropertyChangeListener and will always adjust the JToggleButton according to the action's current state. An ActionListener in turn is registered for the JToggleButton invoking the action when the button is pressed.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/topic83.htm0100644 0000000 0000000 00000002202 12114157751 025152 0ustar000000000 0000000

Adding a standard tool bar

Already having all functions of SimplyHTML as actions connected to the menu bar and having a method createToolBar with stage 3 of SimplyHTML as described previously, creating additional tool bars is done with almost no additional effort.

To create an additional tool bar for standard actions such as create a new document, open or save a document, for instance an additional tool bar definition in the resource bundle has been prepared.

The additional tool bar definition is read by calling createToolBar in method customizeFrame of class FrmMain. The new standard tool bar then is added to the panel on top of the main frame where our font formatting tool bar is located too.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74/topic76.htm0100644 0000000 0000000 00000005322 12114157751 025162 0ustar000000000 0000000

Customizing Java for CSS

Before we can look at how to build GUI and functionality for font manipulation, we need to understand the way, HTML documents are handled in Java a little more.

As shown previously, text and HTML are used in a model-view-controller environment consisting of EditorKit, Document and EditorPane. However, up to Java 2 Standard Edition version 1.4 this environment supports HTML 3.2 only. Especially it does not totally support CSS elements although attributes are stored partly in CSS format already.

SimplyHTML is based on CSS for dealing with styles and font settings belong to styles so consequently, font settings are implemented using CSS as well.

Design approach

In stage 3 of SimplyHTML font manipulation is enabled for a contiguous run of characters. HTML has tag SPAN to set font attributes for a contiguous run of characters using CSS . In addition there are tags FONT, B, I etc. allowign the same without using CSS.

In SimplyHTML universal usage of CSS has been chosen because almost any part of a HTML structure can be formatted with CSS regardless of its type.

Solution approach

To show and manipulate font information for documents with SimplyHTML using CSS on character level, support for the SPAN tag has to be built into the MVC environment for HTML documents in Java.

SimplyHTML does this by extending classes HTMLDocument, HTMLDocument.HTMLReader, HTMLEditorKit and HTMLWriter accordingly, as described in the next chapters.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic112.htm0100644 0000000 0000000 00000001316 12114157751 023737 0ustar000000000 0000000

Planned development stages

The following is an overview of planned stages to be completed in the future. The plan is subject to change but it shall document, what the author intends to add or change in SimplyHTML for upcoming releases:

  1. Link target window (clickable links)
  2. Extension/consolidation of persistent application settings (Prefs)
  3. Refine context sensitive help, popup menus, other GUI enhancements
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic168/0040755 0000000 0000000 00000000000 12114157751 023242 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic168/topic170.htm0100644 0000000 0000000 00000016621 12114157751 025325 0ustar000000000 0000000

Adding syntax highlighting

The previous chapter describes how a simple HTML code editor can be built. But with a plain text view structure and content of a HTML file is not visually separated. To improve legibility, syntax highlighting can be used: By displaying certain parts such as tags or attributes in a color or style different to the one used for content the reader can easily find certain parts of the document.

There are different approaches possible to implement syntax highlighting. For SimplyHTML regular expressions are used for their simple way of defining patterns in a single expression.

Class SyntaxPane

A new class SyntaxPane is created as a subclass of JEditorPane. In the constructor of SyntaxPane method setupPatterns is called, which defines the patterns for HTML tags, attributes and attribute content. Method setMarks (see below) is used to apply syntax highlighting to a given part of the document in the SyntaxPane.

The SyntaxPane registers itself as a CaretListener and uses method caretUpdate to keep the syntax highlighting up to date for any changed text. When a document is shown initially, setMarks is called for the entire content (making it a lengthier process for bigger documents to display the highlighting initially). During changes only the highlighting of the current line is updated so that typing text is not slowed down too much.

A tradeoff with above approach is that multiline formats such as multiline comments are not handled with it.

Method setupPatterns

Method setupPatterns uses regular expressions to define a pattern for each element to be shown different from normal content. A HTML tag for instance is enclosed in < and > and can have letters and numbers with or without a slash inside those markers. An attribute ends with =, etc. For each Pattern an AttributeSet is created having the style to apply for that particular Pattern.

In method setupPatterns a Vector is used to hold pairs of one Pattern and one AttributeSet wrapped into inner class RegExStyle.

Inner class RegExStyle

Inner class RegExStyle is used as a convenience class to bundle a Pattern with a set of attributes. It simply has two class fields for the Pattern and the AttributeSet and respective getters and setters. All defined RegExStyles are stored in Vector patterns of class SyntaxPane .

Method setMarks

Method setMarks is the public member of SyntaxPane which is used to apply syntax highlighting to a given portion of the current document. Method setMarks creates an instance of inner class StyleUpdater (see below) and calls invokeLater of class SwingUtilities to have styles updated without conflicts in the event dispatch thread.

Inner class StyleUpdater

Class StyleUpdater implements the Runnable interface by wrapping its functionality in a public method named run. Its main task is to apply styles associated with regular expression patterns to a given portion of the document which is currently edited.

This is done by iterating through Vector patterns of class SyntaxPane . For each Pattern found a Matcher is created. To all instances of the the Pattern found by the Matcher the style associated to the Pattern is applied.

Method caretUpdate

Method caretUpdate finds out the start and end position of the line the caret currently is in and calls method setMarks for this portion of text each time the caret position changes.

Recommended readings

'Regular Expressions and the JavaTM Programming Language' at

http://developer.java.sun.com/developer/technicalArticles/releases/1.4regex/

and

presentation slides 'Rich Clients for Web Services' from JavaOne 2002 at

http://servlet.java.sun.com/javaone/resources/content/sf2002/conf/sessions/pdfs/2274.pdf

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic168/topic171.htm0100644 0000000 0000000 00000016041 12114157751 025322 0ustar000000000 0000000

Integrating the new component

Class DocumentPane is used as the GUI representation of a document in application SimplyHTML. To let the user switch between layout view and HTML view in stage 10 class DocumentPane has some additional parts:

  • an editor pane to show and edit HTML code additional to the one used to show and edit the text and layout
  • a JTabbedPane to hold two editor panes and to switch between the two
  • a method to track the state of the new JTabbedPane and to react on state changes
  • methods that handle transfer of content between the two editor panes

Initializing the two views

The JTabbedPane is initialized in the constructor of DocumentPane and a reference is kept in new class field tpView. The JTabbedPane is added to the center area of the content pane of class DocumentPane.

Adding two editor panes

A new class field htmlEditor of class DocumentPane references the new SyntaxPane. The field is initialized in the constructor of class DocumentPane with a new instance of class SyntaxPane.

The SHTMLEditorPane in class field editor and the SyntaxPane in class field htmlEditor are added to the JTabbedPane in the constructor of class DocumentPane. Now the two resulting tabs in the DocumentPane can be used to toggle display between layout view and HTML view.

Tracking tab clicks

Class DocumentPane implements interface ChangeListener by adding new method stateChanged. Class DocumentPane is added to the JTabbedPane as a ChangeListener. Method stateChanged is called by the JTabbedPane whenever its state changes (another tab has been clicked, that is).

Method stateChanged of class DocumentPane checks if the state of the JTabbedPane of class DocumentPane has changed. Every time the state of the JTabbedPane chages, the view associated to the clicked tab is opened through methods setLayoutView and setHTMLView.

Method setLayoutView

In method setLayoutView the current content of the SyntaxPane is taken (the HTML code) and transferred over to the SHTMLEditorPane. Because method setLayoutView is used when the HTML display is hidden and the layout display is shown, it removes the instance of DocumentPane as a DocumentListener from the SyntaxPane and adds it to the SHTMLEditorPane so that changes are tracked by DocumentPane accordingly.

Method setHTMLView

Method setHTMLView works the same as setLayoutView in the way that it removes and adds class DocumentPane as a DocumentListener accordingly. It takes contents of SHTMLEditorPane and adds them to the SyntaxPane too.

To see the HTML code instead of the textual representation of the document however, method setHTMLView transforms the document content, before storing the resulting HTML code in the SyntaxPane. It uses the HTMLWriter or SHTMLWriter of the editor kit of class SHTMLEditorPane to generate HTML code for the particular document. This HTML code then is set to be the initial content of the SyntaxPane.

Finally it calls method setMarks to apply syntax highlighting initially.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic168/topic169.htm0100644 0000000 0000000 00000005266 12114157751 025340 0ustar000000000 0000000

HTML code editor: a simple approach

Although SimplyHTML is mainly meant for text processing, sometimes it is useful to do a change directly in the HTML representation of a document. For this purpose a component to display and edit HTML code is required. The editor shall not replace a powerful web page HTML editor, it only adds basic HTML manipulation functions.

To implement such an editor an ordinary JEditorPane is used. Setting the content type to "text/plain" and adding the HTML code of a given document as content for the JEditorPane leads to have a fully working editor.

Obtaining the HTML code for a given text document

To get the HTML code for a given document which can be shown in above mentioned editor pane, class HTMLWriter (or SHTMLWriter, depending on the user selection) is used. The writer creates HTML code for any given instance of class Document and its subclasses. By using method getEditorKit of an EditorPane the EditorKit for a displayed document is taken. Method write of class EditorKit uses HTMLWriter implicitly.

See new method setHTMLView of class DocumentPane about how this approach is used.

Simple but not enough

While the above would already be enough to edit HTML for any given text document it is comparably hard to work with HTML in a plain text display. In plain text the structural elements of HTML are not visually separated from content elements. Thus, the next chapter explains how syntax highlighting is added to our new simple HTML editor for improved legibility.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic168.htm0100644 0000000 0000000 00000002555 12114157751 023760 0ustar000000000 0000000

Stage 10: HTML code editor and syntax highlighting

SimplyHTML focuses on creation and manipulation of text documents. The fact that the documents are stored as HTML files along with cascading style sheets (CSS) was hidden from the user interface of SimplyHTML intentionally. The autor should not be forced to know or work with HTML code to write a text document.

On the other hand, for an experienced user being familiar with HTML it sometimes is quicker to manipulate a certain portion of HTML directly instead of having to wade through GUI elements. For this reason this stage of SimplyHTML implements a way to work on the HTML representation of any given text document.

The following parts are covered in this stage:

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic34.htm0100644 0000000 0000000 00000001462 12114157751 023664 0ustar000000000 0000000

Spotlights

While most functions of SimplyHTML are explained from the perspective of the structure in the source code by explaining certain methods or classes, for some functionality it makes sense to view it from a rather process oriented perspective.

In this section, such cases are explained in the process context, wrapping together several functions located at different places but belonging to a particular process. As well topics are reflected which do belong to certain part or class of SimplyHTML but rather should be explained in more general context.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/0040755 0000000 0000000 00000000000 12114157751 023227 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/topic119.htm0100644 0000000 0000000 00000005133 12114157751 025311 0ustar000000000 0000000

Example: Making a new plug-in

To create a new plug-in for SimplyHTML,

  1. copy file PluginTemplate.java into your plug-in project's source directoy
  2. rename PluginTemplate.java to a name you would like to give your new plug-in class
  3. change the paramteters of call super in PluginTemplate 's constructor according to names you choose (refer to class AbstractPlugin for a definition of these parameters)
  4. create one or more . properties files as needed for your new plug-in (you can use file PluginTemplate.properties a an example)
  5. adjust parameter "pluginTemplateLabel" in method getGUIName to an ID String referring to respective entry in your . properties file(s)
  6. add methods and fields to implement the functionality desired for your new plug-in
  7. compile the new plug-in's source file ( .java) to a .class file
  8. place .class file and .properties file(s) into a Java archive (JAR) file, package com.lightdev.app.shtm.plugin.installed
  9. place the JAR file into the directory SimplyHTML.jar is installed in

When starting SimplyHTML for the next time, your new plug-in should be listed in the 'Manage Plugins...' dialog.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/topic110.htm0100644 0000000 0000000 00000006041 12114157751 025277 0ustar000000000 0000000

Class PluginManager

Class PluginManager finds and loads plug-ins. Upon creation it calls its method loadPlugins which in turn calls method findPlugins to locate any plug-ins to be loaded and to create a class loader for them. Method findPlugins uses method readJar to get the class names from any Java archive (JAR) file found. readJar finds out the URLs for respective plug-in classes too.

Once all JAR files are searched, the found classes are loaded by method loadPlugins. Method plugins returns all loaded plug-ins as an Enumeration.

Restrictions for making plug-ins available to SimplyHTML

Plug-in classes and their accompanying classes are to be installed as Java archive (JAR) files. They are to be placed into package com.lightdev.app.shtm.plugin.installed. The plug-ins in this package need to be present in the directory where package com.lightdev.app.shtm is installed. This restriction makes it easier and faster to locate and load plug-ins.

If the actual plug-in class (the class implementing interface SHTMLPlugin, that is) needs additional classes as part of a plug-in package, the additional classes are best placed in sub-packages of package .plugin.installed . This makes it faster to load the actual plug-in classes too.

How plug-ins are found

PluginManager looks for JAR files in the directory where the class file of PluginManager is installed (the application directory of SimplyHTML, that is). PluginManager opens any JAR file found and goes through all content of the JAR file. Any class name found in package com.lightdev.app.shtm.plugin.installed. is stored along with its URL.

A class loader is created for the found URLs and all found classes are loaded.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/topic117.htm0100644 0000000 0000000 00000006126 12114157751 025312 0ustar000000000 0000000

Changing plug-in settings individually

In addition to its installation each plug-in can be configured individually per user. With class PluginManagerDialog all loaded plug-ins are displayed and can be configured. PluginManagerDialog is shown with the help of class ManagePluginsAction which is registered with respective menu item of SimplyHTML's plug-in menu.

ManagePluginDialog is the GUI representation of plug-in manipulation methods actually provided by class AbstractPlugin. It uses the methods each plug-in class has to provide through implementing interface SHTMLPlugin to display and change settings such as whether or not the plug-in is active or where it shall dock. Class AbstractPlugin provides an implementation of interface SHTMLPlugin which persistently stores the settings made in MangagePluginDialog automatically. To do so, an additional class Prefs is used, which is introduced in this stage 5 of SimplyHTML (see below).

Class Prefs

Class Prefs provides a simple approach to store user settings persistently. It maintains a Hastable of key/value pairs through a set of getter/setter methods. Whenever the Hashtable is changed, it is serialized to a file. Upon construction of class Prefs the serialized Hashtable is read from disk. If none is found a new and empty one is created. The preferences file created by class Prefs is named SimplyHTML.prf and is stored in the directory pointed to by expression System.getProperty("user.home") which usually references the home directory of the user currently logged in.

By using the home directory of the user preferences can be stored individually per user.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/topic114.htm0100644 0000000 0000000 00000006713 12114157751 025311 0ustar000000000 0000000

Making plug-ins available to SimplyHTML

Plug-ins need to reside in Java archive (JAR) files. Inside the JAR file a plug-in must be in package com.lightdev.app.shtm.plugin.installed. If a plug-in is accompanied by . properties files for internationalization or dynamic menu creation, the .properties files have to reside in package com.lighdev.app.shtm.plugin.installed too.

Should there be additional classes distributed along with the actual plug-in class (the class implementing interface SHTMLPlugin, that is), these additional classes should be placed into sub-packages such as com.lightdev.app.shtm.plugin.installed.mypluginaddons inside the JAR file.

Any JAR file containing a plug-in must be placed into the application directory of SimplyHTML. The application directory of SimplyHTML is

  1. the directory in the file system, where the JAR file of SimplyHTML is installed or
  2. the root package directory of file PluginManager.class, if SimplyHTML is not operated out of a JAR file

Removing plug-ins

To remove a plug-in from SimplyHTML, remove its JAR file from the application directory and restart SimplyHTML.

Examples for plug-in installation

In the following examples it is assumed that a plug-in is to be added to SimplyHTML from a file MyPlugIn.jar

Example 1

SimplyHTML is operated out of file SimplyHTML.jar and SimplyHTML.jar is in directory C.\Programs\SimplyHTML\

Installation: File MyPlugIn.jar must be placed into directory C:\Programs\SimplyHTML\

Example 2

SimplyHTML is operated as an uncompressed class file not residing in a JAR file, the SimplyHTML package is installed in C:\Programs\SimplyHTML\classes\, i.e. file PluginManager.class is installed in C:\Programs\SimplyHTML\classes\com\lightdev\app\shtm\plugin\

Installation: File MyPlugIn.jar must be placed into directory C:\Programs\SimplyHTML\classes\

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/topic107.htm0100644 0000000 0000000 00000017647 12114157751 025323 0ustar000000000 0000000

Parts of the plug-in architecture

To meet the previously stated requirements SimplyHTML provides the following new parts

Part

Description

SHTMLPlugin

The interface all plug-ins to SimplyHTML must implement

AbstractPlugin

A base class plug-ins can extend

PluginManager

Class to find and load plug-ins

FrmMain

Extended by an initialization method for plug-ins using the PluginManager

PluginTemplate

Class and properties files forming a basic plug-in for explanatory purposes and as copy template for plug-in creation

ManagePluginsAction

Action to show a PluginManagerDialog

PluginManagerDialog

Dialog for managing plug-ins (activate/deactivate, dock location, etc.)

While application SimplyHTML is distributed in package com.lightdev.app.shtm, the above parts are in package com.lightdev.app.shtm.plugin. Package plugin is also the root package for all plug-ins to be added to SimplyHTML.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/topic111.htm0100644 0000000 0000000 00000007155 12114157751 025307 0ustar000000000 0000000

Dynamic resources

In stage 2 of SimplyHTML a dynamic way of using resource bundles was implemented. It is capable of providing internationalization support and of dynamic creation of components such as menus and tool bars from parameters from a resource bundle.

This functionality needs to be made available to plug-ins as well which is why a new class DynamicResource now provides these features formerly contained in class FrmMain.

Class DynamicResource

Class DynamicResource provides methods for menu and tool bar creation based on parameters stored in ResourceBundles. As well, it stores and associates actions with components created in such way allowing for rerieval of component by their associated action name.

Class FrmMain has been changed to now use class DynamicResource for all internationalization and menu creation. It now makes publicly available a static instance of DynamicResource referencing components and actions of SimplyHTML. By having one static instance of DynamicResource in FrmMain, any object can use its utility methods without instanciating DynamicResources again.

All classes relying on FrmMain's former functionality have been changed accordingly. Please see the source code and SimplyHTML's main . properties file for examples of how parameters can be created for automatic component creation.

Using class DynamicResource for plug-ins

Class AbstractPlugin is an abstract base class plug-ins can use. By extending class AbstractPlugin a plug-in inherits some automatic initialization methods being performed upon construction. If a . properties file exists in the plug-in package (e.g. com.lightdev.app.shtm.plugin.installed.MyPlugIn.properties), class AbstractPlugin automatically creates a ResourceBundle for that . properties file. It then uses FrmMain's DynamicResource instance to create menus from the menu definitions found in that .properties file.

If a different approach of plug-in construction is desired, a plug-in class either can be declared not to extend class AbstractPlugin or can override some or all methods of class AbstractPlugin accordingly.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/topic109.htm0100644 0000000 0000000 00000001653 12114157751 025313 0ustar000000000 0000000

Requirements

A plug-in architecture for SimplyHTML has to meet the following requirements

  • allow to incorporate additional functionality without the necessity to change parts of SimplyHTML
  • provide a single interface for external objects to plug-in to SimplyHTML
  • allow to access functionality of external objects from within SimplyHTML without SimplyHTML 'knowing' about the particular functions
  • allow plug-ins to use SimplyHTML's functionality

Read on to find out how the plug-in implementation meets these requirements.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/topic108.htm0100644 0000000 0000000 00000004050 12114157751 025304 0ustar000000000 0000000

Incorporating plug-ins at runtime

To give access to functions of external objects, SimplyHTML adds a plug-in menu to its menu bar. For each plug-in one menu item is added to the plug-in menu. The menu item is to be provided by the plug-in and typically would contain one or more submenus with the functionality delivered by the plug-in.

In the same way SimplyHTML creates a new menu item in the help menu so that the plug-in can provide documentation about the way it is working.

If the plug-in delivers a component to SimplyHTML, it is displayed by SimplyHTML either as a new window or as a panel inside a section of SimplyHTML's main window, whatever is requested by the plug-in.

Plug-in initialization

Upon construction class FrmMain uses method initPlugins to add all plug-ins present to the application. Method initPlugins uses class PluginManager to locate and load plug-ins. In method initPlugins a new instance of class PluginManager is created. All plug-ins returned by method plugins of PluginManager are iterated and their parts (plug-in menu, help menu and component so far) are added to SimplyHTML as described above.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103/topic113.htm0100644 0000000 0000000 00000010267 12114157751 025307 0ustar000000000 0000000

Creating plug-ins for SimplyHTML

A plug-in can be any Java object implementing interface SHTMLPlugin. As specified by interface SHTMLPlugin, a plug-in should provide

  • a menu delivering access to all of the plug-ins functions (plug-in menu)
  • a menu providing documentation about the plug-in as required (help menu)
  • a component containing any GUI representation of the plug-in (plug-in component)
  • an indicator telling whether the plug-in component shall be 'docked' to SimplyHTML's main window (and where by default) or displayed in a separate window.
  • its name as it should be represented on a GUI
  • its internal name for possible internal referencing

Above parts need not to be present in all cases, returning null in one or more parts is sufficient as well. Apart from above specification, plug-in developers are free to design their plug-in in any way they like. As SimplyHTML is open source (and will remain as such), plug-ins can access all its public parts as described in SimplyHTML's API documentation.

Class AbstractPlugin

To further simplify plug-in creation, there is an abstract base class that can be used to build a new plug-in upon. It basically implements a generic way of creating menus from a resource bundle (if one is delivered with the plug-in). See chapter ' Dynamic resources' and the source code for class AbstractPlugin for details.

Plug-ins are not obliged to extend AbstractPlugin or they can override some or all of its classes to adjust the plug-in accordingly. When extending AbstractPlugin unchanged, a .properties file should be provided with the new plug-in having menu definitions, etc.

Class PluginTemplate

Another help for plug-in developers is class PluginTemplate. It constructs a working - though functionless - plug-in for SimplyHTML. Plug-in developers can use it as a copy template for own plug-ins or for explanatory purposes.

With class PluginTemplate two .properties files are provided too. They have the necessary menu definitions and texts for english and german language. Use these .properties files as an example for how to define appropriate .properties files for you plug-ins.

Defining actions for plug-ins

A plug-in typically adds functionality to SimplyHTML which can best be provided to SimplyHTML through respective action classes. To integrate action classes of plug-ins with SimplyHTML, they should be connected to plug-in menu items.

Actions from plug-ins are added to SimplyHTML by using FrmMain's DynamicResource instance. Call method addAction of class DynamicResource passing it an instance of a plug-in action along with its command name. The action command should has to be the same expression as it was used to identify the menu item in the ResourceBundle. See method initActions of class FrmMain for an example.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic128.htm0100644 0000000 0000000 00000002131 12114157751 023742 0ustar000000000 0000000

Stage 7: Images

This stage is enabling application SimplyHTML to add images to documents and to adjust the display of images in documents. It implements an image repository and dialog for all image manipulation inside SimplyHTML.

The functionality for image support in SimplyHTML is described in the following chapters:

Image references in HTML

General concept for image support

Implementing image storage

Creating a GUI to manipulate image references

Making the GUI available to the user

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/0040755 0000000 0000000 00000000000 12114157751 023067 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic12.htm0100644 0000000 0000000 00000002236 12114157751 025062 0ustar000000000 0000000

Creating a main window and menus

As described in the previous chapter ' Creating a basic application', App.class creates and displays the main window of application SimplyHTML. For doing so, it uses another class defining the actual elements and functionality of the main window, class FrmMain.java.

Class FrmMain mainly has two areas of functionality in stage 1:

  • it is the window in which all documents are opened and presented to the user
  • it makes available all functions of the application through a menu bar

This section explains class FrmMain and its functions.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic12/0040755 0000000 0000000 00000000000 12114157751 024350 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic12/topic19.htm0100644 0000000 0000000 00000005720 12114157751 026353 0ustar000000000 0000000

Adding a menu bar and menu items

As class FrmMain defines the main window of application SimplyHTML it should present the application's functions to the user. One way of doing this is to maintain a menu bar and menu items. In class FrmMain this is done mainly in method constructMenu which is called in FrmMain's constructor.

Fields referring to menus and menu items

Class FrmMain holds a field for each menu and menu item in its menu bar. These fields are visible to all methods within class FrmMain. By keeping fields referring to the menu structure it is possible to influence appearance and behaviour of the menu structure later on during the flow of the program.

To build a menu bar using the menu fields of class FrmMain, the fields have to be initialized first. This is done at the same time the fields are declared upon construction of a FrmMain object.

Initialization is done by creating a new instance of JMenu and JMenuItem respectively on the line of code where that field is declared. Each menu item gets associated with an instance of the action class which is meant for handling selctions of the particular menu item.

Method constructMenu

The previously initialized menu fields of class FrmMain are put together on a menu bar by method constructMenu. The method first creates a new JMenuBar object and then adds all previously initialized menus and menu items to it.

It then adds a new MenuListener to the file menu object which takes care of adjusting appearance of the file menu whenever it is selected. Finally the new menu bar is associated with the main window by calling method setJMenuBar, which was inherited from JFrame.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic12/topic14.htm0100644 0000000 0000000 00000003606 12114157751 026347 0ustar000000000 0000000

Handling window close events

Method processWindowEvent of class FrmMain watches window closing events for application SimplyHTML. All other events are not handled and routed on to FrmMain's superclass for possible handling.

Method processWindowEvent

For every WINDOW_CLOSING event, method processWindowEvents creates an instance of SHTMLFileExitAction and calls its actionPerformed method, which actually processes the closing.

For handling window closing events however, it is important that processWindowEvent checks whether or not documents are left open upon return from SHTMLFileExitAction. If, yes, this indicates one or more documents could not be closed resulting in refusing to close FrmMain thus keeping application SimplyHTML from terminating.

Especially see ' Avoiding loss of data in the close process' partly dealing with this topic too.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic12/topic17.htm0100644 0000000 0000000 00000004126 12114157751 026350 0ustar000000000 0000000

The class FrmMain

A window typically is represented by class JFrame of package javax.swing. To create the specific main window of application SimplyHTML, FrmMain extends JFrame by adding certain fields:

  • jtpDocs - a JTabbedPane for displaying one or more documents
  • APP_NAME - a string constant for representing the application's human readable name
  • Several fields for menus and menu items

The fields are declared private to class FrmMain so that they can not be seen or manipulated outside class FrmMain.

In addition, class FrmMain adds several methods with functionality for

  • constructing the window
  • ensure window closings do not cause loss of data
  • adjusting the windows appearance
  • adding a menu bar and menu items

Above functions are described in more detail in the following chapters.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic12/topic13.htm0100644 0000000 0000000 00000006357 12114157751 026354 0ustar000000000 0000000

Constructing the main window

In the constructor of class FrmMain mainly three steps are done

  • preparing the window to watch for events that would close this window
  • adjusting the window to requirements special to application SimplyHTML
  • adding a menu bar and menus

Once above steps are through as described below, application SimplyHTML's main window is shown. Once shown, the start process of the application has ended and control is transferred to the event dispatching thread which Java created for SimplyHTML automatically.

The event dispatching thread controls the program flow by invoking methods attached to certain events, which usually are fired by user actions.

Preparing for window close events

Closing an ordinary window might or might not need special handling. In the simplest case, respective window just closes which would be done automatically. For an application's main window however, closing usually terminates the application which in turn mostly requires certain cleanup before an application actually can be terminated.

A window can be closed through various actions such as the user selecting 'Exit' from menu 'File' or the user likes to switch off the computer etc. Most of such actions are signaled to a JFrame, if it claims to receive respective event notifications.

In class FrmMain, method enableEvents is called in the constructor of the class for this. Method enableEvents is inherited from class Component of package java.awt and is called, when a subclass of Component likes to receive or handle events of a certain type even without a respective event listener in place.

Class FrmMain calls for events defined in AWTEvent.WINDOW_EVENT_MASK and causes events of this type being delivered to method processWindowEvent of class FrmMain. This method handles window closing events for the main window of application SimplyHTML.

Especially see ' Avoiding loss of data in the close process' partly dealing with this topic too.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic12/topic18.htm0100644 0000000 0000000 00000004543 12114157751 026354 0ustar000000000 0000000

Customizing the main window

As mentioned in chapter ' The class FrmMain', this class is extending class JPanel by adding some extra fields and methods. To construct these extras as well as to set up the way the window initially is displayed, method customizeFrame is used.

After setting the window title and size, method customizeFrame gets a reference to the windows content pane, and adds a newly created instance of BorderLayout to it. Finally it creates a new JTabbedPane and adds it to the center of the content pane. As the JTabbedPane is the only component of the application's main window, it covers all of its visible region except for the menu and title bar.

JTabbedPane jtpDocs

A reference to FrmMain's tabbed pane is kept in field jtpDocs as a central place for pointing to all documents open in the main window. Whenever a document is created or opened, it is added to jtpDocs for display.

Thus, jtpDocs is a good place to look for a certain document. If the currently active document shall be adressed for instance, jtpDocs.getSelectedIndex points to the currently selected tab in the JTabbedPane and jtpDocs.getComponentAt fetches the component contained in this tab, which in turn would be the currently active document.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/0040755 0000000 0000000 00000000000 12114157751 024354 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic32.htm0100644 0000000 0000000 00000006472 12114157751 026357 0ustar000000000 0000000

Saving documents

To persistently store a newly created document or to save changes to an existing one, application SimplyHTML uses an object of class HTMLWriter which package javax.swing holds especially for HTMLDocuments. Again, to simply call HTMLWriter's write method is not enough to meet saving requirements of SimplyHTML. Class DocumentPane uses methods saveDocument and saveDefaultStyleSheet to put together its own save routine.

Method saveDocument

In method saveDocument class DocumentPane uses its field sourceUrl to tell HTMLWriter where to save a document. Field sourceUrl usually has been set previously either by method loadDocument or saveDocumentAs. If no soureUrl is set for any reason, saveDocument does nothing.

When field sourceUrl is properly set, saveDocument creates a FileOutputStream for respective URL, attaches it to a new OutputStreamWriter and creates an instance of HTMLWriter with these parameters. HTMLWriter completely takes care of transforming the model of object HTMLDocument to an HTML file when its method write is called.

Once the HTML file is saved, its associated style sheet is saved by calling method saveStyleSheet. Finally field textChanged is set to false to indicate that the document of this DocumentPane does not contain changes which need to be saved.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic42.htm0100644 0000000 0000000 00000004176 12114157751 026357 0ustar000000000 0000000

Style sheets and HTML documents

Before we start looking into how SimplyHTML implements the use of Documents, we should spend some time on reviewing how HTML documents and Cascading Style Sheets (CSS) are related.

What are CSS styles

Styles in CSS syntax are an extension to 'plain' HTML that allow to define attributes describing how HTML content should look. With styles one can define that paragraphs should always have a 6pt margin at the top for instance or that headlines always should be shown in red letters of 18pt size using font Helvetica.

How styles can be used

HTML allows to combine content and styles all in one HTML file, for example by means of adding style attributes to HTML tags. Another way is to store a <style> tag in the <head> tag of the document.

In addition it is possible to store styles in files separated from an HTML file. Storing styles in Cascading Style Sheets (CSS) has the advantage that a set of common styles can be defined at a central location serving as the basis for an arbitrary number of HTML files.

How SimplyHTML uses styles

In SimplyHTML a combination of styles defined separately in style sheets and styles defined as attributes within HTML tags is implemented. Whenever possible SimplyHTML avoids HTML tags such as <font> in favour of styles.

See 'Style handling design in SimplyHTML' for additional details.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic48.htm0100644 0000000 0000000 00000015621 12114157751 026362 0ustar000000000 0000000

The class CSSWriter

As mentioned previously, Java does not provide a class to save information in a StyleSheet to a file. To be able to save CSS information, SimplyHTML defines and uses class CSSWriter. CSSWriter is passed a Writer object and a StyleSheet object upon construction. The StyleSheet object contains the CSS information to be saved and the Writer object identifies the destination to store the CSS information at.

In the constructor of CSSWriter, the two parameters Writer and StyleSheet are stored in respective fields w and s of class CSSWriter for later use. To actually write out the styles found in the StyleSheet s, method write is used.

Method write

In method write an Enumeration of all rules in the StyleSheet is created. An Enumeration is a good way to iterate over a collection of elements. It provides two convenience methods hasMoreElements and nextElement for this purpose. While there are more Elements in the Enumeration of styles, method write gets the next element in the Enumeration being the name of the next Style object.

It then gets the Style object identified by that name. Style objects are AttributeSet objects that can contain attribute objects or other AttributeSet objects. The length of the style name is taken to find out how far from the left side the style attributes have to be written to file. This indentation length is stored in field indentLen of class CSSWriter.

First the style name is written to file followed by the character opening a collection of attributes for a CSS style ( '{' ). Every style except the one identified by StyleContext.DEFAULT_STYLE then is written to file by calling method writeStyle on respective Style object.

Method writeStyle

In method writeStyle again an Enumeration is used to go through all attributes found in the style AttributeSet. An AttributeSet is a pair of objects for the key and the value of a style attribute. For every element in the Enumeration the key and value objects are read.

Attribute StyleConstants.NameAttribute can be left out from writing because it contains the name of the style, this Style object is describing. As well the attribute StyleConstants.ResolveAttribute is not written to file itself, because it contains another AttributeSet in its value object. For attributes of type StyleConstants.ResolveAttribute method writeStyle is called recursively to write out all attributes found in this attribute.

All other attributes are written to file by first writing the key object having the name of the attribute (such as font-size, left-margin, etc.) followed by a colon, the attribute value (such as '12pt' or '20%', etc.) and a semicolon. Every attribute except the first is preceded by a new line and the indentation needed to make the collection of attributes more legible.

Finally the closing character for a CSS style ( '}' ) and a new line is written.

Using the appropriate line separator

At design time it can not be predicted on which operating system application SimplyHTML might be used. As different operating systems use different line separators with their file system, this has to be taken into account for our save routine. CSSWriter gets the correct line separator from a global constant defined in Java with command System.getProperty("line.separator"). The value returned by this call is appended to every line written to the target style sheet file.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic31.htm0100644 0000000 0000000 00000006401 12114157751 026346 0ustar000000000 0000000

Loading documents from file

When a document is loaded into a DocumentPane, the DocumentPane and its components need to be initialized properly. This is done in method loadDocument.

Method loadDocument

In method loadDocument , first the editor kit object is taken from the editor pane of this DocumentPane object. With method createDefaultDocument of the editor kit a properly initialized empty document object is created with the appropriate style sheet attached. The document base is set from the URL the document is to be loaded from. The document base is necessary so that all relative URLs probably contained in the document are correctly resolved.

The contents of the HTML file are then loaded into the new document object by opening an InputStream from the URL and using method read of the editor kit. The DocumentPane registers itself as DocumentListener with the new document, causing the document to notify its DocumentPane about all changes.

Finally the new HTMLDocument is assigned to the editor pane and the URL, the document was loaded from is stored in field sourceUrl of the DocumentPane.

How the style sheet is associated

Application SimplyHTML assumes that every HTML document is associated with a style sheet in a separate CSS file. This style sheet must be referenced by a link in the <head> tag of the HTML document as it is generated by method insertStyleRef.

If such a reference link is contained in the <head> tag of an HTMLDocument and the style sheet file can be found at the referenced location, the read method of the editor kit handles the style sheet reference correctly and the editor pane renders the HTML document with the associated styles.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic41.htm0100644 0000000 0000000 00000010467 12114157751 026356 0ustar000000000 0000000

Constructing a DocumentPane

Simply creating a new instance of an editor pane and throwing it onto a JPanel is not enough to display and use a JEditorPane. Due to the relationship of Document, EditorKit, JEditorPane and StyleSheet it is necessary to initialize all elements properly.

In DocumentPane's basic constructor DocumentPane() a new J EditorPane is created and assigned to field editor. The caret color for the editor pane is set and the typical text cursor is assigned by calling method setEditCursor (see below).

Then a new SHTMLEditorKit is created and assigned to our editor pane. This procedure ensures that a document gets correctly initialized with the style sheet properly attached.

Finally a new JScrollPane is created, the editor pane is added to the scroll pane and both are added to the DocumentPane after defining an appropriate layout.

The editor pane has to reside inside a JScrollPane for a vertical scroll bar automatically being shown as needed. The editor pane automatically wraps words at the right end of the pane so that a horizontal scroll bar is not shown or needed.

Method setEditCursor

Although JEditorPane has a method setCursor inherited from java.awt.Component, setting the cursor with that method does not cause respective cursor to be displayed (probably someone can let me know why sometime). Method setEditCursor therefore adds a MenuListener to our editor, that reacts on mouseEntered and mouseExited events.

When the mouse enters the editor pane, the cursor is set to the text cursor, when the mouse exits the editor pane the cursor is reset to a default cursor.

Method setEditCursor achieves this by getting the glassPane of the DocumentPane's JRootPane, assigning respective cursor and setting the glassPane to visible.

Constructing a DocumentPane with above steps sets up the basic contents of a DocumentPane. Read on to learn how to create a new document for editing or to open an existing one.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic46.htm0100644 0000000 0000000 00000005403 12114157751 026355 0ustar000000000 0000000

Style handling design in SimplyHTML

Java implements class StyleSheet to define central styles in CSS notation for an HTMLDocument. In addition styles in CSS syntax can be included directly in HTMLDocuments by storing them as an attribute to a HTML tag.

In all Java versions up to J2SE 1.4 there is no way however, to store styles persistently although working with HTMLDocuments in Java would hardly work without styles. Applications creating HTMLDocuments have to find a way to save styles on their own. SimplyHTML defines and uses an own class CSSWriter for this matter.

When are styles saved?

In stage 1 of application SimplyHTML styles can not be changed, so a style sheet is only saved, when a newly created document is saved (i.e. the document was not loaded from a file and a style sheet with the same name does not exist at the target location for the document).

This leaves one conflict open:

  • a style sheet exists at the location where a new document is to be saved and
  • the exisiting style sheet has the same name as the style sheet associated with the new document to be saved and
  • the exisiting style sheet is different from the style sheet associated with the new document

In this case, the style sheet is not saved and the existing style sheet is used. A solution for this case is not implemented in SimplyHTML yet. A workaround for the time being is to save newly created documents in a directory which holds only documents sharing the default style sheet of SimplyHTML.

In later stages of SimplyHTML it will be possible to change styles. Then the association of style sheets and documents as well as the handling of saving style sheets will be refined.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic38.htm0100644 0000000 0000000 00000011276 12114157751 026363 0ustar000000000 0000000

The class DocumentPane

DocumentPane implements an application centric approach of a document, wrapping the classes of javax.swing together with application and GUI functionality into one class.

DocumentPane follows the idea of having a single interface between an application and its documents. It serves as the single point for displaying, editing, opening, saving etc. For this, it combines GUI elements and 'MVC' elements to form one consistent and compact class to work with in applications.

Elements of DocumentPane

The major element of DocumentPane is a JEditorPane. DocumentPane extends JPanel and combines a JScrollPane and the J EditorPane as the only visible components.

In addition it has fields for storing the name of the document, the source where it has been loaded from and a field indicating whether or not the document has changed. Finally constants for a default document name as well as two cursor definitions are contained as fields in the DocumentPane.

There are two fields reflecting save operations as well: saveInProgress is set to true while a save operation runs and saveSuccessful is set by a save operation before (false) and after a save (true, if and only if all went fine) to indicate any errors while saving.

Last but not least, DocumentPane implements interface DocumentListener to recognize changes.

Interface DocumentListener

In field textChanged class DocumentPane 'remembers' if there have been changes to it's document since its creation or since the last save to file operation. But the DocumentPane needs to be notified about changes for being able to 'remember' them. It implements interface DocumentListener for that purpose.

Interface DocumentListener defines what a class would have to do to listen to DocumentEvents . DocumentEvents are fired to registered listeners whenever a document is changed in any way for instance.

Class DocumentPane defines methods insertUpdate, changedUpdate and removeUpdate to implement the DocumentListener interface. By doing that, class DocumentPane is a DocumentListener and can register itself with any document to listen for changes.

DocumentPane sets field textChanged to true whenever it is notified of a DocumentEvent.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic29.htm0100644 0000000 0000000 00000014714 12114157751 026363 0ustar000000000 0000000

Creating new documents

To create a new document basically two steps are necessary

  1. create a new DocumentPane for viewing and editing a new document
  2. create a new HTMLDocument, initialize it properly and attach it to the editor pane of the DocumentPane

With method DocumentPane(URL url) class DocumentPane has a constructor especially for that purpose. When a DocumentPane is created by calling this constructor with null as the url parameter, the DocumentPane is told to create a new HTMLDocument after creating the basic DocumentPane. The constructor calls this() to create a basic DocumentPane and then it calls method createNewDocument for creating the new document.

Method createNewDocument

As with creating a new editor pane, it is not enough to simply create a new HTMLDocument object and send it to the editor pane. We need additional steps inorder to adjust the HTMLDocument to the needs of application SimplyHTML which is why it makes sense to put these steps into an own method.

createNewDocument first gets the SHTMLEditorKit which was attached to the editor pane upon construction of the DocumentPane. A new HTMLDocument is created by calling method createDefaultDocument of the editor kit. createDefaultDocument ensures that we get a new HTMLDocument properly initialized and with our default style sheet attached to it.

The HTMLDocument then gets inserted a link reference to the style sheet file. Because we create a new HTMLDocument , this link refers to the name of SimplyHTML's default style sheet. The link reference is necessary because once we save the HTMLDocument, it can be used with other applications too. For an application other than SimplyHTML, the only way to determine which style sheet to use when the HTMLDocument is loaded is this particular style sheet reference (see 'Style sheets and HTML documents'). To insert the style sheet reference to the document, method insertStyleRef is used (see below).

The DocumentPane is registered as DocumentListener with the new document, causing the document to notify its DocumentPane about all changes. Finally the new HTMLDocument is assigned to the editor pane.

Method insertStyleRef

Method insertStyleRef inserts a reference link to the style sheet file to be associated with the HTML document having this reference link. The reference link has a syntax such as <link rel=stylesheet type="text/css" href="style.css"> and is to be placed in the <head> tag of a HTML document (see ' Style sheets and HTML documents').

To insert the reference, method insertStyleRef 'walks' through the element structure of a HTMLDocument and looks for its <head> and <body> tags. If a <head> tag is found, it is assumed that it already has the appropriate reference and the method does nothing.

Otherwise, it uses method insertBeforeStart of class HTMLDocument to insert a new <head> tag before the start of the <body> tag. The <head> tag is inserted along with the reference link to the style sheet by passing a respective HTML string to method insertBeforeStart.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic45.htm0100644 0000000 0000000 00000006366 12114157751 026365 0ustar000000000 0000000

The class SHTMLEditorKit

Class HTMLEditorKit in package javax.swing.text.html automatically associates a style sheet with any newly created HTMLDocument. The style sheet used is taken from the Java Runtime Environment and holds default styles for all possible HTML tags.

To use a different set of styles, we can either load another style sheet afterwards and delegate it to the HTMLDocument or we have to override this behaviour at its original location.

Class SHTMLEditorKit overrides all methods in HTMLEditorKit dealing with the default style sheet and uses our own set of styles.

Method getStyleSheet

This method returns the style sheet found in field defaultStyles of SHTMLEditorKit. If this field points to a StyleSheet object, this StyleSheet is returned.

If defaultStyles is not initialized so far, a new StyleSheet object is created. Then the default style sheet, identified by constant DEFAULT_CSS , is located by calling method getResourceAsStream inherited from class Class. getResourceAsStream looks for the style sheet file in the class path and returns an InputStream for it if found.

A CSS file ' default.css' is distributed with the classes of application SimplyHTML so that it can be loaded as default in this way. A new BufferedReader is created which reads from a new InputStreamReader used on the InputStream. Method loadRules of class StyleSheet then reads all styles from the BufferedReader.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25/topic47.htm0100644 0000000 0000000 00000015104 12114157751 026355 0ustar000000000 0000000

Saving CSS style information

A style sheet is saved by application SimplyHTML with method saveStyleSheet in class DocumentPane. This method is called by method saveDocument whenever a document is saved. To read more about HTML and style sheets in general and about how SimplyHTML uses style sheets, see chapters 'Style sheets and HTML documents' and ' Style handling design in SimplyHTML'.

Method saveStyleSheet

Method saveStyleSheet first determines the file name of the style sheet associated with the document to be saved by calling mehtod getStyleSheetName (see below).

getStyleSheetName returns the file name as a URL string. A URL object is created with that string and method getFile is called on that URL object to transform the URL string to a file string. This file string is taken to create a new File object for the style sheet to be saved.

With the File object it is first ensured that the file does not already exist with the help of method File.exists. If it exists, the style sheet is not saved (see ' Style handling design in SimlpyHTML').

If the file does not exist, it is created by calling method createNewFile on the File object. An OutputStream object is opened on the newly created file and an OutputStreamWriter is created for that OutputStream.

If the document to be saved has been newly created, the StyleSheet object of SHTMLEditorKit is taken to be written to file. If the document was loaded from file, the style sheet of the document is taken instead (the second case will not happen in stage 1 of SimplyHTML).

A new CSSWriter object is created with that style sheet and the previously created Writer. The style sheet is written to file by calling CSSWriter's write method. Once done, OutputStreamWriter and OutputStream are closed.

Method getStyleSheetName

A style sheet is saved at the location pointed to by the style sheet link reference of its associated document. The style sheet link reference usually is a relative expression containing the file name of the style sheet and an optional path which usually is a relative path.

Method getStyleName returns the path and name of the style sheet by combining the document base (the path the document actually is stored at) and the (possibly relative) path and name of the style sheet reference.

First path and name of the style sheet as contained in the document's style sheet reference link is taken by calling method getStyleRef (see below). Then the document base is read with method getBase of class HTMLDocument.

If a style sheet reference link is not found in the document, the default style sheet name is taken from class SHTMLEditorKit. Finally a relative path possibly contained in the style sheet reference is resolved by method resolveRelativePath and the resulting name is returned.

Method getStyleRef

In method getStyleRef , the first occurrence of a <link> tag is searched in the element tree of the document with the help of method findElement. If a <link> tag is found, the value object of its href attribute is copied to the local string variable linkName.

If no <link> tag is found or it does not contain a href attribute, null is returned.

There is no implementation for the case that there is more than one link reference to style sheets.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic25.htm0100644 0000000 0000000 00000007072 12114157751 025071 0ustar000000000 0000000

Creating and storing documents

From the perspective of application SimplyHTML there is a distinction between documents as represented in package javax.swing and documents of application SimplyHTML. Package javax.swing provides a very powerful set of classes for working with documents, which relieves applications from having to completely implement their own editor, document model, etc.

How this set of classes is implemented in an application is left to the application developer however, allowing a maximum of flexibility while reducing effort and still retaining a common basis for the particular functionality.

Documents in Java

The package javax.swing implements a model-view-controller (MVC) approach to work with documents as shown below:

Document - the model for swing text components

EditorKit - the controller for text components

JTextComponent - the view component

The interface Document is a container for text that serves as the model for swing text components. The goal for this interface is to scale from very simple needs (a plain text textfield) to complex needs (an HTML or XML document, for example).

The abstract class EditorKit serves as the controller for text components establishing the set of things needed by a text component to be a reasonably functioning editor for some type of text content.

Abstract class JTextComponent finally is the view component in the MVC context serving as the base class for swing text components such as JEditorPane.

Documents in SimplyHTML

SimplyHTML defines an own class for dealing with the documents used in the application. Class DocumentPane is used to create new documents, load documents from file, save documents, view and edit documents and to define a SimplyHTML document in general.

With DocumentPane there is only one interface to deal with for both GUI and functionality when working with documents.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic20.htm0100644 0000000 0000000 00000007071 12114157751 025063 0ustar000000000 0000000

Connecting GUI and functionality

Functionality of an application - more or less the application kernel - usually is separated from the graphical user interface (GUI) in separate classes. In some cases it makes sense, to combine both in one class or to create inner classes within another class too.

Still there must be a connection between the two, application kernel and GUI, which can be built by using actions.

Actions are explained in general below. In the follwoing chapters some of the actions of FrmMain are explained in greater detail. Additional comments about the actions can be found in the source code.

Actions

Action classes are a design approach to make a central connection between GUI and functionality. With functions being coded as actions, respective functionality is kept in a central place and can be centrally combined with code dealing with availability and behaviour of the respective functions during certain states of the application.

Why actions make sense

Coding certain functionality as actions makes code easier to maintain, because code is stored at exactly one location while the resulting action and its appearance can be connected to several GUI elements such as a menu item and a tool bar button for instance.

What actions do

An action can be constructed with a certain name and icon. Components such as menu items or buttons automatically display an action's name and icon when it is associated with them. As well the action's state (enabled or not enabled) automatically is reflected in the component's display (dimmed or normally shown).

When selecting a component that has an action associated to it, this component automatically fires an action event that calls method actionPerformed of that action. An ActionEvent object is created automatically describing the event that led to calling the action.

In essence a big advantage of using actions aside of their easier maintenance is that no additional coding is required to support the mentioned interactions and dependencies.

How actions are designed in class FrmMain

In class FrmMain, the actions are designed as inner classes. With that the actions have access to FrmMain's fields and methods without FrmMain having to pass references to the actions explicitly. The actions defined so far have a lot to do with the documents shown in FrmMain so that access to jtpDocs is quite helpful.

In the following chapters we look into some of the actions of FrmMain in detail.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic20/0040755 0000000 0000000 00000000000 12114157751 024347 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic20/topic36.htm0100644 0000000 0000000 00000002355 12114157751 026352 0ustar000000000 0000000

SHTMLFileCloseAllAction

SHTMLFileCloseAllAction uses SHTMLFileCloseAction to close all currently open documents. It declares a field for an own private instance of SHTMLFileCloseAction. This field gets initialized it with an SHTMLFileCloseAction object upon construction of the SHTMLFileCloseAllAction object.

In it's actionPerformed method SHTMLFileCloseAllAction then simply loops through all open documents and calls method closeDocument of SHTMLFileCloseAction.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic20/topic37.htm0100644 0000000 0000000 00000002272 12114157751 026351 0ustar000000000 0000000

SHTMLFileExitAction

This action will terminate the application and takes care of saving possibly existing unsaved changes before. The application will only be terminated, if all possibly open documents could successfully and safely be closed.

To ensure a safe termination of SimplyHTML, SHTMLFileExitAction fires an SHTMLFileCloseAll action to safely close all possibly open documents. It then checks whether or not documents are left open. If yes, one or more documents could not be closed safely and the application will not be terminated.

To properly handle window close events, this action is used in method processWindowEvent of FrmMain too.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic20/topic27.htm0100644 0000000 0000000 00000013134 12114157751 026347 0ustar000000000 0000000

SHTMLFileCloseAction

All actions implement method actionPerformed which gets called automatically by components bound to that action. Usually all functionality to be performed by an action goes here.

SHTMLFileCloseAction adds some flexibility to that approach by having an additional method closeDocument. This method is called by the action's actionPerformed method on the currently active document so that whenever the action is fired, the currently active document is safely closed.

Method closeDocument

With method closeDocument an arbitrary document shown in the main window can be closed even if it is not the currently active one. The method is declared public so that it can be called and reused from other places simply by instantiating class SHTMLFileCloseAction.

The method's main task is to ensure that a document is only closed, when all of its contents are properly saved. It does this by

  1. check, whether or not the particular document needs to be saved (if it was newly created and/or contains unsaved changes, that is)
  2. if not, the document is closed
  3. if yes, the user is asked if the document shall be saved
  4. if the document shall be saved, it is saved by calling the appropriate action class (perform a 'save' or 'save as')
  5. if the save was successful or if the user chose not to save changes, the document is closed
  6. if the save was not successful or the user wanted to cancel, the document is not closed

In all cases, closing the document is done simply by removing the respective DocumentPane from FrmMain's jtpDocs.

Special case: save thread in progress

As outlined in ' Using threads for lengthy operations', saving a document among other functions is set forth in an own thread. Any close operation has to consider, that a save operation on a particular document could be in progress at the time the user selects to close a document. Method closeDocument takes this into account by calling Method scheduleClose (see below) in cases where it detects a save operation being in progress or where it caused a save operation itself while attempting to close a document.

Method scheduleClose

When a save operation is in progress on a document that is to be closed, SimplyHTML has to wait for the save operation to finish because a document may only be closed when it was saved successful. Whether or not the save operation was successful can only be determined, once it completely finishes, so in this case, the application has to wait with closing until then.

To block the application from other activities, method scheduleClose creates a Timer thread and schedules a TimerTask with the Timer . The TimerTask periodically checks whether or not the save operation of the particular document has finished with the help of field saveInProgress of class DocumentPane. If it has finished, the document is closed and the Timer and TimerTask are disposed. If there was an error during the save operation, the document remains open.

Design advantage

The advantage of this design is that closing a document safely is implemented only once. Still it can be reused either as action or as method from anywhere in the application as done in SHTMLFileCloseAllAction or SHTMLFileExitAction for instance.

Especially see 'Avoiding loss of data in the close process' partly dealing with this topic too.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic20/topic49.htm0100644 0000000 0000000 00000007421 12114157751 026355 0ustar000000000 0000000

Using threads for lengthy operations

Most of the operations we encountered so far are not considerably time consuming. Especially loading or saving documents however, can be a lengthy task depending on the amount of data to be processed. Without any special handling of these tasks, application SimplyHTML could block for the time a particular save or load process would take. Java provides a mechanism to overcome this potential problem with class Thread.

Threads

Usually all activities of an application are done within the event dispatching thread. All lines of code contained in a method called by the event dispatching thread are executed sequentially in the order they are coded. In Java however, this must not be the case always. By opening a new Thread object and starting the code placed in its run method, this piece of code is executed in parallel or at least asynchonous from the event dispatching thread.

How SimplyHTML uses threads

In SimplyHTML three operations are executed in separate threads so far: saving a document to a file, loading a document from a file and closing one or more documents. All operations are embedded in an inner class of the respectice Actions SHTMLFileSaveAction, SHTMLFileOpenAction, SHTMLFileSaveAsAction and SHTMLFileCloseAction.

The inner classes FileSaver, NewFileSaver, FileLoader are subclasses of class Thread and simply wrap the call to saveDocument or loadDocument respectively in the run method inherited from the Thread class. Once an action is fired, its actionPerformed method creates an instance of FileSaver, NewFileSaver or FileLoader and calls its start method.

In addition, method scheduleClose in SHTMLFileCloseAction creates a Timer thread for each close operation waiting for a save operation to complete.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic20/topic28.htm0100644 0000000 0000000 00000013501 12114157751 026346 0ustar000000000 0000000

Actions of FrmMain

The actions in FrmMain take use much of class DocumentPane. They call some of the functions for files and documents available in this class. Also other functions are wrapped into actions.

In this section it is explained, how to make existing functions available to the user. The functions of stage 1 themselves are explained in 'Creating and storing documents' and in ' Documenting the application'.

Actions for the file menu

In the file menu, the basic actions on documents and files are selectable. Create new documents, open documents from file, save changes to documents, save new documents or save exisiting documents under a new name and finally exiting the application can be done by the user through this menu.

Actions for the help menu

The help menu makes all kinds of documentation available to the user. With stage 1 of application SimplyHTML the help menu has links to the help file (this tutorial), the API documentation and an 'About this application' dialog.

Action list and short description

SHTMLFileNewAction - create a new document and show it

SHTMLFileOpenAction - open an existing document and show it

SHTMLFileCloseAction - close a currently open document and take care of saving the document before closing if necessary

SHTMLFileCloseAllAction - close all currently open documents and take care of saving the documents before closing if necessary

SHTMLFileExitAction - exit the application and take care of saving open documents before closing if necessary

SHTMLFileSaveAction - save a document

SHTMLFileSaveAsAction- save a document under a new name

SHTMLHelpAppInfoAction - shows SimplyHTML's about dialog

SHTMLHelpShowContentsAction - brings up online help

Dynamic interaction

There is a certain dependability between some of the actions which requires to aviod redundancy of code in the design therefore. Using actions bears an advantage for this because it allows to build a more complex process by combining actions individually.

Especially see 'Avoiding loss of data in the close process' in the Spotlights section partly dealing with this issue too.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic39.htm0100644 0000000 0000000 00000001514 12114157751 025071 0ustar000000000 0000000

Documenting the application

Designing and developing an application can be hard work already. But the resulting solution is nothing without proper documentation.

Documentation starts at providing information about the name of the application or license and copyright notices and goes all the way through installation guidelines to a user manual, tutorial and information for developers.

Most of the time, documenting an application is at least as much the work as developing it. This section shall give some hints about how a proper documentation can be accomplished.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic39/0040755 0000000 0000000 00000000000 12114157751 024361 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic39/topic43.htm0100644 0000000 0000000 00000011436 12114157751 026362 0ustar000000000 0000000

Adding online help

Once an application is started by the user it is most convenient to provide information and help directly out of that application. With JavaHelp technology the Java language has an extension for online help which any application can use to seamlessly incorporate documentation.

JavaHelp extension

JavaHelp extends the Java Runtime Environment in the way that it provides a specification and platform to display any kind of documentation. All Information presented in JavaHelp is stored in HTML files. Table of contents, index and map are XML files all wrapped together with the HTML topic files into a framework of classes to view and query the information through a common user interface.

Unfortunately the JavaHelp extension is not a part of the core Java Runtime Environment (JRE) so the extension has to be applied manually by coping the extension file for JavaHelp into the extensions directory of the JRE.

How to use JavaHelp in an application

Once a JavaHelp documentation is set up by creating HTML documents, table of contents, index and map files and the JavaHelp extension is available to the JRE, displaying JavaHelp documentation simply is done by creating a HelpSet object for the corresponding help set and display it with an instance of HelpBroker.

More about the JavaHelp specification and technology is available at

http://java.sun.com/products/javahelp

How help is created for SimplyHTML

All documentation is stored in a single JavaHelp help set in directory help of the SimplyHTML class path ( source/com/lightdev/app/shtm/help). The help set is the one you are currently reading. It was produced entirely with the Java application HelpExpert. HelpExpert is created by the author of SimplyHTML (hey, that's me again!) and is available at

http://www.calcom.de/eng/product/hlpex.htm

How help is implemented in SimplyHTML

A common practice is to deliver documentation through menu 'Help'. Consequently SimplyHTML has an item 'Help Topics' in the 'Help' menu linking to this help set. In stage 1 the link was performed by SHTMLHelpShowContentsAction. This has been refined to now being handled in method initJavaHelp of class FrmMain.

Method initJavaHelp

In method initJavaHelp an instance of a HelpBroker is created pointing to the JavaHelp version of this tutorial (included in the Java archive (JAR) file of SimplyHTML). A reference to this HelpBroker is kept for later use. Then class FrmMain's instance of class DynamicResource is used to get the menu item meant for displaying the application's online help topics overview.

Class CSH (for context sensitive help) of the JavaHelp API is used to create an ActionListener responsible to display context sensitive help on occurrence of a given action (selecting the 'Help contents' menu item in this case). This ActionListener is registered with the menu item.

Important: A 'readme' document should always be distributed with an application as plain text file. The readme file should contain essential information to properly set up and run the application. If all other tools fail, at least the user can be referred to this file to start with.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic39/topic40.htm0100644 0000000 0000000 00000010237 12114157751 026355 0ustar000000000 0000000

Creating an 'About dialog'

An elementary part of an application is its 'about' dialog. An 'about dialog' usually is shown when the user likes to know which application is currently running, by whom it was created, which version the user operates, which terms and conditions are connected to usage, etc.

It is common practice to have an item in the help menu of an application such as 'About SimplyHTML' for this purpose. This menu item in application SimplyHTML creates an instance of its AboutBox class.

Class AboutBox

Class AboutBox descends from class JDialog and hosts a number of information panels. In the upper left part, an image associated with the application is shown (see below). Next to the image on the right a number of JLabels have the application name, current release, the author (wow, that's me!) and the application's home page. The name of the application is taken from the constant APP_NAME of class FrmMain.

Below these components, a license panel shows the full text of the GNU General Public License SimplyHTML is distributed under. Finally a close button is displayed at the bottom of the dialog.

AboutBox does not have any other function than to construct itself and to be shown for information. Once the close button is pressed, it is disposed properly. This is ensured by overriding method processWindowEvent and calling enableEvents in the constructor of AboutBox.

Reading an image from the class path

A common way to display an image is to place a JLabel somewhere onto a visible component and associate an image to it. JLabel has a constructor especially for that, which accepts an ImageIcon object as a parameter. The image is taken from the class path of application SimplyHTML, where file ' App.jpg' is distributed together with the applciation's classes in subdirectory 'image '.

Method getResource in the Class object of respective JLabel is used to create an ImageIcon object for file ' App.jpg'. Using the command this.getClass.getResource("image/App.jpg") gets the class object of this JLabel object and finds out from where it was loaded. Calling getResource on this Class object resolves the name given as a parameter to getResource as a relative path to the path where the class was found.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic39/topic51.htm0100644 0000000 0000000 00000006627 12114157751 026367 0ustar000000000 0000000

The class LicensePane

LicensePane extens class JPanel by adding a JTextArea and a JScrollPane to it. The JTextArea is used to display the full text of the GNU General Public License. By wrapping this functionality into a separate class it is easier for other classes such as AboutBox to show that info text.

Constructing a LicensePane

The constructor takes a Dimension object to determine the preferred size of the panel to be constructed. In the constructor a new non-editable JTextArea is created with the license text to be displayed as a parameter. The license text is delivered by method getLicenseText (see below). A new JScrollPane is created and the JTextArea is associated with it. The vertical and horizontal scroll bar are set to be displayed as needed.

Finally the JScrollPane containing the JTextArea is added to the LicensePane and the license text is scrolled to the top with method setCaretPosition.

Method getLicenseText

The license text is taken from file ' gpl.txt' delivered in the class path of the distribution of SimplyHTML. Method getResourceAsStream is used to locate the file and to open an InputStream object to it. An InputStreamReader is created on that InputStream and the InputStreamReader is used to create a BufferedReader.

As long as lines are found in ' gpl.txt' they are read and appended to a StringBuffer. Finally the readers are closed properly and the contents of the StringBuffer are returned as a String .

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic39/topic44.htm0100644 0000000 0000000 00000002776 12114157751 026372 0ustar000000000 0000000

Creating source code documentation

Source codes usually are documented by comments inserted at positions in the code where the author thought an explanation of what is done there might be needed or otherwise helpful.

If such comments are created following a certain syntax, they can be translated to a set of API documentation files in HTML with the Javadoc tool of the Java Software Development Kit. For details about Javadoc please refer to page 'Javadoc 1.4 Tool' at http://java.sun.com/j2se/1.4/docs/tooldocs/javadoc/index.html

Generally it is a good idea and common practice to comment Java source codes following Javadoc syntax as almost automatically there will be a very transparent and thorough documentation of the sources openly viewable with nothing more than a browser.

With application SimplyHTML this has been done too, so there is a set of API documentation files in directory doc of the SimplyHTML distribution package. By opening file index.html the documentation can be viewed with any browser.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4/topic11.htm0100644 0000000 0000000 00000005350 12114157751 025061 0ustar000000000 0000000

Creating a basic application

For creating a basic application, in Java we need a class having a method called main which accepts possible parameters from the standard Java Runtime Environemt (JRE). This basically is done by the class described in App.java.

Besides providing the application's main method, App.class constructs an instance of the main frame of application SimplyHTML, an instance of the class defined by FrmMain.java, and initially displays it.

When application SimplyHTML is started, the Java Runtime Environment opens the main thread for this aplication and executes method main of class App. Once all steps such as constructing the application's main frame, control is transferred to the event dispatching thread.

Setting a look and feel

An important feature of Java is to support different system platforms making it necessary to design applications independent from any system specific behaviour. The author of a Java application can not predict, on which system the application actually will be used.

Java provides mechanisms to keep applications away from proprietary look and feels or behaviours with class UIManager of package javax.swing. App.class of SimplyHTML uses methods setLookAndFeel and getSystemLookAndFeelClassName of class UIManager to initially set the look and feel to the one of the system, the application is started on.

By setting the L&F at runtime in the application's main method, an application can be kept independent from system specific behaviour. Class UIManager still allows to change L&F settings later in a session with the application if necessary.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic152.htm0100644 0000000 0000000 00000003407 12114157751 023746 0ustar000000000 0000000

Stage 9: Links

In this stage creation and manipulation of links is implemented. While a link seems to be a rather trivial element in an HTML document, working with links without knowing how to to type in HTML code requires a quite complex GUI. This is because links can have many different forms with several exceptional handling of certain link types.

To allow for links referencing certain parts inside a given document, a way to edit so called link anchors is needed in addition.

Besides links and link anchors, stage 9 has some refined handling of paragraph tags and named styles from previous stage. And last but not least it compensates a somehow ugly effect of Java showing font sizes smaller than any web browser.

Read all about above topics in more detail in the following chapters

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic74.htm0100644 0000000 0000000 00000001602 12114157751 023664 0ustar000000000 0000000

Stage 3: Font manipulation and tool bars

Most text processors have one essential functionality in common. They allow to manipulate fonts in various ways.

Typically fonts can be set through a menu allowing to change all font related settings for a certain portion of text at once. In addition, there mostly is a tool bar to quickly change single font attributes such as size or style.

Stage 3 of SimplyHTML is about adding font manipulation features. It builds a font dialog to change a whole set of font related attributes and it creates tool bars to access most functions of SimplyHTML including new font formatting.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic173.htm0100644 0000000 0000000 00000005235 12114157751 023752 0ustar000000000 0000000 Stage 11: Find and replace

Stage 11: Find and replace

Java[tm] technology already offers generic functionality to find portions of text within a given string. This stage of SimplyHTML uses such functions to build a user interface suitable for most find and replace operations. In addition the new find and replace logic implements a way of replacing text over an arbitrary number of separate documents.

This chapter does not go into every detail of how to build respective user interface. It concentrates on aspects in conjunction with find and replace as shown in the following topics

Find and replace basics

Find and replace user interface

Find logic

Replace logic

Supporting find and replace over multiple documents

Using FindReplaceDialog in SimplyHTML

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic152/0040755 0000000 0000000 00000000000 12114157751 023233 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic152/topic153.htm0100644 0000000 0000000 00000011512 12114157751 025311 0ustar000000000 0000000

Links in HTML

A link in HMTL is a reference to another location a user can jump to. The common syntax for a HTML link is

<a href="URI">link text</a>

Link types

Link types are reflected by the type of uniform resource identifier (URI) of attribute href . The following formats for an URI are possible

  1. anchor in the same document (e.g. #anchorname)
  2. other document (e.g. myDoc.htm)
  3. other document in other directory (e.g. ../directory/myDoc.htm)
  4. anchor in other document (e.g. myDoc.htm#anchorname)
  5. anchor in other document in other directoy (e.g. ../directory/myDoc.htm#anchorname)
  6. WWW address (http://... )
  7. address to local document ( file://...)
  8. Gopher address ( gopher://...)
  9. FTP address (ftp://... )
  10. Telnet address ( telnet://...)
  11. Newsgroup address ( news:...)
  12. E-Mail address ( mailto:name@domain.xy)

Link types 1 to 5 above can be summarized as links with relative addresses (i.e. relative to the location the document containing the link is stored), all others work with absolute link addresses. Instead of a link text an image can be specified as well, for instance

<a href="home.htm">

<img src="anImageFile.jpg" width="160" height="34" border="0">

</a>

SimplyHTML models all types of links and supports links of type 1 to 7 above.

Link anchors

As shown in link types 1, 4 and 5 above, links not only refer to other files, they can point to specific locations inside a given file too. To enable a link to point to a certain location inside a file, so called link anchors have to be defined as target locations in respective target files. Link anchors are defined by inserting HTML code such as

<a name="anchorname">link anchor text</a>

Link anchors can be defined with or without an anchor text, however SimplyHTML only allows to create link anchors with text.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic152/topic158.htm0100644 0000000 0000000 00000007436 12114157751 025330 0ustar000000000 0000000

Using LinkDialog and AnchorDialog

The two dialogs making up the new link functionality in this stage are made available through new actions in FrmMain.

  • InsertLinkAction - action to create a new link
  • EditLinkAction - action to edit an exisiting link
  • EditAnchorsAction - action to create or remove anchor links

InsertLinkAction and EditLinkAction both create an instance of class LinkDialog to let the user create or work on a link. When the user has not cancelled upon return of the dialog, method setLink of class SHTMLEditorPane is called to apply the link settings from the LinkDialog object.

EditAnchorsAction is used similar to the above, the only difference is usage of method insertAnchor of class SHTMLEditorPane instead.

Usage of AnchorDialog through LinkDialog

Additional to using AnchorDialog directly on a document currently edited, the dialog can be used from out of class LinkDialog. The idea is that either

  1. an anchor link is applied to a document in one step and a link is created to reference this anchor link in a second step later or
  2. an anchor link is created in the course of creating a link both in one step.

To create a link to a newly created anchor link in one step, as in case 2. above, the anchor link needs to be created directly out of class LinkDialog. To do so, LinkDialog has a browse button next to the text field for typing in a link anchor name. When the browse button is pressed class LinkDialog creates an instance of AnchorDialog passing the URL currently entered as the link address.

AnchorDialog opens the document referenced by the URL received from LinkDialog and shows existing anchors of this document. The user now can choose an existing anchor link or create a new one which then is chosen.

Once a link anchor is selected, its name is taken back to the calling LinkDialog and the anchor name is included with the new link currently worked on in the LinkDialog object.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic152/topic155.htm0100644 0000000 0000000 00000013652 12114157751 025322 0ustar000000000 0000000

How to apply links

In Java links inside HTML documents are represented different to other HTML tags. As previously described a link usually is denoted by a tag such as

<a href="URI">link text</a>

But instead of being a branch element of type HTML.Tag.A inside a HTMLDocument, a link is represented as an AttributeSet for a given content element. To complicate things a little, images are represented as an AttributeSet for a content element too, so image links in turn are represented as two AttributeSets each nested inside the AttributeSet of a given content element.

Applying links

To apply a text or image link, class SHTMLEditorPane has two new methods both called setLink. The two methods only differ in the parameters they expect. One of the methods just wraps the other one into a more convenient call with fewer parameters for text links. Method setLink determines, whether or not the selection currently is inside a link with the help of method findLinkElementUp of class Util (see below).

Text links and image links

If inside a link, this link is replaced by the new link. If the selection is not inside a link, the new link just is created at the current caret position. Possibly selected text is replaced by the text of the new link in this case.

After it has determined, whether the selection is inside a link, method setLink splits into calls to methods setTextLink and setImageLink respectivley, whatever applies from the parameters received from the calling method. If no image file is passed ( linkImage is null ), a text link is assumed and vice versa ( linkText is null instead).

Method setTextLink

Method setTextLink takes the link reference and stores it as HTML.Attribute.HREF in a new AttributeSet. If a style name was passed, it is stored as HTML.Attribute.CLASS in the AttributeSet for the link. The new link then is applied to the selection depending on what is inside the selection.

If the selection contains a link, this link is replaced by the new link. Otherwise, the selected text is replaced by the new link text and the link attributes are applied to this new text.

Method setImageLink

Method setImageLink works similar to method setTextLink. The only difference is that it creates an additional AttributeSet for representing the image (file, width and height). This AttributeSet is applied instead of link text to the selection replacing any existing link or other text along with the new link attributes.

Method findLinkElementUp

To find a link element from the position of a given element upwards in the element strucutre of a document, the attribute sets of elements have to be inspected (not the element names). Method findLinkElementUp does this by iterating through parent elements of a given element and looking for an attribute with key HTML.Tag.A. If such an attribute is found, this attribute represents a nested AttributeSet . Method findLinkElementUp then looks for an attribute with key HTML.Attribute.HREF inside this nested AttributeSet. If HTML.Attribute.HREF is found, a content element with a link attached has been found and this element is returned.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic152/topic157.htm0100644 0000000 0000000 00000011270 12114157751 025316 0ustar000000000 0000000

Creating a GUI to define link anchors

Link anchors denote certain positions inside a document making it possible to link to that particular position directly. Link anchors are identified by a link anchor name which is internally stored in the document at the position it denotes. Link anchors typically are not visible in the document, they are present only in the HTML code making up such document.

to enable a user to create and manipulate link anchors requires some extra work because SimplyHTML hides HTML code with the intention to let an author concentrate on content rather than HTML coding.

Class AnchorDialog

With class AnchorDialog a GUI is built for making link anchors of a document visible and for creating or deleting link anchors. As several dialogs of application SimplyHTML have been explained in earlier stages already, we only look at some additional 'specialties' of class AnchorDialog here.

Document as object or file

AnchorDialog can be constructed either with a Document object or with a URL leading to a document. When constructed with a URL, AnchorDialog loads the Document found at that location. When link anchors are added to or removed from a document loaded from a URL, the changes are saved to the document before AnchorDialog is destroyed.

Method getAnchors

Class AnchorDialog displays a list of link anchors existing in the document associated to the dialog with the help of method getAnchors. Method getAnchors iterates through all elements of the document and looks for HTML.Tag.A in the attribute set of each element. If such attribute is found, getAnchors looks for HTML.Attribute.NAME. Any element having HTML.Attribute.NAME inside HTML.Tag.A is listed as a link anchor.

Whenever method getAnchors is called to find all anchors in a document, a Hashtable anchorTable is filled with the anchors found. Hashtable anchorTable references elements with anchor names. The Hashtable is used to fill the list of available anchors too.

Making anchors visible

As mentioned previously, anchors are only visible in the HTML code of a document. To make a link anchor visible a Highlighter is used in class AnchorDialog. A ListSelectionListener is implemented by class AnchorDialog and registered with the list of anchors. Whenever an item is selected in the list, the element the anchor name refers to is taken from the anchorTable Hashtable. The element is used to determine the anchor position in the document and the position is passed to the Highlighter to be shown.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic152/topic154.htm0100644 0000000 0000000 00000030526 12114157751 025320 0ustar000000000 0000000

New parts in this stage

As with previous stages several adjustments to existing classes as well as some new classes are needed to build the new link functionality

Class

Purpose, Changes

LinkDialog

Dialog to create and edit links

AnchorDialog

Dialog to create and edit anchor links

FrmMain

new actions for link creation and formatting

SHTMLEditorPane

new methods to apply and change links and anchor links

Util

new methods to build relative paths and to locate link elements

Addtional to working with links, stage 9 has some refined features for working with paragraph tags and named styles as well as for rendering HTML:

Class

Purpose, Changes

SHTMLEditorKit

support for additional views

SHTMLInlineView

new view compensating font size differences between Java and web browsers

SHTMLParagraphView

new view compensating font size differences between Java and web browsers

TagSelector

new component to select paragraph tag types from the tool bar

ParaStyleDialog

additional tag type selector to set named styles for tags other than paragraph

SHTMLEditorPane

new method to apply tag types to paragraph tags

Classes SHTMLTableView and SHTMLBlockView have been changed and moved to package com.lightdev.app.shtm. Class LengthValue has been abandoned and removed from the project.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic152/topic156.htm0100644 0000000 0000000 00000010322 12114157751 025312 0ustar000000000 0000000

Creating a GUI to define links

With methods to apply links as explained in previous chapter, a new dialog is required to allow for convenient link creation and manipulation. New class LinkDialog is the central place to allow for link entries of all kind.

Class LinkDialog

Class LinkDialog has a collection of components for all relevant link attributes. It can be used to set a new link or to view and manipulate an exisiting link.

As several dialogs of application SimplyHTML have been explained in earlier stages already, we only look at some additional 'specialties' of class LinkDialog here. LinkDialog establishes a central ActionListener to handle changes to any of its components. If the link type combo box changes for instance, the buttons to select a local file or link anchor are enabled or disabled accordingly.

Switching of relative and absolute paths

If the link type is set to 'relative' or to 'local', the link address is switched between absolute ( file:/C:/Data/aFile.htm) or relative ( ../../aDir/aFile.htm) notation with the help of methods resolveRelativePath and getRelativePath of class Util.

Selection of local files and link anchors

A link type of 'local ' allows to use a JFileChooser dialog to browse for a local file. Link types ' local' and 'relative ' as well allow to define a link anchor with respective browse button.

Link address and link anchor can be typed directly into respective text fields too.

Image selection

By selecting 'show link as image', the dialog switches to display an image panel with a browse button to set an image from the repository. In the LinkDialog any selected image is only shown with the width and height selected in the ImageDialog. These settings can only be changed by using the ImageDialog through respective browse button.

Returning link settings

Once all link attributes are set with class LinkDialog, methods getLinkText, getHref, getStyleName, getLinkImage and getLinkImageSize can be used to find out the user settings.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic120.htm0100644 0000000 0000000 00000002713 12114157751 023740 0ustar000000000 0000000

Stage 6: Lists

The Java language already includes some standard actions to implement list formatting with documents in a JEditorPane. These actions however only provide a very basic approach to working with lists. On top of just starting a new empty list and to enter text into this new list, for application SimplyHTML the requirement is to offer a simple way of switching list formatting on or off for an arbitrary portion of existing text paragraphs. As well there should be a way to change formatting of an existing list partly or completely through a separate dialog.

This stage implements list handling as described above covering the following topics

Lists in HTML documents

Implementing list formatting in SimplyHTML

Switching list formatting on or off

Creating a list format dialog

Adding actions and GUI elements

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic120/0040755 0000000 0000000 00000000000 12114157751 023226 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic120/topic124.htm0100644 0000000 0000000 00000006751 12114157751 025313 0ustar000000000 0000000

Creating a list format dialog

Switching lists on and off as described in the previous chapter formats lists in their default formatting as defined in the style sheet of respective document (i.e. applies tags <ul> and <ol> without additional attributes). To adjust list formatting, additional functionality is needed.

To change list formatting, a dialog is created acting on both list attributes and list elements.

When changing an existing ordered list from number to letter ordering for instance, attributes of the list are to be changed. When switching from an existing list ordered by numbers to an unordered bullet list with square bullet symbol, the list element itself and its attributes are to be changed in one step.

Re-use of existing parts

List formatting functionality in part is similar to what has been implemented for table formatting already. Consequently, some of the existing functionality of SimplyHTML can be re-used: Classes DialogShell, AttributeComboBox and BoundariesPanel which share common classes to work with attributes and attribute sets themselves. In stage 6 class AttributeComboBox has been turned into an own class from the former inner class in class TableDialog.

New parts to implement

To create the new list format dialog, class ListDialog is created extending class DialogShell. Class ListDialog is a container for the class showing the actual list attributes, new class ListPanel. Class ListPanel in turn uses classes AttributeComboBox and BoundariesPanel to make available respective list attributes.

How the list attributes actually are shown and changed is implemented exactly the same as in class TableDialog.

To apply list attributes as set with ListDialog, a new method applyListAttributes is added to class SHTMLEditorPane which is again similar to what applyTableAttributes does.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic120/topic122.htm0100644 0000000 0000000 00000003201 12114157751 025274 0ustar000000000 0000000

Implementing list formatting in SimplyHTML

As described in the introduction of stage 6, to apply list formatting in SimplyHTML the requirement is to offer a simple way to switch list formatting on or off for an arbitrary portion of existing text paragraphs. As well there should be a way to change formatting of an existing list partly or completely through a separate dialog.

While parts of previous stages of SimplyHTML can be re-used to achieve list formatting thorugh a dialog, a certain difficulty is to change an arbitrary portion of existing text to list formatting because the element structure of the existing document content has to be changed.

Changing the element structure

The only way to change the element structure of an HTML document which is publicly available in the Java classes is to insert HTML code replacing a given part of respective document. SimplyHTML uses class SHTMLWriter to synthesize HTML code as already done in table formatting. The process is described in more detail in the following chapters. Please see the source code of stage 6 for additional details.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic120/topic123.htm0100644 0000000 0000000 00000012015 12114157751 025300 0ustar000000000 0000000

Switching list formatting on or off

When an HTML document initially is filled with content by typing text, the content is not formatted as list. To reach list formatting for parts of a document the user could

  1. start list formatting and then type content in the form of list items as needed or
  2. type in content and then switch on list formatting for the recently typed paragraphs

While the first case is comparably easy to achieve, both cases require a simple toggle functionality to switch list formatting on or off.

Basic approach

Such list toggle functionality basically this is achieved by

  1. detecting whether or not the required list formatting is present for a given text portion
  2. if list formatting is present, switch list formatting off
  3. if list formatting is not present (or not present as required), switch list formatting on

Method toggleList

Above scheme is implemented with method toggleList in class SHTMLEditorPane. Method toggleList finds out the parent element of a selected text portion. It then uses method switchOn to determine whether or not list formatting is already present in the selection inside the parent element. If method switchOn 'decides' to switch on list formatting (returns true that is), method toggleList uses method listOn to switch on list formatting. Otherwise list formatting is turned off by calling method listOff.

Difficulties

Up to here the solution sounds rather simple. In detail however, some difficulty is contained in the way how existing list formatting is to be changed. There are two cases we need to look at in more detail:

  1. list formatting is to be switched off for only parts of an existing list
  2. list formatting is to be switched on for one or more lists having mixed list formatting

Splitting lists

The first case above requires to split an existing list into up to three sections: The list remaining at the beginning of the selection, the actual selection for which list formatting is to be switched off and the list part possibly following the selection.

Method listOff splits a list by iterating through all list items in three steps. In the first step, it generates HTML for the list portion remaining unchanged and creates a new list end tag at the start of the selection to mark the start of the list split.

Secondly it continues iterating through the list items belonging to the selection generating HTML code with <li> tags removed.

Finally a new list start tag is created to mark the end of the split and iteration is continued over the remaining portion of the original list generating HTML code for the remaining list elements.

The resulting HTML code is inserted into the document replacing the 'old' part.

Merging lists

The second case above requires to merge different list formattings to one new list formatting. In addition, possible list formatting preceding or following the selection has to be split as described in the previous paragraph.

Method listOn merges lists by iterating through all list items and adjusts list start and end tags, merging and splitting lists as needed. <li> start and end tags are inserted if necessary.

The resulting HTML code is inserted into the document replacing the 'old' part.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic120/topic125.htm0100644 0000000 0000000 00000003472 12114157751 025311 0ustar000000000 0000000

Adding actions and GUI elements

To finally use the new list functionality it has to be made available to the user by adding it to SimplyHTML's GUI. Three new actions are created in class FrmMain

  • ToggleBulletsAction
  • ToggleNumbersAction
  • FormatListAction

They are added to FrmMain's initActions method and inlcuded into menu and tool bar definitions of the resource bundle of application SimplyHTML. Through SimyplHTML's dynamic resource mechanism, new menu items and tool bar buttons are created for above actions automatically.

Class SHTMLEditorPane has an additional action NewListItemAction which is registered with the key map of the editor pane (see also the explanations in the tables section). This action is used to create a new list item when the user presses the [Enter] key while the caret is inside a list.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic120/topic121.htm0100644 0000000 0000000 00000011225 12114157751 025300 0ustar000000000 0000000

Lists in HTML documents

In HTML documents content is embedded into tags such as <p> or <td> etc. To apply list formatting to a set of paragraphs, they have to be enclosed into list item tags ( <li></li>) which in turn are enclosed by list tags for ordered or unordered lists ( <ol></ol> and <ul></ul>).

For example, a list coded as

<ul>

<li>

<p>

Item 1

</p>

</li>

<li>

<p>

Item 2

</p>

</li>

</ul>

would be rendered as

  • Item 1
  • Item 2

Certain attributes can be applied to above HTML code by either storing them directly with a tag inside the HTML document or by defining styles for respective tag in a CSS style sheet.

Read on to see how above list formatting is applied with application SimplyHTML.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic103.htm0100644 0000000 0000000 00000002706 12114157751 023743 0ustar000000000 0000000

Stage 5: Plug-ins, user settings and dynamic resources

While SimplyHTML concentrates completely on text processing for HTML/CSS documents, it shall not be limited to creating and editing such documents. By delivering a solid and powerful editor for single documents it can be the basis for other functions too. Added functionality however is not in the scope of SimplyHTML which is where a plug-in mechanism comes into view.

A plug-in mechanism could allow almost any extension to SimplyHTML while preserving the original scope, leaving additional functions to potential plug-ins. This stage implements such plug-in mechanism along with an enhanced way of working with resource bundles suitable for plug-in usage. To allow users to configure plug-ins individually, a simlpe way of persistently storing user prefernces is implemented too.

The plug-in mechanism is created with the idea of future enhancements according to the needs of plug-in developers. This is best done in an evolutionary process which is why it is started in this stage instead of waiting until additional edititing functions are finalized.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/0040755 0000000 0000000 00000000000 12114157751 023153 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/topic70.htm0100644 0000000 0000000 00000004770 12114157751 025157 0ustar000000000 0000000

Presenting SimplyHTML in multiple languages

By using class ResourceBundle for all string constants in SimplyHTML the source code does not contain string constants except for internal ones not appearing on the GUI.

In the place of the former string constants in the source code a call to method getResourceString() is put. The actual strings are centrally stored in and retrieved from a resource file instead.

By providing resource files for any language, the application shall be presented in, the application can be switched to those languages simply by switching to the particular resource file.

How a language is picked

In Java class Locale is used to represent a specific geographical, political, or cultural region. The Locale of the system an application is running on can be determined by Locale.getDefault() .

Class FrmMain has a public and static field resources referencing the ResourceBundle from which all string constants shall be taken. The ResourceBundle is initialized to the Locale the System is running on. If a resource file for the Locale can not be found, the default candidate is taken, which would be SimplyHTML.properties.

If for instance SimplyHTML is running on a German system and a resource file ending with the appropriate language ("_de") is distributed with the application, this resource file will be taken. If SimplyHTML_de.properties can not be found, SimplyHTML.properties will be taken as the resource file instead.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/topic64.htm0100644 0000000 0000000 00000010773 12114157751 025162 0ustar000000000 0000000

How undo and redo work in SimplyHTML

Currently, only the Document class offers undo and redo support to text components. For any edit action that can be cancelled and reinstated again in a particular Document , the Document sends undoable edit events to all UndoableEditListeners registered with that Document.

Creating an UndoManager

To store undoable edit actions for later use in a possible undo or redo action, class FrmMain creates an instance of class UndoManager. The object is stored in private field undo of class FrmMain for later use in the undo/redo implementation.

Defining and registering an UndoableEditListener

Class FrmMain also defines an inner class UndoHandler that implements the UndoableEditListener interface. In its method undoableEditHappened it stores undoable edit actions in the previously created UndoManager. As well, UndoHandler updates the GUI whenever an undoable edit event is received.

The UndoHandler of FrmMain is registered with any Document created or opened by SimplyHTML in the respective actions SHTMLFileOpenAction and SHTMLFileNewAction. Consequently UndoHandler is properly removed from any open Document before it is closed to ensure proper cleanup of the disposed Document in the garbage collection.

Defining actions for undo and redo

To finally being able to perform an undo or redo command, either through the menu or from other places such as a tool bar, class FrmMain defines two actions UndoAction and RedoAction.

In their actionPerformed methods UndoAction and RedoAction call methods undo and redo of FrmMain's UndoManager accordingly. In addition they both have a method update to bring their GUI representation in line with the current undo state (being enabled or not depending on whether or not undoable edits are present in the UndoManager, that is).

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/topic67.htm0100644 0000000 0000000 00000004103 12114157751 025153 0ustar000000000 0000000

Using resource bundles

Resources are simply text files that are accessible to an application. The text files contain information in the format

key=value

and usually are distributed in the path the application classes are located. Class java.util.ResourceBundle makes available the data to an application. Applications read constants or parameters at runtime through methods of class ResourceBundle.

Why using resources?

Using resources has the following advantages

Maintenance: text constants and parameters can be maintained outside the source code. Changing a text constant or parameter does not require code changes. Information is stored in a central place. If a change is necessary, respective part has not to be searched in the whole source code. Constants have to be changed in only one place regardless of whether they are used in multiple places in the source code.

Internationalization : Resources can be replaced in one step without code changes making it easy to switch an application to another language.

Control: Parts of the application can be controlled dynamically through parameters in a resource file rather than having to 'hard wire' them.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/topic72.htm0100644 0000000 0000000 00000013301 12114157751 025147 0ustar000000000 0000000

Implementing drag and drop

Drag and drop more or less is cut and paste without having to use the menu. As SimplyHTML has implemented cut and paste for styled text and HTML text in stage 2, it has the basis for drag and drop too.

Typical drag an drop

A typical drag and drop operation in the editor would act as follows: By selecting text in the editor and by dragging the selection with the mouse, a copy operation is initiated. Once the selection is dropped anywhere else in the editor, the selection is removed from the original location and pasted at the new location.

To implement drag and drop a mechanism has to be implemented to recognize drag and drop activities and to react in the described way.

The Java Tutorial

There is a good example about how to implement drag and drop in The Java Tutorial. The drag and drop example is well explained there already. At this point therefore only some more general aspects are discussed only.

The Java Tutorial is available online at http://java.sun.com/docs/books/tutorial/

Note: In J2SE 1.4 the original drag and drop is simplyfied by wrapping the 'old' implementation into class TransferHandler partly but as SimplyHTML shall be available to users of the 1.3 Runtime, the 'old' mechanism is implemented which in fact does the same as the 'new' one.

Drag and drop in SimplyHTML

Class SHTMLEditorPane has been created in this stage to allow for own cut and paste handling. For drag and drop support it has an additional section in the source code. The main parts of the drag and drop implementation are methods

  • initDnd
  • dragGestureRecognized
  • drop and
  • doDrop

Method initDnd

Method initDnd instantiates the objects necessary to control our drag and drop implementation: DragSource and DropTarget. The SHTMLEditorPane registers itself as a DropTarget and as a DropTargetListener. In DragSource SHTMLEditPane registers itself as a DragGestureRecognizer. As well it registers as a MouseListener to keep track of the selection during drag and drop operations.

Method dragGestureRecognized

dragGestureRecognized is the method SHTMLEditorPane has to implement to be a DragGestureRecognizer. The method is invoked by all drag initiating gestures on SHTMLEditorPane.

In method dragGestureRecognized an HTMLText object is created on the currently selected text and an HTMLTextSelection transferable is created wrapping the HTMLText object. Finally the drag operation is initiated by invoking method DragSource.startDrag with the newly created transferable.

Method drop

Once a drop event is recognized, method drop is invoked. In SimplyHTML it calls method doDrop if a suitable DataFlavor (HTMLText or String) is found in the dragged element. Otherwise the drop is rejected.

Method doDrop

Method doDrop does the actual cut and paste resulting from the drag and drop operation consisting of adding the dragged element and necessarily removing the dragged element from the original position.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/topic65.htm0100644 0000000 0000000 00000006354 12114157751 025163 0ustar000000000 0000000

Cut and paste mechanism in Java

Cut and paste uses the clipboard to copy a certain part of a document. Data is transferred from one part of a document to the clipboard and later it is transferred from the clipboard to another place of this or another cocument.

Cut, copy and paste actions and methods

In the javax.swing packages there are predefined actions one can use to add cut and paste to an application in an 'Edit' menu for instance. DefaultEditorKit.CutAction, DefaultEditorKit.CopyAction and DefaultEditorKit.PasteAction already deliver functionality by calling a target object's cut , copy and paste methods.

Class JTextComponent , which is a superclass to JEditorPane, in turn implements these methods to perform the actual cut and paste on a Document connected to that JTextComponent.

How data is transported

To be able to transport data regardless of its type, the Java language has an object Transferable which one has to use or design to certain needs. Transferables make use of DataFlavors to inform about the data types they support. Transport of Transferables in cut and paste operations is done through Clipboard objects.

Extending the mechanism

Unfortunately this mechanism is not extended by class JEditorPane for the transport of HTML during cut and paste operations. JEditorPane is limited to plain text in cut and paste operations even when its content type is set to text/html. SimplyHTML therefore extends JEditorPane with class SHTMLEditorPane overriding its cut, copy and paste methods so that cut and paste including styles and HTML specific parts is possible.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/topic63.htm0100644 0000000 0000000 00000003531 12114157751 025153 0ustar000000000 0000000

Typical undo/redo parts

In the Java language undo/redo support is made availabe to all text components through a common set of parts, which can be used almost simliarly between different applications dealing with text processing.

UndoManager

An UndoManager 'remembers' undoable edit acionst. It is the central place to store such edit actions for later use in a possible undo/redo action.

UndoableEditListener

For being able to store undoable edit actions a class has to implement the UndoableEditListener interface. By implementing this interface, an object can register itself with any Document whose undoable edit events it likes to handle.

By listening to undoable edit events, a class acting as an UndoableEditListener can as well update GUI elements according to the undoable edit events received.

Undo and redo actions

To actually undo or redo an undoable edit, actions are used as a common way to make an undo or redo command available on the GUI. These actions call UndoManager methods to undo or redo an undoable edit. As well the actions can be used to adapt the GUI according to the current undo/redo state.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/topic69.htm0100644 0000000 0000000 00000012301 12114157751 025154 0ustar000000000 0000000

How cut and paste work in SimplyHTML

To allow cut and paste for content including styles and all HTML specific parts SimplyHTML extends the cut and paste mechanis of Java. It defines two classes to enable the transport of HTML data and extends JEditorPane to use SimplyHTML's classes instead of the standard ones.

Class HTMLText

Class HTMLText represents a portion of HTML content. It has field htmlText for the HTML code representing the content and field plainText to represent the content as plain text. With methods copyHTML and pasteHTML it enables transport of HTML data into and out of HTMLText objects.

Transport mechanism

To transport HTML text methods copyHTML and pasteHTML use HTMLEditorKit's read and write methods, which allow to read or write a portion of a Document's content as HTML code using a Writer. By taking a StringWriter, data can be transferred into a String for temporary storage inside a HTMLText object.

Different mechanism within one paragraph

When copying and pasting text within one paragraph, i.e. without paragraph breaks, method HTMLEditorKit.read makes an own paragraph of the pasted text. HTMLText avoids this behaviour by implementing an alternate copy and paste mechanism.

When not copying multiple paragraphs the selection is split into text chunks. For each chunk of text it's attributes. Each chunk of text is inserted togehter with its attributes when it is pasted to another place in a document.

Class HTMLTextSelection

To transfer data in cut and paste operation a Transferable object is needed. A Transferable object wraps a data object into a common format describing the contained data to transfer operations. Class HTMLTextSelection is a Transferable for HTMLText objects. Whenever HTMLText is to be transported it is wrapped into an HTMLTextSelection object and passed to any transfer operation such as copy or paste.

Extending JEditorPane

JEditorPane is a subclass of JTextComponent. JTextComponent has methods cut, copy and paste to implement cut and paste operations which are inherited by JEditorPane. To actually use HTMLText and HTMLTextSelection in an editor, JEditorPane has to be extended by an own class named SHTMLEditorPane in SimplyHTML. SHTMLEditorPane overrides methods cut, copy and paste and uses HTMLText and HTMLTextSelection accordingly.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/topic68.htm0100644 0000000 0000000 00000013377 12114157751 025171 0ustar000000000 0000000

Creating a dynamic menu

In stage 1 of SimplyHTML the menu consisting of menu bar, menus and menu items had been hard coded in method constructMenu. This part would always have to be changed when the menu changes or is extended by additional functions. In stage 2 of the application we therefore add functionality to build a menu dynamically controlled by parameters from a resource file.

Connect actions dynamically

Certainly actions triggered by menu selections still have to be coded also because they contain the actual functionality in most cases. But how actions are connected to menu items or other GUI elements does not have to be hard coded and therefore actions are included in the change towards a dynamic menu.

Advantage

By having functions to dynamically construct and control a menu, the code does not have to be changed again once a new menu is to be added or changes in the menu structure occur.

Menus and menu items can be added simply by making an entry in the resource file.

How to automate the menu construction

Each action has a name which we use as unique key, such as new, open, save, etc. In class FrmMain's constructor a commands Hashtable is created with all actions of SimplyHTML and their action commands ( new, save, etc.). With method getAction, an action can be fetched by its command name.

Method createMenuBar

To create the menu bar a menu bar definition string from the resource file is read having the key for each menu delimited by blanks (e.g. file edit help). The keys are in the order as menus shall appear in the menu bar.

Method createMenu

To create menus a menu definition string from the resource file is read having the action key for each menu item delimited by blanks. The keys are in the order as items shall appear in respective menu.

Method createMenuItem

Menu items are created with the key to 'their' action (new, save, etc.) as the action command. The key also serves to get the label for the menu item: In the resource file all menu labels are named fileLabel, newLabel, saveLabel, etc. so they can be read automatically and stored with the menu item.

Consistent state handling over components

Menu items always should reflect if their action is available at a certain point in time during execution of an application. Actions in turn should only be available if it makes sense at that point in time. Action close for instance should only be enabled, if there are documents open, that can be closed.

Listeners for interaction between menus, menu items and actions

A reference between menus, menu items and actions is created in several ways to ensure this behaviour:

  • The menu item is connected to a PropertyChangeListener and registered with the action belonging to that menu item to automatically update the state of the menu item according to the state of the action.
  • The action in turn is registered with the menu item as an ActionListener so that the action can execute its actionPerformed method whenever the menu item fires the action command.
  • Finally with each menu a MenuListener is registered that updates the action state of each item in respective menu whenever it is about to be displayed. This ensures that always the actions are in the correct state. (In later stages, this has to be refined by updating controls other than menus if such are connected to actions in addition.)
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62/topic66.htm0100644 0000000 0000000 00000007545 12114157751 025167 0ustar000000000 0000000

Adding an edit menu

With the functions describing cut and paste and undo/redo in previous chapters we finally have what we need to add common edit functionality to SimplyHTML and to make it available on the GUI.

The edit menu is easily built by adding a new menu definition to our resource bundles: Just a few lines of text describing the new menu and its menu items. The entries in the resource bundle look as follows

# edit menu definition

edit=undo redo - cut copy paste

editLabel=Edit

# edit menu items

undoLabel=Undo

redoLabel=Redo

cutLabel=Cut

copyLabel=Copy

pasteLabel=Paste

Once above text is added to file SimplyHTML.propertiers, construction of the menu as well as proper connection to actions and functionality to enable/disable the menu items is created automatically by the dynamic menu functionality of SimplyHTML.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140/0040755 0000000 0000000 00000000000 12114157751 023230 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140/topic144.htm0100644 0000000 0000000 00000026167 12114157751 025322 0ustar000000000 0000000

Parts needed to implement style manipulation

While many existing functions of SimplyHTML and the Java classes can be used to build the new style setting functionality, some additional parts are needed too. The following table gives an overview of all new or changed items

Class

Purpose, Changes

AttributePanel

Panel to set a group of attributes, base class for other classes such as margin or style panel

CSSWriter

Enhanced method structure for writing individual styles

DocumentPane

additional methods for style sheet storage and merging style sheets

FrmMain

new actions for paragraph and named style formatting as well as new tool bar component for setting named styles, some methods and inner classes consolidated to avoid redundancies

MarginPanel

new class to set margins and paddings, made stand alone class from former inner class to share functionality between table and paragraph dialog

ParaStyleDialog

dialog for setting either paragraph styles or named styles

StylePanel

new class to set paragraph attributes, made stand alone class from former inner class to share functionality between table and paragraph dialog

StyleSelector

component to apply named styles through the tool bar

Util

utility methods for working with internationalized option panes, resolving nested attribute sets and style sheets

Mostly GUI changes

Functionality to read, modify and apply attributes has already been created in previous stages and can be re-used in this stage unchanged. Working with named styles and style sheets is covered by class StyleSheet of the Java Swing package in addition.

Therefore above parts almost all are GUI elements. Some 'non-GUI' methods and changes had to be added in this stage mainly to classes CSSWriter and Util and the only other 'non-GUI' method saveStyleAs in class ParaStyleDialog was too small to create an extension to class StyleSheet for it.

In essence this stage mainly adds GUI extensions and relies on exisiting functionality of previous stages and the Java classes to implement style manipulation.

Much interaction

Nevertheless a lot of interaction between the mentioned parts is necessary so that an emphasis in this stage of the tutorial lies on explaining these interactions and their implementation as well.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140/topic141.htm0100644 0000000 0000000 00000005455 12114157751 025314 0ustar000000000 0000000

Styles in HTML and CSS

Character attributes vs. paragraph attributes

The simplest way to apply a certain format to a portion of a HTML document is to store HTML format attributes such as b, i or align with any tag to be formatted in the particular way. While this approach is most flexible in terms of combination of such attributes, plain HTML attributes allow only limited formatting compared to CSS styles. As well this method adds a lot of formatting information to the plain content of a document with much rendundancy in most cases.

CSS attributes

By using the style attribute, CSS attributes such as margin-top, padding-right, text-align, etc. can be added to HTML tags instead. This method opens additional formatting settings but it still requires attributes to be stored with each tag having the same impact as HTML attributes.

Paragraph styles

To reduce the formatting overhead HTML and CSS attributes can be applied to paragraph tags so that they are valid on any tag contained in such paragraph.

Style sheets with named styles

The most flexibility and power however is reached with usage of Cascading Style Sheets (CSS) in combination with a given HTML document. By defining styles in a separate style sheet groups of format attributes can be held independent from HTML documents resulting in significant advantages

  • styles are defined only once avoiding rendundancies and increasing maintainability
  • styles can be shared over many documents again reducing redundancy and maintenance efforts
  • documents only need references to styles reducing storage space
  • a predefined group of attributes can be applied in a single making formatting faster
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140/topic145.htm0100644 0000000 0000000 00000006253 12114157751 025315 0ustar000000000 0000000

Approach to work with paragraph and named styles

As pointed out, style manipulation functionality is already exisiting in the Java classes and the previous stages of application SimplyHTML. What is needed in addition is a GUI to visualize the existing styles and to let the user add new, change existing or delete styles.

Dialog to manipulate styles

Class ParaStyleDialog is added to SimplyHTML as a new component to achieve this. Class ParaStyleDialog has two major functions:

  • to manipulate any given paragraph style and
  • to maintain all paragraph styles in a given style sheet.

Because both tasks require the same elements (components to reflect settings of paragraph attributes), they are combined inside class ParaStyleDialog and made available as two separate operation modes of the component.

Components to select named styles and alignment

To make available existing named styles for paragraphs as defined in the style sheet, class StyleSelector is created. It shows all available named paragraph styles and lets the user apply a given style to the currently selected paragraph(s).

A paragraph attribute which is used quite often is the text alignment setting (left, center or right). For setting text alignmen in one step, inner class ToggleFontAction has been changed to class ToggleAction and can now can be used as a generic action to toggle certain character and paragraph attributes including text aligment. The new action is used by SimplyHTML's dynamic resource mechanism to add respective toggle button components to the tool bar.

Style sheet storage

As stage 8 allows to change contents of a given style sheet, the way a style sheet is saved has to be revised. When a document is saved, SimplyHTML now recogniszes whether or not a style sheet with the same name exists in the location where a document is to be saved.

Is a style sheet present already, it is merged with the style sheet to be saved.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140/topic147.htm0100644 0000000 0000000 00000003266 12114157751 025320 0ustar000000000 0000000

Class StyleSelector

Class StyleSelector is a component used to make available a list of existing named paragraph styles and to apply a named style to the currently selected parapraph(s). It extends class JComboBox by implementing interfaces AttributeComponent and ChangeListener.

Added to a tool bar its method getValue allows access to the currently set style while method setValue can be used to reflect the style of the paragraph the caret is currently in.

As desribed in more detail in the next chapter, class StyleSelector listens to changes in the JTabbedPane of class FrmMain and to changes of a given style sheet. Whenever the JTabbedPane or the style sheet changes (i.e. another document is active or the styles have changed), the list of named paragraph styles in the StyleSelector is updated.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140/topic149.htm0100644 0000000 0000000 00000010121 12114157751 025306 0ustar000000000 0000000

Adding the new style components to the GUI

As in previous stages actions are used to connect the new functionality to the GUI elements such as menus or the tool bar. There are three new actions and one changed action in class FrmMain

  • FormatParaAction - action to set paragraph styles
  • EditNamedStyleAction - action to manipulate named styles
  • SetStyleAction - action to apply a given named style to the current selection
  • ToggleAction - adaption of former ToggleFontAction to support paragraph alignment too

The actions are initialized in method initActions of class FrmMain and are added to FrmMain's dynamic resource.

FormatParaAction and EditNamedStyleAction

FormatParaAction and EditNamedStyleAction both create an instance of ParaStyleDialog. FormatParaAction uses the set of attributes returned by ParaStyleDialog and applies them to the currently selected paragraph(s). EditNamedStyleAction does nothing on return of ParastyleDialog, the dialog does all the work for manipulation of named styles.

SetStyleAction

SetStyleAction takes the class attribute returned by the StyleSelector component in the tool bar and applies it to the currently selected paragraph(s).

ToggleAction

ToggleAction applies the attribute returned by its getValue method to the current selection. Depending on the attribute key the action object represents attriubtes are applied to either on paragraph or character level. The action then sets a state indicator to allow a bound component such as a JToggleButton to reflect the state accordingly (selected or unselected).

New components

Besides the new StyleSelector component, in method createToolBar of class FrmMain, additional toggle buttons are created to toggle between different paragrpah alignments (left, center, right). A new tool bar button for setting a paragraph style is added too. In menu 'Format', new menu items are created for setting paragraph style, style sheet manipulation and paragraph alignment.

All new components are added by including their associated action names in the properties file of SimplyHTML as described in stage 2 and especially 'Adding an edit menu' as well as ' Dynamic resources' from stage 5.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140/topic146.htm0100644 0000000 0000000 00000017362 12114157751 025321 0ustar000000000 0000000

Class ParaStyleDialog

Class ParaStyleDialog has two major functions:

  • to manipulate any given paragraph style and
  • to maintain all paragraph styles in a given style sheet.

The two tasks are available as two separate operation modes of the component, MODE_PARAGRAPH_STYLE and MODE_NAMED_STYLES.

In MODE_PARAGRAPH_STYLE class ParaStyleDialog is used to directly manipulate a given set of paragraph style attributes. In MODE_NAMED_STYLES, the dialog is used to manipulate styles in a style sheet which does not affect formats of the underlying document directly (only indirect through style sheet changes).

Setting the operation mode

The operation mode is derived from the constructor used to create a ParaStyleDialog. When constructed to operate with a certain Document, MODE_NAMED_STYLES is assumed and the Document's style sheet is taken to be operated upon.

If no Document is passed to the constructor of ParaStyleDialog, it is constructed in MODE_PARAGRAPH_STYLE, i.e. not using a style sheet.

Passing initial dialog settings

Class ParaStyleDialog implements interface AttributeComponent (introduced as FontComponent initially in stage 3) so that its contents can be set or read through a set of attributes in an AttributeSet object. When in MODE_PARAGRAPH_STYLE, initial dialog contents need to be set by a call to method setValue passing an AttributeSet object having all initial paragraph styles to be manipulated.

When in MODE_NAMED_STYLES , a list of existing named paragraph styles is read from the style sheet of the Document passed in the constructor. Whenever a style is picked from those, the dialog is set to show the attributes of this style.

Reading dialog settings

As an AttributeComponent class ParaStyleDialog returns its currents attribute settings in an AttributeSet object through method getValue. In MODE_PARAGRAPH_STYLE method getValue can be used to get the set of attrbutes to be applied.

In MODE_NAMED_STYLES class ParaStyleDialog is not meant to deliver a set of attribute settings, although method setValue certainly can be used too. Instead, the dialog only makes available all named paragraph styles found in a given style sheet.

All changes to a given set of paragraph attributes can be saved to that style sheet using class ParaStyleDialog. By changing attribute settings of an existing named paragraph style and storing them back to the style sheet, format of all paragraphs using respective named style is changed implicitly, immediately and automatically in the underlying document.

Thus no direct reading of attribute settings is necessary in MODE_NAMED_STYLES.

Style sheet manipulation

In MODE_NAMED_STYLES class ParaStyleDialog offers to

  • save settings to an existing named style
  • create a new named style and
  • to remove an exsting named style from the style sheet

Saving attributes to an existing style and creation of a new style both is done using method addRule of class StyleSheet. This method expects a style to be passed in the form of a CSS declaration string,

e.g. p.myStyle { text-align:center; }.

To transform attribute settings from class ParaStyleDialog in to this format, method writeRule of class CSSWriter is used.To remove an existing style from the style sheet class ParaStyleDialog uses method removeStyle of class StyleSheet.

Class ParaStyleDialog adds methods necessary to interact with the user upon style changes accordingly, e.g. by asking whether or not to really delete a particular style or by checking whether or not a style shall be overwritten having the same name as a name entered by the user.

With stage 8 class Util has some new methods combining a generic JOptionPane with calls to SimplyHTML's class DynamicResource for support of messages in other languages. These methods are applied to other usages of JOptionPane in SimplyHTML as well.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140/topic150.htm0100644 0000000 0000000 00000006534 12114157751 025313 0ustar000000000 0000000

Style sheet storage

Style sheets are part of SimplyHTML since stage 1 of the application. Since then they were only saved along with a document with a set of static styles. With stage 8 of SimplyHTML manipulation of named styles is supported so that the original style sheet handling needs to be extended.

Loading style sheets

Class DocumentPane now has two ways of creating a document with respect to style sheets. A new document is created with the underlying EditorKit creating a default style sheet from the resources package of SimplyHTML (as done in previous stages).

This is not longer done when an existing document is loaded. In such case the underlying EditorKit creates a default document without a default style sheet. Class DocumentPane instead looks for the style sheet reference inside this document and loads this style sheet for the particular document instead.

The EditorKit not longer shares a single style sheet among different documents, each document has associated its own style sheet.

Saving style sheets

When a style sheet is saved, four cases are now handled

  1. no styles are present at save location, create new style sheet
  2. the style sheet was loaded from somewhere else and now is being saved at a new location where a style sheet exists havig the same name
  3. the style sheet is saved at the same location where it was loaded from
  4. the style sheet was newly created and now is being saved at a location where a style sheet exists havig the same name

In case 2 and 4 above, the style sheets are merged overwriting existing styles in the found style sheet with styles from the saved style sheet. Styles from the found style sheet not existing in the saved style sheet are kept in the newly saved style sheet.

In case 3 above the existing style sheet is overwritten by the new version.

Tradeoffs

While above save strategy does not require user interaction other than to choose a save loaction and name for the respective document (as before) it still leaves the problem that an existing style sheet with the same name could have styles with the same name as altered ones in the saved style sheet. Overwriting such styles could cause unwanted styles to appear in other documents sharing the particular style sheet.

Therefore the user is obliged to either

  1. not save documents in the same directory when they do not share the same set of named styles or
  2. use different style names for different styles over all documents sharing the same style sheet
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140/topic148.htm0100644 0000000 0000000 00000011122 12114157751 025307 0ustar000000000 0000000

Interaction between style components and style sheet

As described previously , several components are involved in paragraph and named styles manipulation:

  • the style sheet of the document currently edited,
  • the JTabbedPane of class FrmMain ,
  • the StyleSelector in the tool bar,
  • the list of named styles in class ParaStyleDialog.

Components reflecting named styles have to be updated accordingly when

  • a new document is created,
  • an existing document is opened,
  • another document in the group of currently open documents is activated or
  • the style sheet of the currently edited document changes.

Listeners to watch for changes

Instead of implementing hard wired relations between objects to handle style related events, application SimplyHTML implements listeners with these objects.

StyleSelector

Class StyleSelector implements the ChangeListener interface to handle ChangeEvents. The StyleSelector object in the tool bar is registered as a ChangeListener with the style sheet of any document with method registerDocument of class FrmMain. With that the StyleSelector object is notified whenever a style sheet changes. When a document is closed, class StyleSelector is removed as a ChangeListener in method unregisterDocument of class FrmMain.

In method createToolBar of class FrmMain class StyleSelector is registered with FrmMain's JTabbedPane as ChangeListener too. Whenever another document is activated in the JTabbedPane, the StyleSelector object in the tool bar is notified.

ParaStyleDialog

Class ParaStyleDialog is also implementing the ChangeListener interface. It registers itself as a ChangeListener with the style sheet of the currently active document. Whenever class ParaStyleDialog is used in MODE_NAMED_STYLE and a style is saved to the style sheet, the respective change event triggers an update of class ParaStyleDialog's list of named styles.

Class ParaStyleDialog overrides method dispose to remove itself from the list of ChangeListeners of the underlying style sheet.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic128/0040755 0000000 0000000 00000000000 12114157751 023236 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic128/topic130.htm0100644 0000000 0000000 00000020670 12114157751 025314 0ustar000000000 0000000

Creating a GUI to manipulate image references

As with table and list support, to create and manipulate image references a graphical user interface (GUI) is needed. Class ImageDialog is created for this purpose.

Class ImageDialog

Class ImageDialog is used to insert image references and to set all relevant attributes for these references. As well it provides a repository from which images can be selected, added and removed. An ImageDialog is divided into three panels from left to right. The left panel has a list which shows all files present in the image directory of the given document as well as buttons to add and remove images from the list. The middle panel is used as a preview region for any image selected from the image list. In the right panel all attributes of a selected image are shown and can be set.

Image list maintenance

When an ImageDialog is created, it is passed a directory which is to be used as the image repository. The image list is filled with the names of all files found in this directory by calls to method updateFileList (the method has only one line setting the JList content to the the current result of a directory listing of the directory referenced by imgDir).

Method handleAddImage

When button 'Add' is pressed, a file chooser is opened to allow selection of an image file to be placed into the repository. If a file is picked in the file chooser it is copied to the image repository with the help of method Util.copyFile and method updateFileList is called to reflect the new file in the file list.

Method handleDeleteImage

When button 'Delete' is pressed, an option dialog asks the user whether or not to delete the image file currently selected in the image list (if any). If the user chooses to really delete the selected file, it is deleted and the image list and preview are updated accordingly.

Image attribute manipulation

Once an image is selected from the list of images, all attributes of the selected image are displayed in the panel on the right of an ImageDialog. From there all attributes of the image can be set accordingly. Changes to image attributes such as size or scale are reflected in the preview immediately. In addition, attributes such as border width or distance to the surrounding text can be set and will be effective on the image in the document once applied (see below).

In class ImageDialog a set of listeners is used to synchronize all parts of the dialog to user changes of particular attributes. Section 'event handling' in the source code of class ImageDialog has the mentioned listeners which are applied to respective components in the constructor of the dialog. Each listener calls helper methods such as applyPreviewHeight, applyPreviewWidth or applyPreviewScale in case an event occurs which a listener is bound to.

Returning image reference and image attributes

Once an image is selected and all attributes settings meet the desired display in the document, method getImageHTML returns the HTML code representing an image reference with all attributes according to the selection in the ImageDialog. Method getImageHTML uses class SHTMLWriter to create an image tag and attributes from the settings on the ImageDialog.

The components on the ImageDialog used for setting image attributes are implementing the AttributeComponent interface so each of them returns its value in an AttributeSet object. All such settings are brought together in an instance of SimpleAttributeSet and passed to method startTag of class SHTMLWriter along with the actual image reference returned by method getImageSrc .

Setting an initial image reference and attributes

Besides creating new image references class ImageDialog can be used to display and manipulate an existing image reference too. The same functionality is used as described above after the ImageDialog has been set to an existing image reference with method setImageAttributes.

Method setImageAttributes iterates through the Vector of AttributeComponents and applies attributes from a given AttributeSet to the components. As well it sets the ImagePreview to the image reference found in the AttributeSet.

Image preview

When an image is selected from the list of images or when attributes af a selected image are changed, the resulting image as it would appear in the document is shown in the preview section of class ImageDialog. The preview is produced by class ImagePreview which is an extension to class JComponent.

Class ImagePreview takes care of displaying any image and has methods to apply a given scale percentage to that image. It implements the Scrollable interface so it can be embedded in a JScrollPane for cases where an image is to be viewed in a region being smaller than respective image.

You can refer to the source code of ImagePreview for more details about how the preview of images is achieved.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic128/topic129.htm0100644 0000000 0000000 00000011010 12114157751 025310 0ustar000000000 0000000

General concept for image support

In HTML images are separated from documents. Documents contain references to image files in the place where images should appear inside a document. An image reference describes the location and name of respective image file as well as how it is to be rendered. The actual images are stored separately in image files and loaded dynamically when documents are displayed.

SimplyHTML supports image references by maintaining an image repository for each document. An image repository in this context is a directory containing all images referenced by a HTML document.

Restrictions

To keep image maintenance simple, the following restrictions are used in SimplyHTML

  • image files referenced from HTML documents are automatically placed into directory images
  • directory images is created automatically inside the directory, a HTML file is saved

SimplyHTML has no support for creation or manipulation of image files as in image editing software . Image files need to be present already to be added to documents created or maintained with SimplyHTML.

Supported image formats

SimplyHTML supports the following image file formats

  • Graphics Interchange Format (GIF)
  • Joint Photographic Expert Group (JPEG) format

Temporary storage

To allow image processing for newly created documents (i.e. documents not having been saved at the time images are added) a temporary directory is maintained. The temporary storage is maintained automatically by SimplyHTML in directory

[user home]/SimplyHTML/temp/.

[user home] in this context is the directory returned by the Java expression System.getProperty("user.home"). It is usually the directory where a user logged in to a given system has all rights and where no other user except for system administrators has access rights unless explicitly granted by the owner or system administrator.

Using directory [user home] has the effect that every user has an own temporary storage area.

Images in new documents

For each newly created document a directory is created inside the temporary directory named after the document,

e.g. [user home]/SimplyHTML/temp/Untitled 1/.

If images are added to a document which has not been saved so far, directory images is created inside the temporary directory,

e.g. [user home]/SimplyHTML/temp/Untitled 1/images/.

Once a new document is saved, the image directory is copied from the temporary storage to the directory the new document has been saved.

Images in existing documents

If images are added to an existing document, respective image files are stored in directory images inside the directory the document was loaded from. Directory images is created in the directory the document was loaded from by application SimplyHTML when it is not already present an images are added to that document.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic128/topic131.htm0100644 0000000 0000000 00000025323 12114157751 025315 0ustar000000000 0000000

Implementing image storage

To enable image reference manipulation functionality as described in the next chapter, maintenance of an image repository is required. The following parts have been created to support image repository maintenance:

Class, Method, Field

Description

FrmMain.appTempDir

new field referencing the temporary directory of application SimplyHTML

FrmMain.initAppTempDir()

method to initialize the temporary directory of application SimplyHTML

FrmMain.getAppTempDir()

method to get the directory for temporary storage of application SimplyHTML

DocumentPane.getImageDir()

method to get the image directory for a particular document open in SimplyHTML

DocumentPane.saveImages()

method to save images at a new location

Util.copyFile()

method to copy files

Generally speaking, an image repository always is kept with the document it belongs to. Whenever a document is saved, the image directory of the document is saved at the same location, necessarily copying image files as described below.

Methods initAppTempDir and getAppTempDir

With method initAppTempDir of class FrmMain new field appTempDir is initialized. The method creates a file object referencing a directory named [user home]/SimplyHTML/temp. If the directory does not exist it is created by method initAppTempDir.

Method getAppTempDir publicly makes available field appTempDir for read only access.

Method saveImages

In class DocumentPane documents are being saved with method saveDocument. With the new image support of stage 7 of SImplyHTML document storage has to be extended by a method to save any image files referenced in a particular document.

New method saveImages of class DocumentPane is called by method saveDocument for this task. It uses new method getImageDir (see below) to find out the source location of any image files. It then copies all image files to the location, the document is being saved to using method copyFile of class Util (see below).

Method getImageDir

Method getImageDir finds out the source location of image files for a document to be saved. The method checks whether images are currently stored in a temporary directory for a given document. If the document was not newly created, getImageDir finds out if the document is about to be saved at a new location (save as) or if it is being saved at the location it was loaded from (save) in which cases the source locations are to be taken from different locations in class DocumentPane .

The source image directory is returned to the calling method.

Method copyFile

Method copyFile in class Util is a simple way to copy a file from one location to another. It accepts two file objects as parameters for the source and destination file to be copied. It opens RandomAccessFile objects for the two files and creates the destination file if necessary. It then reads blocks of content bytes from the source file and writes them to the destination file.

If the destination file already exists, copyFile does nothing.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic128/topic134.htm0100644 0000000 0000000 00000006776 12114157751 025333 0ustar000000000 0000000

Image references in HTML

In HTML images are separated from documents. Documents contain references to image files in the place where images should appear inside a document. An image reference describes the location and name of respective image file as well as how it is to be rendered. The actual images are stored separately in image files and loaded dynamically when documents are displayed.

An image reference in HTML is expressed by an img tag having the reference attribute and additional attributes such as in

<img src="images/picture.jpg" border=0 width=100 height=200>

The image reference attribute (attribute src) can be either an absolute or relative path and file name expression. The other attributes specify information about how the image is to be displayed such as image width and height or how much space between text and image is to be rendered.

Absolute references

An absolute reference is an expression containig the full path and name of an image file such as in

file:/C:/data/documents/myDoc/images/picture.jpg

Absolute image references should be avoided as they need to be changed whenever the image file is moved to another location.

Relative references

A relative reference has path and file name information relative to the location a HTML document is stored. An expression such as

images/picture.jpg

inside a document stored as

c:\data\document\myDoc\doc.htm

would mean the same as the absolute reference expression from previous paragraph. The main difference however is that whenever the document and image file are moved to a new location together, the relative reference can remain unchanged.

Resolving relative references

In SimplyHTML's implementation of image support only relative references are used. To resolve relative image references, class HTMLDocument allows to specify a base location with method setBase. When a document is loaded, its source path is passed to method setBase as the the base directory. When a new document is created, a temporary directory is set with method setBase.

In both cases all relative references are resolved against the base directory set with method setBase.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic128/topic133.htm0100644 0000000 0000000 00000006264 12114157751 025322 0ustar000000000 0000000

Making the GUI available to the user

As functionality and GUI for manipulation of image references as described in previous chapters is present, a way to use it is needed in addition.

Actions InsertImage and FormatImage

Similar to the procedure used in previous stages, two actions are added to class FrmMain as inner classes. The new actions are added to the DynamicResource instance of class FrmMain with method initActions (see the documentation of stage 2 and stage 5 for a detailed description of actions and dynamic resources).

Actions InsertImageAction and FormatImageAction both create an instance of class ImageDialog. FormatImageAction shows the dialog reflecting settings for an image currently selected in the editor to allow for attribute adjustments or to change the image file. InsertImageAction brings up the dialog to select a file from the image repository and adjust attributes.

How image settings are applied

When a selection is made in class ImageDialog InsertImageAction applies the settings with the help of method insertBeforeStart of class HTMLDocument. The chosen image from class ImageDialog is taken as HTML code got from method getImageHTML and passed to method insertBeforeStart.

When an existing image reference is changed with FormatImageAction, method getImageHTML is used to get the image settings as HTML code again. The HTML code is passed to method setOuterHTML of class HTMLDocument in this case, replacing the changed image reference.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86.htm0100644 0000000 0000000 00000004455 12114157751 023700 0ustar000000000 0000000

Stage 4: Tables

Implementing support for tables is a comparably complex task because there are no special objects for a table, table row or table column inside a HTML document. Each table part is represented by elements hierarchically linked, each element having many attributes. Iterating through all cells of a table column for instance needs a special way of handling for this reason.

To complicate things a little, there are only comparably limited ways to manipulate table elements in a document in Java. An additional challenge is to support table borders in Java because up to J2SE 1.4, table cell rendering is not appropriate compared to existing text processors when it comes to borders.

This stage of SimplyHTML implements support for tables trying to solve these limitations. In the follwing chapters is described how this is done in more detail.

Table manipulation parts to implement

Table structure in documents

Creating a new table

Enabling element and attribute changes

CSS shorthand properties

Manipulating the table structure

Enhancing cell border rendering

Changing table and cell attributes

Caret movement in tables

Due to the complexity of the topic the documentation does not cover all details of the resulting source code completely. The source code itself should be taken in addition to understand how the implementation is accomplished.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic4.htm0100644 0000000 0000000 00000010476 12114157751 023606 0ustar000000000 0000000

Stage 1: Documents and files, menus and actions

This stage builds the basis for application SimplyHTML. It produces a basic executable capable to create, open and save simple HTML documents. Stage 1 concentrates on how to build a basic application with a main application frame and menus. As well it shows some concepts on how to work with documents and files.

Following is a short description of classes making up this stage and what they do in general.

AboutBox - A dialog showing information about application SimplyHTML.

App - The class containing the main method. This class constructs application SimplyHTML.

FrmMain - The application's main frame containing all menus and the view of all documents in a tabbed pane.

DocumentPane - GUI representation of a document of SimplyHTML.

ExampleFileFilter - a helper class from Sun Microsystems, Inc. for conveniently applying file filters to JFileChooser components

ElementTreePanel - a panel to show the element tree of a document

SHTMLEditorKit - the editor kit used for controlling documents in SimplyHTML

CSSWriter - a class for writing style sheets to CSS files

LicensePane - subclasses JPanel for displaying SimplyHTML's license

Util - a class with static utility methods

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic140.htm0100644 0000000 0000000 00000004604 12114157751 023743 0ustar000000000 0000000

Stage 8: Paragraph styles and named styles

Paragraph styles

In stage 3 of SimplyHTML font manipulation was added to the application. Font settings are applied to individual parts of a document down to single characters with this functionality. With paragraph styles such settings can be applied to one or more paragraphs in one step too. As well paragraph styles allow to manipulate additional attributes such as alignment or margins.

Named styles

Named styles in turn are an elegant way to define styles that are frequently used and store them in a separate style sheet. Usage of style sheets was already part of application SimplyHTML since the first stage however this stage finally adds functionality to use style sheets to their original purpose.

Contents of this stage

Implementation of both paragraph styles and named styles is the subject of this stage and explained in detail in the follwoing chapters

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic173/0040755 0000000 0000000 00000000000 12114157751 023236 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic173/topic179.htm0100644 0000000 0000000 00000021242 12114157751 025325 0ustar000000000 0000000 Find and replace user interface

Find and replace user interface

To build the find and replace user interface in SimplyHTML the following set of classes is used that already existed from another project:

Class

Purpose

FindReplaceDialog

User interface and main functionality

FindReplaceListener

Listener to achieve search and replace over multiple documents

FindReplaceEvent

Event being thrown when a document search is finalized and another document would be needed for multi document find and replace

Above classes are taken from package de.calcom.cclib.text and encapsulate the complete logic and user interface needed to implement find and replace in a typical dialog. FindReplaceDialog only has been extended with internationalization supprt so that it can be used language independent inside SimplyHTML.

Usage of the dialog is very simple. Once added to SimplyHTML (or any other project) it is instanciated with a JEditorPane as a parameter. The JEditorPane is expected to have the document to perform search and replace upon. FindReplaceDialog then does all find and replace operations including document manipulation, user messages, state handling and optional multi document processing.

To control the state of the dialog FindReplaceDialog uses a flag to indicate the current operation in process. It can be one of

  • OP_NONE
  • OP_FIND and
  • OP_REPLACE

See the following topics to learn more about how find and replace logic is implemented in these classes.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic173/topic175.htm0100644 0000000 0000000 00000021376 12114157751 025331 0ustar000000000 0000000 Find logic

Find logic

In a design that separates functionality from user interface another class would have been needed for the logic such as FindReplaceLogic for instance. Instead the following methods of FindReplaceDialog have the main logic

Method

Purpose

findNext

find the next occurrence of a given phrase from start or end of a given document either in upwards or downwards direction.

findWholeWords

Find the next whole word occurrence of the searched phrase from a given position.

isSeparator

determine whether or not a character is a word separator with the help of character array WORD_SEPARATORS.

In addition methods initFind, doFind and find are used on top of the above methods to

  • initiate a find process ( initFind)
  • manage multi document processing, if applicable ( find) and
  • handle results of findNext (doFind )

Above methods are called by FindReplaceDialog when either the 'find next' button is pressed or when the next occurrence of a phrase to be replaced is searched during a replace operation.

See the next topic to find out more about how the replace logic works.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic173/topic180.htm0100644 0000000 0000000 00000003233 12114157751 025315 0ustar000000000 0000000 Using FindReplaceDialog in SimplyHTML

Using FindReplaceDialog in SimplyHTML

Application SimplyHTML uses a new action in class FrmMain to invoke FindReplaceDialog. Inner class FindReplaceAction instanciates a FindReplaceDialog in its actionPerformed method. Class FindReplaceAction implements interface FindReplaceListener. Whenever more than one document is open inside SimplyHTML FindReplaceAction passes itself as FindReplaceListener to the newly instanciated FindReplaceDialog. Methods getFirstDocument, getNextDocument and findReplaceTerminated in class FindReplaceAction hold functionality to implement find and replace for all documents currently open in SimplyHTML.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic173/topic178.htm0100644 0000000 0000000 00000015367 12114157751 025337 0ustar000000000 0000000 Replace logic

Replace logic

Similar to the find logic described in previous topic the following methods of FindReplaceDialog have the main logic for replacing occurrences of a given phrase:

Method

Purpose

replace

Initiate a replace operation. If no (more) hits are found, a message is displayed and the dialog is unlocked for a new search operation.

replaceOne

Replace the currently selected occurrence of the search phrase.

By pressing button jbtnReplace a find operation is initiated with a call to method initFind and above methods are called with an initial replace option of RO_YES. Subsequent iterations through the replace process are driven by the user through a selection in method getReplaceChoice which is called each time an instance of the search phrase is found and which can be one of

  • RO_YES - replace and find next
  • RO_NO - do not replace and find next
  • RO_ALL - replace all occurrences
  • RO_DONE - exit replace process
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic173/topic176.htm0100644 0000000 0000000 00000005325 12114157751 025326 0ustar000000000 0000000 Supporting find and replace over multiple documents

Supporting find and replace over multiple documents

With SimplyHTML more than one document can be open at the same time. The find and replace logic introduced in stage 11 offers a way to apply find and replace to all open documents optionally. The way to apply find and replace to multiple documents can be customized in addition to account for different purposes such as applying find and replace to a set of documents inside a kind of 'project' as delivered by a possible SimplyHTML plug-in.

FindReplaceListener and FindReplaceEvent

To support find and replace over multiple documents interface FindReplaceListener and class FindReplaceEvent are used. An instance of class FindReplaceListener can be passed as a parameter during construction of a FindReplaceDialog . Passing a FindReplaceListener signals FindReplaceDialog that find and replace is to be performed over more than one document. FindReplaceDialog fires FindReplaceEvents to the given FindReplaceListener to signal that it is through with searching a particular document and that another document is required to continue.

Feedback during a multiple document process

The object registered as FindReplaceListener has to give feedback to FindReplaceDialog during multiple document operations. Methods getFirstDocument and getNextDocument have to call either FindReplaceDialog.resumeOperation or FindReplaceDialog.terminateOperation at their end, depending on whether or not there are documents left to process.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic173/topic174.htm0100644 0000000 0000000 00000005416 12114157751 025325 0ustar000000000 0000000 Find and replace basics

Find and replace basics

Search direction and start position

The actual finding of a given text phrase is achieved with usage of methods indexOf and lastIndexOf of class String. Both methods return the position a given text phrase is found at inside another string. indexOf and lastIndexOf accept an optional position to start the search from so that the variations of these methods already can be taken to implement searching from start or from end of a document in either upward or downward direction.

Whole word search

Methods indexOf and lastIndexOf find any occurrence of a given search phrase as either part of a word or whole word. To restrict matches to whole words a character array of word separators WORD_SEPARATORS is used. Method isSeparator is used in method doFind of class FindReplaceDialog to determine whether or not a found occurrence is a full word.

Case sensitive search

Methods indexOf and lastIndexOf are case sensitive. They return a found occurence only when capitalization of letters matches the given search phrase. To do a case insensitive search method toLowerCase of class String is applied to both the search phrase and the text to search in before a find operation is initiated.

Read on in the next topics to find ouit more about how these basics are applied in the user interface and logic of SimplyHTML.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/0040755 0000000 0000000 00000000000 12114157751 023161 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/topic99.htm0100644 0000000 0000000 00000012343 12114157751 025173 0ustar000000000 0000000

Enhancing cell border rendering

A prerequisite to manipulation of table and cell attributes as described separately is to provide some enhancement to the way cell borders are rendered by Java.

Up to J2SE 1.4 cell borders are not rendered individually and there is no way to have different colors for borders of different sides of a cell. Either a border is drawn around all sides of a table cell or no border is drawn. There is no way for example to draw a vertical border between two cells only while the other sides of these cells have no borders.

Rendering mechanism

In general Elements of an HTMLDocument are rendered through the pluggable design construct of HTMLEditorKit.HTMLFactory. The idea behind this design is to provide individual Views to render Elements .

Parts involved in cell border rendering

A table cell is rendered by class BoxPainter which is an inner class of class StyleSheet. BoxPainter is used in class BlockView which is a superclass of class CellView. CellView in turn is an inner class of class TableView (terrible isn't it?).

To change how borders are painted, StyleSheet.BoxPainter needs to be replaced by an own class. TableView could be subclassed and its create method could be reimplemented to provide a replacement of CellView replacing StyleSheet.BoxPainter.

Enabling for individual border rendering

The constructor of TableView is public but unfortunately the class itself is protected so there is no way to simply subclass TableView to replace the ViewFactory of TableView with an own CellView. It is dificult to change the rendering while leaving the underlying classes untouched due to TableView being protected (I did not want to write a complete new view or view factory only to change a little part - a complete new table view would be hard to write too...).

Solution

SHTMLBoxPainter is created allowing to draw borders around a table cell independently from each other. Width and color for each side are drawn independently and borders of adjacent cells are adjusted so that only one border is drawn instead of two when the adjacent cells have no margin..

To enable SHTMLBoxPainter in place of StyleSheet.BoxPainter the sources of the superclasses BlockView and TableView are copied unchanged into new ones and only bring in SHTMLBoxPainter where appropriate. This is done by classes SHTMLBlockView and SHTMLTableView respectively. Both classes had to be put into package javax.swing.text.html to do so.

Due to class TableView being protected admittedly this is an ugly solution so any other and more elegant and effortless one is welcome and highly appreciated!

Highly appreciated also would be an explanation why TableView is protected...

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/topic96.htm0100644 0000000 0000000 00000006621 12114157751 025172 0ustar000000000 0000000

Enabling element and attribute changes

Before we take a closer look on table manipulation in the following chapters, some techniques used in application SimplyHTML for doing element and attribute changes shall be discussed here. Some functions are available through common methods in classes of package javax.swing.text and javax.swing.text.html, others need to be enabled by own methods.

Adding elements

To add an element such as table, row or cell tags to a document, application SimplyHTML uses methods insertBeforeStart and insertAfterEnd of class HTMLDocument. These methods accept an HTML string to be inserted before or after an existing element of the document.

Removing elements

Almost any removal from an HTML document can be done with method remove from class AbstractDocument. Method remove is passed the start position inside the document and the length of the portion to remove. For some reason, this does not work on the last column of a table (explanations welcome!). An additional method removeElements in class SHTMLDocument is provided as an additional way to remove elements working for the last table column too. This method does basically the same as remove.

Changing attributes

Adding, removing and changing arbitrary attributes all can be done with the help of class MutableAttributeSet and its subclasses. A MutableAttributeSet is created by getting an AttributeSet from an Element and casting it to a MutableAttributeSet. Changes to that MutableAttributeSet then directly affect the Element the attributes belong to.

HTMLDocument however does not provide a way to change attributes in such way. Class SHTMLDocument therefore delivers a method addAttributes for changing attributes of an Element instead.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/topic100.htm0100644 0000000 0000000 00000010420 12114157751 025224 0ustar000000000 0000000

CSS shorthand properties

CSS 'shorthand properties' allow to store a group of properties in one single property which shortens the way the properties are stored. If for instance a margin should be specified for an object that margin usually applies to a certain side such as top, or left. To store individual margins for all four sides of an object one can specifiy four CSS attributes for each of the four sides or the individual properties of all four sides can be stored in one shorthand property.

To store values in a shorthand property, they have to follow the order top, right, bottom, left. Individual values can be omitted, if some or all values are equal. E.g. if the margin of all sides is the same, only one value needs to be stored in the shorthand property which will be taken for all four sides.

Example with four equal values: margin:0pt;

Example with four different values: margin:0pt 1pt 2pt 3pt;

Shorthand properties used with HTML tables

For elements of HTML tables modeled by application SimplyHTML the following shorthand properties can be used to shorten attribute expressions inside individual tags:

  • margin
  • padding
  • border-width
  • border-color

Class CombinedAttribute

To enable usage of shorthand properties, application SimplyHTML provides class CombinedAttribute . CombinedAttribute models a CSS shorthand property by providing methods to manipulate and store four individual CSS properties in one CSS shorthand property.

It is used in classes SHTMLBoxPainter to render table cells, SHTMLEditorPane to manipulate tables and in SHTMLWriter for writing CSS shorthand properties.

Transforming CSS properties to CSS shorthand properties

In the Java languages all CSS shorthand properties are transformed to 'normal' CSS properties when HTML and CSS is modeled (in an HTMLDocument for instance). So for any CSS shorthand property four individual CSS attributes are created for an element.

CombinedAtrribute is constructed from an AttributeSet which may have CSS attributes belonging to a CSS shorthand property or not, so it does not matter whether or not the model uses CSS shorthand properties. When HTML code is to be generated for HTML file creation however, 'normal' CSS properties belonging to a CSS shorthand property need to be transformed from the model to the file accordingly.

Class SHTMLWriter does that by initializing a table of CSS properties for which CSS shorthand properties are to be generated. When creating HTML code, method writeAttributes filters out those single CSS atributes, creates CSS shorthand properties for them, and writes out these instead.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/topic94.htm0100644 0000000 0000000 00000007104 12114157751 025165 0ustar000000000 0000000

Caret movement in tables

As SimplyHTML now has all functionality to create and manipulate tables, it has to provide a way to move the caret inside a table conveniently. Most text processors usually allow to jump to the next or previous cell with the tab key while the caret is inside a table cell. Class SHTMLEditorPane therefore has an own section of methods dealing with this kind of caret movement.

Method getCurTableCell

With method getCurTableCell the caret position inside a table is determined. It returns the cell the caret currently is in or null, if the caret is not inside a table. This is done by using method findElementUp of class Util which looks for the next occurrence of a certain element ( TD in this case) starting at a given element (the character element at the current position in this case).

This method is used in almost any table related methods.

Methods getFirstTableCell and getLastTableCell

When the caret shall be moved from one cell of the table to another, it has to be determined if there are cells to move to from the current cell in a certain direction (previous or next). Methods getFirstTableCell and getLastTableCell return the first and last cell in a table given any cell of that table.

PrevCellAction and NextCellAction

Actions are used to actually move the caret from one cell to the next or previous one. Actions PrevCellAction and NextCellAction use above methods to determine the next or previous cell to move to and then place the caret into that cell. Both actions are added to the key map of SHTMLEditorPane with method adjustKeyBindings upon construction of the editor pane. NextCellAction is connected to the TAB key, PrevCellAction is related to SHIFT TAB .

For the case that the caret is not inside a table, both actions store the original action found in the key map for TAB and SHIFT TAB respectively. If a table action is invoked by TAB or SHIFT TAB thereafter and the caret is not inside a table cell, the original action for the associated key is invoked.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/topic89.htm0100644 0000000 0000000 00000027720 12114157751 025177 0ustar000000000 0000000

Table manipulation parts to implement

Table manipulation is divided into several parts

  • Table creation
  • Table content changes and caret movement
  • Table and cell attribute changes
  • Changes to the table structure

Implementation of these parts is distributed over different parts of application SimplyHTML. The following table is a summary of changes to SimplyHTML to implement table support:

Class

changes

FrmMain

actions needed to interface table manipulation functionality with the GUI

SHTMLEditorPane

table structure manipulation

appliance of attribute changes from TableDialog

caret movement inside tables

keymap and actions for caret movement

SHTMLBoxPainter

new class for table cell rendering

SHTMLWriter

new class with own implementation of an HTML writer (replaces SHTMLWriter of former stages completely)

SHTMLDocument

support for manipulation of element attributes

additional support for removing elements

LengthValue

class to represent a CSS length value divided into value and unit

SHTMLBlockView

extension of BlockView to support SHTMLTableView

SHTMLTableView

extension of TableView to support individual rendering of cell borders

TableDialog

Dialog for table attribute changes

DialogShell

new common base class for dialogs of application SimplyHTML

AttributeComponent

Interface to replace interface FontComponent of former stages of SimplyHTML

SHTMLEditorKit

extended ViewFactory for support of SHTMLTableView

BoundariesPanel

Panel to show and manipulate boundaries of a rectangular object such as a table cell

SizeSelectorPanel

Panel to show and manipulate a CSS size value

CombinedAttribute

Class to model CSS shorthand properties

BorderPanel

Panel to show and manipulate properties of table cell borders

As seen from above list, many classes are affected by table support in SimplyHTML. The major functionality however is in SHTMLEditorPane and TableDialog. Details of the implementation are described in the following chapters.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/topic95.htm0100644 0000000 0000000 00000011466 12114157751 025174 0ustar000000000 0000000

Creating a new table

Compared to other table functions, to create a new table and to insert it into a document is a quite simple task. In SimplyHTML this is done with method insertTable of class SHTMLEditorPane (see below). This method is called by a new action of class FrmMain which allows this method to be connected to menus and tool bar buttons, etc.

Method insertTable

Method insertTable builds HTML code for an empty table having one row with a given number of cells. The number of cells to create is passed as a parameter so a calling method can implement a function asking the user for the desired number of table columns.

The generated HTML code then is inserted into the document of the SHTMLEditorPane by inserting it after the current paragraph element using method insertAfterEnd of class HTMLDocument.

Generating HTML with class SHTMLWriter

Package javax.swing.text.html already provides classes to generate HTML for a given Document . Class HTMLWriter of this package is meant for this job with the help of classes AbstractWriter and MinimalHTMLWriter. Unfortunately these classes can not be used in the way it is needed by application SimplyHTML.

In stage 3 of SimplyHTML we already extended HTMLWriter with support to generate SPAN tags for character level attributes. To use the writer in the new context described here, finally it has been reimplemented completely so class SHTMLWriter now is a completely rewritten class not being a subclass of classes of the Swing package of Java anymore.

Reusing methods of SHTMLWriter

As SHTMLWriter writes HTML code to any output writer passed as an argument, we can use it for generating an empty table as well simply by passing a StringWriter as the target for writing. Usually SHTMLWriter produces HTML based on the element structure of a given document. The methods necessary to do so however can be used to generate HTML not related to a document too.

SHTMLWriter provides two methods startTag and endTag which can be used to generate start and end tags as needed. Method startTag accepts a set of attributes too, so start tags can be generated with appropriate HTML and CSS attributes if necessary.

Using SHTMLWriter in method insertTable

To generate HTML for an empty table as described above, SHTMLWriter is instanciated to write to a new StringWriter. Methods startTag and endTag are called for the table, row and cell tags accordingly passing a set of attributes having the applicable table and cell widths. The StringBuffer of StringWriter is converted to a String and inserted into the document finally.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/topic93.htm0100644 0000000 0000000 00000015734 12114157751 025174 0ustar000000000 0000000

Changing table and cell attributes

In the previous chapters basic methods for creating and manipulating a table structure are explained in detail. In this chapter it is discussed how to select and apply attribute changes to an existing table structure.

Structural changes vs. attribute changes

Structural changes to a table (insert row, delete column, etc.) all can be done in a single step. To add these functions to the GUI of an application, a single menu item or tool bar button is sufficient. A GUI for table attribute changes is achieved not as easy. There are many attributes each talbe element can have and it would be very tedious to change single attributes through single menu items each.

Most of the time, attribute changes are to be applied as a group of changes to a group of elements as one (for instance changing all cells of one column to a certain width and background color). With class TableDialog a new dialog for changing table attributes is created therefore.

Introducing TableDialog and DialogShell

It is called through new action FormatTableAction of class FrmMain. With class TableDialog the second formatting dialog is introduced after class FontDialog which is why a new base class DialogShell is created too. DialogShell has all methods shared by dialogs of application SimplyHTML thus avoiding code redundancies.

Class TableDialog

TableDialog wraps all attributes of tables and table cells changeable in SimplyHTML into one dialog. It partly uses components already used in class FontPanel and partly introduces additional components.

Common setting and getting of attributes

Class AttributeSet in package javax.swing.text provides a good way of grouping an arbitrary number of attributes and passing them between elements and components. For this reason application SimplyHTML uses interface AttributeComponent (renamend from FontComponent of stage 3) to define a common way of setting and getting attributes to and from GUI components via AttributeSets.

All components of TableDialog are implementing interface AttributeComponent. They are held in two Vectors, one for table attributes and one for table cell attributes. Whenever a TableDialog is created to reflect a current set of attributes existing for a table and table cell, simply respective attribute sets are passed to methods setTableAttributes and setCellAttributes.

Both methods then iterate through the mentioned component Vectors calling method setValue on each of their components. Each component then picks its attribute(s) from the attribute set and displays them accordingly. Similarly, attributes are returnd by TableDialog with methods getTableAttributes and getCellAttributes. Again these methods iterate through the component Vectors to call method getValue on each component returning attribute sets with the sum of all changed attributes.

Returning only changed attributes

All components of TagbleDialog 'remember' the original attribute value and only return an attribute when it was changed compared to that original value. This mechanism ensures only attributes being applied, that have been set through the dialog although other attributes were shown in the dialog as well. Without this mechanism always all attributes would be returned by the dialog regardless of wheteher they changed, returning only changed attributes avoids redundant storage of attributes.

Applying attributes returned by TableDialog

To apply table attributes method applyTableAttributes of class SHTMLEditorPane is called. It gets the table element from the current caret position and passes it to method addAttributes of class SHTMLDocument along with the attributes to apply.

Basically the same is done for applying cell attributes with the difference that a range of cells is passed in addition. Depending on the users choice to apply attributes to the current cell only, the current column, the current row or all cells of the table, method applyCellAttributes of class SHTMLEditorPane iterates through the appropriate range of table cells and calls method addAttributes of class SHTMLDocument accordingly.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/topic92.htm0100644 0000000 0000000 00000025151 12114157751 025165 0ustar000000000 0000000

Table structure in documents

As mentioned previously, Documents are modeled by Elements which are hierarchically linked according to the content structure in the document. To manipulate a table structure it is necessary to know how a document models HTML code for a table.

In HTML a table is coded like this

<table>

<tr>

<td>

<p>

row 1, column 1

</p>

</td>

<td>

<p>

row 1, column 2

</p>

</td>

</tr>

<tr>

<td>

<p>

row 2, column 1

</p>

</td>

<td>

<p>

row 2, column 2

</p>

</td>

</tr>

</table>

Rendered inside a document above HTML code might show as follows (display differs depending on style sheet settings)

row 1, column 1

row 1, column 2

row 2, column 1

row 2, column 2

The element strucutre to be generated inside a document has to be built similar to the HTML code above. Above table viewed with the ElementTree class in SimplyHTML would produce a view such as the following.

To manipulate a table or its parts, an application has to work on that element strucutre and its attributes.

Note: To find out or try how a document's element structure look like, SimplyHTML's ElementTree function is quite helpful. It shows a window as shown with a tree having a node for each element in the element structure of the currently shown document. All element attributes are shown next to each tree node.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic86/topic91.htm0100644 0000000 0000000 00000020370 12114157751 025162 0ustar000000000 0000000

Manipulating the table structure

Manipulation of an existing table structure is necessary for following actions

  • append row
  • append column
  • insert row
  • insert column
  • delete row
  • delete column

Each of the above table manipulations is implemented in class SHTMLEditorPane with repective methods ( insertTableColumn, appendTableCol, etc.). All table manipulation methods of class SHTMLEditorPane have following similarities.

Common logic

Insertions and additions of rows and columns are all done by using methods insertAfterEnd and insertBeforeStart of class HTMLDocument respectively. Deletions of rows and columns are both done by using methods remove and removeElements of class HTMLDocument respectively.

All table manipulation methods assume that they are called while the caret is somewhere inside a table cell. If not, they do nothing. As opposed to attribute changes the table manipulation methods are designed for being called with a single action command ('delete row', 'insert column', etc.).

Adding rows

To add a row, the current (insert) or last (append) row is copied by iterating the row and cell elements and creating an HTML string making up that element structure including attributes but without text content. The resulting HTML code is inserted before the current row element (insert) or inserted after the last row element (append) by use of method insertAfterEnd and insertBeforeStart of class HTMLDocument .

How it works

To accomplish the above functionality method createNewRow is shared by methods insertTableRow and appendTableRow of class SHTMLEditorPane. Method createNewRow uses getTableRowHTML of class SHTMLEditorPane to do the actual assembling of HTML code. Method getTableRowHTML in turn uses methods startTag and endTag of class SHTMLWriter to generate HTML.

Entry point for Actions

Methods insertTableRow and appendTableRow are as well the entry points for respective actions of class FrmMain to connect this functionality with GUI elements such as menus and tool bar buttons. They both find out the table row (current or last) by determining the current table cell with the help of method getCurTableCell. Method getCurTableCell is discussed in more detail in the chapter about how to implement a customized caret movement and key mapping.

Removing rows

Removing a table row is comparably simple. Because a table row is represented by a single element with child elements belonging to that row only, it is sufficient to just delete this particular element from the document strucutre.

To remove a row method deleteTableRow is called. It is as well the method used in FrmMain's respective action. Method deleteTableRow gets the row the caret currently is in and deletes it by calling method removeElement.

Adding columns

As opposed to working with rows, table columns are harder to manipulate because the cells of a column are spread over all row elements. To add a column, the same logic is used as in adding rows except that method createTableColumn iterates through all rows of a table working on the particular cell belonging to the column in question in each row.

Retaining table width

Another exception is that SimplyHTML adjusts cell widths by taking half of the width of the current column for the new column. In method creatTableColumn the half width is applied to the column the new column is to be inserted before. Then the new column is created with the same width so that in total the table width did not change.

Removing columns

To remove a column again the same logic is used as with rows but respective method deleteTableCol is the most complicated of table manipulation methods.

Retaining table width

deleteTableCol first determines which column to increase in width after removal of the current column. By default the column on the left of the current column is taken. If the current column is the first in the table the column right of the current column is taken instead.

The method then gets the width values of both columns and finds out the sum of both widths. The sum is only taken if the unit of both width values is the same (both percent or point). If a sum could be taken, it is added to an attribute set.

Removing cells

deleteTableCol then iterates through all rows in the table removing the cell of each row belonging to the column to remove and then adds the new width to its adjacent cell left or right respectively. To remove a cell method removeElements of class SHTMLDocument is used. For some reason I did not find out up to now why but method remove of class HTMLDocument does not work when used on the last column in a table.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic62.htm0100644 0000000 0000000 00000001276 12114157751 023670 0ustar000000000 0000000

Stage 2: Resource bundles and common edit functions

While stage 1 has been a comparably big step, stage 2 will be more concise. Here we have a look on how to add resource bundles to a Java application and how they can be used. As well we will add edit functions common to most applications:

  • cascading undo and redo,
  • cut and paste and
  • drag and drop
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic34/0040755 0000000 0000000 00000000000 12114157751 023152 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic34/topic115.htm0100644 0000000 0000000 00000016047 12114157751 025236 0ustar000000000 0000000

Discrepancies in HTML and CSS rendering

SimplyHTML tries to implement HTML and CSS usage as close to the specified standard as possible. Still there are discrepancies for rendering of the resulting documents when viewed in different environments.

This chapter lists known discrepancies, why they seem to occur and how SimplyHTML tries to compensate the effects, if possible. Any additional hints and ideas to the author are appreciated.

Results have been tested in the following environments so far:

  • Netscape 6.2.1 (SuSE Linux 8.0)
  • Opera 6.0 B 1 (SuSE Linux 8.0)
  • Internet Explorer 5.5 (Windows Me)
  • Java J2SE 1.4 (SuSE Linux 8.0, Windows Me, Windows NT 3.51)

Following is a list of known discrepancies.

Font names

Fonts are locally bound to the machine SimplyHTML is running on. When formatting text to display font ' Palatino' for instance it is not possible to predict if respective document will display similarly in any given environment. To make it easier to exchange similar font settings over different system environments, some standardized font names can be used. Common font names for that purpose are

  • Sans-Serif
  • Serif
  • Monospace

Unfortunately, the Java language has the name SansSerif for the font that most other applications know as Sans-Serif. As well Java uses name Monospaced, while other applications partly use Monospace.

Solution

This effect is fixed by mapping between the possible values mentioned above with class AttributeMapper. Class AttributeMapper is used in class SHTMLWriter to map from Java to HTML and in class SHTMLDocument.SHTMLReader.SHTMLCharacterAction to map from HTML to Java.

In the defualt style sheet of SimplyHTML this is solved by having several font family names with the one relevant for Java as the first, e.g. p { font-family:SansSerif, Sans-Serif; }. For some reason, however, this does not work with Java on Linux, i.e. having more than one font family name in the style sheet causes Java to not recognize the font stlye name at all under Linux.

Font sizes

Due to a bug in the javax.swing package, font sizes are rendered approximately 1.3 times smaller in JEditorPane than in any browser (bug id 4765271, see http://developer.java.sun.com/developer/bugParade/bugs/4765271.html).

Solution

SimplyHTML compensates this bug by providing customized views in class SHTMLEditorKit.SHTMLFactory. The views adjust font sizes before they are rendered so inside SimplyHTML fonts are displayed similar to as they are displayed in web browsers.

Unfortunately this does not fix the bug for cases where HTML is being displayed through Java APIs such as JavaHelp. So a bug fix from Sun to become available soon would still be highly welcome.

Table cell borders

Up to J2SE 1.4 cell borders are not rendered individually and there is no way to have different colors for borders of different sides of a cell. Either a border is drawn around all sides of a table cell or no border is drawn. There is no way for example to draw a vertical border between two cells only while the other sides of these cells have no borders.

Solution

SimplyHTML uses customized views to establish individual border rendering for table cells. Unfortunately this does not apply for cases where HTML is being displayed through Java APIs such as JavaHelp. A fix from Sun to become available soon would still be highly welcome.

Table cell margins

The CSS specification describes CSS attribute margin and its variations margin-top, margin-bottom, etc. as a way to set the distance between two block elements such as two paragraphs to each other but also for elements such as a table cell. However, a setting of margin-left:2pt for an arbitrary table cell is not being rendered up to now in any of the tested browsers.

Instead, only HTML attribute cellspacing is rendered so far, which is applicable only in the table tag (i.e. affecting all cells of respective table). Therefore specification and rendering of distances between individual table cells or for individual sides of a table cell is done correctly in SimplyHTML but it will not be shown in a web browser as it is shown in SimplyHTML.

Because SimplyHTML is built around formatting through CSS attributes, the cellspacing attribute can not be set for a given table in SimplyHTML. Attribute cellspacing is rendered in SimplyHTML, when contained in an exisitng HTML file.

Solution

There is no solution for this effect up to now.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic34/topic71.htm0100644 0000000 0000000 00000006360 12114157751 025154 0ustar000000000 0000000

Using interfaces

Interfaces are a good way to define rules by which objects interact. If one object likes to communicate with another it has to have a way to determine whether or not that other object 'understands'. If an object can determine from which class another object was instantiated, it can expect or not expect certain methods being present.

If an object is to implement an interface it has to implement all methods the particular interface defines. How the methods are implemented, i.e. which code they actually hold, is up to the implementing object. A single object can implement many interfaces.

Example: SHTMLAction

In application SimplyHTML this is demonstrated by interface SHTMLAction.

In the process of dynamic menu creation method createMenu adds an SHTMLMenuListener to each menu. SHTMLMenuListener is used to update all actions to reflect the up to date enabled state prior to selection of a menu. To be able to do this, SHTMLMenuListener must determine, whether or not an action that is to be updated, actually has a method to update its enabled state.

Interface SHTMLAction is defined so that SHTMLMenuListener can do that. SHTMLMenuListener checks if an action is of instance SHTMLAction which it only would be if it implements interface SHTMLAction. Only if an action is an instance of SHTMLAction, its update method is called, because otherwise SHTMLMenuListener can not be sure if there is a method update in the particular action object.

Another advantage of the interface methodology is that objects of any class can implement interface SHTMLAction so that an object instantiated from class SHTMLUndoAction can be an instance of SHTMLUndoAction and an instance of SHTMLAction too even if SHTMLAction is not a superclass according to the class hierarchy of SHTMLUndoAction .

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic34/topic84.htm0100644 0000000 0000000 00000005253 12114157751 025160 0ustar000000000 0000000

Using listeners

Listeners are referred in many places of this documentation probably already giving an idea about how and why they are used. Anyway the listener concept should be explained in more detail here.

Example: Font manipulation

In stage 3 of SimplyHTML many classes dealing with font manipulation had been added. These classes mostly are GUI elements or are related to GUI elements in some way. The interaction between objects during font manipulation demonstrates the importance of proper design in handling such a rather complex topic.

In class FontPanel for instance several objects allow changes to font attributes while all attributes are reflected in another object, the sample view.

Listener interface instead of hard coding object relations

Instead of hard coding a relationship between the font attribute selectors and the sample view component, each attribute selector defines a listener interface. Whenever an attribute is changed, a change event is fired in the format that interface defines.

The sample view component in turn implements the interface by having a method valueChanged which is defined in the listener interface of the font selectors. The sample view component is then registered as a listener with the component defining the interface.

Having functions to handle calls to method valueChanged, the functions need to be coded only once and only at the place they belong to - the sample view component in our case.

Conclusion

The listnener concept is an elegant way of letting an arbitrary number of objects dynamically interact without having to hard code relationships between objects. By defining interfaces and listening to events a clear separation according to object boundaries is accomplished.

By implementing code reacting on events, redundancies are avoided and objects do not need to 'know' about how other objects have to be changed by own actions.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic34/topic33.htm0100644 0000000 0000000 00000007142 12114157751 025151 0ustar000000000 0000000

Avoiding loss of data in the close process

The process of closing one or more documents technically is easy to achieve. However, making sure that changes to a document are not lost when it is closed is a more complex task.

Step 1: Intercept all close actions

Because a close operation can be caused by different actions, it is important to take into account all occasions that would cause a document to close. So the first step to take is to ensure that each and every possible close action is intercepted by a check wheter or not it is ok to close that document or what requirements are bound to closing it.

In the design of SimplyHTML proper handling of close requests is ensured by having all methods to call the same action for closing a document: SHTMLFileCloseAction. Having all related functionality in a central place and having all other related methods to call that central functionality makes it easier to implement exactly the correct functionality and makes sure it is implemented only once.

The close actions to intercept are

Step 2: Ensure documents are closed only when conditions allow it

The second step is to ensure that a document is only closed when conditions allow to close it. Before it closes a document, SHTMLFileCloseAction tests in a central place, if changes are to be saved for that document first or if a save process currently is going on which finalization has to be waited for.

In this functionality the logic is placed to notify the user, that he is about to close a document which contains unsaved changes and to ask the user for a decision whether or not the changes should be saved before closing.

Step 3: Testing the result of the close action

In cases where an action has to follow the close action, such as when the application shall be terminated, the exit action needs to test if a document has been actually closed after it requested to close it. Otherwise, an application would terminate even if the user opted to cancel the operation during the close action (e.g. when asked to save the document first).

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic34/topic151.htm0100644 0000000 0000000 00000027031 12114157751 025231 0ustar000000000 0000000

Using Java Web Start to launch SimplyHTML

Java Web Start - a technology for simplifying deployment of Java applications - gives users the power to launch full-featured applications with a single click from a Web browser. Introduced in version 1.4 of the Java 2 Standard Edition (J2SE) Java Web Start allows to download and launch applications, such as SimplyHTML, without going through complicated installation procedures.

Benefits

The following benefits as listed in the Java Network Launching Protocol (JNLP) specification result from using Java Web Start:

  • No installation phase: A JNLP Client simply needs to download and cache the application’s resources. The user does not need to be prompted about install directories and the like.
  • Transparent update: A JNLP Client can check the currently cached resources against the versions hosted on the Web Server and transparently download newer versions.
  • Incremental update: The JNLP Client only needs to download the resources that have been changed when an application is updated. If only a few of the application’s resources have been modified, this can significantly reduce the amount of data that needs to be downloaded when upgrading to a new version of an application. Furthermore, incremental update of individual JAR files is also supported.
  • Incremental download: A JNLP Client does not need to download an entire application before it is launched. For example, for a spreadsheet application the downloading of the graphing module could be postponed until first use. JNLP supports this model by allowing the developer to specify what resources are needed before an application is launched (eager), and what resources can be downloaded later (lazy). Furthermore, JNLP provides an API so the developer can check if a resource is local or not (e.g., need to be downloaded or not), and to request non-local resources to be downloaded.
  • Offline support: A JNLP Client can launch an application offline if a sufficient set of resources are cached locally. However, most applications deployed using JNLP are expected to be Web-centric, i.e., they will typically connect back to a Web server or database to retrieve their state. Hence, many applications will only work online. The application developer specifies if offline operation is supported, and what resources are needed locally to launch the application offline.

How Java Web Start works for SimplyHTML

With stage 8 of SimplyHTML, the application home page at http://www.lightdev.com/dev/sh.htm holds a link to a .jnlp file which in turn specifies all details of application SimplyHTML (required files, descriptions, etc.). When the link is clicked, Java Web Start is invoked on the client and the application is loaded down to the client. Once loaded, SimplyHTML is launched and the application can be used immediately.

No manual installation, no copying of files, no command line scripting or desktop links, no compatibility checking, nothing.

Users can choose to always start SimplyHTML through the web or to download it to the client permanently and work with the application offline.

How it is done

To achieve a Java Web Start for SimplyHTML a .jnlp file is created as follows (with codebase below having an example entry)

<?xml version="1.0" encoding="UTF-8"?>

<jnlp spec="1.0+" codebase="http://www.lightdev.com/dev/">

<information>

<title>SimplyHTML</title>

<vendor>Light Development</vendor>

<homepage href="http://www.lightdev.com/dev/sh.htm" />

<description>SimplyHTML text processor for HTML and CSS</description>

<offline-allowed/>

</information>

<security>

<all-permissions/>

</security>

<resources>

<j2se version="1.4+" />

<jar href="SimplyHTML.jar"/>

<extension name="Java Help" href="javahelp.jnlp">

</extension>

</resources>

<application-desc main-class="com.lightdev.app.shtm.App" />

</jnlp>

A similar .jnlp file is created to deploy the JavaHelp runtime extension (file jhall.jar). The .jnlp files are copied onto the web server along with the signed application .jar file. Once the application home page has the mentioned link to the .jnlp file, it is ready to be 'Web Started'.

References

A very good article about how to 'Web Start' an application can be found at

http://developer.java.sun.com/developer/technicalArticles/Programming/jnlp/

A perfect explanation of how to obtain a certificate from a Certificate Authority and how to sign own code with such certificate can be found at

http://www.dallaway.com/acad/webstart/

The official Java Web Start product page is at

http://java.sun.com/products/javawebstart/

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic16/topic34/topic50.htm0100644 0000000 0000000 00000005122 12114157751 025144 0ustar000000000 0000000

Using layouts for proper alignment of visible components

The Java language holds a powerful mechanism to properly align and size GUI elements within a frame with the Layout concept. Other than by stating absolute coordinates at design time of GUI elements, layouts define a display model relative to certain rules.

Examples in Class AboutBox

Class AboutBox of SimplyHTML is an example of how to apply the layout concept. GUI elements such as labels or images are placed onto panels. For each panel exactly one layout scheme is associated by which the panel controls positions and sizes of it's contents.

The panel textPane for instance uses a GridLayout with one column and six rows to arrange the labels contained in the panel one below the other with a gap of 5pt between each other. The contentPane of AboutBox uses a BorderLayout to align all other panels with a border of the contentPane. buttonPane sticks to the bottom edge of the contentPane, northPane to the top edge and so on.

Conclusion

By using layouts, the GUI elements are sized automatically to fit the resulting scheme. Most important, they all are resized according to the rules of respective layout when the container is resized. By defining layouts, the developer does not have to worry about how the components need to be sized and resized. Only their positions relative to each other and relative to their container need to be taken into account.

So the layout model rather follows the original intention of the developer rather than forcing him to transform the design intentions into coding models over and over again.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/JavaHelpSearch/0040755 0000000 0000000 00000000000 12114157751 023220 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/JavaHelpSearch/POSITIONS0100644 0000000 0000000 00000154532 12114157751 024541 0ustar000000000 0000000 ƒžœœœžœœC¬V6]}Zñ”PcUUHeÿÿÿÔAXÖTœ‰*ÐÒ¼Ñà¶Ô×>K¼ }GÜ´3_¦û'¦ˆÁ†¤¯4E&Î   +Ñ/âbc ¯Dž(B½숯DŸ82þ")+Ñ.ŠôI”d"Jó4Rwy`Š [Ñ#‚ =¹¸£È‚$(ÿÿÿý ð˜ÚT‰jÚÓ=1,á]\™fÑÔxrèO´0'< Z¬% ÿÿÿý H 3 1ÆJ+!­½¸•3j…ÊÈ»'¤{ÿ‡°B£×Jõ! i7M•6ÐD+lO¬Rjåÿÿÿý„€;+iEÓdvöŒN)•¨ÍmÃQn烇AÄmà(cµjÿÿÿý²À5/R‹¦³ú59¯Þó®çƒ+0@¨j˜NÛ¢ÀÿÿÿýÝ=/SªÉ–¿còΗ]s{~ÏûÀc"€ƒ/(uÔ%oEz¬ÿÿÿý‡`!‰*Tž zÎÓ=ñlÙSÞYFßÖ¾gáD¤ÀÿÿÿýOÀ82 AÇHË™¬¼X™2ª}²q³:GŒÇ„‹ˆpò#ÐÿÿÿýÊÀ åiä–­lÂòÂ<¼R-ߎš‹×xÕýÞÀÿÿÿýö€ Ipt¤Ë+;:LM;U·NQ»ôžwÑF„ ÿÿÿýbœ 7-ŸÐÄ¥6Ð'>#ÓZµÛ9¶æ>æ@ÿÿÿÿ@3 3Ç*jµµÜW<Õ¾M»s°ƒ¾›žîLƼ÷Ñ¢%´(P@¾ÖÓ‘ R"¿ªÑÑ21-<¦ É oBw™~ú'ó^2RQdÀÁX‘£\“Q!1šä‰†«‘øê™ÂtžIIhµNÐeÀ˜ŸÿýD–­­bÚÔO\[4Ö¡Å÷ž;oUæú«k* dbéjeÀ÷КÝB¿|0ŽÔÀÿÿÿýŽeyЕ+ƒ&=1DõRÝY5ç·tßÉGŸÁqüèöÆå¥°ÿÿÿýÞŽÅQÜ“-­Sá,Ï„¹"5Wrå»_gṸBÍËJ€ÿÿÿýß@ ñ æ%%/·:ÄÝEÎFÇwŽx ÿÿÿô¤ Š`¥èµÃÇŒ/‚|§™So.hÔƒ¶i6a/Ÿ–ùa–aŸ–ùaùùùùéééùéééééééééùéÙÙÔ1" ê7G1N’KÚµ¦F8‚ƒ)~jÔ^y¶:Ï^3Ñ51Ù,WN¾€ÂÀ³ Pªªªªªªªª@a)ši)™6"åˆ@ w‘zãц§LadC“>¡yïØŠˆ ó$*ÿ[ vA©ã 8RRôDc•Æ‘ä~È‚OŸT‹¨þêOdF­uæI®@U¤Sb‘x•Å *qˆASbP𸆖oŽÐ…¥”);a’h²!”Ba ñ(°ù»Ù7zÊPm÷µxÔ+™`ˆŒ_V1Ã2ÉP@³••×W/ÅÚ:çƒÆŒOH 1ô`ªE•Ë.Æ8> †ŒÊŠ÷8à|) AVˆ›@÷%Ôa¬›ž®ëšR‚hºš´Îñu‚:?æÆy ·"íÊÛpµH¶6¬D3L±ðC{îNna¸¾¢0³M‹Æ¢5ª’LÆ+Qt™ôÈ'è¢ÃÊc xÆt2—ˆñ†6 4 9¿’²ûZK|SOAòlìX˰µ‰¬K2í­Ž+]*ßÖj»4Ï4κIú:«ê¥M'©›Ýkƒ:º´Ú›¾¬¿‡U¢ž#Fž="AmÔ²šoLÙ—RªÑ»½Öeõ©{—ŠfΚyµÍ¾ìkc3ì&û†NÒ]ºl£{[Û•eª§XºçV¾TÉ)e\þ§•^¬=ojTÏ‘¥;šBº©KNÚµTj´S¨'Eš4œ-À`ÇBªêqcºÕV¥!n±–9FBÓ¤1HÖÝírÍ“¦È¨-„输GÒèÉšÉè¦R–›MŒèÓ›¾ˆýÉl$Û`³ÃE˜' n%.¡¿ØNˆøðŠ(îˆûHµ ž% 3°dƒ ’ƒMâ¾A£ [ Ù £}i] £3¨"³`h2Ç `ð€‚„Ê‚„ÓĘÆðGb¦ïæ˜r(žr:#è Ñ€‚‡.XÙV¦„{¦žX I§úFç1 øËà¢X"†|=>Îq@ÇTŸ§#v¢>ŽLH¢?'a:#ïeß»éQä#ÆJá=¼uc:#ÐÿÿÿýV„0Å”ÃRD¬cB¼CÌ=C\.DlC{D–h¦âK&ªD¬Z0é>èi Ä$žÉ""†(ƒˆž;(¼¤'‡­ KbÙÈBlƒX(I%b˜æ¡¨Àˆ J°ˆ„«l‰RD¬òD«:‰š:b"7‚ 4%(YX§òD©Ú!¬cC<CÌ=C\.DlC{¸(Yw&±_4V‚±´Ø±†ÀÿÿÿýgB 0†œ%!k h»Ã¢'0ª‡\7"[ è/þñ®Z ÙšBÙ⢠jBÙ¿K½k8 ¥C JBÖ؃å¨íGjÑÂQ’%{#§ßD`‡2¡– œ‰;“½OsÃŽ¥„㉄jóÐÁ†@d2¢Y2¬yt@¤6Ì…L– Ia‡2S‚ÿÿÿýè(Ê”qÖ$bÅ(Ê”ËB½BMRÝClGC}}†Hà4+Ë‹BK¨L1‹i54+°aÂÓ‘éNøn>š}~Åã)©&ÕlÖíkå‹$QmžÍÅfÜmž›FÆ×nØhÞß醑>ë¦$7½› úÖêß †©½-xRj“²;K¹™¡§ê𻡻dɤ N¤[.Q‰¸;E¾Úh¹BY™ÛË­þÅ¢`’¹Ë‡É„‘ŒÁlFŒ·¢Ó…¶z†ä§9íÓªüäÖ¾zk™‰žµ»+¨æTgØTos]NšÖ–X$ÚfúXúŒ1øá0.Ïx_oÖ¹ ‚í0Bêÿÿÿý¨Š12œI"+hûÆ£1 ‡ÜC#{éÆUùâÖqʾV1¤DE Êôšêx¨Nt¡!Û¬ž¢0Aó å^ìŒ.Q Y‡±Ñ:ÊŠŠºQÞŒ|Ðÿÿÿýh@°ÆiŤjÉ4;ÅDyÆÌ]GrÆÜ®fì‡Zþ±…œaF›¿€™áOzäaæ JZ#öîI¿º1Vˆ²¶bÇÀϬ‚‡zækÿÿÿý9(IÐRx±”š´åq˜UA®y­‚ô'øA™Z"–ªR‡li‘øèù0q֊ņB*&¿  X˜ÍŒž=úY²`0î—·28.ÑS:ÌV°õ¿,5a)XÄÿÿÿýêƒ)¡Ã°–¤¼Z!3¨…ÃÞ%íŽô/ÇY…²åD; ¬€š©‡ŒÔ$>áˆg ÃÂ"%ð†Ã¢B!\ÿÿÿý—€ƒ(¦dš±Ú£ÇŠsB¯\öŸ“vÞVì>äðÔ¾ ÀÀ7[—ÁDÌñ‡@Á‹§a)À€zB@ƒŒ°ÿÿÿýYàޏGË$:Ì4;ÅD<ÆUýÃäñÆœ.Ët7Òœ-S‡² ³’h.±ÑŽ›ð£Fdiœ.â->oF„•og´Üš ½GdfVd ¦\eªÄí‰ÂîùÂÕLU.ÆÕ6ò†Q°IQÔõÛœß=2›Ì‡Œ,Ó-JI.µz .¶Ð2%¥ C È·>Hí=RpdÐT0b~T:‹DÄ?>ÊåK!dÒÁû21 ÷Xñbgç ÿÿÿý€ @1”"« h+Äâ0úˆÜK#{ 诟äȬƒÝ@B»ONˆ2¾,oK)j†ÎNÉ`¢!dÿÿÿýÁà YCœÚJ-J½DGÌ¡ÓT-ìå†Ät—šyZðÂÖA®h…âÜvF›f•û% %ÃÖ°ŒˆÀânmª€ÿÿÿýïÀ 1‚œa «isäâ!2š’]ƒ »“é_Ë©òab> ÈùCœí˜%…ÁÐd-‚G4ïȨ¦Àÿÿÿý(ðÃ(‰Æ’*´6†ôqQ´Ö ¦¢5¤Nz5"•íK_œº@Ö×m@Ùçè€Ö~v 3ØMˆ cë¸C ÿÿÿý à°Î‰Ê'ÂÓµ«ÌDôÒ Ç%¥FJK]¾FîEÿŽëhî&›fµ!…tôÀþr‚2ÔNì6‹ ¬Ãl‰Bâ³ÿÿÿý·èHÄ—HD²(‹,SG=EÌ•OÜÖHlçD|íìØ<\a_B) †XxKΆ ðÏ.1rÿÿÿýÉÈÆ”IÕ%ZÅ4+øÄdÔShÖjlOû_Úß-Êo°–gn;Ç‘™öa¶Ì;D0.ƒúÀÿÿÿý܈pÆ‘å¤2̵3ÄD\Õƒ(aIÝd]UÖvÆuß¼>õ“SËgSÿÿÿýýØ@*2J©Ò)¡p4çñ'Ý\¹™+mt¯ JÁ? "xžòkïN.Àÿÿÿýa Ã0¨›U+Y«¤—–¼ã\⦽©²VëénÁ°¾G0xõ‡ôƒä¯,ÿÿÿýcIã8ŒÉ䪳ÛCÕŠsR¦Ûg›êôï7ÿ$F2ÈÿÿÿýÊ® ŒqiƒhÂR­›Y¼\_ÌEJ\†blC{!Æ;¡îÁ¨ÿÿÿýh£x¦$°¸[#÷Žón¤]LÛ²±óÈ1t2@±'ÃXÜ"yÿÿÿýjÃ0œ'Y«-¬<¸¥.Z\Üì½³ú¯Õà4¾à]±Øÿÿÿýl…<›fJ+%ª¼Xë3+mÊÈû'«ø7·ÆV9Œ@ÿÿÿýn<Cd²…K1·>¨2k-ÐOciu?~ mÿÿÿýpub“&$NZN[-)åE‰ÇUî¾T¾šëÚ<’¾~›5€ÿÿÿýsHbȇ”¢´šKÔ‰Ó¢§\Ì¥¯®Jx`_@ÿÿÿýuØCXš&I«±d4ç©f}L¿ygõw8¡ANŽÞÀÿÿÿýwrc(¦œ¤Ö¦%ˆ“é‰Ó9äT‹¥žöGHè¾u€ÿÿÿýy!#8£žt´¬æG½èiÊÒ¤Þ ™¯.Yü®@ÿÿÿý÷6  YeZR,iÍ´[ÃE4ñ’Ñ ÛQž:‡ô´R‚¶08:¥ ÿÿÿýˆã|¬œt¨¹’»dyqžz¾ ¹ KRd}·:¯Œ¥ÿÿÿý>@‹q-ž‰š¾²SÑM…Ïœko#zÿÿÿ@°Ã1Tv¤k¢F³5®å\“”ÅlƒÍ®ž–¡DkµWz瘜Ïe„gò‹Õ®\1^šsøƒ£¨â_ ”3Ñ•Ó}#1W@¼]?Z…g2ÝñS>&åD ­s%}¡Ñç‰Ñ].ˆõ¦‰ó…Œ‘¶Ú¬Ä…w2bp%0‹TîÛÚÍû7¥¼ox ”GhìHVç–Ðÿÿÿý`È–‘Ç'2Ç´;âÄôÇTUÃfÄô'ëºÿÿÿý€61I hT ²ëÈ‹ó"¬\œ›¬>GÖ_@Öꃽ¿îžžaTg¿\ð=6A³r¬2òæÑ×Þ iS ”!rÎ m1*ºñô~!ª˜~I<Þ³*o²BZäû´ËoÌøþï[L§8’ÙÒó¦r•>œT®þSDK¶´\"´% GÒjÅÉ9{bñÿÿÿý19ŠIèF‚#–*Xio*)N“l|­©.vâGÞE’îä‰Ì¶ÍLt!€ÿÿÿÐoÀv3ËÙôK‹˜€ˆvÌ$&…G»€ƒ½ÞÞžžžatÆ*—¶ªÆÑI €z!,–ë4"Ûõ#Êw<§[šLH<§B`žÄÉSºB2t“+@+;'B~g #â×Ã0 n<™T•]iÛu~ôc}Ñ+ÄGuH©#ƒ>!{V2[KI騴¼–ÓõÊg³)SÄNl™-Œ ¬î—™ˆ¶ë<Ñ›†^É>KjÙ{Ø“?96ãåL˜ºÎH ¸j ºZ4Ø`…»Bdȃ*ÁBRÿý7Íœ3Jv”.&zÆië´Ôë©•7_Iûk´TËh’6Ær¢f]V¤²ÆfÑ×là Hÿÿÿý„cܬ+dó67 u¢ŒÓ©,éÆk§—vîÇé§ëÜ&hØ”Ò9±úi›ã@ÿÿÿý+*‚pà)!Pd¼tÓ á #=ºà°BsÈŠ`2LzuÒˆZvŸ¹7dtÝ<0”H‡íÉ»úñ¼ŸéÖùÖƒÝíž–éuÏ“h«>šƒ»®‡È¥²_¹4 Ö'!ØU„Æh©wlsD#6Øöç0Ør–abŸ¦§*mßd†Ä”¯nØ}ÅëÈ{¶rĪ]sÈmª¥Û7ËIµrSm7ëÂ_Úö}qØZœ©×p´› ¢'li–€õ›pÃå¼i Uí(0kª{~ºßÙ,ƒ„Cÿý3æÏzéÖïuå¤ÑUS¹IËA­Üy>Þ@"gºóKˆëgk:^PŽÎŠ­è91‰ÿÿÿ^Y4MŒ´i²„+<øÀ ЬÂÉäU Nåf]£Öíš ŒýÌu€Ö;‚ƒ¾Î゙žžžau÷›±yWx°ŒÓà˜#±‚¶á{“¿K%£YQ62KØÈÀô–æÖ"þÆIo{ë[PÙkkná5.&ÆËY5/äø“–6àéÚ“‹'m*—ž·¶éÞzÃÔ4RÚ×Õ¶ËW6æE°•Ñ…›«e—¾w>Æ\gï^F¨ß'§]ÄŒÝúÃ$fëÛoG[û8ÑÖ™¦¹Ï8;’ôpLLÂûfóϽñ~â­âc"m%%eBYµ!ìâ]ÀXƒ`ƒáIø F#Æõ3P»‚|ôêxÌ$·xnÄ"¥H‡„öRï’ùlÕ»eÈ­. y‡v]R¸N´×WãQnÕÀ¯Šôê-Ÿ¹ŽŸi«Ó6´ÖðL²C"”:-Z溩ŒM„i?À©7Ù:hÜ…n]Ù±¾‚0é<#Ÿ±ìmÙ½y±¨o­ÓN>ù~Wûp(ýåìþ·ÆÆýÛ§¢› ”|j °š~X ½–d„3€æc0Œ$ÈŸÿMZÅ9<òIFQQ?f¦[‚TùÏkOž¦³–]›ÿâ[ò¼™•Rœôd¨®ÒzŒ áNÓ²§£ÿÿÿý¥‡(©,è"õg5Ç•!È,È©×3mW¼õûÄòsŸêU" €ÿÿÿýà@X8‰Ši’“SJxñp-V–2¦³ajË(eMjyŠUëË»ó«ÏÒ=Ø Ü­Ž5œKu@ÿý¡@A¨u„¢ |ŠZh[KªeÀ±Ùƒ¾¿ÏïßžŸaD7E¯u%ji^aÛÅë@¶ÓÙšºœž½Q"Dˉ䬙 ñ"\B–f ( qEKᜢ¦â`rB¥ÂœQVèb(ª`´‰ŒÚvŽÍFº?®|ngºB£[¿ÕØT†¿Gãwñ¿Q­‹‰oÆ›éi;éË»;*JVdNdnÆóF]äx3 å q8y Ï:bÆ$’óR˜í–Ó-9S«;l€1‘-_V“h‰æbu-;JÙÖ/&Å |+‰yº˜¨ðˆ…Š·þŒsÙ¹´Éº«Ô³²¿ œ*ûŒÛ¥nŽ’´“L:Œ…;¥Êç°ä#§"6N(‹z¿¨HªèJ54ùSM|ãªgƒLæ »pÚÓ?‰B …¢’ÑvŒFºR}GÙë!kú`RØ÷Ì!U&GÞÉ]Ö]îFJé·‰ æh¦S¶áKeºKƕҌ“…Õ±ÖÜçêšHgbêÆLШGifìòÉÔ?v›<$òµ$Î2 ÀÉ­ ~Ù ÀÿôÒœ3 uÉ ¨ƒ®ÞÎÞ¿¿Ÿ—!Tp¥Ñ¥¼Nƒšø¶íhˆ æÉº&ëµÆÌãF£]ö¡Q¯ãȆ!®0ÞÈã£]Œ¹žê5¿õ ’š'ʪ+5ÔkÁú¾¿·ãqSÂäŽ(r¼‡ºQ‡‚ –Ûpsi9µŠ©]³Ÿ5x:åM„’ñ¾óÆJì1ö@Ìþ˜6gù±‚Šâ2Oƒ-ƒ•¨a˜™¢´íƒ¹#·0’6c ÐÌ®§t®›”œ4’næ±…e(!ÐrNŸ’lç\áß̹“VjêÖ Ò›Â’lá!9IɺïÔ”£Ó«²_$Š9ðþëÚnVzAcc"l-[iÁbõÔç‘a¥ÅÙx,dp]fŠÎæMˆ«ûOQðí×0úî­1û¯|±$æÃè$‰³³^0&ùåw‘[N˜+~7ËûÚ5l§š=cr>¬&±Lñ¡µ w¢¾ 1! Æ*Z–KDÖ7‘[t»*€Ý±ÌD§©9™™•–Óm“5)kWñmwù¡± òr]ý­0£T0—p–6Ìʶ›6䛼hͤóSšÿÿÿýH Yr¢T«XÈûîDDÏÕ%Œ‚.kgȇÚâÖVÁ‚eÖT˜Å[gt9É»æRžçjFq0HÆ!²tË (-ít3ÙÇ2è¶•ÀM-!þ¢•sjâ”ÐÍ íŽ­‰ùÑA1„.2ˆ1»G§¨BQÉŠ´wÇ‚P[Ra–ê™Å݆[n$ãКìÇr.7k5¶Zc)Ûˆ2Œcî¡øMÒ"¢otUÍð†Î3¤˜©m»)tvçl:QÖ„ƒÎîÏžŸeu³wc¥J³F§H96@÷kHµÞdl SÞ¼ϩÅâ.Lj±‚<¼-$ñæAZ£¦­³mz-t. Œ¦HoMXÛcne©’Ýèµ·VzϨÛ5p¬mŽV–1}Yg!1­6@ŒÝHäo9I7Šˆ‘`¦f*ï‚ùï Æ9u¯DwŽ£ð’w(ìéÀ‚8 fGÓH‘׿5wýøç ÿõât´³{Í ²íK×aã8> 6Zßîí7`•}¥k{³}åsZ4ìßidÚë%¯i½$ô³ªK2I¿Œ6ú!€v©ÆF–Ô3”"ݪV€ÿÿÿýð$8ÊQ÷‰Ùh´wíæmJ»×›v¥l{›XÿÐbø HN„9‘‘ju‚ƒÎÞïΞŸeTk—ùC£±v"Nˆ…Å©kI—вBÑ æœ"žfÐ!>!´Å\Û&5ÅË%^𻋜n.]¸¹Öâã*йòí]-¶þiák;§-ƒh\—zUÌZ1pÛ“·Bâçë‹•îvÓê1P†40‰[g¾©÷µ>²$AZl§I;æ‹>.W|©6¢0ÖÛ‹qnËÖRû‡åÏu‘V•á ¼­EïÍ2Hj£!¬n‹xmðZAŸ9eÚ»8”(Ù“ŒniëAßš©f˜xl·öT˜ŠÅ줴µH³Ò*Ë¡%"¢Ô›Q¨ÆQT¥U£h¥~ѱàZeìƒm »q¿¿Jš7q©ð“r\f‚#¡hà ‡ÿÿýp…°ã¦°Ì·CÅÙÉQÎrù¶v(ö÷ZñË™@cÔ s@ÿÿÿC+€‹ž3å4u)0°ížõQå.i!GES6€ƒÝîža46ûú©€Ž»cÖˆ¡í¡RŒò-t×>ÌmõƒZóm€ÞJÓc\úeHÆ õMªÀâÁ_çÓHebÅ>l¶Œ®'̼Ÿ2ë†'‹€eiY%â•cº–t°‘ Yú^ÞtÿÿÔ°´5ƒq3ƒ '¨‹UØž´ ‚fŸ{§Iˆ]ÿ_ÖçÚ«Ë0Ê®jžçøösn‹3Ÿp¡4¨2J¿`«707õ®ùÖû'F9³c*tÜ¿™º1çèÇ.$÷“ѲçèÙ_«v똹‡É®•ù‹~l ¨õçÔž:6ÍéÝÛ£~Œu:v_Ù½ç£dî³Ùz6Xú½æË RýôtlÌÎCdîÓѳ^TÄÆp‰ÌD_ûv6P¼úç5&¢’ožÎBeæDMaóÂtæ$ª>ÄåR|ëv¦¡Æü!›Ê楈ýFÞ~e½5›3iC‚ÓFû§öZ™Æ@®dЃf®z3éY†¥iuÎy>ñH ÂbÐx© o#¢I"`CÂó`™Ëbs,JDMz‘Ëlš7(Þ–÷H.¾bkóHåÓǼØÌ¥ˆå¶qc*S]?2|¶ÄÖs”ÎéhiK–Û¦ó~å@œe8µ1’Å£,C×:}𻀤ÞþÓêì–my…_§›˜xª3aâ8lÿÐ×$•¸Æ=äŽDƃÿýKÊ5„¿.©L~,1 D³k ‰`«èñkgXu‚eÚc[½OÔnTv’4ôbÝ5M<ǵ*]W*Í*VkKÛ}¬Ò½‰+„Ç2ºÅŸ””÷zJŽblTɶ½ŠÐÙbP»Ž¡õNÖºÂZçㆢ ¡Â¹ò_Þº1˜(”¾§¾vF—1V³Ú¤Ìd=E Qž c¬)H©6§“ Jø$:ë– &[k›ïj‚fš{ÖùÒblê­_q•^ª^/£zNyžé¹Ò9¤c|bJõF–û)´†[J†k£NšŒ¬EC3ËYsTe¬±šêâ Õy¬f–±› ~¨Ën£,ꌿ¢RÆN_sTiÚWÿÙQ-ÿ¬f®ºj2ΨÊÕ¤4Ó¨ËÖ£N¸”±“:T߆$‚¡2 ˜¾:T‰èŒ¥¬va!DøDÿ·CYJd}xßòˆDº‹@4®œFºÍtúåã7(©U–1^Ίd,¢³ŸÔ×Ê•Y^̲Ç|0^ºäV í§vš•¥3:bª½ä[‰/úG'Éõ4ªœ 3(+UL¢Ëá,ÏT·e™˜”öœyu€€Y ʼnÿÿõë8š| $²–²´ì´8,¼H9©àÄ9¸á«äGæ/mQÆK6/È‚eÚkyV•ŠrêÙ€Íg›G‘‹Åf²1”¾jÂ2[ªjI•”Ý­ÖÞëÍåÇYv·M{/7•Y…pú^TñÙ&gZ}ŸÇåþvzR Z’ýåŠTìDÅŠ\NÙèCƒ>aÉQn)‹ }2/dnƵ^.bþÀui¡xµ Òÿ|ÅËkˈÀ`¹¢ºV41m[j#͈óäqù6S)gɱÙ+^‚ZÍ+Å\¶¡ýC™æÆ¼@Åé õC†a[šPù}‘*MŠ’Ã’XÓX¡‡´‚îßD»çÃcý ÏB€bQʳ2I›Î5°he*”Z GÔ©ï-¥_*Ãyæ4b9ê˜.yºÔ\ô‘{Q*r‡F ñYËú¼}QñtÛ.W‰Í®‘iÿ³|ô…¹…ŠFã©Öè(S’ÛU”@@ÐÝRÕƒ¢4Èvð–è¢éœì»†w Ï-pÊÑ|J¸¤÷EwµuÊÏÄ€³3I sÿ`=Ø Å¥¥cŒ~Jý~¿_¯@ Õð8BIŠ¡ùD‚eºkÍ|WZ u`:P€b¶]ŒxÁu׌@®’U¡%_Ù£Q’U©›Ôé1N’eQÍRÂ0ÐŒ¹¡’Q««Ô鑽뒔ëéÚ’RáUG5K äÚæ†ìF¦n\zRÓÝ N#“±±%-ŽüæIJt°ö_oqEwSÙ~–«'nbUﲚr¿‘6‰T4 @rje>²sˆ­ZUâ-fÈ$gdâ.bÆ›ªŒU`Û`d‚KR,•J¾šˆóm˜wy|jŽëuðmÉ œêrK)§¦m  ­QÚZ21Mº^|ê¡4NIaF¥}_ÒÑ¡éšÉ\•2Ý)Åí  BVèa†V ÉV8ÇG³J û™ZaÍ0÷¸¨aÍ’aå–Çv0 ‚eÖÔšÅÞ.r‘ÜÚ° »‰q‚}S“¤j·—©¾Z°0yU¡Ñ{ó©åJމ­šÝ¿yxÆrµz÷y®>y{$mÄw—¿ÉÞ^°ÞqûšáùÖ/"@ƒ´Ç+¤~Q8DàŽ¢(ÐâE¿ª ÃeJwŸ¡Éf¿ÂèÊÈêQ1­ZÁoº2…1…BQ‚@®ªŒŠd*7nF†SÝØÃœh \¦ êɆ /Ä”ömgœ_ÒýþãLõÉ‚ ‚f×s%[N¯Ùd†žß¤ŒcöøÙZ/Ü /FÝöpÉ/™Fˆ™%%.Œ¸K :Sô½U ÞƒÄ)ê( ®yQ‹•Žˆ®é:ö°Xe6´›jß͘e6º›k9·iÆ`ÎØ_AÅ¢ 0VR°Œ<ŒDÌACž·à†"ƒíí–¥u‹zœÊ`à tˆrbX1duŒtH,#ÐêýçÎhtdî%»Fb˜]=‰}‰Ñ“ ºøˆ©»gÏ/º)\‡PÂ%÷>J©e¬’ç´óÄèéEót¡;Bc-àÁƒ Šu/pœh.ʃrE’¾ ²¨ù ÉcD[Áçœr­›ž½aÑÜ1+fç¶ýb “CI‚eºÔ˜Å[ÝÕ¹û®:žÆ ¶ä4DÂÊqjâɃ°m'¨gsZK²[V¯¼#wR7°£Ta¸‘{ð‹'€ÒXµÝÁ€ži¹–Ýq1Ë‘O‘¥—Û¯üÂ&ºl¡£´v–ÒÛ+£6÷ÈJÑáš{‘.z'48eir?È膠AjÒZµsôˆÆ0’jÝzÿÆyŠeÜÝ%Ɔ ÿ×¼#£&ziÈ#考ÞÞœiTë|®rîêò€Ùá (ý„Í ¸™D¹¥ ü%‰zZÐZªÓ¢[\§š»X½„k øFÜÇñ®nfÌ}ä‚0"!ÛtA§J÷>E½iZ•ìlÙªÜ1…ˆØ½s³Ö2Ñp ‡ŠOâo>Eì@P7Àô @AÿÿÓšFF#Ò­·îÁ ÓE_wçÏvòö^:¤€ÿÿÿ@õ€¨ø¨qJ'Öl¥ù6Eµf؃ÝïήŸaTd•X\ú*wŒ`޾jþ(c+½•= IeË——kÚT]VÆ\²¬—ŠKx“ªÀ ằ2\»’I4Ñ“-*HQáÄ9Qs$·¶Òô‚‘DXôûj—Êþyßá©À‹x^Ëi(<:´<ÜÕ¸ÅÛyrþIe©Ûr@èë;ð`.£œk¨E:34Ú“m>Vs+qšrQõs Åù÷Xp‰ÜI4lÅ@y€› Ôb!¶Úœe`ˆMJÁ}pÁéa¢jŸMf» ÓÇDe¥'Ö|Þ‘ ÿÐÜ` `áŒ``YIœê ƒÝí–eTå{`ýu€ª¡lSŒÕ!lx°g^e'^T!–Sõs,¿7¦”º7Ñg]¤Ù§Èoeß«EÅfÆÒ;_FþÝÐù™Êa§IÍ TBÚSþ0I‰ög—Â> y¢Z+áQ‡º­Ä>£ëóÆ)‚ë6ëɦ vP‚:¡ŒUõÜ9¿}òB½òÇ^ÐÝ_}é&USPáU›øËycB“òbžúÆLE¡h]ï³ Š¬ÃäAßz¿[¦õÂX¬>ÐʯֲGFIyÏX:WŠvšCLøÜG0Ò6§2Ë©Ðâ¢èáas9Ûùè@ÆhBpkسEt1ÁµË §V~T æåµYÝcØ:bÿÿÿý0)b™+fãEbÉlIY,¿«'½M·:&®UÚhÐÔšséCΉ„6.1ž'`ÿÿÿýA Å”Qý%rñ5KÔÄríM%u]¹`åÞÒu? ×ø„©yb˜­ç{bxŽ#lUôúh`WÀì·‹Îùµ+&<–¢¶ýTãB´f«¹Z ©MætÛÏvQ¤‹t« D§Dˆ™6&6èBHDy %¢ÍL šˆŒ8D§ÆÀ“bW&@™b˜„ 4„³‘,1B†„Ý’@Á¡¯#yF,VFF"d°šÅPV!cþ;)KXлµ&ÙÚQnX#fãÆ³Æj6O&ê¢F¯ËÄ·\oÂUlœÜ” AoE»¿Öô÷ô˜*4¬ÅBîš3æé49cÃê=1{4 ˜ÆQ³ Úµ3'Á®ˆìaŠ@´u”rÔk!‚$’ÐÆB§6®Ö8ÂÕÎau¶|P?‡ê,³G–fŸã48®Á|«\É•bû( `CÂ{ Ò©r¦ÌÒ$#Mà£ßÈhÓo1/r¯#Ã?بÅåu ²Â]š]´ÐÒZnd°Ÿ8Ò6‘ÏšÄE ‘'Á¼†üÂ0³*Ñ…17C6-H/Fî*_A¬µ'uÌ .bx¼Ár}Ó'>mIªæd.€ç3E74\¡Ãƒ ®Äž:cûcM‚¦ŒêR1½G™€ùÃ:+œ›”ÉÙµ®ëŽR¦“„©yIÉÊ[¤¥‡Í»$tX³DŠdx=¦U¯V,±³„t2q yËÍPK@ÿÿÿý9V#z(QÇR(³öî=²;òÒé_×![ æ?ìÿÿ@dЃ࣠úK)‰n·—@ƒÎÎﯖ!Tmï bóœCð’)lå#¥òœìÑTb°†eÓ1Ír1±PÊq*"åÜjšÆªHtÒ<d(EÇ6æ@y›¡¢qœÐ·3£Y6!¤^HR›_/K Sð* Õq¦ÜÊ ájz‡ùøÉ„‰ŒÍ¢,Vü3~ª>¯2B*Ò ÂÛY*˜"áïH¸ÖÌG“òšRúh!Áy ’Ò¦URUÃÖJ"®Õöˆ&FÅÃÚ‘âáíHm"*áíH@Õ6$×:bQ¬oˆ‘>ÄÔ¡(bÖ¾s'>KKî†ô*üaÏÍ«'æâ3h·e‡uYjÙÉKº†p®!jІ ‡^4 !ÛmªõµFáéS5¤£¤¿NVL°~ª®o;¶QlÖ?°>–ü¿nÙZ]=ÿô7x´ ‡ H" ƒ®ÏîÖùùæC¸`…~t»`Àe2²!î§ YL;¸ÂšÌ6Uœbz’&»[ÊßšC¶Y:’>nÙžìÝÕê½ CŽ˜{6+ºü'§ÀÐÇ#jNQ‚&Ølb%(M‘Z_ÌSä Tèb±þ‡X›ÅbÉi…Áȱ·9ÑŒU‰fÔUš°·ŽŽX®˜ø©tÏ:dºøêÒÁ6ÃhN"«ÇB(TѾfZjM†Ó¶€äÙ6™å¹k.Måß½ã4½†˜ä„À&ðÝi°ÔÉT÷ÙÃ$pá´,be%8æÜãÙé„dªç¼€«OÍHH褲˜¦Ùoýk`ücZ/öÓÝ´ÄÞJu==N#éŠIWÜIâFXÌÌÿÿÿý{ƒf*f›¢’ê ¶£F6«ËD¼ÃÔíÎe‹Ed]ËîëGõwº Àÿ@ÙP ’Êv ƒÝÞß¾ŸeTeXÌì­mÇR¹ˆ£Ÿë!«¨ZÐó¹lò&"XÃ)Y¢ä"Q'’35ÎB&%$ 3 ,îB&uÏ­“4Y3F±zÝ’ÄÜóˆ‘Ç·ÆÖò#¡°Æ8ªÞ.*¶-w‘™yD¹;õïtXk*#"Åyr’“ó‘© +«JmÅUtÄa²O¤ƒZ=WŠ3cN&¾j£!+t¸ÇŠ×ÑÑZÙ+r›¬Ü•r_l ¢Å"_4Ϩù;TµSÄ™™MHSM“qᯈ÷‹²F—8ÓÒ ²c\l¢`ÿÿÐÂÀ XÏ”‰Á¦Šá´ƒ¾pÀ‚í¾í’ó[­–æð Ïžg¤¾Ù¦vŽK MˆeD3e«—ðH0V'Ó˜.3+lH’ÍÁQ=.ÓڱΥC.—ÎÆ¥€Š(awAïœÐçtMbsBѺÏWÿn°ÝlÕ¹ññ„òáÃ3›qÒ«m:½Ÿ¯™<‹µ©Žj»]­LsUÛ{¡N&"¬D è9IÖŠ‚fºí’`¼g\Ijù2Áq±IÊ-O,Q±D¢\ߦÆ7LúaûÚx¿ûIMu_´²“ñÏi,bÑViÕT(n»/1Cr…¡Ryö—Z,aA«&GX:‹ ýˆ3{àÕHÀ¸ÊŸ„%–êÉ{óE[zÑiuWõ sžÈñ޵%ÏÙ9ôf…wµ:lzár·|ËôLEÖ/£ºùé¬ìäôLlÊ?odà¿r1vD–r2]€0Ä* ›/sp«Ýjh0`¦ÄÅChÓD™gŽ^Çýõ„ôÝZûçj2ád© ÿOÀ)B‘Jh‚f¾ûçžô½.KÿlÊ…k‰w}yUf¾Œ¬º:y¯ÌH„$ôãŠ4I8ßWe$jwEû£ˆ"•¹Óú/ç9y¢ý’¯Ø ßEó9Äõs{¿qët_½Ñ~—Eÿýþ­eÕBûÓ¾Õä/Öè¾mø‚ûS¿ÿ¢ô_aòÙܤæ!O19$“;ØÍí÷¤´¼Ç“{bLî#(%E‹‰Ñr3<¹ 7:è¾%Ö.'Eü]Ûª³I_¢äèå“£p ¯(Ú•«–‡{Rç‡ •¬~œ\9D•+ãÖGµÊˆÿÙÑòÙÎyá¶E©žá_¼Ž÷ŸÇìò\¤Ô`­³jÔ áæLãÎYáŸ.Q„WCcÑ’—h|üLîºuÑr„/Ãd3²J‡^î L/´ØY™*Îxš-Ž.¨ŸÚTWM±pÌœr„ÛÏlwi÷(Ê]ß €´è’ߘýÅ“,X`frOkù=Ù(¯Ø,-£ûÓB¸ 2hx¿{a´´µL$®ÈÆ¡ë1ê„kŠsÆ’‘ÛT’»Mpî¶ìRe§ŽZ OgÞk«¤÷ŠÚiUï–нtùmºÙ3Ž4°ÿÿÿý¬€,9JaûCj¤E˜î¤F¤&Î<Ì©M†íãb‡+fõ4Ï—Rï¼ÏLjCKØÿýL ŒñJžru­«8À‚îŸy§Y‚/2U›e~œYkž«IO\ÂÆ@¾Ó"I5.”nF Tþ˜noN¤e“›ïEÿ]ç©Üy‰ËŽßÑJÿ7ù þ*«Æö@y%ÆØYr¬eÌ“”ç,Äé‡#3e<Ñ–zŒÄÊ8Ý„_Yæ®':¹SœW'Û.¦­^©¼ñ‰ÄÂ¥3¦oª’#l÷tkCKÞuræ°J6›¥J±Ì¤Úx•)LôžØ–à›`ˆrqÍaùÀ¤îlÉvP‘—¾ùˆ¾oaU(›öe{hU Šz:¡FØ›[΋­ZѲ¦I¸zem@Ä26zV®}̨+ü’‘mØd¯šNø%£¼½vœÌÃYxŠq’x“Ñ—¤9JT錴g#¹ÛÑ¡ Î$Ñ”ŠY…‡)»£!‚”ôÚÚ>PL# PÁì1ÂXø†ffeuNY'´Dn5)»rÒ#°Þ‹}Ï£m¤†æz-õaÞ*ÿÿôR Ã*Zrr 0µÍ"ÍË£¥ds1«(ƒ½þî¾–¡47ý®°&å/­€¶ñ9ѼA»1,Oi7šCIŒ ¬Z× Ýš\|º§6‰¯Qº4žfÞ£k•L‰™“Î0ŽÕ”Þˆ‘©ì‚­‹pÒÅ/.?“ñR²g4•y‚(¿5F‡L(Ù"ö#CÒ-öP›¶ì¨Ö²Im¬aJ‰žº‰Ôâ"å=j6yø"O¦NȰç I0<{‰’ªI ¿ú?T7BàA™™”qàŠMJÆr–`K¼ pµa€rD1öÛX쿨PÖû‚fÚíÍowXãÖè«(«>ÊÛh¶ù›4* ÆÙ°Âä/Ë&ë¢ 4Êì3¥TÀ`òÙØ¥û]i)¬Ñ£ÑY†hû›Ñ~pU迃¦*²ÒQmíî òîÙÌy¸3VâÉ!âï¬ÅDG°Õ)Ü‹0…'WõÒI\@ÂOìá‘YSXäG©M«#瘢vú”¹¢#ŠÈäkŠ@/S‹Çb:Ë;ýY-zè<ðZ+<ëªÊáRj²·ãNXLC ˜Á`‚gºûçžd¼ÁfÕÛLÅ—\ÝšrÁgFžç–(q^fqœf©ÉǤGÙ`]æô¹Bœ˜A~8'’1z6¤aB*=ÀÄ‹xBdâÂL俦5NQë͉4”쯑¨,*ö‹5N,*ï–ärßgé¶ä0.5Žk6¶%ªc™%Bê…¤†¶!/Špø:¥Ùä£,嚥K(¦Øt‘®µh*´4ÐQ®aNùeÝd°-Óq4±Ž›•.®ÔÆ08A¨ÉQ*¦r®BKÅ®5Õq²]« 7 rŽU\­Ž–íw†:»ëtQ‘0€• ™±@Å,…ÑôRÈþ„C˸‘|»î•qéŒ ¹õ¼fPmSIŽxè‹L*&ÐŽÈìŽX÷ ‡È¤ž^ ÂíYá ¸yX×l¬§1OVÖ`~Kw·ù­«£÷‘{ä+‘!zûK±jùŠ\‘c4qÅ$ÓÁZ3”ãgÍÚbÎè4”â1n–2­þ}!ªÅäXKÛ[¦,§Û•v•Z ýf(PˆÔ¦6LZ­Ô“ç©r–Js%íQb²Yeó aæš%ÖÒ2­rc$F’¬ùØ:âGây"¡ä«N³JÌËG”F™#çR‘´öRhiŒ”† ØÍŒLAŒC"Æ1Šï؉ôõ«¶ßÓ3PÝ7ø ïb<Ù©ËJ‹ÙÎc[ek ÿÐfˆGNù’„r ªA”?b;À€ƒÆëaVUa_¿Æa_ŸŸžaTfŒ°è`gqÈQRêKÏጭJwbÆP„AôãŠJ5¶§›NJj\§ ÕgX]¢îü0Å|î(BÒeˆße‡"Û,G¶q¸–jª‚Ž Óɯ“f×9P`e¸‚º½`PÒ‘Yœë3¦™™™¬¬·¶^ǹïÛÌÍ')fKfÔѪ«Ó\·ÝظÛÚð\mrÚZãhËB‚ã>ǬømÔ(G˜ñuùhg¿qµ‚é˺¯-ÛÝöâµMœ«&xþ%~0ÀïY»7—Ý·³÷‹Ú•œ†Ðü˜’êÖĵ€Äº8ļ(˜³‰D–õÝk·¼/ °I›³fRÎÀµ!±-jí0*PÔ·%ºÝùTyò†á¥Bf˜¤Pr…˜=wcº‰^(;é¹–¾[‘NéOú»3;ÈØ¥ Òƒ¼™PlQŠ®î¦^¦¯ $•yATÕýßÕ^ó&CˆÔÀƒs™®±pë ŒêÙ_Ü%tn  ¾9§‹s0¡F½/H§_ãç¤h&óKyÉÓPu=©æmØý[Dçí «¢å¢O PñâIŸšZ?úÑ4ÉöEþ@·I¨mI¡m(Ò8”'7&¼ÅePËë0БŽ!ø€êx!UÕÌdR¾4QäôlEùaÜ.±P¹éÁÝ21 T<Ÿlý:̵ϲQ‰(|s$°÷Sq,™a*ÊšZªcG¤rõÆÕ'¹Úa£‘ ŽVwÊcK ,K>l_í=°‡‰ŽäŽbã¢GrOµZxšÀœ©¸Îö›d\t…Ó‡Ü`iC'»wCFp9F‰³PÕþòR›å Ú׫Ðkf”"¦Y’_Ͻìé¼™[Æfh5Y¼Ï¦]±§*@h«¸õÊ*(³Rš€ ÔðÕ‰¨z ¸Ýã.¡ÚŒÝ*ÏÙ$oö;hª,wÆX©é{ ¼ÓWŽq»4Âä²4€ž[÷ Ä*”#aRgA†ýv_’Y}¬&$\ßþ&.'+XU¸­-±0(·¨vecååx@èØ²Ùž•*æ4„¶²ÿÿÿý AÈД±Ó¤‚³,¬<ÈeF$øÔ]éai õï ÿÿÿý ùJ›BJâG¬»J<¬QÌzFSAq·“Š.Ùôߌ‡¢2êÿÿÿÿ@v`‚¸Œü„6TΪJWc'ÞIJ)59‘_/fpÝÝgØlÀƒ¾¾ßßßÞžŸ—!dÑà pÀž°ò®†I\\-upľÜ`È¡V HòîÔNꦋ\¬n`5- ‹F¨ N¢\ní|’²}fFŽ’Àõi+E&jŒoЉÉ&wqi\}š‹:éƒe„Ë=cg{4eœ@1-áˆL´îù£‰tBÀ1%6ÙÈ âØ|9ó‚òk¿±x`†vh!¡Â8hî\Œ5 mÐCC"\#Dÿ–£µYb$Â44I,ï0hOFÊGÎÐ÷Q¶ZXŒˆÀ…™Jv”Ë/gM§h¢hröÂʦ”$AxãR $a\56”*Íæ“ ¼Ì{@Å*Þ‹Â’!zÄ(gƒ —°m1‰+$mRÊ*YTc—µr9Û'j¡÷âîa†É 5,]ªKGscyãº<ÉuT#X[ 6êó©ý®3dOº¿‡0¯ Àÿÿÿý*@p°d±AÊ%š·FŠ% Ë4˵‰LËÉ¿0/gh¦JÜšjD„IN.ÝuGÝ÷Lõ ýl< $0$‚f¿k¯IŠU˜ežrçm}ƒU“›@Äk<ÎsËô²¥š¼‰Š(s!L&ŸÉt3OÎ.C# ‘ò^غ„²ù ¹šžÎí½ä’“Y>ÿKûÆàäÙêGT÷~@ž930È=#¡á#>pÉBt{¿ø˜EÓ]­V!ǶipKJ­F‰6ä}ýÇŠJ¤Fèzå7×úqaòͲ"-“ú±6`”YÈщ…ô97ŽX #›6=ãá”ø”ÄÀ"ÓX¬’*^S£…‘ÔÀSã|^ks&´kfVÝû½WgÈ·#éd“ÊÎ\/³°þæw: ‰×Û’!†S‰H…ÍækXxC¦Ö§K}›Æÿ@ÞóTèg\cÇÌàCÄØeü˜CÆð\*ÿôi°"ÈÎËÆq´¿ bÈ€‚fžù§KÒãÞ}í¶Î%¢Íþ\òþqÓVôêñ#´:ÍoV+f¢UA,ª•K5—QžDxʴĤ˜ÜCEeæÒ†´•qzf£=õLºC;eÖ#GyÀäÒê3–SõÕ+êæ[Žgƒ¹´ƒ¹´²ËùŠG“ðHت–±%[cí§W!QÚØ\bE,š%¤™7ˆ–jMNHR¡?ªNZhO¨Ð¶‹ãéOÎ’[¨¶xwÿì®§9OÖßoSÛ[´ÒÆbõW üIX9ºÆÚVºïí:Ù2€ž„³dòç©“yÊW[Nýµ'lwé ´X6bkbdð#y‚æW#øL0Q\žoôIÜþrB*¤ôÚíT#ÒSvµ®Õ± z½õjO0)Ø*_›1ÅœÃ6‘<€Ük¦ÈµSØÓF!™F/S9’)¸»Å.\âB(e“wú‚(dËA$=À”n.““ØTB¾Aî&¦+Ó;Y Dï"æ«JæÞ¦-º<CÐC_ÿõ`h$°¹«Ñ9s\Õt`¶Bu…‘¯a¬5.sÝí‰ê"Õ[Ä€ÿô ÈD˜—BÈra¬ÒJë4ƒÍíefUN¶ ǘ±›qµÔú&1À'Óf›Ç'°UKêeЩë:T)wJšè‹joe¥¿14ˆbì+ô#0‘Gzª6ˆ­Šyõ……xVïHLõÍL”päŠÌCi5ÑdóUFvÍÔ°H·¨Ež}^ ¹î¨zžûÎ(|î=½}Q|ž3ÙwêBáщ´\²€ºÛ ÆÂ¬·à¦ÿÞ|RTVQ |¦1Ž „KS‘hµ»®‘.UY¦â¤ª°^†¥*cË®¨Ìx(„­)Ò»¦-HD%RæùÐdIP9 \՛ѺÐÃ9DKò¾X§E£—E8£e¡} “?aì>sH{kO|°©‰1%@ÓEEΠ Àg€C@l@@ÿõ`“«ôØ£"ý1g9u¿q™¾EØ¿ÈrhÿÿÿÐ0tgªš14,–¤òâôá©7o8҂Κ}リ"ä¼ÁF4Ö (jqL§ø_£¸‚bäAî¦!+­}MªäF­Áö ³¨ª¸6£.NèÑÿ£kõãK%-uµ’¶WFÜyYØÆ’ÉK££ltÝr Ú‹ `„ˆÜ‚‹Ñ¶\$qD¾B˜'X´å‰Æ1ëPý«…îNl£ãùùÖ˜îɘdL•Tèq..= =&ÄÉë‚ñ4$ô›'® VиsQ $`8{AvJúLX^3.“5–¾±C±nV8óµ;5Ç{\baX„íwQQt»z²m ¦^RàŸ/E£øX×uxY€©'”H‘pZ„óG0ro/uÝKˆ¶ÖT£šé7Écöbm Ž«Ë=”ý~Ã(¨8o(ú`Žº{0_¦Ë>(âxÕÒDŽ…D>ÈÁàµK[X`¦V±â— >ÆAÇäÇXËC¨"¹N©m8Hó¹ìñ:öª–§†8ØàG•DS΋*p¨ŠÆucå JÏöm—<Ý+¨F‘fzS«+î·ý »‡¤°…dNÖa†W´'ÑX˜µP“¶pë‹d7e=øñò>r¢ÕÊÉ#K^å=mCg š"4þØ×+%¤•›Ñ£бƒx…¬ÖAš#Ÿ£Di£§àlˆÓÚ†œ‡&#Jijɹ¾¦rÄÁPþ©‡s-2“öìryo Â]£k!lŠ"Õ±4rE3ôó›Á£âøâCÖ ¤ºyÚY&çf§—£ˆO«Û†î*…o¶³·kÊHé‡M¨®Ìø@àÀ'c¾9)æs˜‰‘ÙIH†²Š£×ìÆó‚h|0DL\ ’j‹ü½NÅCw_!Âþv }HfM޶4zª‰.…l&Aëõ§5‹)é³Ï×…ÚtÓ`‹x à~ L<¨cÏ.ºøÀêÊ$’DH´²xü}5]–}1z¤í*“/s¨~{Z–-9 ·Ïù±ÑêÅŽvvrƒôà)±àÁ‚¢/FóO‹ÎIˆh°ùRÑÏ¥y¤šA0H3ºNéyÿÿ@Æ€²Æx“Z¿XfŽ€ƒ¾ÏÆaVaVùùùÿÔ2"£ wßße´.çݸ1òç0¿@¾ºqJ›žž™^ ÆÉQk%lϤèy ÉQ±®ìÈ-uDDiŒæXÊL!¤ÐAšìøŠ·[v"µ“Èl•¶"4üŒ ¶'±QŒ°ˆ:6JÚ_#L·›ósTF–!²TcˆÓÇ£A§‚ 1­¿Èh§Ñ¢ÿF‰ñsĪ¥ 8>pCâ4˜#!)*#;FÐÉD1ÅA™@“d2Ü©^Oz´î¡"Z š&ÿc¹¨˜¡<Û`9nì©w©"‹Zl!A§i­¯qíùúÚŠZ­& ­¥†Šè½Œ˜±‰(25 (”+§‰¬òdÜŒä>³1X$¾¯»)§ß¾ßjL¶šÔÖâ×Dð ÏcX:å«ÄÒ£é—)ÚÖ2VFmâKÒ¬{Rþf](Â7Ô<ç—ˆø†f÷ÑyZ.Ö;²t(«­¾u‡8)ƒž¨?“O½Õlj³*$ÙR;`AÔóý&.6 l!·½û®-¡fséjÚœnÞ‘4­×š!md좶5N¹QYQ·‹˜Š‡Çu®ÔtOóG :œ ÜÁq âÈÐFê¼;†”-ô««¥3 Ldl§ªÌÈîþ^ØMŠ Tä 3èÀí2h×…;% t.ˆ ah`Y¾|cnB­S*bV™èÛ³6 ˆ!¢ä4ÒšRÛjda!KpšæðVŸ$÷§e¹lG*…hËŒ °çAÓ8ñ¨¨¬–Æ[’OìÃ$òú0ÆS°ÆVd ó!>–™ËçiðÎð–KÍ[ì0M%a™¬½¼œ±Ù,`’ŽÜòÿÿO #2rÅðl¢N*`t­ö±äмY*1¥2¦gH·S—O“‡_±=k>1ˆ^g,,ÿÿÿý2@' Jé°È”­“L½,iMl^Ž_î2ÞÀÿ@ñ` ƒ$UÇ‚mÛ䘅[å—?¡Æk¬aC†Øa—*ìÒ=¡4T•¥8zeâ³I²Ì;»)Ëü%äºö;Ši™œD×áõj´Ã13Š1Lµt,ÇR ë>Îû6Á ‘À€k.1)ä Îvý™Q‰)‘æwñy Ãñ\î7JB3 ©ìŽÈˆÆƒcïÑʈ9¨*ªQ“ÒqúwåŸí9fÅ›KÕÞB˳©#ÿL08ÐJ4u@ƒ¾Þîþ–æWZ:Öš4j^óÚÎA¶‘bÓŸé“ÒÕ@ä-UŒÌ8GTiŽô–3>© =ê4íÎ>ÖT3gL Ôí-n˜ ©»I±š±o/K£!Ñ.:LÊJÆgJ¥–3K¼ƒª£KíPÌ€†=#Ú£H¯žÎB^ÿ¾Û…qiH«$ìf†*dˆÆìxXÔ±4Yµ‘U… ’×¾¢¡4ÉTS‰»ˆˆù³’*á+7|3"eg´™ÆV±yj€5%qÊ;1 JV¨0d³m¿‰+f›²‘VmÜ‘”ÈýSÆkd©VíUÿà]~6 +q¤¨É[r—‰ÆƒÎ¾ƒoÐl[ø(ë¨5߯’¥:Ø4œœnì g ]K»½oBÐß'„«Þ`Ú{i2˜wŠuÑZ +§íAÝÔo•V­;ÏzµŸFîˆÑ ÿÿÿýtT0Rƒu!«lªAß™žUåùÚ›6ậèÿ@ñ° Ôh‚î[ï¾ûïKÌfQsfšûë©gÙ/ ú^[½—iIþ™Ö)%?â+KêýXEO::®5FVì ËD $N½ &cHLˆMiû~÷4Fxù½V%.oÏ–:ÆhÏF,ûxeYdÀÎdÄžj^G.OÇ4ÐB 4½,oí*§,m»×æw¤wñ #˜p‚Í`G<›þ„N]SÄ”KO"†¹ã…É §u‹/IhõÐTüÕ´Îx¬Q³6ÊkQ.ÆÖWPMÆ8é§6i›:Lº™øˆpÄ)ŠÄxãlA…L,Ã¥%j¡¡¤nÑ·deA]~VÿT5%¦íj¡O¾5$Õ×3ŽËòŽ,/ÚH™ÐÞªuw™7&&äZRë¤:ç2°¸íbñ8Ñ[`kÄòý_³X´h¢úœWcŸ(¤îÍU]Í_ Å%;#GÅ,ÝsSê$6­3ã²73 Ž)/ûY™ªd<2H¥öæ—Ëú&ÁÄ*Û»WDµul: Ha£©ŠÒæ6Jò%‘4mŒBgšt—Û²ôøe O`Œ¸¦ÎϘÛýætÑOzo¤tÁa+ט^N²+‰ä2Èù=žÍšµÛžrã%øE&‘³Ms}¢LfDÔÛâN蚈xfDv ü +BS‹UH„EÓcX ÊíÞÝë[¼´GJƒÍÎ߾ߟžeD5’ Ç.p³¿q£Tyžçy3CJM•æêE¤§g$¨‘[f¹¥‰šX³‚(Á©a†…FMŠNoYŸNÐ%¢•ÍsúZWÑ¥ŠNõ4ä†Í›:ë0i]ÊÖZWa)ÙU¥}Å !dW P#–¦2šˆÎUäNìêoäçÆë_¦|h§þ7oøÝ3ìC/òQ…mÎDµsh‚“ {Ö’FÖ©?N¼“'‹­e`…[ˆt½õk‘‚2/Uš²:¡E ˹‡Q UuNTåQTÝHãVH:Qƒ.j.ÑýƒM‹ÒX±Ì› ãÆ Á3“™a+ÿµí `aÂk–©iØ4ažé— Œ¹U…^A‰'ÇeÕú¿¬C…Ý·‘ÂòÊû €Ë™a¿kÌRWKG€S—µ9:ÿ|DÖmn×»Ƥ%àHaÆ%ß14„Æ4.À@W‚ÿýq¤êÃ63ÈÄcI-#ZM `UUENUELeàäŽGlnÕô§š ™B‚ €óÊÿÿAÆÀ!Pâ'É[4²XY­…Ђî—k¶KÍgÿÉT7‹»rÇì”e½i’›™šU‚I=eHÔÖÔS\1zb»"1önT‚‹Â0à©éìlÀuÙà—R¢º'óë;-Þî‰òmžeË3ŸX¤j†JF6äfFX©àøbŠÖÙñ¾)fl\jx`ûYX~*NDñ¶Š“‘5g\EÓ$ ¦¬5~iYЗŠ.Ó6JcÈ«)4nË3Y~?`K‰X .ˆE7 ?*Ii»U®R£÷[óŒiÒj›dXù{8âÎÙ,6˸¬Y+9ñË¡ò%î(—±YøÊt­Óa® Ql:XåGÍå•`c æö4×È—ã@@`Áƒ øÆ \ô©Pù¬“µXï–>µMѼ`.%ôJ ø˜ÿý ¤ Œà`“ähši¬« ‚f¦ yçžô¹/0Už…ο—4º—±³¯ÞFñ%$ôãËf8œk*ZªŠÉÈ«4oÝ1‹8ëÚg+b18 <¾+$F+K}²y¤>­VB„ž`ªAi4¶±ñ;Jø÷vüFg1ÜbÖËŸ®†E|Fv²-¬|F«¸~#Rlž}˜™5“ñ£dƒvdv5ƒB¥‚¦¼ŽÈŽ}e²\9Ÿ&˜TÕcé–ÈIM”0åMP©Á«ŒrrrÜ^PaÆÅÝurÈñ”o Lþù톃¼{êÊù2’ØÔÔo¸±pB€ m!¶Z•Œ©ä¹Óõƒâ;éæËgÕeüvrÐo£¦@™ä¬…ŠZgÙžù*±œmÈ©šCg¢CZPb*’¤-9©ŠnÙz©˜êÌ31*"’ú:}é;©ß¨æ¹ëK“r@³ô¿½k¦éwÿÄÊ©Gç äxr ˆÑñב;ŽŸÇù•~oyñ‹y'g/­ž:G˜O™Ð©%öZúª©šØªq@<‘uÇ*Lÿ,Õ7z«ÿiÙ<û6³úÖŸHŸØmž¥é½£ì›ŽHvÅ ÕÁ‰»‰ìÝ¥¸“ƒá‚ŠX³šwp¡(jV—hƒÖiÓj¢á‰ìœ.Fµhº@Šì â*ª‡½Ü©U¹V‘Uó_Ýî£I€¶95YŽ¿µÈK• ~^µäùŸq^=ßÃ+,I+/k¥ Ãð„AÿÿÑ“281‹RWW[%÷§%//`Ú½¢¤xõhL ƒ @ÿÿÿô=øÎ,¸¨Àr ¾-xôG&vѯ-ø§C¶’‰Jf8[WTÃoy¨ƒ®¿ßß®ßΟeTdÂXÙ¾y9.™ñ³9}’ÕÁæáEÝM¶˜êíFÕ¹µÜX›,[ÙjÛÙzÞ9)ºòÜ6²»$×¶…ÉW)¶;‹¾.`ߺ6æ´£SêâÐôèÚöÊ«.½Øh™¹›ËúqWƒh ÖÇE¦zB^mÈßR`„[N.ݸNŒC\êq]Üþô½dÔàåUÕUM ]VŽP|ñÉ ¾WÛŒ·{'þÆ÷XšLef·×Ê|{o“´ÃB$?é ¶`äzXFýäKÑ/~Í,*±/F0ïˆÚS÷™éº~bK¥B{ò uWÞë䯒w¦Ýöð“z²¶x‡[(( ÖûÿíO6:c Ї‘ÓºËY¦¤×Ö”OkæGÌ£OiÈ,õ4ìÄÕuë-ÎŽÒÕÄmgÖG"½çLß(çŽãH…ýÍúEó-Gœ$59ˆ‘+Ò.€‚fºû羘"b”cmpµ±÷+c9ÆYJí¬`ÀžçRQìxÁ *®ÄY‘f}zqnCb²Ð%z^ˆ³¤(”¡æu òˆ³î"ÍL®8Û bÉ„,Ìÿnü„,óhã'{)ò‚}>FLù<¥'Là,pcDˆÍÛ@Ž8÷ŽÈ›äX‹FöæÙà Ä;xŠìQ>bãiÓÐÙ4ÁÿÆ[´áÙÊÃÏ5çìib°¦çÓ¼™qû‹¯£'[hB)Ù¬›½zÏ=iÕ Ž §*éxÌr.| òHWÑ䥸ž„,°Ãîébôóë‹hªœW¤¼¥G4Ì‘€Þ.乡NP 1ˆ¨vª= Y¯{B™hlàr­É2_䯢8"‡~&P’ÆæUiê›w©6,ÀÕcFhµ[ÖÛ‘¤¢áŒ„å/¢Ñ»\ÄØ„1z¾GLÞJcý;𧝢‘F‚гŽMæ•éÖ·HvuQdHx¡„œÃЇ˜ì‡±ÛTxC•‚¼wú¡;\‚5IgŸU1‰Ö&Ù%†Ñ¦‰ÄшÉmúJuÉžˆ¨Í"k;ˆîN‰/aÂTß/»*é¯áhyhŒFæ§7‰tE¨ñˆÙDÎ2©<,ÜB21𠇎'щhØH‰ÿÿý=CÃ*šy$¸…‰®Qb­(j’ÑpôÈØÌ¶¸Ï~”‚fºû·Ù&)F3ż½§l¯l»tÆÍ`ÄkÈÎSÔbñ j·en‰6ÆSÏYû¢O)÷ _N™žzš×í“#äÕ\,Ÿþ“ÂtΙÙ8æüÝ ³§únéÅ´½½<)Ûd‰è’€»34–xôaQ®(’ÔBÍ“7ã±!ëߌðgJY<âÃSDÄ2BBêX3Ú¥ž-Ĥ]J®ÃT‡¡ÊU¬å–V帕ëå©ã‘œ,Í 8ÉÙÅdï1¶•|Y´NzëIú8šžl@`’…!ÙVæäLâ–¹ã…3LïúÊ0T'èpü‡Çµú~yYŸÅ#ù|̘L]GtÁ‹vþ¶™%)”¾©?Ù¦{7؃B 2Ù\sôX’Âdý&ܵ¢½q:S ¤a9j~Õè‘“zim8¬ÓŠ Ø³]ÁŸ›­Ñ4‡v?od=c H>6ÖÐk©Y?L‘1Ú4«èö.Z´°Gê+û’— «‹jåýoá‘wvl0BʾÍI‡¯y,7ÐDšF61€ÿ×,&CQ Ùc†®c*Qƒ­¾Îî¿aD~M¯)Î\ÌŒ1€2E‹‰SmÖÞ¤ †7¸&[[ú >¨)] øy e4‰{Giú!YÝ*%šBaÍ,¼ölÔÄ$÷ ß÷þ%”™Ù©™rÆŽÙU1ë>ÒÄáõc#Pù›rK+MH«]ë{*œ€Ö£fPçFäBà3A`©ß*&ï³A„f)ˆßa©Hkà›¬ŸžUru°ˆ°öØK"íÚUåÛ£îj2ª¼vO²ƒ¾Ð»CŠ/»ù Öua,ãÌÒ–qåX-ÃX ffep´«÷”4]Ì;ê¸ÜFJ˜¼æ‡¢ƒ¾¯ÆaVýüýaVÙéùéÿÔ|&þlf—†eÍ—M›Å¿èürL £©Žc$$$¶Óßž“?™™Tʸ^#3e|1âhÕrÉ×ÄÌùÚwÇoë;Õ»ûý§ƒØ-¿D4‘U£<Ù´†×»Å úvÃFy¿krl^ß±xÕ têÔ/iDadyÓ²AfˆÊ°Ò# #ÏÙû"«ÎàG fV<£Ãˆj$¶ÓÃ%ú¯ª€ØÓ1u-ý[»e/1ÁËÓSFšã²ytŽ<ªxEÑ=|I7tËcñ*]ócÆŸ7‰8zn*ob##f`Ä"@ð”v*)c“@ÿF”ü`A®¼ðÒ8Ó >´ð˜“Y+õ¼}&ÏÞüïϬ¥“XïN$î9 9'“c}N#žûÿÙÑ«1 äPÇ 1F 8,dŒªÂ % `¯r‘¬‘ÆšŒê·†4Ùµ>K ]`ƒì:`+.¡Ôsuö¾$&ž0#/p*.29¸ô9<Ê쪉EUºªzšYN’éå*¢«jÞ–'`‰O¬þnL\‰´ ®”Çe>‚Úé³ÁCƒ|Ø×’Qô•å%8CX„xƆ;ô“E %"Ìù€fJ—i›@îÖõ&Õž™™Åöت·â狃‘;Þñf^c' ÆÐs–õ<¯ÜzáBš'“<ÅÔ&ᘷR 'Í@¤ãkn90“]ªµf»Ê?a¦ «Ø£ŒÓè”Ø|“ç¯k Ì< î¢Âô¸AK2ZPaHo¹.Ã#Œ!•šÊ¤£¸úÞy¶—8W„6;&»þ·…F+V•F‚âd¬öÍŽÆG6"\ãǤ÷åMˆØ9ŒNûJ6så Ï…Æ÷nÙg$bóRSƒ¯Eˆð*§Ž;>Øsꪢ í^kLŠåy´NwxŠ>–¾«‡­)øÙs¿eËÌDÑòlt•&ÂH†h–…KÒŽê~€–¹aêÆ…v¥LçŸ )Æ\´°´^åª^OÌä\ýýõ}̈CÛ ÏJFI«f.¤11Q“9?˜yxÑ;¶ò¥#ˆÇ.ëá/žÆ– ¯äW¢¹¶à¡À´Gbê^ÏSZp6Ç:¦òޤÆ+,ÐÝ,ŠNÆù󓌙Jì›ö”$ÿÿýmÀÛ•éÝ$bè5£ËÇÄÌÔeçcwª¹Vôÿèæûh76 ièÿÿÿÐc@;‹ˆIAÿ$ºÓ4›âÇ´µuj ‚f¾ë÷K’ãDÐã­#¶§Tê΄ƞðe ÚQᔺe,Q…¿^-6Æu©ÖÑÛ™óH —“4®Ó»àÅö/¥‘7„ÇmH-xÀžš”(©©«é«Á7‡{jè’ÓŒíÌK NE%¦">ˆCfÈ÷B”䤱H4ŽÚÖþ n‰'`ÙB™¡ )w†Ç£$æÁ²aJ¸Òæ&Úh¼cbüÛÊÏ·“˜¦t{ü™2'ÏmÙà·r¢U†nÚÊé¡ öC ˆScБ½²ÇŒ’ÖÉšŒŽ(Û7Qó7ý»jV̘íÜÿ8`ð€Yž³¹Z6Z±·ŽRœõJ~|†ÛèA t]¤ÌÐ$„ØD(ø–¿™¥Eb‹è²G©j8eöÐ CP‡ÿýHY‰ä/7×ߜ٩õåòÝÂÐìüLíÞ¿ö-×G-P²E’œs"PPÿÿÿý°ˆ¢‡+‡}>Õ–Õ^aÍŠ¯ByS:åZ÷5T«9zI9‚fŸm’䛥Ÿ¾ ¥m[Ì“ì©Êž¢öK˜Óú-öFŠ/×ÊçÛ™ëË‘tÜŒŠsíŒØb…Äõ6*S3Ì‘‚ªÚ¨¾¨ÌRÕ£f‡B Dæš6( Ù^ª™GŽË®~iT)mm>Ü”³aÍûo˜9–Ì\.m #4½Þ>xÓuÔ—?ÖÓhIæ©.9å‘rŠpw ÓQÁ«Ôt²Ù°Oùü‚T3â]ö…t ¾$a…?øoïÿé~yÓÔÔ >7EÜBÛ¤ñú°­KXv{fxhe¡©ø°šJ¹R÷S… ’Ä/ˆ#ý¦O©’å½tò­˜s©\Y¾>$p¦U»hþ¿D=@ÃŒ%¹¤JÔfffeoµƒ°Ñu(GAD\†6B” NÔv é}³|d(Dà€ÿÿôŒ©SžÂ],ãO¼tQÊ‚m÷ã$Ð×퀸¦áÙrų…Š´øjÄEg™ŒäŒ²°õ)0¤6T°ÿÿÿÐÖ` ÈôѰHŸ¬KK>€ÿÐi0$€Õù´èÀƒîþîŸeTÛ¾-·é/¥€‡.Ç;¯D=½Ñ1»U›r„^“Ì Ð%FUí­ªÄŸß‰ØkFѧ0;ÿ!@Å·Ð…èĆhÑx9U-£¼ÝÇ”=½…'¾¶ßoû[–ë4{½> tJVQ-Þ6~ÔŽ€ƒîîîžeT߆ ªgŽÉ€‡/më²çö¿ù1?ÚBŒœÒƒû9´»É‡R\•«%ÈJ[)1‹7«¤,¸Íó¹íÅœ?bCÀKötWR*ÔË´^ɘµó™Id«ˆÌ¯UªØêu] ›­œÈœú±ŸZ¦ÈÖ#2Ð&þ¹ŠrÚ˜4JkgæX¢/ Tʹ긖vbZÚhn Tïÿõ€`À( Âÿ×óvš…>­F¼ž43¥cO!S^2­ˆ‚núý’ô›Å[—ec¥:²•ój„€É¥™ä¢|k¼Ñ$;½n¹±V)ÃMÍØ|êœ@ÆI§')7'×iËT°õA‘ø¾þ{3)è‘Ź–Æ ŠÏiP¼‰˜åK¢ù®rõ/"Xö®¥ç@³µ@QVÿEùu8)³CúÅ„·EûóÜϺ°ã˜faPª»¥Ð×"!Ñy÷B[ì3N•YKÄ(F`E2ÄÇ÷½!ðœmTuÉÃj\:»`Ë‹òðÒ``HºÂ8 ÄfèÕ-œg ÕßhŽÊ>ñá‹ÂFý4ñIuÅêü”ìÀ}$D„ÿÔóµ›ÈFtØ„¼ÇÖ™3«„º_$ýö$ÚFŽ®KìÏ^zÖ9‚‚fÞù·IŒ]íY[áX){Œ0 KçHžùÃ)ót¯ú«„*\Å©:4´À`¶bÔ‰Ÿ½Ù(¸Ë2fŒÅ®‘ÊYr»É§Løãsþ5½&®‰M ø²ygÜÏŒ“´]cÊ…l-+œŒÈù7ŠÄ? °b&+™¬CíÿÿÖ°,‚Ë ²h)!KŸipÒžJ2è´O‚m÷ã%[no©{VŽlduF—Šï°Å$Ìb¹˜M)€¤`1zC™.J?=ðÍMì¸WgëaÕÍ)‹å¨7rÁƒˆÀ—ˆŠÃ†mÐUÖ²¶‡äõ÷ßdÚ²±„¤ZZ¬¬Å°Œ¨.@¨´Ÿ¸"„›‰:A‚„ÆŒWI–“™âÜ9îÚ†‚g¾+ïŸyÒô¸ÁVbZZhbUQqkÝyÚ2i5!,"žùsyn¼ÔB¬bôK¾!Q{s½*wŸxרŒ²— …,ŠØ-b×mQÒfªáÌky«–hÊÛ,µa/ ø)ª©oº³¤iP€õ?{øòK–ÜÀÈMvèÊ(ôÒá1^„æÎÄæ.•'îˆæŽc¶®@â `_aðIê©LÈÃâ ¯3+ÂG¼9U5#ɧ˜ðÕHÓ@v)áDñ[1ˆ#lÄ (Ê ¼c§Ñ÷uµÚ^J /gß™·0—%‡ÔQ’vcñUj‹Žm†µzƳ{Ö¥)gÍn²}v0Ï#5@é3!"tng#ýº«I–·o͉w¨élšÆËëŽ$TÒ;j!Èb¢«»`Ѓ’Ó¬DD™ÿÿÿý ¼ Ö‘œÒcº·Œ†<¤FN%|4õîefÑ5×SåS_þñÙGêâÿÿд!N7ȈœÄ¦SR½ZÉ"’sŸ¨`‚îšëîÝ/0QŸò2§\«8¶Ù‘ç½€¾Ö¹©sH§5RQ.-ïõŠè·œ³ßH­Žç_ÄŒQF_Ì ÷:fÝéó]‘ƒkñRa;~÷’«ËtB䀟¹;ÅÚ8¶NÍTâ¾—ŽÌF‘‹°!V`E]<ÀDym¬å±¼*Û Qk^s à!m„ñ=ú¦« Ãg¢f„ñŸk Δ.¯ÔÏ+x 1nd^â–†¹9mÌQÉiYeô?DC^ðÑ)¯Äb”Ó°M1I›´Ò‚È%9I³‘ÅÒ<ãäE }1èx¯ÜÒ'™î5Ïs]{WKv]*ò“¾õï ô¯(ó,ŸŒó¬æªãI³/Lóƒ¤óƒÆœN·©Q2Qµ8€…¥ Ãt¥˜Û'e›eSr¾9"àÒ“ ¤Ôx%´¬¬h\!hÂ,^–•¨ñëÉjZ¯š*–xõ­3§£;Yšó0–Ë]½5Æ·‰»ø@à¬4“X#@;Æ;ž–Ùã|œò’‹“«@ H ÀýÿÿôÝ™¡¤Â—{&ît0DÓqWW·—›Ñ Õ’42^€ƒüîme¹vJŽtѳ¥)dJNØY& cÑËK®ªtuW1’3Á/þQ;W ‰ ¥G‡U÷ZÐ £­*¾È*Ø¡ýî%@·¨ã(§\rG&¢ƒía\æC•!ˆZ¤‡{Ãyg‰«^â íQY žS‹û± ¢¼YlÓ 4“Yo¦¾îùVä²ÛIb¿É÷Ø@ Æ¢¹H²!í ÖD ¢ÞUÁ’q˰'æ’í§oU±asj‹ßI&ÕÉ'Í KH1ŒQÒ"Š)ïI+p ˜ ‚f¾ùñ‚½/I¸U»Ý7ý3ò2=î˜ÇÒ žÜ}mç†GÒë'n³©3fH¡YY~ùŸÑ‹Ö†wçá¯Smìsä-ƒ“šp ¿Á†tšzä«ùb©±%¡iÅAS±§95cƒ*2Y\TBk{ÉÏ-8e1ª,S äJþ•Zššœ¡eZÅŸd¦é74Èó“·WSDzmc±|ÂÂ…QÑt³Å<ô Áˆ,u©hf€Ç‡¹&çÆ™ ¡,’þ¢œivÜLb¨Uì /ƶ@”ûýê´L £gÞÛ°Ê[‡Ý•ºÝ ã@kùèÇ`m@ºM$JÂtÚ »W¼‚B¿6×®›äªÐøÂE”¡Ò ÆB–'ˆqpF%åæ–5hcšÈaQ5¿ã,@Dšix‰lÈ"s‘ ¾¢¹+ÓCÈx>²g@QlÂR"3÷ hÿÿÿýnû“frȧ¬K6.ˆ¬dJÏr·4¿Ü^GlS{sh^HjÖ¿‚f[ï¾{ïý/IŠU˜E¥†cÞxÐé Ó Èïl‡=mt€@ö(a#î“®¤Å]°»ÅøúQ]µk Ø4`j‚!ðÎÍ Iv¸u£Y´4Àaè¿X@‡=Y‘<ê³3çºâÄ#~€Ù‚ËoQÙdĪ–ùi¶œ™ EÎõòSK 1C̯;q(ËDLÕòmvphÔ ¿Ð¥ 2|¥¾â ’Šô¦&íRáÉ:nÓW«|à6øÄ?{£U @§Ue‹ÕÀŠæ‰Þm7ÂÖ˜¥ŽÊͲµ/hy¾õÄ]šÎ’XÑ-œÕLÑõ4ßλ&m¯&÷3"´W2K•Ó':ç³Ñ²³±”Ì{'jÑ©°Ó cñ]2ší,èÛ^¤Ž«q„ lÓJ™¹SŽ<ˆdö)éFXÑE­=æ­]£¤H®°¬²Œ¹‰È†C<ò2ÄÛ[ኩ7…–u·…Ôj²Ê‘b©¶VµFclªÐ0‘o*R±V“Õ¶(¼ûšê&åôu‰jv+F  Ÿ©û®»j}©gLæÉ{tŸþdpšð«C®Ôš¦ýÅ ö¯„‘R†ÅF‡á$TEš’몈„ƒŒÇ™Çš0r”‘N¦ýÆŒ¯óF’y=Ü; FLÅré€8ßHuéÕa™x’Z <`?Vÿõ™jȲÊî|K<„®åÉ(…•r¹ðy{YÓ^0Ð ÿÐô>7Rèžy‚f¾˜+ç¿ô½& Vz8|eI¡bÛƒ õvÝpça1XžçÈÎqÎ(Ý ¢¥å¬ñšò¶7†Þĸl0• 놈`ÜAá*-þEfŒñ¾ÖXŽ åÃHT\64g’}Ú3Öj%£;üQ8ÈT®†¨þåGµØZ~hÈÜ?pe¤½?jÜ?|¯íÃö[ÙŽ ‡É1•YóȈ3ÂÖäÈ6Æì’¬gY½Æ°†¤(%š{‡ÁsBrøeÁ|µ¼[wåIönK|ÓüŠçÉ9蟋nE&‹m—ƒF‚[‰HÏŸ‹†Î‘ÈìÙŽ¶Éˆ@¯Ã ‚Úï…­»«éYSžH¤BD÷ltŒÆD'êÇF4ÉÏ´1\ˆ<ı¦Y ÂBëlH±)PÔ>6_“{i! „À„a¨9˜š¦~…¸/qÝb³+8¸k6W«JôËz­ã¬o•ù×3–«(óeÐñ½•¹(üNÅ:è5Žæª½±k`ï<£17Nsâ26í¨",€ð8€ÿÿýy®™O"p˜ lÖ¯Ò4‰0 ¬$“ ÔÁ€Ãö"6ToR\µÇù«ZýJ š7b¥ tx3‚eºý”ã%ÞFæ*®¶¦Uý˜žåãšÍâyšÊoœ¾qlUfaá1Ϩÿòli£! Z7t·TfÒìÁfn2µ™¾|æ³3Ü •õ™¦Ã@ˆÅ™ž8t„=#¯ññ¦•ÆäŒÖV§Zž|iÿ.¹²Èž·y›3cf{)OUMi=)t¡’ÍMè9f¶/Wì¶™øñ¾Ë”?¬D½¤i›´*©êT쨠ìqGO6 Ä:Hðñ–ô_2½óD2×á;‰îc ñG38×2ѵÝÂS’oGÓ a‰¤m²å ø@r)R0Ä]®áoØ¥ß1Õ:çÖqÅ£ôìîK•UèYÒ(Ékß*ß ŽÈ2nС¯´ûì–ZÓ9‡3TýJ-q­òÄkDiž¶jÄm'ox &s.Èb¯»‡ŸçÈwn€2€šYÿÖšëK·€ËAIåÚçÈßm’[ô·å•‰Mƒ‘‘‘…!uÿÿAÇ`(QH“hrI/ÓRŽë ‚gÆ í·½/IºU»\d:sC¼ •Œãe°„÷ï«ãFF‚9“D)«„ü¤Œ!°’†:6¯yÚ‰v&c%1¾  WJØkU\žÛÖ°ä“JîšódÞÏ7Ÿù¿XpSnb®› ˜Ø¸°Ü;ä÷!®üÌ–&rœd@¨R´âФÅ!a¡§„/è‹Ie™e»ËΕ´LteÀ±Þḑ8±ÜÆ¡ XïqÚʇzrêÇ}-ö Àb굕÷Ï2Ç{Ôi¼²’Ê+b‹š»'n„Ùz©h„÷V«Ð176‘ ¬ÏDXÆFoûî±ÐÎAM´‹á} íùž~•—Ò¹‹ @ÍUˆ‡•“&‚ƒ}Z.Dª‘_ñÚY‹ž¢‚8I{=`R· ²k9+¹1 ’ŒLêäMOÆø?fŽ—â:(Ž¿kÔÞM^/âYjæÏD·ÈqÍŒ¡ ­WÉÆ«…eý–ë‰ø>Mg˜Ûèö×»vud µjØøÊK.H€™°Ý–켑ַc‘ËHÕ#I#ÑË[›’aÄF-%¬È¤!·{¯‡þÖRÆP¢`f€ñŠšªˆÀëæ|Õ¢rÉp4s°°­¡3•e91cfn‡w¤¬ì,$€‚î¿{羘"b—lꉵ}r¥ü’”Þ×áñ€¾Û3!– èŠòéMJ‰düœ˜Éí飒™‚‚\J'U#50gjE#-±í]Y“‹R[†¢mDŒ³>9IÝ;p¤Î䌳~bFmÊÕ?\Ö¿¢­­ªm”‘•–(°ŸX RF]©ji€B ²BFXϽ#,gÿ´R2¼‘ ¦2ìHË•#-lÁ#-Þ‘•ˆe4ŽA£¡IœO®†ƒäIßâÒ™2†.–q­I¾ØfeOÜd5Ä£a¼„ù–Ù%ÖFÓª&Í6¡ù•-®#™*:dÏbZjÞâ’‘››Ž›ioH¼ãF×~W@ÖÉ Mè.„ ‰Æ!ÓùŸ»kœ¹üiƒÒ ¶œ(ª¯y2ä+bwé4)‚žªªÊ©‚¨^ÏLŠ«£]kÍio†b™2oà¢u›ùFk[)Qâ^D¹´Ž|afR›V”8¢¯”µ¿‡Ö„õf#DéFU%ª)d]ÄPFžØF›MÈIÕÆì3мwSA¹ ¬kGÂóBˆÂ̓ˎtÍtþµJÈ¥ ý5¶\¥‰´„nc¦¬¨fr]2F®ësHd¢ ›î[ŠR1ÆsØšRÊÈd‰Æ¤Ø­V(¥Šõ业óF²!e™³#”ˆû ¡Ÿ**bMb~\æÞD¾5CuòC"2C ZÌ*0O¤ñÍi{ŸdΆ-$5MXµ‹VUkÕ1\“Ù•quæc"€Ÿ—®:”vÃ-Ë a†\–e(Zb©`¨=ÇîÛœ·¢í_©_dŒ·E¶XÈô¦åaäœÉÿÿÿC €C0~ÆêScžL¤Fgj…–0óh–Ͳ¡6€‚ïŸ{ï¾ÿ’ô˜¥Y¥%•Û9rb.e©rlҹϷ—’A¶ý—¹Gé»Uñµ@Úõ„¦å>ÆGdzÛ)>”Éö—éO»)3¹‰*š;K1i°t‰°Ž!‰?øº`z_4¿¤¥}ä`N¼áB<ÐÅë#(°hƒ,6 cí £Ùªl2B¡ƒ#¦·¤f¤=¢Fv§xi­­ÚY¤È¸ÉŒgBD°¿Nω.çB.VWŠÿ43,›úKÕŸÖÝ &ªôÁ¥'8Óc«ÚgQÙ©Ûýè6ï¬wQýí&',æ¼ÌÁÜ~g.ÿU<¬yŠÖ€‘\©Øxh­Y ÈÉ9Ëû“ýÖu­ ĆÓÄ”êM"‡dfIý1ýaʇÅwõO”êG¿þ˜ÂThC²50­ôø½Os‰ÊD;2\âI¾YÑCN)3{ôãS¶Hm5m³<ÌŸ ~‡If›ù£¢x‘„ÀÁ* 9GLktMî[fƒÌ¼ÏÅ<óÙ qæVâJq˜y’KÖþÉ{9ñf¶ÏÊž·å~?îqÖ¦Æñy©1–Àî¦%&Ldöÿ)nL¹LìQVÑœíAô·HsÈéçMÛWëààI”ñt¯I%ô4`S\ûD0¼à¢ó6˰}Ldca¨KšµºV4¸ú¶¹Ì6öÛŠ® 5¤Iu–“ÞwGÀ»ç‚݃ÖÞpšLljÒ=ÖfcÃQ°™¿ÄEX‹ ‚Äw.‘H™Y­ØÞš=¸µ0[Eã,kmBÒL=GtðaŠ{e—Ác•wF–´™çŠÕ%ŒgÂ*C =cóP‚¹$eT–Ë(ÿÿÿý‚ ¡é’"cœÒã7ûâÅtJÇܲu9r–“Ä®6š…ô›GzÿÿýÀ 1"—q3‹ahÛã"ƒ3Z@1•êÿÉ…Þž­]}ºË²NØ{¦žçÅÝÒU›¥ëÍÌ É1Y6Œc"Éq»vj\«‰¥¢ãVÉc¦ÌÈ–þ‹šÂXZÚC~æîYÇŸƒäªC'ð4@ãvD)ª¢ÎZ“ÚŽÓ)èÚ-™É|ûIE*²`«ŽNNì<2uvK k*´cÒt) ß%H±Ø¦@“C‡¶$ØŠ¼¶%i/)«žÏc"ö|ºe**j¨©€"¯J•ת*~ºÔæäíENÕi | ò+ØE[¿’¢¦ fÊ(·•9¡6¦XN“ ¤‚P‹+†õöKÙ?«´`¨#¨†È¹ÝMºÍ‡ /6!t‰eðᔌ*g˜9`–‡Å‚ÊŒ‡ˆÒ‚‡Þ‘Q÷Û} [óL¦"Ͼv.ÐÎ++Ç[¬¥É’Þ€¾Š”ïa“HkÙØ÷ÑwLš§«­š¯ÀÓÛC5Û+3ò÷ˆñE,l«¡Eä“Q^oÍ¥E34€ÿý´>8²¤¼#éMZ½`‚îžýá‚Þô¹'Uš¥†Ù]µõ»¶m‘æo‡s.²@¼÷Ã’›N~lÕU\9mi.#³ŒFÒRl*ݼ˜þ?Ïû¾Óä‰IÍÝÃ- _~íŒiWÜŽ!ÿmåó?¸òcçdÀ¥¹Ë‡cúó~õŸsú}™¾ÀÛ¨/j†Œ{3B£ÖídÕ¢t*6‚^âV¤+ëÁbžd`dv¢>šcFÐc¼bX«ù4m\†+IŸ U\ÍqÒA˜ EJµÏCN³¡À±›„h Eã~+Ýsù9|÷-z6€m'øZƒÞ4—àÕÍ:ëèµÙ}i­ýÿdb[A§I°¾F–+ôY|k*Ö`ˆ[o“pÐìðÍlk.3²1ö'‡B­ñZÍi%:s+H4XBCx0ˆeï[EVqé™!0«E”„Ó&"‚ƒ\&¸`ˆÆ®¬Ê&çµ|m)BBWÔ=Ð,0N>–Ls¹)×ïAœiŒ¦oîKWBÄlIìvÁÔçADÍLĦDÓ–ª{u…>ZŒœÄ™8¸¦<ÔÍɘîS˜ÜCaýQLœ !œ$ƒÛ”‡bÐ(L šÚ³z¡ß°³Ê*…ÔFú§Êhé†&ÒŽB™Sh«Öþ¹æ@ý/³Ue»(>=Áƒ ™S*eÍ*yŸ!èQõ“]\ÇŸÁÅ’¸15Mß0lO’ìeJ‘™ížù@ÿNÈ@=qCG„­q\×3C~¾Tù6êGTH¢|!ާe·g’4"Ǻ:}ßlP™;%õf3Œ švÇ›cj™._á]ØNv€™×äºÈe¶écŠâ"ÉUµú`¢S.ȧƪ› dD¨`á(BÀå‡ýŒ½aaAšOZÏŒ÷qdb´ ’%!âæ5DBNPE¾¾Ð-ÇTU ƒyކœÖCÅ0Ä3Ì8^ã,ØÄáÑf&F‹ÊÚHÒ“^$ûUÁŠõw€USŒDJ×þ@ƒV]Ýn•åZñY&°‰b>ÍÉCâ[#·+Þ‰1Âk¬™Ý«Í­ç ™°@øßN½n#àÁƒ ½[saønt޼£ky#†,ƒÎÞýaiöZz3Ktb~ÈѦìqxÕòOÛcÔ+ ÅÚ}-r£‘ |yK|ôâÂ'U(ÅñÅÓŠûŠ(´©¡,vºú4AèÅ„áu•Š0©Zö¨ò•³9‡èd¹®‰R4˜8¶F7žÌ«tîk?Qô€ŒÈ¢Æ¼‚ÃÃï㎼U$ªÀˆªÊ+Tr O—I¥Û$¤«.·š}ùƒbsfTKõ{%+ÊP¸ß•²P¿NV”.6ç(ŒP’ªÆ¥v>€£.„¡¿x.Æ¡ch’´ÍïŸþ\`¶#e•R][ZáUP¸Acæ)ÿF=EZeì!VXÁ¢ &F€‚f¾ý¶Ý.0Uºå‘€]\òŒ7):§M€ž£øœÈz¬˜«¶"®Š·çŠ©©ë-aÌ`ÚÔ¤¯løŸyAIF‚ ¼›¢ÑÝbXáŒÚ r†q‘)±ÙJu;1쇶³1c“Šû½mJ¨+c6VÇ%OÐDiU]Q°´‚…ðJÜÏ!a¿àN¡.Ê\|*è;RÈeGîÑÞQ€±7\J{³ÄÉ.jÅ«4B••Qf$ ‘nf Ðec‹Cª¹i¨É%lV¤’¬ý2FËÂHW,¡“ZES¬$“¥‡ovfRÛÐÔ)%t¤…:ˆBVó±ýI’Á"ÞØ8È8ó§`—F’0fAc`ý7‘­¢sH–ó)ê€wn©Ù÷›eR§­Ž‘©ô…¨º-ÙÆ¨u°bÞ¤Š%fé­uŠ˜²‹EöÎ~—´,L˜ ~“TŠˆy‡Ïð»w™T Bb@2€µÿÿG‰ˆ2\v ¥Qäfzÿœ º¯§­3.âe—‰& ºÁ3‡SHmÙýÌÍ⟲hƒÎÞîïaiöAš÷>²mÓípÀžçŪJJ7iÕjȪwdV87RC©¶ˆ¹‘S2Ïy!õ?^’ˆ£dú¥Ä›”È«*ãÕ9L™êdUó¸QƒHÅܳ:«LŠ ÒɆFT{:ŸMª’ø› 4Æ”ʬkåS3‘G«Ëqd±¤›ôR]KŸWÍâJ»d0 ®"ghã4ÅKu8UzÕã6æÏÖxªÙˆ¯´âš]n?à–DP¥WÅÿ½/×yò¨½xJµOºÈi¥Ý裟Â<è6YÉ»sè+ }õ.œ×ò*°¥Ü–…‰—Ç_Ñ!a‘.ëʸ9TÂF‹S…@…^`ó¾ìc¹ áIê߈•.¼ï‰ß¶¦®îï/3ÎÒ’ýw"qñ­áJÆ«¢+nÆÇ3 ¤ ë @ÿÿôw`Cƒ@Yæò¢4¸í|óQ5Kíz°ƒÝîþžiD|­8ÑŽ`޶ô$šža†qxÜ£ñPgP_Fÿl(‰{Å2∗mÿ "t§ýº’bveËK8ÚC™¶UÒEì…sI¦#ÒÙÔ’;K™‚ ¿yö/]*£!¨Sggâ0„—cšGb÷G£´;·Ë‡:£+KÉ™ ŠcÑÈ8¸ÿ:w¹F3B›Gë&”¬rDžÔެD­Öí1ò'þ»$lµ¬ºÖJ™§$Ê Bp \ÿýi|E|LÉ’D ´sÐŽrÕ ‚n[®Ûó[®4âûeú=ù¢>¨†‹/öZÔ‡“ $RsA-ù=|€¦Æ†ÊH¢z|õ>–ã$½²±9½SÙ©¨,<•@¬î¦"Í èmFAEB.#Øp¾ÐÞ—28‰"OÐCOpã)  ³‰.FoGD&î¿@¦húŽ$xê”aDá<ÀCá n_M 2øE—h9c=¯Õ5PYHƒ%Ü v)&áÅŠ¿frb³Óª8v yŠ"$£`1¼â#ÙÞheá‘_g|‹öÈR%‚…”ÑbZNèów*¾ÄagâØn½Æ„ðÈ–;NÜpcTaâOU´Q!ÈZJ«Âú‚æ ºû邞ûé‚&˜…Ýï}î.Þ–=rªÞõ¾¶l´˜oÔÁE`½œ–Íyy4!•(XQ~Âó2ƒ ’¸ƒo½hßUR(n‰'»ž£äv&k &Ší0¥FK¢IéÚS6•­ÕZ›¿3(JyœLÀž”Öóª£Óx³=ÝoU×!ø¬’`ºC.kyý²!Ùn¼yÁað= βJÓ¹ø9˜Ïo¬’Ê÷ÉèYÒøIlvë§Æ9Y%%Ü0~îÔäà¶y^nGY;Eò:ÍÛO™ÖÎÛt§ «xbÂI‘¦“è…= `A¹R÷u¹ÀórfQ­æ›’Æ÷WS"±÷ñŒ¡i }ˆ7 ~ÒK=‘Î"„€Š¶$Èú’vT/¨Ú¸“6Èêó÷~Ïk ªïEla^%tV«7Γí;³˜¦çëUŠiy¢á&Û¨¹îÞÙkg†k±Z…ÊdNÔÉ&oÊ*\c’zU26%ø.†™Ìt/gvŒ™Ú_Æö¡$*+¹Žy˜LN¶&©¤É"± “yŠ Hr·=ž¥‹[ìÈBâ‚F’ÌÉwo:^q_`Þä ½?ªÙé,:pŠT’œ·,çño.B³(VÕ.bù- @›[5`Eºiv1È)ë™0®QiyŸ7¾OIeIÌžK•Êå]ù‹fß.CŸ…Û~F„žJáÄp×­ÄGãØ‹~zdfE.>SÜ<Ðæ®Vʦ‘8« šéÂA”¨ß .¥£Ccq;‡…ð³álàiòìÌ"¡ X(1(ƒàÛ(AÊuK7LZÊ¢d¯¸"F€¦º]¯Äk„#ó5‡ÿø´êE½‰SLIh!A—Jï™hffŠPTô™nP¯)A 0C_ÿõtÂPŒ¡f%à1ëÈǬûÄcÕðŸÕއ̓ÝZ$§·û õM.™G:4`ÿÿôna$jQÇ*8èâ x4«FÜQóVÈ‚îšýé‚»yÒ䘥Ÿ÷ÃÏFBç–õ1N×Ýxó賂½·$܇ûæ©Bÿ/",-¶ò"ÒM(Âq av$‚ó6P¼e$‚÷þ2a ÈØeÚ¥]äDfóËè%i–Ð7®B!œé­ÓñŽâá-x§¼˜ŠòcZK ÙtLRy=äÃèN˜¶¶^L7y.òbûÉŠdˆŠOG“M­êôòb™¸[eVÙ¯&?CÑ0λV¦[¢`×»•Ë„## 8+‹ ´£]™U♥&9¼:°.$[KŠƒê¾G¤÷M–œå]éF·0´f€îÔBR€—¾„Ž“Ì ÃúFš¥&gÐyŽ™ÎÛº©Œ¶ôòõUÍ3%*2æ Ò0°´2 ª)®©ãçªç`áDÂHdŠö5yeÁù&6Ñm´å!-6hlÆ–>²q`©ªö1b¦ ™2 诱ÇX(G5 3I3iXŸù%²÷ËSPÚ@[ ºR Š<ћɓ™X"wÙ§³>þØ4®»[¯ÔcºÅ•­¦c5>øKNjôÎ$™ØÊ¦€·ïÖ!¹ 5¢´´å¹“ÈÅ ‰kxí½±uŽßìJÖ¬68zâHFA’áÉÊԼ湆pHNÜ6Œàæ„RÊêÉ cÆ*ÌAEŦt†7U†žºC/K06ï˜_EAi"¥J÷É&I.€ÿÿÿý†€7ÊpO ÐDzöZ]%¾Hã5«øÖl.n^g»¥o¡WÝŽ$ôh8âZƒÝýee©lÈ¢Dð’ÎaÚ”C0GË@£dËàÌP“6¤­§êí\‹¬äuU6€k¡—ì*JÈ¡]®¼11ìxLR¥QÎW²_}I^÷‹ø+ •*U>KK ƒ¿ÏÆaVaOÖýþažaTglWêc>Ó]*‡®»ðY‚ˆÉ}^Õ$@¶‘q9©ºçÑÙR¿ R ŒCñ? i¬W¡Å&¦Cä S –¬‡ªÛKò?;ó? ð>Y†Œá‚¹%Œ¿N¹ôþeDæÙË`¹# ¿ Äq)å¢=¶LãKv¹îdšEœq]£‘gÊhÎ n&ÌØ‹[ å†Eöv~¬š‘è†eêÂás‚5œý”ƒJd¦É”‡ÊegÉ€ÿÆ2ž"K.vcdâ@Ž%<᲋ZŒd–+–xƒ¾H$ci¯“9R\¥_[¹N °J”¶XI{xn¼Æñ ešäˆð**Îkç™4„U/ #‹Ñš¦Yžs!s´åß-¡ Kí’Ö Ut9q:ÊUösc™Õ„’voÕ™Ñ䲊{Ÿ(Ö@5¡=8ºqÂÕ˜Àú„Ä3²Ä¹:l8¡Ky×U#Úe‹…ÒàüðÅ&`~^ƹ¶‚ ¤ }ÁùÕó;Š«~2ž< ]0(rd¾~ÐëžA¬õ¸zsFâù_x†³§D ­2xƳÓ/=‘ˆ5ÆÁêoc‡ËÌï-ÒVåÂðF£w¢~„¤uÃU&ÆØÑÓcăžNš°EǼÝÃ:$[q¤h`ž¾#Ã8ƒ8©h‰ ˜˜P¤‹(½¡<ì¢WV¡•*V¹¶ZV·õ–ž9X£&7-¡2ý!}ÌøOö²¥  c?ïÎE“ãkBaAÄñ*aªtúV‘,+]^L†¡Å“ÓI Uk³‚hÒù‰Þ–m4;ÓmçxD£ºûôRe²sºyZR>À¿c‰™‚ѶRÚâÎU#!íûžN{peçÜf† Ùj\Y—-?P-”M/p¥'¿S¼Æ›‘„Õêfš Ðæ,õŒéã“è™ëü¨qUy ¯˜ºg– 0g­9ýß<&/1z!·ý(¬ò¬ö­@h†–Íëö ŒÝ#›h ¡»™Nüam]óÒQåš9rXSNu«äBÜ…Íõ¸c¾½þÁûõy¨éìu4®ßyÐr(Œ³.-·þøQ4ÚÎXQg"¸ÅÓÂRÝÆ[×=Ôv"•†È~½Í ·a ¨MÏžÐ2¡³¼<åÏI ÇÁˆ °`Þ­¶Ú®.æ²T†ÒæsÁp±EÅ¿8&}Iƒ†$ÁoÝ3Mä„&(É€•(¹sq*EX<¸êß–Üz%*+´LÙ0jGï<ºWL­ãzÙVÛUQ<<âîÈ€ÿÿôRÂö r‰Jº':ÔeDVÏBùà"C(‚Î[î¾{öKÌ—zv÷vy9wê: 4 ²åŸÅ€î«¦Èÿ_“ú½¤QžRìqvAE8¾écietÔv[âÓÝ< p¡Wñ† ržú¹…ÐEʦGSup‘€" ¦6W¹Y®#Iנ牗5y€–«¹Ë=/KÆL‹X2¬•:Ï÷×l±E.NBŰÑSÿ‰8ÙMðK[3Ã*Éýs*M ïYóeUcEŠwä¹{…#¤ÂõÖz„'+¿" 7Â-uFìBøz¤ÑÏÒò¡]J EFXbVJìÀži·¤Æ%ý¢k»Cht¹÷•³0Mu/—–Ô#B|l7ÏRv‹w~„~ ƒ%¿>öm³.€­mÊɺ(ÄÆTG™(,¼Úž™FÕùUÖ±Ò7  ‡Žˆ©Ùˆ©æX‡ÃwEÕ÷ÀzX’©ág!v+,Ä:»jY û ¥û ‹zÇÜ|—´ò¶Úf4H¸kcB›¨ûVdÂ:MqS0ÕOëìgžDãtÛ9ùûC˜ 3¬„HŠwc/Óµwm¤·Œê¢,òÉ[¹]#¨ŒbÜLÚpò]J féÎ!ê~ Õ„K4¦wŸ !jÖŽÕ\9–ƒ™ jh:Ñæzr¤™Ó~Ú«otV±Ù€Ìt€ÿõ¢Qæÿ‚~Ž*>Â5¶ N<ÓrÇX»Ó©)L˜­eTßíCâ‚gšûç»t˜¥[¡¨XÛGù::à®[}Ÿ?Vö%`£W¥=ÊRJ5as tÒ!asarv/ê7¢>ÝBeóe*ãîí·U½HbmÜæ`žâmhS:cKöŽO.èÅ/þ)M9Øb•óÇoB–^Ž1K0Å(úý=¨1Iò(Rœ1K¸Å#Ò$›…&r*W*6g^%jàÅ*møÅ­´1IeUó8c†»ÙUû˜eXûÙ‚’§:Vk *¶EÝÐl{ :È7NW¼Õ˜‰Ê dRŽŠìhZš(g‹Š”[imèHFk~&ï:Ö‡üÂîi‚ÉT%ÊØÜÎ Î Óª–%ÉRk¯v‰w&*6ß­_ÍjϲñÓ·â5 ¤½ @h]Ž{¤2Hèù« ƒK©²€k„ œ¾s9Gÿ82£ýS×1²D³tm˜fψ×ÎM+žÚg\ˆëçN¤’ã@¥È5°éø\ᯠv;r¡{w1Z Û²Íæ=%(¾oí‡ZÍL†}$B(«F8аAÿÿõš^0"Ag O:á[@ñXÉ,Íâ±™tî·h‚g›}¯ù.ñž ½ŽvõCðíà @ìy8Ê3¼[!Õ"£:ø®&á^òV¼¨¶-YuFÖf—š£&]i±™THg²hkm³´¸‡…kå†0—ëÊZ3lu)#ìt¡]4±WËÓâ:Ÿ/«C W‡Dï_Fnb.—9Í,¸Œ6¸U! €ÃT(YcÕ×âe¥RäF›ªígá'èÛ×3-R£„|©O*‚–oNè?aæ1b­¡È•Ö;åe͸¸©¬h–HòÎ] ¿0·QˆîN³se€U«HíÀÉ@Õ…Ø\À±ù^9îëGEz0‘µt唲I ¨cØÑ{h«F|‘Ôœ‹f‚4æ‹« HiH¤r`«ÿôŒ ñ”Xº¬¥¨ƒ¿¿ÏÖýa_ßÖafaÔe—fYÅÆU%ï½ÌôoÚ½Ÿ^/]Ã{8A¾ÉÌa=Ó„rÕUÞÛ hàņoß\cÙ\SÙÝ­Ý^½D^üSüi—ÅÜÒ[ÛŠÿxA“Y9_Å㸆œý®ëFÑÆ†‡©KBM(¼i:q;Ú›¿•F™7.ÜIS¸¤eÉPåÌÉpÒ]@2L¢,3Ú ¤> pgå…xÀ«±§¹5,ÄTOBú¹˜Ž`#^:ÄL¤KRK*b2é;öÆiêÉ£Ä×  ›PV­~Êfy<žÊWÅ‹ gáEƒ^Óbíî6ô"4¥D†)á`Bž™YØRò™Z‰ÞXÕ›sq)¢d‚›æt‚éo–‡Æ¬Ø4{P—©Rsnk,¦"3J #Û7"1!÷wi¾£i£9Cf-^·RaåIF Á-ÏIðïÉŠd;ªÞÖ-'†T½é3ysL9 { –EÊ„²àØc“!V͈áôG&ŒC‰õ(òo²Yägu…ŠæP·õÌìVRì«ï¾Ë˰NÁ„vÊI¼xÔvacJ¾ Ø–2YPHéó¦_£uézr® ªF(¢öHçá “‹KHξÂpÌÒMͬ°ÄŠÍ‚;Zb&Aɇg*LÂ2‡4—ƒd‡•§ùA£áEÂ<ñ8îâ¨Srè†?ðœ×$öËy™Mâ)W£¸Ì{UÅWÔ͘Ó?8B;‡H>KöÞq4…ÜÐéÊÖ%%l›®?8«XÏ6R]P@¸k‹hŽ"dü-Tc~xœÿøŒHD4)iÒÊ­-*•á;Ò2§¯¡½Fiƒ0-C‰m@JªÔÉ;$¦ÄäQ JHÐaòƒDx+-¶n»Lä5þ„ÅjÏ &P­f§mM Í~ÎȵR7JIËÚbò+›ÿÿÿý>àFã¢&Út„D«Á¨°FT.ÁáÍÞ¥"ÈÞÈúMs_@ýjð#0;$‚mÚíÌ”gáà»ú†|jâJ«…™aræ+,Ö˜\¹éвÑED,±£—ZL• o"ý‹ßˆ=ˈÅ/”‰ø+Äa&ŒaWKïr»¡Ùç6 ”SDÈM^jTœ†,ÚtºÏÒõ–+l€w„îPÿ›@éëÏ 1ˆ9ø¤×#cqMˆÚ‚¬²›Sk‹"c6„šW±žêÂùu‡KYl"¦îFf‚ ©ÌfèÝ ýÄ=ñ*' ÁƒA;ý’IÌœÕVÍUzΨL–PÇÔ­Jž¢ ‚e×c6o:Ô÷§N¹0ªu'Î&%'ØÄÀ´Éu'¤]f$QšåŒMùJÎnÄ]jC ]Út_²]3“Kr]±+Éué´K¦Hµ*O1BDWÓB®úZë·f_%[i&ÂVú‹‹¥è>Ô¨ïK‚ÂÑžIQ®ƒFÊ~TÄÏKH3b¼Ñì”þ,eÒ„ö϶g¨ÿز…-«U¡r9ôjü°E ÛýYù,‚í»cSnVVä†ã`¾5"Æ><ÖÉí¤TC6Æò®<2Ä!WãÛ—±Ä/í3»#ŒÛ(vò«¢›ÏÓ¬HòÒŠgd.¨|Frÿ;ðá,zÚ.Åê ¥}˜s:¾eíà÷—ß ¯&˜8‡k¢€—’…&¬Í*øÄ•œrë2þy¹&j†Z¶çò;=Ìi¦èlú i\Õ¨˜ºBu¬(HC>X!@‚g¦ i·Ù.Iš]ã×ðéXåâ««#òì†ðÕážæé|mI±G¿±o{á.ëyý¶+ÞïE·Dk)±§ ^cµ+ócS¿S!ªup´ˆ§ ¡Oö¤²º7Š«ížŒ'¡––DŸSÖ¬%¿8ÐÒþa9ˆ†Ô[¯ûË#KË„U€T—\}µT`…£.ÄM8ò2Zµ.*…c$êCÄîYð„·¢#fâb3†Šá+¦Ú㥶~‰ÉÂkEhœ”樈Ï£œu‹±ö'%˜‰sÝ»Kµ‘‹É%¢r{ö$&aÌɯ™®»Ñ®ÕJ5±€°è-¥Ì¿WÒ3¥ñõˆµ1²C*DFË=«{Ülšz¬7ËDÓ”‚UOŽM3,d&íž^d’((/‰+æYj Ù-d´O€6dLë†Ç~Ü>Ó PâÅâßî.`f^$áŽ<{×è¡L³«êwï&t zñ|ŸÊ¬®B‹ »ÔGƒÔˆYó>'Ê&ì)Ý_.óýW£‰ï‹±‘µ xXÔXèBÝOuâ44I»¡ÉÄß CD‚Ï44V»Yk@Clh0Œ<)cÔ?¾˜Ÿ=NKöO¼)<ÀŽ[0Ñ_jZ£1†Œ_¢X@©F°N•[Çœrð##èë]|äeIl‰ÎtxÒ|}¥Îq¤Ô7Ál­ÈˆhøYz¦õ5ö,GÙG0ʼƒTí™) ޹;®N듺ù°ƒ£&äF}â[2†ŒÖìwf™{Â~œþ 8¸ €®Ef·ÙxgÙ|‘‘‡ÆRjk-?˜Ä’*³ŽÏã…Ä„éŠb—®)zbw®){b˜Ž1d㌺b.Ûq`@_mશ¬?’+͇`ÓLë¸@¾Ö!)“’Õ'&”\ðÏ[KÌãK4›FË=iÈ3 ‰ãT6ÔR”ÎÂ,¤î$FוjÝîK‡lÍ¢˜Ñ‘èHuè³ü¢Lì÷ÌTÉç¢=ä©§ JF- É.²µr®Ï‰k‹*"Pʦg/Òñäcs I¹ò 9¨CÎÐÙ‚ÖÈãGhîo`‘Dެ™%)S ÂhRÌ›0¯Æ0Z¥›óHlPod(¡(ó®aˆ›‘»Dù³ªˆ–´f S›)W·$I—9«1ÂV1c®Ë…üuÈ•4°+*ÃŽ›StY†tâzëüŽÄ=0¯˜ÊF²‚Í\ÀFâ$„o#ÄR¤¨w¨æî6d‘¢1â#Ëžˆ[ÁIØ¥¹”§‡;Qp…¹8…ºO4l%sŠíc¥€§™üÓ-(¢ù-CÁce€D—c†Žqà  Èäjv1TÙ ˆjJÔ ŒD½…2+ˆï.©í_7lzü–@ðÁ.Õvq`‹3”h¦ŽÛ#vT²¦§£É£Ú7TõOfë= 48  xÕ%»\sºks¤öäì":`¤8¾5!ƒ}#6¥Ñs‘»ãºySç:s•;#tMB?30J@5ñÙrrÛu7œLŠYÌ$§£Uºu˘YF…I‚|‹uT†£®ÿ Q/!EŠ$x>·@¡K)Âí§"8ˆ]"ˆ€ªºm[“ÒO1!–©½H”¤øÃ”b¿_Kˆ¶Z4jÔ¹‡D ¼š L¾&ˆ÷AÃQ€hý¾ È3š6ñˬ¡E·Ã êüaž8â—(­ïø”ùàÙ< “¦&`!ÝRwijöÄo’’7'Ù6iÐuÄÒ<ÚõL›Õ`Œ›® È̇@Ž»e³•úr Øc#þ:—‚Y‰Û‰ÜiÆïˆÈ~ñL*1ä¶Ö{ù÷aÒî ;q— BÙž7ß¹ÍÔ‰6$Ðaý‚Ë@Ãr~¾Æzƒ‡²ñ=šÞ \¥ë*‡Í‹sžP Ö”Oíêµ"ªÚÀQ…8îqŒc­Z:8E’×é$]É¿yoÛOJ<ׯT#NOa@1Ò‹¯!Xÿÿÿô<Ô> Ô½GJiTVÎÒG‹Å´òÞ© _禧L@‚fßi·Æ1w…m—·p§Vz§³_– žþñw;¹0Ìw «Àù[‚­Y“­­‘ð®•Gä½=»—O¤Ƙ-Ö1×VרyDçM$_@œU ôåZªÀãM–¯ƒûm Pš ^_¶.å½]…æ6S«5Z%š–fU f1²ãÜ$VZL"#âŠC2F…7lbŒ„rÓ»7J45¼¶näDã(Æ\¢)ñ€ŽÅ:„‰“Kp`r&n#ˆÒ‚À· R®åÛ0d†`çmHWExénÀG¤ñaºˆ_"šà#ÄÀ # ð]íd‰qܨu\[¡piå£@‘3ÛØ£\¯Û„Ò‘V³3(#b¹=-ë‹ G‘_‘ȉ–¡¯ßä÷¹ú¨xæ:Ì`©=ÿÿ@yè;弿–õŒ0`Á‚³§Í ŠÇrÓRæ¼Z5ó&x´Vj'vê‰ß',­pÏÑ‚ïžûç¾ÿÒfcY(º–;¿ºÏAÔCƒ(ÀŒÍ@¾Ûe3Rym>ªµ°•1S» XA0[:Ë/R¥ì2#õ:|Í p¥´qoQ9ûñ1aTSÑP²Û tüÇʆw«?\çRùæÂ•Å"{G%X›ƒR‘‡HÜXá he"e¢¨Õ“èûx ,ÂÄHUÄ©ýu©ýKŸ*’’šþ¥ø²™šþÂíC´fŒ“žéYÓ®c}_þÒ ;ÙÊg0¯@Õ©B¢¦ù)4 A…H•_7Ê:ÐŽd®N—a1ÙÑt“¡ÅýÕkòwYC[L$¤þbðA{ÒÒF€Â–D£†ÒŠ{†0±A}k毩 ‚¶Ñ\4Ø´QøŽéʈÚÎJBOCv•GNʧêØÂð¢j ÆRåd¯Nj€…d^¦úW¢OöcJqír]%ôôùO-ˆðLÛÉ8˜’¤"ô®qª:ü0HHbÙ}£umK§Ï5 4ñ¹2aònÈò.ñi[l£‡çXʹ![Âuuñ[Î<=*›ùèµ&|N[J£¨Q½ ¬RZ ‚ Âêâ­m«•G2*éHý•des"2Õ}ê±ôAœ2J‚g¾ûïÆ {ÒóYš[y¹ŠWñóâjœíaÖÍ¡ö1æ©'™"YS©‡œ9ù\ÛJÊhëºÑÁ¸ÇÈ&WÑçŒi©£˜™¨š™­˜™þ–ÝÔy†Ú Ì 7íÉÈFœÍVv.jnÀ¬ù@‰‘J8p†®f’zð¡ΑWLÖŸ4ô· o ‘_d‚UDÊ44ºŒ¥ÅöSÑ›8IéM‡ÕS × ‡~çÂÍn‰ ùT x_k²WI±#ÈLÊ/[¥ÓÏÍ»gNÌaDDF¤ÃNC j£´h’BÌúø&tzeM͚Ŷ¤Ýø¶¹,…HîGP>Zˆu>ànòʳ1èÛQiŸ¹&GÇÅé§XŠCÖw±“ZàJH¢g4=g²²ÐäF( ˜I¤£Z£®4 v›Í³í;¡¸›š“ÈÖÊœ?ÖÈòÓ_ë ñüb"2é\ö(ڬԙƓ§V¦F⪭éÌ”L1`£+)%2C5³ŠÅÈØ ¢ÙƒôcQrd.ƒÃÑ=Ó”~ÐÐÁ=vÁ c†¸(iÉ45b‚i*FiªšáòÞX¿2½*ðÿU'šv絊¶¨æ˜G9áÊ5áÉÂ!¾°Ý²Z¬M†TÑg.Œ#Â}ª¶ ¯¿ûÞÓ ëSÌÄ{þ»r–N¬Õ&“G=ún…(È•k¸ÎÀÿÿÿÿC§€‡Š1CnQ'L‘+žØ¯2jÆÂÜ2T1m–(øÜ*S¦UØßç‚f¿{®1 „Ðþ[Üö /™Ñ ‘`±{N¶rŽ9`r'(CådÝazfcÌÙ¬U%؉yâÎéPFM‘4bì˧¢B¶¿e ø'EÄœ«ÊsU92œG9šHá›hî›v•Ã%g†ÕEnÆü;áÆFMõýJÍÈv›Š6FÅdÞBŒ4VyFÌW0¶Ex¤ÎPßkDéXNOGþÝýëe•tëäË{èÿA¼ôÅYÂAN4õ³Á€Ób+ö“¹¾ö™…’%v˜4^„,®+Ã"Ž¡ˆßúÜÓ¸žI(¾–G)É~nÙu&\‘Š2œejQò©gÍT»F´ã’5é®Ô›&¦22¤h>³,@,BT&w/îD^[BHϘ“Ήá{Xž[–B]÷`ž˜J)wÝLoˆnaãä­fÝ8¸Ñì¡P˜ð3rűHºòŽ>qù¥= y¢ðýä¶áf€§ÇTF>¢mˆj×Ùl‰›dèw§{:ж¦ !Ä|ö0õ"“³V4w}a'΃–Ä\E$HÐkÂÁÒ}åa”èñ«!³Y0„7á×ìgÑm¯ªžø¬ÂO0™Ì,‹ž±–Ç…Ò¨´a ˆåbF@‰¯¤ï ÙÚdgárª‘‹?$ƒ7IkД~ˆ°¢çɰÙ4×낆ÀÕ‡m” SQ XúZÍ‘›ÞÉ^ñÜ|ÞCS„2ãvLã ba*ŒpÜÓÿÿÿýGB(íÉá“$‰²ô¶óÐÆ<êÕEédNáõ? ÿ@ÉðC]Æ€‚e›éRjgü@a®¿Dfq‡mãIª •C/+ÒU’&¯JT•dÃt•d‡@K»r‘Ý-Dv¿X?Þ^6Ð…5»âN\ÑQåîÓñ1œ…äŒe˜Ê6¡ˆ¢µ®F|šÑšpˆ˜yQæ-©ûÇïÅ,IK¥ÐÅ~.IvÛ^a助ñf`Ú×Ϊzÿj±ü|øû(i*‡±+P‚øPÿåoÀšRWˆ(8«-íAXÖ‡I“»1„d€ÙG'`ÝÖ@€1Œc¡qjˆ ¼”ºkd[“&c H“¹í+ ˜Å"I˜÷MJ¹2ñÁ³é.O›Yùäù¶Û“×jƒ½¾ïï®–!D2v,3§¤Þï­i`½5Ÿ!÷á[“r3UXV’¬‘ðnÔ,‘Oföæê'ng9Æ?{Ú’¬‘9p â"‹¶œ€¾'hŠÀÎ q00&™Nh‰à€–þù;ý`ìœø¶ªÂ L!HøæI¡–vÞÂìËëµ3ãQd3"Q—d5£õòzʸ}Áe\>ýpûHD¸}Pè}¼5ÀE ã]Tíb7Äía,Î5Êå%s#²b1cƒ!/†e‰$zëK·fÿ@áDßèêÁq/-8ü$Z»ö¡ Éí® ‚uàØN–n¤>° B¨‰Á ï­ÈšŽ¸ß«z4i%þÐaá ˆ¿à0?`Ž¿€mÖÚŽ•¤ïj¬±úqO÷øsi¶Äı —£"¦º±"3J–Û¡$FiSÐÿÓû1¬dæÄAN¥™ƒÍ¾þýaTýµÏÏ(ÀmãIW”ñƒ¡ Êô°.IEäì$w3ˆÛvu6ÖR"B¿¦Lì+.tEY+kDU’»IVJðŸd¦ì‚Øii™›­—ìÜdS(âzUdc‚’ ¦Ã‘'!,”“ŸØF+wA¨”§ÔL]P£2TÆè4÷@†ÇŠÊŒ°ÞŒfÑÙ©C ™PŽ¨Ù¦ƒ­/Á>å#·P8PÉj]«md¯Ä¬D`8âˆÂž¨çn”q›ÄO6–±€Ï†T»HZ3æ,Q­*šˆîЯq¶³)ä†ÜU˜\F­­K ‚mÖã%¢ëy†Ž»á•Ñ Ó4ŒÂ}™E´;B¥%åYsÿA ' ŒRB팈ëER7J†ˆañJÊg£hŠûOiúY°é[BQÍÎ""\óÍ4@Ü4pbpEpcpW0¡„a†aTšimyÄ$LµÎ5!®‚EσgpöЖ¨É™™Í RHéV}-šg„dhM)=ß+½‹Êsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/JavaHelpSearch/DOCS.TAB0100644 0000000 0000000 00000005107 12114157751 024300 0ustar000000000 0000000 eÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý—ÿÿö_ÿÿÿÿÿÙÿÿÿÿÿÿÿÿÿÿÿÿý×ÿÿÿÿÿÿÿÿÿö_ÿÿÿÿÿÿÿÙÿÿÿÿÿÿÿÿÿý×ÿÿÿÝÿÿÿÿÿÿÿÿÿÿý×ÿÿÿÿÿÿuÿÿÝÿeÿÿÿÿÿý•—ÿÿÿÿÿÿÿö_ÿÿÿÿÿý—ÿý—ÿÿeý×ÿÿÝÝuÿuÿÙeÿÿeÿeÿÿÿÿÿö_ÿÿÙÙÿÿÿö_ÿÿÿÿÿÿÿý—ÿÿÿÿÿý—ÿÿÿÙÿÿÿÿeÿÿÿý—ÿÿÿö_ÿý—ÿÙÿeÿÿÿÿÿÿÿÿÿý—ÿÿÿÿeÿý—eÿÿý—ÿÿÿÿÿÿÿÿÿÿÿÿö_ÿÿÿÿÿÿý—ÿÿÙÿÿÿmÿÿÿÿÿeÿÿÿÿÿÿÿeÿÿý—ÿÿÿÿÿÿÿý—ÿeÿÿÿÿý—ÿÿý—ÿÿý×Ýÿÿÿý×ÿÿÿuÿÿÿÿÿÿÝÿÿÿuÿÿÿÝÝ÷_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿuÿÿÿÿÿÿý—ÿÿÿÿÿý—ÿÿÿeÿÿÿÿö_eÿÿÿÿÿÙÿÿý—ÿý—ÿö_ÿÿeÿÿÙYÿÿÿÿÿÿeÿÿý•—ö_ÿÿÿö_ÿÿÿÙÿÿÿeö_ÿÿý—ÿÿÿÿeÿÿÿÿö_ÿö_ÿÿeý—ÿÿÿý—ÿÿÿÿÿÿö_ÿý—ÿÿÿÿö_ÿÿý—ý—ÿý—eÿeÿÙÿÙÿÿeÿý—Ùÿÿÿý—ÿÿÿÿeÿÿÿeÿÿÿÿÿÿÚ_ÿÿÿÿÿÿÙÙÿÿÿö_ÿý—ÿÿÙÿÙÿÿÿÿÙmÿÿÿý—ÿÿÙÿÿÿÿÿÿÿÿeÿeÿÿÿÿÿeÿÙÿÙeÿÿÿÿý—ö_ÿÿÿÿÿÙÿÙÿö_Ùÿ@¡œDÌÓI¯ÚMH®Ÿ\ô¢R8&Ê­£å«k*±•q•k I*«*«ô«hجȡLªxÔŠ«+3¤»[HÄŠ«*˜R¹å+j«ëtȉ”«[\À›˜äªK"¯ÊªíÎêÃÒ-3$Ê®ª§ìʪa_¿ú¬4$î2$ª¬Þ¯¬ªm,mF©-Êum2"¦Tijª­æRjR1"ªqcBs2.ª>$e*ª®ºªÊ£„˜2)N¨À‹1•4ÀŠ›J©”«êÙIõ{~³q”ŒÈ›J¶ª«kr²ªª©…ª¾ªªªª©Ô«jŸJª«rª«*ªö´À¥*­ºª®c’ʪ},Íi'”qj¦qJ­¼Ê¬ª­¬®¿®ÊªªªªÓ*ªm*ªª0"÷ J’ª¬ªÚ§ÖeJª¦R3Ò&«q*ªÜʪ}N¬0"§ªªÇ¦’ª§ªªª¬ºÌ1&Ó²#³7,ð›2ª«72ªªª˜Kê«û¹£"l«ö×m#,iVªwUÓ‚•MI¿1Å4Ж©…79눅ó1•)Ū¶²éÅZÃr¥«1•;¿3÷;3©´›]ÛèK]ƒRÕ ‹u,~Ò0"ª0#q6>oÎ –çMɹ¥ IY…ZšW1õÛQ¥ZS÷³^J½È«7*5)O)´½¥I¤Œ‰ÿ=¨K5ŠO©Õ??{+ZQ¤©¥›…+[I¥~žK™VÙSS+3·??©õ\ÈÕ ì5*PĹJ}oü»½ÖÒÆRÏf­Ç»¼Ö”ËǶ"aVâ¶Žóï;.íÅòõ¤þû][s;[¶ÝOó.ï-œM™Zÿ.îñ„öîó7.ÚMÅï.ó>îó3òï.íÔþï¤í…ZOS2í„ï2ï.îï®îóvó3ïRó™O/®ñå-„ïNþîÿ/:îï²íÕ:îï2îîöñ›Vó/o.îî÷/ý•{o.ïrîóRîòîó.ï;ó.ÿ;.îØSÚ¤öïrîïúïwNòîï2îÚVÛWîÙ[W.ïnòî÷3^O2îØNòïrï3³2û//.ó.ñ„î÷2ï¾òîﯮõ¥/®ó/þîòóS¯orîîû{òþû2ó.òï.ï32ï.ØS.÷3ï=‰O1¥7.ïnöîïÿ­¥.ï/®ó²îï3vóû/.îîîîîî÷woØVîïw.ï3ss;/o6îîîîó™S2úï2òï.ïnòó®îîîîòîí„ï2òó/2îïnîïÚ¤ïróRîØNó®îöù„ó/:ïnîï7s:òúòîöï.û3.îîîó6îîï.îîîþîîîîîû3ï²îþöîïnîîîîù„ï.îîï÷.ï:þí”÷··».ï7nï-¥/{:îîîòîï.îï®ÚOo.þï®í´í…òþúîòóo{>þÚS:ï»m„îîîîïo73/o>þï.ÙNï/³îÿ/súñµ.öïó.þþöïs²îöîîó.òîîòîósnós/.îîîïo.îöîòîîÿ6ó2òòï¯.ïnïn÷ÿ7zïorï²ï.îï33/o6ÿ.ï.îï.ï®òîîîîîîòîîîïozîîòó.òîîîîîþîïvîï.ïvîîîîîîîîòîòïröîï.ïo.ï;rîîîîîîîîòîîîîîòîîîó.ïró//2òîîï.îîîîï3nï.îîîîòúîîîóròîï///®ï2îòîï.îï.îîîîîï®îîîîîîîîîîï/nîîîîîó.îï2ïnîöï.îîó®îîîîïvîîîï.îîï3.ï.öîîï.îîîîîîîîîîîîöîîîîîórîîîîó3.îîîîïo.îîó/.îîîîîï2òîï.îîï.îîîîîï.îîîîîòîîîîï.îîîîîîîîîï.îîîîîîîîîîîîîòîòó3.îîîîîîîîîîîísimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/JavaHelpSearch/DOCS0100644 0000000 0000000 00000037344 12114157751 023703 0ustar000000000 0000000 ¶_ÝÝXÂYÿÿÙ}¹v_Ý]ÿö_eeÝÿÚ]—Ù}åÿÿeÿ@€c€cœ J€ã©@Š}f’Öe¹9§™š@•ƒ‚{íîÖûs˜`‘Â,™wo«UëX äVK™Š’ÐäLLasvP½Õ—ÙöS ¥ý©ÿeö—ÿÿeÙ}¥Ùÿÿÿeý—ÙYÙÿee@š €Ì@KWX»O·Û­¦Ûµ£1 öݯìWµé·Ü€qJfœm'¹VTé½™@€â@†ši#’<šªË¦”Ïýe&@†çÊ€„³kC%°u@™±&œIŠÖÝëíõÑ6°áp©´™½7nxl'Z„cÈ› O°¢$–²'Wš%{é¢,‰¨‘6Ûm¨…c‘"ñ@šz†™aš2%­ªÙ™u„B»@€À@Áã.†žº¹6'ΣRÃBa€Î"ÊÔŸÖ…dÝ$§‰z1§cÙÇLRFÈ]ÖîÕ5Zû½ìNÛ±mÕ;5O]0J¹H™É·[kÄßujm¶ÛíÖÝn¸Ñõ»[k2»½×mP„§*úRôݧ“ELR ŠhÄŠje¸Iô^Sëe•&g-Šîçi9–™¹›1!Ü–Sšþ¤†ÒëùÉ½šš¹šªÖv–­©«ËÔ„hp/E›#ŒPб¨FúãB>£B$–Ýk˜—*Û»ÄÝÿÕ‰bw¶ën¶Û}¶Ûuúí{Ó®¿Ô€ÛŽ@€Å´¿4¥ÿ3,ÒÆD—uÎBù}—ÿýŒ'cá±CŒá ޼™£b'èÁ†Óó’.š®×!Y”Š–X€B™Ü©½—Ñj®aT‰ùÄ3$ãŠÉjøì¤·ÏÈre•Š4YV¹/‰¿]õ¹š˜)ôÜÔëë•‚(÷«ù¹g`„JÚþ¼s6jZ£Œ¤€€Ü•@ƒ9cj1…Û¡”e‹R-SÕ‚|G€Å@„gÁ/Ðñ Œ 4„Ä= †ªÓ²™–’‘Ž@€Äµ@€Ä@ƒÐ…—üÂ}×Ýý—ö×ÿÿ÷]•×ÿi}—ö—ÿeÿýƒÒI©@Š5¢*@Ž–’£2™šaa7ÆPŽÞ¹£šùeaÌA©@„š­–ó4„AÎiK`gf«j†+„A›íÜzÎH™FP™ªbm¶ÛußÛL:b­¹‡¹7w&êÖнÃ")¶qN3XŽ@N®U·ÓD©‚_VÅg†€ƒí¹†PŠ?%É6"ºjÒºu:e€‚¾btE@¹5!¶’ªe*6"ù¾¹¬™¼q€€Ä@Ž˜Œ@™Š}j>$–“Ä+©Ú@ƒ"±HëX9:JÛö@ƒ"³Vn‘Ö°ñ+k©¢É+oÙ™6t~`‚&½ãöX¢Ž;-FjróùÃ"ë–U´…¸7Ä …¸7Ä Š4!3!éɺ»5%æ-N©´„¹C!âDµæ€Ï:H¡jù2%û@Š‘¼ÄC@„¿nVÝ•å¹r4¥©ëÆ(¾B‚_s§(õšd‚=cNÆ¢Ê4€åŒ@‰™£ÂΚ8-Q”XNpĆП€H‰æ ò$‚ã‰}•€³¢@™€ã@€Û¬@ÐGeÖòŠêóÒ•3dÈdØ•¤je€Ò@„†“K™™„¸õU†Ó0Ž¢´ŠÁŒM´¦©¤rYÕ»}ƒÉ««S¯0ŠÊ=)9)K>«¿^`„¥qž!£ëzH„$ç)åi[üP‚W)PõµD7”eœ©»üži*¬©™™@¼à@„²Ñ˜"´©=G´¥=Ë@™‘မ–1!›ùœúјBª¦­™@ÿÀà˜ºŽDwa€™néÇ EVëÐaЙDw<ÒƒŠ#ÐÐÐã˜@·ê4Â!‰±¾·Áš¬MìØÈ bÈÈ Ê`“è!±„™ª ‹£¥€ aPŠWÔ\çÊ*P¡ôÈÖ†¤®8"ŸÌ«y*©q2(O½‹ëÈÈÈ Ä@ÈÈȡͫ=!«"gF¹ÁÈ…¡ºÀ à@È£F×ϩӢwV@…ö7©ˆ«˜`¬Ì˾ÆTšªœ¦k̈絛PÈÈ­6$Ñ,@ Ã@‘°°Ÿ˜h†@éWêÈÈÈÈ‚Þ@« ð’@¡ W"Ê‘ÈЉQäbGê$–0gtÈ«¦Ì‰¶²]¸V©µ6k·eÈÈÈ£#’+™‘ÌR@® ›`£òQúŸžŸÊ™™Êšš¹œ­ù@ŠþmdX@Ì×TæE_wÆ &™®&Ñ€†ÖVÈ â 8@ÈÈ݉¾æ-™F ]}¶ÜÄÝ}ÌËjn¶íX½mb~ÖÛ¡±€‰¡ :ò©€ÈÈÈÈÈ…öx(£dÈÈÈ ä@ÈÈ®¦x䇹•ægfìÀ†™`‘Œcˆ,ÑÈ«>!jD4¬|Ä’ö˜GæzY”̈ë'eÊÔzŠR꧘ÈÈ©>.^æ³ÛV«çfsn¥†È Õ@ ß`‹+Ú}¯§ Ù`—#WL“Äqf†1•79ãq`’X@ÈÈ•[¡Ÿ%‡æ"ÈÈÈ–Od*YÉÈÈ”Mq)/úÈ£–—"î©äÁoÈ•·.O˶­e{Ë,Ø‚Ä@‹–´jP‹M¦ÒÍ¿0@È `‰åô Ã@‰¹*¼â®.ÈÃñ ¢,–H«ÜŸF"n6Kހȗ älÿ?œØ!’ÈÈ‚Ø@ÈÈÈȬETÈ…¢À¡±€È â@ÈÈÈÈÈÈ„ÂKe……’w{hÈÈ—Æ(˜SdÈÈÈÈÈÈÈÈÈÈ–76 ó½JÉtæ€ÈÈ…ÉšÀÈȧ `‡/;ë«)¨Y•˜`ÈÈÈÈÈ‚õ†@ Ê`ÈÈÈÈ aPÈÈȬ@‚ô‹@Ȃٓ@•½o]‚RôÌëNGVŒPŠ&5¨çâPÈÈÈ‚÷ˆ@ÝÈÈÈÈ…Ú„ˆÇ hâ9¯–U鈴ÔA¬Ö´È î`ÈÈÈ– Yj“¼íLÆŒÑáÈ Ö@¡Ñ–OE¦”ùFɽ9DöÀÈ•1]ÊX¤eZÚøt²H¡õ€“…ñõ/jäÐB¬P®D:@ÈÈ•4ïÔLù Ã@¦Gqµô¯fom…šÈȋӬ@ ô@ Ä@ÈÈ¡‘ÈÈÈ—.f¢È‰»VÈ„ÂYM@£¶‰ÎÄ}f@È‹V™¡ÁÈÈÈÈÈÈÈÈÈ‹#­€£2Ø[hСY³L¿›Ú@ÈÈÈÈ‘´”Ác˜³O$ÈÈÈÈÈÈ”ÿ¸Îö¶¤ýwIbQÔPÈ Äiœ”¡ÀNVzg&ÍH«ÆKcN"l¥“XŸ»Rd¶VÈ–3&i¨ÈøE@ÈÈÈÈ¡ÕÈȉ£)ØÈÈ“eXf’r¬§ØÇQŽ,€‹è¤8X‘œƒÏÄ`ˆ$ Ö@ÈÈÈÈ‹6¯iÙȧ ˆm¸S+ ˆgjjÚG&å‰`®™Ï¶›eLœª©©e\›ÉšÏi€È‰‘r|ª``ÈȦX”¢õ=›aG¥œEÈÈÈȂѕ@ å@¡ÕÈÈÈ®eJš0²›g%e@ ß` Â€@È À@­R©¹ÊÌé4%Ú¤ÈÈŠühŠ™tŠ}bÍ‹ÆÈÈÈÈ Ã@”Låd8É€¢†t@‚Ǫ@È®ã"V­«PÈÈÈÈ“ä?¿éyR-Pƈ ÿ`ÈÈÈÈÈÈȮУÆÈ—"–F˽™ßÖWÇ“›amÙÙ1,E‡®&½Ó€ú±:ß^šh“Sµ­S*ž›µ¶Ûn×fj›M`è³µq…iÃb<ùž¬q€úëššÅZpû³ªíFbL›½¶í®­_cè°å@°å@ƒÁ¤@°å@õb¾×ìd•ù‰ºÝ﵋ï_jmÚÛ­¬VìU·íê§ääþÀÀbÀbÄÄÄÄÁ倄â¦@ÀbÀÀ@ÄÄÄÄÄÄÄ¡À`»Yĉ¤ŽÄ€ÄÄĬxÑA퀗Èûk`—ÚUÙ¤U@ѼAé<%©a€ÙÓ²'Ö3!É™ŸqÛ@Ù P›`Ñý€ÔÙ@Ù…ì@«ô±œdr#7I„—Ý!|F@¯ØACär–"Í*ÔÙ¦’™œmmÄîúÛe€ÔÐ`PÑÔR!Ùº¦LP•«Tô—wY1ª”ÔÔÔÓA€E©©ÙÆMीÙD&œ›©>/ˆ–…Δ@‹¡´Àji²éÚëë}»V+2HºÙåNj}b¯k—¶D,o¶k‹ñŒÀjN¤“:nÖç%§î¶ßÚª€¨Öü@”À#±·”Ðà@Ðà@¯ó,gñ‹äOí‹Úº…Ø…@Ún›ÇÑ“"©™ÃR,Ùš©@—Ù ÔÔ¬L ȶÍPÐÒ@•î”ß 4—Ó8—@®Ü¬j› “KŒe8 ¬[*ö-'ñI] •ÊÄ¡Á©ð†Îª9t­³4G› ѾDZü@Ðß`Ðß`àí`ê–™4* áý€ä±œ®†ÆB,å.ɳ¨$öPää†î’@äá¡€ãæäµ/ÚKQ›%}¼ð€†É™@·hÔÆIŒùî ¶ç1Cæ5Ù0r³—¨ÈFÅ]mûÖQ ñDÛî¶Û­¨´„*Êh¹±ÓD. äᡀᡀìesB©ràë`àãäàãä’æxÓáý€á…†ö PÐã!@áဌÆSÉ@íXáù€™—”jä¶.ÕUó7KX!@ä™z!–£µ<5ÿf»d“ÛdÛÜdÅE$€»ôÚb½ªIZ9¢þ¦Ò«“ %š™™6)¦»4ZÁ¨‡ôŸxMD¼Ú!b§ñpª’•º€žMcˆä PöS™›¼yq+›©ìªë–ºÙ·ó’F’¦VÃ2¹›@ñÉùaœÊݪ¬›Úª©8L’§æfª¥ü3"™£2»O1‘…|U¦=ÝhÀ^ÀôŸiü£r£Õ²o®˜ˆVq¾»IRWß‘Ó6ôz­ÖÛm¶Û†ÂSƒ˜N`ôôôÿ™ºšÓ*ͬ6"É™1@ó¶û4$Ñ”A™™›Øóæ÷=>ê1ÇØù®É–Ñvž„]œÌ¤zØ›u·Px óA¤BéÙ™—m€ûìš1'«»«±ˆR)I0U:±;gJ¬ÃSi‚©Í‹öÖË"¶Ýbɪ À‰KP¢öТöÐÄc9I*ζƛ&Ò0Gk\–(é­ÌM·{¨ÄDÕpÀ¾@ÄbÕ^ÐĸÊì“ÍÁÕ€`bfXG2jî˜['XWªzffæPÁá€ÃÔ9p†&.#”džÀ…²‘Š *|$ ÁÝ€a!˜R-F«ff­•fÐÁ¨—; Âű/¶1´á"Æ\Ù€Áõ€ÁÕ€ÃD‘«Tó-;çÆ"€ÑRC†€ÅÝ¥–kÈRš‘â¢k¢¯âÆÈIJñ€Äg©HÆA”b€‘¾Za.ººÐˆÁ›@£Ö TPÀÙ`ŰŀÄe©³S+î2ŽíFIÂÂÄœm9å€ÄbÒ)·wˆå¼MÅŽdlGqºa'Í‹æ:uk¤Ä™¤ŒdÔþDŽ2KzÙ’ÌŸ˜¯uôáX–E[³Œ”éÝÌrÁf%“ée©ª¬fÑ©»–S™}úq@ÌgØÈI9°gØÌp€ÊeyñœB\ù¤ÊÌi:7B€Ìs)·O ÏMèÉ„‹ªÌÒG@ÊÌlýxÆ wÔÊ4B"®¶ÿ·hØCFB>ÄÝòªfdg@jNÈfiÖÑ•ÕóéFMØÕ+'(g“™%Ö]¡‹]ý2ÒÖNLÖæÝ€Õ#q¶#ò€ÖXªŒ£öŒ˜jX•ÞTnÒÕ$ý]L„höQ¬£Ñ‘ªXz·IÕ"Ó…˜Ð`ŠÒŠ@«sh@Ñõ€Ò•µšÀÓ•Ìkxͼ©—6Ó¥g-jFhÒÒÕŸ£Ü¢!QÑ —( Ôkw5Š{ë1ŽÖ^«`“@Õ‡«ÒÖÞ¢@©•Lè²z‰ÒÔäûVRÍ´€Ñô¹¦ÕŒÄImÒÔu)EªhÆz½:Ý6ÒÐìó@Ñ”‡»ÌüA@•é‚ÀÛ–ÞL$|DðAºØß`Ùé€Ûû!Õ1oÚ\¢ØÀ@ØÀ@ÚØÙ`Øú`­H—åˆÀ—æB¯û%rJ@Øä@ÝNš#ÅÝGŽ@Úm.º6!™–R«vQüi€Ü¢`ÊܱÜV2•ó*b­Øܳw‰¥ýZ9Õê€ÝÍÄko½@Ü£¦È·~ÏŒX¦šØÍ@m3²©­žÉcá|ë¼êÄÞ$u*/÷PŒÅ¶@²ždÉ™úÂpØŠöjª§+&²žSúfª®rviãU¹µãA&ÈæZqM4!ž›œ›«xìŸ%”upÀ¶evæqy©ûº”™¦ªâq/»5'Æñªœ©îu€·z[*`u)Þ-ìŽä=›é…ììììbq`ìºi@y;$7!©ÉÉ@òñÝ€õe3q’nÀòðï@ñ€ƒW`õÜ¡ em0xxþ´ÐŽòÐó„oQJQ¬fþLóf¢*óDÜf‹÷лÊÐñÝõÐñµ¹·DPñµñµõeÍZlæžv£Hôc7Ñ90ðè@¸Ä!%ÂH¸Ó$µûºÙZú¬8d@Âc°†@0!«6,\÷‰&§Â=Û“jÂHPÂ=¦i $ï„@u·PÚ2@¼7\€ÁCA[m@´ŽD€`ŠTÄLŽeiÕÂ?ì1Àò`Â0!ì¦Sša¶Ùa\é0-NéÀï`0(L`®g¾®éÚµrÐÀø`Àñ`Â6£ÁÁ߆¡½€@`wÁ3’ï„@aÖC! [ªÀÌ%î‘¡–<(¡ž&SÃüØT$@Å£ÒŠc¦Åc–ÇxÐÅÍ€ÇxÐű:?d…c¦Ɔ,Óª@1"2%œ¬ž­£rY¹fQÝôÆI0…ŠÄc–Äæ`b¿bŽÔœÞ#Î,€c›ÅÅÄø`ÆŒl‘õ Ǹë›v‰cœ0cãPcÔ,ç3‰Œ…( ÅÄÐ@2(1¢s’Y™Ë»û©«@2(L“òy™¬@2‚O¬Ê}"*¶aI™q€ÉÊ÷$Éɉ€ ÌDª†1ìdJØÈß@Ê4Îö$Ðfj/Mgl θ¾h`ÍÎd3d…ÔŒ¦]^»s*oPÍͧÎfdÌÄ@ÍÑ£„$ÒêêHb’bJÐÓ©¡Ä!Òz±Š*ü``уvkø°•Í™@ÔÞ@«¦Z5/ĶeˆZi”­¥'+}5"·Ñ“B*5#ÙöUž™™™š@Ö$Õ‰.4«Š"ÕÕÕÙ€Ú&SÙÚ|áÒíXm–ÙÍB?PÝÝÝÝÝáá¥äâa®i¤gE½â˜n͵€᥀ár–ŒÊ¼´c-7Mdåå®ôdäÝ@™à„@äÝ@äÚ@:T’æë$roffktb"GÍA€ê,ÌôšÜ@ë=§é€v„ÔFc=·`ïL/a`íííÝ€ííá€íííñy4Üœu2E¸ òéM$œ×‚@ññˆõ¨z¢ÒÅñ)k7½¥ŠÆ“@öHp‘/#@öö’$@ˆ}4Q,]¬†ˆÛnØá)Ù6ÛîÈ{5gmš öÛØˆvíMöÝuµ‰ÕŠºÝv¶Ûv×]³km·mm¨öXÐöØþš)d³™öTöTöTöt=$–©F¦š™uYÆQ™Ú™@ötõõõötö?õödúO䥕%}†‚4g˜Éº$ùúF¥«Ð|6Ý'¹F1¥3}Ê4Šøæ`ù¡€|ŽfÂ%3¹4`>"›ÙœŸ¶“κª¹™–”Ê®ª´ù}1Õ_¢X+@ú)Pù?'a¾¬»³J"ªÉ©Ôýý¡&hfÉý‘€~—Ó¹•yoýÁ*7;ˆÁn2øŠN~¦NI7ÐÀ€À€À€À€`倠À@`J–†t-c©ÀÁ`ÁÀ€ÁQ?››?¢:š¦˜Á À€`[’-æd``…ha™0i¼ÇF‚Ã{’f à ”ÀaA—<¢ŠæÆD0‘7þ~üŠÃS Ã|m¨€0’tÁ/9Wè !š"Õ=5ÊÖë^»ûÓnÛk«­½€aõ€0л9@€Ã":ÃklˆÈÃSš€€€0uسEW¶£Ã*T^Óà "€Ã€ÀaA¥‘rIa‘€Ã>4\P€Ã–ƒ@ÃVôD>¿®6%Ï2!2@ÃSQ€0ô…$ÃâÅÅ ˆ‚@Åab]¿MæÅ6yHÅvÌŒHb¤$Ä€°AªùΩ–¤Ä‰Ä€Å# Ý0ÐÅeŦêĀĀĀÄÎ`Ä€15&by¥â€1Ù(¿gKâ6ŸÄ€bU¯n䊈1L¸ŠÑ¦í¤Å TMÈÄ€Å*,ŸÐPc倯€Æ€Æ€Æ€ÇQßÉ7'PÆ€cT£”ƀƀƀÆÁ`ÆÁ`c¡fDÆÁ`ȀȀ2;Œ~yµ34È€2Ý¢œbÍ- dT‡hÚÈ€É È€È€ÈÏ`ÈÏ`Éwl˜0PȀȀȀÉKRÉ+šÉ)dJ‹_™ØÉ)ʀʀËaʀʀʀ2’°Õ&)YO¤cÄÊ€eÍ.9ö@ʀʀË}2”®«ÄcKŠ2–×F7¥œ.@ʀʀË#NÈÐa ¦ÂŸ@fC‘•"Œé–4Ì€͆Ú3ÎMÞò@Ìß`fÁ€ÌË`̀̀3¾ñD¥4̀̀fÁ€Í~j̀̀Ï;z΀΀΀΀΀Î×CÄ@΀΀΀΀Ñ:Ñs”¤¨Ø‚@ЀÑ Ñ Ñ 4D›ò`Ò€ÓiDÞZkdC‘Ó RÒ€ÓÓ<æ3iFÕÅ"d@Ò€Õ’Ô€ÔÈFÇ@Ô€Ô€Õu5bÉ Í$Ir1”b€ÕUÔ€jWŸ °±Ô€Ô€Õ„gÓy-šª©¦-\ª@Ô€Ô€jZî‹q@Ô€Ô€Ô€5»,ÝÔÅ`Ô€ÕUÕU€‡@Ô€Ô€Ô€Ô€5B„ù ÔÙ`Ô€Ô€5jÁµ›ê”µâ€ÔÇ`Õb%¬(Ô€Ô€Ô€Ô€ÔÕ`kù€Ö€Ö€Ö€Ö€Ö€«Óƒ@Ö€ÖÓ`kDšY|‡ªÐkF’éŠK«Óƒ@Ö€×rÚ×c*Ö€k[‡Ö€ÖÖ`؀؀؀؀؀؀Ùc䨨`Ø€6•î†Ù¢mXö?&@mRµ6ja@Ý,dݾH(Ý>FôÜ€Ý6RÜ€Ü€Ü€Ý ÔÝ Ü€Ü€Ü€á€ß zßF&øp  ßbâÞ€Þ€Þ€ßZ Þ€Þ€Þ€ß#ÊßÞ€Þ€Þ€ß!7’cÉ 7“¹˜Pß"rßUÞ€Þ€oJÃ,å@ÞÄ`ßjØ(oå€Þ€Þ€Þ€Þ€ß.T)™  ß ß" €Ĉ…":Ü´oCâj¡¢44Þ€¯Ð„@Þ€Þ€ßKô$ÞÄ`Þ€Þ€Þ€Þ€7ć*˜o_¶™Þ€Þ€7ðƒ¤ßW ´ßR á6&¸s¤ à€à€á¢"à€à€à€à€àÅ`àÁ`à€à€à€pJÚI!Ž,©@ᾄà€^7!Ù™œ®@p™dáZdM°Ðà€à€Zù‘ Z`pX–XÐá5i2%©«kRôánÆ%à€à€à€°Í†@â€â€q­€â€â€â€ã#äã5q‰€qžtT8zå i´C‰R1@â€å-å-ä€ä€ä€‹+šóÆä€ä€å]ä€ä€å6ôå ä€ä€åž:åMæÑ@Á@èÍ`écêéMè€èÍAC@éè€éAè€é è€è€éz‚éEè€è€:e€tRšan`è€è€è€´Ë‚@t¹€é}è€ê€ë>Î ë D1Ðê€ëSÂê€ê€ê€ê€¡ë²ê€¡[ê€ê€ëRë HP`ÁÈ€u¶ê€ë^$Hê€ê€u‹u½€ê€uAª,l@:‘%éÛê€ê€ê€u‰€ì€íúí†â;}R­ íí6¦˜hì€vATì€íì$íì€ì€î€î€î€wJÚýÐw¥?;ðY ïÜh1Ðï]î€ï˜Òî€wYÒüï¶:ýî€îÒ`íî€î€îÁ`î€î€ïBÊî€î€î€ïF îÌ`ï9ïrjî€î€ïæjî€ð€bÙ_;ñþñð€ð€xAÖòÌ`óMyPúhw@ò€óA<‘ó<´ƒ<“fÒÊm<”Çìsdò€òÁ`óŠÂ<“mÏv¥5$òÁ`óìDó‚Âò€ò€ò€y•Fdò€ò€õ/Mô€ô€ô€ô€õ"^iÐPzEßM¡@õ„©Èô€=çöHi˜Åµ€ô€ô€ô€÷nö€{…(ö€ö€öÂ`{RßP÷¢âö€ö€ö€ö€{‘€÷mù%„Àù7eø€ùm}[–)°@}B›:û ú€>Ù€ú€ú€ú€ú€ú€ú€ú€ú€ú€ú€ú€ú€ú€ú€û+´4„*”„9”ý ’Àü€~EòZ¨@…¢J¦6Xý‚Àü€ü€ü€ü€¾À‡@¾À‡@üÀ`ü€þ€þ€ÿ þÇ`ÕFÿÞZ¥:´ÿ ÿ%]ΚPÿ"þ€…Z”þ€ÿºÂ?”ý&þ€þ€þ€þ€þ€þ€þ€þ€þ€ÿy?“y 8´µN À¬@À@À@À@`)%I@À@À@ÀŸ@À @À@À¨@À@À@À @À@À@Á@Áƒ@Á@Á@ÁŽ@Á@Á†@"^@Á@Á@Á@Á@Á@Á@Á@Â@Â@Â@¨@Â@Â@Ã@Ã@a áÊŸ’ Ã@Ã@x@ :@Ä@Ī@Ä@į@Ä£@Ä@ I@ K@Ä@Ä@Ä@Ä@ÄÄ@Ä@Ä@Ä@Ä@Ä@b" ù‚€1¦³Hb†1¦²Œd?@@bÂ˪ªªªªªªªªªªªªªªªª¦simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/JavaHelpSearch/SCHEMA0100644 0000000 0000000 00000000066 12114157751 024102 0ustar000000000 0000000 JavaSearch 1.0 TMAP bs=2048 rt=1 fl=-1 id1=3249 id2=1 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/JavaHelpSearch/TMAP0100644 0000000 0000000 00000104000 12114157751 023674 0ustar000000000 0000000 €ñ¿”0 “1 2111N9 Ùptœ1­.0 1.2S384²0ì01Õ2 wpt)3!07‹5L1§6¼0 :8pt|98991 Úpt2o0 x%í02™19kptŽ3.2:5130½4 Ûpt4M1U765271J9}55.59øptx63.0.2.1B5830O79 6ptp7K21a81.0 9About SimplyHTMLúChanging paragraph styles$reating a GUI to define link anchorss nd deleting link anchors manipulating links named stylesíHow to apply links MImage dialogËJava Web StartLink anchor dialog dialog ¿s in HTMLÐNew parts in this stageÂo TitlejOptions dialogSetting the element typeütage 7: Images$©8: Paragraph styles and named styles"9: Links£yle sheet storagewUser interfaceing LinkDialog and AnchorDialog ÏabandonedleÐoutðboxåveÉsenceîolutely›tract ¿action Pdocumentwplugin EwriterÅcadIceptTanceÕs7ss ýibleÊngÃompanied 5sy 1ing lish (edrdéing ØlyÑuntïhieventerïral ×ly ic;rtainRlyÂificate jhallenge4nge (ableÕd updatexeventsklistener„s!s*ingápterss£racter Žaction õsKge„eckaingÒs ild’oiceoseùr•s ’se˜n &unk %s zircumstance |sþlaim€svssües Neanupreriickableeding€ent åpboardÚose{d Šocument Nrßs/ing ks æocumentÚde«base d£s ing [llect„ionve von½r`ing `panelas¹umn¥sÎm"Ä.lighdev.app.shtm.plugin.installed Vtdev.app.shtmº.app helpWplugin z.installed&Ÿ.myplugin.properties/ÅaddonsYbination Ôe9d  åatrribute átribute]sbingßo¾es½mandàs7ence ¸t zedRsBitÞon unicate pact.rablyed©tibilityionsly[oriesÛyëy ûsableèdÅclaimers ‰repancies >ussed vs …ingák ;patchingBlayžedµing*sƒose ¥dštancensinctionguish ¬ing&ribute ›d Ring Åon+vided pingÜocò.htmUkÔed¡s)umentI'sÝationyed vent sqing îlistenerÌpane 's Á.getimagedir Âsaveimages2s Edrop {ing·main.xyTn't½eor¥wnwloadèeding•rag Sged Dsturerecognized Kræing Gsource P.startdragdwcn–op ;ped Htarget IlistenerPue‹ring€ynamicyally‰resource•sEe-mailp.goach•gerñrliertsier ºst óly¬yýdgeßit table nchorsactioned¿ingvon ting ¡on ôlabel inkactionˆnamedstyleactionfor—kit pane ãs‰ffectœive òly _panel'sýort hlesss¬ight¡ther lectronic¦gantment aryçs tree 8panelwsembedded3phasisloyeryty¼nablecd Sevents]sûingclosed®oding ¿unteredêd Jed 6ing tag^forcing÷g¶lishJhanced Vment s Ting çoughÓsureØd ìs ³ingterûed +sùirelyðriesþy yumeration 0vironemt@ment ³s Øqual9ivalent3rror s ispecially-senceÎtialftablish óes ‹ing;venÖt clistenerws±rúy#one's olutionaryfxactly`mple 7filefilter™s>ceptëion al µsterng€êÑŸexcludedingôsionjuse ˜e¤cutable e Åd 8s ionrcise0isitingpngÊtâingžsÜt5ing ,sÚpect›edXsrt wlain*edtingls‘nation qsLory›icitbly4orerÆressed¾ion Ýs?lyVsting tøendGedœingis ½sÙion .sÀtÁrnalraîctóor qs Efƒacility Bt dory ³il2sæure6lling se ¯miliar#y \picklist «ncyµr|ster €vor =urIxUeature’s*ingceTl ?s tched ys‹wÝeríieldös\le U.exists label Íoader  outputstreamÞs Ëaverl,ed Ïters ánalization edly{d aelement ;up ßlinkelementupspluginsos e žish±ed Ÿs red ¼sšst3tÊness>s\xsed  lexibilityle Kowocuses ‹lder“low medingªs,wing¨oingCntM-family tsize ®action  bold Jcomponent Zs «dialog Ÿfamily ¸action ¤picker ¡italic panel;s  ¹izeaction ¥pickerXrbid cingmatÝimage áaction llistaction ‡paraaction‹s  'tableaction’edzing Qs˜erŠlyÏings ˜th÷undation ='sšr{rame §workõee3dom§quentlyFmmain—'s Ä.action_selected Æ_key  Åunselected ½pptempdir Àgetapptempdir¾initapptempdir 5java´tp›ull u-featured ™nctionual Àities sy Œing´less~sZrtherŽmore°ture÷gap àrbage Atenstrasseeneral¿ly teõdEsJingcic‡ous œographical·rmanDy OsturesOt action nchors Çpptempdir  Xttributesrng€òÁ ^getbase 4cellattributes  urtablecell Tfile  pt/rnel¬y ìmap sÅind ªs ëtRnowking „ledge^nCriftel Dl abelñs“nguagees]st¯terËstrunchRed}ing£wíyoutës–zy }eading 2rn³st¡vesing ÝdBft u-marginlgal {ible jngthÎvalue —y Óss²t'ssWter 9s #ing¤veliØable ŽbDraryÓcenseŽd¯e÷s ¼pane-sWor4es³ghtÏdevG.comke s­mitation hseedõne p-through  €.separator Äs.kÁdialog ’edåimage-ng cname-sætext0uxystbdialogãed nŸers %ingmsŽing "nener:off7nepanel!selectionlistenerÖttle oactionEd ÐdocumentÅedtr•ingrplugins Jrulespsäcal 0e  3.getdefaulted{ingœsŠenance jor8ke sŠingÖnage Öplugindialog OsactionRingÙgageplugindialog)ipulate Ld Þing ,on ÷s¦ual ªlyëyppHpingrginh-bottomjlefttoppanel¾sFk Mtter „ximumšyóean sPt chanis½m >s¿diumÐetms Rhtod;ntionedu „listener†s ÉrchantabilityePgeŽdSsing4ssagedsIthod 's ologyŒs :icrosystemsŠddlekghtnd Fimalhtmlwriter>xedode _named_styleFsEparagraph_style”l †-view-controllerJed¾s;s ’ification ¦suede €ìÕ Qmodifying sular’e"sDnospace.st&lyhuse-clicks )entered *xited Llistener¡ve­dªment¯s³ing8ultiple Qtableattributeset vcðydoc±.htm Ëplugin.jarøname4ds ingžvigating ÅecessarilyÑyiityVedÍedôs?sted-tscape|work 2verthelessRw‰er  Ìfilesaver label oistitemactionfy¶sµgroupøxt  @cellaction ielement°o "de {n  À-editable)gui˜local commerciale%rmalÞlyþthpane÷tation'eLhing–iceÈs  Rficationszedäy`vember9t¬ull­mberÔedYs object ë'sÂs nligations²ed‡tainÒccasions%ur _rence¤sntoberjffer6ingUsÇicial™line´-allowed=ten¦klNd Œer —mitˆtedÄnceêe”sxline¸y¼to pen^ed„nd ingzly–s2raçteÇd ‘s0ing:on šsqposed ŸtèedÌion \alshrderÇedXingäinary -ientedyginal>ther|sAwiserursÝt •lined·put Xstream writer²side·ver }all Ãcome head †ridden¥e‚s šing view•writingtenƒwnerpZ.mystyleÆackage çs Þdding-rights gegs ]inted prÜs=latino nel‰s%sper2ragraph ¡s’pah Èllelñmeter ‡sítetershpraph !styledialog's3ent ‹serÙtyicularÝes¹lyÄsy ássxed!s¼ing>te #d html ùlabel ingˆtent‚sîhÃs qying¦df1ending†opleÔr cent¶ageÁfect ormÍanceâeding  £iodicallye €òÓ mpermission öspt$tedsistent †lynpectiveotinenthase ~otographicÊysicallyþick‘ed ¾r 1sótureë.jpg ÇeceQslace—d™sìingŒin textn®nedWtform ict |edOferencesnces!redÚs ©liminary Òpared Hing  Urequisite§sentšation ˆed +ing¹rving(identÙsÀedrs«ing  ?vcellactionŒent iewrousdly ¯ouslyJiceånt or ivateobably¢lemzsÞcedure zsŒss Âed ^sWing“or as Ûwindowevent \s ducejd ÌsøtÉs?gram¾'s  bmaticallyerÀingGs ”essOhibitedÁjectê'sÓminent¼oting –pt…edÖperìlytiesy  Èchangeevent listener 9ortionally‹rietaryTtect aedƒingqonòocolÎveçideÀd¼s„ing jtublic‘lyÃsh edXrposeMs ‚t ÃvalueÌquality ¨ery stion"ick ¶ly5.htm K6.htm Q7.htm d8.htm Æ39.htm  Ž/topic40.htm ¤3.htm ´4.htm »51.htm U62.htm  Ñ/topic63.htm Ù4.htm ä5.htm ò6.htm ü7.htm 8.htm 9.htm *70.htm 92.htm Ó74.htm  W/topic75.htm u6.htm |7.htm 8.htm œ9.htm ©80.htm ´1.htm Ë2.htm Ñ3.htm i86.htm  Ô/topic100.htm é89.htm ö91.htm 2.htm #3.htm :4.htm B5.htm M6.htm S9.htm ¦22.htm  ‰/topic23.htm ”52.htm  6.htm °73.htm-s tal vly wards r Mackždeoffs Šitional )nsfer îable ïs Ahandler :red Ëing[orm äed ing]late §d«ionƒparent ˆly íort ìed `ee´ies ggered€sviallouble¼ue !y kingÑurn€TÂÐturningWtorialhwo&y peñd7s !icalZlyØng!ugly Aimanagerl?rich0nchanged Ìompressedœder oline 'd¿ying™stand ssªo Õable  Þedithappened ×listener Ûs áction Ýhandler õlabel Ômanager xenforceable Efortunately®iform que ît zversal ™x3lessÉordered predictable|registerdocumentåsaved‘elected3ter il|tled couchedused–wantedpcoming date—d‹filelist  Ïormatcontrols ßs inggradingÈon –peréwards­rilvs"saˆgefsed].toýfulrå.home;sbsäing ‘rÌuallyáy¯tfÑil ’.copyfile“ityÕvalidity8ue changedIs briablegtions—ety²ous¯ector .s²ndor'rbatimìsaion¤s tusCteicaluy -ia)ceêew ºable¹ed ñfactory äingÇssible ¤t7ualizeBoid lumeþs w aitâeding ôlksPnt “edÈrrantiestyqtch YesÌysFebœ-centricÆstart­eks lcomeel®nt"rehateverenever 6teherÕraher°ileØole m@se‰ide;thºs‘llingÕndow [_closing6sïzip resdMshthjin¿out`ord #s•k Oarounded1ingós ry«w Ïrap ¨ped .ingÛs¹ite  çattributes Zr\uleÚs otyleÃingtenüote²ww ö.calcom.de Ädallaway.com mlightdev.com¬xml ùtract.htmûyears2ouryodyneÈzipng]late §d«ionƒparent ˆly íort ìed `ee´ies ggered€sviallouble¼ue !y kingÑurnsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/0040755 0000000 0000000 00000000000 12114157751 021664 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic58.htm0100644 0000000 0000000 00000007141 12114157751 023671 0ustar000000000 0000000

Saving documents

To save a document, it has to be visible as the currently edited document. To save this document

  • select 'Save' from menu 'File'

The document will be saved at the location it was openend from. If respective document was newly created and thus not saved so far, you will be prompted for a location and file name for the document as with saving a document under a different name (see below).

Important: You have to enter a file name including extension (.htm or .html). The extension will not be completed by the application if it was omitted.

Note: Documents are opened using the HTML version set in the options dialog. Unless set otherwise, HTML 3.2 is used as the default.

Image directory

Along with the document file, an image directory is created with any image file referenced in the saved document.

Style sheet maintenance

If named styles were created or changed for a document, a style sheet will be created during the save process if none is already present. If a style sheet with the same name is found at the target location, it is merged with the style sheet to be saved. Instead of defining named styles for a document and merging them with an existing style sheet, a new document can also use an existing style sheet right away. See ' Creating new documents' and chapter ' Options dialog' for an explanation about this option.

Caution: An existing style sheet with the same name could have styles with the same name as altered ones in the saved style sheet. Overwriting such styles could cause unwanted styles to appear in other documents sharing the particular style sheet.

Therefore you should consider to either

  1. not save documents in the same directory when they do not share the same set of named styles or
  2. use different style names for different styles over all documents sharing the same style sheet

Saving a document under a different name

To save a document under a different name or at a different location than where it has been openend from

  • select 'Save as...' from menu 'File'
  • choose a new location and/or new file name and
  • press 'Save'

Respective document again has to be visible as the currently edited document.

The document will be saved at the chosen location under the given file name. The original document remains intact at the original location but SimplyHTML switches the current document to be the newly saved one so subsequent save operations go to that file unless explicitly changed again.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic59.htm0100644 0000000 0000000 00000002212 12114157751 023664 0ustar000000000 0000000

Opening existing documents

To open an existing document

  • select 'Open...' from menu 'File',
  • choose a document file to open from the selection frame possibly by navigating to a different location than initially shown
  • press 'Open'

A new tab in the main frame of SimplyHTML will be opened with the chosen document opened into it. All other open documents remain open which is indicated by respective tabs identifying other open documents. The display switches to the opened document with its tab on top.

The document is now ready to be edited .

Note: Documents are opened using the HTML version set in the options dialog. Unless set otherwise, HTML 3.2 is used as the default.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60a.htm0100644 0000000 0000000 00000004225 12114157751 024023 0ustar000000000 0000000

Searching documents

SimplyHTML supports find+replace which can be accessed using the find & replace button next to the help icon, or the Edit->Find & Replace menu item.

Searching

In order to search incrementally for a search term, enter that term in the Text to find text box and hit the Find next... button (or press enter) each time to get the next search result. Have a look at the Search/Replace options below.

Replacing

In order to replace occurrences of a search term by a replacement string, enter the search term in the Text to find text box and the replacement string in the "Replace with text box, and hit the Replace... button. You will be prompted whether you want to replace for each occurrence. Have a look at the Search/Replace options below. In particular, when using approximate search, check every occurrence that is to be replaced carefully!

Search/Replace options

Match case
Whether to honor case when matching.
Match approximately
Whether to use approximate ("similarity") matching, i.e. allow differences between the search term and the match(es) in the text. For example, searching for files will find flies or setup=set up.
Whole words only
Restrict matches to whole words. Not supported for approximate matching.
Search up/down
Enter the direction for (incremental) search or replace.
Search from start
Whether to search from the beginning of the document or from the current cursor position.
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic57.htm0100644 0000000 0000000 00000002432 12114157751 023666 0ustar000000000 0000000

Creating new documents

To create a new empty document

  • select 'New' from menu 'File'

A new tab in the main frame of SimplyHTML will be opened with a new empty and untitled document to work with. All other open documents remain open which is indicated by respective tabs identifying other open documents. The display switches to the newly created document with its tab on top.

The document is now ready to be edited .

Using an existing style sheet for new documents

Usually a reference between a document and a style sheet containing named styles is only created when named styles are added to a document explicitly. In cases where a new document shall use an existing style sheet which was previously created for another document, option 'Link new documents with default style sheet' has to be set in the options dialog.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic61.htm0100644 0000000 0000000 00000002437 12114157751 023666 0ustar000000000 0000000

Closing documents

To close the document, that is currently shown in the editor

  • select 'Close' from menu 'File'

To close a currently open document, that is not shown in the editor

  • click on the tab representing the document and
  • select 'Close' from menu 'File'

To close all currently open documents

  • select 'Close all' from menu 'File'

Closing a document will attempt to save pending changes before closing. If this fails, an error message will be shown the document will remain open.

When exiting the application while documents are open, as well attempts are made to save pending changes for all open documents. If this fails, an error message will be shown for each document, where a save could not commence, respective document(s) stay open and the application is not terminated.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/0040755 0000000 0000000 00000000000 12114157751 023150 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic177.htm0100644 0000000 0000000 00000002111 12114157751 025227 0ustar000000000 0000000 Using find and replace

Using find and replace

In development stage 11 of application SimplyHTML functionality to find and replace text phrases inside of documents was added. Find and replace is implemented as a dialog combining all relevant functions. To invoke find and replace

  1. open menu Edit
  2. select Find and replace

A dialog will appear allowing to set all relevant options and to initiate a find or replace operation. When more than one document is open while find and replace is invoked option 'Search whole project' allows to perform find and replace over all open documents.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic172.htm0100644 0000000 0000000 00000004002 12114157751 025223 0ustar000000000 0000000

Working with HTML code

For an experienced user being familiar with HTML it sometimes is quicker to manipulate a certain portion of HTML directly instead of having to wade through GUI elements. For this reason in stage 10 of application SimplyHTML a way to work on the HTML representation of any given text document is implemented.

To see and edit the HTML representation of a given text document

  1. open the document
  2. click on the tab HTML code view at the bottom of the editor pane

The display will switch from layout view to HTML code view. In the HTML code view the structural information for a particular document is shown along with its content. Tags and attributes are mixed with plain text.

The HTML code view does not show how the plain text will look. Instead it highlights the structural portions of HTML and allows to work on the HTML code.

All changes to the HTML code are transformed to the resulting layout automatically when switching back to layout view (clicking on tab Layout view at the bottom of the editor pane, that is).

Important: To work on HTML code should only be done by users being experienced in HTML use. Changing HTML code can destroy the structure of the document leading to unpredictable results in the layout of respective document and even loss of content.

Note: With bigger documents it can take a short while to display the HTML code initially.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic159.htm0100644 0000000 0000000 00000004101 12114157751 025230 0ustar000000000 0000000

Creating and manipulating links

A link in HMTL is a reference to another location and/or document a user can jump to. See chapter ' Links in HTML' for a technical description of links.

To create a link

  • select a portion of text to format as link in the editor
  • select 'Link...' from menu 'Format'
  • make link settings as appropriate and
  • press 'OK'

To change settings for an existing link

  • move the caret into text formatted as link
  • select 'Link...' in menu 'Format'
  • make link changes as appropriate and
  • press 'OK'

With both selections above, a link dialog is brought up to set link attributes accordingly. Once the link dialog is completed, the previously selected text portion is formatted as link accordingly.

Note: A link is only shown as link. It does not work as a link in the way that the link target is shown when the link is clicked. This probably will be added in a future stage.

See also

Links in HTML

Creating and manipulating link anchors

Link dialog

Link anchor dialog

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic135.htm0100644 0000000 0000000 00000007366 12114157751 025242 0ustar000000000 0000000

Working with images

In line with the HTML specification for image references, images can be added to any document of application SimplyHTML by inserting references to separate image files. To insert an image

  • place the caret to the point in the document where an image shall appear
  • select 'Image...' from menu 'Insert'
  • in the image dialog press button 'Add'
  • locate the file containing the image in the file chooser
  • press 'OK' in the file chooser
  • adjust attributes in the image dialog as needed and
  • press 'OK' in the image dialog

The selected image is inserted by placing a reference to the selected image file into the document. The image appears at the chosen location as long as the associated image file is present in the image repository. The image repository is created and maintained by application SimplyHTML automatically. When adding image files as described above, they are copied to and kept in the image repository. Once an image file is present in the image repository of a document, it does not have to be located in the file system again.

Important: If a document containing image references is moved to another storage location by hand, the associated directory with image files (the image repository, a directory named images) has to be moved too.

Making changes to images in documents

To change settings of an image

  • click on the image
  • select 'Image...' from menu 'Format'
  • adjust attributes in the image dialog as needed and
  • press 'OK' in the image dialog

To remove an image and its file reference from a document

  • click on the image
  • press the [Backspace] or [Delete] key

To remove an image file from the image repository

  • select 'Image' from menu 'Insert'
  • select the image file to remove from the list of files in the image dialog
  • press button 'Delete'
  • press 'Yes' in the option pane that appears
  • press button 'Cancel' in the image dialog

This will permanently delete the image file from the image directory of the currently open document.

Important: When deleting an image file from the image repository, all references to that image file inside the document remain intact but the image is rendered as 'broken' link (broken icon picture).

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic102.htm0100644 0000000 0000000 00000005016 12114157751 025222 0ustar000000000 0000000

Creating and manipulating tables

To create a table,

  • move the caret to the location where the table shall be inserted
  • select 'Table...' from menu 'Insert'
  • choose the number of columns the new table initially shall have and
  • press 'OK'

A new table with a single row and the chosen number of columns will be inserted at the caret postion. The caret is placed into the first cell of the new table.

Caret movement inside tables

By pressing the TAB key, the caret is moved into the next cell. Pressing SHIFT TAB moves the caret to the previous cell. Pressing the TAB key while the caret is in the last cell of the table will append a new row before moving to the next cell.

Changing the table structure

Structural changes include inserting or appending rows and columns and deleting rows and columns. To change the table strucutre

  • move the caret into the table and
  • select an option from menu 'Table' accordingly

Rows and columns always are inserted before the current caret position. Column widths are adjusted to maintain the table width.

Changing the table format

Table format includes all attributes of the table and its cells such as table width, cell width, background color of cells, text alignment inside cells, margins and borders, etc. To change the table format

  • move the caret into a table,
  • select 'Table...' from menu 'Format',
  • set table and cell attributes in the table format dialog and
  • press 'OK'

The table dialog initially shows all attributes currently set for that table. All changes made are applied to the underlying table at once when button 'OK' is pressed. Cell attributes can be applied to the current cell, the current row, the current column or to all cells.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic162.htm0100644 0000000 0000000 00000005467 12114157751 025242 0ustar000000000 0000000

Creating and deleting link anchors

Link anchors are a way to designate certain positions inside a document. By using link anchors a link can directly reference and jump to a certain position inside a document. To make this possible, a link anchor has to be defined in that document before any link can reference to that position.

To define a link anchor inside a document that is to be targeted by a link

  • open the target document
  • select 'Anchors...' from menu 'Format'
  • in the link anchor dialog select the text portion inside the document to be used as a link target
  • click button 'Add'
  • type in a name for the new link anchor
  • press 'OK'
  • select 'save' from menu 'File' to save the document

A link to the newly defined link anchor can now be set in the link dialog. As an alternative to above steps a link and link anchor can be set in one step using the link dialog too.

To delete a link anchor

  • open the target document
  • select 'Anchors...' from menu 'Format'
  • in the link anchor dialog select the name of the link anchor to delete
  • click button 'Delete'
  • press 'OK'
  • select 'save' from menu 'File' to save the document

Important: Above steps only delete the selected link anchor. Links referencing this link anchor remain the same thus leading to a 'dead' location.

As an alternative to above steps a link anchor can be deleted through the link dialog too.

See also

Links in HTML

Creating and manipulating links

Link dialog

Link anchor dialog

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic160.htm0100644 0000000 0000000 00000002635 12114157751 025232 0ustar000000000 0000000

Setting the element type

A specialty of HTML is to have different element types for content of a document separate from the content formatting. Element types could be paragraph, link or a certain kind of heading. One can think of element types being a way to distinguish certain structures in a document while named styles are a way to differentiate between certain formats both independent from each other.

Element types are applied to one or more paragraphs always. By default a paragraph is of element type paragraph as well.

To set another element type,

  • select one or more paragraphs in the editor pane
  • choose an element type from the element type combo box in the tool bar

Currently the following element types can be set

  • paragraph
  • heading 1
  • heading 2
  • heading 3
  • heading 4
  • heading 5
  • heading 6
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic139.htm0100644 0000000 0000000 00000010120 12114157751 025224 0ustar000000000 0000000

Creating and manipulating named styles

The previous chapter describes how a certain format can be applied to one or more paragraphs at once. However, in many cases similar formats are applied to the content over and over again. To reduce maintenance effort and storage space, named styles can be defined for such formats.

Named styles are not applied directly to a document when created. Instead they are saved to the style sheet of a document. To use named styles two steps are necessary:

  1. define a named style by setting all its formatting attributes and saving it to the style sheet
  2. select a portion of the document in the editor and pick a named style for it from the style selector in the tool bar

Once a named style is saved in the style sheet, it will appear in the style selector in the tool bar of SimplyHTML. It is necessary to define named styles only once for a combination of document and style sheet. The style sheet for a document is maintained automatically by SimplyHTML and saved whenever a document is saved.

Creating a named style

To define and save a named style

  • select 'Named style...' from menu 'Format'
  • set formatting attributes in the paragraph style dialog accordingly
  • select an element type for the new style (selector located above list of named styles)
  • press button 'Save as...'
  • enter a name for the style and press 'OK'

Important: Styles are created only for the element type that is currently selected (paragraph, link, heading, etc.). Only changed attributes are saved to the style sheet.

Changing a name style

To change settings for an existing named style

  • select 'Named style...' from menu 'Format'
  • select the element type (selector located above list of named styles)
  • select the named style to change settings for in the list of existing named styles
  • change formatting attributes in the paragraph style dialog accordingly
  • switch back to the 'Paragraph' tab if you are on tab 'Font'
  • press button 'Save'
  • press button 'OK'

The named style for the selected element type (paragraph, link, heading, etc.) will be overwritten with the new settings. At the same location named styles can be deleted from the style sheet too.

Deleting a named style

To delete a named style

  • select 'Named style...' from menu 'Format'
  • select the named style to change settings for in the list of existing named styles
  • press button 'Delete'

Caution: Deleting a named style will cause content portions formatted with that style to be rendered in an unpredictable style. Be sure to delete only unused styles. Keep in mind that a named style could be used in another document sharing styles with the currently edited one.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic127.htm0100644 0000000 0000000 00000001616 12114157751 025233 0ustar000000000 0000000

Common edit functions

SimplyHTML implements the common edit functions such as cut, copy and paste as usualy with any text processing application. They are available through menu 'Edit'.

To cut, copy or paste contents of SimplyHTML documents

  • select a text portion and
  • choose an option from menu 'Edit'

Cut, copy and paste are performed including styles. In addition, the editor of SimplyHTML has drag and drop capabilities, by which the same functionality can be reached simply by selecting, clicking and dragging certain text portions.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic126.htm0100644 0000000 0000000 00000005074 12114157751 025234 0ustar000000000 0000000

Creating and manipulating lists

Lists are a consecutive amount of paragraphs being displayed with a symbol separating each paragraph at the beginning. There can be ordered lists, which follow an order such as 1., 2., 3., 4. or a., b., c., d. and unordered lists following no specific order such as lists with a bullet symbol at the beginning of each paragraph.

Lists can be created in two ways

  1. start list formatting and then type content in the form of list items as needed or
  2. type in content and then switch on list formatting for recently typed paragraphs

Turning list formatting on or off

To turn list formatting on or off

  • place the caret to the position list formatting shall be switched on or off or
  • select a text portion for which list formatting shall be switched on or off and
  • select 'Bulleted list on/off' or 'Numbered list on/off' from menu 'Format'

Alternately, respective toggle buttons in the tool bar can be used as well.

Caret movement inside lists

While typing text, the caret moves similar to a region not being formatted as list. Pressing [Enter] while the caret is inside a list will create a new list item after the item the caret is currently in.

Changing the list formatting

List format can be changed individually through a list format dialog too. The list format dialog can be used to change the list type (bulleted, numbered, bullet symbol and order criteria) as well as the indentation of a list. To change list formatting individually

  • select the the text portion to change list formatting for
  • select 'List...' from menu 'Format'
  • make settings in the list format dialog and
  • press 'OK'
simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic85.htm0100644 0000000 0000000 00000002670 12114157751 025157 0ustar000000000 0000000

Changing font settings

Stage 3 of application SimplyHTML adds font manipulation functionality in two ways. Font settings can be changed either through a font dialog or through respective controls in the tool bar.

Changing several font settings at once

To change all font settings for a text portion at once

  • select the part of text to change font settings for
  • select 'Font' from menu 'Format' or press respective button in the tool bar
  • select font settings from the dialog, that appears and
  • press 'OK'

Changing single font settings

A quick way to change single font settings is through the tool bar

  • select the part of text to change a single font setting for and
  • press respective button in the tool bar

This way the tool bar allows to switch font family and size as well as to toggle between bold/normal, italic/normal and underlined/not underlined each in one step.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60/topic138.htm0100644 0000000 0000000 00000002333 12114157751 025232 0ustar000000000 0000000

Changing paragraph styles

As opposed to styles applied to single characters, paragraph styles are a way to define a group of formatting settings for one or more paragraphs at once.

To set the style for one or more paragraphs

  • select the paragraph(s) to be formatted in the editor
  • choose 'Paragraph...' in menu 'Format'
  • set formatting options accordingly and
  • press 'OK'

All contents of the selected paragraph(s) will assume the settings formatted in the Paragraph Style Dialog accordingly, unless other individual styles have been set for single characters inside the respective paragraph(s).

Read on to find out how to define and store a set of predefined formats as named styles.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic163.htm0100644 0000000 0000000 00000001755 12114157751 023753 0ustar000000000 0000000

User interface

This chapter explains parts of the user interface of application SimplyHTML. The chapter describes from the perspective of certain parts of the GUI (e.g. a dialog) as opposed to previous chapters explaining parts of the application from a functional perspective (e.g. 'how to save a file').

This section covers of the following GUI parts of application SimplyHTML

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic116.htm0100644 0000000 0000000 00000005350 12114157751 023744 0ustar000000000 0000000

What is SimplyHTML?

As shortly described in chapter 'About the SimplyHTML project ', SimplyHTML is an application for text processing on the basis of HTML documents formatted with CSS. It combines functionality of a word processor with the standards of HTML and CSS.

Usage of the HTML and CSS standard for documents created with SimplyHTML opens a wide variety of usage possibilities because many other applications 'understand' and use HTML and CSS such as

  • web browsers
  • other text processors
  • presentation and graphics software

Features

SimplyHTML features the following functionality

  • opens, maintains and saves documents in HTML and CSS format
  • any number of documents can be opened at the same time and are displayed in a tabbed pane
  • formatting of paragraph styles
  • creation of own named styles for paragraphs
  • formatting of font attributes on character level
  • rich table formatting
  • list formatting
  • insertion and formatting of JPEG and GIF images
  • creation and manipulation of links and link anchors
  • drag and drop in the editor pane
  • cut and paste for styled HTML text
  • cascading undo/redo
  • plug-in facility for extension of SimplyHTML
  • support of Java Web Start for easy deployment
  • editor for HTML code with syntax highlighting

See 'Planned development stages' for additional features to be present in future.

Differences of HTML and CSS in various environments

SimplyHTML tries to consequently adhere to HTML and CSS standards. However, there are discrepancies over different environments, which sometimes can be compensated, sometimes only partly or not at all. Please see chapter ' Discrepancies in HTML and CSS rendering' on that subject too.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic163/0040755 0000000 0000000 00000000000 12114157751 023234 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic163/topic166.htm0100644 0000000 0000000 00000010546 12114157751 025324 0ustar000000000 0000000

Image dialog

The image dialog is used to add images to a document and change attribute settings for such image. It also allows to maintain the image repository a document is associated to. See also chapter ' Working with images' about how images are applied to documents.

the image dialog

Image files panel

In panel image files all files are shown that are stored in the image repository that is associated to the currently edited document. Clicking on one image file name listed in panel Image files shows the image in panel Preview and lists image attributes in panel Properties.

Add

Press button Add to add a new image file to the repository. A file chooser dialog will appear to select a file from. The selected file is copied into the image repository associated with the document and displayed in pnale Image Files.

Delete

Button Delete is used to remove a file from the image repository. Respective file is permanently deleted from the directory used as an image repository.

Note: All references to the deleted image in any document associated to this image repository will be 'broken' when deleting an image from the repository.

Preview panel

Panel Preview displays an image selected in panel Image files. An image is scaled to the current size of the preview area when initially displayed. When image attributes are changed in panel Properties, the image size changes accordingly. The size of the preview area remains unchanged and scroll bars are shown when an image does not fit into the preview area. The size of the preview area can be changed by resizing the dialog window. In this case the image size remains the same and only the size of the preview area changes along with the window size.

Properties panel

In panel Properties attributes of the image currently displayed in panel Preview are shown. Changes to image attributes in panel Properties directly affect the image shown in panel Preview, i.e. the image changes in size accordingly.

Scale

Use this control to enter a value by which the size of the image is to be scaled. Size is scaled proportionally, i.e. the relation of width and height is always kept.

Width

Use this control to set a width for the image. Height and scale are adjusted accordingly.

Height

Use this control to set a height for the image. Width and scale are adjusted accordingly.

Horiz. Space

This field can be used to set a value for space between the image and objects on the left and right of the image.

Vert. Space

This field can be used to set a value for space between the image and objects on top and bottom of the image.

Alignment

Alignment controls how the image is to be aligned relative to other objects, i.e. an alignment setting of left aligns the image left of any other objects. (This setting is modeled but not rendered in SimplyHTML)

Border

Set the width of a border around the image. A setting of 0 means no border, any other value means a border around the image in the specified width.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic163/topic167.htm0100644 0000000 0000000 00000006071 12114157751 025323 0ustar000000000 0000000

Options dialog

The options dialog allows to set general preferences for application SimplyHTML. All settings are persistently stored for each individual user and reinstated when SimplyHTML is launched.

the options dialog

Look and feel

A feature of the Java[tm] platform is its 'pluggable look and feel' concept. With this setting the user can select a look and feel from those installed on the machine SimplyHTML is running on.

Write HTML files as HTML 3.2

Setting this option causes SimplyHTML to create document files in HTML 3.2. This means that some features from later HTML versions are not available such as certain CSS formats and HTML tags. Setting this option is useful for environments that require HTML 3.2 such as JavaHelp.

Write HTML files as HTML 4

Setting this option causes SimplyHTML to create documents in HTML 4. This enables certain formatting features such as individual table borders or background coloring for individual parts of text.

Link new documents with default style sheet

Usually a reference between a document and a style sheet containing named styles is only created when named styles are added to a document explicitly. In cases where a new document shall use an existing style sheet which was previously created for another document, this option can be used.

Selecting this option causes creation of a style sheet reference in newly created documents linking to the default style sheet (' style.css'). File ' style.css' always is created by SimplyHTML, when named styles are added to a document.

When a new document is created with this option selected, the style sheet reference to ' style.css' is included in the new document upon creation even without named styles being defined for the new document so far. The new document has then to be saved to the directory where respective file ' style.css' is already located for the styles from ' style.css' to be in effect in the new document.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic163/topic165.htm0100644 0000000 0000000 00000005376 12114157751 025330 0ustar000000000 0000000

Link anchor dialog

The link anchor dialog allows to create and remove link anchors for a document. It is shown with either the document that is currently edited (when opened through menu 'Format') or with the document targeted in a link (through the link dialog).

the link anchor dialog

Defined anchors panel

In panel defined anchors all existing link anchors for the shown document are listed. Clicking one link anchor name in panel defined anchors highlights the position of that link anchor in panel Document.

Add

Clicking button Add will show an input dialog to enter a name for a new link anchor to create. Button Add is only enabled when a portion of text is selected in panel Document. The new link anchor will be created for the position selected in panel Document.

Delete

When button Delete is clicked, the selected link anchor is removed from the assocated document. Button Delete is only enabled when a link anchor is selected in the defined anchors panel.

Document panel

Panel Document shows the document this link anchor dialog allows to set link anchors for. In the shown document, the currently selected link anchor is highlighted. If a new link anchor is to be created, panel Document is used to select the text to formatted as a link anchor first.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic163/topic181.htm0100644 0000000 0000000 00000011673 12114157751 025323 0ustar000000000 0000000 Find and replace dialog

Find and replace dialog

Find and replace is done in a dialog window where all relevant options can be set:

the find and replace dialog

Text to find

In field Text to find an arbitrary text phrase can be entered. This text phrase will be searched during a find or replace operation.

Replace with

In field Replace with a text phrase is entered that is to replace the text phrase entered in field Text to find.

Find next...

Button Find next... is pressed to initiate a find operation with the settings currently selected in the find and replace dialog.

Replace...

By pressing button Replace... a replace operation is initiated. The find and replace dialog is hidden and the next occurrence of the phrase in field Text to find is located. When found, an option pane lets the user choose whether or not the found occurrence shall be replaced by the phrase in field Replace with. Alternately the user can choose to replace all occurrences or to terminate the replace operation. Once done, the find and replace dialog appears again.

Cancel

Button Cancel is used to terminate a find operation. It is only enabled (not dimmed) when a find operation is in progress (button Find next... has been pressed). With pressing button Cancel the current find operation is terminated and the find and replace dialog can be used to start a new find and replace operation with different settings.

Close

With button Close the find and replace dialog is closed.

Whole words only

Option Whole words only is used to restrict matches to whole words. When not selected all occurrences of the phrase in field Text to find are located even when they are found as part of a word.

Match case

By selecting option Match case a find operation only locates occurence of the phrase in field Text to find when they match the exact case.

Search from start

Selection of option Search from start causes a find operation to begin either at the first or last position of the particular document regardless of the current caret position, depending on setting Search up and Search down respectively.

Search whole project

Option Search whole project is only visible when more than one document is open at the moment. With selection of option Search whole project all documents are searched that are currently open.

Search up

Option Search up causes the search to be performed from bottom to top of any given document.

Search down

Option Search down causes the search to be performed from top to bottom of any given document.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic163/topic164.htm0100644 0000000 0000000 00000014333 12114157751 025320 0ustar000000000 0000000

Link dialog

In the link dialog all link attributes are shown and can be changed (see also 'Creating and manipulating links').

the link dialog

Style

Combo box Style allows to set by which named style the link shall be formatted in the editor. Any existing named style from the associated style sheet is listed in this combo box.

Type

With combo box Type a link type can be set such as 'local file', 'relative address', 'mailto', etc. Other elements of the link dialog are enabled or disabled according to selections in combo box Type.

When switching between types 'local' and 'relative' or vice versa, contents of text field Address is automatically changed accordingly (relative or absolute address snytax),

Address

In text field Address the address of the link target is shown. An address can be typed into this field at any time. No protocol such as telnet:/ or file:/ needs to be typed into field Address. The protocol information is automatically generated from the selection in combo box Type.

Browse (Address)

With button Browse next to text field Address a local file can be selected through a file chooser dialog. This button is enabled, if ' local' is selected in combo box Type.

Anchor

In text field Anchor the name of a link anchor can be entered (see also ' Creating and deleting anchor links'). The entered anchor name refers to an anchor link inside the target referred to by the entry in text field Address. The separator character (# ) for link anchor names does not need to be entered, it is completed automatically by the link dialog.

Browse (Anchor)

With button Browse next to text field Anchor an existing link anchor can be selected or a new link anchor can be defined throgh the link anchor dialog. The link anchor dialog is shown for the link targed specified in text field Address.

Show link as Text

With button Show link as Text is selected that the link specified in this dialog is to be entered as a text link into the associated document. When this button is selected, panel Text will be shown.

Text panel

In panel Text a text can be entered that is to be formatted as link. Panel Text is only visible, if button Show link as Text has been selected.

Show link as Image

With button Show link as Image is selected that the link specified in this dialog is to be entered as an image link into the associated document.

Image Panel

In panel Image an image and image size can be selected. The selected image will be shown in the associated document as link. All image settings in panel Image are only shown. They can be set or changed through button Set....

the link image panel

Button Set...

All image properties in panel Image are set through button Set... which will bring up an image dialog. In the image dialog an image file and its size can be selected accordingly.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic118.htm0100644 0000000 0000000 00000004276 12114157751 023754 0ustar000000000 0000000

Using plug-ins

SimplyHTML provides a mechanism to extend the main editing functionalities of the application by external objects called 'plug-ins'. Plug-ins are installed by copying one or more plug-in files into the directory SimplyHTML is installed in. Once installed, a plug-in is loaded and activated inside SimplyHTML upon start of the application by default.

Accessing plug-in functions

Each plug-in provides an own menu in menu 'Plugin' of SimplyHTML where all plug-in functionality is available. In addition there usually is a menu item in the 'Help' menu having documentation on the plug-in.

To access the functions of a plug-in

  • open menu 'Plugin'
  • open the sub-menu for respective plug-in

To access help information for a given plug-in

  • open menu 'Help'
  • select the menu item referring to help for a given plug-in

Activating and de-activating plug-ins

Loaded plug-ins can be activated or deactivated for each user individually. As well the location where a plug-in is docked inside SimplyHTML's main window can be changed. To change the settings of any loaded plug-in

  • select 'Manage plug-ins...' from menu 'Plugin'
  • choose a plug-in from the list of loaded plug-ins
  • adjust settings accordingly and
  • press 'Close'

The settings are applied to respective plug-ins and are stored individually for the user who made the changes. See respective chapter in section 'Inside SimplyHTML' if you would like to know more about plug-ins.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic15/topic60.htm0100644 0000000 0000000 00000003113 12114157751 023655 0ustar000000000 0000000

Editing documents

The main functionality of SimplyHTML is to create and manipulate text so editing an existing text is a main part of this documentation as well. In this section all editing functions are explained in detail. They are divided into the follwing topics.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/help.hs0100644 0000000 0000000 00000001637 12114157751 021670 0ustar000000000 0000000 help item15 TOC javax.help.TOCView helpTOC.xml Index javax.help.IndexView helpIndex.xml Search javax.help.SearchView JavaHelpSearch simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/toc.htm0100644 0000000 0000000 00000000612 12114157751 021673 0ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/project.prf0100644 0000000 0000000 00000000340 12114157751 022551 0ustar000000000 0000000 ¬ísrjava.util.Hashtable»%!Jä¸F loadFactorI thresholdxp?@ w tpdfftPage tcsdbtnotphfst8tpttltContentstpshdq~tppblt2tpsftq~tpdfhtSimplyHTML Tutorialtphfft Sans-Serifxsimplyhtml-0.17.3/src/com/lightdev/app/shtm/help/helpIndex.xml0100644 0000000 0000000 00000127430 12114157751 023046 0ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/helpTOC.xml0100644 0000000 0000000 00000030462 12114157751 022422 0ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic1.htm0100644 0000000 0000000 00000006532 12114157751 022314 0ustar000000000 0000000

About SimplyHTML

SimplyHTML is an application for text processing. It stores documents as HTML files in combination with Cascading Style Sheets (CSS). The application combines text processing features as known from popular word processors with a simple and generic way to store textual information and styles.

SimplyHTML is not intended to be used as an editor for web pages in the first place (although it can be used for this purpose with some limitations too). Especially HTML and CSS produced by other applications might or might not work with SimplyHTML, as other applications as well have trouble with content not produced by themselves.

See also chapter 'What is SimplyHTML? ' in section 'Using SimplyHTML' for additional details.

The SimplyHTML Project

The SimplyHTML project was started to build an application with above features and to document, how this can be done. This is approached by building SimplyHTML in several stages and by documenting all stages thoroughly. Each stage is covering certain functionality making it easier to concentrate on some of the many details such an application is made of. Stages are described in this tutorial, which as well serves as online help. Source codes are documented with Javadoc in addition.

Open Source

All source code is openly available along with the application and can be used to find out, how the application is working. Among serving for above functions, SimplyHTML shall be an example for developers intending to build applications with similar functionality. As well it can serve as basis for other applications.

Please see chapter License for details about terms and conditions for availability and distribution of this product.

Documentation

Sources are documented by Javadoc comments, which are compiled to an API documents collection with Javadoc.

The project and application is documented with this tutorial. The tutorial covers general information about the project and the application, information about installation and requirements, the internal structure and functions of SimplyHTML and finally its usage .

The tutorial can be used as online help for SimplyHTML as well as for reading about how SimplyHTML is built. It is available in formats plain HTML, JavaHelp and PDF. The tutorial was built with application HelpExpert entirely.

Application HelpExpert is available at http://www.calcom.de/eng/product/hlpex.htm

simplyhtml-0.17.3/src/com/lightdev/app/shtm/help/topic22.htm0100644 0000000 0000000 00000001756 12114157751 022402 0ustar000000000 0000000

Getting started

Alright, you somehow found this tutorial, made it through lots of preliminary information about SimplyHTML and finally like to use it somehow. Thank you! And here is how it works:

  1. find out, if you meet the requirements
  2. make sure, download and installation is complete (yes, you have to do it without fancy installers, but hey, relax: it is simple, even I can do it...)
  3. and off you go by starting the application

Seriously you should at least be familiar with this part of the documentation before you try to use SimplyHTML.

simplyhtml-0.17.3/src/com/lightdev/app/shtm/HTMLText.java0100644 0000000 0000000 00000030674 12114157751 021733 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.io.IOException; import java.io.StringWriter; import java.util.Vector; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.ElementIterator; import javax.swing.text.html.HTMLDocument; /** * A class to represent a portion of HTML text. * *

In stage 9 copy and paste have been refined to correct bugs that * occurred when cut and paste was happening in nested paragraph elements

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class HTMLText { /** the HTML representation of the text */ private String htmlText; /** the plain text representation of the text */ private String plainText; /** holds the copied plain text chunks */ private final Vector clipText = new Vector(0); /** holds the copied character attributes mapping to clipText */ private final Vector clipAttr = new Vector(0); /** * indicates whether or not the html represented by this * HTMLText instance contains more than one paragraph */ //private boolean multiPara = false; private boolean stringRepresentation = false; /** * construct a new empty HTMLText object */ public HTMLText() { } public HTMLText(final String htmlText, final String plainText) { this.htmlText = htmlText; this.plainText = plainText; stringRepresentation = true; } /** * copy an HTML string representation of a content portion from the * given editor pane. * * @param src the SHTMLEditorPane to copy from * @param start the position to start copying at * @param length the length of the content portion to copy * * @return an HTML string representation of the copied portion of content * * @see com.lightdev.app.shtm.SHTMLEditorPane */ public void copyHTML(final SHTMLEditorPane src, final int start, final int length) throws BadLocationException, IOException { final HTMLDocument doc = (HTMLDocument) src.getDocument(); if (doc.getParagraphElement(start).equals(doc.getParagraphElement(start + length))) { stringRepresentation = false; clearStyledText(); copyStyledText(src); } else { stringRepresentation = true; final StringWriter sw = new StringWriter(); final SHTMLWriter w = new SHTMLWriter(sw, doc, start, length); final Element first = doc.getParagraphElement(start); final Element last = doc.getCharacterElement(start + length); w.write(first, last); htmlText = sw.getBuffer().toString(); plainText = doc.getText(start, length); } } /** * insert this HTMLText into a Document. * * @param document the document to insert into * @param position the text position to insert at */ public void pasteHTML(final Document document, int position) throws BadLocationException, IOException { /** * if only text within one paragraph is to be inserted, * iterate over copied text chunks and insert each * chunk with its own set of copied attributes. Else * simply read copied HTML code back in. */ if (!stringRepresentation) { final int nrTextChunks = getClipTextSize(); String text; for (int i = 0; i < nrTextChunks; i++) { text = getCharactersAt(i); document.insertString(position, text, getCharacterAttributes(i)); position += text.length(); } } } /** * Determines the HTML string resulting from pasting the given HTML string at the given * position within the given paragraph element. * * @param doc the document to insert to * @param characterElement the character element to split * @param paragraphElement the paragraph element to split * @param targetPosition the text position inside the document where to split * @param pastedHtml the html text to insert at pos */ public String splitPaste(final SHTMLDocument doc, final Element characterElement, final Element paragraphElement, final int targetPosition, final String pastedHtml, final boolean pastedHTMLHasParagraphTags) { // A model for this method: SHTMLEditorPane.InsertLineBreakAction. final StringWriter sw = new StringWriter(); final SHTMLWriter w = new SHTMLWriter(sw, doc); String paragraphElementAdjustedName = paragraphElement.getName(); final boolean impliedParagraph = paragraphElementAdjustedName.equalsIgnoreCase("p-implied"); if (impliedParagraph) { paragraphElementAdjustedName = "p"; } try { final int count = paragraphElement.getElementCount(); if (!impliedParagraph || pastedHTMLHasParagraphTags) { w.writeStartTag(paragraphElementAdjustedName, paragraphElement.getAttributes()); } for (int elementIdx = 0; elementIdx < count; elementIdx++) { final Element element = paragraphElement.getElement(elementIdx); if (element.equals(characterElement)) { // Why the following? //if(elementIdx > 0) // w.writeStartTag(paragraphElementAdjustedName, paragraphElement.getAttributes()); //Write first part of the splitted text. final SHTMLWriter htmlStartWriter = new SHTMLWriter(sw, doc, element.getStartOffset(), targetPosition - element.getStartOffset()); htmlStartWriter.write(element); if (pastedHTMLHasParagraphTags) { w.writeEndTag(paragraphElementAdjustedName); } //Write the pasted text. sw.write(pastedHtml); //Write the second part of the splited text. if (pastedHTMLHasParagraphTags) { w.writeStartTag(paragraphElementAdjustedName, paragraphElement.getAttributes()); } final SHTMLWriter htmlEndWriter = new SHTMLWriter(sw, doc, targetPosition, element.getEndOffset() - targetPosition); htmlEndWriter.write(element); // Why the following? //if(elementIdx > 0) // w.writeEndTag(paragraphElementAdjustedName); } else { w.write(element); } } if (!impliedParagraph || pastedHTMLHasParagraphTags) { w.writeEndTag(paragraphElementAdjustedName); } } catch (final Exception e) { e.printStackTrace(); } return sw.getBuffer().toString(); } /** * Copy the selected portion of an SHTMLEditorPane as styled text, * i.e. chunks of plain text strings with an AttributeSet associated * to each of them. * * @param src the SHTMLEditorPane to copy from */ private void copyStyledText(final SHTMLEditorPane src) throws BadLocationException { final Document doc = src.getDocument(); final int selStart = src.getSelectionStart(); final int selEnd = src.getSelectionEnd(); int eStart; int eEnd; final ElementIterator eli = new ElementIterator(doc); Element elem = eli.first(); while (elem != null) { eStart = elem.getStartOffset(); eEnd = elem.getEndOffset(); if (elem.getName().equalsIgnoreCase(AbstractDocument.ContentElementName)) { if ((eEnd >= selStart) && (eStart <= selEnd)) { clipAttr.addElement(elem.getAttributes()); if (eStart < selStart) { if (eEnd > selEnd) { // both ends of elem outside selection clipText.addElement(src.getText(selStart, selEnd - selStart)); } else { // only first part of elem outside selection clipText.addElement(src.getText(selStart, eEnd - selStart)); } } else if (eEnd > selEnd) { // only last part of elem outside selection clipText.addElement(src.getText(eStart, selEnd - eStart)); } else { // whole element inside selection clipText.addElement(src.getText(eStart, eEnd - eStart)); } } } elem = eli.next(); } } /** Gets the number of text chunks in this StyledText object. */ private int getClipTextSize() { return clipText.size(); } /** * get the attributes of a certain chunk of styled text * * @param chunkPos - the number of the chunk to get the attributes for * @return the attributes for respective character position */ private AttributeSet getCharacterAttributes(final int chunkNo) { return (AttributeSet) clipAttr.elementAt(chunkNo); } /** * get the characters of a certain chunk of styled text * * @param chunkNo - the number of the chunk to get the characters for * @return the characters for respective chunk as String */ private String getCharactersAt(final int chunkNo) { return (String) clipText.elementAt(chunkNo); } /** clear all styled text contents of this HTMLText object */ private void clearStyledText() { clipText.clear(); clipAttr.clear(); } /** * get a String containing all chunks of text contained in this object * * @return string of all chunks in this object */ public String toString() { final StringBuffer text = new StringBuffer(); if (stringRepresentation) { text.append(plainText); } else { int i; for (i = 0; i < clipText.size(); i++) { text.append((String) clipText.elementAt(i)); } } return text.toString(); } /** (See also isParagraphTag.) */ public static boolean containsParagraphTags(final String htmlText) { //An simplistic heuristic. Does not handle tags in comments, for instance. return htmlText .matches("(?ims).*<(blockquote|dir|div|dl|dt|frameset|h1|h2|h3|h4|h5|h6|hr|li|menu|ol|p|pre|table|td|th|tr|ul).*?>.*"); } /** Determines whether the text has a table with exactly one cell and one row. */ public boolean isOneCellInOneRow() { //return false; if (htmlText.matches("(?ims).*.*.*http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class ListPanel extends JPanel implements AttributeComponent { /** selector for list type */ private final AttributeComboBox listType; /** selector for list position */ private final AttributeComboBox listPosition; /** list indent selector */ private final BoundariesPanel bndPanel; /** list tag from setValue/getValue (UL or OL) */ private String listTag; /** * construct a new ListPanel */ public ListPanel() { setLayout(new BorderLayout()); // have a grid bag layout ready to use final GridBagLayout g = new GridBagLayout(); final GridBagConstraints c = new GridBagConstraints(); // build list format panel final JPanel formatPanel = new JPanel(g); // add label for list type Util.addGridBagComponent(formatPanel, new JLabel(Util.getResourceString("listTypeLabel")), g, c, 0, 0, GridBagConstraints.EAST); // add combo box for list type selection String[] items = new String[] { Util.getResourceString("listTypeNone"), Util.getResourceString("listTypeDecimal"), Util.getResourceString("listTypeLowerRoman"), Util.getResourceString("listTypeUpperRoman"), Util.getResourceString("listTypeLowerAlpha"), Util.getResourceString("listTypeUpperAlpha"), Util.getResourceString("listTypeDisc"), Util.getResourceString("listTypeCircle"), Util.getResourceString("listTypeSquare") }; String[] names = new String[] { "none", "decimal", "lower-roman", "upper-roman", "lower-alpha", "upper-alpha", "disc", "circle", "square" }; listType = new AttributeComboBox(items, names, CSS.Attribute.LIST_STYLE_TYPE, null); Util.addGridBagComponent(formatPanel, listType, g, c, 1, 0, GridBagConstraints.WEST); // add label for list position Util.addGridBagComponent(formatPanel, new JLabel(Util.getResourceString("listPositionLabel")), g, c, 0, 1, GridBagConstraints.EAST); // add combo box for list postion selection items = new String[] { Util.getResourceString("listPosInside"), Util.getResourceString("listPosOutside") }; names = new String[] { "inside", "outside" }; listPosition = new AttributeComboBox(items, names, CSS.Attribute.LIST_STYLE_POSITION, null); Util.addGridBagComponent(formatPanel, listPosition, g, c, 1, 1, GridBagConstraints.WEST); // create list boundaries panel bndPanel = new BoundariesPanel(CSS.Attribute.MARGIN); bndPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("listIndentTitle"))); // add components to this ListPanel add(formatPanel, BorderLayout.CENTER); add(bndPanel, BorderLayout.SOUTH); } /** * get the list tag currently selected in this ListPanel * * @return the list tag currently selected or null, if no list is selected */ public String getListTag() { return listTag; } /** * translate list types as per CSS attribute list-style-type into list * tag (UL or OL). */ private void setTagFromType() { final int index = listType.getSelectedIndex(); if (index > 5) { listTag = HTML.Tag.UL.toString(); } else if (index > 0) { listTag = HTML.Tag.OL.toString(); } else { listTag = null; } } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { final Object name = a.getAttribute(javax.swing.text.StyleConstants.NameAttribute); if (name != null) { listTag = name.toString(); } listType.setValue(a); listPosition.setValue(a); bndPanel.setValue(a); return true; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { setTagFromType(); final SimpleAttributeSet set = new SimpleAttributeSet(); set.addAttributes(listType.getValue()); set.addAttributes(listPosition.getValue()); set.addAttributes(bndPanel.getValue()); return set; } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { setTagFromType(); final SimpleAttributeSet set = new SimpleAttributeSet(); set.addAttributes(listType.getValue(includeUnchanged)); set.addAttributes(listPosition.getValue(includeUnchanged)); set.addAttributes(bndPanel.getValue(includeUnchanged)); return set; } else { return getValue(); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/NamedObject.java0100644 0000000 0000000 00000003531 12114157751 022465 0ustar000000000 0000000 /*FreeMind - A Program for creating and viewing Mindmaps *Copyright (C) 2000-2006 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitri Polivaev and others. * *See COPYING for Details * *This program is free software; you can redistribute it and/or *modify it under the terms of the GNU General Public License *as published by the Free Software Foundation; either version 2 *of the License, or (at your option) any later version. * *This program is distributed in the hope that it will be useful, *but WITHOUT ANY WARRANTY; without even the implied warranty of *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *GNU General Public License for more details. * *You should have received a copy of the GNU General Public License *along with this program; if not, write to the Free Software *Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Created on 08.05.2005 * */ package com.lightdev.app.shtm; /** * Utility Class for displaying local object names in GUI components. * * @author Dimitri Polivaev * 18.01.2007 */ public class NamedObject { private String name; private Object object; private NamedObject() { } public NamedObject(final Object object, final String name) { this.object = object; this.name = name; } static public NamedObject literal(final String literal) { final NamedObject result = new NamedObject(); result.object = literal; result.name = literal; return result; } public boolean equals(final Object o) { if (o instanceof NamedObject) { final NamedObject ts = (NamedObject) o; return object.equals(ts.object); } return object.equals(o); } public String toString() { return name; } public Object getObject() { return object; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/FrmMain.java0100644 0000000 0000000 00000010343 13147564753 021655 0ustar000000000 0000000 /* /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Toolkit; import java.awt.event.WindowEvent; import java.lang.reflect.InvocationTargetException; import javax.swing.JFrame; /** * Main window of application SimplyHTML. * *

This class constructs the main panel and all of its GUI elements * such as menus, etc.

* *

It defines a set of inner classes creating actions which can be * connected to menus, buttons or instantiated individually.

* * @author Ulrich Hilger * @author Dimitri Polivaev * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class FrmMain extends JFrame { public static final String APP_NAME = "SimplyHTML"; public static final String VERSION = "0.17.3"; /** static reference to this instance of class FrmMain */ private SHTMLPanelImpl mainPane; private FrmMain() { SHTMLPanelImpl.setInternalUiResources(); setIconImage(Toolkit.getDefaultToolkit().createImage( DynamicResource.getResource(SHTMLPanelImpl.getUiResources(), "appIcon"))); setTitle(APP_NAME); } private void start() { SplashScreen.showInstance(); try { EventQueue.invokeAndWait(new Runnable() { public void run() { mainPane = new SHTMLPanelMultipleDocImpl(); // mainPane = new SHTMLPanelSingleDocImpl(); mainPane.setContentPanePreferredSize(new Dimension(1024, 500)); getContentPane().add(mainPane); pack(); final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); final Dimension frameSize = getSize(); if (frameSize.height > screenSize.height) { frameSize.height = screenSize.height; } if (frameSize.width > screenSize.width) { frameSize.width = screenSize.width; } //Center the window setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2); setVisible(true); // show the window getSHTMLPanel().getMostRecentFocusOwner().requestFocus(); } }); } catch (final InterruptedException e) { e.printStackTrace(); } catch (final InvocationTargetException e) { e.printStackTrace(); } SplashScreen.hideInstance(); } /** * catch requests to close the application's main frame to * ensure proper clean up before the application is * actually terminated. */ protected void processWindowEvent(final WindowEvent e) { if (!(e.getID() == WindowEvent.WINDOW_CLOSING) || mainPane.close()) { super.processWindowEvent(e); } } protected SHTMLPanel getSHTMLPanel() { return mainPane; } static void run() { final FrmMain frmMain = new FrmMain(); frmMain.start(); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLEditorKit.java0100644 0000000 0000000 00000026621 13147564753 023040 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Cursor; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.LabelView; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; import javax.swing.text.View; import javax.swing.text.ViewFactory; import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.MinimalHTMLWriter; import javax.swing.text.html.StyleSheet; /** * Extensions to HTMLEditorKit for application SimplyHTML. * *

In stage 1 this only re-implements how style sheets are handled by * default.

* *

Stage 3 adds functionality for usage of the custom HTML document * and HTML reader used with SimplyHTML from this stage on.

* *

With stage 9 some additional views have been added to * the view factory as a workaround for bug id 4765271 * (see http://developer.java.sun.com/developer/bugParade/bugs/4765271.html).

* *

OK, I give up: With release 2 of stage 9 above views are used no longer and * bug fixing is not done anymore. The HTML support is almost taken as is in the hope * that Sun will enhance it some day. To do compensation inside a single application * is not possible with a reasonable effort.

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ public class SHTMLEditorKit extends HTMLEditorKit { SHTMLEditorKit() { super(); final Cursor textCursor = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); setDefaultCursor(textCursor); } /* --------------- SHTMLDocument implementation start ------------ */ /** * Create an uninitialized text storage model * that is appropriate for this type of editor. * * @return the model */ public Document createDefaultDocument() { final SHTMLDocument doc = (SHTMLDocument) createEmptyDocument(); try { final String standardContent; if (Util.preferenceIsTrue("gray_row_below_end")) { standardContent = "

\n

\n

\n" + SHTMLDocument.SUFFIX + "\n

\n"; } else { standardContent = "

\n

\n

\n" + SHTMLDocument.SUFFIX + "\n

\n"; } doc.setOuterHTML(doc.getParagraphElement(doc.getLength()), standardContent); } catch (final BadLocationException e) { e.printStackTrace(); } catch (final IOException e) { e.printStackTrace(); } return doc; } Document createEmptyDocument() { getStyleSheet(); final StyleSheet ss = new ScaledStyleSheet(); try { ss.importStyleSheet(Class.forName("javax.swing.text.html.HTMLEditorKit").getResource(DEFAULT_CSS)); } catch (final Exception e) { } final SHTMLDocument doc = new SHTMLDocument(ss); doc.setParser(getParser()); doc.setAsynchronousLoadPriority(-1); doc.setTokenThreshold(1); return doc; } /** * Inserts content from the given stream. If doc is * an instance of HTMLDocument, this will read * HTML 3.2 text. Inserting HTML into a non-empty document must be inside * the body Element, if you do not insert into the body an exception will * be thrown. When inserting into a non-empty document all tags outside * of the body (head, title) will be dropped. * * @param in the stream to read from * @param doc the destination for the insertion * @param pos the location in the document to place the * content * @exception IOException on any I/O error * @exception BadLocationException if pos represents an invalid * location within the document * @exception RuntimeException (will eventually be a BadLocationException) * if pos is invalid */ public void read(final Reader in, final Document doc, final int pos) throws IOException, BadLocationException { if (doc instanceof SHTMLDocument) { final SHTMLDocument hdoc = (SHTMLDocument) doc; final Parser parser = getParser(); if (parser == null) { throw new IOException("Can't load parser"); } if (pos > doc.getLength()) { throw new BadLocationException("Invalid location", pos); } final ParserCallback receiver = hdoc.getReader(pos); if (doc.getLength() == 0) { final Boolean ignoreCharset = (Boolean) doc.getProperty("IgnoreCharsetDirective"); parser.parse(in, receiver, (ignoreCharset == null) ? false : ignoreCharset.booleanValue()); } else { parser.parse(in, receiver, true); } receiver.flush(); } else { super.read(in, doc, pos); } } /** * Write content from a document to the given stream * in a format appropriate for this kind of content handler. * * @param out the stream to write to * @param doc the source for the write * @param pos the location in the document to fetch the * content * @param len the amount to write out * @exception IOException on any I/O error * @exception BadLocationException if pos represents an invalid * location within the document */ public void write(final Writer out, final Document doc, final int pos, final int len) throws IOException, BadLocationException { if (doc instanceof SHTMLDocument) { try { final SHTMLWriter w = new SHTMLWriter(out, (SHTMLDocument) doc, pos, len); w.write(); } catch (final Exception e) { e.printStackTrace(); } } else if (doc instanceof StyledDocument) { final MinimalHTMLWriter w = new MinimalHTMLWriter(out, (StyledDocument) doc, pos, len); w.write(); } else { super.write(out, doc, pos, len); } } /* --------------- SHTMLDocument implementaion end --------------- */ void updateInputAttributes(final SHTMLEditorPane e) { // EditorKit might not have installed the StyledDocument yet. final Document aDoc = e.getDocument(); if (!(aDoc instanceof StyledDocument)) { return; } final int start = e.getSelectionStart(); // record current character attributes. final StyledDocument doc = (StyledDocument) aDoc; // If nothing is selected, get the attributes from the character // before the start of the selection, otherwise get the attributes // from the character element at the start of the selection. Element run; final Element currentParagraph = doc.getParagraphElement(start); if (currentParagraph.getStartOffset() == start || start != e.getSelectionEnd()) { // Get the attributes from the character at the selection // if in a different paragrah! run = doc.getCharacterElement(start); } else { run = doc.getCharacterElement(Math.max(start - 1, 0)); } createInputAttributes(run, getInputAttributes()); } /* --------------- ViewFactory implementation start -------------- */ /** Shared factory for creating HTML Views. */ private static final ViewFactory defaultFactory = new SHTMLFactory(); /** * Fetch a factory that is suitable for producing * views of any models that are produced by this * kit. * * @return the factory */ public ViewFactory getViewFactory() { return defaultFactory; } static public void removeCharacterAttributes(final StyledDocument doc, final Object attributeName, final int start, final int length) { // clear all character attributes in selection final int end = start + length; SimpleAttributeSet sasText = null; for (int i = start; i < end;) { final Element characterElement = doc.getCharacterElement(i); sasText = new SimpleAttributeSet(characterElement.getAttributes().copyAttributes()); final int endOffset = characterElement.getEndOffset(); final ArrayList attributeNames = Collections.list(sasText.getAttributeNames()); for (final Object entryKey : attributeNames) { if (attributeName != null && entryKey.equals(attributeName) || attributeName == null && !entryKey.equals(StyleConstants.NameAttribute)) { sasText.removeAttribute(entryKey); } } final int last = end < endOffset ? end : endOffset; try { doc.setCharacterAttributes(i, last - i, sasText, true); } catch (final Exception e) { } i = i < last ? last : i + 1; } } public static class SHTMLFactory extends HTMLEditorKit.HTMLFactory implements ViewFactory { public View create(final Element elem) { View view = null; final Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute); if (o instanceof HTML.Tag) { final HTML.Tag kind = (HTML.Tag) o; //System.out.println("SHTMLEditorKit.SHTMLFactory o is HTML.Tag kind=" + kind.toString()); if (kind == HTML.Tag.TABLE) { view = super.create(elem); } else if (kind == HTML.Tag.COMMENT) { view = new InvisibleView(elem); } else if (kind instanceof HTML.UnknownTag) { view = new InvisibleView(elem); } else { view = super.create(elem); } } else { view = new LabelView(elem); } return view; } } /* --------------- ViewFactory implementation end -------------- */ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/TagSelector.java0100644 0000000 0000000 00000006757 12114157751 022543 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.util.Vector; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.text.html.HTML; /** * Component to select a tag, extending JComboBox. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class TagSelector extends JComboBox { /** table with available tags to select */ private final Vector tags = new Vector(); /** table with tag names corresponding to tags */ private final Vector tagNames = new Vector(); /** * construct a new TagSelector */ public TagSelector() { super(); initTags(); setModel(new DefaultComboBoxModel(tagNames)); } /** * Gets the name of the tag that is currently selected. * * @return the tag name */ public String getSelectedTag() { return (String) tags.elementAt(getSelectedIndex()); } /** * get the list of tags selectable through this component * * @return a Vector of tags available from this component */ public Vector getTags() { return tags; } /** * set the tag that is to be shown in this component * * @param tag the name of the tag to show */ public void setSelectedTag(final String tag) { final int index = tags.indexOf(tag); if (index > -1) { setSelectedIndex(tags.indexOf(tag)); } else { setSelectedIndex(0); } } /** * initialize content types hashtable */ private void initTags() { tags.addElement(HTML.Tag.P.toString()); tags.addElement(HTML.Tag.H1.toString()); tags.addElement(HTML.Tag.H2.toString()); tags.addElement(HTML.Tag.H3.toString()); tags.addElement(HTML.Tag.H4.toString()); tags.addElement(HTML.Tag.H5.toString()); tags.addElement(HTML.Tag.H6.toString()); tagNames.addElement(Util.getResourceString("cTagNamePara")); tagNames.addElement(Util.getResourceString("cTagNameHead1")); tagNames.addElement(Util.getResourceString("cTagNameHead2")); tagNames.addElement(Util.getResourceString("cTagNameHead3")); tagNames.addElement(Util.getResourceString("cTagNameHead4")); tagNames.addElement(Util.getResourceString("cTagNameHead5")); tagNames.addElement(Util.getResourceString("cTagNameHead6")); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/AbstractPlugin.java0100644 0000000 0000000 00000031655 13147564753 023257 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.prefs.Preferences; import javax.swing.Action; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JMenuItem; /** * Base class for plug-ins of application SimplyHTML. * *

Defines some common methods for reuse in plug-ins. All * settings such as dockLocation or activation state * of a plug-in are stored persistently in a preferences file * with the help of class Prefs. The preferences * file is valid for the current user, so each user has own * plug-in settings.

* *

Menus are constructed with the help of class * DynamicResource. This class needs menu definitions * accessible in a .properties file as described in the API docs * of DynamicResource. I.e., methods of class * AbstractPlugin only work as defined herein when * accompanied by such .properties file accordingly.

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * * * @see com.lightdev.app.shtm.DynamicResource */ public abstract class AbstractPlugin implements SHTMLPlugin { /** * construct an AbstractPlugin * *

Constructor may not have parameters so that * java.lang.Class.newInstance can be used on it.

*/ public AbstractPlugin() { //System.out.println("AbstractPlugin constructor"); /*SecurityManager security = System.getSecurityManager(); if (security != null) { security. } */ prefs = Preferences.userNodeForPackage(getClass()); } /** * init the plug-in * * this is called by the PluginManager directly after instantiating the plug-in */ public void initPlugin(final SHTMLPanelImpl owner) { this.owner = owner; } /** * create the plug-in menu */ protected void createPluginMenu() { if (pluginMenuId != null) { pMenu = owner.dynRes.createMenu(AbstractPlugin.textResources, pluginMenuId); } } /** * create the help menu */ protected void createHelpMenu() { if (helpMenuId != null) { hMenu = owner.dynRes.createMenu(AbstractPlugin.textResources, helpMenuId); initHelpMenu(); } } public void initHelpMenu() { } /** * create a frame for the component of this plug-in, if it * has a JComponent to display. */ protected void createFrame() { if (c != null) { frame = new JFrame(getGUIName()); frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); frame.setSize(300, 400); frame.getContentPane().add(c); } frame.pack(); } /** * create, show or hide frame as needed, depending * on a given dock location * * @param location the dock location of the plug-in, one of * DOCK_LOCATION_TOP, DOCK_LOCATION_BOTTOM, * DOCK_LOCATION.LEFT, DOCK_LOCATION_RIGHT or DOCK_LOCATION_NONE, if the * component shall not dock. */ protected void createFrameAsNeeded(final int location) { if (location == SHTMLPlugin.DOCK_LOCATION_NONE) { if ((frame == null) && (c != null)) { createFrame(); } if (frame != null) { frame.setVisible(true); } } else { if (frame != null) { frame.setVisible(false); } } } /* ----------- SimplyHTML plugin interface implementation start --------- */ /** * initialize the plugin * * @param owner the owner of this plug-in * @param internalName the internal name this plug-in shall have * @param pluginMenuId the id of the plug-in menu in the TextResources, * or null if no plugin-in menu is to be created * @param helpMenuId the id of the help menu for this plug-in in the * TextResources, or null if no help menu is to be created */ public void initPlugin(final SHTMLPanel owner, final String internalName, final String pluginMenuId, final String helpMenuId) { this.owner = (SHTMLPanelImpl) owner; this.internalName = internalName; this.pluginMenuId = pluginMenuId; this.helpMenuId = helpMenuId; try { //System.out.println("AbstractPlugin this.getClass.getName=" + this.getClass().getName()); if (SHTMLPanelImpl.pluginManager != null) { final ClassLoader plLoader = SHTMLPanelImpl.pluginManager.getPluginLoader(); if (plLoader != null) { final ResourceBundle resourceBundle = ResourceBundle.getBundle(this.getClass().getName(), Locale.getDefault(), plLoader); textResources = new InternalUiResources(resourceBundle); //System.out.println("AbstractPlugin plLoader != null, resources=" + resources); } else { final InternalUiResources instance = new InternalUiResources(ResourceBundle.getBundle(this .getClass().getName(), Locale.getDefault())); textResources = instance; //System.out.println("AbstractPlugin plLoader == null, resources=" + resources); } } else { final InternalUiResources instance = new InternalUiResources(ResourceBundle.getBundle(this.getClass() .getName(), Locale.getDefault())); textResources = instance; //System.out.println("AbstractPlugin pluginManager = null, resources=" + resources); } active = prefs.getBoolean(internalName + PREFSID_PLUGIN_ACTIVE, true); dockLocation = prefs.getInt(internalName + PREFSID_PLUGIN_DOCK_LOCATION, SHTMLPlugin.DOCK_LOCATION_LEFT); createFrameAsNeeded(dockLocation); } catch (final MissingResourceException mre) { Util.errMsg(null, this.getClass().getName() + ".properties not found", mre); } } /** * set the owner of this plug-in * * @param owner the main frame of the instance of SimplyHTML creating the plug-in */ public void setOwner(final SHTMLPanelImpl owner) { this.owner = owner; } /** * get the owner of this plug-in * * @return the owner of this plug-in */ public SHTMLPanelImpl getOwner() { return owner; } /** * set status of plug-in and persistently store setting in * a preferences file * * @param isActive indicates whether or not the plug-in shall be activated */ public void setStatus(final boolean isActive) { active = isActive; prefs.putBoolean(getInternalName() + PREFSID_PLUGIN_ACTIVE, isActive); try { prefs.flush(); } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } } /** * set the location the component returned by getDockComponent() * shall be docked at. Persistently store setting in * a preferences file. * * @param location the dock location, one of DOCK_LOCATION_TOP, DOCK_LOCATION_BOTTOM, * DOCK_LOCATION.LEFT, DOCK_LOCATION_RIGHT or DOCK_LOCATION_NONE, if the * component shall not dock. */ public void setDockLocation(final int location) { dockLocation = location; prefs.putInt(getInternalName() + PREFSID_PLUGIN_DOCK_LOCATION, location); try { prefs.flush(); } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } createFrameAsNeeded(location); } /** * get a menu of actions this plug-in provides. * *

JMenu is a decendant of JMenuItem * so this method may return a single menu item up to a whole * structure of submenus in its return value.

* * @return the plug-in menu */ public JMenuItem getPluginMenu() { return pMenu; } /** * get a menu item providing documentation about this * plug-in. * *

JMenu is a decendant of JMenuItem * so this method may return a single menu item up to a whole * structure of submenus in its return value.

* * @return a menu item with help for this plug-in */ public JMenuItem getHelpMenu() { return hMenu; } /** * get the name of the plug-in as it shall appear * on a GUI. * * @return the name of the plug-in */ public String getGUIName() { return "AbstractPlugin"; } /** * get the name used internally for this plug-in * * @return the internal name of this plug-in */ public String getInternalName() { return internalName; } /** * get the location the component returned by getDockComponent() * shall be docked at. * * @return the dock location, one of DOCK_LOCATION_TOP, DOCK_LOCATION_BOTTOM, * DOCK_LOCATION.LEFT, DOCK_LOCATION_RIGHT or DOCK_LOCATION_NONE, if the * component shall not dock. */ public int getDockLocation() { return dockLocation; } /** * get the component that this plug-in produces, if any * * @return the component produced by this plug-in, or null if none * is produced */ public JComponent getComponent() { return c; } /** * get the status of the plug-in * * @return true, if activated, false if not */ public boolean isActive() { return active; } /** * get a string from the resource bundle of the owner of this plug-in * * @param nm the name of the string resource to get * * @return the string with the given name or null, if none is found */ public String getOwnerResString(final String nm) { return Util.getResourceString(SHTMLPanelImpl.getUiResources(), nm); } /** * get an action from the resource bundle of the owner of this plug-in * * @param cmd the name of the action to get * * @return the action with the given name or null, if none is found */ public Action getOwnerAction(final String cmd) { return owner.getDynRes().getAction(cmd); } /* ----------- SimplyHTML plugin interface implementation end --------- */ /* --------------- class fields start --------------------- */ /** TextResources of plug-in */ public static UIResources textResources; /** constant for active setting in preferences file */ public static final String PREFSID_PLUGIN_ACTIVE = "Active"; /** constant for dock location setting in preferences file */ public static final String PREFSID_PLUGIN_DOCK_LOCATION = "DockLocation"; /** the internal name of this plug-in */ protected String internalName; /** the id in the ResourceFile for the menu of this plug-in */ protected String pluginMenuId; /** the id in the ResourceFile for the help menu of this plug-in */ protected String helpMenuId; /** the plug-in menu provided by this plug-in */ protected JMenuItem pMenu = null; /** the help menu provided by this plug-in */ protected JMenuItem hMenu = null; /** status of plug-in */ protected boolean active = true; /** current dock location */ protected int dockLocation = SHTMLPlugin.DOCK_LOCATION_LEFT; /** component of this plug-in */ protected JComponent c = null; /** JFrame for dockLocation=none */ protected JFrame frame = null; /** reference for user preferences for this class */ protected Preferences prefs; /** the owner of this plug in */ protected SHTMLPanelImpl owner; /* ------------- class fields end ------------------ */ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/InternalUiResources.java0100644 0000000 0000000 00000004361 13147564753 024274 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Created on 23.11.2006 * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.net.URL; import java.util.MissingResourceException; import java.util.Properties; import java.util.ResourceBundle; import javax.swing.Icon; import javax.swing.ImageIcon; /** * Default implementation of TextResources based on java.util.ResourceBundle * * @author Dimitri Polivaev * 14.01.2007 */ public class InternalUiResources implements UIResources { private final Properties properties; private final ResourceBundle resources; public InternalUiResources(final ResourceBundle languageResources) { this(languageResources, null); } public InternalUiResources(final ResourceBundle languageResources, final Properties properties) { super(); resources = languageResources; this.properties = properties; } public String getString(final String key) { try { return resources.getString(key); } catch (final MissingResourceException ex) { if (properties != null) { return properties.getProperty(key); } System.err.println("SimplyHTML : Warning : resource is missing: " + key); return key; } } @Override public Icon getIcon(String name) { final URL url = DynamicResource.getResource(this, name); if (url != null) { return new ImageIcon(url); } else return null; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/AttributePanel.java0100644 0000000 0000000 00000010600 12114157751 023230 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; import java.util.Enumeration; import java.util.Vector; import javax.swing.JPanel; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; /** * Panel set a group of attributes. * *

Abstract base class for other panels such as margin or style panel.

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ abstract class AttributePanel extends JPanel implements AttributeComponent, ContainerListener { /** container for all AttributeComponents shown on this AttributePanel */ private final Vector components = new Vector(); /** * construct a new AttributePanel */ public AttributePanel() { super(); this.addContainerListener(this); } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { /* System.out.println("AttributePanel setValue"); de.calcom.cclib.html.HTMLDiag hd = new de.calcom.cclib.html.HTMLDiag(); hd.listAttributes(a, 4); System.out.println("\r\n"); */ boolean result = true; final Enumeration elements = components.elements(); AttributeComponent ac; while (elements.hasMoreElements()) { ac = (AttributeComponent) elements.nextElement(); if (!ac.setValue(a)) { result = false; } } return result; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet attributes = new SimpleAttributeSet(); final Enumeration elements = components.elements(); AttributeComponent ac; while (elements.hasMoreElements()) { ac = (AttributeComponent) elements.nextElement(); attributes.addAttributes(ac.getValue()); } return attributes; } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { final SimpleAttributeSet attributes = new SimpleAttributeSet(); final Enumeration elements = components.elements(); AttributeComponent ac; while (elements.hasMoreElements()) { ac = (AttributeComponent) elements.nextElement(); attributes.addAttributes(ac.getValue(includeUnchanged)); } return attributes; } else { return getValue(); } } public void componentAdded(final ContainerEvent e) { final Object component = e.getChild(); if (component instanceof AttributeComponent) { components.add(component); } } public void componentRemoved(final ContainerEvent e) { final Object component = e.getChild(); if (component instanceof AttributeComponent) { components.remove(component); } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLPrefsChangeListener.java0100644 0000000 0000000 00000000235 12114157751 025013 0ustar000000000 0000000 package com.lightdev.app.shtm; public interface SHTMLPrefsChangeListener { void shtmlPrefChanged(String propertyName, String newValue, String oldValue); } simplyhtml-0.17.3/src/com/lightdev/app/shtm/DynamicResource.java0100644 0000000 0000000 00000043036 13147564753 023425 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Component; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import java.util.Enumeration; import java.util.Hashtable; import javax.swing.AbstractAction; import javax.swing.AbstractButton; import javax.swing.Action; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import org.dpolivaev.mnemonicsetter.MnemonicSetter; /** * Provides methods to dynamically combine components and resource bundles. * *

The actions, menus and menuitems created by this object are * stored privately inside this object. This allows for later * access to one of the stored items through the action command name.

* *

IMPORTANT: Action command names must be unique, if actions * or menus are added to an instance of this class and if the actions or * menus are defined in different TextResourcess, the action names * must be unique over all TextResourcess involved, because the action * names are used as identifiers for connection of actions to compnents * such as menus and menu items.

* *

Component creation methods such as createMenu or createMenuItem * expect definitions coming from a TextResources, typically a * text file ending with '.properties'.

* *

* Inside the .properties file, a menu definition is looking similar to *

 *   # plugin menu definition
 *   plugin=test1 test2 test3
 *   pluginLabel=Test Plug-In
 *
 *   # plugin menu items
 *   test1Label=Test 1
 *   test1Image=images/test1.gif
 *   test1Tip=test menu item 1
 *   test2Label=Test 2
 *   test3Label=Test 3
 * 
*

* *

* The calling class has to define actions named accordingly, e.g. *

 *    DynamicResource dynRes = new DynamicResource("com.foo.bar.myPlugin");
 *    dynRes.addAction("test1", new MyAction("test1");
 *    dynRes.addAction("test2", new MyAction("test2");
 *    dynRes.addAction("test3", new MyAction("test3");
 * 
*

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class DynamicResource { /** name constant for labels in the resource file */ public static final String labelSuffix = "Label"; /** name constant for accelerators in the resource file */ public static final String acceleratorSuffix = "Accelerator"; /** name constant for indicating image resources in the resource file */ public static final String imageSuffix = "Image"; /** name constant for tool tip strings in the resource file */ public static final String toolTipSuffix = "Tip"; /** indicator for menu separators */ public static final String menuSeparatorKey = "-"; /** dynamic storage for menu items */ private final Hashtable menuItems = new Hashtable(); /** dynamic storage for actions */ private final Hashtable commands = new Hashtable(); /** dynamic storage for menus */ private final Hashtable menus = new Hashtable(); public static final String IMAGE_EMPTY = "empty.gif"; /** * construct a new DynamicResource. */ public DynamicResource() { } /** * add an action to this DynamicResource * * @param cmd the internal identifier for the action * @param action the action to associate with actionCommand */ public void addAction(final String cmd, final Action action) { commands.put(cmd, action); } /** * Create a menu bar. This reads the * definition of the menu from the associated resource file. * * @param resources the TextResources to get the menu definition from * @param name name of the menu bar definition * * @return the created menu bar */ public SHTMLMenuBar createMenubar(final UIResources resources, final String name) { final SHTMLMenuBar mb = new SHTMLMenuBar(); final String[] menuKeys = Util.tokenize(Util.getResourceString(resources, name), " "); for (int i = 0; i < menuKeys.length; i++) { final JMenu m = createMenu(resources, menuKeys[i]); if (m != null) { mb.add(m); } } MnemonicSetter.INSTANCE.setComponentMnemonics(mb); return mb; } /** * Create a menu for the app. This reads the * definition of the menu from the associated resource file. * * @param resources the TextResources to get the menu definition from * @param key the key of the menu definition in the resource file * @return the created menu */ public JMenu createMenu(final UIResources resources, final String key) { JMenu menu = null; String def = Util.getResourceString(resources, key); if (def == null) { def = ""; } final String[] itemKeys = Util.tokenize(def, " "); menu = new JMenu(Util.getResourceString(resources, key + labelSuffix)); for (int i = 0; i < itemKeys.length; i++) { if (itemKeys[i].equals(menuSeparatorKey)) { menu.addSeparator(); } else { final JMenuItem mi = createMenuItem(resources, itemKeys[i]); menu.add(mi); } } menu.addMenuListener(new DynamicMenuListener()); /** * store the menu in the menus hashtable for possible later use */ menus.put(key, menu); MnemonicSetter.INSTANCE.setComponentMnemonics(menu.getPopupMenu()); return menu; } /** * Create a menu for the app. This reads the * definition of the menu from the associated resource file. * * @param resources the TextResources to get the menu definition from * @param key the key of the menu definition in the resource file * @return the created menu */ public JPopupMenu createPopupMenu(final UIResources resources, final String key) { JPopupMenu menu = null; String def = Util.getResourceString(resources, key); if (def == null) { def = ""; } final String[] itemKeys = Util.tokenize(def, " "); menu = new JPopupMenu(); for (int i = 0; i < itemKeys.length; i++) { if (itemKeys[i].equals(menuSeparatorKey)) { menu.addSeparator(); } else { final JMenuItem mi = createMenuItem(resources, itemKeys[i]); menu.add(mi); } } MnemonicSetter.INSTANCE.setComponentMnemonics(menu); return menu; } public JMenu getMenu(final String cmd) { return (JMenu) menus.get(cmd); } /** * create a menu item * * @param resources the TextResources to get the item definition from * @param cmd the action command to be associated * with the new menu item * @return the created menu item */ public JMenuItem createMenuItem(final UIResources resources, final String cmd) { /** * create a new menu item with the appropriate label from the * resource file. This label later is set from the action this * menu item is associated to (see below). */ JMenuItem mi; mi = new JMenuItem(); mi.setActionCommand(cmd); /** * connect action and menu item with appropriate listeners */ final Action a = getAction(cmd); if (a != null) { final Object aKey = a.getValue(AbstractAction.ACCELERATOR_KEY); if (aKey != null) { mi.setAccelerator((KeyStroke) aKey); } /** * add Action 'a' as the listener to action events * fired from this menu, i.e. execute action 'a' with * menu item 'mi' */ mi.addActionListener(a); /** * cause an instance of inner class ActionChangeListener * to listen to property changes of Action 'a' and to apply * changed properties of Action 'a' to menu item 'mi' */ a.addPropertyChangeListener(createActionChangeListener(mi)); /** * if the action has an image, * associate it with the menu item */ final Icon icon = (Icon) a.getValue(Action.SMALL_ICON); if (icon != null) { mi.setHorizontalTextPosition(JButton.RIGHT); mi.setIcon(icon); } String name = (String) a.getValue(Action.NAME); if(name == null) name = Util.getResourceString(resources, cmd + labelSuffix); mi.setText(name); /** * initially set the enabled state of the menu item * according to its action's enabled state */ mi.setEnabled(a.isEnabled()); } else { mi.setText(Util.getResourceString(resources, cmd + labelSuffix)); mi.setEnabled(false); } /** * store the menu item in the menuItems hashtable for possible later use */ menuItems.put(cmd, mi); return mi; } /** * get a string from the resources file * * @param resources the TextResources to get the string from * @param nm the key of the string * @return the string for the given key or null if not found */ static public String getResourceString(final UIResources resources, final String key) { //System.out.println("getResourceString nm=" + nm); if (resources != null) { return resources.getString(key); } System.err.println("SimplyHTML : Warning : resources are null."); new Throwable("Dummy").printStackTrace(); return key; } /** * listen to menu select events for proper updating of menu items * * whenever a menu is selected, its menu items are iterated and the * update method of the item's action is called causing * the menu item to reflect the correct enabled state. * * As each menu item is connected with a PropertyChangeListener * listening to property changes on it'Saction, the menu item is * updated by the PropertyChangeListener whenever the enabledState * of the action changes. */ private class DynamicMenuListener implements MenuListener { public DynamicMenuListener() { } public void menuSelected(final MenuEvent e) { final Component[] items = ((JMenu) e.getSource()).getMenuComponents(); Action action; for (int i = 0; i < items.length; i++) { if (items[i] instanceof JPopupMenu.Separator) { } else if (items[i] instanceof JMenuItem) { action = getAction(((JMenuItem) items[i]).getActionCommand()); if (action instanceof SHTMLAction) { ((SHTMLAction) action).update(); } } } } public void menuDeselected(final MenuEvent e) { } public void menuCanceled(final MenuEvent e) { } } /** * get an action from the commands table * * @param cmd the name of the action the get * @return the action found for the given name */ public Action getAction(final String cmd) { return (Action) commands.get(cmd); } /** * get all actions registered in this DynamicResource * * @return all actions */ public Enumeration getActions() { return commands.elements(); } /** create our PropertyChangeListener implementation */ private PropertyChangeListener createActionChangeListener(final AbstractButton b) { return new ActionChangedListener(b); } /** * associate a menu item to an action. * * When registering this * action listener with an action, it gets informed by * property changes of that particular action. * * By passing a menu item to the constructor of ActionChangedListener, * an instance of ActionChangedListener 'remembers' the menu item * its property are associated to. */ private class ActionChangedListener implements PropertyChangeListener { AbstractButton menuItem; ActionChangedListener(final AbstractButton mi) { super(); menuItem = mi; } public void propertyChange(final PropertyChangeEvent e) { final String propertyName = e.getPropertyName(); if (e.getPropertyName().equals(Action.NAME)) { final String text = (String) e.getNewValue(); menuItem.setText(text); } else if (propertyName.equals("enabled")) { final Boolean enabledState = (Boolean) e.getNewValue(); menuItem.setEnabled(enabledState.booleanValue()); } } } /** * get the menu item that was created for the given * command. * * @param cmd name of the action. * @return item created for the given command or null * if one wasn't created. */ public JMenuItem getMenuItem(final String cmd) { return (JMenuItem) menuItems.get(cmd); } /** * get the icon for a given command. * *

If the resource bundle has a reference to an icon for the * given commamd, an icon is created for the respective image resource. * otherwise, null is returned.

* * @param resources the TextResources to get the icon from * @param cmd the command an icon is requested for * * @return the icon for that command or null, if none is present * for this command */ static public Icon getIconForCommand(final UIResources resources, final String cmd) { return DynamicResource.getIconForName(resources, cmd + imageSuffix); } static public Icon getIconForName(final UIResources resources, final String name) { return resources != null ? resources.getIcon(name) : null; } /** * get the location of a resource. * *

Resources such as images are delivered with the application in * the path containing the application's classes. The resources file * coming with SimplyHTML has a key for every resource pointing to * the subdirectory relative to the class path.

* * @param resources the TextResources to get the resource from * @param key the key of the resource in the resource file * @return the resource location as a URL */ static public URL getResource(final UIResources resources, final String key) { final String name = Util.getResourceString(resources, key); if (name != null/* && !name.endsWith(IMAGE_EMPTY)*/) { final URL url = DynamicResource.class.getResource(name); return url; } return null; } /** * Create a tool bar. This reads the definition of a tool bar * from the associated resource file. * * @param resources the TextResources to get the tool bar definition from * @param nm the name of the tool bar definition in the resource file * * @return the created tool bar */ public JToolBar createToolBar(final UIResources resources, final String nm) { Action action; AbstractButton newButton; final java.awt.Dimension buttonSize = new java.awt.Dimension(24, 24); new java.awt.Dimension(3, 20); JSeparator separator; final String[] itemKeys = Util.tokenize(Util.getResourceString(resources, nm), " "); final JToolBar toolBar = new JToolBar(); toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE); for (int i = 0; i < itemKeys.length; i++) { /** special handling for separators */ if (itemKeys[i].equals(menuSeparatorKey)) { separator = new JSeparator(JSeparator.VERTICAL); toolBar.add(separator); } else { action = getAction(itemKeys[i]); newButton = toolBar.add(action); newButton.setMinimumSize(buttonSize); newButton.setPreferredSize(buttonSize); newButton.setMaximumSize(buttonSize); newButton.setFocusPainted(false); } } return toolBar; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLHelpBroker.java0100644 0000000 0000000 00000005455 13147564753 023201 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Created on 16.12.2006 * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.event.KeyEvent; import java.net.URL; import javax.help.CSH; import javax.help.HelpBroker; import javax.help.HelpSet; import javax.help.HelpSetException; import javax.swing.AbstractButton; import javax.swing.JButton; import javax.swing.JMenuItem; import javax.swing.KeyStroke; class SHTMLHelpBroker { public static final String APP_HELP_NAME = "help"; public static final String JAVA_HELP_EXT = ".hs"; private SHTMLHelpBroker() { } /** our help broker */ private static HelpBroker helpBroker; /** * get the HelpBroker of our application * * @return the HelpBroker to be used for help display */ private static HelpBroker getHelpBroker() { if (helpBroker == null) { final URL url = SHTMLPanelImpl.class.getResource(APP_HELP_NAME + Util.URL_SEPARATOR + APP_HELP_NAME + JAVA_HELP_EXT); HelpSet hs; try { hs = new HelpSet(null, url); } catch (final HelpSetException e) { return null; } helpBroker = hs.createHelpBroker(); } return helpBroker; } static AbstractButton createHelpButton(final String helpTopicId) { AbstractButton newButton; newButton = new JButton(); CSH.setHelpIDString(newButton, helpTopicId); newButton.addActionListener(new CSH.DisplayHelpFromSource(SHTMLHelpBroker.getHelpBroker())); return newButton; } static void initJavaHelpItem(final JMenuItem mi, final String helpTopicId) { CSH.setHelpIDString(mi, helpTopicId); mi.addActionListener(new CSH.DisplayHelpFromSource(SHTMLHelpBroker.getHelpBroker())); mi.setIcon(DynamicResource.getIconForCommand(SHTMLPanelImpl.getUiResources(), SHTMLPanelImpl.helpTopicsAction)); mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0)); mi.setEnabled(true); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLEditorPane.java0100644 0000000 0000000 00000507377 13147564753 023210 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.util.Vector; import java.util.prefs.Preferences; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.Icon; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import javax.swing.TransferHandler; import javax.swing.event.CaretEvent; import javax.swing.plaf.TextUI; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import javax.swing.text.Element; import javax.swing.text.ElementIterator; import javax.swing.text.MutableAttributeSet; import javax.swing.text.NavigationFilter; import javax.swing.text.Position.Bias; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.CSS; import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLDocument; /** * An editor pane for application SimplyHTML. * *

This is extending JEditorPane by cut and paste * and drag and drop for HTML text. * JEditorPane inherits cut and paste from * JTextComponent where handling for plain text is implemented only. * JEditorPane has no additional functionality to add cut * and paste for the various content types it supports * (such as 'text/html').

* *

In stage 4 support for caret movement inside tables and * table manipulation methods are added.

* *

In stage 6 support for list manipulation was added.

* * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * * * @see com.lightdev.app.shtm.HTMLText * @see com.lightdev.app.shtm.HTMLTextSelection */ public class SHTMLEditorPane extends JEditorPane implements DropTargetListener, DragSourceListener, DragGestureListener { private static final String DO_NOTHING = "do nothing"; private static final String TAB = "\t"; private static final String TAB_REPLACEMENT = " "; private static DataFlavor getSupportedHtmlFlavor(Transferable t) { try { // final DataFlavor prototypeFlavor = new DataFlavor(com.lightdev.app.shtm.HTMLText.class, "HTMLText"); final DataFlavor prototypeFlavor = new DataFlavor("text/html; class=java.lang.String"); for (DataFlavor dataFlavor : t.getTransferDataFlavors()) if(dataFlavor.getPrimaryType().equals(prototypeFlavor.getPrimaryType()) && dataFlavor.getSubType().equals(prototypeFlavor.getSubType()) && dataFlavor.getRepresentationClass().equals(prototypeFlavor.getRepresentationClass()) ) return dataFlavor; } catch (ClassNotFoundException e) { throw new RuntimeException("Couldn't fetch appropriate text/html DataFlavor!"); } return null; } public enum PasteMode { PASTE_HTML("Paste as HTML"), PASTE_PLAIN_TEXT("Paste as plain-text"); private final String displayName; private PasteMode(final String displayName) { this.displayName = displayName; } public String getDisplayName() { return displayName; } public PasteMode invert() { if (this == PASTE_HTML) return PASTE_PLAIN_TEXT; else if (this == PASTE_PLAIN_TEXT) return PASTE_HTML; else throw new RuntimeException("Expected value for SHTMLEditorPane.PasteMode: " + name()); } public static PasteMode getValueFromPrefs() { // try to get freeplane pref value, and use simplyhtml pref value if that fails Preferences prefs = Preferences.userNodeForPackage(SHTMLEditorPane.class); PasteMode pm = SHTMLEditorPane.PasteMode.valueOf( SHTMLEditorPane.PasteMode.class, Util.getPreference( "default_paste_mode", prefs.get( PrefsDialog.PREFS_DEFAULT_PASTE_MODE, SHTMLEditorPane.PasteMode.PASTE_HTML .name()))); return pm; } } private static final boolean OLD_JAVA_VERSION = System.getProperty("java.version").compareTo("1.5.0") < 0; private JPopupMenu popup; private final ListManager listManager = new ListManager(); private PasteMode pasteMode; private boolean forceConstantPasteMode; /** * construct a new SHTMLEditorPane */ public SHTMLEditorPane() { super(); setCaretColor(Color.black); setNavigationFilter(new MyNavigationFilter()); addMouseListener(new MouseAdapter() { public void mousePressed(final MouseEvent e) { maybeShowPopup(e); } public void mouseReleased(final MouseEvent e) { maybeShowPopup(e); } public void mouseClicked(final MouseEvent ev) { if ((ev.getModifiers() & MouseEvent.CTRL_MASK) != 0) { final String linkURL = getURLOfExistingLink(); if (linkURL != null) { final SHTMLPanelImpl panel = SHTMLPanelImpl.getOwnerSHTMLPanel((Component) ev.getSource()); panel.openHyperlink(linkURL); } } } private void maybeShowPopup(final MouseEvent e) { if (popup != null && e.isPopupTrigger()) { popup.show(e.getComponent(), e.getX(), e.getY()); } } }); setPasteModeFromPrefs(); /** implement customized caret movement */ adjustKeyBindings(); /** init drag and drop */ initDnd(); } @Override public void setUI(TextUI newUI) { super.setUI(newUI); getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke('\u0004'), DO_NOTHING); getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("control T"), DO_NOTHING); getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("control H"), DO_NOTHING); } public PasteMode getPasteMode() { if (forceConstantPasteMode) { return pasteMode; } else { return PasteMode.getValueFromPrefs(); } } public void setPasteMode(final PasteMode pasteMode) { this.pasteMode = pasteMode; this.forceConstantPasteMode = true; } public void setPasteModeFromPrefs() { this.forceConstantPasteMode = false; } /** * adjust the key bindings of the key map existing for this * editor pane to our needs (i.e. add actions to certain keys * such as tab/shift tab for caret movement inside tables, etc.) * * This method had to be redone for using InputMap / ActionMap * instead of Keymap. */ private void adjustKeyBindings() { final ActionMap myActionMap = new ActionMap(); final InputMap myInputMap = new InputMap(); final KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0); myActionMap.put(SHTMLPanelImpl.nextTableCellAction, new TabAction(SHTMLPanelImpl.nextTableCellAction)); myInputMap.put(tab, SHTMLPanelImpl.nextTableCellAction); final KeyStroke shiftTab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK); myActionMap.put(SHTMLPanelImpl.prevTableCellAction, new ShiftTabAction(SHTMLPanelImpl.prevTableCellAction)); myInputMap.put(shiftTab, SHTMLPanelImpl.prevTableCellAction); final KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); myActionMap.put(newListItemAction, new NewParagraphAction()); myInputMap.put(enter, newListItemAction); final KeyStroke lineBreak = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_MASK); myActionMap.put(insertLineBreakAction, new InsertLineBreakAction()); myInputMap.put(lineBreak, insertLineBreakAction); final KeyStroke backspace = KeyStroke.getKeyStroke('\b', 0); myActionMap.put(deletePrevCharAction, new DeletePrevCharAction()); myInputMap.put(backspace, deletePrevCharAction); final KeyStroke delete = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0); myActionMap.put(deleteNextCharAction, new DeleteNextCharAction()); myInputMap.put(delete, deleteNextCharAction); myActionMap.put(moveUpAction, new MoveUpAction()); myInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), moveUpAction); myActionMap.put(moveDownAction, new MoveDownAction()); myInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), moveDownAction); myActionMap.put(homeAction, new HomeAction()); myInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), homeAction); myActionMap.put(endAction, new EndAction()); myInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), endAction); myActionMap.put(shiftHomeAction, new ShiftHomeAction()); myInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, KeyEvent.SHIFT_MASK), shiftHomeAction); myActionMap.put(shiftEndAction, new ShiftEndAction()); myInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, KeyEvent.SHIFT_MASK), shiftEndAction); //myActionMap.put("TTH", getDnew SHTMLEditorKitActions.ToggleTableHeaderCellAction(null)); //myInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, KeyEvent.SHIFT_MASK), "TTH"); myActionMap.setParent(getActionMap()); myInputMap.setParent(getInputMap()); setActionMap(myActionMap); setInputMap(JComponent.WHEN_FOCUSED, myInputMap); /* implementation before 1.4.1 ------------------------------------- Keymap map = getKeymap(); KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0); map.addActionForKeyStroke(tab, new NextTableCellAction(map.getAction(tab))); KeyStroke shiftTab = KeyStroke.getKeyStroke( KeyEvent.VK_TAB, InputEvent.SHIFT_MASK); map.addActionForKeyStroke(shiftTab, new PrevTableCellAction(map.getAction(shiftTab))); setKeymap(map); */ } /* (non-Javadoc) * @see javax.swing.JComponent#processKeyBinding(javax.swing.KeyStroke, java.awt.event.KeyEvent, int, boolean) */ protected boolean processKeyBinding(final KeyStroke ks, final KeyEvent e, final int condition, final boolean pressed) { final int maximumEndSelection = ((SHTMLDocument) getDocument()).getLastDocumentPosition(); if (getSelectionStart() >= maximumEndSelection && !(ks.getKeyCode() == KeyEvent.VK_LEFT || ks.getKeyCode() == KeyEvent.VK_UP || ks.getKeyCode() == KeyEvent.VK_HOME)) { return true; } if (getSelectionEnd() >= maximumEndSelection) { setSelectionEnd(maximumEndSelection - 1); } return super.processKeyBinding(ks, e, condition, pressed); } /** * Convenience method for setting the document text * @param sText the html-text of the document */ public void setText(String sText) { final SHTMLDocument doc = (SHTMLDocument) getDocument(); doc.startCompoundEdit(); if (sText == null || sText.equals("")) { sText = "

"; } doc.putProperty(SHTMLDocument.AdditionalComments, null); super.setText(sText); setCaretPosition(0); doc.endCompoundEdit(); if (OLD_JAVA_VERSION) { SHTMLPanelImpl.getOwnerSHTMLPanel(this).purgeUndos(); } } private class MyNavigationFilter extends NavigationFilter { /* (non-Javadoc) * @see javax.swing.text.NavigationFilter#moveDot(javax.swing.text.NavigationFilter.FilterBypass, int, javax.swing.text.Position.Bias) */ public void moveDot(final FilterBypass fb, int dot, final Bias bias) { dot = getValidPosition(dot); super.moveDot(fb, dot, bias); } /* (non-Javadoc) * @see javax.swing.text.NavigationFilter#setDot(javax.swing.text.NavigationFilter.FilterBypass, int, javax.swing.text.Position.Bias) */ public void setDot(final FilterBypass fb, int dot, final Bias bias) { dot = getValidPosition(dot); super.setDot(fb, dot, bias); } } private int getValidPosition(int position) { final SHTMLDocument doc = (SHTMLDocument) getDocument(); final int lastValidPosition = doc.getLastDocumentPosition() - 1; if (position > lastValidPosition) { position = lastValidPosition; } int startPos = 0; if (doc.getDefaultRootElement().getElementCount() > 1) { startPos = doc.getDefaultRootElement().getElement(1).getStartOffset(); } final int validPosition = Math.max(position, startPos); return validPosition; } private class DeletePrevCharAction extends AbstractAction { public void actionPerformed(final ActionEvent actionEvent) { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); final SHTMLDocument doc = (SHTMLDocument) getDocument(); if (selectionEnd >= doc.getLastDocumentPosition()) { return; } if (selectionStart == selectionEnd) { final boolean intervention = listManager.deletePrevChar(actionEvent); if (intervention) { return; } // Prevent deletion of table cell. final Element tableCell = selectionStart == 0 ? null : getTableCell(selectionStart - 1); if (tableCell != null && tableCell.getEndOffset() == selectionStart) { performDefaultKeyStrokeAction(KeyEvent.VK_LEFT, 0, actionEvent); return; } } performDefaultKeyStrokeAction('\b', 0, actionEvent); } } private class DeleteNextCharAction extends AbstractAction { public void actionPerformed(final ActionEvent actionEvent) { final int selectionStart = getSelectionStart(); if (selectionStart == getSelectionEnd()) { final SHTMLDocument doc = (SHTMLDocument) getDocument(); if (selectionStart >= doc.getLastDocumentPosition() - 1) { return; } boolean intervention = treatTables(actionEvent); if (intervention) { return; } intervention = listManager.deleteNextChar(actionEvent); if (intervention) { return; } } performDefaultKeyStrokeAction(KeyEvent.VK_DELETE, 0, actionEvent); } /** Treats tables. Returns true if intervention was necessary. */ private boolean treatTables(final ActionEvent event) { final int selectionStart = getSelectionStart(); final int nextPosition = selectionStart + 1; Element elem = null; final SHTMLDocument doc = getSHTMLDocument(); // Table cell element at the start of the selection elem = getCurrentTableCell(); if (elem != null && elem.getEndOffset() == nextPosition) { // Do nothing to avoid deletion of the whole cell. //return; } if (nextPosition < doc.getLength()) { // Table cell element at next position elem = getTableCell(nextPosition); if (elem != null && elem.getStartOffset() == nextPosition) { // In most cases, do nothing to avoid deletion of parts of the following table. final Element paragraphElement = getCurrentParagraphElement(); final boolean emptyParagraph = elementIsEmptyParagraph(paragraphElement); if (!caretWithinTableCell() && emptyParagraph) { // Empty paragraph outside a table, before a table. return false; } else if (caretWithinTableCell() && emptyParagraph) { // Remove empty paragraph at the end of the cell. removeElement(paragraphElement); setCaretPosition(getCaretPosition() - 1); return true; } else { return true; } } } return false; } } private class MoveUpAction extends AbstractAction { public void actionPerformed(final ActionEvent e) { if (caretWithinTableCell()) { if (getCaretPosition() == 0) { // The table is at the top of the document. // Insert new paragraph before the table. final Element tableElement = getCurrentTableCell().getParentElement().getParentElement(); try { getSHTMLDocument().insertBeforeStart(tableElement, "

"); } catch (final Exception ex) { } } if (tryDefaultKeyStrokeActionWithinCell(KeyEvent.VK_UP, 0, e)) { return; } final Element cellElement = getCurrentTableCell(); final Element rowElement = cellElement.getParentElement(); final Element tableElement = rowElement.getParentElement(); final int cellIndexInRow = rowElement.getElementIndex(cellElement.getStartOffset()); final int rowIndexInTable = tableElement.getElementIndex(rowElement.getStartOffset()); if (rowIndexInTable > 0) { final Element previousRowElement = tableElement.getElement(rowIndexInTable - 1); final int elementCount = previousRowElement.getElementCount(); if (elementCount > 0) { final Element targetCellElement = previousRowElement.getElement(Math.min(elementCount, cellIndexInRow)); setCaretPosition(targetCellElement.getEndOffset()); } else { setCaretPosition(tableElement.getStartOffset()); } } else { setCaretPosition(tableElement.getStartOffset()); } //} } performDefaultKeyStrokeAction(KeyEvent.VK_UP, 0, e); } } private class MoveDownAction extends AbstractAction { public void actionPerformed(final ActionEvent e) { if (caretWithinTableCell()) { if (tryDefaultKeyStrokeActionWithinCell(KeyEvent.VK_DOWN, 0, e)) { return; } final Element cellElement = getCurrentTableCell(); final Element rowElement = cellElement.getParentElement(); final Element tableElement = rowElement.getParentElement(); final int cellIndexInRow = rowElement.getElementIndex(cellElement.getStartOffset()); final int rowIndexInTable = tableElement.getElementIndex(rowElement.getStartOffset()); if (rowIndexInTable < tableElement.getElementCount() - 1) { final Element nextRowElement = tableElement.getElement(rowIndexInTable + 1); final int elementCount = nextRowElement.getElementCount(); if (elementCount > 0) { final Element targetCellElement = nextRowElement.getElement(Math.min(elementCount, cellIndexInRow)); //Element targetInnerElement = targetCellElement.getElement(0); // p or p-implied. setCaretPosition(targetCellElement.getStartOffset() - 1); } else { setCaretPosition(tableElement.getEndOffset() - 1); } } else { setCaretPosition(tableElement.getEndOffset() - 1); } //} } performDefaultKeyStrokeAction(KeyEvent.VK_DOWN, 0, e); } } private class HomeAction extends AbstractAction { public void actionPerformed(final ActionEvent e) { if (caretWithinTableCell()) { if (tryDefaultKeyStrokeActionWithinCell(KeyEvent.VK_HOME, 0, e)) { return; } setCaretPosition(getCurrentParagraphElement().getStartOffset()); } else { performDefaultKeyStrokeAction(KeyEvent.VK_HOME, 0, e); } } } private class EndAction extends AbstractAction { public void actionPerformed(final ActionEvent e) { if (caretWithinTableCell()) { if (tryDefaultKeyStrokeActionWithinCell(KeyEvent.VK_END, 0, e)) { return; } setCaretPosition(getCurrentParagraphElement().getEndOffset() - 1); } else { performDefaultKeyStrokeAction(KeyEvent.VK_END, 0, e); } } } private class ShiftHomeAction extends AbstractAction { public void actionPerformed(final ActionEvent e) { if (caretWithinTableCell()) { final int originalCaretPosition = getCaretPosition(); if (tryDefaultKeyStrokeActionWithinCell(KeyEvent.VK_HOME, KeyEvent.SHIFT_MASK, e)) { return; } final int newCaretPosition = getCurrentParagraphElement().getStartOffset(); if (newCaretPosition > originalCaretPosition) { select(originalCaretPosition, newCaretPosition); } else { select(newCaretPosition, originalCaretPosition); } } else { performDefaultKeyStrokeAction(KeyEvent.VK_HOME, KeyEvent.SHIFT_MASK, e); } } } private class ShiftEndAction extends AbstractAction { public void actionPerformed(final ActionEvent e) { if (caretWithinTableCell()) { final int originalCaretPosition = getCaretPosition(); if (tryDefaultKeyStrokeActionWithinCell(KeyEvent.VK_END, KeyEvent.SHIFT_MASK, e)) { return; } final int newCaretPosition = getCurrentParagraphElement().getEndOffset() - 1; if (newCaretPosition > originalCaretPosition) { select(originalCaretPosition, newCaretPosition); } else { select(newCaretPosition, originalCaretPosition); } } else { performDefaultKeyStrokeAction(KeyEvent.VK_END, KeyEvent.SHIFT_MASK, e); } } } /* ------- list manipulation start ------------------- */ /** * apply a set of attributes to the list the caret is * currently in (if any) * * @param a the set of attributes to apply */ public void applyListAttributes(final AttributeSet a) { final SHTMLDocument doc = (SHTMLDocument) getDocument(); final Element list = listManager.getListElement(getSelectionStart()); if (list != null) { if (a.getAttributeCount() > 0) { doc.addAttributes(list, a); /** * for some reason above code does not show the changed attributes * of the table, although the element really has them (maybe somebody * could let me know why...). Therefore we update the editor pane * contents comparably rude (any other more elegant alternatives * welcome!) * * --> found out why: the swing package does not render short hand * properties such as MARGIN or PADDING. When * contained in a document inside an AttributeSet * they already have to be split into MARGIN-TOP, * MARGIN-LEFT, etc. * adjusted AttributeComponents accordingly so * we don't need refresh anymore */ //refresh(); } } } /** * Action to create a new paragraph element, which * may be a paragraph proper or a list item. */ private class NewParagraphAction extends AbstractAction { /** construct a NewParagraphAction */ public NewParagraphAction() { } /** * create a new list item, when the caret is inside a list * *

The new item is created after the item at the caret position

*/ public void actionPerformed(final ActionEvent ae) { try { final int caretPosition = getCaretPosition(); // if we are in a list, create a new item final Element listItemElement = listManager.getListItemElement(caretPosition); if (listItemElement != null) { listManager.newListItem(); return; } // we are not in a list, call alternate action else { performDefaultKeyStrokeAction(KeyEvent.VK_ENTER, 0, ae); } } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } } } /** * toggle list formatting on or off for the currently * selected text portion. * *

Switches list display on for the given type, if the selection * contains parts not formatted as list or parts formatted as list * of another type.

* *

Switches list formatting off, if the selection contains * only parts formatted as list of the given type.

* * @param listTag the list tag type to toggle on or off (UL or OL) * @param attributeSet the attributes to use for the list to toggle to * @param forceOff indicator for toggle operation. If true, possibly * exisiting list formatting inside the selected parts always is switched * off. If false, the method decides, if list formatting for the parts * inside the selection needs to be switched on or off. */ public void toggleList(final String listTag, final AttributeSet attributeSet, final boolean forceOff) { listManager.toggleList(listTag, attributeSet, forceOff); } /** range indicator for applying attributes to the current cell only */ public static final int THIS_CELL = 0; /** range indicator for applying attributes to cells of the current column only */ public static final int THIS_COLUMN = 1; /** range indicator for applying attributes to cells of the current row only */ public static final int THIS_ROW = 2; /** range indicator for applying attributes to all cells */ public static final int ALL_CELLS = 3; /** default table width */ public static final String DEFAULT_TABLE_WIDTH = "80%"; /** default vertical alignment */ public static final String DEFAULT_VERTICAL_ALIGN = "top"; /** * Insert a new table. * * @param colCount the number of columns the new table shall have */ public void insertNewTable(final int colCount) { final int selectionStart = getSelectionStart(); final int start = selectionStart; final StringWriter sw = new StringWriter(); final SHTMLDocument doc = (SHTMLDocument) getDocument(); final SHTMLWriter w = new SHTMLWriter(sw, doc); // some needed constants final String table = HTML.Tag.TABLE.toString(); final String tr = HTML.Tag.TR.toString(); final String td = HTML.Tag.TD.toString(); final String th = HTML.Tag.TH.toString(); final String p = HTML.Tag.P.toString(); final boolean insertPlainTable = !Util.preferenceIsTrue("table.insertStyle", "true"); final boolean insertTableHeader = Util.preferenceIsTrue("table.insertHeader"); try { // the attribute set to use for applying attributes to tags final SimpleAttributeSet tableAttributeSet = new SimpleAttributeSet(); // build table attribute Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.WIDTH, DEFAULT_TABLE_WIDTH); Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.BORDER_STYLE, "solid"); Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.BORDER_TOP_WIDTH, "0"); Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.BORDER_RIGHT_WIDTH, "0"); Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.BORDER_BOTTOM_WIDTH, "0"); Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.BORDER_LEFT_WIDTH, "0"); tableAttributeSet.addAttribute(HTML.Attribute.BORDER, "0"); w.writeStartTag(table, insertPlainTable ? null : tableAttributeSet); // get width of each cell according to column count // build cell width attribute Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.WIDTH, Integer.toString(100 / colCount) + Util.pct); tableAttributeSet.addAttribute(HTML.Attribute.VALIGN, DEFAULT_VERTICAL_ALIGN); Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.BORDER_TOP_WIDTH, "1"); Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.BORDER_RIGHT_WIDTH, "1"); Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.BORDER_BOTTOM_WIDTH, "1"); Util.styleSheet().addCSSAttribute(tableAttributeSet, CSS.Attribute.BORDER_LEFT_WIDTH, "1"); final SimpleAttributeSet pSet = new SimpleAttributeSet(); Util.styleSheet().addCSSAttribute(pSet, CSS.Attribute.MARGIN_TOP, "1"); Util.styleSheet().addCSSAttribute(pSet, CSS.Attribute.MARGIN_RIGHT, "1"); Util.styleSheet().addCSSAttribute(pSet, CSS.Attribute.MARGIN_BOTTOM, "1"); Util.styleSheet().addCSSAttribute(pSet, CSS.Attribute.MARGIN_LEFT, "1"); tableAttributeSet.removeAttribute(HTML.Attribute.BORDER); if (insertTableHeader) { w.writeStartTag(tr, null); for (int i = 0; i < colCount; i++) { w.writeStartTag(th, insertPlainTable ? null : tableAttributeSet); w.writeStartTag(p, insertPlainTable ? null : pSet); w.writeEndTag(p); w.writeEndTag(th); } w.writeEndTag(tr); } w.writeStartTag(tr, null); for (int i = 0; i < colCount; i++) { w.writeStartTag(td, insertPlainTable ? null : tableAttributeSet); w.writeStartTag(p, insertPlainTable ? null : pSet); w.writeEndTag(p); w.writeEndTag(td); } w.writeEndTag(tr); w.writeEndTag(table); // read table html into document Element para = doc.getParagraphElement(selectionStart); if (para == null) { throw new Exception("no text selected"); } for (Element parent = para.getParentElement(); !parent.getName().equalsIgnoreCase(HTML.Tag.BODY.toString()) && !parent.getName().equalsIgnoreCase(HTML.Tag.TD.toString()); para = parent, parent = parent .getParentElement()) { ; } if (para != null) { try { doc.startCompoundEdit(); doc.insertBeforeStart(para, sw.getBuffer().toString()); } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } finally { doc.endCompoundEdit(); } // w.write(para); //doc.replaceHTML(para, 1, sw.getBuffer().toString()); } } catch (final Exception ex) { Util.errMsg(null, ex.getMessage(), ex); } select(start, start); } /** * apply a new anchor to the currently selected text * *

If nothing is selected, this method does nothing

* * @param anchorName the name of the new anchor */ public void insertAnchor(final String anchorName) { if (getSelectionStart() != getSelectionEnd()) { final SimpleAttributeSet aSet = new SimpleAttributeSet(); aSet.addAttribute(HTML.Attribute.NAME, anchorName); final SimpleAttributeSet set = new SimpleAttributeSet(); set.addAttribute(HTML.Tag.A, aSet); applyAttributes(set, false); } } /** * insert a line break (i.e. a break for which paragraph * spacing is not applied) */ public void insertBreak() { final int caretPos = getCaretPosition(); final SHTMLDocument doc = (SHTMLDocument) getDocument(); try { ((SHTMLEditorKit) getEditorKit()).insertHTML(doc, caretPos, "
", 0, 0, HTML.Tag.BR); } catch (final Exception e) { } setCaretPosition(caretPos + 1); } /** * set a text link at the current selection replacing the selection * with a given text. * *

If nothing is selected, but the caret is inside a link, this will * replace the existing link. If nothing is selected and the caret * is not inside a link, this method does nothing.

* * @param linkText the text that shall appear as link at the current selection * @param href the target this link shall refer to * @param className the style class to be used */ public void setLink(final String linkText, final String href, final String className) { setLink(linkText, href, className, null, null); } /** * Sets a hyperlink at the current selection, replacing the selection * with the given text or image. * * @param linkText the text to show as link (or null, if an image shall appear instead) * @param href the link reference * @param className the style name to be used for the link * @param linkImage the file name of the image be used for the link (or null, if a text link is to be set instead) * @param size the size of the image or null */ public void setLink(final String linkText, final String href, final String className, final String linkImage, final Dimension size) { if (linkImage == null) { setTextLink(getCurrentLinkElement(), href, className, linkText, getSHTMLDocument()); } else { setImageLink(getSHTMLDocument(), getCurrentLinkElement(), href, className, linkImage, size); } } /** * set an image link replacing the current selection * * @param doc the document to apply the link to * @param e the link element found at the selection, or null if none was found * @param href the link reference * @param className the style name to be used for the link * @param linkImage the file name of the image be used for the link * @param size the size of the image */ private void setImageLink(final SHTMLDocument doc, final Element e, final String href, final String className, final String linkImage, final Dimension size) { final String a = HTML.Tag.A.toString(); SimpleAttributeSet set = new SimpleAttributeSet(); set.addAttribute(HTML.Attribute.HREF, href); final String sStyleName = Util.getResourceString("standardStyleName"); if (className != null && !className.equalsIgnoreCase(sStyleName)) { set.addAttribute(HTML.Attribute.CLASS, className); } final StringWriter sw = new StringWriter(); final SHTMLWriter w = new SHTMLWriter(sw, doc); try { w.writeStartTag(a, set); set = new SimpleAttributeSet(); set.addAttribute(HTML.Attribute.SRC, Util.getRelativePath(new File(doc.getBase().getFile()), new File(linkImage))); set.addAttribute(HTML.Attribute.BORDER, "0"); if (size != null) { set.addAttribute(HTML.Attribute.WIDTH, Integer.toString(new Double(size.getWidth()).intValue())); set.addAttribute(HTML.Attribute.HEIGHT, Integer.toString(new Double(size.getHeight()).intValue())); } w.writeStartTag(HTML.Tag.IMG.toString(), set); w.writeEndTag(a); if (e != null) { System.out.println("SHTMLEditorPane.setImageLink setOuterHTML html='" + sw.getBuffer() + "'"); doc.setOuterHTML(e, sw.getBuffer().toString()); } else { final int start = getSelectionStart(); if (start < getSelectionEnd()) { replaceSelection(""); System.out.println("SHTMLEditorPane.setImageLink insertAfterEnd html='" + sw.getBuffer() + "'"); doc.insertAfterEnd(doc.getCharacterElement(start), sw.getBuffer().toString()); } } } catch (final Exception ex) { Util.errMsg(this, ex.getMessage(), ex); } } /** * set a text link replacing the current selection * * @param e the link element found at the selection, or null if none was found * @param href the link reference * @param className the style name to be used for the link * @param linkText the text to show as link * @param doc the document to apply the link to */ private void setTextLink(final Element e, final String href, final String className, String linkText, final SHTMLDocument doc) { final String sStyleName = Util.getResourceString("standardStyleName"); final SimpleAttributeSet set = new SimpleAttributeSet(); final SimpleAttributeSet aSet = new SimpleAttributeSet(); if(href != null){ aSet.addAttribute(HTML.Attribute.HREF, href); if (className != null && !className.equalsIgnoreCase(sStyleName)) { aSet.addAttribute(HTML.Attribute.CLASS, className); } } if (e != null) { // replace existing link set.addAttributes(e.getAttributes()); if(href != null){ set.addAttribute(HTML.Tag.A, aSet); } else{ set.removeAttribute(HTML.Tag.A); } final int start = e.getStartOffset(); int length = e.getEndOffset() - start; try { if(linkText == null) linkText = doc.getText(start, length); doc.replace(start, length, linkText, set); } catch (final BadLocationException ex) { Util.errMsg(this, ex.getMessage(), ex); } } else if(href != null){ // create new link for text selection final int start = getSelectionStart(); if (start < getSelectionEnd()) { set.addAttribute(HTML.Tag.A, aSet); try { if(linkText == null) linkText = doc.getText(start, getSelectionEnd() - start); replaceSelection(linkText); doc.setCharacterAttributes(start, linkText.length(), set, false); } catch (final BadLocationException ex) { Util.errMsg(this, ex.getMessage(), ex); } } } } /** * remove an anchor with a given name * * @param anchorName the name of the anchor to remove */ public void removeAnchor(final String anchorName) { //System.out.println("SHTMLEditorPane removeAnchor"); AttributeSet attrs; Object nameAttr; Object link; final ElementIterator eli = new ElementIterator(getDocument()); Element elem = eli.first(); while (elem != null) { attrs = elem.getAttributes(); link = attrs.getAttribute(HTML.Tag.A); if (link != null /*&& link.toString().equalsIgnoreCase(HTML.Tag.A.toString())*/) { //System.out.println("found anchor attribute"); nameAttr = ((AttributeSet) link).getAttribute(HTML.Attribute.NAME); if (nameAttr != null && nameAttr.toString().equalsIgnoreCase(anchorName)) { // remove anchor here //System.out.println("removing anchor name=" + nameAttr); final SimpleAttributeSet newSet = new SimpleAttributeSet(attrs); newSet.removeAttribute(HTML.Tag.A); final SHTMLDocument doc = (SHTMLDocument) getDocument(); final int start = elem.getStartOffset(); doc.setCharacterAttributes(elem.getStartOffset(), elem.getEndOffset() - start, newSet, true); } } elem = eli.next(); } } /** * insert a table column before the current column * (if any) */ public void insertTableColumn() { final Element cell = getCurrentTableCell(); if (cell != null) { createTableColumn(cell, Util.getElementIndex(cell)/*getColNumber(cell)*/, true); } } /** * append a table column after the last column * (if any) */ public void appendTableColumn() { final Element cell = getCurrentTableCell(); if (cell != null) { final Element lastCell = getLastTableCell(cell); createTableColumn(lastCell, Util.getElementIndex(cell)/*getColNumber(lastCell)*/, false); } } /** * create a table column before or after a given column * * the width of the first cell in the column * (if there is a width attribute) is split into * half so that the new column and the column * inserted before are sharing the space originally * taken by the column inserted before. * * @param cell the cell to copy from * @param cIndex the number of the column 'cell' is in * @param before true indicates insert before, false append after */ private void createTableColumn(final Element cell, final int cIndex, final boolean before) { // get the new width setting for this column and the new column final SHTMLDocument doc = (SHTMLDocument) getDocument(); doc.startCompoundEdit(); final Element table = cell.getParentElement().getParentElement(); Element srcCell = table.getElement(0).getElement(cIndex); final SimpleAttributeSet set = new SimpleAttributeSet(); final Object attr = set.getAttribute(CSS.Attribute.WIDTH); if (attr != null) { //LengthValue lv = new LengthValue(attr); //String unit = lv.getUnit(); //int width = (int) lv.getAttrValue(attr.toString(), unit); final int width = (int) Util.getAbsoluteAttrVal(attr); // Util.getAttrValue(attr); //System.out.println("SHTMLEditorPane.createTableColumn width=" + width); final String unit = Util.getLastAttrUnit(); //System.out.println("SHTMLEditorPane.createTableColumn unit=" + unit); final String widthString = Integer.toString(width / 2) + unit; //System.out.println("SHTMLEditorPane.createTableColumn widthString=" + widthString); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.WIDTH, widthString); } // adjust width and insert new column //SimpleAttributeSet a = new SimpleAttributeSet(set.copyAttributes()); //int cellIndex = getCellIndex(srcCell); //boolean insertFirst = (before && (cellIndex == 0)); for (int rIndex = 0; rIndex < table.getElementCount(); rIndex++) { srcCell = table.getElement(rIndex).getElement(cIndex); /* if(rIndex > 0) { adjustBorder(a, a, a, CombinedAttribute.ATTR_TOP); } adjustBorder(a, a, a, CombinedAttribute.ATTR_LEFT); */ doc.addAttributes(srcCell, set); try { if (before) { doc.insertBeforeStart(srcCell, createTableCellHTML(srcCell)); } else { doc.insertAfterEnd(srcCell, createTableCellHTML(srcCell)); } } catch (final IOException ioe) { Util.errMsg(null, ioe.getMessage(), ioe); } catch (final BadLocationException ble) { Util.errMsg(null, ble.getMessage(), ble); } } //adjustColumnBorders(table.getElement(0).getElement(cIndex)); //adjustColumnBorders(table.getElement(0).getElement(++cIndex)); doc.endCompoundEdit(); } /** * Appends a row to a table if the caret is inside a table. */ public void appendTableRow() { final Element cell = getCurrentTableCell(); if (cell != null) { final Element table = cell.getParentElement().getParentElement(); final Element lastRow = table.getElement(table.getElementCount() - 1); createTableRow(lastRow, Util.getRowIndex(lastRow.getElement(0)), false, null); } } /** * Inserts a row to a table, assuming the caret is currently * inside a table. */ public void insertTableRow(final String forcedCellName) { final Element cell = getCurrentTableCell(); if (cell != null) { createTableRow(cell.getParentElement(), Util.getRowIndex(cell), true, forcedCellName); } } /** * Creates a new table row, inserting it before, or appending after the given * row, depending on a parameter. * * Is shared by appendRow and insertRow actions. * * @param srcRow the row element to copy from * @param before true indicates insert before, false append after * @param forcedCellName if non-null, that cell name will be used in the new table row. Values: "td", "th". */ private void createTableRow(final Element srcRow, int rowIndex, final boolean before, final String forcedCellName) { try { if (before) { getSHTMLDocument().insertBeforeStart(srcRow, createTableRowHTML(srcRow, forcedCellName)); if (rowIndex == 0) { rowIndex++; } } else { getSHTMLDocument().insertAfterEnd(srcRow, createTableRowHTML(srcRow, forcedCellName)); rowIndex++; } } catch (final IOException ioe) { Util.errMsg(null, ioe.getMessage(), ioe); } catch (final BadLocationException ble) { Util.errMsg(null, ble.getMessage(), ble); } } /** * Returns the HTML string of the given table row, without the cell contents, possibly * forcing the cell name on "td" or "th", if the parameter for forced cell name is non-null. * * For each table column found in srcRow, a start and end * tag TD is created with the same attributes as in the * column found in srcRow. The attributes of srcRow * are applied to the newly created row HTML string as well. * * @param modelRow the table row Element to copy from * @param insert indicates if a row is inserted before another row * * @return an HTML string representing the new table row * (without cell contents) */ private String createTableRowHTML(final Element modelRow, final String forcedCellName) { final StringWriter stringWriter = new StringWriter(); final SHTMLWriter shtmlWriter = new SHTMLWriter(stringWriter, (SHTMLDocument) getDocument()); final String tr = HTML.Tag.TR.toString(); try { shtmlWriter.writeStartTag(tr, modelRow.getAttributes()); for (int i = 0; i < modelRow.getElementCount(); i++) { final Element modelCell = modelRow.getElement(i); final String cellName = forcedCellName != null ? forcedCellName : modelCell.getName(); createTableCellHTML(shtmlWriter, modelCell, cellName); } shtmlWriter.writeEndTag(tr); } catch (final IOException ex) { Util.errMsg(null, ex.getMessage(), ex); } return stringWriter.getBuffer().toString(); } private void createTableCellHTML(final SHTMLWriter w, final Element cell, final String cellName) throws IOException { { w.writeStartTag(cellName, cell.getAttributes()); final Element paragraph = cell.getElement(0); final String parName = "p"; //Was: paragraph.getName(); UL and OL are parNames not to be copied. --Dan if (!parName.equalsIgnoreCase(HTML.Tag.IMPLIED.toString())) { w.writeStartTag(parName, paragraph.getAttributes()); w.writeEndTag(parName); } w.writeEndTag(cellName); } } /** * build an HTML string copying from an existing table cell * * @param srcCell the cell to get the HTML for * @param a set of attributes to copy if we are inserting first table column * @param insertFirst indicates if we are inserting first table column * @param rNum number of row a cell is to be inserted to * (can be any value if insertFirst is false) * * @return the HTML string for the given cell (without cell contents) */ private String createTableCellHTML(final Element srcCell) { final StringWriter sw = new StringWriter(); final SHTMLWriter w = new SHTMLWriter(sw, (SHTMLDocument) getDocument()); try { createTableCellHTML(w, srcCell, srcCell.getName()); } catch (final IOException e) { Util.errMsg(null, e.getMessage(), e); } //System.out.println("getTableCellHTML buffer='" + sw.getBuffer().toString() + "'"); return sw.getBuffer().toString(); } /** * delete the row of the table the caret is currently in (if any) */ public void deleteTableRow() { final Element cell = getCurrentTableCell(); final int finalCaretPosition = cell.getStartOffset(); if (cell != null) { removeElement(cell.getParentElement()); final int docLength = getDocument().getLength(); if (docLength >= finalCaretPosition) { setCaretPosition(finalCaretPosition); } else { setCaretPosition(docLength); } } } /** * delete the column of the table the caret is currently in (if any) * *

width of adjacent column is adjusted, if there is more than one * column in the table. Width adjustment only works, if width * attributes of both the column to remove and its adjacent column * have the same unit (pt or %).

* *

If there is only one cell or if the caret is not in a table, * this method does nothing

* *

Smart border handling automatically sets the left border of a cell * to zero, if the cell on the left of that cell has a right border and * both cells have no margin. In that case removing the first column * will cause all cells of the new first column to have no left border.

*/ public void deleteTableCol() { final Element cell = getCurrentTableCell(); if (cell == null) { return; } Element row = cell.getParentElement(); final int lastColIndex = row.getElementCount() - 1; if (lastColIndex <= 0) { return; } final int cIndex = Util.getElementIndex(cell); //getColNumber(cell); int offset = -1; // adjacent cell is left of current cell if (cIndex == 0) { // if current cell is in first column... offset *= -1; // ...adjacent cell is right of current cell } final Object attrC = cell.getAttributes().getAttribute(CSS.Attribute.WIDTH); final Object attrA = row.getElement(cIndex + offset).getAttributes().getAttribute(CSS.Attribute.WIDTH); SimpleAttributeSet set = null; if (attrC != null && attrA != null) { //LengthValue lvC = new LengthValue(attrC); //LengthValue lvA = new LengthValue(attrA); final int widthC = (int) Util.getAbsoluteAttrVal(attrC); // Util.getAttrValue(attrC); final String cUnit = Util.getLastAttrUnit(); //String cUnit = lvC.getUnit(); final int widthA = (int) Util.getAbsoluteAttrVal(attrA); // Util.getAttrValue(attrA); final String aUnit = Util.getLastAttrUnit(); if (aUnit.equalsIgnoreCase(cUnit)) { int width = 0; width += widthC; width += widthA; if (width > 0) { final String widthString = Integer.toString(width) + cUnit; set = new SimpleAttributeSet(row.getElement(cIndex + offset).getAttributes()); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.WIDTH, widthString); } } } final Element table = row.getParentElement(); final SHTMLDocument doc = (SHTMLDocument) getDocument(); doc.startCompoundEdit(); if (cIndex < lastColIndex) { offset = 0; } for (int rIndex = table.getElementCount() - 1; rIndex >= 0; rIndex--) { row = table.getElement(rIndex); try { doc.removeElements(row, cIndex, 1); /* the following line does not work for the last column in a table so we use above code instead removeElement(row.getElement(cIndex)); */ } catch (final BadLocationException ble) { Util.errMsg(null, ble.getMessage(), ble); } if (set != null) { doc.addAttributes(row.getElement(cIndex + offset), set); } //adjustColumnBorders(table.getElement(0).getElement(cIndex + offset)); } doc.endCompoundEdit(); } /** For each cell within the selection, turns a table data cell into a table header cell or vice * versa. */ public void toggleTableHeaderCell() { final int originalCaretPosition = getCaretPosition(); final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); Element tableCell = getTableCell(selectionStart); while (tableCell != null && tableCell.getStartOffset() <= selectionEnd) { final String content = elementToHTML(tableCell); String newContent = content; if (content.matches("(?is)\\s*\\s*$", ""); } else if (content.matches("(?is)\\s*\\s*$", ""); } else { // Unexpected } final Element row = tableCell.getParentElement(); final int tableCellIdx = row.getElementIndex(tableCell.getStartOffset()); try { getSHTMLDocument().setOuterHTML(tableCell, newContent); } catch (final Exception ex) { } tableCell = row.getElement(tableCellIdx); // Restore the cell reference after the operation. tableCell = getNextCell(tableCell); } setCaretPosition(originalCaretPosition); } /** Moves the table row up. Does not treat multirow cells. */ void moveTableRowUp() { final Element tableCell = getCurrentTableCell(); final Element tableRow = tableCell.getParentElement(); final Element table = tableRow.getParentElement(); final int indexOfRowInTable = table.getElementIndex(getCaretPosition()); if (indexOfRowInTable == 0) { return; } try { getSHTMLDocument().startCompoundEdit(); final SHTMLWriter writer = new SHTMLWriter(getSHTMLDocument()); writer.writeStartTag(table); for (int i = 0; i < indexOfRowInTable - 1; i++) { writer.write(table.getElement(i)); } writer.write(tableRow); writer.write(table.getElement(indexOfRowInTable - 1)); for (int i = indexOfRowInTable + 1; i < table.getElementCount(); i++) { writer.write(table.getElement(i)); } writer.writeEndTag(table); final int offsetWithinCurrentRow = getCaretPosition() - tableRow.getStartOffset(); final int finalCaretPosition = table.getElement(indexOfRowInTable - 1).getStartOffset() + offsetWithinCurrentRow; getSHTMLDocument().setOuterHTML(table, writer.getWrittenString()); setCaretPosition(finalCaretPosition); } catch (final Exception ex) { } finally { getSHTMLDocument().endCompoundEdit(); } } /** Moves the table column left. Does not treat multicolumn cells. */ void moveTableColumnLeft() { final Element tableCell = getCurrentTableCell(); final Element tableRow = tableCell.getParentElement(); final Element table = tableRow.getParentElement(); final int indexOfCellInRow = tableRow.getElementIndex(getCaretPosition()); if (indexOfCellInRow == 0) { return; } try { getSHTMLDocument().startCompoundEdit(); final SHTMLWriter writer = new SHTMLWriter(getSHTMLDocument()); writer.writeStartTag(table); for (int rowIdx = 0; rowIdx < table.getElementCount(); rowIdx++) { final Element row = table.getElement(rowIdx); writer.writeStartTag(row); for (int i = 0; i < indexOfCellInRow - 1; i++) { writer.write(row.getElement(i)); } writer.write(row.getElement(indexOfCellInRow)); writer.write(row.getElement(indexOfCellInRow - 1)); for (int i = indexOfCellInRow + 1; i < row.getElementCount(); i++) { writer.write(row.getElement(i)); } writer.writeEndTag(row); } writer.writeEndTag(table); final int offsetWithinCurrentCell = getCaretPosition() - tableCell.getStartOffset(); final int finalCaretPosition = tableRow.getElement(indexOfCellInRow - 1).getStartOffset() + offsetWithinCurrentCell; getSHTMLDocument().setOuterHTML(table, writer.getWrittenString()); setCaretPosition(finalCaretPosition); } catch (final Exception ex) { } finally { getSHTMLDocument().endCompoundEdit(); } } /** Moves the table column right. Does not treat multicolumn cells. */ void moveTableColumnRight() { final Element tableCell = getCurrentTableCell(); final Element tableRow = tableCell.getParentElement(); final Element table = tableRow.getParentElement(); final int indexOfCellInRow = tableRow.getElementIndex(getCaretPosition()); if (indexOfCellInRow == tableRow.getElementCount() - 1) { return; } try { getSHTMLDocument().startCompoundEdit(); final SHTMLWriter writer = new SHTMLWriter(getSHTMLDocument()); writer.writeStartTag(table); for (int rowIdx = 0; rowIdx < table.getElementCount(); rowIdx++) { final Element row = table.getElement(rowIdx); writer.writeStartTag(row); for (int i = 0; i < indexOfCellInRow; i++) { writer.write(row.getElement(i)); } writer.write(row.getElement(indexOfCellInRow + 1)); writer.write(row.getElement(indexOfCellInRow)); for (int i = indexOfCellInRow + 2; i < row.getElementCount(); i++) { writer.write(row.getElement(i)); } writer.writeEndTag(row); } final Element cellToTheRight = tableRow.getElement(indexOfCellInRow + 1); final int finalCaretPosition = getCaretPosition() + cellToTheRight.getEndOffset() - cellToTheRight.getStartOffset(); getSHTMLDocument().setOuterHTML(table, writer.getWrittenString()); setCaretPosition(finalCaretPosition); } catch (final Exception ex) { } finally { getSHTMLDocument().endCompoundEdit(); } } /** Moves the table row down. Does not treat multirow cells. */ void moveTableRowDown() { final Element tableCell = getCurrentTableCell(); if (tableCell == null) { return; } final Element tableRow = tableCell.getParentElement(); final Element table = tableRow.getParentElement(); final int indexOfRowInTable = table.getElementIndex(getCaretPosition()); if (indexOfRowInTable == table.getElementCount() - 1) { return; } try { getSHTMLDocument().startCompoundEdit(); final SHTMLWriter writer = new SHTMLWriter(getSHTMLDocument()); writer.writeStartTag(table); for (int i = 0; i < indexOfRowInTable; i++) { writer.write(table.getElement(i)); } writer.write(table.getElement(indexOfRowInTable + 1)); writer.write(tableRow); for (int i = indexOfRowInTable + 2; i < table.getElementCount(); i++) { writer.write(table.getElement(i)); } writer.writeEndTag(table); final Element rowBelow = table.getElement(indexOfRowInTable + 1); final int finalCaretPosition = getCaretPosition() + rowBelow.getEndOffset() - rowBelow.getStartOffset(); getSHTMLDocument().setOuterHTML(table, writer.getWrittenString()); setCaretPosition(finalCaretPosition); } catch (final Exception ex) { } finally { getSHTMLDocument().endCompoundEdit(); } } /** * Removes an element from the document of this editor. Removes it manually if the parent is not body. * Leaves it to the caller to set the caret position after the removal. */ private void removeElement(final Element element) { try { final int start = element.getStartOffset(); final Element parent = element.getParentElement(); if (parent.getName().equalsIgnoreCase("body")) { getSHTMLDocument().remove(start, element.getEndOffset() - start); } else { // If the parent is not body, remove manually, to avoid quirks, e.g. in tables. final int indexInParent = parent.getElementIndex(element.getStartOffset()); final SHTMLWriter writer = new SHTMLWriter(getSHTMLDocument()); writer.writeStartTag(parent); for (int i = 0; i < indexInParent; i++) { writer.write(parent.getElement(i)); } for (int i = indexInParent + 1; i < parent.getElementCount(); i++) { writer.write(parent.getElement(i)); } writer.writeEndTag(parent); getSHTMLDocument().setOuterHTML(parent, writer.getWrittenString()); } } catch (final Exception ex) { Util.errMsg(null, ex.getMessage(), ex); } } /** * apply a set of attributes to the table the caret is * currently in (if any) * * @param a the set of attributes to apply */ public void applyTableAttributes(final AttributeSet a) { final Element cell = getCurrentTableCell(); if (cell != null) { final Element table = cell.getParentElement().getParentElement(); if (a.getAttributeCount() > 0) { //System.out.println("applyTableAttributes count=" + a.getAttributeCount() + " a=" + a); ((SHTMLDocument) getDocument()).addAttributes(table, a); /** * for some reason above code does not show the changed attributes * of the table, although the element really has them (maybe somebody * could let me know why...). Therefore we update the editor pane * contents comparably rude (any other more elegant alternatives * welcome!) * * --> found out why: the swing package does not render short hand * properties such as MARGIN or PADDING. When * contained in a document inside an AttributeSet * they already have to be split into MARGIN-TOP, * MARGIN-LEFT, etc. * adjusted AttributeComponents accordingly so * we don't need refresh anymore */ //refresh(); } } } /** * refresh the whole contents of this editor pane with brute force */ private void refresh() { final int pos = getCaretPosition(); final String data = getText(); setText(""); setText(data); setCaretPosition(pos); } /** * apply a set of attributes to a given range of cells * of the table the caret is currently in (if any) * * @param a the set of attributes to apply * @param range the range of cells to apply attributes to */ public void applyCellAttributes(final AttributeSet a, final int range) { //System.out.println("SHTMLEditorPane applyCellAttributes a=" + a); final Element cell = getCurrentTableCell(); int cIndex = 0; int rIndex = 0; final SHTMLDocument doc = (SHTMLDocument) getDocument(); if (cell != null) { Element row = cell.getParentElement(); final Element table = row.getParentElement(); Element aCell; switch (range) { case THIS_CELL: doc.addAttributes(cell, a); break; case THIS_ROW: for (cIndex = 0; cIndex < row.getElementCount(); cIndex++) { aCell = row.getElement(cIndex); doc.addAttributes(aCell, a); } break; case THIS_COLUMN: cIndex = Util.getElementIndex(cell); //getColNumber(cell); for (rIndex = 0; rIndex < table.getElementCount(); rIndex++) { aCell = table.getElement(rIndex).getElement(cIndex); doc.addAttributes(aCell, a); } break; case ALL_CELLS: while (rIndex < table.getElementCount()) { row = table.getElement(rIndex); cIndex = 0; while (cIndex < row.getElementCount()) { aCell = row.getElement(cIndex); //System.out.println("applyCellAttributes ALL_CELLS adjusted a=" + adjustCellBorders(aCell, a)); doc.addAttributes(aCell, a); cIndex++; } rIndex++; } break; } } } public SHTMLDocument getSHTMLDocument() { return (SHTMLDocument) getDocument(); } /** * Gets the number of the table column in which the given cell is located. * * @param cell the cell to get the column number for * @return the column number of the given cell */ private int getColNumber(final Element cell) { int i = 0; final Element thisRow = cell.getParentElement(); final int last = thisRow.getElementCount() - 1; Element aCell = thisRow.getElement(i); if (aCell != cell) { while ((i < last) && (aCell != cell)) { aCell = thisRow.getElement(++i); } } return i; } /* ------- table manipulation end -------------------- */ /* ------- table cell navigation start --------------- */ /** * Action to move the caret from the current table cell * to the next table cell. */ private class TabAction extends AbstractAction { /** action to use when not inside a table */ /* removed for changes in J2SE 1.4.1 private Action alternateAction; */ /** construct a NextTableCellAction */ public TabAction(final String actionName) { super(actionName); } /** * construct a NextTableCellAction * * @param altAction the action to use when the caret * is not inside a table */ /* removed for changes in J2SE 1.4.1 public NextTableCellAction(Action altAction) { alternateAction = altAction; } */ /** * move to the previous cell or invoke an alternate action if the * caret is not inside a table * * this will append a new table row when the caret * is inside the last table cell */ public void actionPerformed(final ActionEvent ae) { if (listManager.caretAtTheBeginningOfListItem()) { // Increase indent within list listManager.increaseIndent(true); return; } final Element cell = getCurrentTableCell(); if (cell != null) { // Within a table cell. goNextCell(cell); return; } if (listManager.caretWithinListItem()) { // Increase indent within list listManager.increaseIndent(true); return; } // Do nothing; above all, do not enter tab character. //performDefaultKeyStrokeAction(KeyEvent.VK_TAB, 0, ae); if (Util.preferenceIsTrue("table.insertNewOnTabKey", "false")) { insertNewTable(3); } } } /** * Action to move the caret from the current table cell * to the previous table cell. */ private class ShiftTabAction extends AbstractAction { /** action to use when not inside a table */ /* removed for changes in J2SE 1.4.1 private Action alternateAction; */ /** construct a PrevTableCellAction */ public ShiftTabAction(final String actionName) { super(actionName); } /** * construct a PrevTableCellAction * * @param altAction the action to use when the caret * is not inside a table */ /* removed for changes in J2SE 1.4.1 public PrevTableCellAction(Action altAction) { alternateAction = altAction; } */ /** * Moves to the previous cell or invokes an alternate action if the * caret is not inside a table. */ public void actionPerformed(final ActionEvent ae) { // Decrease intent within list if (listManager.caretAtTheBeginningOfListItem()) { listManager.decreaseIndent(true); return; } final Element cell = getCurrentTableCell(); if (cell != null) { goPrevCell(cell); return; } // Decrease intent within list if (listManager.caretWithinListItem()) { listManager.decreaseIndent(true); return; } performDefaultKeyStrokeAction(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK, ae); } } /** * Action to create a new list item. */ private class InsertLineBreakAction extends AbstractAction { /** construct a NewListItemAction */ public InsertLineBreakAction() { } /** * create a new list item, when the caret is inside a list * *

The new item is created after the item at the caret position

*/ public void actionPerformed(final ActionEvent ae) { try { final SHTMLDocument doc = (SHTMLDocument) getDocument(); final int caretPosition = getCaretPosition(); final Element paragraphElement = doc.getParagraphElement(caretPosition); if (paragraphElement != null) { final int so = paragraphElement.getStartOffset(); final int eo = paragraphElement.getEndOffset(); if (so != eo) { final StringWriter writer = new StringWriter(); if (caretPosition > so) { final SHTMLWriter htmlStartWriter = new SHTMLWriter(writer, doc, so, caretPosition - so); htmlStartWriter.writeChildElements(paragraphElement); } // Workaround:
is written twice by Java. if (!doc.getCharacterElement(caretPosition).getName().equalsIgnoreCase(HTML.Tag.BR.toString())) { writer.write("
"); } if (caretPosition < eo - 1) { final SHTMLWriter htmlEndWriter = new SHTMLWriter(writer, doc, caretPosition, eo - caretPosition); htmlEndWriter.writeChildElements(paragraphElement); } final String text = writer.toString(); try { doc.startCompoundEdit(); doc.setInnerHTML(paragraphElement, text); } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } finally { doc.endCompoundEdit(); } setCaretPosition(caretPosition + 1); } } } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } } } public void goNextCell(final Element cell) { if (cell == getLastTableCell(cell)) { appendTableRow(); setCaretPosition(getNextCell(cell).getStartOffset()); } else { setCaretPosition(getNextCell(cell).getStartOffset()); } } public void goPrevCell(Element cell) { int newPos; if (cell != getFirstTableCell(cell)) { cell = getPrevCell(cell); newPos = cell.getStartOffset(); select(newPos, newPos); } } /** * Gets the table cell following the given table cell, continuing * on the next row if the cell is the last one in the row, returning * null if the cell is the last one in the table. * * @param cell the cell whose following cell shall be found * @return the Element having the cell following the given cell or null * if the given cell is the last cell in the table */ private Element getNextCell(final Element cell) { Element nextCell = null; final Element row = cell.getParentElement(); Element nextRow = null; final Element table = row.getParentElement(); final int lastCellIdx = row.getElementCount() - 1; final Element lastCellInRow = row.getElement(lastCellIdx); if (lastCellInRow != cell) { // The cell is not the last one in the row. Element runningCell = lastCellInRow; int cellIdx = lastCellIdx; while ((cellIdx > 0) && (runningCell != cell)) { nextCell = runningCell; runningCell = row.getElement(--cellIdx); } } else { // The cell is the last one in the row. int rowIdx = table.getElementCount() - 1; Element aRow = table.getElement(rowIdx); while (aRow != row) { nextRow = aRow; aRow = table.getElement(--rowIdx); } nextCell = nextRow == null || nextRow.getElementCount() == 0 ? null : nextRow.getElement(0); } return nextCell; } /** * get the table cell preceding a given table cell * * @param cell the cell whose preceding cell shall be found * @return the Element having the cell preceding the given cell or null * if the given cell is the first cell in the table */ private Element getPrevCell(final Element cell) { final Element thisRow = cell.getParentElement(); final Element table = thisRow.getParentElement(); Element prevCell = null; int i = 0; Element aCell = thisRow.getElement(i); if (aCell != cell) { while (aCell != cell) { prevCell = aCell; aCell = thisRow.getElement(i++); } } else { Element prevRow = null; Element aRow = table.getElement(i); while (aRow != thisRow) { prevRow = aRow; aRow = table.getElement(i++); } prevCell = prevRow.getElement(prevRow.getElementCount() - 1); } return prevCell; } /** * get the last cell of the table a given table cell belongs to * * @param cell a cell of the table to get the last cell of * @return the Element having the last table cell */ private Element getLastTableCell(final Element cell) { final Element table = cell.getParentElement().getParentElement(); final Element lastRow = table.getElement(table.getElementCount() - 1); final Element lastCell = lastRow.getElement(lastRow.getElementCount() - 1); return lastCell; } /** * get the first cell of the table a given table cell belongs to * * @param cell a cell of the table to get the first cell of * @return the Element having the first table cell */ private Element getFirstTableCell(final Element cell) { final Element table = cell.getParentElement().getParentElement(); final Element firstCell = table.getElement(0).getElement(0); return firstCell; } /** * Gets the table cell at the current caret position. * * @return the Element having the current table cell or null if * the caret is not inside a table cell */ public Element getCurrentTableCell() { return getTableCell(getCaretPosition()); } /** * */ public Element getCurrentLinkElement() { Element element2 = null; Element element = getSHTMLDocument().getCharacterElement(getSelectionStart()); Object linkAttribute = null; //elem.getAttributes().getAttribute(HTML.Tag.A); Object href = null; while (element != null && linkAttribute == null) { element2 = element; linkAttribute = element.getAttributes().getAttribute(HTML.Tag.A); if (linkAttribute != null) { href = ((AttributeSet) linkAttribute).getAttribute(HTML.Attribute.HREF); } element = element.getParentElement(); } if (linkAttribute != null && href != null) { return element2; } else { return null; } } /** * Gets the table cell element at the given position, or null if none. */ public Element getTableCell(final int position) { final Element element = getSHTMLDocument().getCharacterElement(position); return Util.findElementUp("td", "th", element); } /** Gets the string URL of an existing link, or null if none. */ String getURLOfExistingLink() { //setIgnoreActions(true); final Element linkElement = getCurrentLinkElement(); final boolean foundLink = (linkElement != null); if (!foundLink) { return null; } final AttributeSet elemAttrs = linkElement.getAttributes(); final Object linkAttr = elemAttrs.getAttribute(HTML.Tag.A); final Object href = ((AttributeSet) linkAttr).getAttribute(HTML.Attribute.HREF); return href != null ? href.toString() : null; } /** Gets the paragraph element in which the caret is located. */ public Element getCurrentParagraphElement() { return getSHTMLDocument().getParagraphElement(getCaretPosition()); } @Override public void replaceSelection(String content) { if(content != null){ final String expandedContent = content.replaceAll(TAB, TAB_REPLACEMENT); super.replaceSelection(expandedContent); } else super.replaceSelection(content); } /* ---------- table cell navigation end --------------*/ /** * Replaces the currently selected content with new content * represented by the given HTMLText. If there is no selection * this amounts to an insert of the given text. If there * is no replacement text this amounts to a removal of the * current selection. * This method overrides replaceSelection in JEditorPane for usage * of our own HTMLText object. * * @param replacementHTMLText the content to replace the selection with */ public void replaceSelection(final HTMLText replacementHTMLText) { final SHTMLDocument document = (SHTMLDocument) getDocument(); final Caret caret = getCaret(); if (document != null) { try { final int p0 = Math.min(caret.getDot(), caret.getMark()); final int p1 = Math.max(caret.getDot(), caret.getMark()); if (p0 != p1) { document.remove(p0, p1 - p0); } if (replacementHTMLText != null) { pasteHTML(replacementHTMLText, p0); } } catch (final Exception e) { getToolkit().beep(); } } } /** */ private void pasteHTML(final HTMLText pastedHTMLText, final int position) throws Exception { final SHTMLDocument sDocument = (SHTMLDocument) getDocument(); if (!pastedHTMLText.usesStringRepresenation()) { pastedHTMLText.pasteHTML(sDocument, position); return; } // [ Uses string representation ] String pasteHtmlTextModified = pastedHTMLText.getHTMLText(); final Element characterElement = sDocument.getCharacterElement(position); final Element paragraphElement = characterElement.getParentElement(); if (position == paragraphElement.getStartOffset()) { if (caretWithinTableCell() && pastedHTMLText.isOneCellInOneRow()) { pasteHtmlTextModified = pasteHtmlTextModified.replaceAll("(?ims).*", "").replaceAll( "(?ims).*", ""); } // We are at the start of the paragraph to insert at. if (!HTMLText.containsParagraphTags(pasteHtmlTextModified)) { sDocument.insertAfterStart(paragraphElement, pasteHtmlTextModified); // Remove whitespace before the end tag of paragraph element to avoid quircky behavior. final Element newParagraph = getCurrentParagraphElement(); final String fixedContent = elementToHTML(newParagraph).replaceAll("(?ims)\\s*

", "

"); sDocument.setOuterHTML(newParagraph, fixedContent); // setCaretPosition(newParagraph.getEndOffset() - 1); return; } // Contains paragraph tags. if (caretWithinTableCell() && pasteHtmlTextModified.matches("(?ims).*", "").replaceAll( "(?ims).*", ""); final Element cellElement = getCurrentTableCell(); final Element tableRowElement = cellElement.getParentElement(); sDocument.insertBeforeStart(tableRowElement, strippedHTMLText); return; } // Contains paragraphs tags and // (a) is not within a table cell or // (b) the pasted content is not a table. sDocument.insertBeforeStart(paragraphElement, pasteHtmlTextModified); if (caretWithinTableCell()) { final Element cellElement = getCurrentTableCell(); final Element lastElementInCell = cellElement.getElement(cellElement.getElementCount() - 1); if (elementIsEmptyParagraph(lastElementInCell)) { // Remove empty paragraph at the end of the cell. A workaround. removeElement(lastElementInCell); setCaretPosition(cellElement.getEndOffset() - 1); } } return; } if (paragraphElement.getEndOffset() == position + 1) { // We are at the end of the paragraph to insert at, if (HTMLText.containsParagraphTags(pasteHtmlTextModified)) { sDocument.insertAfterEnd(paragraphElement, pasteHtmlTextModified); } else { sDocument.insertBeforeEnd(paragraphElement, pasteHtmlTextModified); } return; } // We are somewhere else inside the paragraph to insert at. final String newHtml = pastedHTMLText.splitPaste(sDocument, characterElement, paragraphElement, position, pasteHtmlTextModified, HTMLText.containsParagraphTags(pasteHtmlTextModified)); final int paragraphOldEndOffset = paragraphElement.getEndOffset(); final int oldCaretPosition = getCaretPosition(); sDocument.setOuterHTML(paragraphElement, newHtml); final Element newParagraphElement = getSHTMLDocument().getParagraphElement(oldCaretPosition); // Place the caret after the pasted text setCaretPosition(oldCaretPosition + newParagraphElement.getEndOffset() - paragraphOldEndOffset); } /* ------ start of drag and drop implementation ------------------------- (see also constructor of this class) */ /** enables this component to be a Drop Target */ DropTarget dropTarget = null; /** enables this component to be a Drag Source */ DragSource dragSource = null; /** the last selection start */ private int lastSelStart = 0; /** the last selection end */ private int lastSelEnd = 0; /** the location of the last event in the text component */ private int dndEventLocation = 0; /** *

This flag is set by this objects dragGestureRecognizer to indicate that * a drag operation has been started from this object. It is cleared once * dragDropEnd is captured by this object.

* *

If a drop occurs in this object and this object started the drag * operation, then the element to be dropped comes from this object and thus * has to be removed somewhere else in this object.

* *

To the contrary if a drop occurs in this object and the drag operation * was not started in this object, then the element to be dropped does not * come from this object and has not to be removed here.

*/ private boolean dragStartedHere = false; /** * Initialize the drag and drop implementation for this component. * *

DropTarget, DragSource and DragGestureRecognizer are instantiated * and a MouseListener is established to track the selection in drag * operations

* *

this ideally is called in the constructor of a class which * would like to implement drag and drop

*/ public void initDnd() { dropTarget = new DropTarget(this, this); dragSource = new DragSource(); dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this); this.addMouseListener(new MouseAdapter() { public void mouseReleased(final MouseEvent e) { this_mouseReleased(e); } public void mouseClicked(final MouseEvent e) { this_mouseClicked(e); } }); } /** a drag gesture has been initiated */ public void dragGestureRecognized(final DragGestureEvent event) { final int selStart = getSelectionStart(); try { if ((lastSelEnd > lastSelStart) && (selStart >= lastSelStart) && (selStart < lastSelEnd)) { dragStartedHere = true; select(lastSelStart, lastSelEnd); final HTMLText text = new HTMLText(); final int start = getSelectionStart(); text.copyHTML(this, start, getSelectionEnd() - start); final HTMLTextSelection trans = new HTMLTextSelection(text); dragSource.startDrag(event, DragSource.DefaultMoveDrop, trans, this); } } catch (final Exception e) { //getToolkit().beep(); } } /** * this message goes to DragSourceListener, informing it that the dragging * has ended */ public void dragDropEnd(final DragSourceDropEvent event) { dragStartedHere = false; } /** is invoked when a drag operation is going on */ public void dragOver(final DropTargetDragEvent event) { dndEventLocation = viewToModel(event.getLocation()); try { setCaretPosition(dndEventLocation); } catch (final Exception e) { //getToolkit().beep(); } } /** * a drop has occurred. If the dragged element has a suitable * DataFlavor, do the drop. * * @param event - the event specifiying the drop operation * @see java.awt.datatransfer.DataFlavor */ public void drop(final DropTargetDropEvent event) { dndEventLocation = viewToModel(event.getLocation()); if ((dndEventLocation >= lastSelStart) && (dndEventLocation <= lastSelEnd)) { event.rejectDrop(); select(lastSelStart, lastSelEnd); } else { final SHTMLDocument doc = (SHTMLDocument) getDocument(); doc.startCompoundEdit(); try { final Transferable transferable = event.getTransferable(); /* if (getPasteMode() == PasteMode.PASTE_PLAIN_TEXT) { event.acceptDrop(DnDConstants.ACTION_MOVE); final String content = transferable.getTransferData(DataFlavor.stringFlavor).toString(); doDrop(event, content); } else */ if (transferable.isDataFlavorSupported(htmlTextDataFlavor)) { event.acceptDrop(DnDConstants.ACTION_MOVE); final HTMLText s = (HTMLText) transferable.getTransferData(htmlTextDataFlavor); doDrop(event, s); } else if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) { event.acceptDrop(DnDConstants.ACTION_MOVE); final String s = (String) transferable.getTransferData(DataFlavor.stringFlavor); doDrop(event, s); } else { event.rejectDrop(); } } catch (final Exception exception) { exception.printStackTrace(); event.rejectDrop(); } finally { doc.endCompoundEdit(); } } } /** * do the drop operation consisting of adding the dragged element and * necessarily removing the dragged element from the original position */ private void doDrop(final DropTargetDropEvent event, final Object s) { int removeOffset = 0; int moveOffset = 0; int newSelStart; int newSelEnd; setCaretPosition(dndEventLocation); if (s instanceof HTMLText) { replaceSelection((HTMLText) s); } else if (s instanceof String) { replaceSelection((String) s); } if (dndEventLocation < lastSelStart) { removeOffset = s.toString().length(); } else { moveOffset = s.toString().length(); } newSelEnd = dndEventLocation + (lastSelEnd - lastSelStart) - moveOffset; newSelStart = dndEventLocation - moveOffset; if (dragStartedHere) { lastSelStart += removeOffset; lastSelEnd += removeOffset; select(lastSelStart, lastSelEnd); replaceSelection(""); } lastSelEnd = newSelEnd; lastSelStart = newSelStart; select(lastSelStart, lastSelEnd); event.getDropTargetContext().dropComplete(true); } /** A groupping of list operations, mostly related to toggling of lists. */ private class ListManager { //private Element parentElement; class SwitchListException extends Exception { } private ListManager() { } private Element getParagraphElement(final int pos) { Element paragraphElement = getSHTMLDocument().getParagraphElement(pos); if (paragraphElement.getName().equalsIgnoreCase("p-implied")) { paragraphElement = paragraphElement.getParentElement(); } return paragraphElement; } /** */ private Element getListParent(final Element elem) { Element listParent = elem.getParentElement(); if (elem.getName().equalsIgnoreCase(HTML.Tag.LI.toString())) { listParent = listParent.getParentElement(); } return listParent; } /** Gets the list item element at the caret position, or null if the position * is not within a list. */ private Element getListItemElement(final int caretPosition) { final Element paragraphElement = getParagraphElement(caretPosition); return Util.findElementUp(HTML.Tag.LI.toString(), paragraphElement); } /** Gets the innermost list element at the caret position, or null if the position is not * within a list. */ private Element getListElement(final int caretPosition) { final Element paragraphElement = getParagraphElement(caretPosition); final Element listElement = Util.findElementUp("ul", "ol", paragraphElement); return listElement; } private boolean isValidParentElement(final Element e) { final String name = e.getName(); return name.equalsIgnoreCase(HTML.Tag.BODY.toString()) || name.equalsIgnoreCase(HTML.Tag.TD.toString()) || name.equalsIgnoreCase(HTML.Tag.LI.toString()); } private boolean isValidElement(final Element e) { final String name = e.getName(); return name.equalsIgnoreCase(HTML.Tag.P.toString()) || name.equalsIgnoreCase(HTML.Tag.UL.toString()) || name.equalsIgnoreCase(HTML.Tag.OL.toString()) || name.equalsIgnoreCase(HTML.Tag.H1.toString()) || name.equalsIgnoreCase(HTML.Tag.H2.toString()) || name.equalsIgnoreCase(HTML.Tag.H3.toString()) || name.equalsIgnoreCase(HTML.Tag.H4.toString()) || name.equalsIgnoreCase(HTML.Tag.H5.toString()) || name.equalsIgnoreCase(HTML.Tag.H6.toString()); } private boolean isListRootElement(final Element e) { final String name = e.getName(); return name.equalsIgnoreCase(HTML.Tag.UL.toString()) || name.equalsIgnoreCase(HTML.Tag.OL.toString()); } /** * Switches OFF list formatting for a given block of elements. * *

Switches off all list formatting inside the block for the * given tag.

* *

Splits lists if the selection covers only part of a list.

* @throws BadLocationException * @throws IOException */ private void listOff() throws IOException, BadLocationException { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); final Element firstParagraphElement = getParagraphElement(selectionStart); final int fistParagraphElementStart = firstParagraphElement.getStartOffset(); final int lastParagraphElementEnd = getParagraphElement(selectionEnd).getEndOffset(); final Element parentOfTheListElement = getListParent(firstParagraphElement); if (isListElement(parentOfTheListElement)) { // Currently, no support for nested lists. return; } final SHTMLWriter sHTMLwriter = new SHTMLWriter(getSHTMLDocument()); int elementIdx; Element nextElement = null; for (elementIdx = 0; elementIdx < parentOfTheListElement.getElementCount(); elementIdx++) { nextElement = parentOfTheListElement.getElement(elementIdx); if (nextElement.getEndOffset() > fistParagraphElementStart) { break; } } final Element startElementToBeReplaced = nextElement; int nrElementsToBeReplaced = 1; int listItemIdx = 0; Element listItemElement = null; if (nextElement.getStartOffset() < fistParagraphElementStart) { sHTMLwriter.writeStartTag(nextElement); elementIdx++; // Write list item elements before the selection. for (;; listItemIdx++) { listItemElement = nextElement.getElement(listItemIdx); if (listItemElement.getStartOffset() == fistParagraphElementStart) { break; } sHTMLwriter.write(listItemElement); } sHTMLwriter.writeEndTag(nextElement); // Turn the list item elements in the selection into paragraph element. for (; listItemIdx < nextElement.getElementCount(); listItemIdx++) { listItemElement = nextElement.getElement(listItemIdx); if (listItemElement.getStartOffset() >= lastParagraphElementEnd) { break; } sHTMLwriter.writeStartTag("p", null); sHTMLwriter.writeChildElements(listItemElement); sHTMLwriter.writeEndTag("p"); } } if (nextElement.getEndOffset() <= lastParagraphElementEnd) { for (; elementIdx < parentOfTheListElement.getElementCount(); elementIdx++) { nextElement = parentOfTheListElement.getElement(elementIdx); if (nextElement.getEndOffset() > lastParagraphElementEnd) { break; } if (nextElement != startElementToBeReplaced && nextElement.getStartOffset() < lastParagraphElementEnd) { nrElementsToBeReplaced++; } if (isListRootElement(nextElement)) { for (listItemIdx = 0; listItemIdx < nextElement.getElementCount(); listItemIdx++) { sHTMLwriter.writeStartTag("p", null); sHTMLwriter.writeChildElements(nextElement.getElement(listItemIdx)); sHTMLwriter.writeEndTag("p"); } } else { sHTMLwriter.writeStartTag("p", null); sHTMLwriter.writeChildElements(nextElement); sHTMLwriter.writeEndTag("p"); } } } // nextElement is final at this point. if (elementIdx <= parentOfTheListElement.getElementCount() && nextElement.getStartOffset() < lastParagraphElementEnd) { if (nextElement != startElementToBeReplaced) { nrElementsToBeReplaced++; } for (; listItemIdx < nextElement.getElementCount(); listItemIdx++) { listItemElement = nextElement.getElement(listItemIdx); if (listItemElement.getStartOffset() >= lastParagraphElementEnd) { break; } sHTMLwriter.writeStartTag("p", null); sHTMLwriter.writeChildElements(listItemElement); sHTMLwriter.writeEndTag("p"); } if (listItemIdx < nextElement.getElementCount()) { sHTMLwriter.writeStartTag(nextElement); for (; listItemIdx < nextElement.getElementCount(); listItemIdx++) { listItemElement = nextElement.getElement(listItemIdx); sHTMLwriter.write(listItemElement); } sHTMLwriter.writeEndTag(nextElement); } } getSHTMLDocument().replaceHTML(startElementToBeReplaced, nrElementsToBeReplaced, sHTMLwriter.getWrittenString()); } /** * switch ON list formatting for a given block of elements. * *

Takes care of merging existing lists before, after and inside * respective element block.

* @throws BadLocationException * @throws IOException * */ private void listOn(final String listTag, final AttributeSet attributeSet, final boolean forceOff) throws IOException, BadLocationException { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); final Element firstParagraphElement = getParagraphElement(selectionStart); int fistParagraphElementStart = firstParagraphElement.getStartOffset(); int lastParagraphElementEnd = getParagraphElement(selectionEnd).getEndOffset(); final Element parentElement = getListParent(firstParagraphElement); Element startElementToBeRemoved = null; int removeCount = 0; final SHTMLWriter writer = new SHTMLWriter(getSHTMLDocument()); if (fistParagraphElementStart > 0) { final Element before = getParagraphElement(fistParagraphElementStart - 1); if (before.getName().equalsIgnoreCase(HTML.Tag.LI.toString())) { final Element listRoot = before.getParentElement(); if (listRoot.getParentElement() == parentElement && listRoot.getName().equalsIgnoreCase(listTag)) { fistParagraphElementStart = listRoot.getStartOffset(); } } } if (lastParagraphElementEnd < getSHTMLDocument().getLength() - 1) { final Element after = getParagraphElement(lastParagraphElementEnd); if (after.getName().equalsIgnoreCase(HTML.Tag.LI.toString())) { final Element listRoot = after.getParentElement(); if (listRoot.getParentElement() == parentElement && listRoot.getName().equalsIgnoreCase(listTag)) { lastParagraphElementEnd = listRoot.getEndOffset(); } } } int i; Element next = null; for (i = 0; i < parentElement.getElementCount(); i++) { next = parentElement.getElement(i); if (next.getEndOffset() > fistParagraphElementStart) { break; } } startElementToBeRemoved = next; removeCount = 1; int j = 0; Element li = null; if (next.getStartOffset() < fistParagraphElementStart) { i++; writer.writeStartTag(next); for (;; j++) { li = next.getElement(j); if (li.getStartOffset() == fistParagraphElementStart) { break; } writer.write(li); } writer.writeEndTag(next); writer.writeStartTag(listTag, attributeSet); for (; j < next.getElementCount(); j++) { li = next.getElement(j); if (li.getStartOffset() >= lastParagraphElementEnd) { break; } writer.write(li); } } else { writer.writeStartTag(listTag, attributeSet); } if (next.getEndOffset() <= lastParagraphElementEnd) { for (; i < parentElement.getElementCount(); i++) { next = parentElement.getElement(i); if (next.getEndOffset() > lastParagraphElementEnd) { break; } if (startElementToBeRemoved != next && next.getStartOffset() < lastParagraphElementEnd) { removeCount++; } if (isListRootElement(next)) { writer.writeChildElements(next); } else { writer.writeStartTag("li", null); writer.writeChildElements(next); writer.writeEndTag("li"); } } } if (i < parentElement.getElementCount() && next.getStartOffset() < lastParagraphElementEnd) { if (startElementToBeRemoved != next) { removeCount++; } for (; j < next.getElementCount(); j++) { li = next.getElement(j); if (li.getStartOffset() >= lastParagraphElementEnd) { break; } writer.write(li); } writer.writeEndTag(listTag); if (j < next.getElementCount()) { writer.writeStartTag(next); for (; j < next.getElementCount(); j++) { li = next.getElement(j); writer.write(li); } writer.writeEndTag(next); } } else { writer.writeEndTag(listTag); } getSHTMLDocument().replaceHTML(startElementToBeRemoved, removeCount, writer.getWrittenString()); } /** * decide to switch on or off list formatting * @return true, if list formatting is to be switched on, false if not * @throws SwitchListException */ private boolean switchOn(final String listTag, final AttributeSet attributeSet, final boolean forceOff, final int start, final int end, final Element parentElement) throws SwitchListException { boolean listOn = false; final int count = parentElement.getElementCount(); for (int i = 0; i < count && !listOn; i++) { final Element elem = parentElement.getElement(i); if (elem.getStartOffset() >= start && elem.getEndOffset() <= end && !isValidElement(elem)) { throw new SwitchListException(); } final int eStart = elem.getStartOffset(); final int eEnd = elem.getEndOffset(); if (!elem.getName().equalsIgnoreCase(listTag)) { if (((eStart > start) && (eStart < end)) || ((eEnd > start) && (eEnd < end)) || ((start >= eStart) && (end <= eEnd))) { listOn = true; } } } return listOn; } /** */ private void toggleList(final String listTag, final AttributeSet attributeSet, final boolean forceOff) { try { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); final Element firstParagraphElement = getParagraphElement(selectionStart); final int fistParagraphElementStart = firstParagraphElement.getStartOffset(); final int lastParagraphElementEnd = getParagraphElement(selectionEnd).getEndOffset(); final Element parentElement = getListParent(firstParagraphElement); getSHTMLDocument().startCompoundEdit(); // Why is OL not a valid parent element? How could a parent element be invalid? --Dan //if (!isValidParentElement(parentElement)) // throw new SwitchListException(); if (selectionStart != selectionEnd) { final Element last = getParagraphElement(lastParagraphElementEnd - 1); if (parentElement != getListParent(last)) { throw new SwitchListException(); } } // if (!switchOn(listTag, attributeSet, forceOff, fistParagraphElementStart, lastParagraphElementEnd, parentElement) || forceOff) { listOff(); } else { listOn(listTag, attributeSet, forceOff); } if (selectionStart == selectionEnd) { setCaretPosition(selectionStart); } else { select(selectionStart, selectionEnd); } requestFocus(); } catch (final SwitchListException e) { } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } finally { getSHTMLDocument().endCompoundEdit(); } } /** */ private boolean isListElement(final Element element) { return "ul".equalsIgnoreCase(element.getName()) || "ol".equalsIgnoreCase(element.getName()); } /** */ private boolean isListItemElement(final Element element) { return "li".equalsIgnoreCase(element.getName()); } /** Determines whether the caret is currently at the beginning of a list item. */ private boolean caretAtTheBeginningOfListItem() { final Element parent = getCurrentParagraphElement().getParentElement(); return ("li".equalsIgnoreCase(parent.getName()) && getCaretPosition() == parent.getStartOffset()); } /** Determines whether the caret is currently at the beginning of a list item. */ private boolean caretWithinListItem() { final Element currentParagraphElement = getCurrentParagraphElement(); if(currentParagraphElement == null) return false; final Element parent = currentParagraphElement.getParentElement(); return parent != null && "li".equalsIgnoreCase(parent.getName()); } /** Increases the intent of selected list items. ("Including subitems" should default to "true".) */ private void increaseIndent(final boolean includingSubitems) { final Element paragraphElement = getCurrentParagraphElement(); final Element listItemElement = paragraphElement.getParentElement(); if (!isListItemElement(listItemElement)) { return; } // Increase indent int selectionStart = getSelectionStart(); int selectionEnd = getSelectionEnd(); if (selectionStart != selectionEnd && getListElement(selectionStart) == getListElement(selectionEnd)) { // Of block } else { // Of single list item selectionStart = getCaretPosition(); selectionEnd = getCaretPosition(); } // final Element list = getListElement(selectionStart); final int indexOfSelectionStart = list.getElementIndex(selectionStart); final int indexOfSelectionEnd = list.getElementIndex(selectionEnd); try { getSHTMLDocument().startCompoundEdit(); final SHTMLWriter writer = new SHTMLWriter(getSHTMLDocument()); writer.writeStartTag(list); for (int i = 0; i < indexOfSelectionStart - 1; i++) { writer.write(list.getElement(i)); } Element tagModel = null; if (indexOfSelectionStart > 0 && isListElement(list.getElement(indexOfSelectionStart - 1))) { tagModel = list.getElement(indexOfSelectionStart - 1); } else if (indexOfSelectionEnd + 1 < list.getElementCount() && isListElement(list.getElement(indexOfSelectionEnd + 1))) { tagModel = list.getElement(indexOfSelectionEnd + 1); } else { tagModel = list; } // The list item before the selection start should be the new parent. if (indexOfSelectionStart == 0) { // Cannot increase indent of the first item in a list, unlike in MSO and OOo. return; } final Element newParentListItem = list.getElement(indexOfSelectionStart - 1); writer.writeStartTag(newParentListItem); writer.writeChildElements(newParentListItem); // writer.writeStartTag(tagModel); for (int i = indexOfSelectionStart; i <= indexOfSelectionEnd; i++) { if (includingSubitems) { writer.write(list.getElement(i)); } else { final Element listItem = list.getElement(i); writer.writeStartTag(listItem); for (int j = 0; j < listItem.getElementCount(); j++) { if (!isListElement(listItem.getElement(j))) { writer.write(listItem.getElement(j)); } } writer.writeEndTag(listItem); for (int j = 0; j < listItem.getElementCount(); j++) { if (isListElement(listItem.getElement(j))) { writer.writeChildElements(listItem.getElement(j)); } } } } writer.writeEndTag(tagModel); writer.writeEndTag(newParentListItem); // for (int i = indexOfSelectionEnd + 1; i < list.getElementCount(); i++) { writer.write(list.getElement(i)); } writer.writeEndTag(list); final String newContent = writer.getWrittenString().replaceAll("(?ims)\\s*]*>", "") .replaceAll("(?ims)\\s*]*>", ""); getSHTMLDocument().setOuterHTML(list, newContent); select(selectionStart, selectionEnd); } catch (final Exception ex) { } finally { getSHTMLDocument().endCompoundEdit(); } } /** Decreases the indent of selected list items. ("Including subitems" should default to "true". */ private void decreaseIndent(final boolean includingSubitems) { // The parameter "includingSubitems should always be set to "true". The value of "false" currently causes // problems, in that it leads to the creation of incorrect HTML such as //
    • c
; the consecutive ULs are incorrect. // Example: // * a <-- Outer list item, beginning the outer list. // * b <-- Inner list. Cursor here, before "b". // * c // * d // paragraphElement: P or P-implied. // parent: LI (b) // list: UL (containing LI (b)) // outerListItem: LI (a) // outerList: UL (containing LI (a) and the rest.) // Result, with "including subitems" set to "true": // * a // * b // * c // * d final Element paragraphElement = getCurrentParagraphElement(); final Element parent = paragraphElement.getParentElement(); // Guard 1 if (!isListItemElement(parent)) { return; } // Guard 2 final Element list = parent.getParentElement(); final Element outerListItem = list.getParentElement(); if (!isListItemElement(outerListItem)) { return; // Already the outermost item. } final Element outerList = outerListItem.getParentElement(); // Decrease indent int selectionStart = getSelectionStart(); int selectionEnd = getSelectionEnd(); if (selectionStart != selectionEnd && getListElement(selectionStart) == getListElement(selectionEnd)) { // Of block } else { // Of single list item selectionStart = getCaretPosition(); selectionEnd = getCaretPosition(); } final int indexOfSelectionStart = list.getElementIndex(selectionStart); final int indexOfSelectionEnd = list.getElementIndex(selectionEnd); final int indexOfSelectionInOuterItem = outerListItem.getElementIndex(selectionStart); final int indexOfSelectionInOuterList = outerList.getElementIndex(selectionStart); try { getSHTMLDocument().startCompoundEdit(); final SHTMLWriter writer = new SHTMLWriter(getSHTMLDocument()); // Write the beginning of the outer list writer.writeStartTag(outerList); for (int i = 0; i < indexOfSelectionInOuterList; i++) { writer.write(outerList.getElement(i)); } // Write the beginning of the outer list item writer.writeStartTag(outerListItem); for (int i = 0; i < indexOfSelectionInOuterItem; i++) { writer.write(outerListItem.getElement(i)); } // Write the beginning of the inner list if (indexOfSelectionStart > 0) { writer.writeStartTag(list); for (int i = 0; i < indexOfSelectionStart; i++) { writer.write(list.getElement(i)); } writer.writeEndTag(list); } // Close the outer list item writer.writeEndTag(outerListItem); // Write the promoted (moved to the left) items except for the last one for (int i = indexOfSelectionStart; i <= indexOfSelectionEnd - 1; i++) { if (includingSubitems) { writer.write(list.getElement(i)); } else { writeListItemForDecreaseIndent(writer, list.getElement(i), includingSubitems, false); } } // Write the end of the inner list into the last promoted item writeListItemForDecreaseIndent(writer, list.getElement(indexOfSelectionEnd), includingSubitems, /*withoutEndTag=*/ true); if ((indexOfSelectionEnd + 1) < list.getElementCount()) { writer.writeStartTag(list); for (int i = indexOfSelectionEnd + 1; i < list.getElementCount(); i++) { writer.write(list.getElement(i)); } writer.writeEndTag(list); } writer.writeEndTag(list.getElement(indexOfSelectionEnd)); // Write the end of the outer list item, as another list item if (indexOfSelectionInOuterItem + 1 < outerListItem.getElementCount()) { writer.writeStartTag(outerListItem); for (int i = indexOfSelectionInOuterItem + 1; i < outerListItem.getElementCount(); i++) { writer.write(outerListItem.getElement(i)); } writer.writeEndTag(outerListItem); } // Write the end of the outer list for (int i = indexOfSelectionInOuterList + 1; i < outerList.getElementCount(); i++) { writer.write(outerList.getElement(i)); } writer.writeEndTag(outerList); final String newContent = writer.getWrittenString().replaceAll("(?ims)\\s*
    ", "") .replaceAll("(?ims)\\s*
      ", ""); getSHTMLDocument().setOuterHTML(outerList, newContent); select(selectionStart, selectionEnd); } catch (final Exception ex) { } finally { getSHTMLDocument().endCompoundEdit(); } } /** Writes the list item for the "descrease indent" action. */ private void writeListItemForDecreaseIndent(final SHTMLWriter writer, final Element listItem, final boolean includingSubitems, final boolean withoutEndTag) { try { writer.writeStartTag(listItem); if (includingSubitems) { writer.writeChildElements(listItem); } else { boolean childListItemsPresent = false; // Write all child elements that are not list items for (int j = 0; j < listItem.getElementCount(); j++) { if (isListElement(listItem.getElement(j))) { childListItemsPresent = true; } else { writer.write(listItem.getElement(j)); } } // if (childListItemsPresent) { writer.writeStartTag(listItem.getParentElement()); for (int j = 0; j < listItem.getElementCount(); j++) { if (isListElement(listItem.getElement(j))) { writer.write(listItem.getElement(j)); } } writer.writeEndTag(listItem.getParentElement()); } } if (!withoutEndTag) { writer.writeEndTag(listItem); } } catch (final Exception ex) { } } /** Performs the action appropriate on pressing of the key delete, as far as lists * are concerned, treating those cases that Java handles poorly. Returns whether * intervention was necessary. */ private boolean deleteNextChar(final ActionEvent actionEvent) { final int nextPosition = getCaretPosition() + 1; final SHTMLDocument doc = getSHTMLDocument(); final Element listAtNextPosition = getListElement(nextPosition); if (listAtNextPosition != null && listAtNextPosition.getStartOffset() == nextPosition) { // List element starting at the next position. if (!caretWithinListItem()) { if (elementIsEmptyParagraph(getCurrentParagraphElement())) { return false; } mergeSecondElementIntoFirst(getParagraphElement(getCaretPosition()), getListItemElement(nextPosition)); return true; } else { // Caret within list item. final Element listAtCaret = getListElement(getCaretPosition()); final boolean isSurroundingList = listAtCaret.getStartOffset() <= listAtNextPosition .getStartOffset() && listAtCaret.getEndOffset() >= listAtNextPosition.getEndOffset(); if (isSurroundingList) { mergeNestedListItemIntoParent(getListItemElement(getCaretPosition()), getListItemElement(nextPosition)); return true; } else { // Two adjacent lists. To be merged. return false; } } } // Empty paragraph within a list item. if (caretWithinListItem()) { final Element paragraphElement = getCurrentParagraphElement(); final boolean emptyParagraph = elementIsEmptyParagraph(paragraphElement); if (emptyParagraph) { // Remove the empty paragraph manually, to avoid quircks. removeElement(paragraphElement); //setCaretPosition(getCaretPosition() - 1); return true; } } // List item element starting at the next position final Element listItem = getListItemElement(nextPosition); if (listItem != null && listItem.getStartOffset() == nextPosition) { mergeSecondElementIntoFirst(getListItemElement(getCaretPosition()), listItem); return true; } final Element listElementAtCurrentPosition = getListElement(getCaretPosition()); final Element parentElementOfNextPosition = doc.getParagraphElement(nextPosition).getParentElement(); if (listElementAtCurrentPosition != null && !isListItemElement(parentElementOfNextPosition)) { // A non-list after a list. // Avoid currently buggy behavior. A workaround. if ("body".equalsIgnoreCase(parentElementOfNextPosition.getName()) || getTableCell(getCaretPosition()) == getTableCell(nextPosition)) { mergeSecondElementIntoFirst(getListItemElement(getCaretPosition()), getParagraphElement(nextPosition)); } else { performDefaultKeyStrokeAction(KeyEvent.VK_RIGHT, 0, actionEvent); } return true; } return false; } /** Performs the action appropriate on pressing of the key backspace, as far as lists * are concerned, treating those cases that Java's Swing handles poorly. Returns whether * intervention was necessary. */ private boolean deletePrevChar(final ActionEvent actionEvent) { final int selectionStart = getSelectionStart(); final Element list = getListElement(selectionStart); if (list != null && list.getStartOffset() == selectionStart) { // A list starts at the caret position. final Element listAtPrevPosition = selectionStart == 0 ? null : getListElement(selectionStart - 1); if (listAtPrevPosition == null) { performToggleListAction(actionEvent, list.getName()); return true; } final boolean isSurroundingList = listAtPrevPosition.getStartOffset() <= list.getStartOffset() && listAtPrevPosition.getEndOffset() >= list.getEndOffset(); if (isSurroundingList) { mergeNestedListItemIntoParent(getListItemElement(selectionStart - 1), getListItemElement(selectionStart)); return true; } else { // Two adjacent lists. To be merged. return false; } } // Empty paragraph within a list item. if (caretWithinListItem()) { final Element paragraphElement = getCurrentParagraphElement(); final boolean emptyParagraph = elementIsEmptyParagraph(paragraphElement); if (emptyParagraph) { // Remove the empty paragraph manually, to avoid quirks. removeElement(paragraphElement); setCaretPosition(getCaretPosition() - 1); return true; } } final Element listItem = getListItemElement(selectionStart); if (listItem != null && listItem.getStartOffset() == selectionStart) { // List item starts at the current position. final int previousPosition = listItem.getStartOffset() - 1; mergeSecondElementIntoFirst(getListItemElement(previousPosition), listItem); setCaretPosition(previousPosition); return true; } if (selectionStart > 0) { final int previousPosition = selectionStart - 1; final Element listElementAtPreviousPosition = getListElement(previousPosition); final Element parentElement = getCurrentParagraphElement().getParentElement(); if (listElementAtPreviousPosition != null && !"li".equalsIgnoreCase(parentElement.getName())) { // A non-list after a list. // Avoid currently buggy behavior. A workaround. if ("body".equalsIgnoreCase(parentElement.getName()) || getTableCell(previousPosition) == getTableCell(selectionStart)) { mergeSecondElementIntoFirst(getListItemElement(previousPosition), getParagraphElement(selectionStart)); } else { performDefaultKeyStrokeAction(KeyEvent.VK_LEFT, 0, actionEvent); } return true; } } return false; } /** Determines whether a list item element contains nested list items. */ private boolean containsNestedListItems(final Element listItem) { for (int i = 0; i < listItem.getElementCount(); i++) { if (isListElement(listItem.getElement(i))) { return true; } } return false; } /** Merges the second paragraph element into the first one, * setting the caret at the point where the two elements are newly joined. */ private void mergeSecondElementIntoFirst(final Element first, final Element second) { final SHTMLDocument doc = (SHTMLDocument) getDocument(); final SHTMLWriter writer = new SHTMLWriter(doc); doc.startCompoundEdit(); try { int finalCaretPosition = first.getEndOffset() - 1; writer.writeStartTag(first); writer.writeChildElements(first); writer.removeLastWrittenNewline(); writer.writeChildElements(second); writer.writeEndTag(first); getSHTMLDocument().setOuterHTML(first, writer.getWrittenString()); removeElement(second); finalCaretPosition = Math.min(finalCaretPosition, first.getEndOffset() - 1); setCaretPosition(finalCaretPosition); } catch (final IOException e) { e.printStackTrace(); } catch (final BadLocationException e) { e.printStackTrace(); } finally { doc.endCompoundEdit(); } } /** Merges a nested list item into a list item in the parent list. (Inaccurate description.) * See also {@link #mergeSecondElementIntoFirst(Element first, Element second)}. * @param parentListItem a list item * @param childListItem a list item */ private void mergeNestedListItemIntoParent(final Element parentListItem, final Element childListItem) { final SHTMLDocument doc = (SHTMLDocument) getDocument(); final SHTMLWriter writer = new SHTMLWriter(doc); doc.startCompoundEdit(); try { if (!isListItemElement(parentListItem)) { return; } final int finalCaretPosition = parentListItem.getElement(0).getEndOffset() - 1; writer.writeStartTag(parentListItem); for (int i = 0; i < parentListItem.getElementCount(); i++) { if (!isListElement(parentListItem.getElement(i))) { writer.write(parentListItem.getElement(i)); } } writer.removeLastWrittenNewline(); writer.writeChildElements(childListItem); for (int i = 0; i < parentListItem.getElementCount(); i++) { if (isListElement(parentListItem.getElement(i))) { // Write the nested list except for the merged child. final Element list = parentListItem.getElement(i); if (list.getElementCount() >= 2) { // Write the nested list only if it had at least two elements. writer.writeStartTag(list); for (int j = 0; j < list.getElementCount(); j++) { if (list.getElement(j) != childListItem) { writer.write(list.getElement(j)); } } writer.writeEndTag(list); } } } writer.writeEndTag(parentListItem); getSHTMLDocument().setOuterHTML(parentListItem, writer.getWrittenString()); setCaretPosition(finalCaretPosition); } catch (final IOException e) { e.printStackTrace(); } catch (final BadLocationException e) { e.printStackTrace(); } finally { doc.endCompoundEdit(); } } /** Inserts a new list item after the current list item, breaking the text in the list * item into two parts if the caret is not at the end of the current list item. */ private void newListItem() { final SHTMLDocument doc = getSHTMLDocument(); final int caretPosition = getCaretPosition(); final Element listItemElement = listManager.getListItemElement(caretPosition); if (listItemElement == null) { return; } try { final String listItemContent = elementToHTML(listItemElement); if (listItemContent.matches("(?ims)\\s*]*>\\s*\\s*")) { // Empty list item, so switch the list off. toggleList("", null, true); return; } final int so = listItemElement.getStartOffset(); final int eo = listItemElement.getEndOffset(); if (so != eo) { final StringWriter stringWriter = new StringWriter(); final SHTMLWriter writer = new SHTMLWriter(stringWriter, doc); writer.writeStartTag(listItemElement); if (caretPosition > so) { final SHTMLWriter htmlStartWriter = new SHTMLWriter(stringWriter, doc, so, caretPosition - so); htmlStartWriter.writeChildElements(listItemElement); } writer.writeEndTag(listItemElement); writer.writeStartTag(listItemElement); if (containsNestedListItems(listItemElement) && (listItemElement.getElement(0).getEndOffset() - 1) == caretPosition) { // Contains nested list and the caret is at the end of the list items's paragraph. // Let it be. Workaround for the user: press undo. // ---Old:--- // Block the action. User can enter new item by pressing enter when the caret // is not at the end of the list item. //return; //stringWriter.write("

      "); // A workaround. Otherwise, the caret cannot reach the position in the new // list item. Ideally, Java's editor kit would create implied paragraph, which // it does not. } if (caretPosition < eo - 1) { final SHTMLWriter htmlEndWriter = new SHTMLWriter(stringWriter, doc, caretPosition, eo - caretPosition); htmlEndWriter.writeChildElements(listItemElement); } writer.writeEndTag(listItemElement); final String text = stringWriter.toString(); try { doc.startCompoundEdit(); doc.setOuterHTML(listItemElement, text); } catch (final Exception e) { Util.errMsg(null, e.getMessage(), e); } finally { doc.endCompoundEdit(); } setCaretPosition(caretPosition + 1); } } catch (final Exception ex) { } } } /** remember current selection when mouse button is released */ void this_mouseReleased(final MouseEvent e) { lastSelStart = getSelectionStart(); lastSelEnd = getSelectionEnd(); } /** remember current selection when mouse button is double clicked */ void this_mouseClicked(final MouseEvent e) { if (e.getClickCount() > 1) { lastSelStart = getSelectionStart(); lastSelEnd = getSelectionEnd(); } } /** is invoked if the user modifies the current drop gesture */ public void dropActionChanged(final DropTargetDragEvent event) { } /** is invoked when the user changes the dropAction */ public void dropActionChanged(final DragSourceDragEvent event) { } /** is invoked when you are dragging over the DropSite */ public void dragEnter(final DropTargetDragEvent event) { } /** is invoked when you are exit the DropSite without dropping */ public void dragExit(final DropTargetEvent event) { } /** * this message goes to DragSourceListener, informing it that the dragging * has entered the DropSite */ public void dragEnter(final DragSourceDragEvent event) { } /** * this message goes to DragSourceListener, informing it that the dragging * has exited the DropSite */ public void dragExit(final DragSourceEvent event) { } /** * this message goes to DragSourceListener, informing it that * the dragging is currently ocurring over the DropSite */ public void dragOver(final DragSourceDragEvent event) { } // ------ end of drag and drop implementation ---------------------------- /* ------ start of cut, copy and paste implementation ------------------- */ public TransferHandler getTransferHandler() { final TransferHandler defaultTransferHandler = super.getTransferHandler(); if (defaultTransferHandler == null) { return null; } class LocalTransferHandler extends TransferHandler { /* (non-Javadoc) * @see javax.swing.TransferHandler#canImport(javax.swing.JComponent, java.awt.datatransfer.DataFlavor[]) */ public boolean canImport(final JComponent comp, final DataFlavor[] transferFlavors) { return defaultTransferHandler.canImport(comp, transferFlavors); } /* (non-Javadoc) * @see javax.swing.TransferHandler#exportAsDrag(javax.swing.JComponent, java.awt.event.InputEvent, int) */ public void exportAsDrag(final JComponent comp, final InputEvent e, final int action) { defaultTransferHandler.exportAsDrag(comp, e, action); } /* (non-Javadoc) * @see javax.swing.TransferHandler#exportToClipboard(javax.swing.JComponent, java.awt.datatransfer.Clipboard, int) */ public void exportToClipboard(final JComponent comp, final Clipboard clip, final int action) { final SHTMLDocument document = (SHTMLDocument) getDocument(); if (document.getParagraphElement(getSelectionStart()) != document .getParagraphElement(getSelectionEnd())) { defaultTransferHandler.exportToClipboard(comp, clip, action); return; } try { final HTMLText htmlText = new HTMLText(); final int start = getSelectionStart(); htmlText.copyHTML(SHTMLEditorPane.this, start, getSelectionEnd() - start); final Transferable additionalContents = new HTMLTextSelection(htmlText); final Clipboard temp = new Clipboard(""); defaultTransferHandler.exportToClipboard(comp, temp, action); final Transferable defaultContents = temp.getContents(this); if (defaultContents == null) { return; } clip.setContents(new Transferable() { public DataFlavor[] getTransferDataFlavors() { final DataFlavor[] defaultFlavors = defaultContents.getTransferDataFlavors(); final DataFlavor[] additionalFlavors = additionalContents.getTransferDataFlavors(); final DataFlavor[] resultFlavor = new DataFlavor[defaultFlavors.length + additionalFlavors.length]; System.arraycopy(defaultFlavors, 0, resultFlavor, 0, defaultFlavors.length); System.arraycopy(additionalFlavors, 0, resultFlavor, defaultFlavors.length, additionalFlavors.length); return resultFlavor; } public boolean isDataFlavorSupported(final DataFlavor flavor) { return additionalContents.isDataFlavorSupported(flavor) || defaultContents.isDataFlavorSupported(flavor); } public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (additionalContents.isDataFlavorSupported(flavor)) { return additionalContents.getTransferData(flavor); } return defaultContents.getTransferData(flavor); } }, null); } catch (final Exception e) { getToolkit().beep(); } } /* (non-Javadoc) * @see javax.swing.TransferHandler#getSourceActions(javax.swing.JComponent) */ public int getSourceActions(final JComponent c) { return defaultTransferHandler.getSourceActions(c); } /* (non-Javadoc) * @see javax.swing.TransferHandler#getVisualRepresentation(java.awt.datatransfer.Transferable) */ public Icon getVisualRepresentation(final Transferable t) { return defaultTransferHandler.getVisualRepresentation(t); } /* (non-Javadoc) * @see javax.swing.TransferHandler#importData(javax.swing.JComponent, java.awt.datatransfer.Transferable) */ public boolean importData(final JComponent comp, final Transferable transferable) { final SHTMLDocument doc = (SHTMLDocument) getDocument(); doc.startCompoundEdit(); boolean result = false; try { //System.out.format("getPasteMode()=%s\n", getPasteMode()); if (getPasteMode() == PasteMode.PASTE_PLAIN_TEXT) { final String content = transferable.getTransferData(DataFlavor.stringFlavor).toString(); if (content != null) { replaceSelection(content); } result = true; } else if (transferable.isDataFlavorSupported(htmlTextDataFlavor)) { // This path is taken if: // (a) the copy and paste is internal, from SimplyHTML to SimplyHTML, and // (b) the copied part is not multi paragraph. final HTMLText htmlText = (HTMLText) transferable.getTransferData(htmlTextDataFlavor); replaceSelection(htmlText); result = true; } else { final DataFlavor htmlFlavor = getSupportedHtmlFlavor(transferable); String stringContent = null; String htmlContent = null; if (htmlFlavor != null && transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) { htmlContent = (String) transferable.getTransferData(htmlFlavor); if (htmlContent.charAt(0) == 65533) { return importDataWithoutHtmlFlavor(comp, transferable); } stringContent = (String) transferable.getTransferData(DataFlavor.stringFlavor); htmlContent = new Remover(htmlContent).removeFirstAndBefore("body").removeLastAndAfter("/body") .getProcessedText() .replaceAll("", "") .replaceAll("", ""); final HTMLText htmlText = new HTMLText(htmlContent, stringContent); replaceSelection(htmlText); result = true; } else { //result = defaultImportData(comp, transferable); result = importExternalData(comp, transferable); } } } catch (final Exception e) { getToolkit().beep(); } doc.endCompoundEdit(); return result; } private boolean importExternalData(final JComponent comp, final Transferable t) throws ClassNotFoundException, UnsupportedFlavorException, IOException { // workaround for java decoding bug // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6740877 final DataFlavor htmlFlavor = new DataFlavor("text/html; class=java.lang.String"); if (t.isDataFlavorSupported(htmlFlavor)) { final String s = (String) t.getTransferData(htmlFlavor); if (s.charAt(0) == 65533) { return importDataWithoutHtmlFlavor(comp, t); } } return defaultImportData(comp, t); } private boolean importDataWithoutHtmlFlavor(final JComponent comp, final Transferable t) { return defaultImportData(comp, new Transferable() { public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (isValid(flavor)) { return t.getTransferData(flavor); } throw new UnsupportedFlavorException(flavor); } public DataFlavor[] getTransferDataFlavors() { final DataFlavor[] transferDataFlavors = t.getTransferDataFlavors(); int counter = 0; for (int i = 0; i < transferDataFlavors.length; i++) { if (isValid(transferDataFlavors[i])) { counter++; } } final DataFlavor[] validDataFlavors = new DataFlavor[counter]; int j = 0; for (int i = 0; i < transferDataFlavors.length; i++) { final DataFlavor flavor = transferDataFlavors[i]; if (isValid(flavor)) { validDataFlavors[j++] = flavor; } } return validDataFlavors; } public boolean isDataFlavorSupported(final DataFlavor flavor) { return isValid(flavor) && t.isDataFlavorSupported(flavor); } private boolean isValid(final DataFlavor flavor) { return !flavor.isMimeTypeEqual("text/html"); } }); } private boolean defaultImportData(final JComponent comp, final Transferable t) { return defaultTransferHandler.importData(comp, t); } } return new LocalTransferHandler(); } /* ------ end of cut, copy and paste implementation --------------- */ /* ------ start of font/paragraph manipulation --------------- */ public void removeCharacterAttributes() { final int p0 = getSelectionStart(); final int p1 = getSelectionEnd(); if (p0 != p1) { final SHTMLDocument doc = (SHTMLDocument) getDocument(); doc.startCompoundEdit(); SHTMLEditorKit.removeCharacterAttributes(doc, null, p0, p1 - p0); doc.endCompoundEdit(); } } public void removeParagraphAttributes() { final int p0 = getSelectionStart(); final int p1 = getSelectionEnd(); final SHTMLDocument doc = (SHTMLDocument) getDocument(); doc.removeParagraphAttributes(p0, p1 - p0 + 1); select(p0, p1); } public void applyAttributes(final AttributeSet attributeSet, final boolean applyToCompleteParagraph) { applyAttributes(attributeSet, applyToCompleteParagraph, false); } /** * Sets the attributes for a given part of this editor. If a range of * text is selected, the attributes are applied to the selection. * If nothing is selected, the input attributes of the given * editor are set thus applying the given attributes to future * inputs. * * @param attributeSet the set of attributes to apply * @param applyToCompleteParagraph true, if the attributes shall be applied to the whole * paragraph, false, if only the selected range of characters shall have them * @param replace true, if existing attribtes are to be replaced, false if not */ public void applyAttributes(final AttributeSet attributeSet, final boolean applyToCompleteParagraph, final boolean replace) { final SHTMLDocument doc = getSHTMLDocument(); requestFocus(); final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); if (applyToCompleteParagraph) { doc.setParagraphAttributes(selectionStart, selectionEnd - selectionStart, attributeSet, replace); } else { if (selectionEnd != selectionStart) { doc.setCharacterAttributes(selectionStart, selectionEnd - selectionStart, attributeSet, replace); } else { final MutableAttributeSet inputAttributes = ((SHTMLEditorKit) getEditorKit()).getInputAttributes(); inputAttributes.addAttributes(attributeSet); } } } /** (Unfinished.)*/ public void applyCharacterTag(final String tag) { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); final SHTMLDocument doc = (SHTMLDocument) getDocument(); doc.startCompoundEdit(); doc.endCompoundEdit(); } /** * Switches the elements in the current selection to the given tag. If allowedTags * is non-null, applies the tag only if it is contained in allowedTags. * * TODO: The new parameter does not work. So the method only works for paragraph tags, * like H1, H2 etc. --Dan * * @param tag the tag name to switche elements to * @param overwritableTags Tags that may be overwritten by the new tag. */ public void applyParagraphTag(final String tag, final Vector overwritableTags) { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); final StringWriter stringWriter = new StringWriter(); final SHTMLDocument doc = (SHTMLDocument) getDocument(); try { doc.startCompoundEdit(); final SHTMLWriter writer = new SHTMLWriter(stringWriter, doc); final Element paragraphElement = doc.getParagraphElement(selectionStart); final int regionStart = paragraphElement.getStartOffset(); final int regionEnd = Math.max(paragraphElement.getEndOffset(), selectionEnd); //System.out.println("SHTMLEditorPane applyTag start=" + start + ", end=" + end); final Element parentOfparagraphElement = paragraphElement.getParentElement(); int replaceStart = -1; int replaceEnd = -1; int index = -1; int elementsToRemoveCount = 0; final int elementCount = parentOfparagraphElement.getElementCount(); //System.out.println("SHTMLEditorPane applyTag parent elem=" + elem.getName() + ", eCount=" + eCount); for (int i = 0; i < elementCount; i++) { final Element child = parentOfparagraphElement.getElement(i); //System.out.println("SHTMLEditorPane applyTag child elem=" + child.getName() + ", eCount=" + eCount); final int elementStart = child.getStartOffset(); final int elementEnd = child.getEndOffset(); //System.out.println("SHTMLEditorPane applyTag eStart=" + eStart + ", eEnd=" + eEnd); if ((elementStart >= regionStart && elementStart < regionEnd) || (elementEnd > regionEnd && elementEnd <= regionEnd)) { elementsToRemoveCount++; if (overwritableTags.contains(child.getName())) { //System.out.println("SHTMLEditorPane applyTag element is in selection"); writer.writeStartTag(tag.toString(), child.getAttributes()); writer.writeChildElements(child); writer.writeEndTag(tag.toString()); if (index < 0) { index = i; } if (replaceStart < 0 || replaceStart > elementStart) { replaceStart = elementStart; } if (replaceEnd < 0 || replaceEnd < elementEnd) { replaceEnd = elementEnd; } } else { writer.write(child); } } } //System.out.println("SHTMLEditorPane applyTag remove index=" + index + ", removeCount=" + removeCount); if (index > -1) { doc.insertAfterEnd(parentOfparagraphElement.getElement(index), stringWriter.getBuffer().toString()); doc.removeElements(parentOfparagraphElement, index, elementsToRemoveCount); } //SHTMLEditorKit kit = (SHTMLEditorKit) getEditorKit(); //System.out.println("SHTMLEditorPane applyTag new HTML=\r\n" + sw.getBuffer().toString() ); //kit.read(new StringReader(sw.getBuffer().toString()), doc, getCaretPosition()); } catch (final Exception e) { Util.errMsg(this, e.getMessage(), e); } finally { doc.endCompoundEdit(); } } /* ------ end of font/paragraph manipulation --------------- */ /* ---------- class fields start -------------- */ static Action toggleBulletListAction = null; static Action toggleNumberListAction = null; private void performToggleListAction(final ActionEvent e, final String elemName) { if (elemName.equalsIgnoreCase(HTML.Tag.UL.toString())) { if (toggleBulletListAction == null) { final Component c = (Component) e.getSource(); final SHTMLPanelImpl panel = SHTMLPanelImpl.getOwnerSHTMLPanel(c); toggleBulletListAction = panel.dynRes.getAction(SHTMLPanelImpl.toggleBulletsAction); } toggleBulletListAction.actionPerformed(e); } else if (elemName.equalsIgnoreCase(HTML.Tag.OL.toString())) { if (toggleNumberListAction == null) { final Component c = (Component) e.getSource(); final SHTMLPanelImpl panel = SHTMLPanelImpl.getOwnerSHTMLPanel(c); toggleNumberListAction = panel.dynRes.getAction(SHTMLPanelImpl.toggleNumbersAction); } toggleNumberListAction.actionPerformed(e); } } public static final String newListItemAction = "newListItem"; public static final String insertLineBreakAction = "insertLineBreak"; public static final String deletePrevCharAction = "deletePrevChar"; public static final String deleteNextCharAction = "deleteNextChar"; public static final String moveUpAction = "moveUp"; public static final String homeAction = "home"; public static final String shiftHomeAction = "shiftHome"; public static final String shiftEndAction = "shiftEnd"; public static final String endAction = "end"; public static final String moveDownAction = "moveDown"; /** a data flavor for transferables processed by this component */ private final DataFlavor htmlTextDataFlavor = new DataFlavor(com.lightdev.app.shtm.HTMLText.class, "HTMLText"); /* Cursors for mouseovers in the editor */ void updateInputAttributes() { ((SHTMLEditorKit) getEditorKit()).updateInputAttributes(this); fireCaretUpdate(new CaretEvent(this) { public int getDot() { return getSelectionStart(); } public int getMark() { return getSelectionEnd(); } }); } public JPopupMenu getPopup() { return popup; } public void setPopup(final JPopupMenu popup) { this.popup = popup; } /** Determines whether the caret is currently within a table cell. */ private boolean caretWithinTableCell() { final Element tableCell = getCurrentTableCell(); return tableCell != null; } /** Returns the string HTML representation of the element. */ public String elementToHTML(final Element element) { final HTMLDocument document = (HTMLDocument) getDocument(); final StringWriter stringWriter = new StringWriter(); final SHTMLWriter shtmlWriter = new SHTMLWriter(stringWriter, document); try { shtmlWriter.write(element); } catch (final Exception ex) { } return stringWriter.getBuffer().toString(); } /** Determines whether an element is an empty paragraph. */ private boolean elementIsEmptyParagraph(final Element element) { final String elementContent = elementToHTML(element); return elementContent.matches("(?ims)\\s*]*>\\s*

      \\s*"); } private void performDefaultKeyStrokeAction(final int keyCode, final int modifiers, final ActionEvent event) { final KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers); final Object key = getInputMap().getParent().get(keyStroke); if (key != null) { getActionMap().getParent().get(key).actionPerformed(event); } } /** Performs the default key stroke action, assuming that the caret is within * a table cell and that the action is a cursor move; if the cursor leaves * the current table cell, undoes the action. * Returns true if the cursor stayed within the table cell. */ public boolean tryDefaultKeyStrokeActionWithinCell(final int keyCode, final int modifiers, final ActionEvent event) { final int originalCaretPosition = getCaretPosition(); final Element cellElement = getCurrentTableCell(); performDefaultKeyStrokeAction(keyCode, modifiers, event); final Element cellElementAfter = getCurrentTableCell(); if (cellElement == cellElementAfter) { return true; } setCaretPosition(originalCaretPosition); return false; } @Override public String getSelectedText() { final String text = super.getSelectedText(); return text != null ? text.replace('\u00a0', ' ') : null; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/FontDialog.java0100644 0000000 0000000 00000004564 12114157751 022347 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Frame; import javax.swing.text.AttributeSet; /** * Dialog to show and manipulate font attributes. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class FontDialog extends DialogShell { /** the font panel to use in this dialog */ private final FontPanel fontPanel; /** * constructor * * @param parent the main frame having the TextResources * @param title the title for this dialog * @param a the set of attributes to show and manipulate */ public FontDialog(final Frame parent, final String title, final AttributeSet a) { super(parent, title); // construct font panel fontPanel = new FontPanel(a, false); // add font panel to content pane of DialogShell final Container contentPane = super.getContentPane(); contentPane.add(fontPanel, BorderLayout.CENTER); // cause optimal placement of all elements pack(); } /** * get the set of attributes set in this dialog * * @return the attributes set in this dialog */ public AttributeSet getAttributes() { return fontPanel.getAttributes(); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/LicensePane.java0100644 0000000 0000000 00000007463 12114157751 022510 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; /** * A panel for displaying license information of application SimplyHTML. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class LicensePane extends JPanel { /* line separator character (sequence) */ private final String lineSeparator = System.getProperty("line.separator"); /** * construct a LicensePane */ public LicensePane(final Dimension d, final String licensePath) { /* create a text area to show the license text in */ final JTextArea licText = new JTextArea(getLicenseText(getClass().getResourceAsStream(licensePath))); licText.setEditable(false); licText.setFont(new Font("Courier", Font.PLAIN, 12)); /* create a scroll pane as the license text is long */ JScrollPane licPane = new JScrollPane(); licPane = new JScrollPane(licText); licPane.setPreferredSize(d); licPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); licPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); /* add the scroll pane to this panel */ setLayout(new BorderLayout()); add(licPane, BorderLayout.CENTER); licText.setCaretPosition(0); // go to top of text (for JRE versions earlier than 1.4) } /** * read and return the license text * * @return the license text */ private String getLicenseText(final InputStream is) { final StringBuffer license = new StringBuffer(); try { // InputStream is = getClass().getResourceAsStream(getLicense()); final BufferedReader r = new BufferedReader(new InputStreamReader(is)); String buf = r.readLine(); while (buf != null) { license.append(buf); license.append(lineSeparator); buf = r.readLine(); } r.close(); is.close(); } catch (final Exception e) { Util.errMsg( this, "The license text could not be opened.\n\nPlease consult file 'readme.txt' for installation guidelines\n\nSimplyHTML and all of its parts are distributed under\nthe terms and conditions of the GNU General Public License (GPL).\nYou may want to obtain a free and complete distribution package at\nhttp://www.lightdev.com", e); } return license.toString(); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SizeSelectorPanel.java0100644 0000000 0000000 00000025776 12114157751 023724 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.CSS; /** * Panel to show and manipulate a CSS size value * *

      Added support for negative integers in stage 8.

      * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class SizeSelectorPanel extends JPanel implements AttributeComponent, ActionListener { private final Object attributeKey; private final Object htmlAttrKey; private final JSpinner valueSelector; private JComboBox unitSelector; private JLabel unitName; //private LengthValue lv; private int setValueCalls = 0; private int originalValue = 0; private String originalUnit; private boolean allowNegative = false; public static final String UNIT_PT = "pt"; public static final String UNIT_PERCENT = "%"; public static final String[] UNIT_VALUES = { UNIT_PT, UNIT_PERCENT }; public static final int UNIT_TYPE_PT = 0; public static final int UNIT_TYPE_PERCENT = 1; public static final int TYPE_NONE = 0; public static final int TYPE_LABEL = 1; public static final int TYPE_COMBO = 2; /** * construct a basic SizeSelectorPanel with a * JSpinner to select a value * * @param key the attribute key this instance of SizeSelectionPanel * represents * @param allowNegative true, if negative values are to be allowed in the * panel, false if not */ public SizeSelectorPanel(final Object key, final Object htmlKey, final boolean allowNegative) { super(new FlowLayout()); attributeKey = key; htmlAttrKey = htmlKey; valueSelector = new JSpinner(new SpinnerNumberModel()); final Dimension dim = new Dimension(50, 24); valueSelector.setMinimumSize(dim); valueSelector.setPreferredSize(dim); add(valueSelector); originalUnit = getUnit(); this.allowNegative = allowNegative; } /** * construct a SizeSelectorPanel with a * JSpinner to select a value and either a JComboBox to select a given * unit for the selection value or a JLabel showing a fixed unit. * * @param key the attribute key this instance of SizeSelectionPanel * represents * @param allowNegative true, if negative values are to be allowed in the * panel, false if not * @param type the type of unit indicator, one of TYPE_LABEL and * TYPE_COMBO */ public SizeSelectorPanel(final Object key, final Object htmlKey, final boolean allowNegative, final int type) { this(key, htmlKey, allowNegative); switch (type) { case TYPE_LABEL: //System.out.println("SizeSelectorPanel constructor setting Label"); unitName = new JLabel(UNIT_PT); add(unitName); break; case TYPE_COMBO: //System.out.println("SizeSelectorPanel constructor setting COMBO"); unitSelector = new JComboBox(UNIT_VALUES); unitSelector.addActionListener(this); add(unitSelector); break; } originalUnit = getUnit(); } public void actionPerformed(final ActionEvent ae) { if (ae.getSource().equals(unitSelector)) { //System.out.println("actionPerformed is unitSelector new value = " + unitSelector.getSelectedItem().toString()); adjustMinMax(unitSelector.getSelectedItem().toString()); } } public void setValue(final String val) { //System.out.println("SizeSelectorPanel setValue STRING, val=" + val); String unit = null; int newVal = 0; //if(attributeKey instanceof CSS.Attribute) { //lv = new LengthValue(val); final float aVal = Util.getAbsoluteAttrVal(val); //System.out.println("SizeSelectorPanel aVal=" + aVal); unit = Util.getLastAttrUnit(); //lv.getUnit(); //System.out.println("SizeSelectorPanel unit=" + unit); adjustMinMax(unit); if (unitSelector != null) { //System.out.println("SizeSelectorPanel setValue setting combo"); unitSelector.setSelectedItem(unit); } else if (unitName != null) { //System.out.println("SizeSelectorPanel setValue setting label"); unitName.setText(unit); } newVal = (int) aVal; // new Float(lv.getValue(100)).intValue(); //System.out.println("SizeSelectorPanel setValue newVal=" + newVal); valueSelector.setValue(new Integer(newVal)); //} /* else { newVal = Integer.parseInt(val); valueSelector.setValue(new Integer(val)); unit = UNIT_PT; } */ if (++setValueCalls < 2) { originalValue = newVal; originalUnit = unit; } } /** * set the value of this AttributeComponent * * @param a the set of attributes possibly having an * attribute this component can display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { boolean success = false; //System.out.println("SizeSelectorPanel setValue SET attributeKey=" + attributeKey + ", htmlAttrKey=" + htmlAttrKey); Object valObj = a.getAttribute(attributeKey); if (valObj != null) { //System.out.println("SizeSelectorPanel CSS valObj=" + valObj); setValue(valObj.toString()); success = true; } else { if (htmlAttrKey != null) { valObj = a.getAttribute(htmlAttrKey); if (valObj != null) { //System.out.println("SizeSelectorPanel HTML valObj=" + valObj); setValue(valObj.toString()); success = true; } } } return success; } /** * adjust the minimum and maximum values of the component * according to the unit */ private void adjustMinMax(final String unit) { //if(lv != null) { final SpinnerNumberModel model = (SpinnerNumberModel) valueSelector.getModel(); int minVal = 0; if (allowNegative) { minVal = Integer.MIN_VALUE; } if (unit.equalsIgnoreCase(UNIT_PERCENT)) { //System.out.println("adjustMinMax percent"); model.setMinimum(new Integer(minVal)); model.setMaximum(new Integer(100)); } else { //System.out.println("adjustMinMax pt"); model.setMinimum(new Integer(minVal)); model.setMaximum(new Integer(Integer.MAX_VALUE)); } //} } /** * get the unit string of this SizeSelectorPanel * * @return the unit string (one of UNIT_PT and UNIT_PERCENT) */ public String getUnit() { String unit = ""; if (unitSelector != null) { unit = unitSelector.getSelectedItem().toString(); } else if (unitName != null) { unit = unitName.getText(); } else { unit = UNIT_PT; } if (unit.equalsIgnoreCase(UNIT_PT)) { unit = ""; } return unit; } public boolean valueChanged() { final Integer value = (Integer) valueSelector.getValue(); return ((value.intValue() != originalValue) || (getUnit() != originalUnit)); } public String getAttr() { final Integer value = (Integer) valueSelector.getValue(); final String unit = getUnit(); return value.toString() + unit; } public Integer getIntValue() { return (Integer) valueSelector.getValue(); } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet a = new SimpleAttributeSet(); final Integer value = getIntValue(); final String unit = getUnit(); if (valueChanged()) { if (attributeKey instanceof CSS.Attribute) { //a.addAttribute(attributeKey, value.toString() + unit); Util.styleSheet().addCSSAttribute(a, (CSS.Attribute) attributeKey, value.toString() + unit); } else { a.addAttribute(attributeKey, value.toString()); if (htmlAttrKey != null) { a.addAttribute(htmlAttrKey, value.toString()); } } } //System.out.println("SizeSelectorPanel getValue()='" + a + "'"); return a; } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { final SimpleAttributeSet a = new SimpleAttributeSet(); final Integer value = getIntValue(); final String unit = getUnit(); if (attributeKey instanceof CSS.Attribute) { //a.addAttribute(attributeKey, value.toString() + unit); Util.styleSheet().addCSSAttribute(a, (CSS.Attribute) attributeKey, value.toString() + unit); } else { a.addAttribute(attributeKey, value.toString()); if (htmlAttrKey != null) { a.addAttribute(htmlAttrKey, value.toString()); } } //System.out.println("SizeSelectorPanel getValue()='" + a + "'"); return a; } else { return getValue(); } } public JSpinner getValueSelector() { return valueSelector; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/Util.java0100644 0000000 0000000 00000110412 13147564753 021237 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Point; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URL; import java.util.Enumeration; import java.util.StringTokenizer; import java.util.Vector; import java.util.prefs.Preferences; import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.text.AttributeSet; import javax.swing.text.Element; import javax.swing.text.ElementIterator; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.html.HTML; import javax.swing.text.html.StyleSheet; /** * Utility methods for application SimplyHTML. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ public class Util { /* some constants */ public static final String JAR_PREFIX = "jar:"; public static final String JAR_EXTENSION = ".jar"; public static final String FILE_PREFIX = "file:"; public static final String CLASS_EXT = ".class"; public static final String JAR_SEPARATOR = "!/"; public static final String URL_SEPARATOR = "/"; public static final char URL_SEPARATOR_CHAR = '/'; public static final String CLASS_SEPARATOR = "."; public static final char CLASS_SEPARATOR_CHAR = '.'; public static final String DIR_UP_INDICATOR = ".."; public static final String RELATIVE_PREFIX = "../"; public static final String PROTOCOL_SEPARATOR = ":"; public static final String ANCHOR_SEPARATOR = "#"; public static final String pct = "%"; public static final String pt = "pt"; public static final String px = "px"; /** the default block size in bytes for file operations */ private static int blockSize = 1024; private static Vector startTimes = new Vector(); private static final String ERR_TITLE = "Error"; private static String unit = ""; /** a style sheet instanciated once for access to its utility methods */ private static StyleSheet s = new StyleSheet(); /* CSS Attribute constants */ public static final String CSS_ATTRIBUTE_NORMAL = "normal"; public static final String CSS_ATTRIBUTE_UNDERLINE = "underline"; public static final String CSS_ATTRIBUTE_LINE_THROUGH = "line-through"; public static final String CSS_ATTRIBUTE_NONE = "none"; public static final String CSS_ATTRIBUTE_ALIGN_LEFT = "left"; public static final String CSS_ATTRIBUTE_ALIGN_CENTER = "center"; public static final String CSS_ATTRIBUTE_ALIGN_RIGHT = "right"; public Util() { } /** * rename a file to have a given extension * * @param from the file to rename * @param newExt the new extension the file shall have * * @return the renamed file */ public static File renameFile(final File from, final String newExt) { final String fileName = Util.removeExtension(from.getName()); final String saveFileName = from.getParentFile().getAbsolutePath() + File.separator + fileName + newExt; final File newFile = new File(saveFileName); from.renameTo(newFile); return newFile; } /** * find the next link attribute from a given element upwards * through the element hierarchy * * @param elem the element to start looking at * * @return the link attribute found, or null, if none was found */ public static Object findLinkUp(Element elem) { //Element elem = ((SHTMLDocument) doc).getCharacterElement(selStart); Object linkAttr = null; //elem.getAttributes().getAttribute(HTML.Tag.A); Object href = null; while ((elem != null) && (linkAttr == null)) { linkAttr = elem.getAttributes().getAttribute(HTML.Tag.A); if (linkAttr != null) { href = ((AttributeSet) linkAttr).getAttribute(HTML.Attribute.HREF); } elem = elem.getParentElement(); } if (linkAttr != null && href != null) { return linkAttr; } else { return null; } } /** * remove the extension from a file name * * @param fileName the file name to remove the extension from * * @return the file name without extension */ public static String removeExtension(final String fileName) { String newName = fileName; final int pos = newName.lastIndexOf("."); if (pos > -1) { newName = fileName.substring(0, pos); } return newName; } /** * resolve sets of attributes that are recursively stored in each other * * @param style the set of attributes containing other sets of attributes */ public static AttributeSet resolveAttributes(final AttributeSet style) { final SimpleAttributeSet set = new SimpleAttributeSet(); if (style != null) { final Enumeration names = style.getAttributeNames(); Object value; Object key; while (names.hasMoreElements()) { key = names.nextElement(); //System.out.println("Util resolveAttributes key=" + key); value = style.getAttribute(key); //System.out.println("Util resolveAttributes value=" + value); if ((!key.equals(StyleConstants.NameAttribute)) && (!key.equals(StyleConstants.ResolveAttribute)) && (!key.equals(AttributeSet.ResolveAttribute)) && (!key.equals(AttributeSet.NameAttribute))) { set.addAttribute(key, value); } else { if (key.equals(StyleConstants.ResolveAttribute) || key.equals(AttributeSet.ResolveAttribute)) { //System.out.println("Util resolveAttributes resolving key=" + key); set.addAttributes(Util.resolveAttributes((AttributeSet) value)); } } } } return set; } /** * get a name by asking from the user * *

      Wrapper for JOptionPane with I18N support

      * * @param initialName the name initially shown in option pane * @param title the title to be shown in the option pane * @param text the text to be shown in the option pane * * @return the entered name or null if action was cancelled */ public static String nameInput(final Frame parent, final String initialName, final String regex, final String title, final String text) { String name; do { final Object input = JOptionPane.showInputDialog(null, Util.getResourceString(text), Util.getResourceString(title), JOptionPane.QUESTION_MESSAGE, null, null, initialName); name = input == null ? null : input.toString(); } while (name != null && !name.matches(regex)); return name; } /** * Show a message with options to choose from * *

      Wrapper for JOptionPane with I18N support

      * * @param options the options to be shown in the dialog * @param title the title to be shown in the dialog * @param msg the message to be shown in the dialog * @param item a variable part to be shown before msg * @param sep a separator for msg and item (return or blank etc.) * * @return the choice */ public static int msgChoice(final int options, final String title, final String msg, final String item, final String sep) { final String message = item + sep + Util.getResourceString(msg); return JOptionPane.showConfirmDialog(null, message, Util.getResourceString(title), options, JOptionPane.QUESTION_MESSAGE); } /** * Show a message with options to choose from * *

      Wrapper for JOptionPane with I18N support

      * * @param options the options to be shown in the dialog * @param title the title to be shown in the dialog * @param msg the message to be shown in the dialog * @param item a variable part to be shown before msg * @param sep a separator for msg and item (return or blank etc.) * * @return true, if YES was chosen, false if not */ public static boolean msg(final int options, final String title, final String msg, final String item, final String sep) { return (Util.msgChoice(options, title, msg, item, sep) == JOptionPane.YES_OPTION); } /** * get names of all styles for a given tag * * @param styles the style sheet to look for style names * @param tag the tag to find style names for * * @return a Vector with all style names found */ public static Vector getStyleNamesForTag(final StyleSheet styles, final String tag) { return Util.getStyleClassVector(tag, styles.getStyleNames()); } /** * get names of all styles for a given tag * * @param styles the style sheet to look for style names * @param tag the tag to find style names for * * @return a Vector with all style names found */ public static Vector getStyleNamesForTag(final AttributeSet styles, final String tag) { return Util.getStyleClassVector(tag, styles.getAttributeNames()); } private static Vector getStyleClassVector(final String tag, final Enumeration e) { String name; final Vector v = new Vector(0); while (e.hasMoreElements()) { name = (String) e.nextElement(); if (name.toLowerCase().startsWith(tag + ".")) { //System.out.println("getStyleClassVector adding name '" + name + "'"); v.addElement(name.substring(2)); } } return v; } /** * get the names of all styles found in a given StyleSheet * * @param styles the StyleSheet to look for style names * * @return a Vector with all names found */ public static Vector getStyleNames(final StyleSheet styles) { final Vector styleNames = new Vector(0); try { final Enumeration rules = styles.getStyleNames(); while (rules.hasMoreElements()) { styleNames.addElement(rules.nextElement()); } } catch (final Exception ee) { Util.errMsg(null, ee.getMessage(), ee); } return styleNames; } /** * delete a directory with all its contents * *

      CAUTION: This method deletes all content of the given * directory including all subdirectories and their conent

      * * @param dir the directory to delete */ public static void deleteDir(final File dir) { if (dir.exists()) { final File[] list = dir.listFiles(); for (int i = 0; i < list.length; i++) { if (list[i].isDirectory()) { Util.deleteDir(list[i]); } else { list[i].delete(); } } dir.delete(); } } /** * copies a single file. * *

      If destFile already exists or if both files are the same * the method does nothing. The complete destination path will be * created before copying, if necessary.

      * * @param srcFile the file to copy from * @param destFile the file to copy to */ public static void copyFile(final File srcFile, final File destFile) throws FileNotFoundException, IOException { if (!srcFile.toString().equals(destFile.toString())) { if (!destFile.exists()) { RandomAccessFile src; RandomAccessFile dest; File destDir; final byte[] buf = new byte[blockSize]; int bytesRead = 0; src = new RandomAccessFile(srcFile.getPath(), "r"); destDir = new File(destFile.getParent()); destDir.mkdirs(); dest = new RandomAccessFile(destFile.getPath(), "rw"); bytesRead = src.read(buf); while (bytesRead > -1) { dest.write(buf, 0, bytesRead); bytesRead = src.read(buf); } src.close(); dest.close(); } } } /** * get the index of a given element in the list of its parents elements. * * @param elem the element to get the index number for * @return the index of the given element */ public static int getElementIndex(final Element elem) { int i = 0; final Element parent = elem.getParentElement(); if (parent != null) { final int last = parent.getElementCount() - 1; Element anElem = parent.getElement(i); if (anElem != elem) { while ((i < last) && (anElem != elem)) { anElem = parent.getElement(++i); } } } return i; } /** * Get the path of the class file for a given class. * *

      This is either a directory of a class file or a directory of * a JAR file. Thus, this class must reside in the same place as * the application in question, not in a separate library * for instance.

      * * @param cls the class to get the path for * * @return the path of this class file or the path of the JAR file this class * file resides in, whatever applies */ public static String getClassFilePath(final Class cls) { int end = 0; String urlStr = null; String clsName = cls.getName(); final int clsNameLen = clsName.length() + CLASS_EXT.length(); int pos = clsName.lastIndexOf(CLASS_SEPARATOR); if (pos > -1) { clsName = clsName.substring(pos + 1); } clsName = clsName + CLASS_EXT; final URL url = cls.getResource(clsName); if (url != null) { urlStr = url.toString(); pos = urlStr.indexOf(JAR_SEPARATOR); if (pos > -1) { urlStr = urlStr.substring(0, pos); end = urlStr.lastIndexOf(URL_SEPARATOR) + 1; } else { end = urlStr.length() - clsNameLen; } pos = urlStr.lastIndexOf(FILE_PREFIX); if (pos > -1) { pos += FILE_PREFIX.length() + 1; } else { pos = 0; } urlStr = urlStr.substring(pos, end); urlStr = urlStr.replaceAll("%20", " "); } return urlStr; } /** * quick hack for getting the point value from an attribute value string * (needs to be refined and consolidated with length value) * * @param valStr the attribute value string to get the point size for * * @return the point size from the given attribute value */ public static float getPtValue(String valStr) { float len = 0; int pos = valStr.indexOf(pt); if (pos > -1) { unit = pt; valStr = valStr.substring(0, pos); len = Float.valueOf(valStr).floatValue(); } else { pos = valStr.indexOf(px); if (pos > -1) { unit = px; valStr = valStr.substring(0, pos); len = Float.valueOf(valStr).floatValue() * 1.3f; } else { pos = valStr.indexOf(pct); if (pos > -1) { unit = pct; valStr = valStr.substring(0, pos); //System.out.println("Util.getPtValue valStr=" + valStr); len = Float.valueOf(valStr).floatValue() / 100f; //System.out.println("Util.getPtValue len=" + len); } else { // assume relative value 1 .. 6 try { len = Float.valueOf(valStr).floatValue(); unit = pt; /* switch((int) len) { case 1: len = 8; break; case 2: len = 10; break; case 3: len = 12; break; case 4: len = 14; break; case 5: len = 18; break; case 6: len = 24; break; default: len = len; break; } */ } catch (final Exception e) { // unsupported number format (em ex, etc.) } } } } return len; } /** * get the unit string from the last attribute object which was * converted to a numerical value * * @return the unit string from the last attribute object */ public static String getLastAttrUnit() { return unit; } /** * get the numerical value for an attribute object * * @param attr the attribute to get the value from * * @return the numerical value */ public static float getAttrValue(final Object attr) { float val = -1; if (attr != null) { val = Util.getPtValue(attr.toString()); //System.out.println("Util.getAttrValue val=" + val); } return val; } /** * get the absolute value of an attribute * * @param attr the attribute to get the value from * * @return the absolute numerical value */ public static float getAbsoluteAttrVal(final Object attr) { String valStr = null; if (attr != null) { valStr = attr.toString(); int pos = valStr.indexOf(pt); unit = pt; if (pos < 0) { pos = valStr.indexOf(pct); unit = pct; if (pos < 0) { pos = valStr.indexOf(px); } } if (pos > -1) { valStr = valStr.substring(0, pos); return Float.valueOf(valStr).floatValue(); } else { unit = ""; } } try { return Float.valueOf(valStr).floatValue(); } catch (final Exception e) { // unsupported number format (em ex, etc.) return 0f; } } /** * get the row index for a given table cell * * @param cell the cell element to get the row index for * * @return the row index of the given cell element */ public static int getRowIndex(final Element cell) { final Element thisRow = cell.getParentElement(); final Element table = thisRow.getParentElement(); int index = 0; final int count = table.getElementCount(); Element elem = table.getElement(index); while (!elem.equals(thisRow) && index < count) { elem = table.getElement(++index); } return index; } /** * Get an arry of strings from a given string having several entries * delimited by blanks. * *

      In the resource file of SimplyHTML for instance menu bar and menu * definitions are contained as strings having a key for each item. * The keys are delimited with blanks.

      * *

      A string "file edit help" from the resource file for instance * would be broken into an array of strings looking as follows

      * *

      * String[0]="file"
      * String[1]="edit"
      * String[2]="help" *

      * * @param input the string to transform into a string array * @return the resulting string array */ public static String[] tokenize(final String input, final String delim) { final Vector v = new Vector(); final StringTokenizer t = new StringTokenizer(input, delim); String result[]; while (t.hasMoreTokens()) { v.addElement(t.nextToken()); } result = new String[v.size()]; for (int i = 0; i < result.length; i++) { result[i] = (String) v.elementAt(i); } return result; } /** * write a message with a time stamp to System.out and remember * the time stamp in a LIFO Vector */ public static void msgStart(final String startMsg) { final long startTime = System.currentTimeMillis(); startTimes.addElement(new Long(startTime)); //System.out.println(startMsg + " startTime=" + startTime); } /** * find the first occurrence of an Element in the * element tree above a given Element * * @param name the name of the Element to search for * @param start the Element to start looking * * @return the found Element or null if none is found */ public static Element findElementUp(final String name, final Element start) { Element elem = start; while (elem != null && !elem.getName().equalsIgnoreCase(name)) { elem = elem.getParentElement(); } return elem; } /** * find the first occurrence of an Element in the * element tree above a given Element * * @param name1 the primary name of the Element to search for * @param name2 an alternative name for the Element to search for * @param start the Element to start looking * * @return the found Element or null if none is found */ public static Element findElementUp(final String name1, final String name2, final Element start) { Element elem = start; while (elem != null && !(elem.getName().equalsIgnoreCase(name1) || elem.getName().equalsIgnoreCase(name2))) { elem = elem.getParentElement(); } return elem; } /** * find the first occurrence of an Element in the * element tree below a given Element * * @param name the name of the Element to search for * @param parent the Element to start looking * * @return the found Element or null if none is found */ public static Element findElementDown(final String name, final Element parent) { Element foundElement = null; final ElementIterator eli = new ElementIterator(parent); Element thisElement = eli.first(); while (thisElement != null && foundElement == null) { if (thisElement.getName().equalsIgnoreCase(name)) { foundElement = thisElement; } thisElement = eli.next(); } return foundElement; } /** * convenience method for adding a component to a container * layed out by a GridBagLayout * * @param container the container to add a component to * @param comp the component to add to container * @param g the GridBagLayout associated with container * @param c the GridBagConstraints to use * @param gx the value to use for GridBagConstraints.gridx * @param gy the value to use for GridBagConstraints.gridy * @param a the value to use for GridBagConstraints.anchor */ public static void addGridBagComponent(final JComponent container, final JComponent comp, final GridBagLayout g, final GridBagConstraints c, final int gx, final int gy, final int a) { /* c.gridx = gx; c.gridy = gy; c.anchor = a; c.insets = new Insets(2, 2, 2, 2); c.ipadx = 2; c.ipady = 2; g.setConstraints(comp, c); container.add(comp); */ Util.addGridBagComponent(container, comp, g, c, gx, gy, a, 1, 1, GridBagConstraints.NONE, 0, 0); } /** * convenience method for adding a component to a container * layed out by a GridBagLayout * * @param container the container to add a component to * @param comp the component to add to container * @param g the GridBagLayout associated with container * @param c the GridBagConstraints to use * @param gx the value to use for GridBagConstraints.gridx * @param gy the value to use for GridBagConstraints.gridy * @param a the value to use for GridBagConstraints.anchor * @param gw the value to use for GridBagConstraints.gridwidth * @param gh teh value to use for GridBagConstraints.gridheight */ public static void addGridBagComponent(final JComponent container, final JComponent comp, final GridBagLayout g, final GridBagConstraints c, final int gx, final int gy, final int a, final int gw, final int gh) { Util.addGridBagComponent(container, comp, g, c, gx, gy, a, gw, gh, GridBagConstraints.NONE, 0, 0); } /** * convenience method for adding a component to a container * layed out by a GridBagLayout * * @param container the container to add a component to * @param comp the component to add to container * @param g the GridBagLayout associated with container * @param c the GridBagConstraints to use * @param gx the value to use for GridBagConstraints.gridx * @param gy the value to use for GridBagConstraints.gridy * @param a the value to use for GridBagConstraints.anchor * @param gw the value to use for GridBagConstraints.gridwidth * @param gh teh value to use for GridBagConstraints.gridheight * @param f the value to use for GridBagConstraints.fill */ public static void addGridBagComponent(final JComponent container, final JComponent comp, final GridBagLayout g, final GridBagConstraints c, final int gx, final int gy, final int a, final int gw, final int gh, final int f) { Util.addGridBagComponent(container, comp, g, c, gx, gy, a, gw, gh, GridBagConstraints.NONE, 0, 0); } /** * convenience method for adding a component to a container * layed out by a GridBagLayout * * @param container the container to add a component to * @param comp the component to add to container * @param g the GridBagLayout associated with container * @param c the GridBagConstraints to use * @param gx the value to use for GridBagConstraints.gridx * @param gy the value to use for GridBagConstraints.gridy * @param a the value to use for GridBagConstraints.anchor * @param gw the value to use for GridBagConstraints.gridwidth * @param gh teh value to use for GridBagConstraints.gridheight * @param f the value to use for GridBagConstraints.fill * @param wx the value to use for GridBagConstraints.weightx * @param wy the value to use for GridBagConstraints.weighty */ public static void addGridBagComponent(final JComponent container, final JComponent comp, final GridBagLayout g, final GridBagConstraints c, final int gx, final int gy, final int a, final int gw, final int gh, final int f, final double wx, final double wy) { c.gridx = gx; c.gridy = gy; c.anchor = a; c.insets = new Insets(2, 2, 2, 2); c.ipadx = 2; c.ipady = 2; c.gridwidth = gw; c.gridheight = gh; c.fill = f; c.weightx = wx; c.weighty = wy; g.setConstraints(comp, c); container.add(comp); } /** * resolve a relative URL string against an absolute URL string. * *

      the absolute URL string is the start point for the * relative path.

      * *

      Example:

      *
           *   absolute path:  file:/d:/eigene dateien/eigene bilder/
           *   relative path:  ../images/test.jpg
           *   result:         file:/d:/eigene dateien/images/test.jpg
           * 
      * * @param relPath the relative URL string to resolve * @param absPath the absolute URL string to start at * * @return the absolute URL string resulting from resolving relPath * against absPath */ public static String resolveRelativePath(final String relPath, final String absPath) { String newAbsPath = absPath; String newRelPath = relPath; if (absPath.endsWith(URL_SEPARATOR)) { newAbsPath = absPath.substring(0, absPath.length() - 1); } int relPos = newRelPath.indexOf(RELATIVE_PREFIX); while (relPos > -1) { newRelPath = newRelPath.substring(relPos + RELATIVE_PREFIX.length()); newAbsPath = newAbsPath.substring(0, newAbsPath.lastIndexOf(URL_SEPARATOR)); relPos = newRelPath.indexOf(RELATIVE_PREFIX); } if (newRelPath.startsWith(URL_SEPARATOR)) { return newAbsPath + newRelPath; } else { return newAbsPath + URL_SEPARATOR + newRelPath; } } /** * get the path to a given file relative to a given directory * * @param fromDir the directory having the file from which the link refers * @param toFile the file to which a link refers * * @return the relative path */ public static String getRelativePath(final File fromDir, final File toFile) { String fromStr = fromDir.getAbsolutePath(); if (!fromStr.endsWith(File.separator)) { fromStr = fromStr + File.separator; } final String toStr = toFile.getAbsolutePath(); int pos = fromStr.indexOf(File.separator); final int fromLen = fromStr.length(); final int toLen = toStr.length(); int oldPos = pos; while ((pos > -1) && (pos < fromLen) && (pos < toLen) && (fromStr.substring(0, pos).equalsIgnoreCase(toStr.substring(0, pos)))) { oldPos = pos + 1; pos = fromStr.indexOf(File.separator, oldPos); } final int samePos = oldPos; int level = 0; while (pos > -1) { ++level; oldPos = pos + 1; pos = fromStr.indexOf(File.separator, oldPos); } final StringBuffer relPath = new StringBuffer(); if (level > 0) { for (int i = 0; i < level; i++) { relPath.append(".."); relPath.append(File.separator); } } relPath.append(toStr.substring(samePos)); return relPath.toString().replace(File.separatorChar, URL_SEPARATOR_CHAR); } /** * show an error message and print a stack trace to the console if * in development mode (DEV_MODE = true) * * @param owner the owner of the message, or null * @param msg the message to display, or null * @param e the exception object describing the error, or null */ public static void errMsg(final Component owner, final String msg, final Throwable e) { if (e != null) { e.printStackTrace(); } if (msg != null) { JOptionPane.showMessageDialog(owner, msg, ERR_TITLE, JOptionPane.ERROR_MESSAGE); } } /** * center a Component relative to * another Component. * * @param parent the Component to be used as the * basis for centering * @param comp the Component to be centered within parent * */ public static void center(final Component parent, final Component comp) { final Dimension cSize = comp.getPreferredSize(); final Dimension fSize = parent.getSize(); final Point loc = parent.getLocation(); comp.setLocation((fSize.width - cSize.width) / 2 + loc.x, (fSize.height - cSize.height) / 2 + loc.y); } /** * get a StyleSheet object for using its utility methods */ public static StyleSheet styleSheet() { return s; } /** * remove all occurrences of a given char from a given string * * @param src the string to remove from * @param c the char to remove * * @return a string copy of src with all occurrences of c removed */ public static String removeChar(final String src, final char c) { final StringBuffer buf = new StringBuffer(); int start = 0; int pos = src.indexOf(c); while ((pos > -1) && (start < src.length())) { pos = src.indexOf(c, start); if ((pos > -1) && (start < src.length())) { buf.append(src.substring(start, pos)); start = pos + 1; } } if (start < src.length()) { buf.append(src.substring(start)); } if (buf.length() == 0) { buf.append(src); } return buf.toString(); } /** * get a string from the resources file * * @param resources the TextResources to get the string from * @param nm the key of the string * @return the string for the given key or null if not found */ static public String getResourceString(final UIResources resources, final String nm) { return DynamicResource.getResourceString(resources, nm); } static public String getResourceString(final String nm) { final String resourceString = DynamicResource.getResourceString(SHTMLPanelImpl.getUiResources(), nm); return resourceString != null ? resourceString : nm; } static public String getPreference(final String key, final String defaultValue) { String paramValue = DynamicResource.getResourceString(SHTMLPanel.getResources(), key); if (paramValue != null) { return paramValue; } paramValue = defaultValue; try { final Preferences prefs = Preferences.userNodeForPackage(PrefsDialog.class); paramValue = prefs.get(key, paramValue); } catch (final Exception ex) { } return paramValue; } static boolean preferenceIsTrue(final String key) { return Util.getPreference(key, "false").equalsIgnoreCase("true"); } static boolean preferenceIsTrue(final String key, final String defaultValue) { return Util.getPreference(key, defaultValue).equalsIgnoreCase("true"); } static boolean useSteStyleSheet() { return Util.getPreference(PrefsDialog.PREFS_USE_STD_STYLE_SHEET, "false").equalsIgnoreCase("true"); } /** Tells whether the user should be able to switch between the WYSIWYG editor pane and the HTML source * editor pane using the UI element of tab, shown below the editing panes. If false, switching * between editor panes is still possible, albeit not using the tabs. */ static boolean showViewsInTabs() { return Util.preferenceIsTrue("showViewsInTabs", "true"); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/InvisibleView.java0100644 0000000 0000000 00000015422 12114157751 023073 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Container; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; import javax.swing.text.BadLocationException; import javax.swing.text.Element; import javax.swing.text.JTextComponent; import javax.swing.text.Position; import javax.swing.text.View; /** * A view to hide HTML tags (e.g. comments) * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class InvisibleView extends View { /** indicates whether or not this view is to be shown in its component */ boolean isVisible = false; /** * constructor */ public InvisibleView(final Element e) { super(e); } /** * Determines the preferred span for this view along an * axis. * * @param axis may be either View.X_AXIS or * View.Y_AXIS * @return the span the view would like to be rendered into. * Typically the view is told to render into the span * that is returned, although there is no guarantee. * The parent may choose to resize or break the view * @see View#getPreferredSpan */ public float getPreferredSpan(final int axis) { return 0; } /** * Determines the maximum span for this view along an * axis. * * @param axis may be either View.X_AXIS or * View.Y_AXIS * @return the maximum span the view can be rendered into * @see View#getPreferredSpan */ public float getMaximumSpan() { return 0; } /** * Determines the minimum span for this view along an * axis. * * @param axis may be either View.X_AXIS or * View.Y_AXIS * @return the minimum span the view can be rendered into * @see View#getPreferredSpan */ public float getMinimumSpan() { return 0; } /** * Renders using the given rendering surface and area on that * surface. The view may need to do layout and create child * views to enable itself to render into the given allocation. * * @param g the rendering surface to use * @param allocation the allocated region to render into * @see View#paint */ public void paint(final Graphics g, final Shape allocation) { if (isVisible) { // paint something here } else { setSize(0, 0); } } /** * Provides a mapping from the view coordinate space to the logical * coordinate space of the model. The biasReturn * argument will be filled in to indicate that the point given is * closer to the next character in the model or the previous * character in the model. * * @param x the X coordinate >= 0 * @param y the Y coordinate >= 0 * @param a the allocated region in which to render * @return the location within the model that best represents the * given point in the view >= 0. The biasReturn * argument will be * filled in to indicate that the point given is closer to the next * character in the model or the previous character in the model. */ public int viewToModel(final float x, final float y, final Shape a, final Position.Bias[] parm4) { return 0; } /** * Provides a mapping, for a given character, * from the document model coordinate space * to the view coordinate space. * * @param pos the position of the desired character (>=0) * @param a the area of the view, which encompasses the requested character * @param b the bias toward the previous character or the * next character represented by the offset, in case the * position is a boundary of two views; b will have one * of these values: *
        *
      • Position.Bias.Forward *
      • Position.Bias.Backward *
      * @return the bounding box, in view coordinate space, * of the character at the specified position * @exception BadLocationException if the specified position does * not represent a valid location in the associated document * @exception IllegalArgumentException if b is not one of the * legal Position.Bias values listed above * @see View#viewToModel */ public Shape modelToView(final int pos, final Shape a, final Position.Bias b) throws javax.swing.text.BadLocationException { return new Rectangle(0, 0); } /** * Establishes the parent view for this view. This is * guaranteed to be called before any other methods if the * parent view is functioning properly. This is also * the last method called, since it is called to indicate * the view has been removed from the hierarchy as * well. When this method is called to set the parent to * null, this method does the same for each of its children, * propogating the notification that they have been * disconnected from the view tree. If this is * reimplemented, super.setParent() should * be called. * * @param parent the new parent, or null if the view is * being removed from a parent */ public void setParent(final View parent) { if (parent != null) { final Container host = parent.getContainer(); if (host != null) { isVisible = ((JTextComponent) host).isEditable(); } } super.setParent(parent); } /** * @return true if the Component is visible. */ public boolean isVisible() { return isVisible; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/DialogShell.java0100644 0000000 0000000 00000015356 12114157751 022511 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dialog; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowEvent; import javax.swing.AbstractButton; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JPanel; /** * Base class for other dialogs of application SimplyHTML. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class DialogShell extends JDialog implements ActionListener { /** panel containing dialog buttons */ protected JPanel buttonPanel; /** button to confirm the operation */ protected AbstractButton okButton; /** button to cancel the operation */ protected AbstractButton cancelButton; /** button to display context sensitive help */ protected AbstractButton helpButton; /** * the result of the operation, one of RESULT_CANCEL and RESULT_OK */ private int result; /** result value for a cancelled operation */ public static int RESULT_CANCEL = 1; /** result value for a confirmed operation */ public static int RESULT_OK = 0; /** id of associated help topic (if any) */ protected String helpTopicId = null; /** Listens to enter and cancel. */ private KeyListener completionKeyListener = null; /** * constructor * * @param parent the parent dialog * @param title the title for this dialog */ public DialogShell(final Dialog parent, final String title) { super(parent, title); buildDialog(); } /** * constructor * * @param parent the parent frame * @param title the title for this dialog */ public DialogShell(final Frame parent, final String title) { super(parent, title); buildDialog(); } /** * constructor * * @param parent the parent frame * @param title the title for this dialog * @param helpTopicId the id of the help topic to display for this dialog */ public DialogShell(final Frame parent, final String title, final String helpTopicId) { super(parent, title); this.helpTopicId = helpTopicId; buildDialog(); } /** * constructor * * @param parent the parent dialog * @param title the title for this dialog * @param helpTopicId the id of the help topic to display for this dialog */ public DialogShell(final Dialog parent, final String title, final String helpTopicId) { super(parent, title); this.helpTopicId = helpTopicId; buildDialog(); } /** * create dialog components */ private void buildDialog() { enableEvents(AWTEvent.WINDOW_EVENT_MASK); // construct dialog buttons okButton = new JButton(Util.getResourceString("okBtnName")); cancelButton = new JButton(Util.getResourceString("cancelBtnName")); cancelButton.addActionListener(this); okButton.addActionListener(this); // construct button panel buttonPanel = new JPanel(new FlowLayout()); buttonPanel.add(okButton); buttonPanel.add(cancelButton); // construct help button if (helpTopicId != null) { try { helpButton = SHTMLHelpBroker.createHelpButton(helpTopicId); helpButton.setText(Util.getResourceString("helpLabel")); buttonPanel.add(helpButton); } catch (final NoClassDefFoundError e) { helpTopicId = null; } } // add all to content pane of dialog final Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout(5, 5)); contentPane.add(buttonPanel, BorderLayout.SOUTH); } /** * dispose the dialog properly in case of window close events */ protected void processWindowEvent(final WindowEvent e) { if (e.getID() == WindowEvent.WINDOW_CLOSING) { cancel(); } super.processWindowEvent(e); } /** * cancel the operation */ protected void cancel() { result = RESULT_CANCEL; dispose(); } /** * confirm the operation */ protected void confirm() { result = RESULT_OK; dispose(); } /** * get the result of the operation performed in this dialog * * @return the result, one of RESULT_OK and RESULT_CANCEL */ public int getResult() { return result; } /** * implements the ActionListener interface to be notified of * clicks onto the ok and cancel button. */ public void actionPerformed(final ActionEvent e) { final Object src = e.getSource(); if (src == cancelButton) { cancel(); } else if (src == okButton) { confirm(); } } protected KeyListener getCompletionKeyListener() { if (completionKeyListener == null) { completionKeyListener = new KeyAdapter() { public void keyPressed(final KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { e.consume(); cancel(); } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { e.consume(); confirm(); } } }; } return completionKeyListener; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/ImagePreview.java0100644 0000000 0000000 00000022254 12114157751 022701 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.Scrollable; import javax.swing.SwingConstants; /** * An ImagePreview is a component to preview * GIF and JPEG images. * *

      The preview adapts (shrinks) images to its size upon their * assignment. When the preview is resized, its image is adapted again.

      * *

      Alternately, when altering the image width, height and scale properties * with respective setters, the image is resized within the preview without * the preview itself adapting in size.

      * *

      Scroll bars are displayed as appropriate.

      * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class ImagePreview extends JComponent implements Scrollable { /** * scroll increment (for Scrollable implementation) */ private int maxUnitIncrement = 1; /** * the image to be previewed */ private ImageIcon pic; /** * Construct an ImagePreview. * * @param pic - the image to be previewed */ public ImagePreview(final ImageIcon pic) { setImage(pic); } /** * Construct an ImagePreview without an image * associated. */ public ImagePreview() { this(null); } /** * Get the original width of the image previewed in this component * * @return the original width of the image previewed in this component * or -1 if no image is assigned */ public int getOriginalWidth() { if (pic != null) { return pic.getIconWidth(); } else { return -1; } } /** * Get the original height of the image previewed in this component * * @return the original height of the image previewed in this component * or -1 if no image is assigned */ public int getOriginalHeight() { if (pic != null) { return pic.getIconHeight(); } else { return -1; } } /** * Set the image to be previewed. */ public void setImage(final ImageIcon pic) { this.pic = pic; if (pic != null) { this.getGraphics().clearRect(0, 0, getWidth(), getHeight()); this.paint(this.getGraphics()); } } /** * Paints this component. * If the image associated with this component is smaller than the size * of the component, the image is painted in its original size. Otherwise, * the image is scaled down to the size of this component. * * @param g - The graphics context to use for painting. */ public void paint(final Graphics g) { if (pic != null) { int dWidth = pic.getIconWidth(); int dHeight = pic.getIconHeight(); final int scale = getScale(); dWidth = dWidth * scale / 100; dHeight = dHeight * scale / 100; g.drawImage(pic.getImage(), 0, 0, dWidth, dHeight, this); } } /** * Gets the size adjustment necessary for the image to fit into this * component and returns the resulting scale percentage. * * @return the scale percentage of the image */ public int getScale() { int scale = 100; if (pic != null) { int vPct = 100; int hPct = 100; final Dimension ps = getPreferredSize(); hPct = (int) (ps.getWidth() / ((double) pic.getIconWidth() / (double) 100)); //System.out.println("ImagePreview getScale ps.getWidth " + ps.getWidth()); //System.out.println("ImagePreview getScale pic.getIconWidth() " + pic.getIconWidth()); //System.out.println("ImagePreview getScale hPct " + hPct + "\r\n\r\n"); vPct = (int) (ps.getHeight() / ((double) pic.getIconHeight() / (double) 100)); //System.out.println("ImagePreview getScale ps.getHeight() " + ps.getHeight()); //System.out.println("ImagePreview getScale pic.getIconHeight() " + pic.getIconHeight()); //System.out.println("ImagePreview getScale vPct " + vPct + "\r\n\r\n"); if (hPct < vPct) { scale = hPct; } else { scale = vPct; } } //System.out.println("ImagePreview getScale=" + scale + "\r\n\r\n"); return scale; } /** * set the preview to a new width maintaining the image proportions * * @param newWidth the new width for the image preview */ public void setPreviewWidth(final int newWidth) { //System.out.println("ImagePreview setPreviewWidth newWidth=" + newWidth); if (pic != null) { try { final int hPct = (int) (newWidth / ((double) getOriginalWidth() / (double) 100)); final int newHeight = getOriginalHeight() * hPct / 100; setPreferredSize(new Dimension(newWidth, newHeight)); } catch (final Exception e) { e.printStackTrace(); setPreferredSize(new Dimension(20, 20)); } revalidate(); } } /** * set the preview to a new height maintaining the image proportions * * @param newHeight the new height for the image preview */ public void setPreviewHeight(final int newHeight) { if (pic != null) { try { final int vPct = (int) (newHeight / ((double) getOriginalHeight() / (double) 100)); final int newWidth = getOriginalWidth() * vPct / 100; setPreferredSize(new Dimension(newWidth, newHeight)); } catch (final Exception e) { e.printStackTrace(); setPreferredSize(new Dimension(20, 20)); } revalidate(); } } /** * Adapt the size of the image previewed by this component to a new * scale. * * @param newScale the new scale the image shall adapt to in size */ public void setScale(final int newScale) { int newWidth; int newHeight; newWidth = getOriginalWidth() * newScale / 100; newHeight = getOriginalHeight() * newScale / 100; setPreferredSize(new Dimension(newWidth, newHeight)); revalidate(); } /* ------------ Scrollable implementation start ---------------------- */ public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } public int getScrollableUnitIncrement(final Rectangle visibleRect, final int orientation, final int direction) { //Get the current position. int currentPosition = 0; if (orientation == SwingConstants.HORIZONTAL) { currentPosition = visibleRect.x; } else { currentPosition = visibleRect.y; } //Return the number of pixels between currentPosition //and the nearest tick mark in the indicated direction. if (direction < 0) { final int newPosition = currentPosition - (currentPosition / maxUnitIncrement) * maxUnitIncrement; return (newPosition == 0) ? maxUnitIncrement : newPosition; } else { return ((currentPosition / maxUnitIncrement) + 1) * maxUnitIncrement - currentPosition; } } public int getScrollableBlockIncrement(final Rectangle visibleRect, final int orientation, final int direction) { if (orientation == SwingConstants.HORIZONTAL) { return visibleRect.width - maxUnitIncrement; } else { return visibleRect.height - maxUnitIncrement; } } public boolean getScrollableTracksViewportWidth() { return false; } public boolean getScrollableTracksViewportHeight() { return false; } public void setMaxUnitIncrement(final int pixels) { maxUnitIncrement = pixels; } /* --------- Scrollable implementation end --------------------------- */ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/ScaledStyleSheet.java0100644 0000000 0000000 00000004341 12114157751 023517 0ustar000000000 0000000 /* * Freeplane - mind map editor * Copyright (C) 2012 Freeplane team and others * * this file is created by Dimitry Polivaev in 2012. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.lightdev.app.shtm; import java.awt.Font; import java.awt.Toolkit; import javax.swing.text.AttributeSet; import javax.swing.text.html.CSS; import javax.swing.text.html.StyleSheet; public class ScaledStyleSheet extends StyleSheet{ /** * */ private static final long serialVersionUID = 1L; public Font getFont(AttributeSet a) { final Font font = super.getFont(a); final float fontScaleFactor = getFontScaleFactor(a); return super.getFont(font.getFamily(), font.getStyle(), Math.round(font.getSize2D() * fontScaleFactor)); } public static float FONT_SCALE_FACTOR; static { float factor = 1f; try { factor = Toolkit.getDefaultToolkit().getScreenResolution() / 72f; } catch (Exception e) { } FONT_SCALE_FACTOR = factor; } private float getFontScaleFactor(AttributeSet a) { final Object attribute = a.getAttribute(CSS.Attribute.FONT_SIZE); if(attribute == null) return FONT_SCALE_FACTOR; final String fontSize = attribute.toString(); final int fsLength = fontSize.length(); if(fsLength <= 1 || Character.isDigit(fontSize.charAt(fsLength-1)) || fontSize.endsWith("pt")) return FONT_SCALE_FACTOR; if(fontSize.endsWith("px")) return 1/1.3f; if(fontSize.endsWith("%") || fontSize.endsWith("em") || fontSize.endsWith("ex") || fontSize.endsWith("er")) return getFontScaleFactor(a.getResolveParent()); return FONT_SCALE_FACTOR; } }simplyhtml-0.17.3/src/com/lightdev/app/shtm/MarginPanel.java0100644 0000000 0000000 00000004542 12114157751 022512 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.GridLayout; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.text.html.CSS; /** * Panel to set text margin attributes. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class MarginPanel extends AttributePanel { private final BoundariesPanel margin; private final BoundariesPanel padding; public MarginPanel() { super(); // margin/padding panel setLayout(new GridLayout(2, 1, 3, 3)); // construct margin panel margin = new BoundariesPanel(CSS.Attribute.MARGIN); // set border and title and add margin panel margin .setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util.getResourceString("marginLabel"))); this.add(margin); // construct padding panel padding = new BoundariesPanel(CSS.Attribute.PADDING); // set border and title adn add padding panel padding.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("paddingLabel"))); this.add(padding); } public void reset() { margin.reset(); padding.reset(); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/FontPanel.java0100644 0000000 0000000 00000047155 12114157751 022212 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.GridLayout; import java.util.Enumeration; import java.util.Vector; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.html.CSS; import javax.swing.text.html.HTML; /** * A panel for showing and manipulating font information. * *

      FontPanel shows and manipulates CSS attributes. To * set it to HTML attributes, methods setAttributes and getAttributes * have to be overridden.

      * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class FontPanel extends JPanel implements TitledPickList.TitledPickListListener, ColorPanel.ColorPanelListener { /** a text field to show a sample of the selected font attributes */ JTextField sample = new JTextField(); /** table for automatic font component value read/write */ private final Vector fontComponents = new Vector(0); public FontPanel(final boolean pickBgColor) { setLayout(new BorderLayout(5, 5)); /** create a label for previewing font selections */ sample.setText(""); sample.setEditable(false); sample.setPreferredSize(new Dimension(200, 50)); sample.setHorizontalAlignment(SwingConstants.CENTER); sample.setText(Util.getResourceString("previewText")); final JPanel previewPanel = new JPanel(new BorderLayout()); previewPanel.add(sample, BorderLayout.CENTER); previewPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("previewLabel"))); /** * create a pick list for family filled with * available font family names */ final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); final FamilyPickList family = new FamilyPickList(ge.getAvailableFontFamilyNames(), Util.getResourceString("familyLabel")); family.addTitledPickListListener(this); fontComponents.add(family); /** create a pick list for font size */ final SizePickList size = new SizePickList(SHTMLPanelImpl.FONT_SIZES, Util.getResourceString("sizeLabel")); size.addTitledPickListListener(this); fontComponents.add(size); /** wrap together family and size */ final JPanel familySizePanel = new JPanel(new BorderLayout(5, 5)); familySizePanel.add(family, BorderLayout.CENTER); familySizePanel.add(size, BorderLayout.EAST); /** create a panel to put font parts family, size and stlye in */ final JPanel fontPartsPanel = new JPanel(new BorderLayout(5, 5)); fontPartsPanel.add(familySizePanel, BorderLayout.CENTER); final String[] fontStyles = new String[] { Util.getResourceString("plainName"), Util.getResourceString("boldName"), Util.getResourceString("italicName"), Util.getResourceString("boldItalicName") }; final StylePickList style = new StylePickList(fontStyles, Util.getResourceString("styleLabel")); style.addTitledPickListListener(this); fontPartsPanel.add(style, BorderLayout.EAST); fontComponents.add(style); /** create a panel for underline / line through */ final EffectPanel linePanel = new EffectPanel(); fontComponents.add(linePanel); /** create a panel for color choices */ final JPanel colorPanel = new JPanel(new GridLayout(2, 1, 3, 3)); colorPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("colorLabel"))); final ColorPanel fCol = new ColorPanel(Util.getResourceString("foregroundLabel"), Color.black, CSS.Attribute.COLOR); fCol.addColorPanelListener(this); fontComponents.add(fCol); colorPanel.add(fCol); sample.setForeground(Color.black); if (pickBgColor) { final ColorPanel bCol = new ColorPanel(Util.getResourceString("backgroundLabel"), Color.white, CSS.Attribute.BACKGROUND_COLOR); bCol.addColorPanelListener(this); fontComponents.add(bCol); colorPanel.add(bCol); sample.setBackground(Color.white); } /** create a panel to combine line and color choices */ final JPanel eastPanel = new JPanel(new BorderLayout()); eastPanel.add(linePanel, BorderLayout.NORTH); eastPanel.add(colorPanel, BorderLayout.SOUTH); /** add all font controls to our font panel */ add(fontPartsPanel, BorderLayout.CENTER); add(eastPanel, BorderLayout.EAST); add(previewPanel, BorderLayout.SOUTH); add(new JPanel(), BorderLayout.NORTH); add(new JPanel(), BorderLayout.WEST); } /** * construct a FontPanel and display a set of attributes * * @param frame the main frame having the TextResources * @param a the set of attributes to display */ public FontPanel(final AttributeSet a, final boolean pickBgColor) { this(pickBgColor); /** set the new FontPanel to display our set of attributes */ setAttributes(a); } /** * handle ColorChangeEvents from one of our color panels * * @param e the ColorPanelEvent to handle */ public void colorChanged(final ColorPanel.ColorPanelEvent e) { final ColorPanel source = (ColorPanel) e.getSource(); if (source.getAttributeKey() == CSS.Attribute.COLOR) { sample.setForeground(source.getColor()); } else if (source.getAttributeKey() == CSS.Attribute.BACKGROUND_COLOR) { sample.setBackground(source.getColor()); } } /** * set all components of this FontPanel to reflect a set of * attributes. * * @param a the set of attributes to show */ public void setAttributes(final AttributeSet a) { final Enumeration components = fontComponents.elements(); while (components.hasMoreElements()) { ((AttributeComponent) components.nextElement()).setValue(a); } } /** * get the set of attributes resulting from the settings on * this FontPanel. * * @return the set of attributes set in this FontPanel */ public AttributeSet getAttributes() { final SimpleAttributeSet attributes = new SimpleAttributeSet(); final Enumeration components = fontComponents.elements(); while (components.hasMoreElements()) { attributes.addAttributes(((AttributeComponent) components.nextElement()).getValue()); } return attributes; } public AttributeSet getAttributes(final boolean includeUnchanged) { if (includeUnchanged) { final SimpleAttributeSet attributes = new SimpleAttributeSet(); final Enumeration components = fontComponents.elements(); while (components.hasMoreElements()) { attributes.addAttributes(((AttributeComponent) components.nextElement()).getValue(includeUnchanged)); } return attributes; } else { return getAttributes(); } } public void reset() { Object c; for (int i = 0; i < fontComponents.size(); i++) { c = fontComponents.get(i); if (c instanceof FamilyPickList) { ((FamilyPickList) c).reset(); } else if (c instanceof SizePickList) { ((SizePickList) c).reset(); } else if (c instanceof StylePickList) { ((StylePickList) c).reset(); } } } /** * if another value was picked from a list, update the * sample */ public void valueChanged(final TitledPickList.TitledPickListEvent e) { final Object source = e.getSource(); final Font saveFont = sample.getFont(); if (source instanceof FamilyPickList) { sample.setFont(new Font(((FamilyPickList) source).getFamily(), saveFont.getStyle(), saveFont.getSize())); } else if (source instanceof SizePickList) { sample.setFont(new Font(saveFont.getFamily(), saveFont.getStyle(), Integer .parseInt((String) ((SizePickList) source).getSelection()))); /*adjustFontSize(Integer.parseInt((String) ((SizePickList) source).getSelection()))));*/ } else if (source instanceof StylePickList) { sample.setFont(new Font(saveFont.getFamily(), ((StylePickList) source).getFontStyle(), saveFont.getSize())); } } /* TODO * fix for bug id 4765271 * (see http://developer.java.sun.com/developer/bugParade/bugs/4765271.html) */ /** * extend TitledPickList with a way to set values * special to font family values */ class FamilyPickList extends TitledPickList implements AttributeComponent { private int setValCount = 0; private Object originalValue; /** * constructor * * @param options the options to be selectable in this list * @param titleText the title for the pick list */ FamilyPickList(final String[] options, final String titleText) { super(options, titleText); } /** * set the value of this TitledPickList * * @param a the set of attributes possibly having a * font family attribute this pick list could display * * @return true, if the set of attributes had a font family attribute, * false if not */ public boolean setValue(final AttributeSet a) { ignoreTextChanges = true; final String newSelection = Util.styleSheet().getFont(a).getFamily(); setSelection(newSelection); ignoreTextChanges = false; if (++setValCount < 2) { originalValue = newSelection; } return true; } public AttributeSet getValue(final boolean includeUnchanged) { final SimpleAttributeSet set = new SimpleAttributeSet(); final Object value = getSelection(); if (includeUnchanged || ((originalValue == null) && (value != null)) || ((originalValue != null) && (value != null) && (!originalValue.toString().equalsIgnoreCase( value.toString())))) { Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_FAMILY, value.toString()); set.addAttribute(HTML.Attribute.FACE, value.toString()); } return set; } public AttributeSet getValue() { return getValue(false); } public String getFamily() { return (String) getSelection(); } public void reset() { setValCount = 0; originalValue = null; } } /** * extend TitledPickList with a way to set values * special to font size values */ class SizePickList extends TitledPickList implements AttributeComponent { private int setValCount = 0; private String originalValue; /** * constructor * * @param options the options to be selectable in this list * @param titleText the title for the pick list */ SizePickList(final String[] options, final String titleText) { super(options, titleText); } /** * set the value of this TitledPickList * * @param a the set of attributes possibly having a * font size attribute this pick list could display * * @return true, if the set of attributes had a font size attribute, * false if not */ public boolean setValue(final AttributeSet a) { ignoreTextChanges = true; final int size = Util.styleSheet().getFont(a).getSize(); final String newSelection = Integer.toString(size); setSelection(newSelection); if (++setValCount < 2) { originalValue = newSelection; } return true; } public AttributeSet getValue(final boolean includeUnchanged) { final SimpleAttributeSet set = new SimpleAttributeSet(); final String value = (String) getSelection(); if ((includeUnchanged || (originalValue == null) && (value != null)) || ((originalValue != null) && (!originalValue.equalsIgnoreCase(value)))) { final String relativeSize = Integer.toString(getIndex() + 1); set.addAttribute(HTML.Attribute.SIZE, relativeSize); Util.styleSheet().addCSSAttributeFromHTML(set, CSS.Attribute.FONT_SIZE, relativeSize /*+ "pt"*/); } return set; } public AttributeSet getValue() { return getValue(false); } public void reset() { setValCount = 0; originalValue = null; } } /** * extend TitledPickList with a way to set values * special to font style values */ class StylePickList extends TitledPickList implements AttributeComponent { private int originalValue; private int setValCount = 0; /** * constructor * * @param options the options to be selectable in this list * @param titleText the title for the pick list */ StylePickList(final String[] options, final String titleText) { super(options, titleText); } /** * set the value of this TitledPickList * * @param a the set of attributes possibly having a * font style attribute this pick list could display * * @return true, if the set of attributes had a font style attribute, * false if not */ public boolean setValue(final AttributeSet a) { ignoreTextChanges = true; final boolean success = false; int styleNo = 0; String value; if (a.isDefined(CSS.Attribute.FONT_WEIGHT)) { value = a.getAttribute(CSS.Attribute.FONT_WEIGHT).toString(); if (value.equalsIgnoreCase(StyleConstants.Bold.toString())) { styleNo++; } } if (a.isDefined(CSS.Attribute.FONT_STYLE)) { value = a.getAttribute(CSS.Attribute.FONT_STYLE).toString(); if (value.equalsIgnoreCase(StyleConstants.Italic.toString())) { styleNo += 2; } } setSelection(styleNo); if (++setValCount < 2) { originalValue = styleNo; } ignoreTextChanges = false; return success; } public AttributeSet getValue() { final SimpleAttributeSet set = new SimpleAttributeSet(); final int styleNo = getIndex(); switch (styleNo) { case 0: Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, Util.CSS_ATTRIBUTE_NORMAL); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_STYLE, Util.CSS_ATTRIBUTE_NORMAL); break; case 1: Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, StyleConstants.Bold.toString()); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_STYLE, Util.CSS_ATTRIBUTE_NORMAL); break; case 2: Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, Util.CSS_ATTRIBUTE_NORMAL); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_STYLE, StyleConstants.Italic.toString()); break; case 3: Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, StyleConstants.Bold.toString()); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_STYLE, StyleConstants.Italic.toString()); break; } return set; } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { final String value = Util.CSS_ATTRIBUTE_NORMAL; final SimpleAttributeSet set = new SimpleAttributeSet(); final int styleNo = getIndex(); switch (styleNo) { case 0: Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_STYLE, value); break; case 1: Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, StyleConstants.Bold.toString()); break; case 2: Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_STYLE, StyleConstants.Italic.toString()); break; case 3: Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, StyleConstants.Bold.toString()); Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_STYLE, StyleConstants.Italic.toString()); break; } return set; } else { return getValue(); } } public int getFontStyle() { int fontStyle = 0; switch (getIndex()) { case 0: fontStyle = Font.PLAIN; break; case 1: fontStyle = Font.BOLD; break; case 2: fontStyle = Font.ITALIC; break; case 3: fontStyle = Font.BOLD | Font.ITALIC; break; } return fontStyle; } public void reset() { setValCount = 0; originalValue = -2; } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/TableDialog.java0100644 0000000 0000000 00000022017 12114157751 022461 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.Enumeration; import java.util.Vector; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.CSS; import javax.swing.text.html.HTML; /** * Dialog to manipulate HTML table attributes. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class TableDialog extends DialogShell { /** collection of all components with table related attributes */ Vector tableComponents = new Vector(); /** collection of all components with cell related attributes */ Vector cellComponents = new Vector(); /** selector for cell range to apply cell attributes to */ JComboBox cellRange; /** * constructor * * @param parent the main frame having the TextResources * @param title the title for this dialog * @param a the set of attributes to show and manipulate */ public TableDialog(final Frame parent, final String title) { super(parent, title); // add to content pane of DialogShell final Container contentPane = super.getContentPane(); contentPane.add(buildTablePanel(), BorderLayout.NORTH); contentPane.add(buildCellPanel(), BorderLayout.CENTER); // cause optimal placement of all elements pack(); } public void setTableAttributes(final AttributeSet a) { setComponentAttributes(tableComponents, a); } public void setCellAttributes(final AttributeSet a) { setComponentAttributes(cellComponents, a); } public void setComponentAttributes(final Vector v, final AttributeSet a) { final Enumeration components = v.elements(); AttributeComponent ac; while (components.hasMoreElements()) { ac = (AttributeComponent) components.nextElement(); ac.setValue(a); } } /** * get the set of attributes resulting from the settings on * this TableDialog. * * @return the set of attributes set in this TableDialog */ public AttributeSet getTableAttributes() { return getComponentAttributes(tableComponents); } public AttributeSet getCellAttributes() { //System.out.println("TableDialog getCellattributes=" + getComponentAttributes(cellComponents)); return getComponentAttributes(cellComponents); } private AttributeSet getComponentAttributes(final Vector v) { final SimpleAttributeSet attributes = new SimpleAttributeSet(); final Enumeration components = v.elements(); AttributeComponent ac; while (components.hasMoreElements()) { ac = (AttributeComponent) components.nextElement(); //System.out.println(ac.getValue()); attributes.addAttributes(ac.getValue()); } return attributes; } /** * build the contents of the cell panel * * this is moved to a separate method to make the code more * legible. */ private JPanel buildCellPanel() { new GridBagLayout(); new GridBagConstraints(); // construct cell format panel final JPanel cellPanel = new JPanel(new BorderLayout()); cellPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("cellPanelTitle"))); // construct tabbed pane for various cell settings final JTabbedPane tp = new JTabbedPane(); tp.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); // add general panel to tabbed pane final StylePanel sp = new StylePanel(StylePanel.TYPE_TABLE_CELL); cellComponents.add(sp); tp.add(Util.getResourceString("cellGenTabLabel"), sp); // add padding panel to cell components and tabbed pane final MarginPanel mp = new MarginPanel(); cellComponents.add(mp); tp.add(Util.getResourceString("cellMarginTabLabel"), mp); // construct border panel final BorderPanel bPanel = new BorderPanel(); // add border width panel and border color panel to cell components cellComponents.add(bPanel); // add border panel to tabbed pane tp.add(Util.getResourceString("cellBorderTabLabel"), bPanel); // create cell range panel final JPanel crPanel = new JPanel(); final String[] cellRangeSelection = new String[] { Util.getResourceString("thisCellRangeLabel"), Util.getResourceString("thisColRangeLabel"), Util.getResourceString("thisRowRangeLabel"), Util.getResourceString("allCellsRangeLabel") }; crPanel.add(new JLabel(Util.getResourceString("applyCellAttrLabel"))); cellRange = new JComboBox(cellRangeSelection); crPanel.add(cellRange); // get the preferred size of the tabbed pane /* int lastTabIndex = tp.getTabCount() - 1; Rectangle tabRect = tp.getBoundsAt(lastTabIndex); int prefWidth = tabRect.x + tabRect.width + 30; tp.setPreferredSize(new Dimension(prefWidth, 300)); */ // add tabbed pane and range selector to cell panel cellPanel.add(tp, BorderLayout.CENTER); cellPanel.add(crPanel, BorderLayout.SOUTH); return cellPanel; } /** * get the range of cells to apply cell attributes to */ public int getCellRange() { return cellRange.getSelectedIndex(); } /** * build the contents of the table panel * * this is moved to a separate method to make the code more * legible. */ private JPanel buildTablePanel() { // layout and constraints to use final GridBagLayout g = new GridBagLayout(); final GridBagConstraints c = new GridBagConstraints(); // table panel final JPanel tablePanel = new JPanel(g); tablePanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util .getResourceString("tablePanelTitle"))); // table width label JLabel lb = new JLabel(Util.getResourceString("tableWidthLabel")); Util.addGridBagComponent(tablePanel, lb, g, c, 0, 0, GridBagConstraints.EAST); // table width combo box final SizeSelectorPanel ssp = new SizeSelectorPanel(CSS.Attribute.WIDTH, HTML.Attribute.WIDTH, false, SizeSelectorPanel.TYPE_COMBO); Util.addGridBagComponent(tablePanel, ssp, g, c, 1, 0, GridBagConstraints.WEST); tableComponents.addElement(ssp); // table background color label lb = new JLabel(Util.getResourceString("tableBgColLabel")); Util.addGridBagComponent(tablePanel, lb, g, c, 0, 1, GridBagConstraints.EAST); // table background color panel final ColorPanel cp = new ColorPanel(null, Color.white, CSS.Attribute.BACKGROUND_COLOR); Util.addGridBagComponent(tablePanel, cp, g, c, 1, 1, GridBagConstraints.WEST); tableComponents.addElement(cp); // table alignment label lb = new JLabel(Util.getResourceString("alignLabel")); Util.addGridBagComponent(tablePanel, lb, g, c, 0, 2, GridBagConstraints.EAST); // table alignment combo box final String[] items = new String[] { Util.getResourceString("alignLeft"), Util.getResourceString("alignCenter"), Util.getResourceString("alignRight") }; final String[] names = new String[] { "left", "center", "right" }; final AttributeComboBox tAlgn = new AttributeComboBox(items, names, CSS.Attribute.TEXT_ALIGN, HTML.Attribute.ALIGN); Util.addGridBagComponent(tablePanel, tAlgn, g, c, 1, 2, GridBagConstraints.WEST); tableComponents.addElement(tAlgn); return tablePanel; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/ActionBuilder.java0100644 0000000 0000000 00000000150 12114157751 023030 0ustar000000000 0000000 package com.lightdev.app.shtm; public interface ActionBuilder { void initActions(SHTMLPanel panel); } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLPanelSingleDocImpl.java0100644 0000000 0000000 00000003210 12114157751 024565 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Created on 07.10.2006 * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; class SHTMLPanelSingleDocImpl extends SHTMLPanelImpl { public SHTMLPanelSingleDocImpl() { super(); } /* (non-Javadoc) * @see com.lightdev.app.shtm.SHTMLPanelImpl#initDocumentPane() */ protected void initDocumentPane() { super.initDocumentPane(); setDocumentPane(new DocumentPane(null, 1)); setEditorPane(getDocumentPane().getEditor()); doc = (SHTMLDocument) getEditorPane().getDocument(); registerDocument(); getDocumentPane().getEditor().setCaretPosition(0); splitPanel.addComponent(getDocumentPane(), SplitPanel.CENTER); } protected void initActions() { super.initActions(); addAction(findReplaceAction, new SHTMLEditorKitActions.SingleDocFindReplaceAction(this)); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/0040755 0000000 0000000 00000000000 13147564753 021475 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/default.css0100644 0000000 0000000 00000002607 12114157751 023622 0ustar000000000 0000000 a { text-decoration:underline; color:#0000ff; } ol { margin-top:6; font-family:SansSerif, Sans-Serif; margin-left:30; font-size:12; list-style-position:outside; } p { text-decoration:none; text-indent:0; font-weight:normal; padding-top:0; margin-top:6; vertical-align:top; font-style:normal; font-size:12; padding-right:0; padding-bottom:0; font-family:SansSerif, Sans-Serif; padding-left:0; color:#000000; margin-left:0; text-align:left; margin-bottom:0; margin-right:0; } h1 { text-decoration:none; text-indent:0; font-weight:bold; padding-top:0; margin-top:6; vertical-align:top; font-style:normal; font-size:18; padding-right:0; padding-bottom:0; background-color:#ffffff; font-family:SansSerif, Sans-Serif; padding-left:0; color:#000000; margin-left:0; text-align:left; margin-bottom:0; margin-right:0; } td { padding-top:0; margin-top:0; border-style:solid; vertical-align:top; padding-bottom:0; padding-right:0; border-color:#000000; padding-left:2; text-align:left; margin-left:0; margin-bottom:0; margin-right:0; } li { margin-top:0; } ul { margin-top:6; font-size:12; margin-left:30; list-style-position:outside; font-family:SansSerif, Sans-Serif; } simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/fmtImg.gif0100644 0000000 0000000 00000001626 12114157751 023376 0ustar000000000 0000000 GIF89a÷„„„„„„„„ÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ù,s   " 0…0ø⇠`±ÀÉ7rTøð€ÇGÀ²€IRŽ`à#˜!9PS €˜+r\Yó Ï  Y¶LJòÀ†ax 5*ªU-^-˜âJªQ=N4¨0 ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/cut.gif0100644 0000000 0000000 00000001571 12114157751 022745 0ustar000000000 0000000 GIF89a ÷ŽŸ±  —±¹ &##¹++¹¼¼Âÿÿÿ!ùÿ, @Vÿ ü§`àøG @€„ü[ñŸÿ PQ¢@ÿLØ@àÃ…!‚‰°€D BŠÀ2¡ƒB €@£À9þ[˜ñáÀ€V ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/bold_de.gif0100644 0000000 0000000 00000000114 12114157751 023532 0ustar000000000 0000000 GIF89a€ÿÿÿ!ù,#Œ©Ëí£œ”‚‹oÅl]u ÷e!¦lù¶òL×ö=;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/uline_on.gif0100644 0000000 0000000 00000002011 12114157751 023750 0ustar000000000 0000000 GIF87a÷Uªÿ$$U$ª$ÿIIUIªIÿmmUmªmÿ’’U’ª’ÿ¶¶U¶ª¶ÿÛÛUÛªÛÿÿÿUÿªÿÿ$$U$ª$ÿ$$$$U$$ª$$ÿ$I$IU$Iª$Iÿ$m$mU$mª$mÿ$’$’U$’ª$’ÿ$¶$¶U$¶ª$¶ÿ$Û$ÛU$Ûª$Ûÿ$ÿ$ÿU$ÿª$ÿÿIIUIªIÿI$I$UI$ªI$ÿIIIIUIIªIIÿImImUImªImÿI’I’UI’ªI’ÿI¶I¶UI¶ªI¶ÿIÛIÛUIÛªIÛÿIÿIÿUIÿªIÿÿmmUmªmÿm$m$Um$ªm$ÿmImIUmIªmIÿmmmmUmmªmmÿm’m’Um’ªm’ÿm¶m¶Um¶ªm¶ÿmÛmÛUmÛªmÛÿmÿmÿUmÿªmÿÿ’’U’ª’ÿ’$’$U’$ª’$ÿ’I’IU’Iª’Iÿ’m’mU’mª’mÿ’’’’U’’ª’’ÿ’¶’¶U’¶ª’¶ÿ’Û’ÛU’Ûª’Ûÿ’ÿ’ÿU’ÿª’ÿÿ¶¶U¶ª¶ÿ¶$¶$U¶$ª¶$ÿ¶I¶IU¶Iª¶Iÿ¶m¶mU¶mª¶mÿ¶’¶’U¶’ª¶’ÿ¶¶¶¶U¶¶ª¶¶ÿ¶Û¶ÛU¶Ûª¶Ûÿ¶ÿ¶ÿU¶ÿª¶ÿÿÛÛUÛªÛÿÛ$Û$UÛ$ªÛ$ÿÛIÛIUÛIªÛIÿÛmÛmUÛmªÛmÿÛ’Û’UÛ’ªÛ’ÿÛ¶Û¶UÛ¶ªÛ¶ÿÛÛÛÛUÛÛªÛÛÿÛÿÛÿUÛÿªÛÿÿÿÿUÿªÿÿÿ$ÿ$Uÿ$ªÿ$ÿÿIÿIUÿIªÿIÿÿmÿmUÿmªÿmÿÿ’ÿ’Uÿ’ªÿ’ÿÿ¶ÿ¶Uÿ¶ªÿ¶ÿÿÛÿÛUÿÛªÿÛÿÿÿÿÿUÿÿªÿÿÿ!ù,@æ%µHp Á‚ÛüS¨í_Cm8üñáĆÿ$M¼v±cCŽÿ-ü’¤IŽ(7NÌ8Q×?]_Rದ̑ ·yìX’#KŽÖLê ª³çJ†2Úú·ëßR‹YºÔ QbÊÿnMÔ9r(Ɉn´¹‰HÕäR–Aº„ú/mR‘mrþ+KW&G:%©mk2(йƒº êví[·OÿÅÁË4bLÇ4³nØÈbP¬Ûb–•+Ggâˆ@cº%¼(`¬0­šlÚPïÄ´4lj¸aX¾©cG 8ÒlÀ—UYV¤ñãÈ“;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/algnRt.gif0100644 0000000 0000000 00000001536 12114157751 023402 0ustar000000000 0000000 GIF89a÷ÐÌÄÿÿÿ!ùÿ,@;ÿ H° Áƒ(\È¡A†8œHaĆ3jÜ8ðâʼn#‚ ‰‘£É“(Sª\ɲ¥Ë—0c ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/delRow.gif0100644 0000000 0000000 00000001561 12114157751 023405 0ustar000000000 0000000 GIF89a ÷„ÿÿÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ù, N(p€Áƒ (\HB„ :|˜0bCŠ j,8 €A@Š y±£G’#EÀ’eJ”%YبѠÇKRÌ9q§ÅžiE;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/italic.gif0100644 0000000 0000000 00000001542 12114157751 023415 0ustar000000000 0000000 GIF89a÷ÿÿÿ!ùÿ,?ÿ H° Áƒ*\Ȱ¡Ã‡€h0@Š Jœˆ@‰3‚é1ã¿‘K’üHÒcHŠMÊœI³&Ä€;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/tableBorders.gif0100644 0000000 0000000 00000001556 12114157751 024565 0ustar000000000 0000000 GIF89a÷ÒÎÆÿÿÿ!ùÿ,@Kÿ H° ÁXȰ!C‚ „HQ¢Â-*tȱaÅŒ 1~y±äÆŽ'ª<1ãD—(QŽ4’dM0OÆÌ‰“¤Æ•*;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/redo.gif0100644 0000000 0000000 00000001505 12114157751 023100 0ustar000000000 0000000 GIF89a ÷„ÐÌÇÿÿÿ!ùÿ, @"ÿý@€Àƒ,HP¡@ƒ :D(ñaņ &¤È0 ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/insTab.gif0100644 0000000 0000000 00000001551 12114157751 023370 0ustar000000000 0000000 GIF89a÷ÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ù,F@°`AXÈPA„200¡Ä…3fœ¨Ð!Æ5ä²cG‡" ’ôÈòdJ•!K‚Dùr¢À“Þ|I0 ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/italic_de.gif0100644 0000000 0000000 00000001544 12114157751 024067 0ustar000000000 0000000 GIF89a÷ÿÿÿNغö¿¤+¹Ð4Q¨Iö¿LNNtæ¿ðpodùp û¿0’ö¿ÿÿÿÿTðp4Jö¿Ð4Q¨Iö¿t4QèöC8OQHùpft4Q üUQ8OQL @8OQWQüUQÈðplQXt4QÀs@ÜHÖffnenw‡wþÿ(Á(§z(ö¿C:\Eigene_Daten\Projekt\JBuilder6\SimplyHTML\src\com\lightdev\app\shtm\resourcesitalic.gifoZ ¬†f2òù  `Ø`ØÆÕ Ã;Ÿ1f2ÿÿ  PRH ÿÿÿHÜíFLÃê"Ÿ´ÃC:\Eigene_Daten\Projekt\JBuilder6\SimplyHTML\src\com\lightdev\app\shtm\resources\italic.gif_PRH ÿÿÿH¤òpp4Q¥ö¿NŒ5Q NNp4Q@Ìòp±¦ö¿NȦö¿NAAfðòpùµö¿N6¶ö¿¤+¹ff fåˆC !ù,@AH° Áƒ$Tˆ°¡Ã‡ 2À0¢Å‹)j\X±áƈh숱¤É“( R\˜qäÅ•+[²LI³¦M;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/icon_trans.gif0100644 0000000 0000000 00000003176 12114157751 024314 0ustar000000000 0000000 GIF89a@@÷!!!!))))))1!11!1119!9!!999B)B))BBBJ1JRRR1RRRRZZZ!9Z1ZZZZccc!9cccckkk!Bs)JsBJsss{)J{{{„)R„1R„JR„JZ„„„„„ŒŒ1RŒRŒŒŒŒŒ””1Z”œœœ9cœœœ¥9c¥¥¥¥­­­9k­­­µBkµµµµ½Æ½Bs½½½½½Î½½Þ½ÆÎ½ÆÞÆB{ÆsÆÆÆÆÆÞÆÎÖÆÎÞÎÎÎÎÎÞÎÎçÎÖçÖÖÖÖÖÞÖÖçÖÞçÞççÞçïçççççïçïïçï÷ïïïïï÷ï÷÷÷”ÿÿÿÿÿÿÿÿÿÿœÿÿÿ!ù_,@@@ÿ¿H° Áƒ*\Ȱ¡Ã‡#J$X!#äÀCTlØ¥ŠI*PRª\É2å“0bÀ##Æ8б…à K¬BeŠ%D’*Uâ •¡CÎpñH†&j@1¡P“Pˆ5ª²j@;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/gpl.txt0100644 0000000 0000000 00000043133 12114157751 023006 0ustar000000000 0000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/font.gif0100644 0000000 0000000 00000001531 12114157751 023114 0ustar000000000 0000000 GIF89a ÷„„„„ÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ù, 6` Aƒ8x€C†:\ñáC† Ndè°ãň *4øQ€D%.ô(Òc@;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/insCol.gif0100644 0000000 0000000 00000001556 12114157751 023404 0ustar000000000 0000000 GIF89a ÷„ÿÿÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ù, K`€A(xpàB… @@‘¢A‰#^ p±cÇ =~RäF“9¢ÄX%hì(æÌ‹/cÞlhÓ%L™;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/image.gif0100644 0000000 0000000 00000001737 12114157751 023240 0ustar000000000 0000000 GIF87a÷Uªÿ$$U$ª$ÿIIUIªIÿmmUmªmÿ’’U’ª’ÿ¶¶U¶ª¶ÿÛÛUÛªÛÿÿÿUÿªÿÿ$$U$ª$ÿ$$$$U$$ª$$ÿ$I$IU$Iª$Iÿ$m$mU$mª$mÿ$’$’U$’ª$’ÿ$¶$¶U$¶ª$¶ÿ$Û$ÛU$Ûª$Ûÿ$ÿ$ÿU$ÿª$ÿÿIIUIªIÿI$I$UI$ªI$ÿIIIIUIIªIIÿImImUImªImÿI’I’UI’ªI’ÿI¶I¶UI¶ªI¶ÿIÛIÛUIÛªIÛÿIÿIÿUIÿªIÿÿmmUmªmÿm$m$Um$ªm$ÿmImIUmIªmIÿmmmmUmmªmmÿm’m’Um’ªm’ÿm¶m¶Um¶ªm¶ÿmÛmÛUmÛªmÛÿmÿmÿUmÿªmÿÿ’’U’ª’ÿ’$’$U’$ª’$ÿ’I’IU’Iª’Iÿ’m’mU’mª’mÿ’’’’U’’ª’’ÿ’¶’¶U’¶ª’¶ÿ’Û’ÛU’Ûª’Ûÿ’ÿ’ÿU’ÿª’ÿÿ¶¶U¶ª¶ÿ¶$¶$U¶$ª¶$ÿ¶I¶IU¶Iª¶Iÿ¶m¶mU¶mª¶mÿ¶’¶’U¶’ª¶’ÿ¶¶¶¶U¶¶ª¶¶ÿ¶Û¶ÛU¶Ûª¶Ûÿ¶ÿ¶ÿU¶ÿª¶ÿÿÛÛUÛªÛÿÛ$Û$UÛ$ªÛ$ÿÛIÛIUÛIªÛIÿÛmÛmUÛmªÛmÿÛ’Û’UÛ’ªÛ’ÿÛ¶Û¶UÛ¶ªÛ¶ÿÛÛÛÛUÛÛªÛÛÿÛÿÛÿUÛÿªÛÿÿÿÿUÿªÿÿÿ$ÿ$Uÿ$ªÿ$ÿÿIÿIUÿIªÿIÿÿmÿmUÿmªÿmÿÿ’ÿ’Uÿ’ªÿ’ÿÿ¶ÿ¶Uÿ¶ªÿ¶ÿÿÛÿÛUÿÛªÿÛÿÿÿÿÿUÿÿªÿÿÿ!ù,@¼µ H° ÀmnþýK(À?~n܈QèF 5m×´YÛf-ã6&TøŒI“€HÊ8R¡¤@µ!ù¯‘@I4sÒ\©­å?¶ðh¤Mm3I²q¨Í¡JmÛý‹3ñŸ1üÄŠ#õ(€8ˆ¶¬›¯:¥HÔ$N…i›Ò $)Œ @`ÁŸ– Ö0SÁ¼»€@R"I˜&N~pÖþTKQ¯aAa ï½à,X9Ú”-Z[@;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/appImage.jpg0100644 0000000 0000000 00000004275 12114157751 023714 0ustar000000000 0000000 ÿØÿàJFIF––ÿÛC   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcÿÛC//cB8BccccccccccccccccccccccccccccccccccccccccccccccccccÿÀk–"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ëRì©£s5®™5”²hÀŽH ©&z°lüˆ5l­fkÀA¨è÷˜'eÏ‘iü0+\Š@V£e«S ÐfZaZ°ËQ• «í¤ÅJVšV‘Q°©È¨ØPv ²Â«Êp()¤lT]M9Žæ§À ¡˜¤"¤Å4ŠŒŠ)ÆŠî¥ÅŠcÅ<þ»òÇï#E#¨ÚA$~Ö…¬ëwgÊhÖ@`Fi÷P­Í¬°9!eB„B1XÞ ¸óü; ,ð3DÙíƒ?"(²E4ŠÓ ""˜ELE0Š„Ši)Â( ‰…N£z +IÀ¬ùß&­]K´c5M»f‚Ð"w4üTp) ˆ¦RM"‘‘E)¢€;qKEÃMržºòüA­iç…óšXÔiÿÙk¬5ÁÞçNøƒoplžQóó(Ÿl·èh¸4˜§UY/"Y<µmÎN¨,O\ãO§¾h1Ò)©8`»–E'³.:}2?\Ô„P2")„T¤S d,*­Ìæ­LÁ“\æ£vd—ËJ @ò¥ÀéVâ PÙÁµC5X–â(‡Ìà~4Á…FÕê0÷Å5o#sÁ D¦šh ”ÐQ¢”Ñ@½´Ps 5Â|B„Å=­Ú’)¦ÓŸý˜~UÞç¼goçh’à(99íŒø°Z·¨ê‰m¡6¢™(Ñ«)FìyôÎj½”ʺj_…`÷@¶G!W8UúŠÎÑÞ-wÁ£Oi£7IŒ)8ÚTü™öáhðýàM"ÖÒõ‚ d¹ùN ã> ä~ô˜¦iã*ÄŒûò8È þUfÛ-l…ŽIƒúUd…¤ö¡]°C:ñÛ?ãZYX“àzšÀÓJ%FèÀÕ=Fé`„œÐ šÍð‰ ƒÍs‹|¨å°Y©š…Û\LNxªT{¦Õ'`£Ú©¼®ç,ÄÓh Ìp&\LŸZ¹b®î1š}¦•4Ä3«ï[vöi€4 !‘¡U©qO"šh,ŒÑJh gn(Å¡¼œÛZÉ2Æd*8@q“AÌ6îæ8 ³¸U{“è+‹šÿSÖ‹ßE˜¬-Ø´q÷“o=ºž:öíUõ »»«Âo_”'Œ{QÜZjߨ±Ç¦È ¡fÈO•×<•'Ó“ýh*Ö3ôm\êwV“’jä^@ Ÿ~œv­íE§_ÞÃq;…ŒdvU<àžÜô®vhõ-«µ¸ ™` (ÉÏÔVmòg>c RC~îs}sÍHéîüCon³Ì=7ÇøÖ Þ±wtÇÌíô Ò´ÓÜï¼#÷‡Gœ³žÄúYú¥ªÅ9Ø@P~´Ø5+‹c”bG¡¨¯uK‹¾àz «½€Ûž*3’h “YÚ=Ü›Põ­tðÌ„|Ò Ðm.$¹‹„I®ÉjhÍÇḗ™µ]‹N··$c>µ¨â q@ÊÌ¡z ‰ªw PQS Hj3@Æš(4P3¶Ú3ÚL±ýò„.}qÅH)h9>–U¼`.†p1½x {T¢ÈÑI”@ppIÎ uü=? ×Ô´)ì˜ÉïmùäT{ëTn,AÑõ#§ãéA¶ûš•¿ökGp›fƒGòðqsøÔzEÌï©FÓ°ûÂF%wÁãó$Uùã[”±*ª/=Î<¯åUà쓉"ûÝwzu ›jt³Y­ÅÚÉov¬êàº3s€GC¯‹©µ¡Ì·Eùû߯OjMC1«6J¼¹ÂŽ0¾ÿáY«’¶ÔBÄöì\ÆWdëõ's¿¢éßH²±w>µgKðÜÓHéLi×iêk°‚Ý ˆ"(tÅlŠÞÖ;h‚F Oj•ª& ’¨TïP½"³Š……NõÐR"4ÃOja ¡†Š  íA§QŠu0ìÖeþ“ù’܈eç ù[އüúðkF’€NÇ ¨Çqi)I¢(Ià(á‡^1ǧùÍ\Òô»«‰îò2$$0œbºÆ¤4Ìb&2ܱr„p+F ;{uÄQ*aSÑ@®ÄÀ) j6§šcP2&¨¦z…è)Þ¡zê»ÐR"jŒÔL40ÑA¢ŸÿÙsimplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/table.gif0100644 0000000 0000000 00000001600 12114157751 023232 0ustar000000000 0000000 GIF89a÷ÄÄÄÏÏÄúúúÿÿÿ!ùÿ,@]ÿ H° Á  paC† @±¢Å‹&t¨"ÇbYqbLJ(#N À²¥K—!’y°¦Í™45žÜù1&€—@[úäé"H““õ4hL›6;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/insRow.gif0100644 0000000 0000000 00000001560 12114157751 023431 0ustar000000000 0000000 GIF89a÷„ÿÿÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ù,M`€Áƒ (\X!Â… >„H ¡Ä^Üh0€ÁŒxÄ(€@I‰"S0i2äH%O†ü˜±&GŠoR´('O‡5" ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/SimplyHTML_de.properties0100644 0000000 0000000 00000027714 13147564753 026235 0ustar000000000 0000000 # # SimplyHTML, a word processor based on Java, HTML and CSS # Copyright (C) 2002 Ulrich Hilger # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # # SimplyHTML_de.properties # # resource bundle with strings for application SimplyHTML # - German Language - # # plug-in menu definition # # this menu is defined by present plugins almost entirely # at least the menu label must be configured here. pluginLabel=Plugin # plug-in menu items managePluginsLabel=Plug-ins verwalten... managePluginsTip=Plug-in Einstellungen \u00e4ndern... # file menu definition # # each word (delimited by blanks) is a key for # menu item definitions (- = separator) # # file + 'Label' = label for file menu # new + 'Label' = label for 'New' menu item # open + 'Label' = label for 'Open' menu item # etc. fileLabel=Datei #file menu items newLabel=Neu newTip=neues Dokument erzeugen... openLabel=\u00d6ffnen... openTip=Dokument \u00f6ffnen... documentTitleLabel=Dokumenttitel... setDefaultStyleRefLabel=Bestehende Standard-Formatvorlage verwenden closeLabel=Schlie\u00dfen closeAllLabel=Alle Schlie\u00dfen saveLabel=Sichern saveAllLabel=Alle sichern saveTip=Dokument speichern... saveAsLabel=Sichern unter... exitLabel=Beenden # edit menu definition editLabel=Bearbeiten # edit menu items undoLabel=R\u00fcckg\u00e4ngig undoTip=R\u00fcckg\u00e4ngig redoLabel=Wiederholen redoTip=Wiederholen cutLabel=Ausschneiden cutTip=Ausschneiden copyLabel=Kopieren copyTip=Kopieren pasteLabel=Einf\u00fcgen pasteTip=Einf\u00fcgen pastePlainTextLabel=Einf\u00fcgen als Text pasteHTMLLabel=Einf\u00fcgen als HTML pastePlainTextTip=Einf\u00fcgen als Text (ohne Formatierung) selectAllLabel=Alles Ausw\u00e4hlen editPrefsLabel=Optionen... findReplaceLabel=Suchen & Ersetzen findReplaceTip=Suchen & Ersetzen #insert menu definition insertLabel=Einf\u00fcgen #insert menu items insertTableLabel=Tabelle Einf\u00fcgen... insertImageLabel=Bild... insertImageTip=Bild Einf�gen... # format menu definition formatLabel=Format # format menu items fontLabel=Zeichen... fontTip=Zeichen formatieren... clearFormatLabel=Formattierung l\u00f6schen clearFormatTip=Formattierung l\u00f6schen fontBoldLabel=Fett fontBoldImage=resources/bold.gif fontBoldTip=fett an- und ausschalten fontItalicLabel=Kursiv fontItalicImage=resources/italic.gif fontItalicTip=kursiv an- und ausschalten fontUnderlineLabel=Unterstreichung fontUnderlineImage=resources/uline.gif fontUnderlineTip=unterstreichen an- und ausschalten fontColorTip=Textfarbe fontColorLabel=Textfarbe selectedFontColorLabel=Selektierte Textfarbe redFontColorLabel=Rot greenFontColorLabel=Gr�n blueFontColorLabel=Blau formatTableLabel=Tabelle... formatTableTip=Tabelle formatieren... toggleBulletsLabel=Aufz\u00e4hlung ein/aus toggleBulletsTip=Aufz\u00e4hlung ein/aus toggleNumbersLabel=Numerierung ein/aus toggleNumbersTip=Numerierung ein/aus formatListLabel=Liste... formatListTip=Listenformat \u00e4ndern formatParaLabel=Absatz... formatParaTip=Absatzformat \u00e4ndern editNamedStyleLabel=Formatvorlage... editNamedStyleTip=Formatvorlagen bearbeiten paraAlignLeftLabel=Linksb\u00fcndig paraAlignLeftTip=Paragraph linksb\u00fcndig ausrichten paraAlignCenterLabel=Zentriert paraAlignCenterTip=Paragraph zentriert ausrichten paraAlignRightLabel=Rechtsb\u00fcndig paraAlignRightTip=Paragraph rechtsb\u00fcndig ausrichten editLinkLabel=Verkn\u00fcpfung... editLinkTip=Verkn\u00fcpfung bearbeiten... editAnchorsLabel=Anker... editAnchorsTip=Anker setzen und \u00e4ndern... increaseFontSizeLabel=Gr��er decreaseFontSizeLabel=Kleiner # table menu definition # table menu items tableLabel=Tabelle nextTableCellLabel=N\u00e4chste Zelle prevTableCellLabel=Vorige Zelle insertTableRowLabel=Zeile einf\u00fcgen insertTableColLabel=Spalte einf\u00fcgen appendTableRowLabel=Zeile anh\u00e4ngen appendTableColLabel=Spalte anh\u00e4ngen deleteTableRowLabel=Zeile l\u00f6schen deleteTableColLabel=Spalte l\u00f6schen # misc menu definition misc=elemTree gc test miscLabel=Verschiedenes # misc menu items elemTreeLabel=Elementbaum gcLabel=Garbage Collection testLabel=Test: derzeit unbenutzt # help menu definition help=helpTopics - about helpLabel=Hilfe # help menu items helpTopicsLabel=Hilfethemen helpTopicsTip=Hilfethemen aboutLabel=\u00dcber SimplyHTML... # About frame aboutFrameTitle=\u00dcber dieses Programm # Font dialog fontDialogTitle=Zeichen formatieren # Font panel uLineLabel=Unterstrichen strikeLabel=Durchgestrichen previewLabel=Vorschau previewText=Dies ist ein Test familyLabel=Familie sizeLabel=Gr\u00f6\u00dfe plainName=Standard boldName=fett italicName=kursiv boldItalicName=fett kursiv styleLabel=Stil effectLabel=Effekt colorLabel=Farbe foregroundLabel=Vordergrund: backgroundLabel=Hintergrund: noLineLabel=kein # Paragraph style panel textIndentLabel=Einr\u00fcckung: alignLabel=Ausrichtung: alignLeft=links alignCenter=zentriert alignRight=rechts valignLabel=Vert. Ausrichtung: valignTop=oben valignMiddle=mittig valignBottom=unten valignBaseline=an Basislinie # Margin panel marginLabel=Au\u00dfen paddingLabel=Innen # Table dialog tableDialogTitle=Tabelle formatieren tablePanelTitle=Tabellenformat cellPanelTitle=Zellenformat tableWidthLabel=Breite: tableBgColLabel=Hintergrundfarbe: cellMarginTabLabel=Abstand cellBorderTabLabel=Rahmen borderWidthLabel=Breite borderColorLabel=Farbe: thisCellRangeLabel=diese Zelle thisColRangeLabel=diese Spalte thisRowRangeLabel=diese Zeile allCellsRangeLabel=alle Zellen applyCellAttrLabel=Anwenden auf cellGenTabLabel=Allgemein # Paragraph style dialog paraStyleDialogTitle=Absatzformat fontTabLabel=Zeichen paraTabLabel=Absatz stylePanelLabel=Formatvorlage saveStyleButtonLabel=Speichern saveStyleAsButtonLabel=Speichern unter... deleteStyleButtonLabel=L\u00f6schen # Named style dialog namedStyleDialogTitle=Formatvorlagen bearbeiten # Tag names cTagNamePara=Absatz cTagNameHead1=\u00dcberschrift 1 cTagNameHead2=\u00dcberschrift 2 cTagNameHead3=\u00dcberschrift 3 cTagNameHead4=\u00dcberschrift 4 cTagNameHead5=\u00dcberschrift 5 cTagNameHead6=\u00dcberschrift 6 cTagNameLink=Verkn\u00fcpfung cTagNameUL=Bullet-Liste cTagNameOL=nummerierte Liste # List dialog listDialogTitle=Liste formatieren listTypeLabel=Typ: listPositionLabel=Position: listIndentTitle=Einr\u00fcckung: listTypeDecimal=1.,2.,3.,4. listTypeLowerRoman=i.,ii.,iii.,iv. listTypeUpperRoman=I.,II.,III.,IV. listTypeLowerAlpha=a.,b.,c.,d. listTypeUpperAlpha=A.,B.,C.,D. listTypeDisc=ausgef\u00fcllter Kreis als Bulletzeichen listTypeCircle=leerer Kreis Bulletzeichen listTypeSquare=rechteckiges Bulletzeichen listTypeNone=kein Bulletzeichen, keine Nummerierung listPosInside=einger\u00fcckt listPosOutside=ausger\u00fcckt # Link dialog linkDialogTitle=Verkn\u00fcpfung erzeugen / bearbeiten linkStyleLabel=Format: linkTypeLabel=Typ: linkAddressLabel=Adresse: linkBrowseLabel=Durchsuchen... linkAnchorLabel=Anker: linkDisplayLabel=zeige Verkn\u00fcpfung als showAsTextLabel=Text showAsImageLabel=Bild linkTextLabel=Text linkImageLabel=Bild setImageLabel=\u00c4ndern... linkTypeName1=relativ linkTypeName2=lokal linkTypeName3=WWW linkTypeName4=Gopher linkTypeName5=FTP linkTypeName6=Telnet linkTypeName7=Newsgroup linkTypeName8=E-Mail setImageLabel=Set... # Anchor dialog anchorDialogTitle=Anker bearbeiten anchorPanelLabel=definierte Anker docPanelLabel=Dokument addAnchorTitle=Anker hinzuf\u00fcgen addAnchorText=Name des Ankers? # Element Tree Dialog elementTreeTitle=Elementbaum # PluginManager dialog pluginManagerDialogTitle=Plug-ins verwalten pluginPanelTitle=Installierte Plugins deactivatePlugin=deaktivieren activatePlugin=aktivieren pluginSettingsPanelTitle=Plugin Einstellungen togglePluginActivationCheckbox=aktiviert dockLocationLabel=Docken auf Seite: pluginDockLocationNone=keine pluginDockLocationTop=oben pluginDockLocationRight=rechts pluginDockLocationBottom=unten pluginDockLocationLeft=links # Preferences dialog prefsDialogTitle=Optionen prfShareDocResourcesLabel=diese Option hat absolut keine Funktion und ist deshalb v\u00f6llig obsolet prfLafLabel=Look and Feel: prefsPasteModeLabel=Standard-Einf\u00fcgemodus: prfWriteModeLabel=HTML Dateien speichern als prfWriteModeHTML32Label=HTML 3.2 (z.B. JavaHelp) prfWriteModeHTML4Label=HTML 4 linkDefaultStyleSheetLabel=verwende Standard-Formatvorlage f\u00fcr neue Dokumente pasteModeHTML=Einf\u00fcgen als HTML pasteModePlainText=Einf\u00fcgen als Text # Image dialog imageDialogTitle=Bild bearbeiten/einf\u00fcgen imgDirPanelTitle=Bilddateien addImgBtnTitle=Hinzuf\u00fcgen... delImgBtnTitle=L\u00f6schen imgPreviewPanelTitle=Vorschau imgPropertiesPanelTitle=Eigenschaften imgScaleLabel=Skalierung: imgWidthLabel=Breite: imgHeightLabel=H\u00f6he: imgHSpaceLabel=horiz. Abstand: imgVSpaceLabel=vert. Abstand: imgAlignLabel=Ausrichtung: imgAlignTop=oben imgAlignMiddle=mittig imgAlignBottom=unten imgAlignLeft=links imgAlignCenter=zentriert imgAlignRight=rechts oWidthLabel=orig. Breite: oHeightLabel=orig. H\u00f6he: imgBorderLabel=Rahmen: # Find & replace dialog findReplaceDialogTitle=Suchen & Ersetzen findNext=Suchen... searchFromStart=vom Anfang aus suchen searchFromStart.tooltip=Von Anfang an (und nicht ab der Cursorposition) suchen. searchDown=nach unten suchen searchDown.tooltip=Von oben nach unten suchen. wholeWordsOnly=nur ganze Worte wholeWordsOnly.tooltip=Treffer auf ganze W�rter beschr�nken. searchUp=nach oben suchen searchUp.tooltip=Von unten nach oben suchen. matchCase=Gro\u00df- und Kleinschreibung beachten matchCase.tooltip=Gibt an, ob Gro�/Kleinschreibung bei der Suche beachtet werden soll. matchApproximately=Approximative Suche matchApproximately.tooltip=Gibt an, ob approximative Treffer angezeigt werden sollen,
      z.B. Suche nach 'files' findet 'flies'. replaceWith=Ersetzen mit: textToFind=Suche Text: replace=Ersetzen... searchWholeProject=gesamtes Projekt durchsuchen noMoreOccurrencesFound=Keine (Weiteren) gefunden allOccurrencesReplaced=alle ersetzt replaceThisQuery=Ersetze dieses Vorkommnis von replaceYes=Ja replaceNo=Nein replaceAll=Alle replaceDone=Fertig # Messages saveChangesQuery=M\u00f6chten Sie die \u00c4nderungen speichern? confirmClosing=Schlie\u00dfen best\u00e4tigen fileExistsQuery= besteht bereits, \u00fcberschreiben? confirmSaveAs=Sichern unter best\u00e4tigen confirmDelete=L\u00f6schen best\u00e4tigen deleteFileQuery=M\u00f6chten Sie die Datei wirklich l\u00f6schen? deleteStyleQuery=M\u00f6chten Sie die Formatvorlage wirklich l\u00f6schen? # Error messages docNameMissingError=Bitte geben Sie Name und Ablageort f\u00fcr das Dokument mit Befehl 'Sichern unter' an. unableToUndoError=R\u00fcckg\u00e4ngig nicht m\u00f6glich: unableToRedoError=Wiederholen nicht m\u00f6glich: unableToOpenFileError=Datei kann nicht ge\u00f6ffnet werden cantCreateURLError=Kann keine g\u00fcltige URL erzeugen f\u00fcr\n helpNotFoundError=Hilfedatei konnte nicht ge\u00f6ffnet werden.\nBitte lesen Sie Kapitel 'Installation' der Datei 'readme.txt' # Miscellaneous text htmlFileDesc=HTML Dateien imageFileDesc=Bilddateien defaultDocName=Ohne Titel cancelBtnName=Abbrechen closeBtnName=Schlie\u00dfen okBtnName=OK leftLabel=links: rightLabel=rechts: topLabel=oben: bottomLabel=unten: insertTableTitle=Tabelle einf\u00fcgen insertTableMsg=Wieviele Spalten? close=Schliessen standardStyleName=standard styleNameInputTitle=Formatvorlage speichern styleNameInputText=Name der Formatvorlage? newStyleDefaultName=neue Formatvorlage docTitleTitle=Dokumenttitel bearbeiten docTitleQuery=Neuer Titel: layoutTabTitle=Layout Ansicht htmlTabTitle=HTML Code Ansicht printLabel=Drucken...simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/new.gif0100644 0000000 0000000 00000001553 12114157751 022743 0ustar000000000 0000000 GIF89a÷ÏÏÄúúúÿÿÿ!ùÿ,@Hÿ H° @*TÀ Cƒ @Ø`D‰ +À±£F‹‚ 9ѢǓ:¼È0å?–;Ž ùæÂš$K´)R¥Ã€;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/fr.gif0100644 0000000 0000000 00000001654 12114157751 022563 0ustar000000000 0000000 GIF89a ÷$:$$:$$$$//::¤¤šÄįÄĺÄÄÄÚÚäïïºïïïïïúúúÚúúäúúúÿÿÿ!ùÿ, @‰ÿ ,0€ÀÀ†„°A€† )(`€†hXà À Hp0A€…”ü7€ƒ H(@À‚Œ6d`aE È à` Š6„Ðð_ax™µáJ’ÿm¡á…¼T€±*Ú„TÀ(ÃF;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/ol.gif0100644 0000000 0000000 00000001544 12114157751 022564 0ustar000000000 0000000 GIF89a÷ÐÌÄÿÿÿ!ùÿ,@Aÿ H° Áƒ  P!ÆH„H±âÀ‡ À±cLj3ŠI²bÈS‚ĘñäH–%IbLédL‘;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/algnLft.gif0100644 0000000 0000000 00000001536 12114157751 023542 0ustar000000000 0000000 GIF89a÷ÐÌÄÿÿÿ!ùÿ,@;ÿ H° Áƒÿ(\È¡A†8œHaĆ3jÜxðâʼn#‚ ‰‘£É“(Sª\ɲ¥Ë—0c ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/save.gif0100644 0000000 0000000 00000001605 12114157751 023106 0ustar000000000 0000000 GIF89a÷€}¾ÃÄÐÌÄÿÿÿ!ùÿ,@bÿ H°`AXÈp!BHœHQ"Á‡ *„8aŠIòÆŒEv ð$KŽ=‚L ³¤Í›#Aê¬yåÄ‹ QÒªPèC ,“*}‰t¦€£+[NŠsd@;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/uline.gif0100644 0000000 0000000 00000000127 12114157751 023262 0ustar000000000 0000000 GIF89a¡€}ÿÿÿÿÿÿ!ù,(”©Ëí£\ ao«‡;i›2`I‘¢©>U hjMR†ÛúÎ÷I;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/SimplyHTML_common.properties0100644 0000000 0000000 00000013657 13147564753 027136 0ustar000000000 0000000 # # SimplyHTML, a word processor based on Java, HTML and CSS # Copyright (C) 2002 Ulrich Hilger # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # # SimplyHTML.properties # # resource bundle with strings for application SimplyHTML # - English Language (default) - # # menubar definition # # each word (delimited by blanks) is a key for # menu definitions below menubar=file edit insert format table plugin misc help # toolbar definition # # each word (delimited by blanks) is a key for # an action in the tool bar (- = separator) toolBar=new open save - undo redo - cut copy paste - insertImage formatImage findReplace - helpTopics # format toolbar definition # # each word (delimited by blanks) is a key for # an action in the tool bar (- = separator) formatToolBar=fontFamily fontSize - fontBold fontItalic fontUnderline fontColor clearFormat # para toolbar definition # # each word (delimited by blanks) is a key for # an action in the tool bar (- = separator) paraToolBar=setTag setStyle - paraAlignLeft paraAlignCenter paraAlignRight - toggleBullets toggleNumbers # plug-in menu definition # # this menu is defined by present plugins almost entirely # at least the menu label must be configured here. plugin=managePlugins - # file menu definition # # each word (delimited by blanks) is a key for # menu item definitions (- = separator) # # file + 'Label' = label for file menu # new + 'Label' = label for 'New' menu item # open + 'Label' = label for 'Open' menu item # etc. file=new - open - documentTitle setDefaultStyleRef - close closeAll - save saveAs saveAll - print exit #file menu items newImage=resources/new.gif openImage=resources/open.gif saveImage=resources/save.gif saveAllImage=resources/saveAll.gif # edit menu definition edit=undo redo - cut copy paste pasteOther - findReplace - selectAll - editPrefs popup=undo redo - cut copy paste pasteOther # edit menu items undoImage=resources/undo.gif redoImage=resources/redo.gif cutImage=resources/cut.gif copyImage=resources/copy.gif pasteImage=resources/paste.gif pasteOtherImage=resources/paste.gif editPrefsImage=resources/options.gif findReplaceImage=resources/fr.gif #insert menu definition insert=insertTable - insertImage #insert menu items insertTableImage=resources/table.gif insertImageImage=resources/image.gif # format menu definition format=font - formatPara paraAlignLeft paraAlignCenter paraAlignRight fontBold fontItalic - increaseFontSize decreaseFontSize - fontColor selectedFontColor redFontColor greenFontColor blueFontColor blackFontColor removeFontColor - editNamedStyle - formatTable - formatList toggleBullets toggleNumbers - formatImage - editLink editAnchors - clearFormat # format menu items fontImage=resources/font.gif clearFormatImage=resources/clearFormat.gif formatTableImage=resources/fmtTab.gif toggleBulletsImage=resources/ul.gif toggleNumbersImage=resources/ol.gif formatParaImage=resources/fmtPara.gif paraAlignLeftImage=resources/algnLft.gif paraAlignCenterImage=resources/algnCtr.gif paraAlignRightImage=resources/algnRt.gif editLinkImage=resources/link.gif # menu accelerators selectAllAccelerator=control A fontBoldAccelerator=control B copyAccelerator=control C increaseFontSizeAccelerator=control shift E removeFontColorAccelerator=control D decreaseFontSizeAccelerator=control E findReplaceAccelerator=control F greenFontColorAccelerator=control G editLinkAccelerator=control H fontItalicAccelerator=control I blackFontColorAccelerator=control K blueFontColorAccelerator=control L newAccelerator=control N openAccelerator=control O exitAccelerator=control Q redFontColorAccelerator=control R saveAccelerator=control S clearFormatAccelerator=control T pasteAccelerator=control V pasteOtherAccelerator=control shift V fontUnderlineAccelerator=control U fontColorAccelerator=control W selectedFontColorAccelerator=control shift W lastFontColorAccelerator=control W nextTableCellAccelerator=TAB prevTableCellAccelerator=shift TAB cutAccelerator=control X redoAccelerator=control Y undoAccelerator=control Z # table menu definition table=nextTableCell prevTableCell - appendTableRow appendTableCol - insertTableRow insertTableCol - deleteTableRow deleteTableCol - toggleTableHeaderCell - moveTableRowUp moveTableRowDown moveTableColumnLeft moveTableColumnRight # table menu items deleteTableColImage=resources/delCol.gif insertTableRowImage=resources/insRow.gif insertTableColImage=resources/insCol.gif deleteTableRowImage=resources/delRow.gif # misc menu definition misc=elemTree gc test # misc menu items # help menu definition help=helpTopics - about # help menu items helpTopicsImage=resources/help.gif # Action properties fontBoldImage=resources/bold.gif fontItalicImage=resources/italic.gif fontUnderlineImage=resources/uline.gif fontColorImage=resources/fontColor.gif # About frame appImage=resources/appImage.jpg appIcon=resources/icon_trans.gif # Splah screen splashImage=resources/splashImage.jpg # Link dialog linkType1=relative linkType2=file linkType3=http linkType4=gopher linkType5=ftp linkType6=telnet linkType7=news linkType8=mailto # Anchor dialog # Element Tree Dialog # PluginManager dialog # Image dialog # Find & replace dialog # Messages # Error messages # Miscellaneous text okBtnName=OK standardStyleName=standard # not use shtml standard style for new documents use_std_styles=false approximate_search_threshold = 0.65 simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/link.gif0100644 0000000 0000000 00000001650 12114157751 023105 0ustar000000000 0000000 GIF89a÷„ú„„„„„„ÄÄÄÏÏÄúúúÿÿÿ!ùÿ,@…ÿ ,@° ÀƒX8 á , À€À 4€ð  0@2äGŽ@ðÏᎶt¹&BFª¹Ó€M:W~\éó A[þHQåB†$0³aR|ê’€€¥ž‹“,XŸfÍŠ6íØµÉþ3+0 ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/open.gif0100644 0000000 0000000 00000001601 12114157751 023105 0ustar000000000 0000000 GIF89a÷„„ÏÏÄúúúúúÿÿÿ!ùÿ,@^ÿ H° A PÈp!€ƒ#JøῊÀ±cGŒC^’ É’ 8É’¥É=–I³àÉšIÚTÙR'D*jŒTcL™;a 0sdÏ›8#;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/copy.gif0100644 0000000 0000000 00000001646 12114157751 023127 0ustar000000000 0000000 GIF89a ÷:Oz¯ºÄÏDOd¯Oš¤¯$$$$$$$$Ä//šÄÄÄúúÚúúäúúïúúúÿÿÿ!ùÿ, @ƒÀ¿ƒ*Áဢ†hÂÀ„„ <PÀA*\áĈ èÀ‘D 6$0P &HbŽ"Ä<¨àQ4 A‹ØØpcƒX>àØÐ â$ ÂBL lA;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/algnCtr.gif0100644 0000000 0000000 00000001536 12114157751 023545 0ustar000000000 0000000 GIF89a÷ÐÌÄÿÿÿ!ùÿ,@;ÿ H° Áƒ(\È¡A†8œHaĆ3jÜXðâʼn#‚ ‰‘£É“(Sª\ɲ¥Ë—0c ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/SimplyHTML.properties0100644 0000000 0000000 00000026343 13147564753 025562 0ustar000000000 0000000 # # SimplyHTML, a word processor based on Java, HTML and CSS # Copyright (C) 2002 Ulrich Hilger # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # # SimplyHTML.properties # # resource bundle with strings for application SimplyHTML # - English Language (default) - # # plug-in menu definition # # this menu is defined by present plugins almost entirely # at least the menu label must be configured here. pluginLabel=Plugin # plug-in menu items managePluginsLabel=Manage plug-ins... managePluginsTip=change plug-in settings... # file menu definition # # each word (delimited by blanks) is a key for # menu item definitions (- = separator) # # file + 'Label' = label for file menu # new + 'Label' = label for 'New' menu item # open + 'Label' = label for 'Open' menu item # etc. fileLabel=File #file menu items newLabel=New newTip=create new document... openLabel=Open... openTip=open a document... documentTitleLabel=Document title... setDefaultStyleRefLabel=Use existing default style sheet closeLabel=Close closeAllLabel=Close all saveLabel=Save saveAllLabel=Save all saveTip=save a document... saveAsLabel=Save as... exitLabel=Exit # edit menu definition editLabel=Edit # edit menu items undoLabel=Undo undoTip=undo redoLabel=Redo redoTip=redo cutLabel=Cut cutTip=cut copyLabel=Copy copyTip=copy pasteLabel=Paste pasteTip=paste pastePlainTextLabel=Paste plain text #pastePlainTextTip=Paste as plain text (without formatting) pasteHTMLLabel=Paste HTML selectAllLabel=Select all editPrefsLabel=Options... findReplaceLabel=Find & Replace findReplaceTip=find & replace #insert menu definition insertLabel=Insert #insert menu items insertTableLabel=Table... insertImageLabel=Image... insertImageTip=Insert image... # format menu definition formatLabel=Format # format menu items fontLabel=Font... fontTip=Format font... clearFormatLabel=Remove Formatting clearFormatTip=Remove Formatting fontBoldLabel=Bold fontBoldImage=resources/bold.gif fontBoldTip=switch bold on/off fontItalicLabel=Italic fontItalicImage=resources/italic.gif fontItalicTip=switch italic on/off fontUnderlineLabel=Underline fontUnderlineImage=resources/uline.gif fontUnderlineSelectedImage=resources/uline_on.gif fontColorTip=Font Color fontColorLabel=Font Color selectedFontColorLabel=Selected Font Color redFontColorLabel=Red greenFontColorLabel=Green blueFontColorLabel=Blue blackFontColorLabel=Black removeFontColorLabel=Remove Color fontUnderlineTip=switch underline on/off fontStrikethroughLabel=Strikethrough formatTableLabel=Table... formatTableTip=Format Table toggleBulletsLabel=Bulleted List on/off toggleBulletsTip=Bulleted List on/off toggleNumbersLabel=Numbered List on/off toggleNumbersTip=Numbered list on/off formatListLabel=List... formatListTip=Change list format formatImageLabel=Image... formatImageTip=Edit image format formatParaLabel=Paragraph... formatParaTip=Change paragraph format editNamedStyleLabel=Named styles... editNamedStyleTip=Edit named styles paraAlignLeftLabel=Align Left paraAlignLeftTip=Set paragraph left alignment paraAlignCenterLabel=Align Center paraAlignCenterTip=Set paragraph center alignment paraAlignRightLabel=Align Right paraAlignRightTip=Set paragraph right alignment editLinkLabel=Link... editLinkTip=Change link settings... editAnchorsLabel=Anchors... editAnchorsTip=Set and change anchor links... increaseFontSizeLabel=Bigger decreaseFontSizeLabel=Smaller # table menu definition # table menu items tableLabel=Table nextTableCellLabel=Next Cell prevTableCellLabel=Previous Cell insertTableRowLabel=Insert Row insertTableColLabel=Insert Column appendTableRowLabel=Append Row appendTableColLabel=Append Col deleteTableRowLabel=Delete Row deleteTableColLabel=Delete Column toggleTableHeaderCellLabel=Toggle Header Cell moveTableRowUpLabel = Move Row Up moveTableRowDownLabel = Move Row Down moveTableColumnLeftLabel = Move Column Left moveTableColumnRightLabel = Move Column Right # misc menu definition misc=elemTree gc test miscLabel=Misc # misc menu items elemTreeLabel=Show Element Tree gcLabel=Force Garbage Collection testLabel=Test: currently unused # help menu definition help=helpTopics - about helpLabel=Help # help menu items helpTopicsLabel=Help Topics helpTopicsTip=show help topics aboutLabel=About SimplyHTML... # About frame aboutFrameTitle=About this application # Font Dialog fontDialogTitle=Format Font # Font panel uLineLabel=Underline strikeLabel=Strikethrough previewLabel=Preview previewText=Preview text familyLabel=Family sizeLabel=Size plainName=plain boldName=bold italicName=italic boldItalicName=bold italic styleLabel=Style effectLabel=Effect colorLabel=Color foregroundLabel=Foreground: backgroundLabel=Background: noLineLabel=none # Paragraph style panel textIndentLabel=Indent: alignLabel=Alignment: alignLeft=left alignCenter=center alignRight=right valignLabel=Vert. Alignment: valignTop=top valignMiddle=middle valignBottom=bottom valignBaseline=baseline # Margin panel marginLabel=Outer paddingLabel=Inner # Table dialog tableDialogTitle=Format Table tablePanelTitle=Table format cellPanelTitle=Cell format tableWidthLabel=Width: tableBgColLabel=Background color: cellMarginTabLabel=Margin cellBorderTabLabel=Borders borderWidthLabel=Width borderColorLabel=Color: thisCellRangeLabel=this cell thisColRangeLabel=this column thisRowRangeLabel=this row allCellsRangeLabel=all cells applyCellAttrLabel=Apply to cellGenTabLabel=General # Paragraph style dialog paraStyleDialogTitle=Paragraph Style fontTabLabel=Font paraTabLabel=Paragraph stylePanelLabel=Named Style saveStyleButtonLabel=Save saveStyleAsButtonLabel=Save as... deleteStyleButtonLabel=Delete # Named style dialog namedStyleDialogTitle=Edit named styles # Tag names cTagNamePara=Paragraph cTagNameHead1=Heading 1 cTagNameHead2=Heading 2 cTagNameHead3=Heading 3 cTagNameHead4=Heading 4 cTagNameHead5=Heading 5 cTagNameHead6=Heading 6 cTagNameLink=Link cTagNameUL=Unordered List cTagNameOL=Ordered List # List dialog listDialogTitle=Format List listTypeLabel=Type: listPositionLabel=Position: listIndentTitle=Indent: listTypeDecimal=1.,2.,3.,4. listTypeLowerRoman=i.,ii.,iii.,iv. listTypeUpperRoman=I.,II.,III.,IV. listTypeLowerAlpha=a.,b.,c.,d. listTypeUpperAlpha=A.,B.,C.,D. listTypeDisc=file symbol as bullet listTypeCircle=round bulled listTypeSquare=square bullet listTypeNone=no bullet, no numbering listPosInside=inside listPosOutside=outside # Link dialog linkDialogTitle=Create / Edit Link linkStyleLabel=Style: linkTypeLabel=Type: linkAddressLabel=Address: linkBrowseLabel=Browse... linkAnchorLabel=Anchor: linkDisplayLabel=show link as showAsTextLabel=Text showAsImageLabel=Image linkTextLabel=Text linkImageLabel=Image emptyLinkImageLabel=Change... linkTypeName1=relative linkTypeName2=local linkTypeName3=WWW linkTypeName4=Gopher linkTypeName5=FTP linkTypeName6=Telnet linkTypeName7=Newsgroup linkTypeName8=E-Mail setImageLabel=Set... # Anchor dialog anchorDialogTitle=Edit anchors anchorPanelLabel=defined anchors docPanelLabel=Document addAnchorTitle=Add anchor addAnchorText=Name for anchor? # Element Tree Dialog elementTreeTitle=Element Tree # PluginManager dialog pluginManagerDialogTitle=Manage plug-ins pluginPanelTitle=Installed plugins deactivatePlugin=deactivate activatePlugin=activate pluginSettingsPanelTitle=Plugin settings togglePluginActivationCheckbox=activated dockLocationLabel=Dock location: pluginDockLocationNone=none pluginDockLocationTop=top pluginDockLocationRight=right pluginDockLocationBottom=bottom pluginDockLocationLeft=left # Preferences dialog prefsDialogTitle=Options prfShareDocResourcesLabel=this option has no function at all and is completeley obsolete prfLafLabel=Look and Feel: prefsPasteModeLabel=Default Paste Mode: prfWriteModeLabel=write HTML files as prfWriteModeHTML32Label=HTML 3.2 (e.g. JavaHelp) prfWriteModeHTML4Label=HTML 4 linkDefaultStyleSheetLabel=link new documents with default style sheet pasteModeHTML=Paste as HTML pasteModePlainText=Paste as plain-text # Image dialog imageDialogTitle=Edit/Insert Image imgDirPanelTitle=Image files addImgBtnTitle=Add... delImgBtnTitle=Delete imgPreviewPanelTitle=Preview imgPropertiesPanelTitle=Properties imgScaleLabel=Scale: imgWidthLabel=Width: imgHeightLabel=Height: imgHSpaceLabel=horiz. space: imgVSpaceLabel=vert. space: imgAlignLabel=Alignment: imgAlignTop=top imgAlignMiddle=middle imgAlignBottom=bottom imgAlignLeft=left imgAlignCenter=center imgAlignRight=right oWidthLabel=orig. width: oHeightLabel=orig. height: imgBorderLabel=Border: # Find & replace dialog findReplaceDialogTitle=Find & Replace findNext=Find next... searchFromStart=Search from start searchFromStart.tooltip=Start searching at the top instead of at the cursor position. searchDown=Search down searchDown.tooltip=Search from top to bottom. wholeWordsOnly=Whole words only wholeWordsOnly.tooltip=Restrict matches to whole words. searchUp=Search up searchUp.tooltip=Search from bottom to top. matchCase=Match case matchCase.tooltip=Whether to honor case when matching. matchApproximately=Match approximately matchApproximately.tooltip=Whether to allow approximate matches,
      i.e. searching for 'files' will find 'flies'. replaceWith=Replace with: textToFind=Text to find: replace=Replace... searchWholeProject=Search whole project noMoreOccurrencesFound=no (more) occurrences found allOccurrencesReplaced=All occurrences replaced replaceThisQuery=replace this occurrence of replaceYes=Yes replaceNo=No replaceAll=All replaceDone=Done # Messages saveChangesQuery=Would you like to save your changes? confirmClosing=Confirm closing fileExistsQuery= already exists, overwrite it? confirmSaveAs=Confirm save as confirmDelete=Confirm delete deleteFileQuery=Would you really like to delete this file? deleteStyleQuery=Would you really like to delete this style? # Error messages docNameMissingError=Please specify name and location for the document through 'Save as' unableToUndoError=Unable to undo: unableToRedoError=Unable to redo: unableToOpenFileError=File can not be opened cantCreateURLError=Can not create a valid URL for\n helpNotFoundError=Help file could not be opened.\nPlease consult chapter 'Installation' of file 'readme.txt' # Miscellaneous text htmlFileDesc=HTML files imageFileDesc=Image files defaultDocName=Untitled cancelBtnName=Cancel closeBtnName=Close okBtnName=OK leftLabel=left: rightLabel=right: topLabel=top: bottomLabel=bottom: insertTableTitle=insertTable insertTableMsg=How many columns? close=Close standardStyleName=standard styleNameInputTitle=Save style styleNameInputText=Name of new style? newStyleDefaultName=new style docTitleTitle=Edit Document Title docTitleQuery=Set title to: layoutTabTitle=Layout view htmlTabTitle=HTML Code view printing_not_supported=Printing not supported on this java version, please upgrade printLabel=Print...simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/clearFormat.gif0100644 0000000 0000000 00000000172 12114157751 024405 0ustar000000000 0000000 GIF89a‘—Á::ÀÀÀ!ù,Kœ©Ëíc ¢Š µ¿íVÈTäÀ9”›ÙTÐ^ž\ V<¾Ñ,»š.ç¢EÇÅë2sAcDÄæS•NUœNæyʈÇäò£;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/paste.gif0100644 0000000 0000000 00000001750 12114157751 023265 0ustar000000000 0000000 GIF89a÷O„¯Ä/Dd¤ºº$$//$DD¤ZZddddoozz„„„„šššššš¤¤¤¤¤¤d¤¤¯¯¯¯¯$¯¯ºººººšÄÄOÄĤÄÄÄÏÏ:ÏÏDÏÏoÏÏÏÏšÏϺÏÏÏÚÚDÚÚOÚÚZÚÚïÚÚúääääZääÚäääïïïïÏïïÚúúúú/úúOúúdúúäúúïúúúÿÿÿ!ùÿ,@Åÿ h€ÁÌ(1¡‡B†ðÈA€F„dd0hð>T@Ð` \„xaÅ ĸ±"€‚=Àða‚¤.d°@Œ à‡† . BÇ!)ü À£Žœ&Iþ“9$L¤hàÂcYlaaƒ 0x˜ ‚) (ˆ @5Èükp‚H†B@@TX!¡C9¾ €P`Tà€@@;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/splashImage.jpg0100644 0000000 0000000 00000010666 12114157751 024427 0ustar000000000 0000000 ÿØÿàJFIFHHÿÛC   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcÿÛC//cB8BccccccccccccccccccccccccccccccccccccccccccccccccccÿÀ"ÿÄÿÄJ  !1AQ‘"Uaq¡±2rs’ÁÑ#356Rb‚“áBCT¢²Â%&DS”ÒðÿÄÿÄ&1A!"Qq3a#BÿÚ ?°E„@ePXDQaDDD@aDD”XDQDX@eD@DDaeaó$ŒŠ7I#ƒXÀ\âzUÅÛ\\*'smîÐæž^áëÏ7b”kšÿÓòFÓ‡Ô¸D=œçÜ1ÚªÕíÓbR[¤FÉßTׇ9ªf ÙÅŒrÙåµ§UgòÇàµìZ¶K-¼RGGƒˆ¸¼¸‚sÖºCÂ$ùÞÝ>´þ IFwâ*ˆzX*5#ïÔÐÜTØw)ÃNÙÇ^[YÜ+èéiâ¶ ¹iK8°Ð9¹ºI÷.•‚óòßãQÆè‹^XöœØôó…¿j«œWª¸¨«È#“­àiÆ6<ã¯+§“'ðŽéª¥•‘¶JÀ^CFbÀß±Yа²1Ï/sZqç'­B4eÚñv»8UU¹ôð°¹ã važ³Ø½5¶ ­·\a¦ ©1˜h9$íÎ=^õrAÎj $ ª*²—Têꢎ§M#œ“oêØ-êúíeANêš¹@€]Ã>ÅËÓI:m ,TU?•×ßóîþ[?Ö¥¸ë•RÒ:1‘ʆFÒýý}\Û#ÓJ<´,°‘Uu:›QRÔ> ëÉc8sLlØ÷.ÞŽÔ·*û·‰ÖÊ'cØHqh¤{–žQŽádåK\ß*ín¤††~JG‡9øí°ãÛÜ¢ôú²þúˆÚÊ·Jâà|›|ýù¶RyJ;²ÖE^ÖUëHb’ª`ø¡hâw c!£Ù¹\«¯£ÿ=ßËgຎšRá¡e° ÃQWÒ__MAVø™Ð8¹úGQ I§ïO®ÓÆáZÃwš0oJ«jêWW5LŸ>W—Ÿi9]iñ{Þîƒ$Úfñzº_)éß]+¢ŽAóFý];Õc¨WƒŠ zª÷ ÞîI‡Ô7>üw.5ÓV݅Ω´•¥´í•Â0Óæƒ¶øIãõ25g"†h‹¥Úí[;ë*Ý%<,oF\NÜè½®.õªqG9†ydç´ ùýd,^&§³±d•PÝY{Ã[\òç#fç¹ZTŒ–:XY<œ¤¡€=øÇ±¹L˜ž>EžÈˆ²(DDD@V À$ô !^hkê䤒žžI ®“iqk‰àz€P¯“«¿ÉT)ß‚™Çá žp–ÞNÎdœý„l¾ÿ´:GËüÁø/~7–Û´çÁššz|rðÉ7KsÞ¼—oSê~¨…Í„Ã-!­.É$ã'܆´Mx¹Åc&¸:gô5½;õ•éSj;¥à«XwDøÃÀrfbÙ{¾hÿhU“‰s‹œI$ä“Ò§ÞkÄt”ÖøÎ Žåì€ïø(%4.©©Šþ|¯ o´œ,tëÚæû ±ô ‰ØÝU õ.ãÏSFÃï=ª{®ùJïSW“Ã#Ïdl=À+RÔÇeÒ¯†ÂLbž!œÆ>*«SN·Iäa’ÿvþZå5sÇ›NÞäyßÓ=ë£á¿‚’š‡y]Ê?~°÷Ÿrëhº!E§`8sægo7» ªî娕ŽâŠ3ÉF}Cúäö®#ü™›ø/G*ŸQl¢!Ï?¹‡f@ìZêAEvDBµeÊ‚¦FœÇä™ìnßžÕ¿ (,µ¶ê:þ§Žn›Æ3…«äå›Ñ´ÿatÖTRk†&Atâ06&·€4lêZNY½OöQašáƒRŽÙC@ç:Ž–8\ᇠe+-T5òÕÒÇ3š0 ÆpÚÔ®ºQ[‹eC!ãÏNNMøäþNY½OöoÂ-pŽ [lDïÊ<ÌÍûû”‹Êk/¤aïGÛ,÷¾çÁHxÃdÉÜÿ¤$ã%)Ù žŽÕuÓ3gJö°¬œ+qÖ Tœ¥,``.`ÎÀ\³”¶W ø¼0¸Ëi]êK;ÜÛ„%ÄàNëLÙ%’œSÏ“–oFÓý…¿ÓD"‚&EækjÖ^­´3ò5U‘E&3Â㾇”Ö_HÃÞ°©¿’ŸoÓö‡½Ï}¾9Ç$–ä’½©-ú¹ZZH¡“âcpqÔµ¼¦²úFôòšËé{Õ©þÁ­Y©¬%óÑ×JË—‚Aõ7ÇôGìSÿñŸÿÕ}Wéý6+êêdc*ÆÙ ›8öÙi|•£= ›ý±P¯CKRÜl·ø½–yY8äl%¥­ã$gs…§‚J™ã‚—É#ƒZÑÒJžPéÝ+_?%ITù¤ˆ±²ôwzÔ†Û`¶ZŸÊRRµ²cn%Îì'›±j³Çi]þÅÊ6Ûí´ôÆ"`i#¤ôžÒ¶×)úŽÏÜÇÜ!i Œó)¬¾‘‡½ydüÑN²/YN(ülÈ<_‡£‡­hyMeôŒ=ë•ø@ë"Ò¡»P\æQÔ²g4eÁ½n£Mr"(" ˆ€"ùÂ[α©-ùÎoj 6ÑjН¢¾…OîŽô¢Y°‹â7ñ·8ÂûBŸ/sXÇ=î kFI'»ëWÇ+Yk®ˆäxÄ­$8þër3Ö3Ï—öðo3:žÓãv6ÿäþèè<ýJ?SÇ_U +yG½Ü-h<îïØ ó‘Ì”z#‡Ù¾E‡§.r]($–^:9L|ma`xàósãÚYFôý¶V2††¡óSÅñ¼œ°M‘Ž£ó²:‘D©>L(‡„†’©]ÄøøJ—¨„Ñ¿_ÿ¶Áù]+’Àlà‹FÒ6¥4zæºŽŽ fRÓ¹°ÆØÁzò¢ZŸO¶Ã%;[Pf僎íáÆ1ëõ«Y@¼%þ~ƒè¿â“O’Nj7଄©ïöw6¹?ùCñP%y­µ9% ÚȈv¨¡uˆ†–W‰]ÚÐð1Î=ʾÁÆq·Z³µ÷êÛþµŸ m-£kGOXæ¸þëšÏ¿êiçP·Û ñÓ•ÿ'_)j Ãø_ôNÇãžÅi^ë…¶ÏUWœ:6¤vò4¥zŠýãÚj×N™8¦ß|·ÍöœžÅsbÝ8°™9;œïÒ°»JÓö‰0ú“,§=^`àjã¯Dd¤­¹çc~E’>Áâäc£*˜WDÿ¢$úƒþÕK¯.“û2yàÑ£’¸;ñ0gí)„x4üÅÒgÀ©ºój?#""Ä¡D@chÉKˆsA!o¯ †|vªˆÍ^J?ذh+áÑãy%v=¸_-àÏä£âõžeÑ úcÎÞÕÄÕWwSD-ôŽÅTíóÞ?˜Ÿiæ§¡oÏTÚY*¦ 6»A@êëÝtŒ¦a–²¥Üs8g†1Ð ºß™rk‰&î\#VœÖË$vÊ(LÒµŽnÙhé9èÇO½Hô……Õg2)2̰¸=à Ý­ÈèÜõŽe!ÓÚ~žÇNCq%LŸ›„ ú€è °¡g‘Ïèò§§†–ÃOb‰ƒ k^¨ˆfaD|$~ˆ¥úÿø•.QmwGYp¤¥§£¥’b$/qhæÛâµÂë" ­U½c£¦uŽÞçSÄ\i£$– üЫ&o^Ž›¸+:ÄÙYd¢Žx‘ÄØÜÇs‚ѹzuRM*dE{®£dZ‰ícy6ìÑ€¹úsõ†ßõíø®î®´].7ù¦¦¡•ñµ­x;~V¦Åw£»RTÉnŸ‚)ZçmÐëXÍzU}²ÒP/ _Ÿ ú/ø…=P­wn¯¹VR¶ŽŽY[^ѶIæ÷/¥‘6V@Uæª/&o^Ž›¸+jºH#{˜Xç4×s´ã™k«’uAí}ú¶ÿ­gÅs´ekô…}#¹¥™í¨ð7½uuµ5Ue™´ÔtïžGÊ Àg‚ðД•”úšjÊi!<¯(Òáó²ÿ½faÿGejö:7¹®i ƒÐW½º‘õ÷ zFg2¼7nÒ{êGªtÕ{ï³ÍCHùa›eƒ`O?¿'µnh?YIu}]u3áGˆøº\vøg½{hú{¬”cÂDmŠ;Tlc#@p(B°uå¾¾å=(é$™±5ÅÎhÛ.#oôûÔSÉ›×£¦î œŠÆ“a–”ÿ¢$úƒþÕK«’gÌë Ü .êrXß‹‡›½V>LÞ½7pY餕Û+$þ ?1_ô™ð*lªº;V¦¡UÀ»¸3ï[ޱë¸}¿ê¹É‰NnJHbÊàhúZê{t¯¹ò¦¦YI<«²îzï/,•:)”D\€ˆˆÁ+(€Óž.nGA_<ËwxÉNׂÆU²Q¿SWÜ! ¥pü»ø€q±é;g›¤ö]J*:{|\•-…™É é=dô­¡NèÆ݇RdzŽÜ„>„ïGÚ½£/pË€|ÇnîÜü²…A¡aeDDVYDeaE•„VE”P…”@aQDDD@DDD@DDD@DDD@DDD@ÿÙsimplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/ul.gif0100644 0000000 0000000 00000001534 12114157751 022571 0ustar000000000 0000000 GIF89a÷ÐÌÄÿÿÿ!ùÿ,@9ÿ H°`ÁXÈ!A„ Jq¢ÄŠ3jÜ8#ÇþkHäE‘=šüˆ‘dÕ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/appIcon.gif0100644 0000000 0000000 00000003215 12114157751 023540 0ustar000000000 0000000 GIF89a@@÷!!!!))))))1!11!1119!9!!999B)B))BBBJ1JRRR1RRRRZZZ!9Z1ZZZZccc!9cccckkk!Bs)JsBJsss{)J{{{„)R„1R„JR„JZ„„„„„ŒŒ1RŒRŒŒŒŒŒ””1Z”œœœ9cœœœ¥9c¥¥¥¥­­­9k­­­µBkµµµµ½Æ½Bs½½½½½Î½½Þ½ÆÎ½ÆÞÆB{ÆsÆÆÆÆÆÞÆÎÖÆÎÞÎÎÎÎÎÞÎÎçÎÖçÖÖÖÖÖÞÖÖçÖÞçÞççÞçïçççççïçïïçï÷ïïïïï÷ï÷÷÷”ÿÿÿÿÿÿÿÿÿÿœÿÿÿ,@@þÇH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠ9ð‡Ž-$-âP!ƒIRJ¼‚ÀHÄÈp b ¸ ðÁ‹=ªPâˆ-€0 éÂ/ Ä8r„-NPa5á– xpåÊb€·(ÊüR!µGrHà!*PbÌ…K—ÃaÂŒ¤»àîÚ 9rdx `ÄJ[¬TÙ̹óf+„ #V\ x¤0E†_Æ|) #‹çÛ¸qƒÎòÇkyøˆ‘Áo_üè’»¹s,gÈ‘úˆÖ,ø‚gçàoþ[i(d€ˆê,‚´5 !ö¢Ô¼Ù‰‘ûF˜ègòŠ(T g }ÀÕù@LŸl ÐÄ@¶ÕGÄ…f¨á…N¸ÐÔZ "ðÀÂÊÑ 4s›Qñߌ4Ò8…g`,T“Zx¥ À^œÄÁ:Þ‘›q¡Ð oy°V ”èA30‚{adxÐ!¢ %°•,˜ð¨€1%ÄÅ–á!´Epå× L@@i€‹ Á –I`jGÜ`‚o i:T¡ ¸ñVÐ ðxDP¸…Ãl6#’22aTxFš@I °WþAˆpCn© Èç–Í•zj†F@ᙥ90ÁZ9LÀÛM4jŒS8¡Ä†J8`n9Ž1ƒŽMfB Äe}UPA­7r9Ææq5k Œ6[Ñ›œ9ADµéâFÅD0áY#ÚøekFä†7Å´÷¦Ú¼ f(þ°ÑáQaï…J8ìY¡Þ ëFZ4·0†ç¶Ä)Ü €´øQÂåò‹áãFƒ˜`pHßqfîÎ=çCÁ(°ƒ]„ô,Dð|¤ ( à RtÆE¶M ' ‚sYpA(F¼nYI Р aa±á u H0À”&9î–!h [vEm÷ÊÄ]ÞeFbßF5®EG_èMÙSºöFôrvy¾pŽ7Réå&$Þ«ë½¥’V}Áú‘ŠË5\È>¬îÙÞ8gQχu±ªñÌ7ïüóÐG/ýôÔW?R@;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/empty.gif0100644 0000000 0000000 00000001500 12114157751 023300 0ustar000000000 0000000 GIF89a÷ÑÑÑÿÿÿ!ùÿ,@ÿ H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/saveAll.gif0100644 0000000 0000000 00000001573 12114157751 023543 0ustar000000000 0000000 GIF89a÷„„ÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ù,X  Áƒ `€C‡ DHQ"†!.¬X‘áņ#2ôˆ1dÁ‘'A>üˆòcÆ,?^ôh0J’6sæ”y’fÍgÜX0¨Í¡@r,:0 ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/fontColor.gif0100644 0000000 0000000 00000003215 12114157751 024114 0ustar000000000 0000000 GIF89a÷ÿf‚ÿÿfffÿÿÿÿÿÿÿÿ!ù ,‡ÿf‚ÿÿfffÿÿÿÿÿÿÿÿjH°`*4` B‡ 68Ñ ÅŠ >ÌXða AjlXÀ€ÉR™ äÉ”VJl2¤À ØY@À2 êäùs@P‚5m, iPdêê©7“~äh1 ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/help.gif0100644 0000000 0000000 00000001760 12114157751 023102 0ustar000000000 0000000 GIF89a÷/:$/DOD¯OšOºOÚOZOo„ZoZ¯Z¯dddzdšdºdddoºd„ZodoÄo¤oOzZzz¯z„zzºz¤¯„o„º„¯„š„$Ä/OĺOZ$ZddšOšZšššš¤šššÏšOššÄšºšºº¤z¤¤¤º¤d¤¤¤¤¯¤¯¤¤Ä¯¯šºzºoººº¤ºº¯ºÄ„ºÏºÄºÏÄÄÄÏϯÏϺäïäúäúÚïäÚïúïïúúúúúúúúïúúúÿÿÿ!ùÿ,@Íÿ ø/ ’P Ž;²q°"@“"H üÀe~LÑ‚£ „Hâ –$$¸€ ‹6tD ``‡- „ܨa!K– @P@DŠW¸P&F4 ˜9P%J®#X¹QáÀׄ ¸ÜP‘eJj@)‹ˆDpˆ‘…J ¢üËAaCCf”aá'–d0q€j0¨ùɈžÿ`±¡ ¤øPz¦CnðH0 ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/ul_on.gif0100644 0000000 0000000 00000002003 12114157751 023255 0ustar000000000 0000000 GIF87a÷Uªÿ$$U$ª$ÿIIUIªIÿmmUmªmÿ’’U’ª’ÿ¶¶U¶ª¶ÿÛÛUÛªÛÿÿÿUÿªÿÿ$$U$ª$ÿ$$$$U$$ª$$ÿ$I$IU$Iª$Iÿ$m$mU$mª$mÿ$’$’U$’ª$’ÿ$¶$¶U$¶ª$¶ÿ$Û$ÛU$Ûª$Ûÿ$ÿ$ÿU$ÿª$ÿÿIIUIªIÿI$I$UI$ªI$ÿIIIIUIIªIIÿImImUImªImÿI’I’UI’ªI’ÿI¶I¶UI¶ªI¶ÿIÛIÛUIÛªIÛÿIÿIÿUIÿªIÿÿmmUmªmÿm$m$Um$ªm$ÿmImIUmIªmIÿmmmmUmmªmmÿm’m’Um’ªm’ÿm¶m¶Um¶ªm¶ÿmÛmÛUmÛªmÛÿmÿmÿUmÿªmÿÿ’’U’ª’ÿ’$’$U’$ª’$ÿ’I’IU’Iª’Iÿ’m’mU’mª’mÿ’’’’U’’ª’’ÿ’¶’¶U’¶ª’¶ÿ’Û’ÛU’Ûª’Ûÿ’ÿ’ÿU’ÿª’ÿÿ¶¶U¶ª¶ÿ¶$¶$U¶$ª¶$ÿ¶I¶IU¶Iª¶Iÿ¶m¶mU¶mª¶mÿ¶’¶’U¶’ª¶’ÿ¶¶¶¶U¶¶ª¶¶ÿ¶Û¶ÛU¶Ûª¶Ûÿ¶ÿ¶ÿU¶ÿª¶ÿÿÛÛUÛªÛÿÛ$Û$UÛ$ªÛ$ÿÛIÛIUÛIªÛIÿÛmÛmUÛmªÛmÿÛ’Û’UÛ’ªÛ’ÿÛ¶Û¶UÛ¶ªÛ¶ÿÛÛÛÛUÛÛªÛÛÿÛÿÛÿUÛÿªÛÿÿÿÿUÿªÿÿÿ$ÿ$Uÿ$ªÿ$ÿÿIÿIUÿIªÿIÿÿmÿmUÿmªÿmÿÿ’ÿ’Uÿ’ªÿ’ÿÿ¶ÿ¶Uÿ¶ªÿ¶ÿÿÛÿÛUÿÛªÿÛÿÿÿÿÿUÿÿªÿÿÿ!ù,@à%µHp Á‚ÛüS¨í_Ç#B„øORDÿYÀ±cÇkÿ¶ý[ø¤µˆ"E‚T)q¤Å†ø§K#M›5ÚÉðfÓ#®tÒæÉ”%‹‚¤¨ð$Æ™º’ €$Ɇ(K¢¼ø°âÅŠm5œôá-4CÚò§dEI`~*³¡I”'‘Øæï_H’=n‹àŸ¤“cm8Ôc×[«j Ð0dM·8þ£Y £S¢;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/fmtPara.gif0100644 0000000 0000000 00000001542 12114157751 023542 0ustar000000000 0000000 GIF89a÷€}ÐÌÄÿÿÿ!ùÿ,@?ÿ H° Aþ 0€Ã‡$ÈáÄ‹$Ü(0"€1ŠüçQâÈ“=.Thq¢Æ"a’Œr¡À€;simplyhtml-0.17.3/src/com/lightdev/app/shtm/resources/fmtLink.gif0100644 0000000 0000000 00000001624 12114157751 023555 0ustar000000000 0000000 GIF89a÷ƒõúõ††ƒÅÅÁúúƒúúõÿÿÿ!ùÿ,@qÿ @À¿‚ÿp`@ œH±âÀ„ ܨÑb€Hœq#ÂŽˆ¸²àD5€i±æÇYXy`gǃ+ D, ÀJ3Yõi0%C‡-:µ¦P0Cެ)ð¦H®o ;simplyhtml-0.17.3/src/com/lightdev/app/shtm/EffectPanel.java0100644 0000000 0000000 00000012145 12114157751 022467 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Font; import java.awt.GridLayout; import javax.swing.ButtonGroup; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.UIManager; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.html.CSS; /** * a panel to display and change line attributes * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class EffectPanel extends JPanel implements AttributeComponent { /** a radio button for the underline attribute */ JRadioButton uLine; /** a radio button for the strike through attribute */ JRadioButton strike; /** a radio button if no line effect is set */ JRadioButton noLine; private Object originalValue; private int setValCount = 0; String selection = Util.CSS_ATTRIBUTE_NONE; public EffectPanel() { super(new GridLayout(3, 1, 3, 3)); /** initialize the line effects button group */ noLine = new JRadioButton(Util.getResourceString("noLineLabel")); uLine = new JRadioButton(Util.getResourceString("uLineLabel")); strike = new JRadioButton(Util.getResourceString("strikeLabel")); final ButtonGroup effectGroup = new ButtonGroup(); effectGroup.add(noLine); effectGroup.add(uLine); effectGroup.add(strike); //JPanel linePanel = new JPanel(new GridLayout(3,1,3,3)); setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Util.getResourceString("effectLabel"))); final Font font = UIManager.getFont("TextField.font"); uLine.setFont(font); strike.setFont(font); noLine.setFont(font); add(noLine); add(uLine); add(strike); } public AttributeSet getValue(final boolean includeUnchanged) { if (includeUnchanged) { return getAttributes(); } else { return getValue(); } } private AttributeSet getAttributes() { final SimpleAttributeSet set = new SimpleAttributeSet(); selection = Util.CSS_ATTRIBUTE_NONE; if (uLine.isSelected()) { selection = Util.CSS_ATTRIBUTE_UNDERLINE; StyleConstants.setUnderline(set, true); } else if (strike.isSelected()) { selection = Util.CSS_ATTRIBUTE_LINE_THROUGH; StyleConstants.setStrikeThrough(set, true); } Util.styleSheet().addCSSAttribute(set, CSS.Attribute.TEXT_DECORATION, selection); return set; } public AttributeSet getValue() { final AttributeSet set = getAttributes(); if (((originalValue == null) && (!selection.equalsIgnoreCase(Util.CSS_ATTRIBUTE_NONE))) || ((originalValue != null) && (!originalValue.toString().equalsIgnoreCase(selection)))) { return set; } else { return new SimpleAttributeSet(); } } public boolean setValue(final AttributeSet a) { boolean success = false; if (a.isDefined(CSS.Attribute.TEXT_DECORATION)) { final String value = a.getAttribute(CSS.Attribute.TEXT_DECORATION).toString(); if (value.equalsIgnoreCase(Util.CSS_ATTRIBUTE_UNDERLINE)) { uLine.setSelected(true); if (++setValCount < 2) { originalValue = Util.CSS_ATTRIBUTE_UNDERLINE; } success = true; } else if (value.equalsIgnoreCase(Util.CSS_ATTRIBUTE_LINE_THROUGH)) { strike.setSelected(true); if (++setValCount < 2) { originalValue = Util.CSS_ATTRIBUTE_LINE_THROUGH; } success = true; } else { noLine.setSelected(true); } } else { noLine.setSelected(true); } return success; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLWriter.java0100644 0000000 0000000 00000032345 12114157751 022403 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.Enumeration; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.ElementIterator; import javax.swing.text.MutableAttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.html.CSS; import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLWriter; /** * FixedHTMLWriter * * */ public class SHTMLWriter extends HTMLWriter { private Element element; private Writer writer = null; private boolean replaceEntities; private boolean inTextArea; //final private MutableAttributeSet oConvAttr = new SimpleAttributeSet(); //final private MutableAttributeSet convertedAttributeSet = new SimpleAttributeSet(); private boolean inPre; public SHTMLWriter(final Writer w, final HTMLDocument doc, final int pos, final int len) { super(w, doc, pos, len); writer = w; } /** Constructs the SHTMLWriter with a new StringWriter. See also the method * getWrittenString. */ public SHTMLWriter(final HTMLDocument doc) { this(new StringWriter(), doc, 0, doc.getLength()); } public SHTMLWriter(final Writer w, final HTMLDocument doc) { this(w, doc, 0, doc.getLength()); } protected ElementIterator getElementIterator() { if (element == null) { return super.getElementIterator(); } return new ElementIterator(element); } protected void output(final char[] chars, final int start, final int length) throws IOException { if (replaceEntities) { if (chars[start] == ' ') { chars[start] = '\u00A0'; } final int last = start + length - 1; for (int i = start + 1; i < last; i++) { if (chars[i] == ' ' && (chars[i - 1] == '\u00A0' || chars[i + 1] == ' ')) { chars[i] = '\u00A0'; } } // if(chars[last] == ' '){ // chars[last] = '\u00A0'; // } } super.output(chars, start, length); } protected void startTag(final Element elem) throws IOException, BadLocationException { if (matchNameAttribute(elem.getAttributes(), HTML.Tag.PRE)) { inPre = true; } super.startTag(elem); } protected void endTag(final Element elem) throws IOException { if (matchNameAttribute(elem.getAttributes(), HTML.Tag.PRE)) { inPre = false; } super.endTag(elem); } protected void text(final Element elem) throws BadLocationException, IOException { replaceEntities = !inPre; super.text(elem); replaceEntities = false; } protected void textAreaContent(final AttributeSet attr) throws BadLocationException, IOException { inTextArea = true; super.textAreaContent(attr); inTextArea = false; } public void write() throws IOException, BadLocationException { replaceEntities = false; super.write(); } protected void writeLineSeparator() throws IOException { final boolean pre = replaceEntities; replaceEntities = false; super.writeLineSeparator(); replaceEntities = pre; } protected void indent() throws IOException { if (inTextArea) { return; } final boolean pre = replaceEntities; replaceEntities = false; super.indent(); replaceEntities = pre; } /** * Iterates over the * Element tree and controls the writing out of * all the tags and its attributes. * * @exception IOException on any I/O error * @exception BadLocationException if pos represents an invalid * location within the document. * */ synchronized void write(Element element) throws IOException, BadLocationException { this.element = element; try { write(); } catch (final BadLocationException e) { element = null; throw e; } catch (final IOException e) { element = null; throw e; } } /** * invoke HTML creation for all children of a given element. * * @param elem the element which children are to be written as HTML */ public void writeChildElements(final Element parentElement) throws IOException, BadLocationException { Element childElement; //Not necessarily a paragraph element. for (int i = 0; i < parentElement.getElementCount(); i++) { childElement = parentElement.getElement(i); write(childElement); } } protected boolean inRange(final Element next) { final Document document = next.getDocument(); if (document instanceof SHTMLDocument && next.getStartOffset() >= ((SHTMLDocument) document).getLastDocumentPosition()) { return false; } final int startOffset = getStartOffset(); final int endOffset = getEndOffset(); if ((next.getStartOffset() >= startOffset && (next.getStartOffset() < endOffset) || next.getEndOffset() - 1 == endOffset) || (startOffset >= next.getStartOffset() && startOffset < next.getEndOffset())) { return true; } return false; } /** * Create an older style of HTML attributes. This will * convert character level attributes that have a StyleConstants * mapping over to an HTML tag/attribute. Other CSS attributes * will be placed in an HTML style attribute. */ private static void convertStyleToHTMLStyle(final AttributeSet source, final MutableAttributeSet target) { if (source == null) { return; } final Enumeration sourceAttributeNames = source.getAttributeNames(); String value = ""; while (sourceAttributeNames.hasMoreElements()) { final Object sourceAttributeName = sourceAttributeNames.nextElement(); if (sourceAttributeName instanceof CSS.Attribute) { // default is to store in a HTML style attribute if (value.length() > 0) { value += "; "; } value += sourceAttributeName + ": " + source.getAttribute(sourceAttributeName); } else { target.addAttribute(sourceAttributeName, source.getAttribute(sourceAttributeName)); } } if (value.length() > 0) { target.addAttribute(HTML.Attribute.STYLE, value); } } /* (non-Javadoc) * @see javax.swing.text.html.HTMLWriter#writeAttributes(javax.swing.text.AttributeSet) */ protected void writeAttributes(final AttributeSet attributeSet) throws IOException { final Object nameTag = (attributeSet != null) ? attributeSet.getAttribute(StyleConstants.NameAttribute) : null; final Object endTag = (attributeSet != null) ? attributeSet.getAttribute(HTML.Attribute.ENDTAG) : null; // write no attributes for end tags if (nameTag != null && endTag != null && (endTag instanceof String) && ((String) endTag).equals("true")) { return; } if (attributeSet instanceof Element) { final Element element = (Element) attributeSet; if (element.isLeaf() || element.getName().equalsIgnoreCase("p-implied")) { super.writeAttributes(attributeSet); return; } } //convertedAttributeSet.removeAttributes(convertedAttributeSet); final MutableAttributeSet convertedAttributeSet = new SimpleAttributeSet(); SHTMLWriter.convertStyleToHTMLStyle(attributeSet, convertedAttributeSet); final Enumeration attributeNames = convertedAttributeSet.getAttributeNames(); while (attributeNames.hasMoreElements()) { final Object attributeName = attributeNames.nextElement(); if (attributeName instanceof HTML.Tag || attributeName instanceof StyleConstants || attributeName == HTML.Attribute.ENDTAG) { continue; } write(" " + attributeName + "=\"" + convertedAttributeSet.getAttribute(attributeName) + "\""); } } /** * write an element and all its children. If a given element is reached, * writing stops with this element. If the end element is a leaf, * it is written as the last element, otherwise it is not written. * * @param e the element to write including its children (if any) * @param end the last leaf element to write or the branch element * to stop writing at (whatever applies) */ private void writeElementsUntil(final Element e, final Element end) throws IOException, BadLocationException { if (e.isLeaf()) { write(e); } else { if (e != end) { startTag(e); final int childCount = e.getElementCount(); int index = 0; while (index < childCount) { writeElementsUntil(e.getElement(index), end); // drill down in recursion index++; } endTag(e); } } } /** * write elements and their children starting at a * given element until a given element is reached. * The end element is written as the last element, * if it is a leaf element. * * @param startElement the element to start writing with * @param endElement the last element to write */ void write(final Element startElement, final Element endElement) throws IOException, BadLocationException { final Element parentElement = startElement.getParentElement(); final int count = parentElement.getElementCount(); int i = 0; Element e = parentElement.getElement(i); while (i < count && e != startElement) { e = parentElement.getElement(i++); } while (i < count) { writeElementsUntil(e, endElement); e = parentElement.getElement(i++); } } /* (non-Javadoc) * @see javax.swing.text.html.HTMLWriter#startTag(javax.swing.text.Element) */ void writeStartTag(final Element elem) throws IOException, BadLocationException { // TODO Auto-generated method stub super.startTag(elem); } /* (non-Javadoc) * @see javax.swing.text.html.HTMLWriter#endTag(javax.swing.text.Element) */ void writeEndTag(final Element elem) throws IOException { // TODO Auto-generated method stub super.endTag(elem); } void writeEndTag(final String elementName) throws IOException { indent(); write('<'); write('/'); write(elementName); write('>'); writeLineSeparator(); } void writeStartTag(final String elementName, final AttributeSet attributes) throws IOException { indent(); write('<'); write(elementName); if (attributes != null) { writeAttributes(attributes); } write('>'); writeLineSeparator(); } public void write(final String string) { try { writer.write(string); } catch (final IOException ex) { } } public String toString() { if (writer instanceof StringWriter) { final StringWriter stringWriter = (StringWriter) writer; return stringWriter.getBuffer().toString(); } return super.toString(); } /** Gets the written string if the writer is a StringWriter, null otherwise. */ String getWrittenString() { if (writer instanceof StringWriter) { final StringWriter stringWriter = (StringWriter) writer; return stringWriter.getBuffer().toString(); } return null; } void removeLastWrittenNewline() { if (writer instanceof StringWriter) { final StringWriter stringWriter = (StringWriter) writer; int charIdx = stringWriter.getBuffer().length(); while (stringWriter.getBuffer().charAt(--charIdx) <= 13) { stringWriter.getBuffer().deleteCharAt(charIdx); } } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/UIResources.java0100644 0000000 0000000 00000002122 13147564753 022530 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Created on 09.11.2006 * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import javax.swing.Icon; /** * interface for getting text based resources * * * @author Dimitri Polivaev * 14.01.2007 */ public interface UIResources { String getString(String pKey); Icon getIcon(String name); } simplyhtml-0.17.3/src/com/lightdev/app/shtm/App.java0100644 0000000 0000000 00000005202 12114157751 021027 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.util.prefs.Preferences; import javax.swing.UIManager; /** * Main class of application SimplyHTML. * *

      This class contains method main and opens the application's main * frame.

      * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ public class App { //Main method public static void main(final String[] args) { try { final Preferences prefs = Preferences .userNodeForPackage(Class.forName("com.lightdev.app.shtm.PrefsDialog")); UIManager.setLookAndFeel(prefs.get(PrefsDialog.PREFSID_LOOK_AND_FEEL, UIManager.getCrossPlatformLookAndFeelClassName())); /* The following line causes UIManager to correctly handle alignments of menu items when they do not have an icon. At the Java Developer Connection, SKelvin writes: "If the UI class does not find an icon it can't calculate its width :-) It won't work if you just set the property to null (don't ask me why), but setting to any type other than icon works." (see http://forum.java.sun.com/thread.jsp?forum=57&thread=126150) */ //UIManager.put("Menu.checkIcon", new ImageIcon("") ); UIManager.put("Menu.checkIcon", UIManager.get("MenuItem.checkIcon")); } catch (final Exception e) { e.printStackTrace(); } FrmMain.run(); // create an instance of the app's main window } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/StyleSelector.java0100644 0000000 0000000 00000012144 12114157751 023113 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.util.Vector; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.HTML; /** * Component to select styles * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class StyleSelector extends JComboBox implements AttributeComponent, ChangeListener { private final SHTMLPanelImpl shtmlPanel; /** the CSS attribute key this AttributeComponent object represents */ private final HTML.Attribute key; /** indicates whether or not to ignore change events */ private final boolean ignoreChanges = false; private final String standardStyleName = Util.getResourceString("standardStyleName"); private String paragraphType; private boolean updateRunning; /** * construct a StyleSelector * * @param key the attribute this component represents */ public StyleSelector(final SHTMLPanelImpl shtmlPanel, final HTML.Attribute key) { this.key = key; this.shtmlPanel = shtmlPanel; updateRunning = false; } /** * set the value of this combo box * * @param a the set of attributes possibly having a * font size attribute this pick list could display * * @return true, if the set of attributes had a matching attribute, * false if not */ public boolean setValue(final AttributeSet a) { boolean success = false; final Object attr = a.getAttribute(key); if (attr != null) { setSelectedItem(attr.toString()); success = true; } else { setSelectedItem(standardStyleName); } return success; } /** * get the value of this AttributeComponent * * @return the value selected from this component */ public AttributeSet getValue() { final SimpleAttributeSet set = new SimpleAttributeSet(); set.addAttribute(key, getSelectedItem()); return set; } public AttributeSet getValue(final boolean includeUnchanged) { return getValue(); } /* --------------- ChangeListener implementation start --------------- */ /** * this method listens and reacts to changes to either the JTabbedPane of FrmMain or * a given StyleSheet this component was registered with. Once either one changes * the list of styles of this componment is refreshed accordingly. */ public void stateChanged(final ChangeEvent e) { paragraphType = null; update(); } /* (non-Javadoc) * @see javax.swing.JComboBox#fireActionEvent() */ protected void fireActionEvent() { if (updateRunning) { return; } super.fireActionEvent(); } public void update() { try { updateRunning = true; final DocumentPane currentDocumentPane = shtmlPanel.getCurrentDocumentPane(); final int selectionStart = currentDocumentPane.getEditor().getSelectionStart(); final SHTMLDocument document = (SHTMLDocument) currentDocumentPane.getDocument(); final String newParagraphType = document.getParagraphElement(selectionStart, true).getName(); if (paragraphType == newParagraphType) { return; } paragraphType = newParagraphType; final Vector styleNames = Util.getStyleNamesForTag((document).getStyleSheet(), paragraphType); styleNames.insertElementAt(standardStyleName, 0); setModel(new DefaultComboBoxModel(styleNames)); } catch (final NullPointerException ex) { setModel(new DefaultComboBoxModel()); } finally { updateRunning = false; } } /* --------------- ChangeListener implementation end ----------------- */ } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLMenuBar.java0100644 0000000 0000000 00000004117 12114157751 022454 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.event.KeyEvent; import javax.swing.JComponent; import javax.swing.JMenuBar; import javax.swing.KeyStroke; /** * A menu bar for handling of key events coming from its parent SHTMLPanelImpl * * @author Dimitri Polivaev * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class SHTMLMenuBar extends JMenuBar { /* (non-Javadoc) * @see javax.swing.JMenuBar#processKeyBinding(javax.swing.KeyStroke, java.awt.event.KeyEvent, int, boolean) */ public boolean handleKeyBinding(final KeyStroke ks, final KeyEvent e, final int condition, final boolean pressed) { if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { return super.processKeyBinding(ks, e, JComponent.WHEN_IN_FOCUSED_WINDOW, pressed); } return false; } /* (non-Javadoc) * @see javax.swing.JMenuBar#processKeyBinding(javax.swing.KeyStroke, java.awt.event.KeyEvent, int, boolean) */ public boolean processKeyBinding(final KeyStroke ks, final KeyEvent e, final int condition, final boolean pressed) { return false; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLPanelMultipleDocImpl.java0100644 0000000 0000000 00000015106 13147564753 025161 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Created on 04.10.2006 * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Dimension; import java.awt.event.ActionEvent; import javax.swing.JTabbedPane; import javax.swing.JToolBar; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.html.HTML; class SHTMLPanelMultipleDocImpl extends SHTMLPanelImpl implements ChangeListener { public static final String newAction = "new"; public static final String openAction = "open"; public static final String closeAction = "close"; public static final String closeAllAction = "closeAll"; public static final String saveAction = "save"; public static final String saveAsAction = "saveAs"; /** the tabbed pane for adding documents to show to */ private JTabbedPane jtpDocs; /** tool bar selector for styles */ private StyleSelector styleSelector; /** number of currently active tab */ private int activeTabNo; public SHTMLPanelMultipleDocImpl() { super(); } protected void initDocumentPane() { dynRes.getAction(newAction).actionPerformed(null); getDocumentPane().getEditor().setCaretPosition(0); } /* (non-Javadoc) * @see com.lightdev.app.shtm.SHTMLPanelImpl#initActions() */ protected void initActions() { super.initActions(); addAction(findReplaceAction, new SHTMLEditorKitActions.MultipleDocFindReplaceAction(this)); addAction(setStyleAction, new SHTMLEditorKitActions.SetStyleAction(this)); addAction(newAction, new SHTMLEditorKitActions.SHTMLFileNewAction(this)); addAction(openAction, new SHTMLEditorKitActions.SHTMLFileOpenAction(this)); addAction(closeAction, new SHTMLEditorKitActions.SHTMLFileCloseAction(this)); addAction(closeAllAction, new SHTMLEditorKitActions.SHTMLFileCloseAllAction(this)); addAction(saveAction, new SHTMLEditorKitActions.SHTMLFileSaveAction(this)); addAction(saveAllAction, new SHTMLEditorKitActions.SHTMLFileSaveAllAction(this)); addAction(saveAsAction, new SHTMLEditorKitActions.SHTMLFileSaveAsAction(this)); addAction(exitAction, new SHTMLEditorKitActions.SHTMLFileExitAction(this)); } /* (non-Javadoc) * @see com.lightdev.app.shtm.SHTMLPanelImpl#customizeFrame() */ protected void customizeFrame() { jtpDocs = new JTabbedPane(); super.customizeFrame(); jtpDocs.addChangeListener(this); splitPanel.addComponent(jtpDocs, SplitPanel.CENTER); } /* (non-Javadoc) * @see com.lightdev.app.shtm.SHTMLPanelImpl#createToolbarItem(javax.swing.JToolBar, java.lang.String) */ protected void createToolbarItem(final JToolBar toolBar, final String itemKey) { if (itemKey.equalsIgnoreCase(setStyleAction)) { styleSelector = new StyleSelector(this, HTML.Attribute.CLASS); styleSelector.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXX"); styleSelector.setAction(dynRes.getAction(setStyleAction)); jtpDocs.addChangeListener(styleSelector); toolBar.add(styleSelector); } else { super.createToolbarItem(toolBar, itemKey); } } /* (non-Javadoc) * @see com.lightdev.app.shtm.SHTMLPanelImpl#registerDocument() */ protected void registerDocument() { super.registerDocument(); ((SHTMLDocument) getDocumentPane().getDocument()).getStyleSheet().addChangeListener(styleSelector); } /* (non-Javadoc) * @see com.lightdev.app.shtm.SHTMLPanelImpl#unregisterDocument() */ protected void unregisterDocument() { super.unregisterDocument(); ((SHTMLDocument) getDocumentPane().getDocument()).getStyleSheet().removeChangeListener(styleSelector); } /** * catch requests to close the application's main frame to * ensure proper clean up before the application is * actually terminated. */ boolean close() { dynRes.getAction(exitAction).actionPerformed(new ActionEvent(this, 0, exitAction)); return jtpDocs.getTabCount() == 0; } /** * change listener to be applied to our tabbed pane * so that always the currently active components * are known */ public void stateChanged(final ChangeEvent e) { activeTabNo = jtpDocs.getSelectedIndex(); if (activeTabNo >= 0) { setDocumentPane((DocumentPane) jtpDocs.getComponentAt(activeTabNo)); setEditorPane(getDocumentPane().getEditor()); //System.out.println("FrmMain stateChanged docName now " + documentPane.getDocumentName()); doc = (SHTMLDocument) getSHTMLEditorPane().getDocument(); //fireDocumentChanged(); if (!ignoreActivateDoc) { getDocumentPane().fireActivated(); } } else { setDocumentPane(null); setEditorPane(null); doc = null; } } /** * @return Returns the jtpDocs. */ JTabbedPane getTabbedPaneForDocuments() { return jtpDocs; } /* (non-Javadoc) * @see com.lightdev.app.shtm.SHTMLPanelImpl#updateFormatControls() */ void updateFormatControls() { super.updateFormatControls(); styleSelector.update(); } void incNewDocCounter() { newDocCounter++; } void createNewDocumentPane() { setDocumentPane(new DocumentPane(null, ++newDocCounter)); } void selectTabbedPane(final int index) { ignoreActivateDoc = true; getTabbedPaneForDocuments().setSelectedIndex(index); ignoreActivateDoc = false; } int getActiveTabNo() { return activeTabNo; } /** * @param activeTabNo The activeTabNo to set. */ void setActiveTabNo(final int activeTabNo) { this.activeTabNo = activeTabNo; } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SplitPanel.java0100644 0000000 0000000 00000027344 12114157751 022375 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.BorderLayout; import java.awt.Component; import java.util.prefs.Preferences; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.border.EmptyBorder; /** * A panel to manage a pluggable panel layout, i.e. around * a center panel, other panels can be placed very similar * to BorderLayout. The difference of this class to BorderLayout is * that it creates JSplitPanes for each panel. * * (Dan: This class is a JPanel somehow trying to achieve what would * better be achieved with Layout. Ideally, it would be removed * altogether.) * *

      This uses JTabbedPanes for each of the panels surrounding * the center panel.

      * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class SplitPanel extends JPanel { /* --------------- class fields start --------------- */ /** constant for the major axis being the horizontal one */ public static final int MAJOR_AXIS_HORIZONTAL = 1; // NOT SUPPORTED /** constant for the major axis being the vertical one */ public static final int MAJOR_AXIS_VERTICAL = 2; /** constant for the north plug-in container of this SplitPanel */ public static final int NORTH = 0; /** constant for the east plug-in container of this SplitPanel */ public static final int EAST = 1; /** constant for the south plug-in container of this SplitPanel */ public static final int SOUTH = 2; /** constant for the west plug-in container of this SplitPanel */ public static final int WEST = 3; /** constant for the center panel of this SplitPanel */ public static final int CENTER = 4; /** the outer panels of this SplitPanel */ private final JSplitPane[] outerPanels; /** current setting for major axis of this SplitPanel */ private final int majorAxis = SplitPanel.MAJOR_AXIS_VERTICAL; /* ------ class fields end ------------------ */ /** * Constructor */ public SplitPanel() { super(); setLayout(new BorderLayout()); final int[] directions = new int[] { NORTH, EAST, SOUTH, WEST }; outerPanels = new JSplitPane[directions.length]; for (int i = 0; i < directions.length; i++) { outerPanels[directions[i]] = new JSplitPane(); outerPanels[directions[i]].setBorder(new EmptyBorder(0, 0, 0, 0)); outerPanels[directions[i]].setContinuousLayout(true); } buildLayout(); restorePrefs(); } /** * Sets up the outer panels. */ private void buildLayout() { removeAll(); outerPanels[NORTH].setOrientation(JSplitPane.VERTICAL_SPLIT); outerPanels[SOUTH].setOrientation(JSplitPane.VERTICAL_SPLIT); outerPanels[WEST].setOrientation(JSplitPane.HORIZONTAL_SPLIT); outerPanels[EAST].setOrientation(JSplitPane.HORIZONTAL_SPLIT); if (majorAxis == SplitPanel.MAJOR_AXIS_VERTICAL) { // [ ALWAYS THE CASE ] //System.out.println("SplitPanel.buildLayout majorAxis = vertical"); outerPanels[SOUTH].setTopComponent(outerPanels[NORTH]); outerPanels[WEST].setRightComponent(outerPanels[SOUTH]); outerPanels[EAST].setLeftComponent(outerPanels[WEST]); this.add(outerPanels[EAST], BorderLayout.CENTER); } else { // [ NOT SUPPORTED ] //System.out.println("SplitPanel.buildLayout majorAxis = horizontal"); outerPanels[SOUTH].setTopComponent(outerPanels[NORTH]); outerPanels[WEST].setRightComponent(outerPanels[SOUTH]); outerPanels[EAST].setLeftComponent(outerPanels[WEST]); this.add(outerPanels[SOUTH], BorderLayout.CENTER); } } /** * Removes panels surrounding the center panel. */ public void removeAllOuterPanels() { // Warning: it does not really remove the outer panels per se. JComponent p; if (majorAxis == MAJOR_AXIS_VERTICAL) { p = (JComponent) outerPanels[NORTH].getTopComponent(); p.removeAll(); p.setVisible(false); p = (JComponent) outerPanels[WEST].getLeftComponent(); p.removeAll(); p.setVisible(false); p = (JComponent) outerPanels[SOUTH].getBottomComponent(); p.removeAll(); p.setVisible(false); p = (JComponent) outerPanels[EAST].getRightComponent(); p.removeAll(); p.setVisible(false); } else { // pending... } } /** * Sets the location of a given divider. * * @param panel the panel to set the location for * @param loc the relative location of the divider (0, 0.1, ..., 0.9, 1) */ public void setDivLoc(final int panel, final double loc) { //System.out.println("SplitPanel.setDivLoc panel=" + panel + ", loc=" + loc); outerPanels[panel].setDividerLocation(loc); } /** * Gets the divider location for a given panel. * * @param panel the panel to get the divider location for * * @return the divider location */ public int getDivLoc(final int panel) { //System.out.println("SplitPanel.getDivLoc panel=" + panel + ", loc=" + outerPanels[panel].getDividerLocation()); return outerPanels[panel].getDividerLocation(); } /** * Saves divider locations to preferences. */ public void savePrefs() { final Preferences prefs = Preferences.userNodeForPackage(getClass()); for (int i = 0; i < 4; i++) { prefs.putInt("divLoc" + i, outerPanels[i].getDividerLocation()); //System.out.println("SplitPanel.savePrefs divLoc" + i + "=" + outerPanels[i].getDividerLocation()); } } /** * Restores divider locations from preferences. */ public void restorePrefs() { final Preferences prefs = Preferences.userNodeForPackage(getClass()); for (int i = 0; i < 4; i++) { outerPanels[i].setDividerLocation(prefs.getInt("divLoc" + i, 300)); //System.out.println("SplitPanel.restorePrefs divLoc" + i + "=" + prefs.getInt("divLoc" + i, 300)); } } /** * Gets the plug-in container for a given panel. * * @param location the location of the desired container (SplitPanel.NORTH, SOUTH, etc.) * * @return the plug-in container */ public JTabbedPane getPanel(final int location) { Component component; switch (location) { case NORTH: component = outerPanels[NORTH].getTopComponent(); break; case EAST: component = outerPanels[EAST].getRightComponent(); break; case SOUTH: component = outerPanels[SOUTH].getBottomComponent(); break; case WEST: component = outerPanels[WEST].getLeftComponent(); break; default: component = null; } return (JTabbedPane) component; } /** * Shows/hides dividers according to visibility of * their associated plug-in containers. */ public void adjustDividerSizes() { JSplitPane splitPane; splitPane = outerPanels[NORTH]; if (splitPane.getTopComponent().isVisible()) { //System.out.println("north panel top is visible, showing divider"); splitPane.setDividerSize(2); } else { splitPane.setDividerSize(0); } splitPane = outerPanels[WEST]; if (splitPane.getLeftComponent().isVisible()) { //System.out.println("west panel left is visible, showing divider"); splitPane.setDividerSize(2); } else { splitPane.setDividerSize(0); } splitPane = outerPanels[SOUTH]; if (splitPane.getBottomComponent().isVisible()) { //System.out.println("south panel bottom is visible, showing divider"); splitPane.setDividerSize(2); } else { splitPane.setDividerSize(0); } splitPane = outerPanels[EAST]; if (splitPane.getRightComponent().isVisible()) { //System.out.println("south panel right is visible, showing divider"); splitPane.setDividerSize(2); } else { splitPane.setDividerSize(0); } } /** * Adds a plug-in container to this SplitPanel ata given location. * * @param c the plug-in container to add * @param location the location to add to (SplitPanel.NORTH, SOUTH, etc.) */ public void addComponent(final JComponent component, final int location) { if (majorAxis == SplitPanel.MAJOR_AXIS_VERTICAL) { //System.out.println("SplitPanel.addComponent majorAxis = vertical"); switch (location) { case CENTER: //System.out.println("SplitPanel.addComponent CENTER"); outerPanels[NORTH].remove(outerPanels[NORTH].getBottomComponent()); outerPanels[NORTH].setBottomComponent(component); break; case NORTH: //System.out.println("SplitPanel.addComponent NORTH"); outerPanels[NORTH].remove(outerPanels[NORTH].getTopComponent()); outerPanels[NORTH].setTopComponent(component); break; case WEST: //System.out.println("SplitPanel.addComponent WEST"); outerPanels[WEST].remove(outerPanels[WEST].getLeftComponent()); outerPanels[WEST].setLeftComponent(component); break; case SOUTH: //System.out.println("SplitPanel.addComponent SOUTH"); outerPanels[SOUTH].remove(outerPanels[SOUTH].getBottomComponent()); outerPanels[SOUTH].setBottomComponent(component); break; case EAST: //System.out.println("SplitPanel.addComponent EAST"); outerPanels[EAST].remove(outerPanels[EAST].getRightComponent()); outerPanels[EAST].setRightComponent(component); break; } } else { // pending... switch (location) { case CENTER: break; case NORTH: break; case WEST: break; case SOUTH: break; case EAST: break; } } } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/DocNameMissingException.java0100644 0000000 0000000 00000003675 12114157751 025042 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; /** * Signals that the name of a document in SimplyHTML is not yet set. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class DocNameMissingException extends Exception { /** * Constructs a DocNameMissingException with null * as its error detail message. */ public DocNameMissingException() { super(); } /** * Constructs an IOException with the specified detail * message. The error message string s can later be * retrieved by the {@link java.lang.Throwable#getMessage} * method of class java.lang.Throwable. * * @param s the detail message. */ public DocNameMissingException(final String s) { super(s); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/StylePanel.java0100644 0000000 0000000 00000011417 12114157751 022374 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import javax.swing.JLabel; import javax.swing.text.html.CSS; import javax.swing.text.html.HTML; /** * Panel to set general text style attributes such as indent or alignment. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class StylePanel extends AttributePanel { public static final int TYPE_PARAGRAPH = 1; public static final int TYPE_TABLE_CELL = 2; private final AttributeComboBox ctAlgn; private final AttributeComboBox cAlgn; public StylePanel(final int type) { super(); JLabel lb; // have a grid bag layout ready to use final GridBagLayout g = new GridBagLayout(); final GridBagConstraints c = new GridBagConstraints(); this.setLayout(g); if (type == TYPE_TABLE_CELL) { // background color label lb = new JLabel(Util.getResourceString("tableBgColLabel")); Util.addGridBagComponent(this, lb, g, c, 0, 0, GridBagConstraints.EAST); // background color panel final ColorPanel cp = new ColorPanel(null, Color.white, CSS.Attribute.BACKGROUND_COLOR); Util.addGridBagComponent(this, cp, g, c, 1, 0, GridBagConstraints.WEST); } // text alignment label lb = new JLabel(Util.getResourceString("alignLabel")); Util.addGridBagComponent(this, lb, g, c, 0, 1, GridBagConstraints.EAST); // text align combo box String[] items = new String[] { Util.getResourceString("alignLeft"), Util.getResourceString("alignCenter"), Util.getResourceString("alignRight") }; String[] names = new String[] { "left", "center", "right" }; ctAlgn = new AttributeComboBox(items, names, CSS.Attribute.TEXT_ALIGN, HTML.Attribute.ALIGN); Util.addGridBagComponent(this, ctAlgn, g, c, 1, 1, GridBagConstraints.WEST); // vertical alignment label lb = new JLabel(Util.getResourceString("valignLabel")); Util.addGridBagComponent(this, lb, g, c, 0, 2, GridBagConstraints.EAST); // vertical alignment combo box items = new String[] { Util.getResourceString("valignTop"), Util.getResourceString("valignMiddle"), Util.getResourceString("valignBottom"), Util.getResourceString("valignBaseline") }; names = new String[] { "top", "middle", "bottom", "baseline" }; cAlgn = new AttributeComboBox(items, names, CSS.Attribute.VERTICAL_ALIGN, HTML.Attribute.VALIGN); Util.addGridBagComponent(this, cAlgn, g, c, 1, 2, GridBagConstraints.WEST); switch (type) { case TYPE_PARAGRAPH: addSizeSelector(Util.getResourceString("textIndentLabel"), CSS.Attribute.TEXT_INDENT, null, true, g, c); break; case TYPE_TABLE_CELL: addSizeSelector(Util.getResourceString("tableWidthLabel"), CSS.Attribute.WIDTH, HTML.Attribute.WIDTH, false, g, c); break; } } public void reset() { ctAlgn.reset(); cAlgn.reset(); } private void addSizeSelector(final String text, final CSS.Attribute ca, final HTML.Attribute ha, final boolean negVals, final GridBagLayout g, final GridBagConstraints c) { // label final JLabel lb = new JLabel(text); Util.addGridBagComponent(this, lb, g, c, 0, 3, GridBagConstraints.EAST); // selector final SizeSelectorPanel ssp = new SizeSelectorPanel(ca, ha, negVals, SizeSelectorPanel.TYPE_COMBO); Util.addGridBagComponent(this, ssp, g, c, 1, 3, GridBagConstraints.WEST); } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/package.html0100644 0000000 0000000 00000000705 12114157751 021730 0ustar000000000 0000000 SimplyHTML, a word processor based on Java, HTML and CSS @author Ulrich Hilger, Dimitry Polivaev @author http://simplyhtml.sourceforge.net/ @author published under the terms and conditions of the GNU General Public License, for details see file gpl.txt in the distribution package of this software @version 0.12.2, 2007 simplyhtml-0.17.3/src/com/lightdev/app/shtm/ManagePluginsAction.java0100644 0000000 0000000 00000005723 13147564753 024222 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Component; import java.awt.event.ActionEvent; import java.util.Enumeration; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; /** * Action to invoke a PluginManagerDialog * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * */ class ManagePluginsAction extends AbstractAction implements SHTMLAction { public static final String managePluginsAction = "managePlugins"; public ManagePluginsAction() { super(); SHTMLPanelImpl.configureActionProperties(this, managePluginsAction); } public void actionPerformed(final ActionEvent e) { final JPopupMenu menu = (JPopupMenu) ((Component) e.getSource()).getParent(); final SHTMLPanelImpl shtmlPanel = (SHTMLPanelImpl) SwingUtilities.getAncestorOfClass(SHTMLPanelImpl.class, menu.getInvoker()); final PluginManagerDialog pmd = new PluginManagerDialog(JOptionPane.getFrameForComponent(shtmlPanel), Util.getResourceString("pluginManagerDialogTitle")); Util.center(shtmlPanel, pmd); pmd.setModal(true); pmd.setVisible(true); /** if the user made a selection, apply it to the document */ if (pmd.getResult() == DialogShell.RESULT_OK) { shtmlPanel.clearDockPanels(); final Enumeration plugins = SHTMLPanelImpl.pluginManager.plugins(); SHTMLPlugin pi; while (plugins.hasMoreElements()) { pi = (SHTMLPlugin) plugins.nextElement(); shtmlPanel.refreshPluginDisplay(pi); } shtmlPanel.paintComponents(shtmlPanel.getGraphics()); } shtmlPanel.adjustDividers(); shtmlPanel.updateActions(); } public void update() { } } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLPanel.java0100644 0000000 0000000 00000007311 13147564753 022174 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Created on 10.09.2006 * Copyright (C) 2006 Dimitri Polivaev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Dimension; import java.awt.LayoutManager; import java.awt.event.ActionListener; import javax.swing.Action; import javax.swing.JEditorPane; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.text.html.HTMLDocument; /** * Class for using SimplyHTML as as component * * @author Dimitri Polivaev * 14.01.2007 */ public abstract class SHTMLPanel extends JPanel implements SHTMLPrefsChangeListener { SHTMLPanel(final LayoutManager layout) { super(layout); } public void shtmlPrefChanged(String propertyName, String newValue, String oldValue) { //System.out.format("SHTMLPanel.shtmlPrefChanged(%s, %s, %s)\n", // propertyName, newValue, oldValue); if (propertyName.equals("default_paste_mode")) { ((SHTMLEditorKitActions.SHTMLEditPasteOtherAction)getAction("pasteOther")) .updateActionName(SHTMLEditorPane.PasteMode.valueOf(newValue).invert()); } } public static SHTMLPanel createSHTMLPanel() { return new SHTMLPanelSingleDocImpl(); } public abstract String getDocumentText(); public abstract boolean needsSaving(); public abstract void setContentPanePreferredSize(Dimension dimension); public abstract void setCurrentDocumentContent(String sText); public static void setResources(final UIResources resources) { SHTMLPanelImpl.setUiResources(resources); } public static void setActionBuilder(final ActionBuilder ab){ SHTMLPanelImpl.setActionBuilder(ab); } public abstract HTMLDocument getDocument(); public abstract JEditorPane getEditorPane(); public abstract JEditorPane getSourceEditorPane(); public static UIResources getResources() { return SHTMLPanelImpl.getUiResources(); } abstract public int getCaretPosition(); public abstract JMenuBar getMenuBar(); public abstract JEditorPane getMostRecentFocusOwner(); public abstract Action getAction(String actionName); public abstract void addAction(String text, Action action); /** * Returns a new menu item for a named action of SimplyHTML. (Can be used for building custom * popup menu, or for invoking the action externally in another way.) */ public abstract JMenuItem createActionMenuItem(String actionName); /** * Switches between the rich text view and the source view, given * tabbed panes are not used. Has no corresponding action; calling * this method is up to the caller application of SimplyHTML. */ public abstract void switchViews(); /** * Sets the handler for the Open Hyperlink action. SimplyHTML itself has * no ability to open hyperlinks, so it forwards the action to the caller * application. */ public abstract void setOpenHyperlinkHandler(ActionListener openHyperlinkHandler); } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SyntaxPane.java0100644 0000000 0000000 00000026524 12114157751 022413 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002, 2003 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.util.Enumeration; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JEditorPane; import javax.swing.JRootPane; import javax.swing.SwingUtilities; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; import javax.swing.text.StyledEditorKit; /** * An editor pane with syntax highlighting for HTML tags. * *

      This is a basic approach to syntax highlighting * using regular expressions. It is used to make the plain * HTML view of application SimplyHTML more legible by * separating tags and attributes from content.

      * *

      The method doing the actual highlighting work * is put into an implementation of the Runnable interface * so that it can be called without conflicts.

      * *

      Can be refined in the way Patterns are set up, i.e. * not hard wire pattern setup and a GUI to let the * use choose styles for patterns.

      * *

      Recommended readings:
      * 'Regular Expressions and the JavaTM Programming Language' at
      * http://developer.java.sun.com/developer/technicalArticles/releases/1.4regex/
      * and
      * Presentation slides 'Rich Clients for Web Services' from JavaOne 2002 at
      * http://servlet.java.sun.com/javaone/resources/content/sf2002/conf/sessions/pdfs/2274.pdf *

      * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * * */ class SyntaxPane extends JEditorPane implements CaretListener { /** * Creates a new SyntaxPane. */ public SyntaxPane() { super(); setEditorKit(new StyledEditorKit()); setupPatterns(); } /** * set up HTML patterns and attributes */ private void setupPatterns() { Pattern p; SimpleAttributeSet set; patterns = new Vector(); // content text p = Pattern.compile("\\b\\w+"); set = new SimpleAttributeSet(); StyleConstants.setForeground(set, Color.BLACK); StyleConstants.setBold(set, false); patterns.addElement(new RegExStyle(p, set)); // a tag p = Pattern.compile("<[/a-zA-Z0-9\\s]+"); set = new SimpleAttributeSet(); StyleConstants.setForeground(set, new Color(0, 0, 128)); StyleConstants.setBold(set, true); patterns.addElement(new RegExStyle(p, set)); // a tag end p = Pattern.compile(">"); patterns.addElement(new RegExStyle(p, set)); // an attribute p = Pattern.compile("\\s[/a-zA-Z0-9]+="); set = new SimpleAttributeSet(); StyleConstants.setForeground(set, new Color(158, 119, 0)); StyleConstants.setBold(set, true); patterns.addElement(new RegExStyle(p, set)); // attribute values p = Pattern.compile("\"[\\x2D;:/.%#?=,\\w\\s]+\""); set = new SimpleAttributeSet(); StyleConstants.setForeground(set, Color.BLUE); StyleConstants.setBold(set, false); patterns.addElement(new RegExStyle(p, set)); } /** * apply syntax highlighting to all HTML tags found in the given * area of the given document * * @param doc the document to apply syntax highlighting to * @param offset the position inside the given document to start to apply syntax highlighting to * @param len the number of characters to apply syntax highlighting to */ public void setMarks(final StyledDocument sDoc, final int offset, final int len) { SwingUtilities.invokeLater(new StyleUpdater(this, sDoc, offset, len)); } /** * overridden from JEditorPane * to suppress line wraps * * @see setSize */ public boolean getScrollableTracksViewportWidth() { return false; } /** * overridden from JEditorPane * to suppress line wraps * * @see getScrollableTracksViewportWidth */ public void setSize(final Dimension d) { if (d.width < getParent().getSize().width) { d.width = getParent().getSize().width; } super.setSize(d); } /** * display a wait cursor for lengthy operations */ private void cursor() { final JRootPane rootPane = getRootPane(); if (rootPane != null) { final Component gp = rootPane.getGlassPane(); if (!gp.isVisible()) { gp.setCursor(waitCursor); gp.setVisible(true); } else { gp.setVisible(false); } } } /** * StyleUpdater does the actual syntax highlighting work * and can be used in SwingUtilities.invokeLater() */ private class StyleUpdater implements Runnable { /** * construct a StyleUpdater * * @param sp the SyntaxPane this StyleUpdater works on * @param doc the document to apply syntax highlighting to * @param offset the position inside the given document to start to apply syntax highlighting to * @param len the number of characters to apply syntax highlighting to */ public StyleUpdater(final SyntaxPane sp, final StyledDocument doc, final int offset, final int len) { sDoc = doc; this.offset = offset; this.len = len; this.sp = sp; } /** * apply snytax highlighting */ public void run() { Matcher m; RegExStyle style; cursor(); sp.removeCaretListener(sp); try { final int length = sDoc.getLength(); if (length > 0 && len > 0) { final String text = sDoc.getText(offset, len); if (text != null && text.length() > 0) { final Enumeration pe = patterns.elements(); while (pe.hasMoreElements()) { style = (RegExStyle) pe.nextElement(); m = style.getPattern().matcher(text); while (m.find()) { sDoc.setCharacterAttributes(offset + m.start(), m.end() - m.start(), style.getStyle(), true); } } } } } catch (final Exception ex) { System.out.println("StyleUpdater ERROR: " + ex.getMessage()); } sp.addCaretListener(sp); cursor(); } /** the document to apply syntax highlighting to */ private final StyledDocument sDoc; /** the position inside the given document to start to apply syntax highlighting to */ private final int offset; /** the number of characters to apply syntax highlighting to */ private final int len; /** the SyntaxPane this StyleUpdater works on */ private final SyntaxPane sp; } /** * CaretListener implementation * *

      updates syntax highlighting for the current line * when the caret moves

      */ public void caretUpdate(final CaretEvent e) { try { final StyledDocument sDoc = (StyledDocument) getDocument(); final int cPos = e.getDot(); final int length = sDoc.getLength(); final String text = sDoc.getText(0, length); final int lineStart = text.substring(0, cPos).lastIndexOf("\n") + 1; int lineEnd = text.indexOf("\n", cPos); if (lineEnd < 0) { lineEnd = length; } setMarks(sDoc, lineStart, lineEnd - lineStart); } catch (final Exception ex) { } } /** * overridden to keep caret changes during the initial text load * from triggering syntax highlighting repetitively */ public void setText(final String t) { removeCaretListener(this); super.setText(t); final StyledDocument sDoc = (StyledDocument) getDocument(); setMarks(sDoc, 0, sDoc.getLength()); setCaretPosition(0); addCaretListener(this); } /** * convenience class associating a pattern with a * set of attributes */ class RegExStyle { /** * construct a RegExStyle instance * * @param p the Pattern to apply this style to * @param a the attributes making up this style */ public RegExStyle(final Pattern p, final AttributeSet a) { this.p = p; this.a = a; } /** * get the Pattern this style is to be applied to * * @return the Pattern */ public Pattern getPattern() { return p; } /** * get the attributes making up this style * * @return the set of attributes */ public AttributeSet getStyle() { return a; } /** * set the Pattern to apply a given set of attributes to * * @param p the Pattern */ public void setPattern(final Pattern p) { this.p = p; } /** * set the set of attributes to apply to a given Pattern * * @param a the set of attributes to use */ public void setStyle(final AttributeSet a) { this.a = a; } /** the Pattern to apply this style to */ private Pattern p; /** the attributes making up this style */ private AttributeSet a; } /** Patterns registered with this SnytaxPane */ private Vector patterns; /** the cursor to use to indicate a lengthy operation is going on */ private final Cursor waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); } simplyhtml-0.17.3/src/com/lightdev/app/shtm/SHTMLAction.java0100644 0000000 0000000 00000002763 12744670610 022350 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2002 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.lightdev.app.shtm; import javax.swing.Action; /** * Adds methods to the javax.swing.Action interface, needed * by Application SimplyHTML. * * @author Ulrich Hilger * @author Light Development * @author http://www.lightdev.com * @author info@lightdev.com * @author published under the terms and conditions of the * GNU General Public License, * for details see file gpl.txt in the distribution * package of this software * * * * @see javax.swing.Action */ public interface SHTMLAction extends Action { /** update the action's state */ public void update(); } simplyhtml-0.17.3/src/com/sun/0040755 0000000 0000000 00000000000 12114157751 014714 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/sun/demo/0040755 0000000 0000000 00000000000 12114157751 015640 5ustar000000000 0000000 simplyhtml-0.17.3/src/com/sun/demo/ExampleFileFilter.java0100644 0000000 0000000 00000022161 12114157751 022043 0ustar000000000 0000000 /* * @(#)ExampleFileFilter.java 1.8 04/07/26 * * Copyright (c) 2004 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ /* * @(#)ExampleFileFilter.java 1.8 04/07/26 */ /* for use in SimplyHTML */ package com.sun.demo; import java.io.File; import java.util.Enumeration; import java.util.Hashtable; import javax.swing.filechooser.FileFilter; /** * A convenience implementation of FileFilter that filters out * all files except for those type extensions that it knows about. * * Extensions are of the type ".foo", which is typically found on * Windows and Unix boxes, but not on Macinthosh. Case is ignored. * * Example - create a new filter that filerts out all files * but gif and jpg image files: * * JFileChooser chooser = new JFileChooser(); * ExampleFileFilter filter = new ExampleFileFilter( * new String{"gif", "jpg"}, "JPEG & GIF Images") * chooser.addChoosableFileFilter(filter); * chooser.showOpenDialog(this); * * @version 1.8 07/26/04 * @author Jeff Dinkins */ public class ExampleFileFilter extends FileFilter { private static String TYPE_UNKNOWN = "Type Unknown"; private static String HIDDEN_FILE = "Hidden File"; private Hashtable filters = null; private String description = null; private String fullDescription = null; private boolean useExtensionsInDescription = true; /** * Creates a file filter. If no filters are added, then all * files are accepted. * * @see #addExtension */ public ExampleFileFilter() { filters = new Hashtable(); } /** * Creates a file filter that accepts files with the given extension. * Example: new ExampleFileFilter("jpg"); * * @see #addExtension */ public ExampleFileFilter(final String extension) { this(extension, null); } /** * Creates a file filter that accepts the given file type. * Example: new ExampleFileFilter("jpg", "JPEG Image Images"); * * Note that the "." before the extension is not needed. If * provided, it will be ignored. * * @see #addExtension */ public ExampleFileFilter(final String extension, final String description) { this(); if (extension != null) { addExtension(extension); } if (description != null) { setDescription(description); } } /** * Creates a file filter from the given string array. * Example: new ExampleFileFilter(String {"gif", "jpg"}); * * Note that the "." before the extension is not needed adn * will be ignored. * * @see #addExtension */ public ExampleFileFilter(final String[] filters) { this(filters, null); } /** * Creates a file filter from the given string array and description. * Example: new ExampleFileFilter(String {"gif", "jpg"}, "Gif and JPG Images"); * * Note that the "." before the extension is not needed and will be ignored. * * @see #addExtension */ public ExampleFileFilter(final String[] filters, final String description) { this(); for (int i = 0; i < filters.length; i++) { // add filters one by one addExtension(filters[i]); } if (description != null) { setDescription(description); } } /** * Return true if this file should be shown in the directory pane, * false if it shouldn't. * * Files that begin with "." are ignored. * * @see #getExtension * @see FileFilter#accepts */ public boolean accept(final File f) { if (f != null) { if (f.isDirectory()) { return true; } final String extension = getExtension(f); if (extension != null && filters.get(getExtension(f)) != null) { return true; }; } return false; } /** * Return the extension portion of the file's name . * * @see #getExtension * @see FileFilter#accept */ public String getExtension(final File f) { if (f != null) { final String filename = f.getName(); final int i = filename.lastIndexOf('.'); if (i > 0 && i < filename.length() - 1) { return filename.substring(i + 1).toLowerCase(); }; } return null; } /** * Adds a filetype "dot" extension to filter against. * * For example: the following code will create a filter that filters * out all files except those that end in ".jpg" and ".tif": * * ExampleFileFilter filter = new ExampleFileFilter(); * filter.addExtension("jpg"); * filter.addExtension("tif"); * * Note that the "." before the extension is not needed and will be ignored. */ public void addExtension(final String extension) { if (filters == null) { filters = new Hashtable(5); } filters.put(extension.toLowerCase(), this); fullDescription = null; } /** * Returns the human readable description of this filter. For * example: "JPEG and GIF Image Files (*.jpg, *.gif)" * * @see setDescription * @see setExtensionListInDescription * @see isExtensionListInDescription * @see FileFilter#getDescription */ public String getDescription() { if (fullDescription == null) { if (description == null || isExtensionListInDescription()) { fullDescription = description == null ? "(" : description + " ("; // build the description from the extension list final Enumeration extensions = filters.keys(); if (extensions != null) { fullDescription += "." + (String) extensions.nextElement(); while (extensions.hasMoreElements()) { fullDescription += ", ." + (String) extensions.nextElement(); } } fullDescription += ")"; } else { fullDescription = description; } } return fullDescription; } /** * Sets the human readable description of this filter. For * example: filter.setDescription("Gif and JPG Images"); * * @see setDescription * @see setExtensionListInDescription * @see isExtensionListInDescription */ public void setDescription(final String description) { this.description = description; fullDescription = null; } /** * Determines whether the extension list (.jpg, .gif, etc) should * show up in the human readable description. * * Only relevent if a description was provided in the constructor * or using setDescription(); * * @see getDescription * @see setDescription * @see isExtensionListInDescription */ public void setExtensionListInDescription(final boolean b) { useExtensionsInDescription = b; fullDescription = null; } /** * Returns whether the extension list (.jpg, .gif, etc) should * show up in the human readable description. * * Only relevent if a description was provided in the constructor * or using setDescription(); * * @see getDescription * @see setDescription * @see setExtensionListInDescription */ public boolean isExtensionListInDescription() { return useExtensionsInDescription; } } simplyhtml-0.17.3/src/com/sun/demo/ElementTreePanel.java0100644 0000000 0000000 00000054340 12114157751 021677 0ustar000000000 0000000 /* * @(#)ElementTreePanel.java 1.16 04/07/26 slightly modified (line 121) * * Copyright (c) 2004 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ /* * @(#)ElementTreePanel.java 1.16 04/07/26 */ /* for use in SimplyHTML */ package com.sun.demo; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Enumeration; import java.util.Vector; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.SwingConstants; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.JTextComponent; import javax.swing.text.StyleConstants; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; /** * Displays a tree showing all the elements in a text Document. Selecting * a node will result in reseting the selection of the JTextComponent. * This also becomes a CaretListener to know when the selection has changed * in the text to update the selected item in the tree. * * @author Scott Violet * @version 1.16 07/26/04 */ public class ElementTreePanel extends JPanel implements CaretListener, DocumentListener, PropertyChangeListener, TreeSelectionListener { /** Tree showing the documents element structure. */ protected JTree tree; /** Text component showing elemenst for. */ protected JTextComponent editor; /** Model for the tree. */ protected ElementTreeModel treeModel; /** Set to true when updatin the selection. */ protected boolean updatingSelection; public ElementTreePanel(final JTextComponent editor) { this.editor = editor; final Document document = editor.getDocument(); // Create the tree. treeModel = new ElementTreeModel(document); tree = new JTree(treeModel) { public String convertValueToText(final Object value, final boolean selected, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) { // Should only happen for the root if (!(value instanceof Element)) { return value.toString(); } final Element e = (Element) value; final AttributeSet as = e.getAttributes().copyAttributes(); String asString; if (as != null) { final StringBuffer retBuffer = new StringBuffer("["); final Enumeration names = as.getAttributeNames(); while (names.hasMoreElements()) { final Object nextName = names.nextElement(); if (nextName != StyleConstants.ResolveAttribute) { retBuffer.append(" "); retBuffer.append(nextName); retBuffer.append("="); retBuffer.append(as.getAttribute(nextName)); } } retBuffer.append(" ]"); asString = retBuffer.toString(); } else { asString = "[ ]"; } if (e.isLeaf()) { return e.getName() + " [" + e.getStartOffset() + ", " + e.getEndOffset() + "] Attributes: " + asString; } return e.getName() + " [" + e.getStartOffset() + ", " + e.getEndOffset() + "] Attributes: " + asString; } }; tree.addTreeSelectionListener(this); /* commented out for use in SimplyHTML */// tree.setDragEnabled(true); // Don't show the root, it is fake. tree.setRootVisible(false); // Since the display value of every node after the insertion point // changes every time the text changes and we don't generate a change // event for all those nodes the display value can become off. // This can be seen as '...' instead of the complete string value. // This is a temporary workaround, increase the needed size by 15, // hoping that will be enough. tree.setCellRenderer(new DefaultTreeCellRenderer() { public Dimension getPreferredSize() { final Dimension retValue = super.getPreferredSize(); if (retValue != null) { retValue.width += 15; } return retValue; } }); // become a listener on the document to update the tree. document.addDocumentListener(this); // become a PropertyChangeListener to know when the Document has // changed. editor.addPropertyChangeListener(this); // Become a CaretListener editor.addCaretListener(this); // configure the panel and frame containing it. setLayout(new BorderLayout()); add(new JScrollPane(tree), BorderLayout.CENTER); // Add a label above tree to describe what is being shown final JLabel label = new JLabel("Elements that make up the current document", SwingConstants.CENTER); label.setFont(new Font("Dialog", Font.BOLD, 14)); add(label, BorderLayout.NORTH); setPreferredSize(new Dimension(400, 400)); } /** * Resets the JTextComponent to editor. This will update * the tree accordingly. */ public void setEditor(final JTextComponent editor) { if (this.editor == editor) { return; } if (this.editor != null) { final Document oldDoc = this.editor.getDocument(); oldDoc.removeDocumentListener(this); this.editor.removePropertyChangeListener(this); this.editor.removeCaretListener(this); } this.editor = editor; if (editor == null) { treeModel = null; tree.setModel(null); } else { final Document newDoc = editor.getDocument(); newDoc.addDocumentListener(this); editor.addPropertyChangeListener(this); editor.addCaretListener(this); treeModel = new ElementTreeModel(newDoc); tree.setModel(treeModel); } } // PropertyChangeListener /** * Invoked when a property changes. We are only interested in when the * Document changes to reset the DocumentListener. */ public void propertyChange(final PropertyChangeEvent e) { if (e.getSource() == getEditor() && e.getPropertyName().equals("document")) { getEditor(); final Document oldDoc = (Document) e.getOldValue(); final Document newDoc = (Document) e.getNewValue(); // Reset the DocumentListener oldDoc.removeDocumentListener(this); newDoc.addDocumentListener(this); // Recreate the TreeModel. treeModel = new ElementTreeModel(newDoc); tree.setModel(treeModel); } } // DocumentListener /** * Gives notification that there was an insert into the document. The * given range bounds the freshly inserted region. * * @param e the document event */ public void insertUpdate(final DocumentEvent e) { updateTree(e); } /** * Gives notification that a portion of the document has been * removed. The range is given in terms of what the view last * saw (that is, before updating sticky positions). * * @param e the document event */ public void removeUpdate(final DocumentEvent e) { updateTree(e); } /** * Gives notification that an attribute or set of attributes changed. * * @param e the document event */ public void changedUpdate(final DocumentEvent e) { updateTree(e); } // CaretListener /** * Messaged when the selection in the editor has changed. Will update * the selection in the tree. */ public void caretUpdate(final CaretEvent e) { if (!updatingSelection) { getEditor(); final int selBegin = Math.min(e.getDot(), e.getMark()); final int end = Math.max(e.getDot(), e.getMark()); final Vector paths = new Vector(); final TreeModel model = getTreeModel(); final Object root = model.getRoot(); final int rootCount = model.getChildCount(root); // Build an array of all the paths to all the character elements // in the selection. for (int counter = 0; counter < rootCount; counter++) { int start = selBegin; while (start <= end) { final TreePath path = getPathForIndex(start, root, (Element) model.getChild(root, counter)); final Element charElement = (Element) path.getLastPathComponent(); paths.addElement(path); if (start >= charElement.getEndOffset()) { start++; } else { start = charElement.getEndOffset(); } } } // If a path was found, select it (them). final int numPaths = paths.size(); if (numPaths > 0) { final TreePath[] pathArray = new TreePath[numPaths]; paths.copyInto(pathArray); updatingSelection = true; try { getTree().setSelectionPaths(pathArray); getTree().scrollPathToVisible(pathArray[0]); } finally { updatingSelection = false; } } } } // TreeSelectionListener /** * Called whenever the value of the selection changes. * @param e the event that characterizes the change. */ public void valueChanged(final TreeSelectionEvent e) { final JTree tree = getTree(); if (!updatingSelection && tree.getSelectionCount() == 1) { final TreePath selPath = tree.getSelectionPath(); final Object lastPathComponent = selPath.getLastPathComponent(); if (!(lastPathComponent instanceof DefaultMutableTreeNode)) { final Element selElement = (Element) lastPathComponent; updatingSelection = true; try { getEditor().select(selElement.getStartOffset(), selElement.getEndOffset()); } finally { updatingSelection = false; } } } } // Local methods /** * @return tree showing elements. */ protected JTree getTree() { return tree; } /** * @return JTextComponent showing elements for. */ protected JTextComponent getEditor() { return editor; } /** * @return TreeModel implementation used to represent the elements. */ public DefaultTreeModel getTreeModel() { return treeModel; } /** * Updates the tree based on the event type. This will invoke either * updateTree with the root element, or handleChange. */ protected void updateTree(final DocumentEvent event) { updatingSelection = true; try { final TreeModel model = getTreeModel(); final Object root = model.getRoot(); for (int counter = model.getChildCount(root) - 1; counter >= 0; counter--) { updateTree(event, (Element) model.getChild(root, counter)); } } finally { updatingSelection = false; } } /** * Creates TreeModelEvents based on the DocumentEvent and messages * the treemodel. This recursively invokes this method with children * elements. * @param event indicates what elements in the tree hierarchy have * changed. * @param element Current element to check for changes against. */ protected void updateTree(final DocumentEvent event, final Element element) { final DocumentEvent.ElementChange ec = event.getChange(element); if (ec != null) { final Element[] removed = ec.getChildrenRemoved(); final Element[] added = ec.getChildrenAdded(); final int startIndex = ec.getIndex(); // Check for removed. if (removed != null && removed.length > 0) { final int[] indices = new int[removed.length]; for (int counter = 0; counter < removed.length; counter++) { indices[counter] = startIndex + counter; } getTreeModel().nodesWereRemoved((TreeNode) element, indices, removed); } // check for added if (added != null && added.length > 0) { final int[] indices = new int[added.length]; for (int counter = 0; counter < added.length; counter++) { indices[counter] = startIndex + counter; } getTreeModel().nodesWereInserted((TreeNode) element, indices); } } if (!element.isLeaf()) { int startIndex = element.getElementIndex(event.getOffset()); final int elementCount = element.getElementCount(); final int endIndex = Math.min(elementCount - 1, element.getElementIndex(event.getOffset() + event.getLength())); if (startIndex > 0 && startIndex < elementCount && element.getElement(startIndex).getStartOffset() == event.getOffset()) { // Force checking the previous element. startIndex--; } if (startIndex != -1 && endIndex != -1) { for (int counter = startIndex; counter <= endIndex; counter++) { updateTree(event, element.getElement(counter)); } } } else { // Element is a leaf, assume it changed getTreeModel().nodeChanged((TreeNode) element); } } /** * Returns a TreePath to the element at position. */ protected TreePath getPathForIndex(final int position, final Object root, final Element rootElement) { TreePath path = new TreePath(root); Element child = rootElement.getElement(rootElement.getElementIndex(position)); path = path.pathByAddingChild(rootElement); path = path.pathByAddingChild(child); while (!child.isLeaf()) { child = child.getElement(child.getElementIndex(position)); path = path.pathByAddingChild(child); } return path; } /** * ElementTreeModel is an implementation of TreeModel to handle displaying * the Elements from a Document. AbstractDocument.AbstractElement is * the default implementation used by the swing text package to implement * Element, and it implements TreeNode. This makes it trivial to create * a DefaultTreeModel rooted at a particular Element from the Document. * Unfortunately each Document can have more than one root Element. * Implying that to display all the root elements as a child of another * root a fake node has be created. This class creates a fake node as * the root with the children being the root elements of the Document * (getRootElements). *

      This subclasses DefaultTreeModel. The majority of the TreeModel * methods have been subclassed, primarily to special case the root. */ public static class ElementTreeModel extends DefaultTreeModel { protected Element[] rootElements; public ElementTreeModel(final Document document) { super(new DefaultMutableTreeNode("root"), false); rootElements = document.getRootElements(); } /** * Returns the child of parent at index index in * the parent's child array. parent must be a node * previously obtained from this data source. This should * not return null if index is a valid index for * parent (that is index >= 0 && index * < getChildCount(parent)). * * @param parent a node in the tree, obtained from this data source * @return the child of parent at index index */ public Object getChild(final Object parent, final int index) { if (parent == root) { return rootElements[index]; } return super.getChild(parent, index); } /** * Returns the number of children of parent. Returns 0 * if the node is a leaf or if it has no children. * parent must be a node previously obtained from this * data source. * * @param parent a node in the tree, obtained from this data source * @return the number of children of the node parent */ public int getChildCount(final Object parent) { if (parent == root) { return rootElements.length; } return super.getChildCount(parent); } /** * Returns true if node is a leaf. It is possible for * this method to return false even if node has no * children. A directory in a filesystem, for example, may * contain no files; the node representing the directory is * not a leaf, but it also has no children. * * @param node a node in the tree, obtained from this data source * @return true if node is a leaf */ public boolean isLeaf(final Object node) { if (node == root) { return false; } return super.isLeaf(node); } /** * Returns the index of child in parent. */ public int getIndexOfChild(final Object parent, final Object child) { if (parent == root) { for (int counter = rootElements.length - 1; counter >= 0; counter--) { if (rootElements[counter] == child) { return counter; } } return -1; } return super.getIndexOfChild(parent, child); } /** * Invoke this method after you've changed how node is to be * represented in the tree. */ public void nodeChanged(final TreeNode node) { if (listenerList != null && node != null) { TreeNode parent = node.getParent(); if (parent == null && node != root) { parent = root; } if (parent != null) { final int anIndex = getIndexOfChild(parent, node); if (anIndex != -1) { final int[] cIndexs = new int[1]; cIndexs[0] = anIndex; nodesChanged(parent, cIndexs); } } } } /** * Returns the path to a particluar node. This is recursive. */ protected TreeNode[] getPathToRoot(final TreeNode aNode, int depth) { TreeNode[] retNodes; /* Check for null, in case someone passed in a null node, or they passed in an element that isn't rooted at root. */ if (aNode == null) { if (depth == 0) { return null; } else { retNodes = new TreeNode[depth]; } } else { depth++; if (aNode == root) { retNodes = new TreeNode[depth]; } else { TreeNode parent = aNode.getParent(); if (parent == null) { parent = root; } retNodes = getPathToRoot(parent, depth); } retNodes[retNodes.length - depth] = aNode; } return retNodes; } } } simplyhtml-0.17.3/src/com/sun/demo/package.html0100644 0000000 0000000 00000000237 12114157751 020120 0ustar000000000 0000000 Parts from the example classes from Sun used for application SimplyHTML, a word processor based on Java, HTML and CSS simplyhtml-0.17.3/src/build.xml0100644 0000000 0000000 00000010741 12676225213 015154 0ustar000000000 0000000 simplyhtml-0.17.3/src/de/0040755 0000000 0000000 00000000000 12114157751 013721 5ustar000000000 0000000 simplyhtml-0.17.3/src/de/calcom/0040755 0000000 0000000 00000000000 12114157751 015157 5ustar000000000 0000000 simplyhtml-0.17.3/src/de/calcom/cclib/0040755 0000000 0000000 00000000000 12114157751 016233 5ustar000000000 0000000 simplyhtml-0.17.3/src/de/calcom/cclib/text/0040755 0000000 0000000 00000000000 13147564753 017232 5ustar000000000 0000000 simplyhtml-0.17.3/src/de/calcom/cclib/text/PseudoDamerauLevenshtein.java0100644 0000000 0000000 00000037245 13147564753 025050 0ustar000000000 0000000 /* * Freeplane - mind map editor * Copyright (C) 2012 Dimitry Polivaev * * This file's author is Felix Natter * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.calcom.cclib.text; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Stack; /** * Pseudo-Damerau-Levenshtein (aka "Optimal String Distance") * implementation which allows some non-adjacent transpositions(?) * Computes the edit distance with insertions/deletions/substitutions/transpositions. * * Optionally the edit distance of a semi-global alignment is computed which * allows the search term to be shifted free-of-cost (i.e. dist("file", "a file is")==0). * * Some properties are explained in the unit test, {@link org.freeplane.features.filter.EditDistanceStringMatchingStrategiesTest}. * * TODO: use unicode code points instead of chars !! * * @author Felix Natter * */ public class PseudoDamerauLevenshtein { public enum Type { Global, SemiGlobal }; private int[][] matrix; private String searchTerm; private String searchText; private final int costIndel = 1; private final int costMismatch = 1; private final int costTranspos = 1; private Type type; private Stack alignmentsInProgress; private ArrayList alignmentsDone; public class Alignment implements Comparable { private final String searchTermString; private final String searchTextString; private final double prob; private final int matchStart; private final int matchEnd; private final int r, c; public int getMatchStart() { return matchStart; } public int getMatchEnd() { return matchEnd; } public boolean overlapsWith(final Alignment other) { return (matchStart <= other.matchStart && other.matchStart <= matchEnd-1) || // endpoint of this lies in other (other.matchStart <= matchStart && matchStart <= other.matchEnd-1); // endpoint of other lies in this } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getOuterType().hashCode(); result = prime * result + c; result = prime * result + matchEnd; result = prime * result + matchStart; long temp; temp = Double.doubleToLongBits(prob); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + r; result = prime * result + ((searchTermString == null) ? 0 : searchTermString .hashCode()); result = prime * result + ((searchTextString == null) ? 0 : searchTextString .hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Alignment other = (Alignment) obj; if (!getOuterType().equals(other.getOuterType())) { return false; } if (c != other.c) { return false; } if (matchEnd != other.matchEnd) { return false; } if (matchStart != other.matchStart) { return false; } if (Double.doubleToLongBits(prob) != Double .doubleToLongBits(other.prob)) { return false; } if (r != other.r) { return false; } if (searchTermString == null) { if (other.searchTermString != null) { return false; } } else if (!searchTermString.equals(other.searchTermString)) { return false; } if (searchTextString == null) { if (other.searchTextString != null) { return false; } } else if (!searchTextString.equals(other.searchTextString)) { return false; } return true; } public String getMatch() { return searchText.substring(matchStart, matchEnd); } public int compareTo(final Alignment other) { if (prob == other.prob) { return new Integer(getMatch().length()).compareTo(new Integer(other.getMatch().length())); } else { return new Double(prob).compareTo(new Double(other.prob)); } } public void print() { System.out.format("Alignment@%x[%.2f]:\n%s\n%s\n=> matches '%s' [%d,%d]\n", hashCode(), prob, searchTermString, searchTextString, getMatch(), matchStart,matchEnd); } @Override public String toString() { return String.format("Ali@%x[%s,%.2f,%d,%d]", hashCode(), getMatch(), prob, matchStart, matchEnd); } public Alignment(final String searchTermString, final String searchTextString, final double prob, final int matchStart, final int matchEnd, final int r, final int c) { this.searchTermString = searchTermString; this.searchTextString = searchTextString; this.prob = prob; this.matchStart = matchStart; this.matchEnd = matchEnd; this.r = r; this.c = c; } private PseudoDamerauLevenshtein getOuterType() { return PseudoDamerauLevenshtein.this; } } private boolean isMatch(int i, int j) { char col = searchTerm.charAt(i-1); char row = searchText.charAt(j-1); if (col == row || row == '-') return true; else return false; } public int distance() { matrix = new int[searchTerm.length()+1][searchText.length()+1]; // [row][col] // first column: start-gap penalties for searchTerm for (int i = 0; i <= (int)searchTerm.length(); i++) matrix[i][0] = i*costIndel; // first row: start-gap penalties for searchText if (type == Type.Global) { for (int j = 1; j <= (int)searchText.length(); j++) matrix[0][j] = j*costIndel; } else if (type == Type.SemiGlobal) { Arrays.fill(matrix[0], 0); } // compute the rest of the matrix for (int i = 1; i <= searchTerm.length(); i++) { for (int j = 1; j <= searchText.length(); j++) { int cost_try_match = matrix[i-1][j-1] + (isMatch(i,j) ? 0 : costMismatch); int cost_ins = matrix[i-1][j] + costIndel; int cost_del = matrix[i][j-1] + costIndel; matrix[i][j] = Math.min(cost_try_match, Math.min(cost_ins, cost_del)); if (i >= 2 && j >= 2 && searchTerm.charAt(i-2) == searchText.charAt(j-1) && searchTerm.charAt(i-1) == searchText.charAt(j-2)) { matrix[i][j] = Math.min(matrix[i][j], matrix[i-2][j-2] + costTranspos); } } } //writeMatrix(matrix); if (type == Type.Global) { return matrix[searchTerm.length()][searchText.length()]; } else { int min = Integer.MAX_VALUE; for (int j = 1; j <= searchText.length()+1; j++) { min = Math.min(min, matrix[searchTerm.length()][j-1]); } return min; } } private void writeMatrix(int[][] H) { for (int i = 0; i < H.length; i++) { for (int j = 0; j < H[0].length; j++) { System.out.format(" %3d", H[i][j]); } System.out.println(); } } public List computeAlignments(final double minProb) { alignmentsInProgress = new Stack(); alignmentsDone = new ArrayList(); int dist = distance(); // this computes the Dynamic Programming matrix according to Levenshtein if (type == Type.Global && getMatchProb(dist) > minProb) { alignmentsInProgress.push(new Alignment("", "", getMatchProb(dist), 0, searchText.length(), searchTerm.length(), searchText.length())); } else { // semi-global "substring" alignment StringBuilder searchTermSuffix = new StringBuilder(); StringBuilder searchTextSuffix = new StringBuilder(); for (int c = searchText.length() + 1; c >= 1; c--) { if (c <= searchText.length()) { searchTermSuffix.append('-'); searchTextSuffix.insert(0, searchText.charAt(c-1)); } double prob = getMatchProb(matrix[searchTerm.length()][c-1]); if (prob > minProb) { alignmentsInProgress.push(new Alignment(searchTermSuffix.toString(), searchTextSuffix.toString(), prob, 0, searchText.length() - searchTextSuffix.length(), searchTerm.length(), c - 1)); } } } while (!alignmentsInProgress.isEmpty()) { developAlignment(alignmentsInProgress.pop()); } // filter (overlapping) alignments alignmentsDone = filterAlignments(alignmentsDone); sortAlignments(alignmentsDone); /* System.out.format("--NON-OVERLAPPPING ALIGNMENTS-------------------\n"); for (Alignment ali: alignmentsDone) { ali.print(); } */ matrix = null; //return alignmentsDone.toArray(new Alignment[alignmentsDone.size()]); return alignmentsDone; } /** * Keep only non-overlapping matches (alignments) while preferring alignments with high score (prob) * TODO: this is a heuristic, is the problem NP complete? * * @param alignments alignments list to filter * @return filtered alignment list */ static ArrayList filterAlignments(final ArrayList alignments) { if (alignments.isEmpty()) return new ArrayList(); // sort by score and match length (see Alignment.compareTo()) Collections.sort(alignments, Collections.reverseOrder()); ArrayList clusters = new ArrayList(alignments.size()); // start with a single cluster clusters.add(alignments.get(0)); alignments.remove(0); // assign alignments to clusters for (Alignment ali: alignments) { boolean found_cluster = false; for (int j = 0; j < clusters.size(); j++) { if (ali.overlapsWith(clusters.get(j))) { found_cluster = true; // keep either current cluster center or set to 'ali' if (ali.compareTo(clusters.get(j)) > 0) { clusters.set(j, ali); } } } if (!found_cluster) { clusters.add(ali); } } return clusters; } /** * Sort alignments (matches) by start positions * @param alignments list of alignments to sort */ static void sortAlignments(final ArrayList alignments) { Collections.sort(alignments, new Comparator() { public int compare(Alignment o1, Alignment o2) { return new Integer(o1.matchStart).compareTo(o2.matchStart); } }); } // private void printAlignmentsFrom(final String searchTermSuffix, final String searchTextSuffix, final int r, final int c, // double prob, int matchStart, int matchEnd) private void developAlignment(final Alignment ali) { // System.out.format("developAlignment(term=%s, text=%s, r=%d, c=%d)", // ali.searchTermString, ali.searchTextString, ali.r, ali.c); if (ali.r == 0 && ali.c == 0) { alignmentsDone.add(ali); // System.out.println(); // ali.print(); } else { // TODO: comments!! // match/mismatch if (ali.r >= 1 && ali.c >= 1 && matrix[ali.r][ali.c] == matrix[ali.r-1][ali.c-1] + (isMatch(ali.r,ali.c) ? 0 : costMismatch)) { // System.out.format("=> match/mismatch\n"); alignmentsInProgress.push(new Alignment( /*searchTerm.charAt(ali.r-1) + ali.searchTermString*/ null, /*searchText.charAt(ali.c-1) + ali.searchTextString*/ null, ali.prob, ali.matchStart, ali.matchEnd, ali.r - 1, ali.c - 1) ); } /* // free insertions at the beginning of the searchTerm if (ali.c >= 1 && type == Type.SemiGlobal && ali.r == 0 && matrix[ali.r][ali.c-1] == 0) { System.out.format("=> insertion at beginning\n"); alignmentsInProgress.push(new Alignment( "-" + ali.searchTermString, searchText.charAt(ali.c-1) + ali.searchTextString, ali.prob, ali.matchStart + 1, ali.matchEnd, ali.r, ali.c - 1) ); } */ if (type == Type.SemiGlobal && ali.r == 0) { // System.out.format("=> insertions at beginning\n"); int c = ali.c, matchStart = ali.matchStart; StringBuilder searchTermPrefix = new StringBuilder(); StringBuilder searchTextPrefix = new StringBuilder(); while (c > 0) { //searchTermPrefix.append('-'); //searchTextPrefix.insert(0, searchText.charAt(c-1)); matchStart += 1; c--; } alignmentsInProgress.push(new Alignment( /*searchTermPrefix.toString() + ali.searchTermString*/ null, /*searchTextPrefix.toString() + ali.searchTextString*/ null, ali.prob, matchStart, ali.matchEnd, 0, 0) ); } // insertion if (ali.c >= 1 && matrix[ali.r][ali.c] == matrix[ali.r][ali.c-1] + costIndel) { // System.out.format("=> insertion\n"); alignmentsInProgress.push(new Alignment( /*"-" + ali.searchTermString*/ null, /* searchText.charAt(ali.c-1) + ali.searchTextString*/ null, ali.prob, ali.matchStart, ali.matchEnd, ali.r, ali.c - 1) ); } // deletion if (ali.r >= 1 && matrix[ali.r][ali.c] == matrix[ali.r-1][ali.c] + costIndel) { // System.out.format("=> deletion\n"); alignmentsInProgress.push(new Alignment( /*searchTerm.charAt(ali.r-1) + ali.searchTermString*/ null, /*"-" + ali.searchTextString*/ null, ali.prob, ali.matchStart, ali.matchEnd, ali.r - 1, ali.c) ); } // Damerau-Extension (transpositions) if (ali.r >= 2 && ali.c >= 2 && matrix[ali.r][ali.c] == matrix[ali.r-2][ali.c-2] + costTranspos && searchTerm.charAt(ali.r-2) == searchText.charAt(ali.c-1) && searchTerm.charAt(ali.r-1) == searchText.charAt(ali.c-2)) { // System.out.format("=> transposition\n"); alignmentsInProgress.push(new Alignment( /*searchTerm.substring(ali.r - 2, ali.r) + ali.searchTermString*/ null, /*searchText.substring(ali.c - 2, ali.c) + ali.searchTextString*/ null, ali.prob, ali.matchStart, ali.matchEnd, ali.r - 2, ali.c - 2) ); } } } private float getMatchProb(final int distance) { if (type == Type.SemiGlobal) { return 1.0F - ((float)distance / searchTerm.length()); } else { return 1.0F - ((float)distance / Math.min(searchTerm.length(), searchText.length())); } } public float matchProb() { //LogUtils.severe("minMatchProb=" +StringMatchingStrategy.APPROXIMATE_MATCHING_MINPROB); int dist = distance(); matrix = null; //LogUtils.severe(String.format("DLevDist(%s,%s) = %d\n", searchTerm, searchText, dist)); return getMatchProb(dist); } public PseudoDamerauLevenshtein() { //LogUtils.severe("minMatchProb=" +StringMatchingStrategy.APPROXIMATE_MATCHING_MINPROB); } public void init(String searchTerm, String searchText, boolean subStringMatch, boolean caseSensitive) { if (searchTerm == null || searchText == null) { throw new IllegalArgumentException("Null searchText/searchTerm!"); } if (caseSensitive) { this.searchTerm = searchTerm; this.searchText = searchText; } else { this.searchTerm = searchTerm.toLowerCase(); this.searchText= searchText.toLowerCase(); } this.type = subStringMatch ? Type.SemiGlobal : Type.Global; } /* public boolean matches(String searchTerm, String searchText, boolean subStringMatch, boolean caseSensitive) { init(searchTerm, searchText, subStringMatch, caseSensitive); return matchProb() > StringMatchingStrategy.APPROXIMATE_MATCHING_MINPROB; } public Match[] getMatches(String searchTerm, String searchText, boolean subStringMatch, boolean caseSensitive, double minProb) { init(searchTerm, searchText, subStringMatch, caseSensitive); List matches = new ArrayList(); for (Alignment ali: computeAlignments(minProb)) { matches.add(new Match(ali.matchStart, ali.matchEnd)); } return matches.toArray(new Match[matches.size()]); } */ } simplyhtml-0.17.3/src/de/calcom/cclib/text/FindReplaceDialog.java0100644 0000000 0000000 00000117252 13147564753 023376 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2003 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ package de.calcom.cclib.text; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.Vector; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; import javax.swing.MutableComboBoxModel; import javax.swing.border.TitledBorder; import javax.swing.text.Document; import com.lightdev.app.shtm.SHTMLPanel; import com.lightdev.app.shtm.Util; import de.calcom.cclib.text.PseudoDamerauLevenshtein.Alignment; /** * Dialog to manage find and replace on a Document shown * in a JEditorPane. * *

      The component has a 'pluggable' interface about how to deal with the * event that one document has been searched to the end. If it is * constructed with a FindReplaceListener, it shows an additional * checkbox 'Whole project' where the user can select, if a group of * documents shall be searched instead of a single document.

      * *

      If only one document is searched, the dialog searches only in the * document currently shown in the editor.

      * *

      By adding a FindReplaceListener listening for FindReplaceEvents, * the FindReplaceDialog notifies other classes about the fact that a * group of documents shall be searched instead of only the current one.

      * *

      Initially FindReplaceDialog notifies the listener that the first document * in the group shall be loaded into the editor.

      * *

      After loading the first document and resuming the find or replace * operation, the listener gets informed that the end of a document has been * reached. A handling method for that event should cause the editor to * load the next document in the group before it resumes the find or * replace operation.

      * *

      Example for an implementation of FindReplaceListener:

      *

      IMPORTANT: the methods of the FindReplaceListener need to * call either resumeOperation() or terminateOperation() on the * FindReplaceDialog, that fired the FindReplaceEvent. Otherwise * the FindReplaceDialog could 'hang'.

      *

      *

       *   FindReplaceDialog frd = new FindReplaceDialog(aFrame,
       *                             myEditorPane, new MyFindReplaceListener());
       *
       *   protected class MyFindReplaceListener implements FindReplaceListener {
       *     public void getNextDocument(FindReplaceEvent e) {
       *       if(documentsLeft()) { // documentsLeft() is a method coded somewhere else
       *         myEditorPane.setDocument(nextDocument()); // nextDocument() is a method coded somewhere else
       *         ((FindReplaceDialog) e.getSource()).resumeOperation();
       *       }
       *       else {
       *         ((FindReplaceDialog) e.getSource()).terminateOperation();
       *       }
       *     }
       *
       *     public void getFirstDocument(FindReplaceEvent e) {
       *       myEditorPane.setDocument(firstDocument()); // firstDocument() is a method coded somewhere else
       *       ((FindReplaceDialog) e.getSource()).resumeOperation();
       *     }
       *   }
       * 
      *

      * *

      Added i18n support for application SimplyHTML in version 1.5

      * * @author Ulrich Hilger * @author CalCom * @author http://www.calcom.de * @author info@calcom.de * * @version 1.5, April 27, 2003 * * @see javax.swing.text.Document * @see javax.swing.JEditorPane */ public class FindReplaceDialog extends JDialog { /* ---- Constructor(s) start -----------*/ /** * Construct a FindReplaceDialog. * *

      Does not show the dialog window, as fields 'editor' and 'doc' * have to be set separately before the dialog is operable.

      * * @see javax.swing.JEditorPane * @see javax.swing.text.Document * @see java.awt.Frame */ public FindReplaceDialog() { try { jbInit(); initDialogContents(); rememberSearchTermFromSelection(); pack(); } catch (final Exception e) { e.printStackTrace(); } } /** * Construct a FindReplaceDialog. * *

      Shows the dialog window modal, packed and centered over the owning * Frame after construction.

      * *

      Using this constructor implies the dialog shall be used in mode * MODE_DOCUMENT

      * * @param owner the Frame that owns this dialog * @param editor JEditorPane displaying the * Document to seach in * * @see javax.swing.JEditorPane * @see javax.swing.text.Document * @see java.awt.Frame */ public FindReplaceDialog(final Frame owner, final JEditorPane editor) { setEditor(editor); setMode(MODE_DOCUMENT); try { jbInit(); initDialogContents(); centerDialog(owner); rememberSearchTermFromSelection(); pack(); setVisible(true); } catch (final Exception e) { e.printStackTrace(); } } /** * Construct a FindReplaceDialog. * *

      Shows the dialog window modal, packed and centered over the owning * Frame after construction.

      * *

      Using this constructor implies the dialog shall be used in mode * MODE_PROJECT

      * * @param owner the Frame that owns this dialog * @param editor JEditorPane displaying the * Document to seach in * @param listener listener for handling FindReplaceEvents * * @see javax.swing.JEditorPane * @see javax.swing.text.Document * @see java.awt.Frame */ public FindReplaceDialog(final Frame owner, final JEditorPane editor, final FindReplaceListener listener) { setEditor(editor); setMode(MODE_PROJECT); addFindReplaceListener(listener); try { jbInit(); initDialogContents(); centerDialog(owner); rememberSearchTermFromSelection(); pack(); setVisible(true); } catch (final Exception e) { e.printStackTrace(); } } /* --------- Constructor(s) end ------------- */ private void rememberSearchTermFromSelection() { if (editor.getSelectedText() != null) { rememberSearchTerm(editor.getSelectedText(), jcomboSearchTerm); } } /* --------- Event handling start ------------- */ /** * add an event listener to this dialog. * * @param listener the event listener to add */ public void addFindReplaceListener(final FindReplaceListener listener) { listeners.addElement(listener); } /** * remove an event listener from this dialog. * * @param listener the event listener to remove */ public void removeFindReplaceListener(final FindReplaceListener listener) { listeners.removeElement(listener); } /** * notify listeners interested in this event that it occurred * * @param node the node, that is affected by the event */ private void fireGetNextDocument() { final Enumeration listenerList = listeners.elements(); while (listenerList.hasMoreElements()) { ((FindReplaceListener) listenerList.nextElement()).getNextDocument(new FindReplaceEvent(this)); } } /** * notify listeners interested in this event that it occurred * * @param node the node, that is affected by the event */ private void fireGetFirstDocument() { final Enumeration listenerList = listeners.elements(); while (listenerList.hasMoreElements()) { ((FindReplaceListener) listenerList.nextElement()).getFirstDocument(new FindReplaceEvent(this)); } } /** * notify listeners interested in this event that it occurred * * @param node the node, that is affected by the event */ private void fireFindReplaceTerminated() { final Enumeration listenerList = listeners.elements(); while (listenerList.hasMoreElements()) { ((FindReplaceListener) listenerList.nextElement()).findReplaceTerminated(new FindReplaceEvent(this)); } } /** * Resume the current operation after a getFirstDocument or getNextDocument * event was fired */ public void resumeOperation() { doc = editor.getDocument(); findInProgress = false; initFind(); switch (operation) { case OP_FIND: find(); break; case OP_REPLACE: replace(); break; } } /** * Terminate the current operation */ public void terminateOperation() { switch (operation) { case OP_FIND: message(Util.getResourceString(SHTMLPanel.getResources(), "noMoreOccurrencesFound")); toggleState(STATE_UNLOCKED); jbtnReplace.setEnabled(true); break; case OP_REPLACE: switch (replaceChoice) { case RO_YES: case RO_NO: message(Util.getResourceString(SHTMLPanel.getResources(), "noMoreOccurrencesFound")); break; case RO_ALL: message(Util.getResourceString(SHTMLPanel.getResources(), "allOccurrencesReplaced")); break; } toggleState(STATE_UNLOCKED); setVisible(true); break; } operation = OP_NONE; fireFindReplaceTerminated(); } /** * perform a find, when button 'find next' is pressed */ private void jbtnFindNext_actionPerformed(final ActionEvent e) { operation = OP_FIND; jbtnReplace.setEnabled(false); if (mode == MODE_PROJECT && jcbProject.isSelected() && listeners.size() > 0 && !findInProgress) { fireGetFirstDocument(); } else { initFind(); find(); } } /** * perform a replace, when button 'replace' is pressed */ private void jbtnReplace_actionPerformed(final ActionEvent e) { operation = OP_REPLACE; replaceChoice = RO_YES; setVisible(false); if (mode == MODE_PROJECT && jcbProject.isSelected() && listeners.size() > 0 && !findInProgress) { fireGetFirstDocument(); } else { initFind(); replace(); } } /** * Cancels the current find operation and switches the dialog back to * normal. */ private void jbtnCancel_actionPerformed(final ActionEvent e) { toggleState(STATE_UNLOCKED); jbtnReplace.setEnabled(true); } /** * When Close is pressed, store the pressed button in the result * of this dialog and dispose the dialog. */ private void jbtnClose_actionPerformed(final ActionEvent e) { result = JOptionPane.OK_OPTION; dispose(); } /* --------- Event handling end ------------- */ /* --------- Getters and setters start ------------- */ /** * Set the JEditorPane holding the document to be searched * * @param editor the JEditorPane holding the document to be searched * * @see javax.swing.JEditorPane */ public void setEditor(final JEditorPane editor) { this.editor = editor; doc = editor.getDocument(); } /** * Set the mode. * *

      Switches between

      *
        *
      • MODE_DOCUMENT: only the document currently viewed in the editor can be * searched
      • *
      • MODE_PROJECT: An additional check box allows to choose, whether or not * the user likes to search a whole group of documents.
      • *
      * * @param mode one of MODE_DOCUMENT and MODE_PROJECT */ public void setMode(final int mode) { this.mode = mode; if (mode == MODE_PROJECT) { jcbProject.setVisible(true); } else { jcbProject.setVisible(false); } } public void setSearchingBusyCursor() { //RootPaneContainer root = (RootPaneContainer)getTopLevelAncestor(); getRootPane().getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); getRootPane().getGlassPane().setVisible(true); } public void setSearchingDefaultCursor() { //RootPaneContainer root = (RootPaneContainer)getTopLevelAncestor(); getRootPane().getGlassPane().setCursor(Cursor.getDefaultCursor()); getRootPane().getGlassPane().setVisible(false); } /* --------- Getters and setters end ------------- */ /* --------- Find implementation start ------ */ /** * Initialize the find operation by reading all relevant settings and * locking the dialog window. */ private void initFind() { //System.out.format("initFind(findInProgress=%s)\n", findInProgress); if (!findInProgress) { try { searchText = doc.getText(0, doc.getLength()); } catch (final Exception e) { e.printStackTrace(); } searchTerm = (String)jcomboSearchTerm.getEditor().getItem(); //System.out.format("initFind(): searchTerm='%s'\n", searchTerm); rememberSearchTerm(searchTerm, jcomboSearchTerm); matchCaseSetting.getAndSet(jcbMatchCase.isSelected()); matchApproxSetting.getAndSet(jcbMatchApprox.isSelected()); replacementText = jtfReplace.getText(); replaceDiff = replacementText.length() - searchTerm.length(); offset = 0; if (!jcbMatchCase.isSelected()) { searchTerm = searchTerm.toLowerCase(); searchText = searchText.toLowerCase(); } if (jcbMatchApprox.isSelected()) { initApproximateSearch(); } else { initExactSearch(); } toggleState(STATE_LOCKED); } } private void initExactSearch() { if (jcbStartOnTop.isSelected()) { if (jrbUp.isSelected()) { lastPosition = doc.getLength(); } else { lastPosition = -1; } } else { lastPosition = editor.getSelectionStart(); } } private void initApproximateSearch() { PseudoDamerauLevenshtein PDL = new PseudoDamerauLevenshtein(); PDL.init(searchTerm, searchText, true, jcbMatchCase.isSelected()); // get the approximate search threshold parameter (0.65 by default) // (see http://freeplane.sourceforge.net/wiki/index.php/Approximate_search) double threshold = Double.parseDouble(Util.getPreference("approximate_search_threshold", null)); //System.out.format("simplyhtml: approximate_search_threshold=%.2f\n", threshold); try { setSearchingBusyCursor(); currentApproximateMatches = PDL.computeAlignments(threshold); } finally { setSearchingDefaultCursor(); } if (jcbStartOnTop.isSelected()) { if (jrbUp.isSelected()) // search bottom-up { currentApproximateMatchIndex = currentApproximateMatches.size(); } else // search top-down { currentApproximateMatchIndex = -1; } } else { int start = editor.getSelectionStart(); // get first match after 'start' int i = 0; currentApproximateMatchIndex = 0; for (Alignment ali: currentApproximateMatches) { if (ali.getMatchStart() >= start) { currentApproximateMatchIndex = i - 1; break; } i++; } } } /** * Initiate a find or find next operation, whatever applies. If no (more) * hits are found, a message is displayed and the dialog is unlocked for a * new search operation. */ private void find() { if (!doFind()) { if (mode == MODE_PROJECT && jcbProject.isSelected() && listeners.size() > 0) { fireGetNextDocument(); } else { terminateOperation(); } } } /** * Look for the next occurrence of the search phrase either as whole word * or as part of a word, depending on the current dialog setting. * *

      If the phrase is found (again), its position is 'remembered' for a * possible findNext and its postion is highlighted in the underlying * JEditorPane. * * @return true, if the phrase was found (again), false if not * * @see javax.swing.JEditorPane */ private boolean doFind() { boolean found = false; int start; if (jcbMatchApprox.isSelected()) start = findNextApproximately(); else { start = findNext(); if (jcbWholeWords.isSelected()) { start = findWholeWords(start); } } if (start >= 0) { // we found a match! lastPosition = start; if (jrbDown.isSelected()) { start += offset; } editor.setCaretPosition(start); editor.moveCaretPosition(start + getMatchLength()); editor.getCaret().setSelectionVisible(true); found = true; } // System.out.format("doFind() => start=%d/found=%s\n", // start, found); return found; } private int getMatchLength() { if (jcbMatchApprox.isSelected()) return currentApproximateMatches.get(currentApproximateMatchIndex).getMatch().length(); else return searchTerm.length(); } private int findNextApproximately() { int nextIndex = currentApproximateMatchIndex + (jrbUp.isSelected() ? -1 : +1); if (nextIndex < 0 || nextIndex >= currentApproximateMatches.size()) { return -1; } else { currentApproximateMatchIndex = nextIndex; return currentApproximateMatches.get(nextIndex).getMatchStart(); } } /** * Finds the next occurrence of the search term, starting from the last position, * either in the direction up or down, returning the position of the found occurrence. * * @return the start position of the next occurrence or * 0 if no more hits were found */ private int findNext() { // avoid endless loop due to "" always being found if (searchTerm.length() == 0) return -1; int start = -1; // -1 means not found. if (jrbUp.isSelected()) { if (lastPosition < doc.getLength()) { start = searchText.lastIndexOf(searchTerm, lastPosition - 1); } else { start = searchText.lastIndexOf(searchTerm, lastPosition); } } else { if (lastPosition >= 0) { start = searchText.indexOf(searchTerm, lastPosition + searchTerm.length()); } else { start = searchText.indexOf(searchTerm, lastPosition); } } return start; } /** * Find the next whole word occurrence of the searched phrase from * a given position. * * @param start the position to start the search at * * @return the start position of the next occurrence or * 0 if no more hits were found */ private int findWholeWords(int start) { while ((start > 0) && ((!isSeparator(searchText.charAt(start - 1))) || (!isSeparator(searchText.charAt(start + searchTerm.length()))))) { lastPosition = start; start = findNext(); } return start; } /* ----------- Find implementation end ------- */ /* ----------- Replace implementation start ------- */ /** * Initiate a replace operation. If no (more) * hits are found, a message is displayed and the dialog is unlocked for a * new search operation. */ private void replace() { while (replaceChoice != RO_DONE && doFind()) { if (replaceChoice != RO_ALL) { replaceChoice = getReplaceChoice(); } switch (replaceChoice) { case RO_YES: replaceOne(); break; case RO_ALL: replaceOne(); while (doFind()) { replaceOne(); } break; } } if (mode == MODE_PROJECT && jcbProject.isSelected() && listeners.size() > 0) { switch (replaceChoice) { case RO_YES: case RO_NO: case RO_ALL: fireGetNextDocument(); break; case RO_DONE: terminateOperation(); break; } } else { terminateOperation(); } } /** * Show an option window asking the user for a decision about what to * do with the found occurrence during a replace operation. * * @return the chosen option, one of RO_YES, RO_NO, RO_DONE and RO_ALL */ private int getReplaceChoice() { final String msg = Util.getResourceString(SHTMLPanel.getResources(), "replaceThisQuery") + " '" + searchTerm + "'?"; return JOptionPane.showOptionDialog(this, msg, Util.getResourceString(SHTMLPanel.getResources(), "findReplaceDialogTitle"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, replaceOptions, null); } /** * Replace the currently selected occurrence of the search phrase */ private void replaceOne() { editor.replaceSelection(replacementText); offset += replaceDiff; } /* ----------- Replace implementation end ------- */ /* ----------- Helper methods start ------- */ /** * Set dialog components to their inital state */ private void initDialogContents() { jbtnCancel.setEnabled(false); jrbUp.setSelected(false); jrbDown.setSelected(true); jcbWholeWords.setSelected(false); jcbMatchCase.setSelected(matchCaseSetting.get()); jcbMatchApprox.setSelected(matchApproxSetting.get()); jcbStartOnTop.setSelected(true); jcbProject.setSelected(false); MutableComboBoxModel searchTermComboModel = (MutableComboBoxModel)jcomboSearchTerm.getModel(); while (searchTermComboModel.getSize() > 0) { searchTermComboModel.removeElementAt(0); } for (final String searchTerm: searchTermHistory) { searchTermComboModel.addElement(searchTerm); } jcomboSearchTerm.setEditable(true); jtfReplace.setText(""); } /** * Center this dialog window relative to its owning Frame. * * @param owner Frame owning this dialog * * @see java.awt.Frame */ public void centerDialog(final Frame owner) { final Dimension dlgSize = getPreferredSize(); final Dimension frmSize = owner.getSize(); final Point loc = owner.getLocation(); setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); } /** * Toggle the state of the dialog window. * *

      The state of the dialog is either unlocked (no find in progress) or * locked (find in progress).

      * * @param unlocked one of FindReplaceDialog.STATE_LOCKED and * FindReplaceDialog.STATE_UNLOCKED */ private void toggleState(final boolean unlocked) { jbtnCancel.setEnabled(!unlocked); jbtnClose.setEnabled(unlocked); jcomboSearchTerm.setEnabled(unlocked); jtfReplace.setEnabled(unlocked); jLabel3.setEnabled(unlocked); jLabel4.setEnabled(unlocked); jcbWholeWords.setEnabled(unlocked); jcbMatchCase.setEnabled(unlocked); jcbMatchApprox.setEnabled(unlocked); jcbStartOnTop.setEnabled(unlocked); jrbUp.setEnabled(unlocked); jrbDown.setEnabled(unlocked); jcbProject.setEnabled(unlocked); findInProgress = !unlocked; } /** * method for determining whether or not a character is a * word separator. */ private boolean isSeparator(final char ch) { int i = 0; while ((i < WORD_SEPARATORS.length) && (ch != WORD_SEPARATORS[i])) { i++; } return (i < WORD_SEPARATORS.length); } /** * Show an information message */ private void message(final String msgText) { JOptionPane.showMessageDialog(this, msgText, Util.getResourceString(SHTMLPanel.getResources(), "findReplaceDialogTitle"), JOptionPane.INFORMATION_MESSAGE); } /* ----------- Helper methods end ------- */ /** GUI builder init */ private void jbInit() throws Exception { final KeyListener escapeKeyListender = new KeyAdapter() { public void keyPressed(final KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { e.consume(); jbtnClose_actionPerformed(null); } } }; titledBorder1 = new TitledBorder(BorderFactory.createEtchedBorder(Color.white, new Color(142, 142, 142)), "Options"); final ButtonGroup bgSearchDirection = new ButtonGroup(); jbtnFindNext.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(final ActionEvent e) { jbtnFindNext_actionPerformed(e); } }); jbtnFindNext.setText(Util.getResourceString(SHTMLPanel.getResources(), "findNext")); jbtnFindNext.setPreferredSize(new Dimension(100, 27)); jbtnFindNext.setMinimumSize(new Dimension(100, 27)); jbtnFindNext.setMaximumSize(new Dimension(100, 27)); jbtnFindNext.addKeyListener(escapeKeyListender); jcbStartOnTop.setText(Util.getResourceString(SHTMLPanel.getResources(), "searchFromStart")); jcbStartOnTop.setToolTipText(Util.getResourceString(SHTMLPanel.getResources(), "searchFromStart.tooltip")); jrbDown.setText(Util.getResourceString(SHTMLPanel.getResources(), "searchDown")); jcbWholeWords.setText(Util.getResourceString(SHTMLPanel.getResources(), "wholeWordsOnly")); jcbWholeWords.setToolTipText(Util.getResourceString(SHTMLPanel.getResources(), "wholeWordsOnly.tooltip")); jrbDown.setToolTipText(Util.getResourceString(SHTMLPanel.getResources(), "searchDown.tooltip")); jrbUp.setToolTipText(Util.getResourceString(SHTMLPanel.getResources(), "searchUp.tooltip")); jpnlBtn.setLayout(gridBagLayout4); jpnlOptions.setBorder(titledBorder1); jpnlOptions.setLayout(gridLayout2); jpnlFind.setLayout(gridBagLayout5); jtfReplace.setMinimumSize(new Dimension(4, 12)); jtfReplace.setPreferredSize(new Dimension(59, 12)); jtfReplace.setText("jtfReplace"); jtfReplace.addKeyListener(new KeyAdapter() { public void keyPressed(final KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { e.consume(); jbtnClose_actionPerformed(null); } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { e.consume(); jbtnReplace_actionPerformed(null); jbtnReplace.requestFocus(); } } }); jpnlMain.setLayout(gridBagLayout6); jrbUp.setText(Util.getResourceString(SHTMLPanel.getResources(), "searchUp")); jcomboSearchTerm.setMinimumSize(new Dimension(4, 12)); jcomboSearchTerm.setPreferredSize(new Dimension(63, 12)); //jcomboSearchTerm.setText("jtfPhrase"); jcomboSearchTerm.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() { public void keyPressed(final KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { e.consume(); jbtnClose_actionPerformed(null); } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { e.consume(); jbtnFindNext_actionPerformed(null); jbtnFindNext.requestFocus(); } } }); jcbMatchCase.setText(Util.getResourceString(SHTMLPanel.getResources(), "matchCase")); jcbMatchCase.setToolTipText(Util.getResourceString(SHTMLPanel.getResources(), "matchCase.tooltip")); jcbMatchApprox.setText(Util.getResourceString(SHTMLPanel.getResources(), "matchApproximately")); jcbMatchApprox.setToolTipText(Util.getResourceString(SHTMLPanel.getResources(), "matchApproximately.tooltip")); jLabel3.setText(Util.getResourceString(SHTMLPanel.getResources(), "replaceWith")); jLabel4.setText(Util.getResourceString(SHTMLPanel.getResources(), "textToFind")); jbtnClose.setMaximumSize(new Dimension(100, 27)); jbtnClose.setMinimumSize(new Dimension(100, 27)); jbtnClose.setPreferredSize(new Dimension(100, 27)); jbtnClose.setText(Util.getResourceString(SHTMLPanel.getResources(), "closeBtnName")); jbtnClose.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(final ActionEvent e) { jbtnClose_actionPerformed(e); } }); gridLayout2.setRows(4); gridLayout2.setColumns(2); this.setModal(true); this.setTitle(Util.getResourceString(SHTMLPanel.getResources(), "findReplaceDialogTitle")); jbtnReplace.setMaximumSize(new Dimension(100, 27)); jbtnReplace.setMinimumSize(new Dimension(100, 27)); jbtnReplace.setPreferredSize(new Dimension(100, 27)); jbtnReplace.setText(Util.getResourceString(SHTMLPanel.getResources(), "replace")); jbtnReplace.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(final ActionEvent e) { jbtnReplace_actionPerformed(e); } }); jbtnReplace.addKeyListener(escapeKeyListender); jbtnCancel.setMaximumSize(new Dimension(100, 27)); jbtnCancel.setMinimumSize(new Dimension(100, 27)); jbtnCancel.setPreferredSize(new Dimension(100, 27)); jbtnCancel.setText(Util.getResourceString(SHTMLPanel.getResources(), "cancelBtnName")); jbtnCancel.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(final ActionEvent e) { jbtnCancel_actionPerformed(e); } }); jcbProject.setText(Util.getResourceString(SHTMLPanel.getResources(), "searchWholeProject")); this.getContentPane().add(jpnlMain, BorderLayout.NORTH); jpnlBtn.add(jbtnFindNext, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(4, 4, 0, 4), 0, 0)); jpnlBtn.add(jbtnClose, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(0, 4, 4, 4), 0, 0)); jpnlBtn.add(jbtnReplace, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0)); jpnlBtn.add(jbtnCancel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0)); jpnlMain.add(jpnlFind, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(4, 4, 4, 4), 0, 0)); jpnlMain.add(jpnlBtn, new GridBagConstraints(1, 0, 1, 2, 1.0, 1.0, GridBagConstraints.NORTHEAST, GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0)); jpnlFind.add(jcomboSearchTerm, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(4, 4, 4, 4), 0, 12)); jpnlFind.add(jLabel4, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0)); jpnlFind.add(jtfReplace, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(4, 4, 4, 4), 0, 12)); jpnlFind.add(jLabel3, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0)); jpnlMain.add(jpnlOptions, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0)); jpnlOptions.add(jcbMatchCase, null); jpnlOptions.add(jcbMatchApprox, null); jpnlOptions.add(jcbWholeWords, null); jpnlOptions.add(jrbUp, null); jpnlOptions.add(jcbStartOnTop, null); jpnlOptions.add(jrbDown, null); jpnlOptions.add(jcbProject, null); bgSearchDirection.add(jrbUp); bgSearchDirection.add(jrbDown); // this is necessary so that the button fires on enter key press // (for continuing a search)ok, getRootPane().setDefaultButton(jbtnFindNext); } /* ----------------- class fields ------------------------------ */ /** result value for this dialog */ private int result; /** mode of dialog */ private int mode; /** JEditorPane containing the document to search in */ private JEditorPane editor; /** Document to search in */ private Document doc; /** search text from the document */ private String searchText; /** search phrase to find */ private String searchTerm; /** new phrase to replace the searched phrase with */ /* search results for approximate matching */ private List currentApproximateMatches; private int currentApproximateMatchIndex; private String replacementText; /** last start position, the search phrase was found at in the document */ private int lastPosition; /** two fields to correct position differences during replace operations */ private int offset; private int replaceDiff; /** indicates if a find is already in progress */ private boolean findInProgress = false; /** indicates the current operation */ private int operation; /** choice of replace operation */ private int replaceChoice; /** the listeners for FindReplaceEvents */ private final Vector listeners = new Vector(0); /** separators for whole words only search */ private static final char[] WORD_SEPARATORS = { ' ', '\t', '\n', '\r', '\f', '.', ',', ':', '-', '(', ')', '[', ']', '{', '}', '<', '>', '/', '|', '\\', '\'', '\"' }; /** options for replacing */ private static final Object[] replaceOptions = { Util.getResourceString(SHTMLPanel.getResources(), "replaceYes"), Util.getResourceString(SHTMLPanel.getResources(), "replaceNo"), Util.getResourceString(SHTMLPanel.getResources(), "replaceAll"), Util.getResourceString(SHTMLPanel.getResources(), "replaceDone") }; /* Constants for method toggleState */ public static final boolean STATE_LOCKED = false; public static final boolean STATE_UNLOCKED = true; /* Constants for replaceOptions */ public static final int RO_YES = 0; public static final int RO_NO = 1; public static final int RO_ALL = 2; public static final int RO_DONE = 3; /* Constants for dialog mode */ public static final int MODE_DOCUMENT = 1; public static final int MODE_PROJECT = 2; /* Constants for operation */ public static final int OP_NONE = 0; public static final int OP_FIND = 1; public static final int OP_REPLACE = 2; /* ---- GUI elements start ---------*/ private TitledBorder titledBorder1; private final JButton jbtnFindNext = new JButton(); private final JCheckBox jcbStartOnTop = new JCheckBox(); private final JRadioButton jrbDown = new JRadioButton(); private final JCheckBox jcbWholeWords = new JCheckBox(); private final JPanel jpnlBtn = new JPanel(); private final JPanel jpnlOptions = new JPanel(); private final JPanel jpnlFind = new JPanel(); private final JTextField jtfReplace = new JTextField(); private final JPanel jpnlMain = new JPanel(); private final JRadioButton jrbUp = new JRadioButton(); private final JComboBox jcomboSearchTerm = new JComboBox(); private final JCheckBox jcbMatchCase = new JCheckBox(); private final JCheckBox jcbMatchApprox = new JCheckBox(); private final JLabel jLabel3 = new JLabel(); private final JLabel jLabel4 = new JLabel(); private final GridBagLayout gridBagLayout4 = new GridBagLayout(); private final GridBagLayout gridBagLayout5 = new GridBagLayout(); private final JButton jbtnClose = new JButton(); private final GridBagLayout gridBagLayout6 = new GridBagLayout(); private final GridLayout gridLayout2 = new GridLayout(); private final JButton jbtnReplace = new JButton(); private final JButton jbtnCancel = new JButton(); private final JCheckBox jcbProject = new JCheckBox(); /* ---- GUI elements end ---------*/ public static synchronized void rememberSearchTerm(final String searchTerm, final JComboBox searchTermCombo) { //System.out.format("rememberSearchTerm(%s)\n", searchTerm); if (searchTerm.equals("")) return; MutableComboBoxModel searchTermComboModel = (MutableComboBoxModel)searchTermCombo.getModel(); // remove this term from the history if (searchTermHistory.contains(searchTerm)) { searchTermHistory.remove(searchTerm); searchTermComboModel.removeElement(searchTerm); } // (re)insert at top of list searchTermHistory.add(0, searchTerm); searchTermComboModel.insertElementAt(searchTerm, 0); searchTermCombo.setSelectedItem(searchTerm); } private final static List searchTermHistory = new LinkedList(); private final static AtomicBoolean matchCaseSetting = new AtomicBoolean(false); private final static AtomicBoolean matchApproxSetting = new AtomicBoolean(false); } simplyhtml-0.17.3/src/de/calcom/cclib/text/FindReplaceListener.java0100644 0000000 0000000 00000005073 12114157751 023746 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2003 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ package de.calcom.cclib.text; import java.util.EventListener; /** * Interface for implementing a listener for FindReplaceEvents. * *

      For implementing this interface, methods getFirstDocument and * getNextDocument need to be overridden by code providing a new document * for the a FindReplaceDialog. Once a new document is on hand, * methods resumeOperation or terminateOperation of the FindReplaceDialog * need to be called from out of this interfaces methods to resume or * terminate the find or replace operation, * which is waiting for the new document in the FindReplaceDialog.

      * *

      Added i18n support for application SimplyHTML in version 1.5

      * * @author Ulrich Hilger * @author CalCom * @author http://www.calcom.de * @author info@calcom.de * * @version 1.5, April 27, 2003 */ public interface FindReplaceListener extends EventListener { /** * this events gets fired, when a FindReplaceDialog has reached * the end of the current document and requires the next document * of a group of documents. * * @param e the object having details for the event */ public void getNextDocument(FindReplaceEvent e); /** * this events gets fired, when a FindReplaceDialog has initiated * an operation for a group of documents which requires to start at * the first document. * * @param e the object having details for the event */ public void getFirstDocument(FindReplaceEvent e); /** * this event gets fired when a FindReplaceDialog has finalized its * task. * * @param e the object having details for the event */ public void findReplaceTerminated(FindReplaceEvent e); } simplyhtml-0.17.3/src/de/calcom/cclib/text/FindReplaceEvent.java0100644 0000000 0000000 00000002630 12114157751 023236 0ustar000000000 0000000 /* * SimplyHTML, a word processor based on Java, HTML and CSS * Copyright (C) 2003 Ulrich Hilger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ package de.calcom.cclib.text; import java.util.EventObject; /** * This is simply overriding EventObject by storing a FindReplaceDialog * into the source field of the superclass. * *

      Added i18n support for application SimplyHTML in version 1.5

      * * @author Ulrich Hilger * @author CalCom * @author http://www.calcom.de * @author info@calcom.de * @version 1.5, April 27, 2003 */ public class FindReplaceEvent extends EventObject { public FindReplaceEvent(final FindReplaceDialog source) { super(source); } } simplyhtml-0.17.3/src/de/calcom/cclib/text/package.html0100644 0000000 0000000 00000000602 12114157751 021473 0ustar000000000 0000000 Work on text in various manners.

      The classes in this package extend the Swing EditorPane by allowing drag and drop and cut and paste on styled text.

      @author Ulrich Hilger, Dimitry Polivaev @author http://simplyhtml.sourceforge.net/ @version 0.12.2, 2007 simplyhtml-0.17.3/src/MANIFEST.MF0100644 0000000 0000000 00000000242 12114157751 014756 0ustar000000000 0000000 Manifest-Version: 1.0 Main-Class: com.lightdev.app.shtm.App Class-Path: SimplyHTML.jar jhall.jar SimplyHTMLHelp.jar gnu-regexp.jar Created-By: Dimitri Polivaev simplyhtml-0.17.3/build.gradle0100644 0000000 0000000 00000004703 13147564753 015035 0ustar000000000 0000000 plugins { id "com.jfrog.bintray" version "1.6" } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'maven-publish' group 'com.lightdev.app.shtm.simplyhtml' version = '0.17.3' targetCompatibility='1.7' sourceCompatibility='1.7' repositories { jcenter() maven { url "http://dl.bintray.com/freeplane/freeplane" } } bintray { user = System.getenv('BINTRAY_USER') key = System.getenv('BINTRAY_KEY') publications = ['simplyhtml'] pkg { repo = 'freeplane' name = 'simplyhtml' userOrg = 'freeplane' licenses = ['GPL-3.0', 'GPL-2.0'] vcsUrl = 'https://github.com/dpolivaev/simplyhtml.git' version { name = project.version } } } dependencies { compile 'javax.help:javahelp:2.0.05', 'org.dpolivaev.mnemonicsetter:mnemonicsetter:0.4' } sourceSets { main { java { srcDir 'src' } } main { resources { srcDir 'src' include 'com/lightdev/app/shtm/resources/**' include 'com/lightdev/app/shtm/help/**' } } } // holds classes included into SimplyHTMLHelp.jar so they are to be excluded from SimplyHTML.jar ext.helpExcludes = [] // help.jar task helpJar(type: Jar) { baseName = 'SimplyHTMLHelp' from(sourceSets.main.output) { include "com/lightdev/app/shtm/help/**" project.ext.helpExcludes.addAll(includes) } } // lib.jar task mainJar(type: Jar, dependsOn: classes) { baseName = 'SimplyHTML' from(sourceSets.main.output) { exclude project.ext.helpExcludes } manifest { def classPath = (configurations.compile.collect { it.name } + helpJar.outputs.files.collect { it.name }).join(' ') attributes( 'Implementation-Title': project.name, 'Implementation-Version': project.version, 'Main-Class': 'com.lightdev.app.shtm.App', 'Class-Path': classPath ) } } task sourceJar(type: Jar) { baseName = 'SimplyHTML' classifier = 'sources' from sourceSets.main.allSource } build.dependsOn mainJar build.dependsOn javadoc publishing { publications { simplyhtml(MavenPublication) { from components.java artifactId 'SimplyHTML' artifacts = [mainJar] artifact sourceJar { classifier "sources" } } } } javadoc { enabled = true options.encoding = "UTF-8" failOnError = false } apply from: './dist.gradle'