001    /////////////////////////////////////////////////
002    //This file is part of Sears project.
003    //Subtitle Editor And Re-Synch
004    //A tool to easily modify and resynch movies subtitles.
005    /////////////////////////////////////////////////
006    //This program is free software; 
007    //you can redistribute it and/or modify it under the terms 
008    //of the GNU General Public License 
009    //as published by the Free Software Foundation; 
010    //either version 2 of the License, or (at your option) any later version.
011    /////////////////////////////////////////////////
012    //Sears project is available under sourceforge
013    //at adress: http://sourceforge.net/projects/sears/
014    //Copyright (C) 2005 Booba Skaya
015    //Mail: booba.skaya@gmail.com
016    /////////////////////////////////////////////////
017    
018    // some suggestions about this class: floriaen@gmail.com
019    
020    package sears.gui;
021    
022    import java.awt.Color;
023    import java.awt.Composite;
024    import java.awt.Font;
025    import java.awt.Graphics;
026    import java.awt.Graphics2D;
027    import java.awt.Rectangle;
028    import java.awt.Shape;
029    import java.awt.font.FontRenderContext;
030    import java.awt.font.TextLayout;
031    
032    import javax.swing.JLabel;
033    import javax.swing.SwingUtilities;
034    import javax.swing.UIManager;
035    
036    import sears.tools.Utils;
037    
038    /**
039     * <code>JLabel</code> subclasse with highlight possibility.
040     */
041    public class SubtitleCellComponent extends JLabel {
042            private static final long serialVersionUID = 859239543259106083L;
043    
044            /** the default highlight color */
045            public static final Color DEFAULT_HIGHLIGTH_COLOR = Color.LIGHT_GRAY;
046            //UIManager.getColor("Table.selectionBackground");
047            /** default font */
048            public static final Font DEFAULT_FONT = UIManager.getFont("Table.font");
049    
050            private static final String EMPTY_STRING = "";
051            // the substring to highlight
052            private static String highligthString;
053            private boolean highlighting;
054            
055            private int rowHeight;
056            
057            private FontRenderContext frc;
058    
059            /**
060             * Instantiates a new object
061             * @param rowHeight the height of row defines by the table
062             */
063            public SubtitleCellComponent(int rowHeight) {
064                    super();                
065                    setFont(DEFAULT_FONT);
066                    frc = new FontRenderContext(null, true, false);
067                    setHighlightedString(EMPTY_STRING);
068                    this.rowHeight = rowHeight;
069            }
070    
071            /**
072             * Sets the highlighted string
073             * @param str   the string to highlight
074             */
075            public void setHighlightedString(String str) {
076                    if( str == null || str.trim().length() == 0 ) {
077                            str = EMPTY_STRING;
078                            highlighting = false;
079                    } else {
080                            highlighting = true;
081                    }               
082                    highligthString = str.toUpperCase();
083    
084            }
085    
086            /**
087             * Returns the sub string
088             * @return the sub string or null
089             */
090            public static String getSubString() {
091                    return highligthString;
092            }
093    
094            /**
095             * Returns the bounds of substring glyph, begin at <code>index</code>
096             * @param index         the index of the first char of the searched word
097             * @param str           the word to search, the highlighted word
098             * @return                      a <tt>Rectangle</tt> object, the substring bounds
099             */
100            public Rectangle getBoundsBeginAtIndex(int index, String str) {
101                    // ensure coherence, throw exception
102                    Rectangle bounds = null;
103                    String text = formatText(this.getText());
104                    TextLayout textLayout = new TextLayout(text, getFont(), frc);
105                    // IllegalArgumentException is thrown by #ensure(String) method before
106                    // it's made by the TextLayout#getBlackBoxBounds(...) method
107                    text = text.toUpperCase();
108                    
109                    // X position:
110                    Shape blackBoxBounds = textLayout.getBlackBoxBounds(index, index + str.length());
111                    // Y DISTANCE:
112                    double tyDistance = rowHeight/2 + (textLayout.getAscent() - textLayout.getDescent())/2;
113                    // BOUNDS:
114                    bounds = new Rectangle(
115                                    blackBoxBounds.getBounds().width,
116                                    (int) textLayout.getAscent()
117                                    );
118                    bounds.translate((int) blackBoxBounds.getBounds2D().getX(), (int) tyDistance);
119                    return bounds;
120            }
121    
122            /**
123             * Returns the searched sub string (respect lower and upper case states) in the specified location in subtitle text
124             * @param index the first index of the first char of the searched text
125             * @param str   the searched text
126             * @return              the searched text in the subtitle text
127             */
128            public String getSubStringBeginAtIndex(int index, String str) {
129                    String text = formatText(getText());            
130                    // StringIndexOutOfBoundsException is thrown (as IllegalArgumentException)
131                    // by #ensure(String) method before it's thrown by the String#substring(...) method
132                    return text.substring(index, index + str.length());
133            }
134    
135            /*
136             * (non-Javadoc)
137             * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
138             */
139            public void paintComponent(Graphics g) {
140                    super.paintComponent(g);
141                    Graphics2D gr = (Graphics2D) g;
142                    Color defaultColor = gr.getColor();
143                    // PAINT BACKGROUND:
144                    gr.setColor(getBackground());
145                    gr.fillRect(0, 0, getWidth(), getHeight());
146                    String text = formatText(getText());
147                    TextLayout layout = new TextLayout(text, gr.getFont(), gr.getFontRenderContext());              
148                    if( highlighting ) {
149                            // calls the highlight method:
150                            paintHighlightShape(gr, layout, text);
151                    }
152    
153                    // PAINT TEXT:
154                    gr.setColor(getForeground());
155    
156                    // center layout:
157                    float layoutYCentered = getHeight()/2 + (layout.getAscent() - layout.getDescent())/2;
158                    this.setSize(getWidth(), getHeight());
159                    layout.draw(gr, 0, layoutYCentered);
160                    gr.setColor(defaultColor);
161            }
162    
163            /**
164             * Graphics context could be modified by this method.
165             * @param gr            the graphic context
166             * @param layout        the layout which contain text to highlight
167             * @param text          the text to highlight
168             */
169            private void paintHighlightShape(Graphics2D gr, TextLayout layout, String text) {
170                    Composite defaultComposite = gr.getComposite();
171                    // real size of the cell
172                    Rectangle cell = SwingUtilities.getLocalBounds(this);
173                    if(cell != null) {
174                            text = text.toUpperCase();
175                            int start = -1;
176                            while( (start = text.indexOf(highligthString, start+1)) != -1 ) {       
177                                    Shape sh = layout.getLogicalHighlightShape(start, start+highligthString.length(), cell);
178                                    gr.setColor(DEFAULT_HIGHLIGTH_COLOR);
179                                    gr.fill(sh);
180                            }
181                    }
182                    gr.setComposite(defaultComposite);
183            }
184            
185            /**
186             * Format the text given in parameters
187             * @param text  the text to format
188             * @return              the formatted text
189             */
190            private String formatText(String text) {
191                    if( text.equals("")) {
192                            text = " ";
193                    } else {
194                            text = text.replace(Utils.LINE_SEPARATOR, " ");
195                    }
196                    return text;
197            }
198    }