001: /*
002: * Lucane - a collaborative platform
003: * Copyright (C) 2004 Vincent Fiack <vfiack@mail15.com>
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: /*
021: * Based on HotSheet ItemListCellRenderer.java by John Munsh
022: */
023: package org.lucane.applications.rssreader.gui;
024:
025: import java.awt.*;
026: import java.awt.font.*;
027: import java.awt.geom.*;
028: import java.text.*;
029:
030: import javax.swing.border.Border;
031: import javax.swing.ImageIcon;
032: import javax.swing.UIManager;
033:
034: import org.jperdian.rss2.dom.RssItem;
035:
036: class ItemComponent extends Component {
037: protected RssItem item;
038: protected boolean isSelected, hasFocus, isViewed;
039: protected int score;
040:
041: private final int margin = 8;
042: private final int height = 100;
043: private final int maxIconWidth = 140;
044: private final int maxIconHeight = height - (margin * 2);
045:
046: ItemComponent(RssItem item, boolean isSelected, boolean hasFocus,
047: boolean isViewed, int score) {
048:
049: this .item = item;
050: this .isSelected = isSelected;
051: this .hasFocus = hasFocus;
052: this .isViewed = isViewed;
053: score = score > 100 ? 100 : score;
054: this .score = score < 0 ? 0 : score;
055:
056: if (isSelected)
057: setBackground(UIManager
058: .getColor("List.selectionBackground"));
059: else
060: setBackground(UIManager.getColor("List.background"));
061: }
062:
063: ////////////////////////////////////////////////////////////////////////////
064: // Helper Functions
065: ////////////////////////////////////////////////////////////////////////////
066:
067: /**
068: * Calculates any scaling and/or translations that may need to be done to a
069: * given image in order to display it centered within an area of the given
070: * size.
071: */
072: protected AffineTransform createTransforms(Image image,
073: Dimension dimension) {
074: int imageHeight = image.getHeight(null);
075: int imageWidth = image.getWidth(null);
076: AffineTransform returnValue = new AffineTransform();
077:
078: if ((imageHeight > dimension.getHeight())
079: || (imageWidth > dimension.getWidth())) {
080:
081: // Scaling will be necessary.
082: double scaleFactor = Math.min(dimension.getHeight()
083: / imageHeight, dimension.getWidth() / imageWidth);
084:
085: // Concatenate a scaling transform on.
086: AffineTransform temp = new AffineTransform();
087: temp.setToScale(scaleFactor, scaleFactor);
088: returnValue.concatenate(temp);
089:
090: // Recalculate the image height and width based on the scale factor.
091: // We need to do this because we might need to horizontally or
092: // vertically center the image.
093: imageHeight *= scaleFactor;
094: imageWidth *= scaleFactor;
095: }
096:
097: // Note that it is possible to need both scaling and centering so we
098: // always do our scaling first and then after we know our final
099: // dimensions for the image we do our centering.
100: if ((imageHeight < dimension.getHeight())
101: || (imageWidth < dimension.getWidth())) {
102: // Centering will be necessary.
103: AffineTransform temp = new AffineTransform();
104: temp.setToTranslation(
105: (dimension.getWidth() - imageWidth) / 2, (dimension
106: .getHeight() - imageHeight) / 2);
107: returnValue.concatenate(temp);
108: }
109:
110: return returnValue;
111: }
112:
113: public void paint(Graphics g) {
114: float curY;
115: int textMargin = maxIconWidth + (margin * 2);
116:
117: Graphics2D g2 = (Graphics2D) g;
118:
119: g2.setBackground(getBackground());
120: g2.clearRect(0, 0, getWidth(), getHeight());
121: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
122: RenderingHints.VALUE_ANTIALIAS_ON);
123: g2.setRenderingHint(RenderingHints.KEY_RENDERING,
124: RenderingHints.VALUE_RENDER_QUALITY);
125:
126: if (hasFocus)
127: paintFocusBorder(g2);
128:
129: boolean hasIcon = paintChannelIcon(g2);
130: if (!hasIcon)
131: textMargin = margin;
132:
133: curY = paintTitle(g2, textMargin);
134: paintDescription(g2, textMargin, curY);
135: paintScore(g2);
136:
137: g2.setColor(Color.lightGray);
138: g2.fillRect(0, getHeight() - 3, getWidth(), getHeight());
139:
140: // If the item has already been viewed, draw a film over the whole
141: // item to render it much subtler.
142: if (isViewed)
143: paintFog(g2);
144: }
145:
146: private void paintFocusBorder(Graphics2D g2) {
147: Border focusBorder = UIManager
148: .getBorder("List.focusCellHighlightBorder");
149: focusBorder
150: .paintBorder(this , g2, 0, 0, getWidth(), getHeight());
151: }
152:
153: private void paintDescription(Graphics2D g2, int textMargin,
154: float curY) {
155: String description = item.getStrippedDescription();
156: if (description.length() == 0)
157: return;
158:
159: Font smallFont = g2.getFont().deriveFont(Font.PLAIN, 10.0f);
160: float wrappingWidth = getWidth() - textMargin;
161:
162: description = description.replace('\n', ' ');
163: AttributedString str = new AttributedString(description);
164: str.addAttribute(TextAttribute.FONT, smallFont);
165: AttributedCharacterIterator strIterator = str.getIterator();
166: FontRenderContext fontRenderContext = new FontRenderContext(
167: null, false, false);
168: LineBreakMeasurer measurer = new LineBreakMeasurer(strIterator,
169: fontRenderContext);
170:
171: while (measurer.getPosition() < strIterator.getEndIndex()) {
172: TextLayout layout = measurer.nextLayout(wrappingWidth);
173:
174: // Adjust current elevation.
175: curY += Math.floor(layout.getAscent());
176:
177: Point2D.Float penPosition = new Point2D.Float(textMargin,
178: curY);
179: layout.draw(g2, penPosition.x, penPosition.y);
180:
181: // Move to next line.
182: curY += layout.getDescent() + layout.getLeading();
183:
184: // If we've done enough lines to fill the printable area, don't
185: // bother getting more.
186: if (curY >= getHeight() - 3)
187: break;
188: }
189: }
190:
191: private float paintTitle(Graphics2D g2, int textMargin) {
192: float curY = margin;
193:
194: String title = item.getTitle().trim();
195: if (title.length() == 0)
196: return curY;
197:
198: g2.setColor(Color.black);
199: Font currentFont = g2.getFont();
200: Font boldFont = currentFont.deriveFont(Font.BOLD);
201: float wrappingWidth = getWidth() - textMargin;
202:
203: AttributedString str = new AttributedString(title);
204: str.addAttribute(TextAttribute.FONT, boldFont);
205: AttributedCharacterIterator strIterator = str.getIterator();
206: FontRenderContext fontRenderContext = new FontRenderContext(
207: null, false, false);
208: LineBreakMeasurer measurer = new LineBreakMeasurer(strIterator,
209: fontRenderContext);
210:
211: while (measurer.getPosition() < strIterator.getEndIndex()) {
212: TextLayout layout = measurer.nextLayout(wrappingWidth);
213: curY += Math.floor(layout.getAscent());
214: Point2D.Float penPosition = new Point2D.Float(textMargin,
215: curY);
216: layout.draw(g2, penPosition.x, penPosition.y);
217: curY += layout.getDescent() + layout.getLeading();
218: if (curY >= getHeight() - 3)
219: break;
220: }
221:
222: return curY;
223: }
224:
225: private boolean paintChannelIcon(Graphics2D g2) {
226: // Draw the icon for the channel of the item.
227: ImageIcon imageIcon = null;
228: try {
229: imageIcon = new ImageIcon(item.getSource().getImage()
230: .getURL());
231: } catch (Exception e) {
232: }
233:
234: if (imageIcon == null)
235: return false;
236:
237: // Create the scaling transformation needed to ensure that the
238: // graphic associated with this channel will fit in the space
239: // set aside for it.
240: AffineTransform transform = createTransforms(imageIcon
241: .getImage(), new Dimension(maxIconWidth, maxIconHeight));
242:
243: // Now that we have our scaling transformation, create another
244: // transform to perform translation (i.e. shift the position of
245: // the image) and append the two translations to each other.
246: AffineTransform temp = new AffineTransform();
247: temp.setToTranslation(margin, margin);
248: transform.concatenate(temp);
249:
250: // Draw the image, applying the transformations to scale and
251: // translate it appropriately.
252: g2.drawImage(imageIcon.getImage(), transform, null);
253: return true;
254: }
255:
256: private void paintFog(Graphics2D g2) {
257: AlphaComposite ac = AlphaComposite.getInstance(
258: AlphaComposite.SRC_OVER, .5f);
259: g2.setComposite(ac);
260:
261: // The background color is what we use to ghost the item.
262: g2.setColor(getBackground());
263: g2.fillRect(0, 0, getWidth(), getHeight());
264: }
265:
266: private void paintScore(Graphics2D g2) {
267: int mod = 255 - ((score - 50) * 5);
268:
269: if (score > 50)
270: g2.setColor(new Color(255, mod, mod));
271: else if (score < 50)
272: g2.setColor(new Color(mod, mod, 255));
273:
274: g2.fillRect(0, 0, margin / 2, getHeight());
275: }
276:
277: public Dimension getPreferredSize() {
278: return (new Dimension(300, height));
279: }
280: }
|