1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
| import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.JToolTip;
import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.text.BadLocationException;
import javax.swing.text.html.HTMLDocument;
/**
*
*/
/**
* Une JTable avec un tooltip pour chaque cellule, sous forme d'une texte dans un scrollpane, avec fonction de copie vers le presse-papier
*/
public class JTableWithCustomTooltip extends JTable {
private static final long serialVersionUID = 1L;
private static final Dimension DEFAULT_TOOLTIP_SIZE = new Dimension(200,100); // la taille par défaut du tooltip
/**
*
*/
public JTableWithCustomTooltip() {
}
public JTableWithCustomTooltip(TableModel dm) {
super(dm);
}
public JTableWithCustomTooltip(TableModel dm, TableColumnModel cm) {
super(dm, cm);
}
public JTableWithCustomTooltip(int numRows, int numColumns) {
super(numRows, numColumns);
}
// public JTableWithCustomTooltip(Vector<?> rowData, Vector<String> columnNames) {
public JTableWithCustomTooltip(Vector<? extends Vector<?>> rowData, Vector<?> columnNames) {
super(rowData, columnNames);
}
public JTableWithCustomTooltip(Object[][] rowData, Object[] columnNames) {
super(rowData, columnNames);
}
public JTableWithCustomTooltip(TableModel dm, TableColumnModel cm,
ListSelectionModel sm) {
super(dm, cm, sm);
}
/**
* Le texte du bouton de copie vers le presse-papier, lorsqu'il y a aucune sélection. Peut-être rédfini pour modifier ce texte.
*/
protected String getTooltipCopyAllLabel() {
return "Copier tout";
}
/**
* Le texte du bouton de copie vers le presse-papier, lorsqu'il y a une sélection. Peut-être rédfini pour modifier ce texte.
*/
protected String getTooltipCopySelectionLabel() {
return "Copier la sélection";
}
private CustomTooltip tooltip;
private Point lastTooltipLocation;
private int lastRowIndex;
private int lastColumnIndex;
/**
* Crée le composant tooltip.
*/
@Override
public JToolTip createToolTip() {
if (getModel() instanceof ITooltipTableModel) {
if ( tooltip!=null && tooltip.isVisible() && lastTooltipLocation!=null ) {
// on conserve le précédent composant visible, pour éviter qu'un nouveau tooltip
// pour une même cellule soit régénéré lorsqu'on déplace la souris sur la cellule
return tooltip;
}
lastRowIndex = lastColumnIndex = -1; // raz variables
tooltip = new CustomTooltip();
tooltip.setComponent(this);
return tooltip;
}
else {
// raz variables
lastRowIndex = lastColumnIndex = -1;
tooltip=null;
return super.createToolTip();
}
}
/**
* Détermine la position du tooltip par rapport à la souris : on cherche à s'assurer que le
* tooltip soit sur la cellule, pour permettre de déplacer la souris sur le composant tooltip, sans
* que tooltip se referme
*/
@Override
public Point getToolTipLocation(MouseEvent event) {
Point tooltipLocation;
if (getModel() instanceof ITooltipTableModel) {
// on cherche la cellule sous la souris
int rowIndex = rowAtPoint(event.getPoint());
int columnIndex = columnAtPoint(event.getPoint());
if (rowIndex == -1 || columnIndex == -1) {
// pas de cellule sous la souris : comportement standard
tooltipLocation = super.getToolTipLocation(event);
lastRowIndex = -1;
lastColumnIndex = -1;
}
else if ( rowIndex==lastRowIndex && columnIndex==lastColumnIndex ) {
// si la cellule est la même que lors du dernier appel, on conserve la position
// précédemment calculée
tooltipLocation = lastTooltipLocation;
}
else {
// on calcule las position du tooltip
// la zone correspondant à la cellule
Rectangle rect = getCellRect(rowIndex, columnIndex, true);
Point point = event.getPoint();
// décalage par rapport à la souris de 5,5
point.x += 5;
point.y += 5;
// on force la position du tooltip à rester dans la cellule
if ( point.x > rect.getMaxX() ) {
point.x = (int)rect.getMaxX();
}
if ( point.y > rect.getMaxY() ) {
point.y = (int)rect.getMaxY();
}
tooltipLocation = point;
// on mémorise pour réutiliser lors des prochains appels sur la même cellule
lastTooltipLocation = tooltipLocation;
lastRowIndex = rowIndex;
lastColumnIndex = columnIndex;
}
} else {
// comportement standard si aucun tooltip n'est géré
tooltipLocation = super.getToolTipLocation(event);
lastRowIndex = -1;
lastColumnIndex = -1;
}
return tooltipLocation;
}
/**
* Détermine le texte du tooltip
*/
public String getToolTipText(MouseEvent event) {
String tooltipText;
if (getModel() instanceof ITooltipTableModel) {
ITooltipTableModel model = (ITooltipTableModel) getModel();
int rowIndex = rowAtPoint(event.getPoint());
int columnIndex = columnAtPoint(event.getPoint());
if (rowIndex == -1 || columnIndex == -1) {
tooltipText = super.getToolTipText(event);
} else {
// pour s'adapter aux tris, il faut convertir les index
rowIndex = convertRowIndexToModel(rowIndex);
columnIndex = convertColumnIndexToModel(columnIndex);
// on utilise la méthode du modèle spécifique
tooltipText = model.getTooltipText(rowIndex, columnIndex);
}
} else {
tooltipText = super.getToolTipText(event);
}
return tooltipText;
}
/**
* La taille par défaut du tooltip. Peut être redéfinie pour modifier cette taille
*/
protected Dimension getTooltipSize() {
return DEFAULT_TOOLTIP_SIZE;
}
/**
* La classe qui réprésente le composant tooltip
*
*/
private class CustomTooltip extends JToolTip {
private static final long serialVersionUID = 1L;
private transient final JEditorPane text; // le texte du tooltip
private transient final JScrollPane scrollPane; // un scrollpane, si le texte est plus grand que le tooltip
private transient final MouseWheelListener mouseWheelListener; // pour gérer la molette de la souris
public CustomTooltip() {
setRequestFocusEnabled(false);
mouseWheelListener = createMouseWheelListener();
setLayout(new BorderLayout());
// création du composant pour afficher le tooltip
text = new JTextPane();
text.setEditable(false);
text.setEnabled(true);
text.setContentType("text/html");
// affectes couleurs et police standards de tooltip au composant
LookAndFeel.installColorsAndFont(text, "ToolTip.background",
"ToolTip.foreground", "ToolTip.font");
text.setSelectedTextColor(text.getBackground());
text.setSelectionColor(text.getForeground());
installBodyFont();
scrollPane = new JScrollPane(text,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
add(scrollPane, BorderLayout.CENTER);
scrollPane.setBorder(null);
// barre d'outils pour le bouton de copie vers le presse-papier
JToolBar toolBar = new JToolBar();
toolBar.setFloatable(false);
toolBar.setRollover(true);
JButton button = new JButton(getTooltipCopyAllLabel());
toolBar.add(button);
button.addActionListener((e)-> saveToClipboard());
add( toolBar, BorderLayout.SOUTH );
// permet d'adapter le texte du bouton à la sélection
text.addCaretListener( (e)-> setCopyButtonLabel( button, e.getDot(), e.getMark()) );
// permet de prendre le focus quand on survole le texte (afin d'utiliser CTRL-A et CTRL-C
text.addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e) {
SwingUtilities.getWindowAncestor(text).requestFocus();
text.requestFocusInWindow();
};
});
// installe l'action de copie spécifique pour le raccourci CTEL-C
installCTRLC();
}
private void installCTRLC() {
Action copyAction = new AbstractAction() {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
saveToClipboard();
}
};
text.getActionMap().put("copy-to-clipboard", copyAction);
}
private void setCopyButtonLabel(JButton button, int start, int end) {
if ( start==end ) {
button.setText( getTooltipCopyAllLabel() );
}
else {
button.setText( getTooltipCopySelectionLabel() );
}
}
/**
* Copie le texte sélectionné vers le presse-papier, ou tout le texte lorsqu'il n'y a pas de sélection. Les passages à la ligne sont consérvés en texte à plat
*/
private void saveToClipboard() {
if ( !"".equals( text.getText() ) ) {
HtmlSelection selection=null;
if ( text.getSelectionStart()==text.getSelectionEnd() ) {
String html = text.getText();
String raw;
try ( StringReader stringReader = new StringReader(html) ) {
raw = new Html2Text().parse( stringReader );
}
catch (IOException ioe) {
try {
raw = text.getDocument().getText(0, text.getDocument().getLength());
} catch (BadLocationException ble) {
ble.printStackTrace();
raw=null;
}
}
if ( raw!=null ) {
selection = new HtmlSelection( html, raw );
}
}
else {
try (StringWriter stringWriter = new StringWriter()){
text.getEditorKit().write(stringWriter, text.getDocument(), text.getSelectionStart(),text.getSelectionEnd());
String html = stringWriter.toString();
String raw;
try ( StringReader stringReader = new StringReader(html) ) {
raw = new Html2Text().parse( stringReader );
}
catch (IOException ioe) {
raw = text.getSelectedText();
}
selection = new HtmlSelection( html, raw );
} catch (BadLocationException | IOException e) {
e.printStackTrace();
}
}
if ( selection!=null ) {
Clipboard clipboard = Toolkit.getDefaultToolkit ().getSystemClipboard ();
clipboard.setContents(selection, null);
}
}
}
private void installBodyFont() {
Font font = UIManager.getFont("ToolTip.font");
String bodyRule = "body { font-family: " + font.getFamily() + "; "
+ "font-size: " + font.getSize() + "pt; }";
((HTMLDocument) text.getDocument()).getStyleSheet().addRule(
bodyRule);
}
/**
* Affecte le texte du tooltip au composant d'affichage
*/
@Override
public void setTipText(final String tipText) {
text.setText(tipText);
text.setCaretPosition(0); // pour afficher le début du texte
text.revalidate(); // pour contourner certains effets de mauvais affichage
text.repaint(); // nécessaire pour contourner certains effets de mauvais affichage
super.setTipText(tipText);
}
@Override
public Dimension getPreferredSize() {
return getTooltipSize();
}
private MouseWheelListener createMouseWheelListener() {
return new MouseWheelListener() {
@Override
public void mouseWheelMoved(final MouseWheelEvent e) {
JComponent component = getComponent();
if (component != null) {
text.dispatchEvent(new MouseWheelEvent(text, e
.getID(), e.getWhen(), e.getModifiers(), 0, 0,
e.getClickCount(), e.isPopupTrigger(), e
.getScrollType(), e.getScrollAmount(),
e.getWheelRotation()));
}
}
};
}
/**
* Enregistre le listener de molette quand le composant est mis dans un conteneur
*/
@Override
public void addNotify() {
super.addNotify();
JComponent component = getComponent();
if (component != null) {
component.addMouseWheelListener(mouseWheelListener);
}
}
/**
* Enregistre le listener de molette quand le composant est retiré de son conteneur
*/
@Override
public void removeNotify() {
JComponent component = getComponent();
if (component != null) {
component.removeMouseWheelListener(mouseWheelListener);
}
super.removeNotify();
}
}
} |
Partager