10年ぶりにSwingでデスクトップアプリを開発し、いつのまにか本格的に実用に耐えるフレームワークに仕上がっていたのを知った。NetBeans IDEはとても快適で、API仕様の確認やAPIライブラリのソースコードの確認も簡単にできる。わからない部分があっても、Web検索をすればたいていのことが解説されているのを発見できた。しかし、それでもいくつか、解決策がすぐには見つからない点があった。せっかくなのでここに書き残しておく。
Swingでは、登場した当初からテキストコンポーネントでHTMLが使えるようになっていた。つまり、テキストの冒頭が「<html>」で始まっていると、それ以降のテキストがHTMLとして解釈される。IEでセキュリティ問題を引き起こしたのと同根のうざい仕様だ。JavaScriptは使えないのでXSS脆弱性になることはないものの、表示するデータの出所によっては予期しないみっともない結果が生じ得る(巨大な文字を表示させられるとか)。
こういう前世紀的な悪仕様は廃止してもらいたい(デフォルトで無効に)ところだが、既に使われているものはそうもいかないのか、コンポーネントごとに putClientProperty で "html.disable" を Boolean.TRUE にすることで解決する策が、JDK 1.5になって導入されていた(Bug ID 4652898)。JTableでは各セルでセットしないといけないので、TableCellRenderer で putClientProperty して解決した。
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.Component;
import javax.swing.JTable;
class HtmlDisabledTableCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
putClientProperty("html.disable", Boolean.TRUE);
return this;
}
}
ところが、JTable の JToolTip にセルの内容を表示するようにしていたため、そっちも対策しないといけない。そこで、JTableをサブクラス化して createToolTip() メソッドをオーバーライドし、そこで putClientProperty するようにした。
import java.swing.JToolTip;
public JToolTip createToolTip() {
JToolTip tip = new JToolTip();
tip.setComponent(this);
tip.putClientProperty("html.disable", Boolean.TRUE);
return tip;
}
外部入力に依存する部分か否かに関わらず全てに対して無効化したいところなのに、こういう泥縄なやり方では気持ち悪い。Bug ID 4652898のコメント欄で指摘されているように、一括で無効化する手段を用意してほしいものだ。
Windowsで日本語を使うとフォントのかっこ悪さにげんなり。awt.useSystemAAFontSettings プロパティをセットし、各コンポーネントのフォントをメイリオにすることで、奇麗にできた。
System.setProperty("awt.useSystemAAFontSettings", "lcd");
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import java.awt.Font;
UIDefaults ud = UIManager.getLookAndFeel().getDefaults();
ud.put("Panel.font", new Font("Meiryo", Font.PLAIN, 12));
ud.put("Label.font", new Font("Meiryo", Font.PLAIN, 12));
ud.put("TextField.font", new Font("Meiryo", Font.PLAIN, 11));
ud.put("TextArea.font", new Font("Meiryo", Font.PLAIN, 11));
ud.put("ToolTip.font", new Font("Meiryo", Font.PLAIN, 12));
ud.put("Button.font", new Font("Meiryo", Font.PLAIN, 12));
ud.put("Table.font", new Font("Meiryo", Font.PLAIN, 11));
ud.put("TableHeader.font", new Font("Meiryo", Font.PLAIN, 11));
ud.put("CheckBox.font", new Font("Meiryo", Font.PLAIN, 12));
(以下略)
参照
JLabelで表示するテキストがコンポーネントの幅を超えるとき、「…」で省略表示が出るようになっていて、通常、右側で省略されるようになっているが、これを左側で省略するようにしたい。
これがわからなかった。NetBeans IDEでもファイルのパス名表示で左側が「…」になっているのでできるはずなのだが、方法が見つからなかった。未だ解決していない。誰か教えて。
(解決策予定地) (追記)はてブコメントで教えて頂いた。以下の例のように、Rendererを自作するしかないみたい。