/*******************************************************************************
* Copyright (c) 2012-present Jakub Kováč, Jozef Brandýs, Katarína Kotrlová,
* Pavol Lukča, Ladislav Pápay, Viktor Tomkovič, Tatiana Tóthová
*
* 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 3 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
package algvis.ui;
import java.awt.Color;
import java.awt.Font;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import javax.swing.JEditorPane;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.StyleSheet;
import javax.swing.undo.StateEditable;
import algvis.core.StringUtils;
import algvis.core.history.HashtableStoreSupport;
import algvis.internationalization.LanguageListener;
import algvis.internationalization.Languages;
public class Commentary extends JEditorPane implements LanguageListener,
HyperlinkListener, StateEditable {
private static final long serialVersionUID = 9023200331860482960L;
private final VisPanel panel;
private final JScrollPane sp;
private int indexOfNextStep = 0, currentPosition = -1;
private List<String> s = new ArrayList<String>(),
pre = new ArrayList<String>(), post = new ArrayList<String>();
private List<String[]> param = new ArrayList<String[]>();
private static final SimpleAttributeSet normalAttr = new SimpleAttributeSet();
private static final SimpleAttributeSet hoverAttr = new SimpleAttributeSet();
static {
StyleConstants.setBackground(normalAttr, Color.WHITE);
StyleConstants.setBackground(hoverAttr, new Color(0xDB, 0xF1, 0xF9)); // #DBF1F9
}
private final String hash = Integer.toString(hashCode());
public Commentary(VisPanel panel, JScrollPane sp) {
super();
this.panel = panel;
setContentType("text/html; charset=iso-8859-2");
setEditable(false);
final Font font = UIManager.getFont("Label.font");
final StyleSheet css = ((HTMLDocument) getDocument()).getStyleSheet();
css.addRule("body { font-family: " + font.getFamily() + "; "
+ "font-size: 12pt; margin: 10pt; margin-top: 0pt; " + "}");
css.addRule(".step { margin-bottom: 5pt; }");
css.addRule("ol { padding-left: 14px; margin: 0px; }");
css.addRule("a { color: black; text-decoration:none; }");
css.addRule("p.note { font-style: italic; margin: 0pt; margin-bottom: 5pt; }");
this.sp = sp;
Languages.addListener(this);
addHyperlinkListener(this);
setText("<html><body></body></html>");
}
public synchronized void clear() {
currentPosition = -1;
indexOfNextStep = 0;
s = new ArrayList<String>();
pre = new ArrayList<String>();
post = new ArrayList<String>();
param = new ArrayList<String[]>();
setText("<html><body></body></html>");
}
private String str(int i) {
// if (i < 0) i = s.size() + i;
assert (0 <= i && i < s.size());
return pre.get(i)
+ StringUtils.subst(Languages.getString(s.get(i)), param.get(i))
+ post.get(i);
}
@Override
public void languageChanged() {
refresh();
}
public void refresh() {
final StringBuilder text = new StringBuilder("");
synchronized (this) {
for (int i = 0; i < s.size(); ++i) {
if (i == currentPosition) {
text.append("<B>").append(str(i)).append("</B>");
} else {
text.append(str(i));
}
}
}
final HTMLDocument html = (HTMLDocument) getDocument();
Element body = null;
final Element[] roots = html.getRootElements();
for (int i = 0; i < roots[0].getElementCount(); i++) {
final Element element = roots[0].getElement(i);
if (element.getAttributes().getAttribute(
StyleConstants.NameAttribute) == HTML.Tag.BODY) {
body = element;
break;
}
}
try {
synchronized (this) {
if (text.toString().equals("")) {
html.setInnerHTML(body, " ");
} else {
html.setInnerHTML(body, text.toString());
}
}
} catch (final BadLocationException e) {
e.printStackTrace();
} catch (final IOException e) {
e.printStackTrace();
}
scrollDown();
}
private void scrollDown() {
final JScrollBar v = sp.getVerticalScrollBar();
v.setValue(v.getMaximum() - v.getVisibleAmount());
}
private synchronized void add(String u, String v, String w, String... par) {
++currentPosition;
pre.add(u);
s.add(v);
post.add(w);
param.add(par);
}
private String[] int2strArray(int[] a) {
final String[] r = new String[a.length];
for (int i = 0; i < a.length; ++i) {
r[i] = "" + a[i];
}
return r;
}
public void setHeader(String h) {
// clear();
add("<h2>", h, "</h2>");
}
public void setHeader(String h, String... par) {
// clear();
add("<h2>", h, "</h2>", par);
}
public void setHeader(String h, int... par) {
setHeader(h, int2strArray(par));
}
public void addNote(String s) {
add("<p class=\"note\">", s, "</p>");
}
public void addNote(String s, String... par) {
add("<p class=\"note\">", s, "</p>", par);
}
public void addNote(String s, int... par) {
addNote(s, int2strArray(par));
}
public void addStep(String s, String... par) {
add("<ol start=\"" + (indexOfNextStep + 1)
+ "\"><li class=\"step\"><p><a href=\""
+ panel.history.getLastEditId() + "\"> ", s, "</a></p></li></ol>",
par);
++indexOfNextStep;
}
public void addStep(String s) {
addStep(s, "");
}
public void addStep(String s, int... par) {
addStep(s, int2strArray(par));
}
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
panel.history.goTo(Integer.parseInt(e.getDescription()));
panel.refresh();
} else {
final Element element = e.getSourceElement();
final int start = element.getStartOffset();
final int length = element.getEndOffset() - start;
final HTMLDocument html = ((HTMLDocument) getDocument());
if (e.getEventType() == HyperlinkEvent.EventType.ENTERED) {
html.setParagraphAttributes(start, length, hoverAttr, false);
}
if (e.getEventType() == HyperlinkEvent.EventType.EXITED) {
html.setParagraphAttributes(start, length, normalAttr, false);
}
}
}
@Override
public void storeState(Hashtable<Object, Object> state) {
HashtableStoreSupport.store(state, hash + "currentPosition",
currentPosition);
HashtableStoreSupport.store(state, hash + "s", s);
HashtableStoreSupport.store(state, hash + "pre", pre);
HashtableStoreSupport.store(state, hash + "post", post);
HashtableStoreSupport.store(state, hash + "param", param);
}
@Override
public synchronized void restoreState(Hashtable<?, ?> state) {
final Object position = state.get(hash + "currentPosition");
if (position != null) {
this.currentPosition = (Integer) HashtableStoreSupport
.restore(position);
}
final Object s = state.get(hash + "s");
if (s != null) {
this.s = (List<String>) HashtableStoreSupport.restore(s);
}
final Object pre = state.get(hash + "pre");
if (pre != null) {
this.pre = (List<String>) HashtableStoreSupport.restore(pre);
}
final Object post = state.get(hash + "post");
if (post != null) {
this.post = (List<String>) HashtableStoreSupport.restore(post);
}
final Object param = state.get(hash + "param");
if (param != null) {
this.param = (List<String[]>) HashtableStoreSupport.restore(param);
}
}
}