diff --git a/src/net/i2p/itoopie/Main.java b/src/net/i2p/itoopie/Main.java
index 969e2357d..e08fd5d8b 100644
--- a/src/net/i2p/itoopie/Main.java
+++ b/src/net/i2p/itoopie/Main.java
@@ -265,6 +265,7 @@ public class Main {
}*/
/*
+ // Test shutdown - worked at one point :) Possibly now as well.
System.out.println("\nSetRouterRunner: Shutdown ");
try {
SetRouterRunner.execute(ROUTER_RUNNER.SHUTDOWN);
diff --git a/src/net/i2p/itoopie/gui/OverviewTab.java b/src/net/i2p/itoopie/gui/OverviewTab.java
index 131461616..1b9034499 100644
--- a/src/net/i2p/itoopie/gui/OverviewTab.java
+++ b/src/net/i2p/itoopie/gui/OverviewTab.java
@@ -1,23 +1,39 @@
package net.i2p.itoopie.gui;
import java.awt.Color;
+import java.awt.EventQueue;
import info.monitorenter.gui.chart.Chart2D;
import info.monitorenter.gui.chart.views.ChartPanel;
import javax.swing.BorderFactory;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import net.i2p.itoopie.gui.component.BandwidthChart;
+import net.i2p.itoopie.gui.component.MultiLineLabel;
import net.i2p.itoopie.gui.component.ParticipatingTunnelsChart;
import net.i2p.itoopie.gui.component.TabLogoPanel;
+import net.i2p.itoopie.gui.component.multilinelabel.MultiLineLabelUI;
+import net.i2p.itoopie.i18n.Transl;
public class OverviewTab extends TabLogoPanel {
+ JLabel lblI2P;
+ JLabel lblVersion;
+ JLabel lblVersionSpecified;
+ JLabel lblStatus;
+ JLabel lblStatusSpecified;
+ JLabel lblUptime;
+ JLabel lblUptimeSpecified;
+ JLabel lblNetworkStatus;
+ MultiLineLabel lblNetworkStatusSpecified;
public OverviewTab(String imageName) {
super(imageName);
super.setLayout(null);
-
+
Chart2D bwChart = BandwidthChart.getChart();
Chart2D partTunnelChart = ParticipatingTunnelsChart.getChart();
ChartPanel pt = new ChartPanel(partTunnelChart);
@@ -25,20 +41,94 @@ public class OverviewTab extends TabLogoPanel {
pt.setLocation(15, 10);
pt.setBorder(BorderFactory.createLineBorder(Color.GRAY));
ChartPanel cp = new ChartPanel(bwChart);
- cp.setSize(300,135);
+ cp.setSize(300, 135);
cp.setLocation(15, 155);
cp.setBorder(BorderFactory.createLineBorder(Color.GRAY));
-
+
add(pt);
add(cp);
+
+ lblI2P = new JLabel();
+ add(lblI2P);
+ lblI2P.setBounds(285, 30, 100, 15);
+ lblI2P.setText("I2P");
+ lblI2P.setHorizontalAlignment(SwingConstants.RIGHT);
+
+ lblVersion = new JLabel();
+ add(lblVersion);
+ lblVersion.setBounds(285, 50, 100, 15);
+ lblVersion.setText(Transl._("Version:"));
+ lblVersion.setHorizontalAlignment(SwingConstants.RIGHT);
+
+ lblVersionSpecified = new JLabel();
+ add(lblVersionSpecified);
+ lblVersionSpecified.setBounds(395, 50, 140, 15);
+ lblVersionSpecified.setHorizontalAlignment(SwingConstants.LEFT);
+ lblVersionSpecified.setText("0.8.7-48rc"); // Delete Me
+
+
+ lblUptime = new JLabel();
+ add(lblUptime);
+ lblUptime.setBounds(285, 70, 100, 15);
+ lblUptime.setHorizontalAlignment(SwingConstants.RIGHT);
+ lblUptime.setText(Transl._("Uptime:"));
+
+ lblUptimeSpecified = new JLabel();
+ add(lblUptimeSpecified);
+ lblUptimeSpecified.setBounds(395, 70, 140, 15);
+ lblUptimeSpecified.setHorizontalAlignment(SwingConstants.LEFT);
+ lblUptimeSpecified.setText("93 min"); // Delete Me
+
+
+ lblStatus = new JLabel();
+ add(lblStatus);
+ lblStatus.setBounds(285, 90, 100, 15);
+ lblStatus.setHorizontalAlignment(SwingConstants.RIGHT);
+ lblStatus.setText(Transl._("Status:"));
+
+ lblStatusSpecified = new JLabel();
+ add(lblStatusSpecified);
+ lblStatusSpecified.setBounds(395, 90, 140, 15);
+ lblStatusSpecified.setHorizontalAlignment(SwingConstants.LEFT);
+ lblStatusSpecified.setText("Rejecting Tunnels"); // Delete Me
+
+ lblNetworkStatus = new JLabel();
+ add(lblNetworkStatus);
+ lblNetworkStatus.setBounds(285, 110, 100, 15);
+ lblNetworkStatus.setHorizontalAlignment(SwingConstants.RIGHT);
+ lblNetworkStatus.setText(Transl._("Netstatus:"));
+
+ lblNetworkStatusSpecified = new MultiLineLabel();
+ add(lblNetworkStatusSpecified);
+ lblNetworkStatusSpecified.setBounds(395, 110, 130, 60);
+ lblNetworkStatusSpecified.setHorizontalAlignment(SwingConstants.LEFT);
+ lblNetworkStatusSpecified.setVerticalTextAlignment(JLabel.TOP);
+ lblNetworkStatusSpecified.setText("WARN-Firewalled with Inbound TCP Enabled".replace('-', ' ')); // Delete Me
+
+ validate();
}
-
-
@Override
public void onTabFocus(ChangeEvent e) {
- System.out.println("OverviewTab onTabFocus()");
-
+ // Do thigns when shown?
}
+ /**
+ * Launch the application.
+ */
+ public static void main(String[] args) {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ JFrame frame = new JFrame();
+ frame.setBounds(0, 0, Main.FRAME_WIDTH, Main.FRAME_HEIGHT);
+ OverviewTab window = new OverviewTab("itoopie-opaque12");
+ frame.add(window);
+ frame.setVisible(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
}
diff --git a/src/net/i2p/itoopie/gui/component/MultiLineLabel.java b/src/net/i2p/itoopie/gui/component/MultiLineLabel.java
new file mode 100644
index 000000000..68fc29c8d
--- /dev/null
+++ b/src/net/i2p/itoopie/gui/component/MultiLineLabel.java
@@ -0,0 +1,124 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2009 Samuel Sjoberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package net.i2p.itoopie.gui.component;
+
+import java.awt.Rectangle;
+
+import javax.swing.JLabel;
+
+import net.i2p.itoopie.gui.component.multilinelabel.MultiLineLabelUI;
+
+
+
+/**
+ * A {@link JLabel} with support for multi-line text that wraps when the line
+ * doesn't fit in the available width. Multi-line text support is handled by the
+ * {@link MultiLineLabelUI}, the default UI delegate of this component. The text
+ * in the label can be horizontally and vertically aligned, relative to the
+ * bounds of the component.
+ *
+ * @author Samuel Sjoberg, http://samuelsjoberg.com
+ * @version 1.0.0
+ */
+public class MultiLineLabel extends JLabel {
+
+ /** Default serial version UID. */
+ private static final long serialVersionUID = 1L;
+
+ /** Horizontal text alignment. */
+ private int halign = LEFT;
+
+ /** Vertical text alignment. */
+ private int valign = CENTER;
+
+ /** Cache to save heap allocations. */
+ private Rectangle bounds;
+
+ /**
+ * Creates a new empty label.
+ */
+ public MultiLineLabel() {
+ super();
+ setUI(MultiLineLabelUI.labelUI);
+ }
+
+ /**
+ * Creates a new label with text
value.
+ *
+ * @param text
+ * the value of the label
+ */
+ public MultiLineLabel(String text) {
+ this();
+ setText(text);
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle getBounds() {
+ if (bounds == null) {
+ bounds = new Rectangle();
+ }
+ return super.getBounds(bounds);
+ }
+
+ /**
+ * Set the vertical text alignment.
+ *
+ * @param alignment
+ * vertical alignment
+ */
+ public void setVerticalTextAlignment(int alignment) {
+ firePropertyChange("verticalTextAlignment", valign, alignment);
+ valign = alignment;
+ }
+
+ /**
+ * Set the horizontal text alignment.
+ *
+ * @param alignment
+ * horizontal alignment
+ */
+ public void setHorizontalTextAlignment(int alignment) {
+ firePropertyChange("horizontalTextAlignment", halign, alignment);
+ halign = alignment;
+ }
+
+ /**
+ * Get the vertical text alignment.
+ *
+ * @return vertical text alignment
+ */
+ public int getVerticalTextAlignment() {
+ return valign;
+ }
+
+ /**
+ * Get the horizontal text alignment.
+ *
+ * @return horizontal text alignment
+ */
+ public int getHorizontalTextAlignment() {
+ return halign;
+ }
+}
diff --git a/src/net/i2p/itoopie/gui/component/multilinelabel/Effects.java b/src/net/i2p/itoopie/gui/component/multilinelabel/Effects.java
new file mode 100644
index 000000000..15b2d1f91
--- /dev/null
+++ b/src/net/i2p/itoopie/gui/component/multilinelabel/Effects.java
@@ -0,0 +1,345 @@
+package net.i2p.itoopie.gui.component.multilinelabel;
+
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2009 Samuel Sjoberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.GradientPaint;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+
+import javax.swing.JComponent;
+
+/**
+ * Static graphics and look and feel effects.
+ *
+ * @author Samuel Sjoberg, http://samuelsjoberg.com
+ * @version 1.3.0
+ */
+public final class Effects {
+
+ /** Prevent initialization. */
+ private Effects() {
+ }
+
+ /** Text drop shadow size. */
+ public static final int TEXT_SHADOW_SIZE = 2;
+
+ /**
+ * Set the color to black with given alpha value.
+ *
+ * @param g
+ * the graphics to paint on
+ * @param alpha
+ * the alpha value between 0-1.
+ */
+ public static void setAlpha(Graphics g, float alpha) {
+ g.setColor(new Color(0, 0, 0, Math.round(255 * alpha)));
+ }
+
+ /**
+ * Remove the alpha channel from the passed color.
+ *
+ * @param c
+ * a color
+ * @return the same color, but without an alpha channel
+ */
+ public static Color removeAlpha(Color c) {
+ if (c.getAlpha() != 100) {
+ c = new Color(c.getRGB());
+ }
+ return c;
+ }
+
+ /**
+ * Draw a string with a blur or shadow effect. The light angle is assumed to
+ * be 0 degrees, (i.e., window is illuminated from top). The effect is
+ * intended to be subtle to be usable in as many text components as
+ * possible. The effect is generated with multiple calls to draw string.
+ * This method paints the text on coordinates tx
,
+ * ty
. If text should be painted elsewhere, a transform should
+ * be applied to the graphics before passing it.
+ *
+ * All modifications to the graphics object is restored by this method + * before returning. + *
+ * Based on code by Romain Guy, {@linkplain http://filthyrichclients.org/},
+ * see examples from chapter 16.
+ *
+ * @param g
+ * graphics component to paint on
+ * @param s
+ * the string to paint
+ * @param c
+ * effect color
+ * @param size
+ * effect size
+ * @param tx
+ * x-coordinate translation (i.e, pixels to move the center of
+ * the x-axis)
+ * @param ty
+ * y-coordinate translation (i.e, pixels to move the center of
+ * the y-axis)
+ * @param isShadow
+ * true
if this is a shadow being painted that
+ * should be slightly offset to look more like a shadow being
+ * casted, otherwise false
.
+ */
+ private static void paintTextEffect(Graphics2D g, String s, Color c,
+ int size, double tx, double ty, boolean isShadow) {
+
+ prepareGraphics(g);
+
+ final float opacity = 0.8f; // Effect "darkness".
+ final Composite oldComposite = g.getComposite();
+ final Color oldColor = g.getColor();
+
+ // Use a alpha blend smaller than 1 to prevent the effect from becoming
+ // too dark when multiple paints occur on top of each other.
+ float preAlpha = 0.4f;
+ if (oldComposite instanceof AlphaComposite
+ && ((AlphaComposite) oldComposite).getRule() == AlphaComposite.SRC_OVER) {
+ preAlpha = Math.min(((AlphaComposite) oldComposite).getAlpha(),
+ preAlpha);
+ }
+ g.setColor(c);
+
+ g.translate(tx, ty);
+
+ // If the effect is a shadow it looks better to stop painting a bit to
+ // early... (shadow will look softer).
+ int maxSize = isShadow ? size - 1 : size;
+
+ for (int i = -size; i <= maxSize; i++) {
+ for (int j = -size; j <= maxSize; j++) {
+ double distance = i * i + j * j;
+ float alpha = opacity;
+ if (distance > 0.0d) {
+ alpha = (float) (1.0f / ((distance * size) * opacity));
+ }
+ alpha *= preAlpha;
+ if (alpha > 1.0f) {
+ alpha = 1.0f;
+ }
+ g.setComposite(AlphaComposite.getInstance(
+ AlphaComposite.SRC_OVER, alpha));
+ g.drawString(s, i + size, j + size);
+ }
+ }
+
+ // Restore graphics
+ g.translate(-tx, -ty);
+ g.setComposite(oldComposite);
+ g.setColor(oldColor);
+
+ g.drawString(s, 0, 0);
+ }
+
+ private static void prepareGraphics(Graphics2D g) {
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+ RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ }
+
+ /**
+ * Draw a string with a drop shadow. The light angle is assumed to be 0
+ * degrees, (i.e., window is illuminated from top) and the shadow size is 2,
+ * with a 1 pixel vertical displacement. The shadow is intended to be subtle
+ * to be usable in as many text components as possible. The shadow is
+ * generated with multiple calls to draw string. This method paints the text
+ * on coordinates 0, 1. If text should be painted elsewhere, a transform
+ * should be applied to the graphics before passing it.
+ *
+ * All modifications to the graphics object is restored by this method + * before returning. + * + * @see #paintTextEffect(Graphics2D, String, Color, int, boolean) + * + * @param g + * graphics component to paint on + * @param s + * the string to paint + * @param c + * the color of the shadow. Any alpha channel will be discarded + */ + public static void paintTextShadow(Graphics2D g, String s, Color c) { + paintTextEffect(g, s, removeAlpha(c), TEXT_SHADOW_SIZE, + -TEXT_SHADOW_SIZE, 1 - TEXT_SHADOW_SIZE, true); + } + + /** + * Draw a string with a drop shadow. The light angle is assumed to be 0 + * degrees, (i.e., window is illuminated from top) and the shadow size is 2, + * with a 1 pixel vertical displacement. The shadow is intended to be subtle + * to be usable in as many text components as possible. The shadow is + * generated with multiple calls to draw string. This method paints the text + * on coordinates 0, 1. If text should be painted elsewhere, a transform + * should be applied to the graphics before passing it. + *
+ * All modifications to the graphics object is restored by this method + * before returning. + * + * @see #paintTextEffect(Graphics2D, String, Color, int, boolean) + * + * @param g + * graphics component to paint on + * @param s + * the string to paint + */ + public static void paintTextShadow(Graphics2D g, String s) { + paintTextShadow(g, s, Color.BLACK); + } + + /** + * Draw a string with a glow effect. Glow differs from a drop shadow in that + * it isn't offset in any direction (i.e., not affected by + * "lighting conditions"). + *
+ * All modifications to the graphics object is restored by this method + * before returning. + * + * @param g + * graphics component to paint on + * @param s + * the string to draw + * @param glow + * the solid glow color. Do not use the alpha channel as this + * will be discarded + */ + public static void paintTextGlow(Graphics2D g, String s, Color glow) { + paintTextEffect(g, s, removeAlpha(glow), TEXT_SHADOW_SIZE, + -TEXT_SHADOW_SIZE, -TEXT_SHADOW_SIZE, false); + } + + /** + * Utility to assist with painting of a vertical gradient. The opaque + * property of the component being painted is not honored. If + * {@link #paint(Graphics, JComponent)} is invoked a gradient background is + * always painted. + *
+ * A note on cyclic gradients:
+ * Cyclic gradients yields better performance, however they cannot be safely
+ * used to paint backgrounds on components that perform partial repaints
+ * (e.g., JList
). Cyclic gradients can be turned on using
+ * {@link #setCyclic(boolean)} but are not used by default in this painter.
+ *
+ * @author Samuel Sjoberg, Extenda AB
+ */
+ public static class GradientPainter {
+
+ /** Start color. */
+ private Color c1;
+
+ /** End color. */
+ private Color c2;
+
+ /** Cached gradient paint. */
+ private GradientPaint paint;
+
+ /** Cyclic gradient. */
+ private boolean cyclic = false;
+
+ /**
+ * Create a new GradientPainter
using the background color
+ * of the passed component. The gradient will fade from the background
+ * color in to a darker shade of the same color.
+ *
+ * @param c
+ * the component owning the painter
+ */
+ public GradientPainter(JComponent c) {
+ this(c, c.getBackground(), c.getBackground().darker().darker());
+ }
+
+ /**
+ * Create a new GradientPainter
using the passed colors.
+ * The gradient will fade from c1
to c2
.
+ *
+ * @param c
+ * the component owning the painter
+ * @param c1
+ * the start color
+ * @param c2
+ * the end color
+ */
+ public GradientPainter(JComponent c, Color c1, Color c2) {
+ this.c1 = c1;
+ this.c2 = c2;
+ c.addComponentListener(new ComponentAdapter() {
+ public void componentResized(ComponentEvent e) {
+ paint = null;
+ }
+ });
+ }
+
+ /**
+ * Set the gradient colors. For changes to take effect, the component
+ * must be repainted by invoking e.g. {@link JComponent#repaint()}.
+ *
+ * @param c1
+ * the start color
+ * @param c2
+ * the end color
+ */
+ public void setColors(Color c1, Color c2) {
+ this.c1 = c1;
+ this.c2 = c2;
+ paint = null;
+ }
+
+ /**
+ * Use cyclic gradients. Default behavior is non-cyclic gradients.
+ *
+ * @param cyclic
+ * If true
, cyclic gradients a used, otherwise
+ * non-cyclic gradients are used.
+ */
+ public void setCyclic(boolean cyclic) {
+ this.cyclic = cyclic;
+ paint = null;
+ }
+
+ /**
+ * Fill the component with a gradient background. This method does not
+ * honor the opaque property.
+ *
+ * @param g
+ * graphics to paint upon
+ * @param c
+ * the component being painted
+ */
+ public void paint(Graphics g, JComponent c) {
+ if (paint == null) {
+ paint = new GradientPaint(0, 0, c1, 0, c.getHeight(), c2,
+ cyclic);
+ }
+ ((Graphics2D) g).setPaint(paint);
+ g.fillRect(0, 0, c.getWidth(), c.getHeight());
+ }
+ }
+}
diff --git a/src/net/i2p/itoopie/gui/component/multilinelabel/MultiLineLabelUI.java b/src/net/i2p/itoopie/gui/component/multilinelabel/MultiLineLabelUI.java
new file mode 100644
index 000000000..eeb51acf8
--- /dev/null
+++ b/src/net/i2p/itoopie/gui/component/multilinelabel/MultiLineLabelUI.java
@@ -0,0 +1,608 @@
+package net.i2p.itoopie.gui.component.multilinelabel;
+
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2009 Samuel Sjoberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+import java.awt.Dimension;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.LabelUI;
+import javax.swing.plaf.basic.BasicLabelUI;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.PlainDocument;
+import javax.swing.text.Segment;
+import javax.swing.text.Utilities;
+import javax.swing.text.View;
+
+import net.i2p.itoopie.gui.component.MultiLineLabel;
+
+
+/**
+ * Label UI delegate that supports multiple lines and line wrapping. Hard line
+ * breaks (\n
) are preserved. If the dimensions of the label is too
+ * small to fit all content, the string will be clipped and "..." appended to
+ * the end of the visible text (similar to the default behavior of
+ * JLabel
). If used in conjunction with a {@link MultiLineLabel},
+ * text alignment (horizontal and vertical) is supported. The UI delegate can be
+ * used on a regular JLabel
if text alignment isn't required. The
+ * default alignment, left and vertically centered, will then be used.
+ *
+ * Example of usage: + * + *
+ * JLabel myLabel = new JLabel(); + * myLabel.setUI(MultiLineLabelUI.labelUI); + * myLabel.setText("A long label that will wrap automatically."); + *+ * + *
+ * The line and wrapping support is implemented without using a
+ * View
to make it easy for subclasses to add custom text effects
+ * by overriding {@link #paintEnabledText(JLabel, Graphics, String, int, int)}
+ * and {@link #paintDisabledText(JLabel, Graphics, String, int, int)}. This
+ * class is designed to be easily extended by subclasses.
+ *
+ * @author Samuel Sjoberg, http://samuelsjoberg.com
+ * @version 1.3.0
+ */
+public class MultiLineLabelUI extends BasicLabelUI implements ComponentListener {
+
+ /** Shared instance of the UI delegate. */
+ public static LabelUI labelUI = new MultiLineLabelUI();
+
+ /**
+ * Client property key used to store the calculated wrapped lines on the
+ * JLabel.
+ */
+ public static final String PROPERTY_KEY = "WrappedText";
+
+ // Static references to avoid heap allocations.
+ protected static Rectangle paintIconR = new Rectangle();
+ protected static Rectangle paintTextR = new Rectangle();
+ protected static Rectangle paintViewR = new Rectangle();
+ protected static Insets paintViewInsets = new Insets(0, 0, 0, 0);
+
+ /** Font metrics of the JLabel being rendered. */
+ protected FontMetrics metrics;
+
+ /** Default size of the lines list. */
+ protected static int defaultSize = 4;
+
+ /**
+ * Get the shared UI instance.
+ *
+ * @param c
+ * the component about to be installed
+ * @return the shared UI delegate instance
+ */
+ public static ComponentUI createUI(JComponent c) {
+ return labelUI;
+ }
+
+ /** {@inheritDoc} */
+ protected void uninstallDefaults(JLabel c) {
+ super.uninstallDefaults(c);
+ clearCache(c);
+ }
+
+ /** {@inheritDoc} */
+ protected void installListeners(JLabel c) {
+ super.installListeners(c);
+ c.addComponentListener(this);
+ }
+
+ /** {@inheritDoc} */
+ protected void uninstallListeners(JLabel c) {
+ super.uninstallListeners(c);
+ c.removeComponentListener(this);
+ }
+
+ /**
+ * Clear the wrapped line cache.
+ *
+ * @param l
+ * the label containing a cached value
+ */
+ protected void clearCache(JLabel l) {
+ l.putClientProperty(PROPERTY_KEY, null);
+ }
+
+ /** {@inheritDoc} */
+ public void propertyChange(PropertyChangeEvent e) {
+ super.propertyChange(e);
+ final String name = e.getPropertyName();
+ if (name.equals("text") || "font".equals(name)) {
+ clearCache((JLabel) e.getSource());
+ }
+ }
+
+ /**
+ * Calculate the paint rectangles for the icon and text for the passed
+ * label.
+ *
+ * @param l
+ * a label
+ * @param fm
+ * the font metrics to use, or null
to get the font
+ * metrics from the label
+ * @param width
+ * label width
+ * @param height
+ * label height
+ */
+ protected void updateLayout(JLabel l, FontMetrics fm, int width, int height) {
+ if (fm == null) {
+ fm = l.getFontMetrics(l.getFont());
+ }
+ metrics = fm;
+
+ String text = l.getText();
+ Icon icon = l.getIcon();
+ Insets insets = l.getInsets(paintViewInsets);
+
+ paintViewR.x = insets.left;
+ paintViewR.y = insets.top;
+ paintViewR.width = width - (insets.left + insets.right);
+ paintViewR.height = height - (insets.top + insets.bottom);
+
+ paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
+ paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
+
+ layoutCL(l, fm, text, icon, paintViewR, paintIconR, paintTextR);
+ }
+
+ protected void prepareGraphics(Graphics g) {
+ }
+
+ /** {@inheritDoc} */
+ public void paint(Graphics g, JComponent c) {
+
+ // parent's update method fills the background
+ prepareGraphics(g);
+
+ JLabel label = (JLabel) c;
+ String text = label.getText();
+ Icon icon = (label.isEnabled()) ? label.getIcon() : label
+ .getDisabledIcon();
+
+ if ((icon == null) && (text == null)) {
+ return;
+ }
+
+ FontMetrics fm = g.getFontMetrics();
+
+ updateLayout(label, fm, c.getWidth(), c.getHeight());
+
+ if (icon != null) {
+ icon.paintIcon(c, g, paintIconR.x, paintIconR.y);
+ }
+
+ if (text != null) {
+ View v = (View) c.getClientProperty("html");
+ if (v != null) {
+ // HTML view disables multi-line painting.
+ v.paint(g, paintTextR);
+ } else {
+ // Paint the multi line text
+ paintTextLines(g, label, fm);
+ }
+ }
+ }
+
+ /**
+ * Paint the wrapped text lines.
+ *
+ * @param g
+ * graphics component to paint on
+ * @param label
+ * the label being painted
+ * @param fm
+ * font metrics for current font
+ */
+ protected void paintTextLines(Graphics g, JLabel label, FontMetrics fm) {
+ ListBasicHTML.isHTMLString(String)
in future JDKs.
+ *
+ * @param s
+ * the string
+ * @return true
if string is HTML, otherwise false
+ */
+ private static boolean isHTMLString(String s) {
+ if (s != null) {
+ if ((s.length() >= 6) && (s.charAt(0) == '<')
+ && (s.charAt(5) == '>')) {
+ String tag = s.substring(1, 5);
+ return tag.equalsIgnoreCase("html");
+ }
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public Dimension getPreferredSize(JComponent c) {
+ Dimension d = super.getPreferredSize(c);
+ JLabel label = (JLabel) c;
+
+ if (isHTMLString(label.getText())) {
+ return d; // HTML overrides everything and we don't need to process
+ }
+
+ // Width calculated by super is OK. The preferred width is the width of
+ // the unwrapped content as long as it does not exceed the width of the
+ // parent container.
+
+ if (c.getParent() != null) {
+ // Ensure that preferred width never exceeds the available width
+ // (including its border insets) of the parent container.
+ Insets insets = c.getParent().getInsets();
+ Dimension size = c.getParent().getSize();
+ if (size.width > 0) {
+ // If width isn't set component shouldn't adjust.
+ d.width = size.width - insets.left - insets.right;
+ }
+ }
+
+ updateLayout(label, null, d.width, d.height);
+
+ // The preferred height is either the preferred height of the text
+ // lines, or the height of the icon.
+ d.height = Math.max(d.height, getPreferredHeight(label));
+
+ return d;
+ }
+
+ /**
+ * The preferred height of the label is the height of the lines with added
+ * top and bottom insets.
+ *
+ * @param label
+ * the label
+ * @return the preferred height of the wrapped lines.
+ */
+ protected int getPreferredHeight(JLabel label) {
+ int numOfLines = getTextLines(label).size();
+ Insets insets = label.getInsets(paintViewInsets);
+ return numOfLines * metrics.getHeight() + insets.top + insets.bottom;
+ }
+
+ /**
+ * Get the lines of text contained in the text label. The prepared lines is
+ * cached as a client property, accessible via {@link #PROPERTY_KEY}.
+ *
+ * @param l
+ * the label
+ * @return the text lines of the label.
+ */
+ @SuppressWarnings("unchecked")
+ protected Listp1
if content does
+ * not need to wrap, otherwise it will be less than p1
.
+ */
+ protected int calculateBreakPosition(Document doc, int p0, int p1) {
+ Segment segment = SegmentCache.getSegment();
+ try {
+ doc.getText(p0, p1 - p0, segment);
+ } catch (BadLocationException e) {
+ throw new Error("Can't get line text");
+ }
+
+ int width = paintTextR.width;
+ int p = p0
+ + Utilities.getBreakLocation(segment, metrics, 0, width, null,
+ p0);
+ SegmentCache.releaseSegment(segment);
+ return p;
+ }
+
+ /**
+ * Static singleton {@link Segment} cache.
+ *
+ * @see javax.swing.text.SegmentCache
+ *
+ * @author Samuel Sjoberg
+ */
+ protected static final class SegmentCache {
+
+ /** Reused segments. */
+ private ArrayListSegment
. When done, the Segment
+ * should be recycled by invoking {@link #releaseSegment(Segment)}.
+ *
+ * @return a Segment
.
+ */
+ public static Segment getSegment() {
+ int size = cache.segments.size();
+ if (size > 0) {
+ return cache.segments.remove(size - 1);
+ }
+ return new Segment();
+ }
+
+ /**
+ * Releases a Segment
. A segment should not be used after
+ * it is released, and a segment should never be released more than
+ * once.
+ */
+ public static void releaseSegment(Segment segment) {
+ segment.array = null;
+ segment.count = 0;
+ cache.segments.add(segment);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/net/i2p/itoopie/gui/component/multilinelabel/MultiLineShadowUI.java b/src/net/i2p/itoopie/gui/component/multilinelabel/MultiLineShadowUI.java
new file mode 100644
index 000000000..59b3e15e1
--- /dev/null
+++ b/src/net/i2p/itoopie/gui/component/multilinelabel/MultiLineShadowUI.java
@@ -0,0 +1,91 @@
+package net.i2p.itoopie.gui.component.multilinelabel;
+
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2009 Samuel Sjoberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.LabelUI;
+
+/**
+ * MultiLineLabelUI
that paints the text with a drop shadow.
+ *
+ * @see MultiLineLabelUI
+ * @see Effects#paintTextShadow(Graphics2D, String)
+ *
+ * @author Samuel Sjoberg, http://samuelsjoberg.com
+ * @version 1.0.0
+ */
+public class MultiLineShadowUI extends MultiLineLabelUI {
+
+ /** Shared UI instance. */
+ public static LabelUI labelUI = new MultiLineShadowUI();
+
+ /**
+ * Get the shared UI instance.
+ *
+ * @param c
+ * the component requesting a UI delegate
+ * @return the shared UI instance.
+ */
+ public static ComponentUI createUI(JComponent c) {
+ return labelUI;
+ }
+
+ /**
+ * Paint the text with a text effect.
+ *
+ * @param g
+ * graphics component used to paint on
+ * @param s
+ * the string to paint
+ * @param textX
+ * the x coordinate
+ * @param textY
+ * the y coordinate
+ */
+ private void paintText(Graphics g, String s, int textX, int textY) {
+ g.translate(textX, textY);
+ Effects.paintTextShadow((Graphics2D) g, s);
+ g.translate(-textX, -textY);
+ }
+
+ /** {@inheritDoc} */
+ protected void paintEnabledText(JLabel l, Graphics g, String s, int textX,
+ int textY) {
+ g.setColor(l.getForeground());
+ paintText(g, s, textX, textY);
+ }
+
+ /** {inheritDoc} */
+ protected void paintDisabledText(JLabel l, Graphics g, String s, int textX,
+ int textY) {
+ g.setColor(l.getBackground().darker());
+ paintText(g, s, textX, textY);
+ }
+}
diff --git a/src/net/i2p/itoopie/gui/component/multilinelabel/ShadowLabelUI.java b/src/net/i2p/itoopie/gui/component/multilinelabel/ShadowLabelUI.java
new file mode 100644
index 000000000..a2af8622e
--- /dev/null
+++ b/src/net/i2p/itoopie/gui/component/multilinelabel/ShadowLabelUI.java
@@ -0,0 +1,71 @@
+package net.i2p.itoopie.gui.component.multilinelabel;
+
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2009 Samuel Sjoberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+
+import javax.swing.JLabel;
+import javax.swing.border.EmptyBorder;
+import javax.swing.plaf.LabelUI;
+import javax.swing.plaf.basic.BasicLabelUI;
+
+/**
+ * Label UI delegate painting the text with a subtle drop shadow.
+ *
+ * @author Samuel Sjoberg, http://samuelsjoberg.com
+ * @version 1.0.0
+ */
+public class ShadowLabelUI extends BasicLabelUI {
+
+ /** Static reference to the UI. */
+ public static LabelUI labelUI = new ShadowLabelUI();
+
+ /** {@inheritDoc} */
+ protected void installDefaults(JLabel c) {
+ super.installDefaults(c);
+ if (c.getBorder() == null) {
+ c.setBorder(new EmptyBorder(2, 2, 2, 2));
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void paintDisabledText(JLabel l, Graphics g, String s, int textX,
+ int textY) {
+ g.setColor(l.getBackground().darker());
+ g.translate(textX, textY);
+ Effects.paintTextShadow((Graphics2D) g, s);
+ g.translate(-textX, -textY);
+ }
+
+ /** {@inheritDoc} */
+ protected void paintEnabledText(JLabel l, Graphics g, String s, int textX,
+ int textY) {
+ g.setColor(l.getForeground());
+ g.translate(textX, textY);
+ Effects.paintTextShadow((Graphics2D) g, s);
+ g.translate(-textX, -textY);
+ }
+}